#include #include "UI.h" // Local UI control static uint16_t portInput; // ------------------------------- // Calibration UI helpers & callbacks // ------------------------------- void refreshCalibrationUI() { 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(); // 0–100 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 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; } } } // ------------------------------- // UI init // ------------------------------- void uiInit(uint16_t& tabSettings, uint16_t& tabLighting, uint16_t& tabCalibration) { // Create tabs tabSettings = ESPUI.addControl(ControlType::Tab, "Settings", "Settings"); tabLighting = ESPUI.addControl(ControlType::Tab, "Lighting", "Lighting"); tabCalibration= ESPUI.addControl(ControlType::Tab, "Calibration", "Calibration"); // Restart button callback auto restartCallback = [](Control *sender, int type) { ESP.restart(); }; // Port input callback auto portInputCallback = [](Control *sender, int type) { Serial.print("Port input changed to: "); Serial.println(sender->value); }; // Save & Apply callback auto savePortCallback = [](Control *sender, int type) { if (type != B_UP) return; // Prevent double-trigger Control* c = ESPUI.getControl(portInput); int newPort = c->value.toInt(); if (newPort < 1024 || newPort > 65535) { Serial.println("Invalid port (1024–65535)"); return; } prefs.putInt("udpPort", newPort); udpPort = newPort; udp.stop(); udp.begin(udpPort); Serial.print("New UDP port applied: "); Serial.println(udpPort); ESPUI.updateControlValue(portInput, String(newPort)); }; // ------------------------------------------- // Connection Status section // ------------------------------------------- ESPUI.addControl( ControlType::Separator, "Connection Status", "", ControlColor::None, tabSettings ); // Live-updating connection status label connectionStatusLabel = ESPUI.addControl( ControlType::Label, "Status", "Disconnected", ControlColor::Wetasphalt, tabSettings ); // ------------------------------------------- // UDP Telemetry Connection Settings section // ------------------------------------------- ESPUI.addControl( ControlType::Separator, "UDP Telemetry Connection Settings", "", ControlColor::None, tabSettings ); // UDP Port Number input portInput = ESPUI.addControl( ControlType::Number, "UDP Port", String(udpPort), ControlColor::Peterriver, tabSettings, portInputCallback ); // Save & Apply button ESPUI.addControl( ControlType::Button, "UDP Port", "Save & Apply", ControlColor::Emerald, tabSettings, savePortCallback ); // Existing separator (leave as-is) ESPUI.addControl( ControlType::Separator, "", "", ControlColor::None, tabSettings ); // Restart button (unchanged) ESPUI.addControl( ControlType::Button, "Restart ESP32", "Restart", ControlColor::Alizarin, tabSettings, restartCallback ); // Lighting tab placeholder ESPUI.addControl( ControlType::Label, "Lighting Placeholder", "Coming soon...", ControlColor::Emerald, tabLighting ); // ------------------------------- // Calibration tab UI // ------------------------------- // Channel selector calChannelDropdown = ESPUI.addControl( ControlType::Select, "Selected Channel", "0", ControlColor::Peterriver, tabCalibration, calChannelCallback ); // Add options 0–7 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( ControlType::Separator, "", "", ControlColor::None, 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( ControlType::Separator, "", "", ControlColor::None, 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( ControlType::Separator, "", "", ControlColor::None, tabCalibration ); // Save button calSaveButton = ESPUI.addControl( ControlType::Button, "Save Calibration", "Save", ControlColor::Emerald, tabCalibration, calSaveCallback ); }