Add project files.
This commit is contained in:
60
MatrixRenderer.cs
Normal file
60
MatrixRenderer.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using SkiaSharp;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
public class MatrixRenderer
|
||||||
|
{
|
||||||
|
private readonly WledDdpClient _wled;
|
||||||
|
private readonly int _width = 64;
|
||||||
|
private readonly int _height = 64;
|
||||||
|
|
||||||
|
public MatrixRenderer(string wledIp)
|
||||||
|
{
|
||||||
|
_wled = new WledDdpClient(wledIp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DrawAndSendFrameAsync(float currentTemp)
|
||||||
|
{
|
||||||
|
// 1. Create a 64x64 canvas
|
||||||
|
using SKBitmap bitmap = new SKBitmap(_width, _height, SKColorType.Rgba8888, SKAlphaType.Premul);
|
||||||
|
using SKCanvas canvas = new SKCanvas(bitmap);
|
||||||
|
|
||||||
|
// 2. Clear background to black
|
||||||
|
canvas.Clear(SKColors.Black);
|
||||||
|
|
||||||
|
// 3. Draw something (e.g., Temperature Text)
|
||||||
|
|
||||||
|
// Paint now only handles the color and rendering style
|
||||||
|
using SKPaint textPaint = new SKPaint
|
||||||
|
{
|
||||||
|
Color = SKColors.Orange,
|
||||||
|
IsAntialias = false // Keep crisp pixels for LED matrices
|
||||||
|
};
|
||||||
|
|
||||||
|
// Font handles the typography
|
||||||
|
using SKFont textFont = new SKFont
|
||||||
|
{
|
||||||
|
Size = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
// DrawText now takes the string, X, Y, the font, and the paint
|
||||||
|
//canvas.DrawText($"{currentTemp:F1}c", 4, 32, textFont, textPaint);
|
||||||
|
// DrawText now takes the string, X, Y, alignment, the font, and the paint
|
||||||
|
canvas.DrawText($"{currentTemp:F1}c", 4, 32, SKTextAlign.Left, textFont, textPaint);
|
||||||
|
|
||||||
|
// 4. Convert 32-bit RGBA to 24-bit RGB for WLED
|
||||||
|
byte[] rgbaBytes = bitmap.Bytes;
|
||||||
|
byte[] rgbBytes = new byte[_width * _height * 3];
|
||||||
|
|
||||||
|
int rgbIndex = 0;
|
||||||
|
for (int i = 0; i < rgbaBytes.Length; i += 4)
|
||||||
|
{
|
||||||
|
rgbBytes[rgbIndex++] = rgbaBytes[i]; // R
|
||||||
|
rgbBytes[rgbIndex++] = rgbaBytes[i + 1]; // G
|
||||||
|
rgbBytes[rgbIndex++] = rgbaBytes[i + 2]; // B
|
||||||
|
// We ignore rgbaBytes[i + 3] (Alpha channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. Fire it over UDP!
|
||||||
|
await _wled.SendFrameAsync(rgbBytes);
|
||||||
|
}
|
||||||
|
}
|
||||||
13
Program.cs
Normal file
13
Program.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
var renderer = new MatrixRenderer("192.168.0.150"); // Replace with WLED IP
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// TODO: Query InfluxDB for your BME680 data here
|
||||||
|
float bme680Temp = 22.5f;
|
||||||
|
|
||||||
|
await renderer.DrawAndSendFrameAsync(bme680Temp);
|
||||||
|
|
||||||
|
// Update screen at 30 FPS for smooth transitions,
|
||||||
|
// or every 5 seconds if just showing static data.
|
||||||
|
await Task.Delay(1000 / 30);
|
||||||
|
}
|
||||||
15
WLED_DDP_Client.csproj
Normal file
15
WLED_DDP_Client.csproj
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net10.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="SkiaSharp" Version="4.151.1" />
|
||||||
|
<PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.6" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
3
WLED_DDP_Client.slnx
Normal file
3
WLED_DDP_Client.slnx
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<Solution>
|
||||||
|
<Project Path="WLED_DDP_Client.csproj" />
|
||||||
|
</Solution>
|
||||||
68
WledDdpClient.cs
Normal file
68
WledDdpClient.cs
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
using System;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
public class WledDdpClient
|
||||||
|
{
|
||||||
|
private readonly UdpClient _udp;
|
||||||
|
private readonly string _ip;
|
||||||
|
private const int Port = 4048; // Standard DDP UDP port
|
||||||
|
private byte _sequence = 1;
|
||||||
|
|
||||||
|
public WledDdpClient(string ipAddress)
|
||||||
|
{
|
||||||
|
_ip = ipAddress;
|
||||||
|
_udp = new UdpClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SendFrameAsync(byte[] rgbData)
|
||||||
|
{
|
||||||
|
// Max payload per packet to stay under standard 1500 byte MTU
|
||||||
|
const int maxPixelsPerPacket = 480;
|
||||||
|
const int maxBytesPerPacket = maxPixelsPerPacket * 3;
|
||||||
|
|
||||||
|
int totalBytes = rgbData.Length;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
while (offset < totalBytes)
|
||||||
|
{
|
||||||
|
int chunkLength = Math.Min(maxBytesPerPacket, totalBytes - offset);
|
||||||
|
bool isLast = (offset + chunkLength) >= totalBytes;
|
||||||
|
|
||||||
|
byte[] packet = new byte[10 + chunkLength];
|
||||||
|
|
||||||
|
// Byte 0: Flags. 0x40 = Version 1. 0x01 = Push bit.
|
||||||
|
// We only "Push" to the display on the final chunk of the frame.
|
||||||
|
packet[0] = (byte)(isLast ? 0x41 : 0x40);
|
||||||
|
|
||||||
|
// Byte 1: Sequence (0-15)
|
||||||
|
packet[1] = _sequence;
|
||||||
|
|
||||||
|
// Byte 2: Data type (1 = RGB)
|
||||||
|
packet[2] = 1;
|
||||||
|
|
||||||
|
// Byte 3: Destination ID (Default 1)
|
||||||
|
packet[3] = 1;
|
||||||
|
|
||||||
|
// Bytes 4-7: Data Offset (32-bit Big Endian)
|
||||||
|
packet[4] = (byte)(offset >> 24);
|
||||||
|
packet[5] = (byte)(offset >> 16);
|
||||||
|
packet[6] = (byte)(offset >> 8);
|
||||||
|
packet[7] = (byte)(offset);
|
||||||
|
|
||||||
|
// Bytes 8-9: Data Length (16-bit Big Endian)
|
||||||
|
packet[8] = (byte)(chunkLength >> 8);
|
||||||
|
packet[9] = (byte)(chunkLength);
|
||||||
|
|
||||||
|
// Copy the pixel payload into the packet
|
||||||
|
Buffer.BlockCopy(rgbData, offset, packet, 10, chunkLength);
|
||||||
|
|
||||||
|
await _udp.SendAsync(packet, packet.Length, _ip, Port);
|
||||||
|
|
||||||
|
offset += chunkLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment sequence for the next frame (wrap at 15)
|
||||||
|
_sequence = (byte)((_sequence + 1) % 16);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user