From c3c9299ca350988b1603b82563072b5a5edff125 Mon Sep 17 00:00:00 2001 From: ThePendulum Date: Thu, 30 Apr 2026 17:38:26 +0200 Subject: [PATCH] Added reverse light and police flashing. --- QuadLightsV3.ino | 98 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 81 insertions(+), 17 deletions(-) diff --git a/QuadLightsV3.ino b/QuadLightsV3.ino index 32e6d69..c0f2530 100644 --- a/QuadLightsV3.ino +++ b/QuadLightsV3.ino @@ -7,6 +7,7 @@ constexpr int KEEPALIVE_TIMEOUT = 5000; // ms constexpr int LEDS_TAIL_PIN = PA6; constexpr int LEDS_TAIL_LENGTH = 10; constexpr int LEDS_TAIL_BRIGHTNESS = 255; +constexpr int LEDS_TAIL_SIDE_LENGTH = 3; constexpr int LEDS_LEFT_PIN = PA5; constexpr int LEDS_RIGHT_PIN = PA7; @@ -30,9 +31,9 @@ constexpr int PIN_PEDAL = PA0; constexpr int PIN_PEDAL_FORWARD = PA3; constexpr int PIN_PEDAL_REVERSE = PA4; -constexpr int PIN_BUTTON_MODE = PB8; -constexpr int PIN_BUTTON_LEFT = PB7; -constexpr int PIN_BUTTON_RIGHT = PB9; +constexpr int PIN_BUTTON_MODE = PA9; +constexpr int PIN_BUTTON_LEFT = PA8; +constexpr int PIN_BUTTON_RIGHT = PA10; constexpr int LED_ON = LOW; constexpr int LED_OFF = HIGH; @@ -61,6 +62,8 @@ Adafruit_NeoPixel ledsDash(LEDS_DASH_LENGTH, LEDS_DASH_PIN, NEO_GRBW + NEO_KHZ80 const uint32_t COLOR_OFF = ledsTail.Color(0, 0, 0, 0); const uint32_t COLOR_WHITE = ledsTail.Color(0, 0, 0, 255); +const uint32_t COLOR_WHITE_FULL = ledsTail.Color(255, 255, 255, 255); +const uint32_t COLOR_WHITE_DIM = ledsTail.Color(0, 0, 0, 100); const uint32_t COLOR_RED = ledsTail.Color(255, 0, 0, 0); const uint32_t COLOR_AMBER = ledsTail.Color(255, 50, 0, 0); const uint32_t COLOR_POLICE_BLUE = ledsTail.Color(0, 0, 255, 0); @@ -82,12 +85,15 @@ unsigned long animationFrame = 0; unsigned long lastFrame = 0; unsigned long lastPedalPress = millis(); +bool isReversing = false; unsigned long indicatorLeftStart = 0; unsigned long indicatorRightStart = 0; bool indicatorLeftActive = false; bool indicatorRightActive = false; +bool policeActive = false; + void setup() { // initialize digital pin LED_BUILTIN as an output. pinMode(LED_BUILTIN, OUTPUT); @@ -138,6 +144,14 @@ void animateHeadlights() { ledsHeadRight.fill(COLOR_WHITE); } +void animateTailLight() { + ledsTail.fill(COLOR_RED); +} + +void animateReverse() { + ledsTail.fill(COLOR_WHITE); +} + constexpr int INDICATOR_DURATION = 3000; // total ms constexpr int INDICATOR_HOLD_FRAMES = 15; constexpr int INDICATOR_OFF_FRAMES = 20; @@ -186,24 +200,60 @@ void animateIndicator(Direction direction) { ledsDash.fill(COLOR_OFF); } } +constexpr float POLICE_SPEED = 0.75; -void animate(long now) { - if (pedalForward.read() == Button::PRESSED) { - ledsTail.fill(ledsTail.Color(255, 0, 0, 0)); - lastPedalPress = now; +enum PoliceStates { POLICE_LEFT, POLICE_RIGHT, POLICE_OFF }; - Serial.println("PEDAL FORWARD"); - } else if (pedalReverse.read() == Button::PRESSED) { - ledsTail.fill(ledsTail.Color(0, 0, 0, 255)); - lastPedalPress = now; +const PoliceStates policeBeaconFrames[12] = { + POLICE_LEFT, + POLICE_OFF, + POLICE_LEFT, + POLICE_OFF, + POLICE_OFF, + POLICE_OFF, + POLICE_RIGHT, + POLICE_OFF, + POLICE_RIGHT, + POLICE_OFF, + POLICE_OFF, + POLICE_OFF, +}; - Serial.println("PEDAL REVERSE"); +void animatePolice() { + const long policeFrame = lround(animationFrame * POLICE_SPEED) % 12; + + if (policeBeaconFrames[policeFrame] == POLICE_LEFT) { + ledsHeadLeft.fill(COLOR_POLICE_BLUE, 0, LEDS_EDGE_LENGTH); + ledsHeadRight.fill(COLOR_OFF, 0, LEDS_EDGE_LENGTH); + } else if (policeBeaconFrames[policeFrame] == POLICE_RIGHT) { + ledsHeadLeft.fill(COLOR_OFF, 0, LEDS_EDGE_LENGTH); + ledsHeadRight.fill(COLOR_POLICE_BLUE, 0, LEDS_EDGE_LENGTH); } else { - ledsTail.fill(ledsTail.Color(100, 0, 0, 0)); + ledsHeadLeft.fill(COLOR_OFF, 0, LEDS_EDGE_LENGTH); + ledsHeadRight.fill(COLOR_OFF, 0, LEDS_EDGE_LENGTH); } + if (policeFrame < 6) { + ledsHeadLeft.fill(COLOR_WHITE_DIM, LEDS_EDGE_LENGTH, LEDS_EDGE_LENGTH + LEDS_RING_LENGTH); + ledsHeadRight.fill(COLOR_WHITE, LEDS_EDGE_LENGTH, LEDS_EDGE_LENGTH + LEDS_RING_LENGTH); + } else { + ledsHeadLeft.fill(COLOR_WHITE, LEDS_EDGE_LENGTH, LEDS_EDGE_LENGTH + LEDS_RING_LENGTH); + ledsHeadRight.fill(COLOR_WHITE_DIM, LEDS_EDGE_LENGTH, LEDS_EDGE_LENGTH + LEDS_RING_LENGTH); + } +} + +void animate(long now) { // always animate headlights, let other modes override animateHeadlights(); + animateTailLight(); + + if (isReversing) { + animateReverse(); + } + + if (activeMode == MODE_POLICE) { + animatePolice(); + } // we need the boolean to prevent the indicators from turning on right after boot if (indicatorLeftActive && now - indicatorLeftStart < INDICATOR_DURATION) { @@ -227,16 +277,15 @@ void loop() { const unsigned long now = millis(); if (buttonMode.pressed()) { - ledsTail.fill(ledsTail.Color(0, 0, 255, 0)); - activeMode = (Mode)((activeMode + 1) % MODE_COUNT); + animationFrame = 0; // ensures animations start cleanly instead of halfway Serial.print("BUTTON MODE "); Serial.println(activeMode); } if (buttonLeft.pressed()) { - animationFrame = 0; // ensures animations start cleanly instead of halfway + animationFrame = 0; indicatorLeftActive = true; indicatorLeftStart = now; @@ -248,7 +297,7 @@ void loop() { } if (buttonRight.pressed()) { - animationFrame = 0; // ensures animations start cleanly instead of halfway + animationFrame = 0; indicatorRightActive = true; indicatorRightStart = now; @@ -259,6 +308,21 @@ void loop() { Serial.println("BUTTON RIGHT"); } + if (pedalReverse.read() == Button::PRESSED && pedalForward.read() != Button::PRESSED) { + lastPedalPress = now; + isReversing = true; + + Serial.println("PEDAL REVERSE"); + } else { + isReversing = false; + } + + if (pedalForward.read() == Button::PRESSED) { + lastPedalPress = now; + + Serial.println("PEDAL FORWARD"); + } + if (now - lastFrame >= ANIMATION_INTERVAL) { lastFrame += ANIMATION_INTERVAL; animationFrame += 1;