~toykeeper/flashlight-firmware/fsm

« back to all changes in this revision

Viewing changes to ToyKeeper/spaghetti-monster/anduril/off-mode.c

  • Committer: Selene ToyKeeper
  • Date: 2023-11-04 15:09:10 UTC
  • mfrom: (483.1.175 anduril2)
  • Revision ID: bzr@toykeeper.net-20231104150910-ddd3afw4nhfvof2l
merged anduril2 branch -> fsm, with *years* of changes
(this also means this code is now Anduril 2 instead of Anduril 1)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// off-mode.c: "Off" mode for Anduril.
 
2
// Copyright (C) 2017-2023 Selene ToyKeeper
 
3
// SPDX-License-Identifier: GPL-3.0-or-later
 
4
 
 
5
#pragma once
 
6
 
 
7
#include "off-mode.h"
 
8
 
 
9
#ifdef USE_SUNSET_TIMER
 
10
#include "sunset-timer.h"
 
11
#endif
 
12
 
 
13
// set level smooth maybe
 
14
void off_state_set_level(uint8_t level);
 
15
 
 
16
 
 
17
uint8_t off_state(Event event, uint16_t arg) {
 
18
 
 
19
    // turn emitter off when entering state
 
20
    if (event == EV_enter_state) {
 
21
        // turn off
 
22
        off_state_set_level(0);
 
23
        #ifdef USE_SMOOTH_STEPS
 
24
            // don't go to sleep while animating
 
25
            arg |= smooth_steps_in_progress;
 
26
        #endif
 
27
        ticks_since_on = 0;
 
28
        #if NUM_CHANNEL_MODES > 1
 
29
            // reset to ramp mode's channel when light turns off
 
30
            channel_mode = cfg.channel_mode;
 
31
        #endif
 
32
        #ifdef USE_INDICATOR_LED
 
33
        // redundant, sleep tick does the same thing
 
34
        //indicator_led_update(cfg.indicator_led_mode & 0x03, 0);
 
35
        #elif defined(USE_AUX_RGB_LEDS)
 
36
        // redundant, sleep tick does the same thing
 
37
        //rgb_led_update(cfg.rgb_led_off_mode, 0);
 
38
        #endif
 
39
        #ifdef USE_SUNSET_TIMER
 
40
        sunset_timer = 0;  // needs a reset in case previous timer was aborted
 
41
        #endif
 
42
        // sleep while off  (lower power use)
 
43
        // (unless delay requested; give the ADC some time to catch up)
 
44
        if (! arg) { go_to_standby = 1; }
 
45
        return EVENT_HANDLED;
 
46
    }
 
47
 
 
48
    // go back to sleep eventually if we got bumped but didn't leave "off" state
 
49
    else if (event == EV_tick) {
 
50
        if (arg > HOLD_TIMEOUT
 
51
            #ifdef USE_SMOOTH_STEPS
 
52
                && (! smooth_steps_in_progress)
 
53
            #endif
 
54
            ) {
 
55
            go_to_standby = 1;
 
56
            #ifdef USE_INDICATOR_LED
 
57
            // redundant, sleep tick does the same thing
 
58
            //indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
 
59
            #elif defined(USE_AUX_RGB_LEDS)
 
60
            // redundant, sleep tick does the same thing
 
61
            //rgb_led_update(cfg.rgb_led_off_mode, arg);
 
62
            #endif
 
63
        }
 
64
        return EVENT_HANDLED;
 
65
    }
 
66
 
 
67
    #if defined(TICK_DURING_STANDBY)
 
68
    // blink the indicator LED, maybe
 
69
    else if (event == EV_sleep_tick) {
 
70
        if (ticks_since_on < 255) ticks_since_on ++;
 
71
        #ifdef USE_MANUAL_MEMORY_TIMER
 
72
        // reset to manual memory level when timer expires
 
73
        if (cfg.manual_memory &&
 
74
                (arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) {
 
75
            manual_memory_restore();
 
76
        }
 
77
        #endif
 
78
        #ifdef USE_INDICATOR_LED
 
79
        indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
 
80
        #elif defined(USE_AUX_RGB_LEDS)
 
81
        rgb_led_update(cfg.rgb_led_off_mode, arg);
 
82
        #endif
 
83
 
 
84
        #ifdef USE_AUTOLOCK
 
85
            // lock the light after being off for N minutes
 
86
            uint16_t ticks = cfg.autolock_time * SLEEP_TICKS_PER_MINUTE;
 
87
            if ((cfg.autolock_time > 0)  && (arg > ticks)) {
 
88
                set_state(lockout_state, 0);
 
89
            }
 
90
        #endif  // ifdef USE_AUTOLOCK
 
91
        return EVENT_HANDLED;
 
92
    }
 
93
    #endif
 
94
 
 
95
    #if (B_TIMING_ON == B_PRESS_T)
 
96
    // hold (initially): go to lowest level (floor), but allow abort for regular click
 
97
    else if (event == EV_click1_press) {
 
98
        off_state_set_level(nearest_level(1));
 
99
        return EVENT_HANDLED;
 
100
    }
 
101
    #endif  // B_TIMING_ON == B_PRESS_T
 
102
 
 
103
    // hold: go to lowest level
 
104
    else if (event == EV_click1_hold) {
 
105
        #if (B_TIMING_ON == B_PRESS_T)
 
106
        #ifdef MOON_TIMING_HINT
 
107
        if (arg == 0) {
 
108
            // let the user know they can let go now to stay at moon
 
109
            blip();
 
110
        } else
 
111
        #endif
 
112
        #else  // B_RELEASE_T or B_TIMEOUT_T
 
113
        off_state_set_level(nearest_level(1));
 
114
        #endif
 
115
        #ifdef USE_RAMP_AFTER_MOON_CONFIG
 
116
        if (cfg.dont_ramp_after_moon) {
 
117
            return EVENT_HANDLED;
 
118
        }
 
119
        #endif
 
120
        // don't start ramping immediately;
 
121
        // give the user time to release at moon level
 
122
        //if (arg >= HOLD_TIMEOUT) {  // smaller
 
123
        if (arg >= (!cfg.ramp_style) * HOLD_TIMEOUT) {  // more consistent
 
124
            set_state(steady_state, 1);
 
125
        }
 
126
        return EVENT_HANDLED;
 
127
    }
 
128
 
 
129
    // hold, release quickly: go to lowest level (floor)
 
130
    else if (event == EV_click1_hold_release) {
 
131
        set_state(steady_state, 1);
 
132
        return EVENT_HANDLED;
 
133
    }
 
134
 
 
135
    #if (B_TIMING_ON != B_TIMEOUT_T)
 
136
    // 1 click (before timeout): go to memorized level, but allow abort for double click
 
137
    else if (event == EV_click1_release) {
 
138
        #if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER)
 
139
            // this clause probably isn't used by any configs any more
 
140
            // but is included just in case someone configures it this way
 
141
            if (cfg.manual_memory) {
 
142
                manual_memory_restore();
 
143
            }
 
144
        #endif
 
145
        off_state_set_level(nearest_level(memorized_level));
 
146
        return EVENT_HANDLED;
 
147
    }
 
148
    #endif  // if (B_TIMING_ON != B_TIMEOUT_T)
 
149
 
 
150
    // 1 click: regular mode
 
151
    else if (event == EV_1click) {
 
152
        #if (B_TIMING_ON != B_TIMEOUT_T)
 
153
        set_state(steady_state, memorized_level);
 
154
        #else
 
155
        // FIXME: B_TIMEOUT_T breaks manual_memory and manual_memory_timer
 
156
        //        (need to duplicate manual mem logic here, probably)
 
157
        set_state(steady_state, memorized_level);
 
158
        #endif
 
159
        return EVENT_HANDLED;
 
160
    }
 
161
 
 
162
    // click, hold: momentary at ceiling or turbo
 
163
    else if (event == EV_click2_hold) {
 
164
        ticks_since_on = 0;  // momentary turbo is definitely "on"
 
165
        uint8_t turbo_level;  // how bright is "turbo"?
 
166
 
 
167
        #if defined(USE_2C_STYLE_CONFIG)  // user can choose 2C behavior
 
168
            uint8_t style_2c = cfg.ramp_2c_style;
 
169
            #ifdef USE_SIMPLE_UI
 
170
            // simple UI has its own turbo config
 
171
            if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple;
 
172
            #endif
 
173
            // 0  = ceiling
 
174
            // 1+ = full power
 
175
            if (0 == style_2c) turbo_level = nearest_level(MAX_LEVEL);
 
176
            else turbo_level = MAX_LEVEL;
 
177
        #else
 
178
            // simple UI: ceiling
 
179
            // full UI: full power
 
180
            #ifdef USE_SIMPLE_UI
 
181
            if (cfg.simple_ui_active) turbo_level = nearest_level(MAX_LEVEL);
 
182
            else
 
183
            #endif
 
184
            turbo_level = MAX_LEVEL;
 
185
        #endif
 
186
 
 
187
        off_state_set_level(turbo_level);
 
188
        return EVENT_HANDLED;
 
189
    }
 
190
    else if (event == EV_click2_hold_release) {
 
191
        off_state_set_level(0);
 
192
        return EVENT_HANDLED;
 
193
    }
 
194
 
 
195
    // 2 clicks: highest mode (ceiling)
 
196
    else if (event == EV_2clicks) {
 
197
        set_state(steady_state, MAX_LEVEL);
 
198
        return EVENT_HANDLED;
 
199
    }
 
200
 
 
201
    // 3 clicks (initial press): off, to prep for later events
 
202
    else if (event == EV_click3_press) {
 
203
        #ifdef USE_SMOOTH_STEPS
 
204
            // immediately cancel any animations in progress
 
205
            smooth_steps_in_progress = 0;
 
206
        #endif
 
207
        off_state_set_level(0);
 
208
        return EVENT_HANDLED;
 
209
    }
 
210
 
 
211
    #ifdef USE_BATTCHECK
 
212
    // 3 clicks: battcheck mode / blinky mode group 1
 
213
    else if (event == EV_3clicks) {
 
214
        set_state(battcheck_state, 0);
 
215
        return EVENT_HANDLED;
 
216
    }
 
217
    #endif
 
218
 
 
219
    #ifdef USE_LOCKOUT_MODE
 
220
    // 4 clicks: soft lockout
 
221
    else if (event == EV_4clicks) {
 
222
        blink_once();
 
223
        set_state(lockout_state, 0);
 
224
        return EVENT_HANDLED;
 
225
    }
 
226
    #endif
 
227
 
 
228
    #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET)
 
229
    // 13 clicks and hold the last click: invoke factory reset (reboot)
 
230
    else if (event == EV_click13_hold) {
 
231
        reboot();
 
232
        return EVENT_HANDLED;
 
233
    }
 
234
    #endif
 
235
 
 
236
    #ifdef USE_VERSION_CHECK
 
237
    // 15+ clicks: show the version number
 
238
    else if (event == EV_15clicks) {
 
239
        set_state(version_check_state, 0);
 
240
        return EVENT_HANDLED;
 
241
    }
 
242
    #endif
 
243
 
 
244
    #ifdef USE_SIMPLE_UI
 
245
    // 10 clicks, but hold last click: turn simple UI off (or configure it)
 
246
    else if ((event == EV_click10_hold) && (!arg)) {
 
247
        if (cfg.simple_ui_active) {  // turn off simple UI
 
248
            blink_once();
 
249
            cfg.simple_ui_active = 0;
 
250
            save_config();
 
251
        }
 
252
        else {  // configure simple UI ramp
 
253
            push_state(simple_ui_config_state, 0);
 
254
        }
 
255
        return EVENT_HANDLED;
 
256
    }
 
257
 
 
258
    ////////// Every action below here is blocked in the (non-Extended) Simple UI //////////
 
259
 
 
260
    #ifndef USE_EXTENDED_SIMPLE_UI
 
261
    if (cfg.simple_ui_active) {
 
262
        return EVENT_NOT_HANDLED;
 
263
    }
 
264
    #endif  // ifndef USE_EXTENDED_SIMPLE_UI
 
265
    #endif  // ifdef USE_SIMPLE_UI
 
266
 
 
267
    // click, click, long-click: strobe mode
 
268
    #ifdef USE_STROBE_STATE
 
269
    else if (event == EV_click3_hold) {
 
270
        set_state(strobe_state, 0);
 
271
        return EVENT_HANDLED;
 
272
    }
 
273
    #elif defined(USE_BORING_STROBE_STATE)
 
274
    else if (event == EV_click3_hold) {
 
275
        set_state(boring_strobe_state, 0);
 
276
        return EVENT_HANDLED;
 
277
    }
 
278
    #endif
 
279
 
 
280
    #ifdef USE_INDICATOR_LED
 
281
    // 7 clicks: change indicator LED mode
 
282
    else if (event == EV_7clicks) {
 
283
        uint8_t mode = (cfg.indicator_led_mode & 3) + 1;
 
284
        #ifdef TICK_DURING_STANDBY
 
285
        mode = mode & 3;
 
286
        #else
 
287
        mode = mode % 3;
 
288
        #endif
 
289
        #ifdef INDICATOR_LED_SKIP_LOW
 
290
        if (mode == 1) { mode ++; }
 
291
        #endif
 
292
        cfg.indicator_led_mode = (cfg.indicator_led_mode & 0b11111100) | mode;
 
293
        // redundant, sleep tick does the same thing
 
294
        //indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
 
295
        save_config();
 
296
        return EVENT_HANDLED;
 
297
    }
 
298
    #elif defined(USE_AUX_RGB_LEDS)
 
299
    // 7 clicks: change RGB aux LED pattern
 
300
    else if (event == EV_7clicks) {
 
301
        uint8_t mode = (cfg.rgb_led_off_mode >> 4) + 1;
 
302
        mode = mode % RGB_LED_NUM_PATTERNS;
 
303
        cfg.rgb_led_off_mode = (mode << 4) | (cfg.rgb_led_off_mode & 0x0f);
 
304
        rgb_led_update(cfg.rgb_led_off_mode, 0);
 
305
        save_config();
 
306
        blink_once();
 
307
        return EVENT_HANDLED;
 
308
    }
 
309
    // 7 clicks (hold last): change RGB aux LED color
 
310
    else if (event == EV_click7_hold) {
 
311
        setting_rgb_mode_now = 1;
 
312
        if (0 == (arg & 0x3f)) {
 
313
            uint8_t mode = (cfg.rgb_led_off_mode & 0x0f) + 1;
 
314
            mode = mode % RGB_LED_NUM_COLORS;
 
315
            cfg.rgb_led_off_mode = mode | (cfg.rgb_led_off_mode & 0xf0);
 
316
            //save_config();
 
317
        }
 
318
        rgb_led_update(cfg.rgb_led_off_mode, arg);
 
319
        return EVENT_HANDLED;
 
320
    }
 
321
    else if (event == EV_click7_hold_release) {
 
322
        setting_rgb_mode_now = 0;
 
323
        save_config();
 
324
        return EVENT_HANDLED;
 
325
    }
 
326
    #endif  // end 7 clicks
 
327
 
 
328
    ////////// Every action below here is blocked in the Extended Simple UI //////////
 
329
 
 
330
    #ifdef USE_SIMPLE_UI
 
331
    #ifdef USE_EXTENDED_SIMPLE_UI
 
332
    if (cfg.simple_ui_active) {
 
333
        return EVENT_NOT_HANDLED;
 
334
    }
 
335
    #endif  // ifdef USE_EXTENDED_SIMPLE_UI
 
336
 
 
337
    // 10 clicks: enable simple UI
 
338
    else if (event == EV_10clicks) {
 
339
        blink_once();
 
340
        cfg.simple_ui_active = 1;
 
341
        save_config();
 
342
        return EVENT_HANDLED;
 
343
    }
 
344
    #endif  // ifdef USE_SIMPLE_UI
 
345
 
 
346
    #ifdef USE_MOMENTARY_MODE
 
347
    // 5 clicks: momentary mode
 
348
    else if (event == EV_5clicks) {
 
349
        blink_once();
 
350
        set_state(momentary_state, 0);
 
351
        return EVENT_HANDLED;
 
352
    }
 
353
    #endif
 
354
 
 
355
    #ifdef USE_TACTICAL_MODE
 
356
    // 6 clicks: tactical mode
 
357
    else if (event == EV_6clicks) {
 
358
        blink_once();
 
359
        set_state(tactical_state, 0);
 
360
        return EVENT_HANDLED;
 
361
    }
 
362
    #endif
 
363
 
 
364
    #ifdef USE_GLOBALS_CONFIG
 
365
    // 9 clicks, but hold last click: configure misc global settings
 
366
    else if ((event == EV_click9_hold) && (!arg)) {
 
367
        push_state(globals_config_state, 0);
 
368
        return EVENT_HANDLED;
 
369
    }
 
370
    #endif
 
371
 
 
372
    return EVENT_NOT_HANDLED;
 
373
}
 
374
 
 
375
 
 
376
void off_state_set_level(uint8_t level) {
 
377
    // this pattern gets used a few times, so reduce duplication
 
378
    #ifdef USE_SMOOTH_STEPS
 
379
        if (cfg.smooth_steps_style) set_level_smooth(level, 8);
 
380
        else
 
381
    #endif
 
382
    set_level(level);
 
383
}
 
384