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 <RP2040_PWM.h>
|
||||
#include <WiFi.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <WiFiManager.h>
|
||||
|
||||
// -------------------------------
|
||||
// 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;
|
||||
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
|
||||
// -------------------------------
|
||||
|
||||
float logicalPoints[5] = {0, 25, 50, 75, 100};
|
||||
|
||||
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};
|
||||
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 lastSerialTime = 0;
|
||||
unsigned long lastPacketTime = 0;
|
||||
const unsigned long watchdogTimeout = 5000; // 5 seconds
|
||||
unsigned long lastFadeTime = 0;
|
||||
|
||||
// -------------------------------
|
||||
// Calibration interpolation
|
||||
// -------------------------------
|
||||
|
||||
float applyCalibration(uint8_t ch, float logicalDuty) {
|
||||
if (logicalDuty <= 0.0f) return calibratedPoints[ch][0];
|
||||
if (logicalDuty >= 100.0f) return calibratedPoints[ch][4];
|
||||
@@ -71,105 +90,116 @@ float applyCalibration(uint8_t ch, float logicalDuty) {
|
||||
// -------------------------------
|
||||
// Setup
|
||||
// -------------------------------
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
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++) {
|
||||
pwm[ch] = new RP2040_PWM(pwmPins[ch], pwmFrequency, 0.0f);
|
||||
if (pwm[ch]) pwm[ch]->setPWM();
|
||||
bool ok = ledcAttach(pwmPins[ch], pwmFrequency, pwmResolution);
|
||||
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
|
||||
// -------------------------------
|
||||
|
||||
void loop() {
|
||||
|
||||
// -------- Serial parsing --------
|
||||
while (Serial.available()) {
|
||||
String s = Serial.readStringUntil('\n');
|
||||
s.trim();
|
||||
// -------- UDP parsing --------
|
||||
int packetSize = udp.parsePacket();
|
||||
if (packetSize > 0) {
|
||||
char buf[256];
|
||||
int len = udp.read(buf, sizeof(buf) - 1);
|
||||
buf[len] = '\0';
|
||||
|
||||
// --- Device identification command ---
|
||||
if (s.equalsIgnoreCase("PING")) {
|
||||
Serial.print("Analog_System_Monitor_");
|
||||
Serial.println(FIRMWARE_VERSION);
|
||||
lastSerialTime = millis();
|
||||
continue;
|
||||
float values[NUM_CHANNELS] = {0};
|
||||
int idx = 0;
|
||||
|
||||
char* token = strtok(buf, ",");
|
||||
while (token != nullptr && idx < NUM_CHANNELS) {
|
||||
values[idx] = atof(token);
|
||||
idx++;
|
||||
token = strtok(nullptr, ",");
|
||||
}
|
||||
|
||||
// --- Batch update command ---
|
||||
if (s.startsWith("SETALL:")) {
|
||||
String payload = s.substring(7);
|
||||
payload.trim();
|
||||
if (idx == NUM_CHANNELS) {
|
||||
|
||||
float newValues[NUM_CHANNELS];
|
||||
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
|
||||
// Start a new slew toward the target
|
||||
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||
currentDuty[ch] = newValues[ch];
|
||||
float calibratedDuty = applyCalibration(ch, currentDuty[ch]);
|
||||
pwm[ch]->setPWM(pwmPins[ch], pwmFrequency, calibratedDuty);
|
||||
targetDuty[ch] = values[ch];
|
||||
slewStartDuty[ch] = currentDuty[ch];
|
||||
}
|
||||
slewStartTime = millis();
|
||||
|
||||
Serial.println("OK");
|
||||
lastSerialTime = millis();
|
||||
continue;
|
||||
}
|
||||
lastPacketTime = millis();
|
||||
|
||||
// --- Unknown command ---
|
||||
if (s.length() > 0) {
|
||||
Serial.println("ERROR");
|
||||
// Debug output
|
||||
Serial.println("Received UDP packet:");
|
||||
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) --------
|
||||
if (millis() - lastSerialTime > watchdogTimeout) {
|
||||
// -------- Slew-rate limiting (smooth 1-second transitions) --------
|
||||
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;
|
||||
|
||||
@@ -187,7 +217,9 @@ void loop() {
|
||||
currentDuty[ch] = 0.0f;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user