From 228203839106d408524c46fd8bbdd6bbc4139f2f Mon Sep 17 00:00:00 2001 From: "Adrian A. Baumann" Date: Wed, 15 Apr 2026 22:46:39 +0200 Subject: [PATCH] Interrupt issue with FastLed circumvented --- Gaugecontroller/Gaugecontroller.ino | 79 +++++++++++++++++------------ gauge.py | 16 +++--- 2 files changed, 56 insertions(+), 39 deletions(-) diff --git a/Gaugecontroller/Gaugecontroller.ino b/Gaugecontroller/Gaugecontroller.ino index ec41ad9..58fe5c5 100644 --- a/Gaugecontroller/Gaugecontroller.ino +++ b/Gaugecontroller/Gaugecontroller.ino @@ -85,6 +85,7 @@ String rxLine; CRGB leds[TOTAL_LEDS]; uint8_t gaugeLedOffset[GAUGE_COUNT]; BlinkState blinkState[TOTAL_LEDS]; +bool ledsDirty = false; void sendReply(const String& s) { CMD_PORT.println(s); @@ -545,7 +546,7 @@ bool parseLed(const String& line) { blinkState[gaugeLedOffset[id] + i].active = false; leds[gaugeLedOffset[id] + i] = color; } - FastLED.show(); + ledsDirty = true; sendReply("OK"); return true; } @@ -553,43 +554,50 @@ bool parseLed(const String& line) { } bool parseBlink(const String& line) { - int id; + int id, onMs, offMs, r, g, b; char idxToken[16]; - long onMs, offMs; - if (sscanf(line.c_str(), "BLINK %d %15s %ld %ld", &id, idxToken, &onMs, &offMs) == 4) { - if (id < 0 || id >= GAUGE_COUNT) { sendReply("ERR BAD_ID"); return true; } - char* dash = strchr(idxToken, '-'); - int idxFirst = atoi(idxToken); - int idxLast = dash ? atoi(dash + 1) : idxFirst; - if (idxFirst < 0 || idxLast >= gaugePins[id].ledCount || idxFirst > idxLast) { - sendReply("ERR BAD_IDX"); return true; - } + // Accept both forms: + // BLINK — use current LED colour + // BLINK — set colour in same command + int count = sscanf(line.c_str(), "BLINK %d %15s %d %d %d %d %d", + &id, idxToken, &onMs, &offMs, &r, &g, &b); + if (count != 4 && count != 7) return false; - if (onMs == 0 && offMs == 0) { - for (int i = idxFirst; i <= idxLast; i++) - blinkState[gaugeLedOffset[id] + i].active = false; - sendReply("OK"); - return true; - } - if (onMs <= 0 || offMs <= 0) { sendReply("ERR BAD_TIME"); return true; } + if (id < 0 || id >= GAUGE_COUNT) { sendReply("ERR BAD_ID"); return true; } + char* dash = strchr(idxToken, '-'); + int idxFirst = atoi(idxToken); + int idxLast = dash ? atoi(dash + 1) : idxFirst; + if (idxFirst < 0 || idxLast >= gaugePins[id].ledCount || idxFirst > idxLast) { + sendReply("ERR BAD_IDX"); return true; + } - unsigned long nowMs = millis(); - for (int i = idxFirst; i <= idxLast; i++) { - uint8_t globalIdx = gaugeLedOffset[id] + i; - BlinkState& bs = blinkState[globalIdx]; - bs.onColor = leds[globalIdx]; - bs.onMs = (uint32_t)onMs; - bs.offMs = (uint32_t)offMs; - bs.currentlyOn = true; - bs.lastToggleMs = nowMs; - bs.active = true; - leds[globalIdx] = bs.onColor; - } - FastLED.show(); + if (onMs == 0 && offMs == 0) { + for (int i = idxFirst; i <= idxLast; i++) + blinkState[gaugeLedOffset[id] + i].active = false; sendReply("OK"); return true; } - return false; + if (onMs <= 0 || offMs <= 0) { sendReply("ERR BAD_TIME"); return true; } + + CRGB color = (count == 7) + ? CRGB(constrain(r, 0, 255), constrain(g, 0, 255), constrain(b, 0, 255)) + : CRGB(0, 0, 0); // placeholder; overwritten per-LED below when count==4 + + unsigned long nowMs = millis(); + for (int i = idxFirst; i <= idxLast; i++) { + uint8_t globalIdx = gaugeLedOffset[id] + i; + BlinkState& bs = blinkState[globalIdx]; + bs.onColor = (count == 7) ? color : leds[globalIdx]; + bs.onMs = (uint32_t)onMs; + bs.offMs = (uint32_t)offMs; + bs.currentlyOn = true; + bs.lastToggleMs = nowMs; + bs.active = true; + leds[globalIdx] = bs.onColor; + } + ledsDirty = true; + sendReply("OK"); + return true; } void updateBlink() { @@ -612,7 +620,7 @@ void updateBlink() { } } - if (changed) FastLED.show(); + if (changed) ledsDirty = true; } void processLine(const String& line) { @@ -691,6 +699,11 @@ void loop() { readCommands(); updateBlink(); + if (ledsDirty) { + FastLED.show(); + ledsDirty = false; + } + for (uint8_t i = 0; i < GAUGE_COUNT; i++) { updateGauge(i); } diff --git a/gauge.py b/gauge.py index acb4f6a..8c98bae 100644 --- a/gauge.py +++ b/gauge.py @@ -224,12 +224,15 @@ def set_status_led(gauge_idx, led_type, on): def _apply_blink_or_led(gauge_idx, led_idx, color, effect): - """Set LED to color; if effect is a known blink preset, start blinking.""" + """Set LED to color, optionally starting a blink effect. + When blinking, color is passed inside the BLINK command so only one + serial command is needed — avoids a FastLED.show() race on the Arduino.""" r, g, b = color - arduino_send(f"LED {gauge_idx} {led_idx} {r} {g} {b}") if effect in _BLINK_PRESETS: on_ms, off_ms = _BLINK_PRESETS[effect] - arduino_send(f"BLINK {gauge_idx} {led_idx} {on_ms} {off_ms}") + arduino_send(f"BLINK {gauge_idx} {led_idx} {on_ms} {off_ms} {r} {g} {b}") + else: + arduino_send(f"LED {gauge_idx} {led_idx} {r} {g} {b}") # --------------------------------------------------------------------------- @@ -553,12 +556,13 @@ def on_message(topic, payload): return _bl_effect[i] = effect if effect: - set_backlight(i, r, g, b, brightness) + scale = brightness / 100 + rs = int(r * scale); gs = int(g * scale); bs_ = int(b * scale) + on_ms, off_ms = _BLINK_PRESETS[effect] + arduino_send(f"BLINK {i} {_LED_BACKLIGHT_RANGE} {on_ms} {off_ms} {rs} {gs} {bs_}") backlight_color[i] = (r, g, b) backlight_brightness[i] = brightness backlight_on[i] = True - on_ms, off_ms = _BLINK_PRESETS[effect] - arduino_send(f"BLINK {i} {_LED_BACKLIGHT_RANGE} {on_ms} {off_ms}") _mark_bl_dirty() else: set_backlight_color(i, r, g, b, brightness)