Add project files.

This commit is contained in:
2026-08-07 16:05:32 +01:00
parent a9084c7305
commit 6fac7e2127
5 changed files with 159 additions and 0 deletions

60
MatrixRenderer.cs Normal file
View 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
View 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
View 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
View File

@@ -0,0 +1,3 @@
<Solution>
<Project Path="WLED_DDP_Client.csproj" />
</Solution>

68
WledDdpClient.cs Normal file
View 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);
}
}