From 110ff0b2ca5f2b08816b30254128b9ce5646b310 Mon Sep 17 00:00:00 2001 From: Jan Grewe <jan@faked.org> Date: Fri, 3 May 2019 01:04:49 +0200 Subject: [PATCH] add LED toggle via double-click --- OpenWatchWinder.ino | 234 +++++++++++++++++++++++++------------------- README.md | 24 +++-- 2 files changed, 146 insertions(+), 112 deletions(-) diff --git a/OpenWatchWinder.ino b/OpenWatchWinder.ino index c20c56a..3156acb 100644 --- a/OpenWatchWinder.ino +++ b/OpenWatchWinder.ino @@ -1,5 +1,5 @@ // Configuration -#define CYCLES 1 +#define CYCLES 2 #define ROT_R 2 #define ROT_L 2 #define PAUSE_MIN 2 @@ -23,51 +23,49 @@ using namespace ace_button; enum StateType { - W_INIT, // 0 - W_SETUP, // 1 - W_IDLE, // 2 - W_CYCLE, // 3 - W_LEFT, // 4 - W_RIGHT, // 5 - W_STOP, // 6 - W_PAUSE // 7 + W_IDLE, // 0 + W_CYCLE, // 1 + W_RIGHT, // 2 + W_LEFT, // 3 + W_STOP, // 4 + W_PAUSE // 5 }; AccelStepper winder(8, 8, 10, 9, 11, false); JLed pwr_led = JLed(LED_PIN).FadeOn(1000); AceButton pwr_sw(SW_PIN); -int TargetPos = 0; -long StartTime = 0; - +StateType WState = W_IDLE; int Rotations = CYCLES; -StateType WState = W_INIT; -int Restart = false; +int Start = false; int Continue = false; -int StopWind = false; -int WaitMin = PAUSE_MIN; +int Stop = false; +int LedOn = true; +int LastMinute = false; +int TargetPos = 0; +long StartTime = 0; void handleSwEvent(AceButton *, uint8_t, uint8_t); void setup() { - ButtonConfig *buttonConfig = pwr_sw.getButtonConfig(); - - WState = W_SETUP; - Serial.begin(9600); + Serial.begin(115200); pinMode(SW_PIN, INPUT_PULLUP); + ButtonConfig *buttonConfig = pwr_sw.getButtonConfig(); buttonConfig->setEventHandler(handleSwEvent); - buttonConfig->setFeature(ButtonConfig::kFeatureClick); + buttonConfig->setFeature(ButtonConfig::kFeatureDoubleClick); buttonConfig->setFeature(ButtonConfig::kFeatureLongPress); buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterLongPress); + buttonConfig->setFeature(ButtonConfig::kFeatureSuppressAfterDoubleClick); + buttonConfig->setFeature(ButtonConfig::kFeatureSuppressClickBeforeDoubleClick); buttonConfig->setClickDelay(500); winder.setMaxSpeed(ROT_SPEED); winder.setAcceleration(ROT_ACCEL); WState = W_IDLE; - Serial.println(">>Winder Ready<<"); + Serial.println("-- Winder: Ready"); } void loop() @@ -75,42 +73,38 @@ void loop() StateType old_state = WState; pwr_sw.check(); - pwr_led.Update(); switch (WState) { case W_IDLE: - if (Restart || Continue) + if (Start || Continue) { + Start = false; + StartTime = millis(); + winder.enableOutputs(); + Rotations = CYCLES; WState = W_CYCLE; - Restart = false; } - StartTime = millis(); - winder.enableOutputs(); - Rotations = CYCLES; break; case W_CYCLE: - pwr_led.Blink(1000, 500).Forever(); + if (LedOn) + { + pwr_led.Reset(); + pwr_led.Blink(1000, 200).Forever(); + } - if (StopWind) + if (Stop) { WState = W_STOP; } else if ((Rotations--) > 0) { - Serial.print("Counter : "); + Serial.print("## Cycles until Pause: "); Serial.println(Rotations); - -#if (ROT_R > 0) WState = W_RIGHT; - TargetPos -= (ROT_R * ROT_STEPS); //-4096; // Une rotation complète avec 2048 pas (1 tour environ 4.5sec) - winder.moveTo(TargetPos); -#elif (ROT_L > 0) - WState = W_LEFT; - TargetPos += (ROT_R * ROT_STEPS); //-4096; // Une rotation complète avec 2048 pas (1 tour environ 4.5sec) + TargetPos -= (ROT_R * ROT_STEPS); winder.moveTo(TargetPos); -#endif } else { @@ -123,23 +117,16 @@ void loop() { winder.run(); } - else if (StopWind) + else if (Stop) { WState = W_STOP; } -#if (ROT_L > 0) else { WState = W_LEFT; - TargetPos += (ROT_R * ROT_STEPS); //-4096; // Une rotation complète avec 2048 pas (1 tour environ 4.5sec) + TargetPos += (ROT_R * ROT_STEPS); winder.moveTo(TargetPos); } -#else - else - { - WState = W_CYCLE; - } -#endif break; // case W_RIGHT case W_LEFT: @@ -147,7 +134,7 @@ void loop() { winder.run(); } - else if (StopWind) + else if (Stop) { WState = W_STOP; } @@ -158,54 +145,58 @@ void loop() break; // case W_LEFT case W_STOP: - StopWind = false; + Stop = false; winder.disableOutputs(); StartTime = millis(); - WState = W_PAUSE; if (Continue) { - pwr_led.Breathe(5000).Forever(); - Serial.println(">>Winder Stopped, will continue<<"); + Serial.println("<< Winder: Stopped, waiting for next cycle"); + WState = W_PAUSE; + if (LedOn) + { + pwr_led.Reset(); + pwr_led.Breathe(5000).Forever(); + } } else { - Serial.println(">>Winder Stopped, will not continue<<"); + Serial.println("<< Winder: Stopped, will not restart"); + if (LedOn) + { + pwr_led.Reset(); + pwr_led.On(); + } + WState = W_IDLE; } break; // case W_STOP case W_PAUSE: - { long temp = millis() - StartTime; long delta = 60L * 1000L; - static int last_min = false; - if (Restart) - { - WState = W_IDLE; - } - else if (Continue && temp > (delta * long(PAUSE_MIN))) // ms per minute + if (Start) { WState = W_IDLE; - last_min = false; - Serial.print("d_time : "); - Serial.print(temp); - Serial.print(" > "); - Serial.println(delta); } - else if ((!last_min) && (temp > (delta * long(PAUSE_MIN - 1)))) // ms per minute + // run once, 1 minute before PAUSE_MIN elapses + else if ((!LastMinute) && (temp > (delta * long(PAUSE_MIN - 1)))) { - Serial.println("Last Minute ..."); - if (Continue) + Serial.println("<< Winder: Restarting in 1 minute"); + if (Continue && LedOn) { + pwr_led.Reset(); pwr_led.Breathe(1000).Forever(); } - last_min = true; + LastMinute = true; } - } - break; // W_PAUSE + // PAUSE_MIN has elapsed && Continue + else if (Continue && temp > (delta * long(PAUSE_MIN))) + { + LastMinute = false; + WState = W_IDLE; + } + break; // W_PAUSE - case W_INIT: - case W_SETUP: default: winder.disableOutputs(); break; @@ -214,62 +205,101 @@ void loop() if (WState != old_state) { - Serial.print("STATE : "); + Serial.print("-- Changing State: "); Serial.print(old_state); - Serial.print(" >> "); + Serial.print(" -> "); Serial.println(WState); } + pwr_led.Update(); + } // loop -void handleSwEvent(AceButton *button, uint8_t eventType, - uint8_t buttonState) +void handleSwEvent(AceButton *button, uint8_t eventType, uint8_t buttonState) { switch (eventType) { - case AceButton::kEventLongPressed: - Serial.println("SW LONG"); - pwr_led.Reset(); + + case AceButton::kEventClicked: switch (WState) { - case W_LEFT: - case W_RIGHT: + case W_IDLE: case W_PAUSE: - Serial.println(">>Stop Winding<<"); - StopWind = true; - Continue = false; - pwr_led.On(); + Serial.println(">> Click: Start Winding"); + Stop = false; + Start = true; + Continue = true; + WState = W_IDLE; break; default: break; } break; - case AceButton::kEventClicked: - Serial.println("SW CLICK"); - pwr_led.Reset(); - Continue = true; - StopWind = false; + case AceButton::kEventLongPressed: switch (WState) { - case W_IDLE: - Serial.println(">>Start Winding<<"); - WState = W_CYCLE; - break; + case W_LEFT: + case W_RIGHT: case W_PAUSE: - Serial.println(">>Restart Winding<<"); - Restart = true; + Serial.println(">> LongPress: Stop Winding"); + pwr_led.Reset(); + pwr_led.Blink(100, 500).Forever(); + Stop = true; + Continue = false; break; default: break; } break; + case AceButton::kEventDoubleClicked: + pwr_led.Reset(); + if (LedOn) + { + Serial.println(">> DoubleClick: LED Disabled"); + pwr_led.Stop(); + LedOn = false; + } + else + { + Serial.println(">> DoubleClick: LED Enabled"); + LedOn = true; + switch (WState) + { + case W_IDLE: + Serial.println("<< LED: On"); + pwr_led.On(); + break; + + case W_PAUSE: + if (!LastMinute) + { + Serial.println("<< LED: Breathe Slow"); + pwr_led.Breathe(5000).Forever(); + } + else + { + Serial.println("<< LED: Breathe Fast"); + pwr_led.Breathe(1000).Forever(); + } + break; + + case W_LEFT: + case W_RIGHT: + Serial.println("<< LED: Blink"); + pwr_led.Blink(1000, 200).Forever(); + break; + + default: + Serial.println("<< LED: WTF?"); + pwr_led.Blink(50, 50).Forever(); + break; + } + } + break; + default: - //Serial.print("SW: "); - //Serial.print(eventType); - //Serial.print(", "); - //Serial.println(buttonState); break; } } diff --git a/README.md b/README.md index 7882c28..2542bae 100644 --- a/README.md +++ b/README.md @@ -5,28 +5,32 @@ This allows you to control the cycles of a stepper motor with a momentary switch ## Using the Winder On powerup, the LED will fade on and the winder will be in standby, waiting for a button press. -The button knows two types of inputs: -* **press**, which is input shorter than 500ms +The button knows three types of inputs: +* **click**, which is input shorter than 500ms * **long-press**, which is input longer than 500ms +* **double-click** The winder works in cycles, which means it will do the configured number of cycles (`CYCLES`), with each cycle consisting of a configured number of rotations in each direction (`ROT_R`, `ROT_L`), then wait for the configured amount of minutes (`PAUSE_MIN`), and eventually restart the cycles. This will be refered to as "_winding_". ### The very short version of a manual: -* press the button to start winding +* click the button to start winding * long-press the button to stop winding +* double-click to toggle the LED (on/off) -### Switching Modes (a.k.a the slightly longer version of a manual) -* When pressing the button while the winder is in standby, it will start winding. +### Switching States (a.k.a the slightly longer version of a manual) +* When clicking the button while the winder is in standby, it will start winding. * When long-pressing the button while it is winding, it will return to the home position and stop winding. * When long-pressing the button while it is waiting for the next cycle, it will stop winding. -* When pressing the buttong while it is waiting for the next cycle, it will start winding again immediately. +* When clicking the button while it is waiting for the next cycle, it will start winding again immediately. +* When double-clicking the button in any state, the LED will turn off if it was on, or on (in the appropriate status mode) if it was off. ## LED Status -* **Solid**: Standby -* **Blinking** (1s on, 0.5s off): Winding -* **Slow Breathing**: Waiting for next cycles -* **Fast Breathing**: Next cycle will start in <1 minute +* **Solid**: Standby / Manually Stopped +* **Slow Blinking** (1s on, 0.2s off): Winding +* **Fast Blinking** (0.1 on, 0.5s off): Stopping, returning to home position (when manually stopped) +* **Slow Breathing**: Paused, waiting for next cycles +* **Fast Breathing**: Paused, the next cycle will start in <1 minute ## Wiring Diagram -- GitLab