added first iteration of a calibration menu to the webui

This commit is contained in:
2026-01-19 17:53:01 +01:00
parent e634563cdd
commit 49daa09aa6

View File

@@ -75,6 +75,20 @@ const char* channelUnits[NUM_CHANNELS] = {
"" // Reserved "" // Reserved
}; };
// -------------------------------
// Channel labels ESPUI
// -------------------------------
const char* channelDropdownLabels[NUM_CHANNELS] = {
"CH0 (CPU Load)",
"CH1 (CPU Temp)",
"CH2 (RAM Usage)",
"CH3 (GPU Load)",
"CH4 (GPU Temp)",
"CH5 (VRAM Usage)",
"CH6 (Reserved 6)",
"CH7 (Reserved 7)"
};
// ------------------------------- // -------------------------------
// Calibration tables // Calibration tables
// ------------------------------- // -------------------------------
@@ -113,6 +127,20 @@ unsigned long lastFadeTime = 0;
// ------------------------------- // -------------------------------
uint16_t portInput; // ID of the UDP port Number control uint16_t portInput; // ID of the UDP port Number control
// --- Calibration UI Controls ---
uint16_t calChannelDropdown;
uint16_t calLabel;
uint16_t calInputs[5];
uint16_t calTestValueInput;
uint16_t calTestButton;
uint16_t calSaveButton;
uint16_t calReleaseButton;
uint16_t calOverrideSwitch;
uint8_t selectedCalChannel = 0;
bool overrideActive[NUM_CHANNELS] = {false};
// ------------------------------- // -------------------------------
// Calibration interpolation // Calibration interpolation
// ------------------------------- // -------------------------------
@@ -133,6 +161,112 @@ float applyCalibration(uint8_t ch, float logicalDuty) {
return calibratedPoints[ch][4]; return calibratedPoints[ch][4];
} }
// -------------------------------
// Calibration UI helpers & callbacks
// -------------------------------
void refreshCalibrationUI() {
String label = "CH" + String(selectedCalChannel) + " (" + channelLabels[selectedCalChannel] + ")";
ESPUI.updateControlValue(calLabel, label);
for (int i = 0; i < 5; i++) {
ESPUI.updateControlValue(calInputs[i], String(calibratedPoints[selectedCalChannel][i], 2));
}
}
void calChannelCallback(Control *sender, int type) {
// Turn override OFF when switching channels
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
overrideActive[ch] = false;
}
ESPUI.updateControlValue(calOverrideSwitch, "0");
selectedCalChannel = sender->value.toInt();
Serial.print("Calibration channel changed to ");
Serial.println(selectedCalChannel);
refreshCalibrationUI();
}
void calPointCallback(Control *sender, int type) {
int index = sender->id - calInputs[0];
if (index >= 0 && index < 5) {
float val = sender->value.toFloat();
calibratedPoints[selectedCalChannel][index] = val;
Serial.printf("Cal[%d][%d] = %.2f\n", selectedCalChannel, index, val);
}
}
void calTestCallback(Control *sender, int type) {
if (!overrideActive[selectedCalChannel]) return;
float logical = sender->value.toFloat(); // 0100 integer
if (logical < 0) logical = 0;
if (logical > 100) logical = 100;
float calibrated = applyCalibration(selectedCalChannel, logical);
int duty = (int)((calibrated / 100.0f) * ((1 << pwmResolution) - 1));
ledcWrite(pwmPins[selectedCalChannel], duty);
Serial.printf("Override update CH%d: logical=%.2f calibrated=%.2f duty=%d\n",
selectedCalChannel, logical, calibrated, duty);
}
void calSaveCallback(Control *sender, int type) {
Serial.printf("Saving calibration for CH%d...\n", selectedCalChannel);
for (int i = 0; i < 5; i++) {
String key = "cal_" + String(selectedCalChannel) + "_" + String(i);
prefs.putFloat(key.c_str(), calibratedPoints[selectedCalChannel][i]);
}
Serial.println("Calibration saved.");
}
void calReleaseOverrideCallback(Control *sender, int type) {
Serial.println("Manual override released for all channels.");
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
overrideActive[ch] = false;
}
}
void calOverrideSwitchCallback(Control *sender, int type) {
bool enabled = sender->value.toInt() == 1;
if (enabled) {
Serial.println("Override enabled.");
// Enable override only for the selected channel
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
overrideActive[ch] = (ch == selectedCalChannel);
}
// Immediately apply the test value
float logical = ESPUI.getControl(calTestValueInput)->value.toFloat();
if (logical < 0) logical = 0;
if (logical > 100) logical = 100;
float calibrated = applyCalibration(selectedCalChannel, logical);
int duty = (int)((calibrated / 100.0f) * ((1 << pwmResolution) - 1));
ledcWrite(pwmPins[selectedCalChannel], duty);
Serial.printf("Override driving CH%d: logical=%.2f calibrated=%.2f duty=%d\n",
selectedCalChannel, logical, calibrated, duty);
} else {
Serial.println("Override disabled. Returning to UDP control.");
// Disable all overrides
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
overrideActive[ch] = false;
}
}
}
// ------------------------------- // -------------------------------
// Setup // Setup
// ------------------------------- // -------------------------------
@@ -176,6 +310,15 @@ void setup() {
prefs.begin("analogmon", false); prefs.begin("analogmon", false);
udpPort = prefs.getInt("udpPort", listenPort); udpPort = prefs.getInt("udpPort", listenPort);
// Load calibration from flash (if present)
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
for (int i = 0; i < 5; i++) {
String key = "cal_" + String(ch) + "_" + String(i);
float val = prefs.getFloat(key.c_str(), calibratedPoints[ch][i]);
calibratedPoints[ch][i] = val;
}
}
// Start UDP with runtime port // Start UDP with runtime port
udp.begin(udpPort); udp.begin(udpPort);
Serial.print("Listening on UDP port "); Serial.print("Listening on UDP port ");
@@ -280,71 +423,103 @@ void setup() {
tabLighting tabLighting
); );
// -------------------------------
// Calibration tab UI
// -------------------------------
// Channel selector
calChannelDropdown = ESPUI.addControl(
ControlType::Select,
"Selected Channel",
"0",
ControlColor::Peterriver,
tabCalibration,
calChannelCallback
);
// Add options 07
for (int i = 0; i < NUM_CHANNELS; i++) {
char value[8];
snprintf(value, sizeof(value), "%d", i);
ESPUI.addControl(
ControlType::Option,
channelDropdownLabels[i], // static label
value, // static-ish value (OK)
ControlColor::None,
calChannelDropdown
);
}
ESPUI.addControl( ESPUI.addControl(
ControlType::Separator, ControlType::Separator,
"CH0 (CPU Load)", "",
"", "",
ControlColor::None, ControlColor::None,
tabCalibration tabCalibration
); );
// Calibration inputs
const char* calNames[5] = {"0%", "25%", "50%", "75%", "100%"};
for (int i = 0; i < 5; i++) {
calInputs[i] = ESPUI.addControl(
ControlType::Number,
calNames[i],
String(calibratedPoints[0][i], 2),
ControlColor::Wetasphalt,
tabCalibration,
calPointCallback
);
}
ESPUI.addControl( ESPUI.addControl(
ControlType::Separator, ControlType::Separator,
"CH1 (CPU Temp)", "",
"", "",
ControlColor::None, ControlColor::None,
tabCalibration tabCalibration
); );
// Test value input
calTestValueInput = ESPUI.addControl(
ControlType::Slider,
"Test Value",
"50",
ControlColor::Carrot,
tabCalibration,
calTestCallback
);
calOverrideSwitch = ESPUI.addControl(
ControlType::Switcher,
"Override",
"0", // default OFF
ControlColor::Alizarin,
tabCalibration,
calOverrideSwitchCallback
);
ESPUI.addControl( ESPUI.addControl(
ControlType::Separator, ControlType::Separator,
"CH2 (RAM Usage)", "",
"", "",
ControlColor::None, ControlColor::None,
tabCalibration tabCalibration
); );
ESPUI.addControl( // Save button
ControlType::Separator, calSaveButton = ESPUI.addControl(
"CH3 (GPU Load)", ControlType::Button,
"", "Save Calibration",
ControlColor::None, "Save",
tabCalibration ControlColor::Emerald,
); tabCalibration,
calSaveCallback
ESPUI.addControl(
ControlType::Separator,
"CH4 (GPU Temp)",
"",
ControlColor::None,
tabCalibration
);
ESPUI.addControl(
ControlType::Separator,
"CH5 (VRAM Usage)",
"",
ControlColor::None,
tabCalibration
);
ESPUI.addControl(
ControlType::Separator,
"CH6 (Reserved 6)",
"",
ControlColor::None,
tabCalibration
);
ESPUI.addControl(
ControlType::Separator,
"CH7 (Reserved 7)",
"",
ControlColor::None,
tabCalibration
); );
// Start ESPUI // Start ESPUI
ESPUI.sliderContinuous = true; // < enables live slider updates
ESPUI.begin("Analog Monitor UI"); ESPUI.begin("Analog Monitor UI");
} }
@@ -374,9 +549,11 @@ void loop() {
// Start a new slew toward the target // Start a new slew toward the target
for (int ch = 0; ch < NUM_CHANNELS; ch++) { for (int ch = 0; ch < NUM_CHANNELS; ch++) {
if (!overrideActive[ch]) { // < NEW
targetDuty[ch] = values[ch]; targetDuty[ch] = values[ch];
slewStartDuty[ch] = currentDuty[ch]; slewStartDuty[ch] = currentDuty[ch];
} }
}
slewStartTime = millis(); slewStartTime = millis();
lastPacketTime = millis(); lastPacketTime = millis();
@@ -407,15 +584,17 @@ void loop() {
if (progress > 1.0f) progress = 1.0f; if (progress > 1.0f) progress = 1.0f;
for (int ch = 0; ch < NUM_CHANNELS; ch++) { for (int ch = 0; ch < NUM_CHANNELS; ch++) {
if (!overrideActive[ch]) {
float newDuty = slewStartDuty[ch] + (targetDuty[ch] - slewStartDuty[ch]) * progress; float newDuty = slewStartDuty[ch] + (targetDuty[ch] - slewStartDuty[ch]) * progress;
currentDuty[ch] = newDuty; currentDuty[ch] = newDuty;
float calibratedDuty = applyCalibration(ch, newDuty); float calibratedDuty = applyCalibration(ch, newDuty);
int duty = (int)((calibratedDuty / 100.0f) * ((1 << pwmResolution) - 1)); int duty = (int)((calibratedDuty / 100.0f) * ((1 << pwmResolution) - 1));
ledcWrite(pwmPins[ch], duty); ledcWrite(pwmPins[ch], duty);
} }
} }
}
// -------- Watchdog fade-to-zero -------- // -------- Watchdog fade-to-zero --------
if (millis() - lastPacketTime > watchdogTimeout) { if (millis() - lastPacketTime > watchdogTimeout) {