added fade in if a new connection is established
This commit is contained in:
@@ -122,6 +122,17 @@ unsigned long lastPacketTime = 0;
|
|||||||
const unsigned long watchdogTimeout = 5000; // 5 seconds
|
const unsigned long watchdogTimeout = 5000; // 5 seconds
|
||||||
unsigned long lastFadeTime = 0;
|
unsigned long lastFadeTime = 0;
|
||||||
|
|
||||||
|
// -------------------------------
|
||||||
|
// Connection state machine
|
||||||
|
// -------------------------------
|
||||||
|
enum ConnectionState {
|
||||||
|
STATE_DISCONNECTED, // fade to zero
|
||||||
|
STATE_CONNECTING, // fade in 0 → 100%
|
||||||
|
STATE_CONNECTED // normal UDP-driven slew
|
||||||
|
};
|
||||||
|
|
||||||
|
ConnectionState connectionState = STATE_DISCONNECTED;
|
||||||
|
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
// ESPUI controls
|
// ESPUI controls
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
@@ -162,8 +173,6 @@ float applyCalibration(uint8_t ch, float logicalDuty) {
|
|||||||
// Calibration UI helpers & callbacks
|
// Calibration UI helpers & callbacks
|
||||||
// -------------------------------
|
// -------------------------------
|
||||||
void refreshCalibrationUI() {
|
void refreshCalibrationUI() {
|
||||||
String label = "CH" + String(selectedCalChannel) + " (" + channelLabels[selectedCalChannel] + ")";
|
|
||||||
|
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
ESPUI.updateControlValue(calInputs[i], String(calibratedPoints[selectedCalChannel][i], 2));
|
ESPUI.updateControlValue(calInputs[i], String(calibratedPoints[selectedCalChannel][i], 2));
|
||||||
}
|
}
|
||||||
@@ -183,7 +192,6 @@ void calChannelCallback(Control *sender, int type) {
|
|||||||
refreshCalibrationUI();
|
refreshCalibrationUI();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void calPointCallback(Control *sender, int type) {
|
void calPointCallback(Control *sender, int type) {
|
||||||
int index = sender->id - calInputs[0];
|
int index = sender->id - calInputs[0];
|
||||||
if (index >= 0 && index < 5) {
|
if (index >= 0 && index < 5) {
|
||||||
@@ -507,7 +515,7 @@ void setup() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Start ESPUI
|
// Start ESPUI
|
||||||
ESPUI.sliderContinuous = true; // <‑‑ enables live slider updates
|
ESPUI.sliderContinuous = true; // enables live slider updates
|
||||||
ESPUI.begin("Analog Monitor UI");
|
ESPUI.begin("Analog Monitor UI");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -535,79 +543,157 @@ void loop() {
|
|||||||
|
|
||||||
if (idx == NUM_CHANNELS) {
|
if (idx == NUM_CHANNELS) {
|
||||||
|
|
||||||
// Start a new slew toward the target
|
// First valid packet after being disconnected → start CONNECTING
|
||||||
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
if (connectionState == STATE_DISCONNECTED) {
|
||||||
if (!overrideActive[ch]) { // <‑‑ NEW
|
Serial.println("STATE CHANGE: DISCONNECTED → CONNECTING (UDP connection established)");
|
||||||
targetDuty[ch] = values[ch];
|
connectionState = STATE_CONNECTING;
|
||||||
slewStartDuty[ch] = currentDuty[ch];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
slewStartTime = millis();
|
|
||||||
lastPacketTime = millis();
|
|
||||||
|
|
||||||
// -------- Improved Debug Output --------
|
// Initialize fade-in: start from 0 on all non-override channels
|
||||||
Serial.println("Received UDP packet:");
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
for (int i = 0; i < NUM_CHANNELS; i++) {
|
if (!overrideActive[ch]) {
|
||||||
Serial.print(" CH");
|
currentDuty[ch] = 0.0f;
|
||||||
Serial.print(i);
|
}
|
||||||
Serial.print(" (");
|
}
|
||||||
Serial.print(channelLabels[i]);
|
|
||||||
Serial.print("): ");
|
lastPacketTime = millis(); // prevent watchdog during fade-in
|
||||||
Serial.print(values[i], 2);
|
// Ignore this packet's values during fade-in (Option B)
|
||||||
if (channelUnits[i][0] != '\0') {
|
}
|
||||||
Serial.print(" ");
|
else if (connectionState == STATE_CONNECTING) {
|
||||||
Serial.print(channelUnits[i]);
|
// Ignore UDP values during fade-in, just keep watchdog alive
|
||||||
}
|
lastPacketTime = millis();
|
||||||
Serial.println();
|
}
|
||||||
|
else if (connectionState == STATE_CONNECTED) {
|
||||||
|
// Normal UDP-driven update
|
||||||
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
|
if (!overrideActive[ch]) {
|
||||||
|
targetDuty[ch] = values[ch];
|
||||||
|
slewStartDuty[ch] = currentDuty[ch];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slewStartTime = millis();
|
||||||
|
lastPacketTime = millis();
|
||||||
|
|
||||||
|
// Debug output
|
||||||
|
Serial.println("Received UDP packet:");
|
||||||
|
for (int i = 0; i < NUM_CHANNELS; i++) {
|
||||||
|
Serial.print(" CH");
|
||||||
|
Serial.print(i);
|
||||||
|
Serial.print(" (");
|
||||||
|
Serial.print(channelLabels[i]);
|
||||||
|
Serial.print("): ");
|
||||||
|
Serial.print(values[i], 2);
|
||||||
|
if (channelUnits[i][0] != '\0') {
|
||||||
|
Serial.print(" ");
|
||||||
|
Serial.print(channelUnits[i]);
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
|
}
|
||||||
|
Serial.println();
|
||||||
}
|
}
|
||||||
Serial.println();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------- Slew-rate limiting --------
|
// -------- Connection state machine --------
|
||||||
if (millis() - lastPacketTime <= watchdogTimeout) {
|
unsigned long now = millis();
|
||||||
|
|
||||||
unsigned long now = millis();
|
switch (connectionState) {
|
||||||
float progress = (float)(now - slewStartTime) / (float)slewDuration;
|
|
||||||
if (progress > 1.0f) progress = 1.0f;
|
|
||||||
|
|
||||||
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
case STATE_CONNECTED: {
|
||||||
|
// Check for lost connection
|
||||||
if (!overrideActive[ch]) {
|
if (now - lastPacketTime > watchdogTimeout) {
|
||||||
float newDuty = slewStartDuty[ch] + (targetDuty[ch] - slewStartDuty[ch]) * progress;
|
Serial.println("STATE CHANGE: CONNECTED → DISCONNECTED (UDP connection lost)");
|
||||||
currentDuty[ch] = newDuty;
|
connectionState = STATE_DISCONNECTED;
|
||||||
|
break;
|
||||||
float calibratedDuty = applyCalibration(ch, newDuty);
|
|
||||||
int duty = (int)((calibratedDuty / 100.0f) * ((1 << pwmResolution) - 1));
|
|
||||||
ledcWrite(pwmPins[ch], duty);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------- Watchdog fade-to-zero --------
|
// Normal slew-rate limiting
|
||||||
if (millis() - lastPacketTime > watchdogTimeout) {
|
float progress = (float)(now - slewStartTime) / (float)slewDuration;
|
||||||
|
if (progress > 1.0f) progress = 1.0f;
|
||||||
|
|
||||||
const unsigned long fadeInterval = 1;
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
|
if (!overrideActive[ch]) {
|
||||||
|
float newDuty = slewStartDuty[ch] + (targetDuty[ch] - slewStartDuty[ch]) * progress;
|
||||||
|
currentDuty[ch] = newDuty;
|
||||||
|
|
||||||
if (millis() - lastFadeTime >= fadeInterval) {
|
float calibratedDuty = applyCalibration(ch, newDuty);
|
||||||
lastFadeTime = millis();
|
int duty = (int)((calibratedDuty / 100.0f) * ((1 << pwmResolution) - 1));
|
||||||
|
ledcWrite(pwmPins[ch], duty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
case STATE_CONNECTING: {
|
||||||
|
// If we lose packets even while connecting, fall back to DISCONNECTED
|
||||||
|
if (now - lastPacketTime > watchdogTimeout) {
|
||||||
|
Serial.println("STATE CHANGE: CONNECTING → DISCONNECTED (no packets during fade-in)");
|
||||||
|
connectionState = STATE_DISCONNECTED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (currentDuty[ch] > 0.0f) {
|
// Fade-in animation: 0 → 100% on all non-override channels
|
||||||
|
const unsigned long fadeInterval = 1;
|
||||||
|
const float fadeInFactor = 0.999f; // fast at start, slow near 100%
|
||||||
|
|
||||||
const float fadeFactor = 0.999f;
|
if (now - lastFadeTime >= fadeInterval) {
|
||||||
currentDuty[ch] *= fadeFactor;
|
lastFadeTime = now;
|
||||||
|
|
||||||
if (currentDuty[ch] < 0.01f)
|
bool allReached = true;
|
||||||
currentDuty[ch] = 0.0f;
|
|
||||||
|
|
||||||
float calibratedDuty = applyCalibration(ch, currentDuty[ch]);
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
int duty = (int)((calibratedDuty / 100.0f) * ((1 << pwmResolution) - 1));
|
if (!overrideActive[ch]) {
|
||||||
|
float cd = currentDuty[ch];
|
||||||
|
|
||||||
ledcWrite(pwmPins[ch], duty);
|
// Exponential approach to 100%
|
||||||
}
|
cd = cd * fadeInFactor + 100.0f * (1.0f - fadeInFactor);
|
||||||
}
|
currentDuty[ch] = cd;
|
||||||
}
|
|
||||||
|
float calibratedDuty = applyCalibration(ch, cd);
|
||||||
|
int duty = (int)((calibratedDuty / 100.0f) * ((1 << pwmResolution) - 1));
|
||||||
|
ledcWrite(pwmPins[ch], duty);
|
||||||
|
|
||||||
|
// Check if we're close enough to 100%
|
||||||
|
if (cd < 99.0f) {
|
||||||
|
allReached = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allReached) {
|
||||||
|
Serial.println("STATE CHANGE: CONNECTING → CONNECTED (fade-in complete)");
|
||||||
|
// Initialize slew baseline from currentDuty
|
||||||
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
|
slewStartDuty[ch] = currentDuty[ch];
|
||||||
|
}
|
||||||
|
slewStartTime = millis();
|
||||||
|
connectionState = STATE_CONNECTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
|
|
||||||
|
case STATE_DISCONNECTED: {
|
||||||
|
// Watchdog fade-to-zero (always active in this state)
|
||||||
|
const unsigned long fadeInterval = 1;
|
||||||
|
|
||||||
|
if (now - lastFadeTime >= fadeInterval) {
|
||||||
|
lastFadeTime = now;
|
||||||
|
|
||||||
|
for (int ch = 0; ch < NUM_CHANNELS; ch++) {
|
||||||
|
|
||||||
|
if (currentDuty[ch] > 0.0f) {
|
||||||
|
|
||||||
|
const float fadeFactor = 0.999f;
|
||||||
|
currentDuty[ch] *= fadeFactor;
|
||||||
|
|
||||||
|
if (currentDuty[ch] < 0.01f)
|
||||||
|
currentDuty[ch] = 0.0f;
|
||||||
|
|
||||||
|
float calibratedDuty = applyCalibration(ch, currentDuty[ch]);
|
||||||
|
int duty = (int)((calibratedDuty / 100.0f) * ((1 << pwmResolution) - 1));
|
||||||
|
|
||||||
|
ledcWrite(pwmPins[ch], duty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user