switched back to ESP32 and UDP, but without OSC, because serial under windows is a bitch
This commit is contained in:
@@ -1,24 +1,42 @@
|
|||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <RP2040_PWM.h>
|
#include <WiFi.h>
|
||||||
|
#include <WiFiUdp.h>
|
||||||
|
#include <WiFiManager.h>
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Firmware version
|
// Firmware version
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
const char* FIRMWARE_VERSION = "V1.0";
|
const char* FIRMWARE_VERSION = "V2.4_UDP_LEDC_WM_SLEW";
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// PWM setup
|
// UDP
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
|
WiFiUDP udp;
|
||||||
|
const int listenPort = 12345; // Must match PC config.json
|
||||||
|
|
||||||
|
// -------------------------------
|
||||||
|
// PWM setup (LEDC, ESP32 Core 3.x)
|
||||||
|
// -------------------------------
|
||||||
const uint8_t NUM_CHANNELS = 8;
|
const uint8_t NUM_CHANNELS = 8;
|
||||||
uint8_t pwmPins[NUM_CHANNELS] = {14, 15, 26, 27, 8, 7, 6, 5};
|
|
||||||
|
|
||||||
const uint32_t pwmFrequency = 10000;
|
uint8_t pwmPins[NUM_CHANNELS] = {
|
||||||
|
26, // D0
|
||||||
|
22, // D1
|
||||||
|
21, // D2
|
||||||
|
17, // D3
|
||||||
|
16, // D4
|
||||||
|
5, // D5
|
||||||
|
18, // D6
|
||||||
|
19 // D7
|
||||||
|
// 23 (D8) remains as a spare
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint32_t pwmFrequency = 25000; // 25 kHz
|
||||||
|
const uint8_t pwmResolution = 10; // 10-bit resolution (0–1023)
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Calibration tables
|
// Calibration tables
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
|
|
||||||
float logicalPoints[5] = {0, 25, 50, 75, 100};
|
float logicalPoints[5] = {0, 25, 50, 75, 100};
|
||||||
|
|
||||||
float calibratedPoints[NUM_CHANNELS][5] = {
|
float calibratedPoints[NUM_CHANNELS][5] = {
|
||||||
@@ -33,24 +51,25 @@ float calibratedPoints[NUM_CHANNELS][5] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Duty tracking
|
// Duty tracking + Slew system
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
|
|
||||||
float currentDuty[NUM_CHANNELS] = {0.0f};
|
float currentDuty[NUM_CHANNELS] = {0.0f};
|
||||||
RP2040_PWM* pwm[NUM_CHANNELS];
|
float targetDuty[NUM_CHANNELS] = {0.0f};
|
||||||
|
float slewStartDuty[NUM_CHANNELS] = {0.0f};
|
||||||
|
|
||||||
|
unsigned long slewStartTime = 0;
|
||||||
|
const unsigned long slewDuration = 1000; // 1 second smooth transition
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Watchdog
|
// Watchdog (UDP-based)
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
|
unsigned long lastPacketTime = 0;
|
||||||
unsigned long lastSerialTime = 0;
|
|
||||||
const unsigned long watchdogTimeout = 5000; // 5 seconds
|
const unsigned long watchdogTimeout = 5000; // 5 seconds
|
||||||
unsigned long lastFadeTime = 0;
|
unsigned long lastFadeTime = 0;
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Calibration interpolation
|
// Calibration interpolation
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
|
|
||||||
float applyCalibration(uint8_t ch, float logicalDuty) {
|
float applyCalibration(uint8_t ch, float logicalDuty) {
|
||||||
if (logicalDuty <= 0.0f) return calibratedPoints[ch][0];
|
if (logicalDuty <= 0.0f) return calibratedPoints[ch][0];
|
||||||
if (logicalDuty >= 100.0f) return calibratedPoints[ch][4];
|
if (logicalDuty >= 100.0f) return calibratedPoints[ch][4];
|
||||||
@@ -71,105 +90,116 @@ float applyCalibration(uint8_t ch, float logicalDuty) {
|
|||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Setup
|
// Setup
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
delay(300);
|
delay(300);
|
||||||
|
|
||||||
|
Serial.println("Booting Analog System Monitor (UDP + LEDC + WiFiManager + Slew)");
|
||||||
|
Serial.print("Firmware: ");
|
||||||
|
Serial.println(FIRMWARE_VERSION);
|
||||||
|
|
||||||
|
// LEDC PWM init (ESP32 Core 3.x API)
|
||||||
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
pwm[ch] = new RP2040_PWM(pwmPins[ch], pwmFrequency, 0.0f);
|
bool ok = ledcAttach(pwmPins[ch], pwmFrequency, pwmResolution);
|
||||||
if (pwm[ch]) pwm[ch]->setPWM();
|
if (!ok) {
|
||||||
|
Serial.print("LEDC attach failed on pin ");
|
||||||
|
Serial.println(pwmPins[ch]);
|
||||||
|
}
|
||||||
|
ledcWrite(pwmPins[ch], 0); // duty = 0%
|
||||||
}
|
}
|
||||||
|
|
||||||
lastSerialTime = millis();
|
// -------------------------------
|
||||||
|
// WiFi Manager (Captive Portal)
|
||||||
|
// -------------------------------
|
||||||
|
WiFiManager wm;
|
||||||
|
|
||||||
|
wm.setHostname("AnalogMonitor");
|
||||||
|
wm.setTimeout(180); // 3 minutes before giving up
|
||||||
|
|
||||||
|
Serial.println("Starting WiFiManager...");
|
||||||
|
|
||||||
|
bool res = wm.autoConnect("AnalogMonitor-Setup");
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
Serial.println("WiFi failed or timed out. Rebooting...");
|
||||||
|
delay(2000);
|
||||||
|
ESP.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
Serial.println("WiFi connected!");
|
||||||
|
Serial.print("IP: ");
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
|
// UDP init
|
||||||
|
udp.begin(listenPort);
|
||||||
|
Serial.print("Listening on UDP port ");
|
||||||
|
Serial.println(listenPort);
|
||||||
|
|
||||||
|
lastPacketTime = millis();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// Loop
|
// Loop
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
|
||||||
// -------- Serial parsing --------
|
// -------- UDP parsing --------
|
||||||
while (Serial.available()) {
|
int packetSize = udp.parsePacket();
|
||||||
String s = Serial.readStringUntil('\n');
|
if (packetSize > 0) {
|
||||||
s.trim();
|
char buf[256];
|
||||||
|
int len = udp.read(buf, sizeof(buf) - 1);
|
||||||
|
buf[len] = '\0';
|
||||||
|
|
||||||
// --- Device identification command ---
|
float values[NUM_CHANNELS] = {0};
|
||||||
if (s.equalsIgnoreCase("PING")) {
|
int idx = 0;
|
||||||
Serial.print("Analog_System_Monitor_");
|
|
||||||
Serial.println(FIRMWARE_VERSION);
|
char* token = strtok(buf, ",");
|
||||||
lastSerialTime = millis();
|
while (token != nullptr && idx < NUM_CHANNELS) {
|
||||||
continue;
|
values[idx] = atof(token);
|
||||||
|
idx++;
|
||||||
|
token = strtok(nullptr, ",");
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Batch update command ---
|
if (idx == NUM_CHANNELS) {
|
||||||
if (s.startsWith("SETALL:")) {
|
|
||||||
String payload = s.substring(7);
|
|
||||||
payload.trim();
|
|
||||||
|
|
||||||
float newValues[NUM_CHANNELS];
|
// Start a new slew toward the target
|
||||||
int count = 0;
|
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
// Parse up to NUM_CHANNELS values
|
|
||||||
while (payload.length() > 0 && count < NUM_CHANNELS) {
|
|
||||||
int comma = payload.indexOf(',');
|
|
||||||
String part;
|
|
||||||
|
|
||||||
if (comma >= 0) {
|
|
||||||
part = payload.substring(0, comma);
|
|
||||||
payload = payload.substring(comma + 1);
|
|
||||||
} else {
|
|
||||||
part = payload;
|
|
||||||
payload = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
part.trim();
|
|
||||||
if (part.length() == 0) {
|
|
||||||
error = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
float val = part.toFloat();
|
|
||||||
|
|
||||||
if (val < 0.0f || val > 100.0f) {
|
|
||||||
error = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
newValues[count++] = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Conditions for a valid SETALL:
|
|
||||||
// - exactly NUM_CHANNELS values parsed
|
|
||||||
// - no leftover payload
|
|
||||||
// - no parse/range errors
|
|
||||||
if (error || count != NUM_CHANNELS || payload.length() > 0) {
|
|
||||||
Serial.println("ERROR");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// All good → apply values
|
|
||||||
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
currentDuty[ch] = newValues[ch];
|
targetDuty[ch] = values[ch];
|
||||||
float calibratedDuty = applyCalibration(ch, currentDuty[ch]);
|
slewStartDuty[ch] = currentDuty[ch];
|
||||||
pwm[ch]->setPWM(pwmPins[ch], pwmFrequency, calibratedDuty);
|
|
||||||
}
|
}
|
||||||
|
slewStartTime = millis();
|
||||||
|
|
||||||
Serial.println("OK");
|
lastPacketTime = millis();
|
||||||
lastSerialTime = millis();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Unknown command ---
|
// Debug output
|
||||||
if (s.length() > 0) {
|
Serial.println("Received UDP packet:");
|
||||||
Serial.println("ERROR");
|
for (int i = 0; i < NUM_CHANNELS; i++) {
|
||||||
|
Serial.print(" CH");
|
||||||
|
Serial.print(i);
|
||||||
|
Serial.print(": ");
|
||||||
|
Serial.println(values[i]);
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- Watchdog fade-to-zero (time-based exponential) --------
|
// -------- Slew-rate limiting (smooth 1-second transitions) --------
|
||||||
if (millis() - lastSerialTime > watchdogTimeout) {
|
unsigned long now = millis();
|
||||||
|
float progress = (float)(now - slewStartTime) / (float)slewDuration;
|
||||||
|
if (progress > 1.0f) progress = 1.0f;
|
||||||
|
|
||||||
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
|
float newDuty = slewStartDuty[ch] + (targetDuty[ch] - slewStartDuty[ch]) * progress;
|
||||||
|
currentDuty[ch] = newDuty;
|
||||||
|
|
||||||
|
float calibratedDuty = applyCalibration(ch, newDuty);
|
||||||
|
int duty = (int)((calibratedDuty / 100.0f) * ((1 << pwmResolution) - 1));
|
||||||
|
|
||||||
|
ledcWrite(pwmPins[ch], duty);
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- Watchdog fade-to-zero (UDP-based) --------
|
||||||
|
if (millis() - lastPacketTime > watchdogTimeout) {
|
||||||
|
|
||||||
const unsigned long fadeInterval = 1;
|
const unsigned long fadeInterval = 1;
|
||||||
|
|
||||||
@@ -187,7 +217,9 @@ void loop() {
|
|||||||
currentDuty[ch] = 0.0f;
|
currentDuty[ch] = 0.0f;
|
||||||
|
|
||||||
float calibratedDuty = applyCalibration(ch, currentDuty[ch]);
|
float calibratedDuty = applyCalibration(ch, currentDuty[ch]);
|
||||||
pwm[ch]->setPWM(pwmPins[ch], pwmFrequency, calibratedDuty);
|
int duty = (int)((calibratedDuty / 100.0f) * ((1 << pwmResolution) - 1));
|
||||||
|
|
||||||
|
ledcWrite(pwmPins[ch], duty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
#nullable enable
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.IO.Ports;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
public class SerialManager : IDisposable
|
|
||||||
{
|
|
||||||
private SerialPort? port;
|
|
||||||
private DateTime lastResponse = DateTime.UtcNow;
|
|
||||||
|
|
||||||
private const int BaudRate = 115200;
|
|
||||||
private const int PingTimeoutMs = 300;
|
|
||||||
private const int WatchdogTimeoutMs = 3000;
|
|
||||||
|
|
||||||
public bool IsConnected => port != null && port.IsOpen;
|
|
||||||
|
|
||||||
// ---------------- DEVICE DISCOVERY ----------------
|
|
||||||
|
|
||||||
public void DiscoverDevice()
|
|
||||||
{
|
|
||||||
DisposePort();
|
|
||||||
|
|
||||||
foreach (string com in SerialPort.GetPortNames().OrderBy(s => s))
|
|
||||||
{
|
|
||||||
string? response = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
using (var testPort = CreatePort(com, PingTimeoutMs))
|
|
||||||
{
|
|
||||||
testPort.Open();
|
|
||||||
Thread.Sleep(350); // RP2040 resets on open
|
|
||||||
|
|
||||||
testPort.DiscardInBuffer();
|
|
||||||
testPort.DiscardOutBuffer();
|
|
||||||
|
|
||||||
testPort.Write("PING\n");
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (testPort.BytesToRead > 0)
|
|
||||||
response = testPort.ReadLine();
|
|
||||||
}
|
|
||||||
catch (TimeoutException)
|
|
||||||
{
|
|
||||||
// No response — move to next port
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response != null &&
|
|
||||||
response.StartsWith("Analog_System_Monitor_"))
|
|
||||||
{
|
|
||||||
port = CreatePort(com, 500);
|
|
||||||
port.Open();
|
|
||||||
Thread.Sleep(350); // allow RP2040 reboot again
|
|
||||||
|
|
||||||
lastResponse = DateTime.UtcNow;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Silent fail — move to next COM port
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
port = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------- PORT FACTORY ----------------
|
|
||||||
|
|
||||||
private SerialPort CreatePort(string com, int timeout)
|
|
||||||
{
|
|
||||||
return new SerialPort(com, BaudRate)
|
|
||||||
{
|
|
||||||
Parity = Parity.None,
|
|
||||||
DataBits = 8,
|
|
||||||
StopBits = StopBits.One,
|
|
||||||
Handshake = Handshake.None,
|
|
||||||
|
|
||||||
NewLine = "\n",
|
|
||||||
Encoding = System.Text.Encoding.ASCII,
|
|
||||||
|
|
||||||
ReadTimeout = timeout,
|
|
||||||
WriteTimeout = timeout,
|
|
||||||
|
|
||||||
DtrEnable = true, // REQUIRED for RP2040 USB CDC
|
|
||||||
RtsEnable = true
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------- WATCHDOG ----------------
|
|
||||||
|
|
||||||
public bool WatchdogExpired =>
|
|
||||||
(DateTime.UtcNow - lastResponse).TotalMilliseconds > WatchdogTimeoutMs;
|
|
||||||
|
|
||||||
// ---------------- SEND COMMAND ----------------
|
|
||||||
|
|
||||||
public void SendSetAll(string cmd)
|
|
||||||
{
|
|
||||||
if (!IsConnected)
|
|
||||||
return;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
port!.Write(cmd + "\n");
|
|
||||||
|
|
||||||
string? response = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (port.BytesToRead > 0)
|
|
||||||
response = port.ReadLine();
|
|
||||||
}
|
|
||||||
catch (TimeoutException)
|
|
||||||
{
|
|
||||||
// No response this tick — totally fine
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response != null &&
|
|
||||||
(response.StartsWith("OK") || response.StartsWith("ERROR")))
|
|
||||||
{
|
|
||||||
lastResponse = DateTime.UtcNow;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Silent fail — watchdog will trigger reconnect
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ---------------- CLEANUP ----------------
|
|
||||||
|
|
||||||
private void DisposePort()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (port != null)
|
|
||||||
{
|
|
||||||
port.Close();
|
|
||||||
port.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
port = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
DisposePort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,15 +9,13 @@ public class Telemetry : IDisposable
|
|||||||
private const int UpdateRateDefaultMs = 1000;
|
private const int UpdateRateDefaultMs = 1000;
|
||||||
public int UpdateRateMs => UpdateRateDefaultMs;
|
public int UpdateRateMs => UpdateRateDefaultMs;
|
||||||
|
|
||||||
private readonly SerialManager serial = new SerialManager();
|
private readonly UdpSender udp = new UdpSender();
|
||||||
private readonly Computer computer = new Computer();
|
private readonly Computer computer = new Computer();
|
||||||
|
|
||||||
// Cached hardware references
|
|
||||||
private IHardware? cpuHw;
|
private IHardware? cpuHw;
|
||||||
private IHardware? gpuHw;
|
private IHardware? gpuHw;
|
||||||
private IHardware? memHw;
|
private IHardware? memHw;
|
||||||
|
|
||||||
// Cached sensors
|
|
||||||
private ISensor[] cpuLoadSensors = Array.Empty<ISensor>();
|
private ISensor[] cpuLoadSensors = Array.Empty<ISensor>();
|
||||||
private ISensor? cpuTempSensor;
|
private ISensor? cpuTempSensor;
|
||||||
|
|
||||||
@@ -33,22 +31,11 @@ public class Telemetry : IDisposable
|
|||||||
|
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
serial.DiscoverDevice();
|
|
||||||
|
|
||||||
// Enable only what we need
|
|
||||||
computer.IsCpuEnabled = true;
|
computer.IsCpuEnabled = true;
|
||||||
computer.IsGpuEnabled = true;
|
computer.IsGpuEnabled = true;
|
||||||
computer.IsMemoryEnabled = true;
|
computer.IsMemoryEnabled = true;
|
||||||
|
|
||||||
computer.IsMotherboardEnabled = false;
|
|
||||||
computer.IsControllerEnabled = false;
|
|
||||||
computer.IsNetworkEnabled = false;
|
|
||||||
computer.IsStorageEnabled = false;
|
|
||||||
computer.IsBatteryEnabled = false;
|
|
||||||
computer.IsPsuEnabled = false;
|
|
||||||
|
|
||||||
computer.Open();
|
computer.Open();
|
||||||
|
|
||||||
CacheHardwareAndSensors();
|
CacheHardwareAndSensors();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,23 +132,8 @@ public class Telemetry : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static string F(float v) => v.ToString("0.###", CI);
|
|
||||||
|
|
||||||
public void UpdateAndSend()
|
public void UpdateAndSend()
|
||||||
{
|
{
|
||||||
if (!serial.IsConnected)
|
|
||||||
{
|
|
||||||
serial.DiscoverDevice();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (serial.WatchdogExpired)
|
|
||||||
{
|
|
||||||
serial.DiscoverDevice();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update only the hardware we need
|
|
||||||
cpuHw?.Update();
|
cpuHw?.Update();
|
||||||
gpuHw?.Update();
|
gpuHw?.Update();
|
||||||
memHw?.Update();
|
memHw?.Update();
|
||||||
@@ -173,14 +145,22 @@ public class Telemetry : IDisposable
|
|||||||
float gpuTemp = GetGpuTemperaturePercent();
|
float gpuTemp = GetGpuTemperaturePercent();
|
||||||
float vram = GetGpuVramPercent();
|
float vram = GetGpuVramPercent();
|
||||||
|
|
||||||
string cmd =
|
// Prepare 8 floats (future‑proof)
|
||||||
$"SETALL: {F(cpu)},{F(cpuTemp)},{F(mem)},{F(gpu3d)},{F(gpuTemp)},{F(vram)},0,0";
|
float[] packet =
|
||||||
|
{
|
||||||
|
cpu,
|
||||||
|
cpuTemp,
|
||||||
|
mem,
|
||||||
|
gpu3d,
|
||||||
|
gpuTemp,
|
||||||
|
vram,
|
||||||
|
0f, // reserved for future use
|
||||||
|
0f // reserved for future use
|
||||||
|
};
|
||||||
|
|
||||||
serial.SendSetAll(cmd);
|
udp.SendFloats(packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------- METRICS ----------------
|
|
||||||
|
|
||||||
private float GetCpuLoadPercent()
|
private float GetCpuLoadPercent()
|
||||||
{
|
{
|
||||||
if (cpuLoadSensors.Length == 0) return 0;
|
if (cpuLoadSensors.Length == 0) return 0;
|
||||||
@@ -236,7 +216,7 @@ public class Telemetry : IDisposable
|
|||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
serial.Dispose();
|
udp.Dispose();
|
||||||
computer.Close();
|
computer.Close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,14 +18,13 @@ public class TrayApp : ApplicationContext
|
|||||||
{
|
{
|
||||||
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath),
|
Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath),
|
||||||
Visible = true,
|
Visible = true,
|
||||||
Text = "Telemetry Running"
|
Text = "Telemetry Running (UDP)"
|
||||||
};
|
};
|
||||||
|
|
||||||
var menu = new ContextMenuStrip();
|
var menu = new ContextMenuStrip();
|
||||||
menu.Items.Add("Exit", null, OnExit);
|
menu.Items.Add("Exit", null, OnExit);
|
||||||
trayIcon.ContextMenuStrip = menu;
|
trayIcon.ContextMenuStrip = menu;
|
||||||
|
|
||||||
// Start telemetry loop
|
|
||||||
var timer = new System.Windows.Forms.Timer();
|
var timer = new System.Windows.Forms.Timer();
|
||||||
timer.Interval = 1000;
|
timer.Interval = 1000;
|
||||||
timer.Tick += (s, e) => telemetry.UpdateAndSend();
|
timer.Tick += (s, e) => telemetry.UpdateAndSend();
|
||||||
|
|||||||
64
analog_system_monitor_dotnet/UdpSender.cs
Normal file
64
analog_system_monitor_dotnet/UdpSender.cs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#nullable enable
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
public class UdpSender : IDisposable
|
||||||
|
{
|
||||||
|
private readonly UdpClient client = new UdpClient();
|
||||||
|
private IPEndPoint endpoint;
|
||||||
|
|
||||||
|
private const string DefaultIp = "192.168.1.50";
|
||||||
|
private const int DefaultPort = 12345;
|
||||||
|
|
||||||
|
public UdpSender()
|
||||||
|
{
|
||||||
|
string exeDir = AppContext.BaseDirectory;
|
||||||
|
string cfgPath = Path.Combine(exeDir, "config.json");
|
||||||
|
|
||||||
|
// Create default config if missing
|
||||||
|
if (!File.Exists(cfgPath))
|
||||||
|
{
|
||||||
|
var defaultCfg = new UdpConfig
|
||||||
|
{
|
||||||
|
esp32_ip = DefaultIp,
|
||||||
|
esp32_port = DefaultPort
|
||||||
|
};
|
||||||
|
|
||||||
|
string json = JsonSerializer.Serialize(
|
||||||
|
defaultCfg,
|
||||||
|
new JsonSerializerOptions { WriteIndented = true }
|
||||||
|
);
|
||||||
|
|
||||||
|
File.WriteAllText(cfgPath, json);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config
|
||||||
|
var jsonText = File.ReadAllText(cfgPath);
|
||||||
|
var cfg = JsonSerializer.Deserialize<UdpConfig>(jsonText)
|
||||||
|
?? throw new Exception("Invalid config.json");
|
||||||
|
|
||||||
|
endpoint = new IPEndPoint(IPAddress.Parse(cfg.esp32_ip), cfg.esp32_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendFloats(float[] values)
|
||||||
|
{
|
||||||
|
string packet = string.Join(",", values);
|
||||||
|
byte[] data = System.Text.Encoding.ASCII.GetBytes(packet);
|
||||||
|
client.Send(data, data.Length, endpoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
client.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UdpConfig
|
||||||
|
{
|
||||||
|
public string esp32_ip { get; set; } = DefaultIp;
|
||||||
|
public int esp32_port { get; set; } = DefaultPort;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,8 +12,8 @@
|
|||||||
<!-- No trimming (LHM + reflection will break) -->
|
<!-- No trimming (LHM + reflection will break) -->
|
||||||
<PublishTrimmed>false</PublishTrimmed>
|
<PublishTrimmed>false</PublishTrimmed>
|
||||||
|
|
||||||
<!-- No compression (avoids startup delays + debugging issues) -->
|
<EnableCompressionInSingleFile>true</EnableCompressionInSingleFile>
|
||||||
<EnableCompressionInSingleFile>false</EnableCompressionInSingleFile>
|
<InvariantGlobalization>true</InvariantGlobalization>
|
||||||
|
|
||||||
<!-- Keep debugging symbols optional -->
|
<!-- Keep debugging symbols optional -->
|
||||||
<DebugType>none</DebugType>
|
<DebugType>none</DebugType>
|
||||||
|
|||||||
Reference in New Issue
Block a user