From a1f746fce12a04d7c5e2995c2d655ae1fef0bcab Mon Sep 17 00:00:00 2001 From: parsons Date: Sat, 8 Aug 2026 01:26:33 +0100 Subject: [PATCH] Added clock --- MatrixRenderer.cs | 35 ++++++++++---------------- Program.cs | 5 +++- ntpService.cs | 64 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 23 deletions(-) create mode 100644 ntpService.cs diff --git a/MatrixRenderer.cs b/MatrixRenderer.cs index b339e22..20d2f00 100644 --- a/MatrixRenderer.cs +++ b/MatrixRenderer.cs @@ -13,36 +13,27 @@ public class MatrixRenderer } // - public async Task DrawAndSendFrameAsync(float currentTemp, int currentWater) + public async Task DrawAndSendFrameAsync(float currentTemp, int currentWater, DateTime currentTime) //public async Task DrawAndSendFrameAsync(string 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) + // --- DRAW CLOCK --- + using SKPaint clockPaint = new SKPaint { Color = SKColors.Cyan, IsAntialias = false }; + using SKTypeface typeface = SKTypeface.FromFamilyName("monospace"); + using SKFont clockFont = new SKFont(typeface, 16); + string timeFormat = (currentTime.Second % 2 == 0) ? "HH:mm" : "HH mm"; + string timeString = currentTime.ToString(timeFormat); + canvas.DrawText(timeString, 4, 16, SKTextAlign.Left, clockFont, clockPaint); - // 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, 12, SKTextAlign.Left, textFont, textPaint); - canvas.DrawText($"{currentWater:F1}%", 4, 32, SKTextAlign.Left, textFont, textPaint); + // --- DRAW TEMPERATURE --- + using SKPaint textPaint = new SKPaint { Color = SKColors.Orange, IsAntialias = false }; + using SKFont textFont = new SKFont { Size = 16 }; + canvas.DrawText($"{currentTemp:F1}c", 4, 30, SKTextAlign.Left, textFont, textPaint); + canvas.DrawText($"{currentWater:F1}%", 4, 50, SKTextAlign.Left, textFont, textPaint); // 4. Convert 32-bit RGBA to 24-bit RGB for WLED byte[] rgbaBytes = bitmap.Bytes; diff --git a/Program.cs b/Program.cs index 39d9bd6..b7154df 100644 --- a/Program.cs +++ b/Program.cs @@ -3,6 +3,7 @@ using System.Diagnostics; using System.Threading.Tasks; var renderer = new MatrixRenderer("192.168.0.150"); // Your WLED IP +var ntp = new NtpService("ntp.parsons.cc"); // Replace these with your actual InfluxDB v2 credentials // If Influx is on the same Pi, you can use the Pi's local IP and port 8086 @@ -31,8 +32,10 @@ while (true) dbTimer.Restart(); } + DateTime now = ntp.GetCurrentTime(); + // Keep pushing frames to WLED at 30 FPS - await renderer.DrawAndSendFrameAsync(currentTemp, currentWaterLevel); + await renderer.DrawAndSendFrameAsync(currentTemp, currentWaterLevel, now); await Task.Delay(1000 / 30); diff --git a/ntpService.cs b/ntpService.cs new file mode 100644 index 0000000..03d2d5e --- /dev/null +++ b/ntpService.cs @@ -0,0 +1,64 @@ +using System; +using System.Net; +using System.Net.Sockets; + +public class NtpService +{ + private readonly string _ntpServer; + private TimeSpan _timeOffset = TimeSpan.Zero; + private DateTime _lastSync = DateTime.MinValue; + + public NtpService(string ntpServer) + { + _ntpServer = ntpServer; + } + + public DateTime GetCurrentTime() + { + // Sync with your NTP server every 1 hour + if ((DateTime.UtcNow - _lastSync).TotalHours > 1) + { + SyncWithNtp(); + } + + // Apply the offset so the clock advances smoothly between syncs + return DateTime.Now.Add(_timeOffset); + } + + private void SyncWithNtp() + { + try + { + var ntpData = new byte[48]; + ntpData[0] = 0x1B; // NTP request header (Client mode, Version 3) + + var addresses = Dns.GetHostEntry(_ntpServer).AddressList; + var ipEndPoint = new IPEndPoint(addresses[0], 123); + + using (var socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) + { + socket.ReceiveTimeout = 3000; + socket.Connect(ipEndPoint); + socket.Send(ntpData); + socket.Receive(ntpData); + } + + // Parse the 64-bit timestamp from the NTP response + ulong intPart = (ulong)ntpData[40] << 24 | (ulong)ntpData[41] << 16 | (ulong)ntpData[42] << 8 | (ulong)ntpData[43]; + ulong fractPart = (ulong)ntpData[44] << 24 | (ulong)ntpData[45] << 16 | (ulong)ntpData[46] << 8 | (ulong)ntpData[47]; + + var milliseconds = (intPart * 1000) + ((fractPart * 1000) / 0x100000000L); + var networkTime = new DateTime(1900, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddMilliseconds((long)milliseconds); + + // Calculate the difference between Pi's local UTC and NTP UTC + _timeOffset = networkTime - DateTime.UtcNow; + _lastSync = DateTime.UtcNow; + + Console.WriteLine($"NTP Sync to {_ntpServer} successful. Offset: {_timeOffset.TotalMilliseconds:F1}ms"); + } + catch (Exception ex) + { + Console.WriteLine($"NTP Sync failed: {ex.Message}"); + } + } +} \ No newline at end of file