~toykeeper/flashlight-firmware/fsm

483.12.10 by Selene ToyKeeper
adapted smooth-sunset patch from SammysHP
1
// ramp-mode.c: Ramping functions for Anduril.
2
// Copyright (C) 2017-2023 Selene ToyKeeper
3
// SPDX-License-Identifier: GPL-3.0-or-later
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
4
483.12.10 by Selene ToyKeeper
adapted smooth-sunset patch from SammysHP
5
#pragma once
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
6
483.1.16 by Selene Scriven
renamed ramping.* -> ramp-mode.*
7
#include "ramp-mode.h"
483.1.30 by Selene Scriven
fixed bug: ramp mode acted strange if ramp level was changed after turning on sunset timer
8
483.1.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
9
#ifdef USE_SUNSET_TIMER
10
#include "sunset-timer.h"
11
#endif
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
12
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
13
#ifdef USE_SMOOTH_STEPS
14
#include "smooth-steps.h"
15
#endif
16
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
17
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
18
uint8_t steady_state(Event event, uint16_t arg) {
19
    static int8_t ramp_direction = 1;
20
    #if (B_TIMING_OFF == B_RELEASE_T)
21
    // if the user double clicks, we need to abort turning off,
22
    // and this stores the level to return to
23
    static uint8_t level_before_off = 0;
24
    #endif
25
483.12.96 by Selene ToyKeeper
added channel mode per strobe mode, and made FSM channel mode more flexible,
26
    #if NUM_CHANNEL_MODES > 1
27
        channel_mode = cfg.channel_mode;
28
    #endif
29
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
30
    // make sure ramp globals are correct...
31
    // ... but they already are; no need to do it here
32
    //ramp_update_config();
33
    //nearest_level(1);  // same effect, takes less space
34
35
    uint8_t mode_min = ramp_floor;
36
    uint8_t mode_max = ramp_ceil;
37
    uint8_t step_size;
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
38
    if (cfg.ramp_style) { step_size = ramp_discrete_step_size; }
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
39
    else { step_size = 1; }
40
483.7.5 by Selene Scriven
made 2C ceiling/turbo behavior configurable in Simple UI
41
    // how bright is "turbo"?
42
    uint8_t turbo_level;
43
    #if defined(USE_2C_STYLE_CONFIG)  // user can choose 2C behavior
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
44
        uint8_t style_2c = cfg.ramp_2c_style;
483.7.5 by Selene Scriven
made 2C ceiling/turbo behavior configurable in Simple UI
45
        #ifdef USE_SIMPLE_UI
46
        // simple UI has its own turbo config
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
47
        if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple;
483.7.5 by Selene Scriven
made 2C ceiling/turbo behavior configurable in Simple UI
48
        #endif
49
        // 0 = no turbo
50
        // 1 = Anduril 1 direct to turbo
51
        // 2 = Anduril 2 direct to ceiling, or turbo if already at ceiling
52
        if (0 == style_2c) turbo_level = mode_max;
53
        else if (1 == style_2c) turbo_level = MAX_LEVEL;
54
        else {
55
            if (memorized_level < mode_max) { turbo_level = mode_max; }
56
            else { turbo_level = MAX_LEVEL; }
57
        }
58
    #elif defined(USE_2C_MAX_TURBO)  // Anduril 1 style always
59
        // simple UI: to/from ceiling
60
        // full UI: to/from turbo (Anduril1 behavior)
61
        #ifdef USE_SIMPLE_UI
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
62
        if (cfg.simple_ui_active) turbo_level = mode_max;
483.7.5 by Selene Scriven
made 2C ceiling/turbo behavior configurable in Simple UI
63
        else
64
        #endif
65
        turbo_level = MAX_LEVEL;
66
    #else  // Anduril 2 style always
67
        // simple UI: to/from ceiling
68
        // full UI: to/from ceiling if mem < ceiling,
69
        //          or to/from turbo if mem >= ceiling
70
        if ((memorized_level < mode_max)
71
            #ifdef USE_SIMPLE_UI
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
72
            || cfg.simple_ui_active
483.7.5 by Selene Scriven
made 2C ceiling/turbo behavior configurable in Simple UI
73
            #endif
74
           ) { turbo_level = mode_max; }
75
        else { turbo_level = MAX_LEVEL; }
76
    #endif
77
483.1.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
78
    #ifdef USE_SUNSET_TIMER
79
    // handle the shutoff timer first
80
    uint8_t sunset_active = sunset_timer;  // save for comparison
81
    // clock tick
82
    sunset_timer_state(event, arg);
83
    // if the timer was just turned on
84
    if (sunset_timer  &&  (! sunset_active)) {
483.12.10 by Selene ToyKeeper
adapted smooth-sunset patch from SammysHP
85
        sunset_timer_orig_level = actual_level;
483.1.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
86
    }
87
    // if the timer just expired, shut off
88
    else if (sunset_active  &&  (! sunset_timer)) {
89
        set_state(off_state, 0);
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
90
        return EVENT_HANDLED;
483.1.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
91
    }
92
    #endif  // ifdef USE_SUNSET_TIMER
93
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
94
    // turn LED on when we first enter the mode
95
    if ((event == EV_enter_state) || (event == EV_reenter_state)) {
96
        #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE)
97
        momentary_mode = 0;  // 0 = ramping, 1 = strobes
98
        #endif
99
        // if we just got back from config mode, go back to memorized level
100
        if (event == EV_reenter_state) {
101
            arg = memorized_level;
102
        }
103
        // remember this level, unless it's moon or turbo
104
        if ((arg > mode_min) && (arg < mode_max))
105
            memorized_level = arg;
106
        // use the requested level even if not memorized
107
        arg = nearest_level(arg);
108
        set_level_and_therm_target(arg);
109
        ramp_direction = 1;
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
110
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
111
    }
112
    #if (B_TIMING_OFF == B_RELEASE_T)
113
    // 1 click (early): off, if configured for early response
114
    else if (event == EV_click1_release) {
115
        level_before_off = actual_level;
116
        set_level_and_therm_target(0);
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
117
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
118
    }
119
    // 2 clicks (early): abort turning off, if configured for early response
120
    else if (event == EV_click2_press) {
121
        set_level_and_therm_target(level_before_off);
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
122
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
123
    }
124
    #endif  // if (B_TIMING_OFF == B_RELEASE_T)
125
    // 1 click: off
126
    else if (event == EV_1click) {
127
        set_state(off_state, 0);
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
128
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
129
    }
130
    // 2 clicks: go to/from highest level
131
    else if (event == EV_2clicks) {
132
        if (actual_level < turbo_level) {
133
            set_level_and_therm_target(turbo_level);
134
        }
135
        else {
136
            set_level_and_therm_target(memorized_level);
137
        }
483.1.30 by Selene Scriven
fixed bug: ramp mode acted strange if ramp level was changed after turning on sunset timer
138
        #ifdef USE_SUNSET_TIMER
483.12.10 by Selene ToyKeeper
adapted smooth-sunset patch from SammysHP
139
        reset_sunset_timer();
483.1.30 by Selene Scriven
fixed bug: ramp mode acted strange if ramp level was changed after turning on sunset timer
140
        #endif
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
141
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
142
    }
483.1.44 by Selene Scriven
reduced rom 34 bytes by merging ramp-up code with ramp-down code
143
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
144
    #ifdef USE_LOCKOUT_MODE
145
    // 4 clicks: shortcut to lockout mode
146
    else if (event == EV_4clicks) {
147
        set_level(0);
148
        set_state(lockout_state, 0);
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
149
        return EVENT_HANDLED;
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
150
    }
151
    #endif
152
483.1.44 by Selene Scriven
reduced rom 34 bytes by merging ramp-up code with ramp-down code
153
    // hold: change brightness (brighter, dimmer)
154
    // click, hold: change brightness (dimmer)
155
    else if ((event == EV_click1_hold) || (event == EV_click2_hold)) {
483.12.128 by Selene ToyKeeper
fixed bug: smooth ramp from turbo down to ceiling caused flickering
156
        // ramp infrequently in stepped mode
157
        if (cfg.ramp_style && (arg % HOLD_TIMEOUT != 0))
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
158
            return EVENT_HANDLED;
483.7.1 by Selene Scriven
added runtime config option for smooth ramp speed
159
        #ifdef USE_RAMP_SPEED_CONFIG
483.12.128 by Selene ToyKeeper
fixed bug: smooth ramp from turbo down to ceiling caused flickering
160
            // ramp slower if user configured things that way
161
            if ((! cfg.ramp_style) && (arg % ramp_speed))
162
                return EVENT_HANDLED;
163
        #endif
164
        #ifdef USE_SMOOTH_STEPS
165
            // if a brightness transition is already happening,
166
            // don't interrupt it
167
            // (like 2C for full turbo then 1H to smooth ramp down
168
            //  ... without this clause, it flickers because it trips
169
            //  the "blink at ramp ceil" clause below, over and over)
170
            if (smooth_steps_in_progress) return EVENT_HANDLED;
483.7.1 by Selene Scriven
added runtime config option for smooth ramp speed
171
        #endif
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
172
        // fix ramp direction on first frame if necessary
173
        if (!arg) {
483.1.70 by Selene Scriven
fixed bug: ramp 2H at floor went up instead of staying at floor
174
            // click, hold should always go down if possible
175
            if (event == EV_click2_hold) { ramp_direction = -1; }
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
176
            // make it ramp down instead, if already at max
483.1.70 by Selene Scriven
fixed bug: ramp 2H at floor went up instead of staying at floor
177
            else if (actual_level >= mode_max) { ramp_direction = -1; }
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
178
            // make it ramp up if already at min
179
            // (off->hold->stepped_min->release causes this state)
180
            else if (actual_level <= mode_min) { ramp_direction = 1; }
181
        }
182
        // if the button is stuck, err on the side of safety and ramp down
483.7.1 by Selene Scriven
added runtime config option for smooth ramp speed
183
        else if ((arg > TICKS_PER_SECOND * 5
184
                    #ifdef USE_RAMP_SPEED_CONFIG
185
                    // FIXME: count from time actual_level hits mode_max,
186
                    //   not from beginning of button hold
187
                    * ramp_speed
188
                    #endif
189
                    ) && (actual_level >= mode_max)) {
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
190
            ramp_direction = -1;
191
        }
192
        #ifdef USE_LOCKOUT_MODE
193
        // if the button is still stuck, lock the light
483.7.1 by Selene Scriven
added runtime config option for smooth ramp speed
194
        else if ((arg > TICKS_PER_SECOND * 10
195
                    #ifdef USE_RAMP_SPEED_CONFIG
196
                    // FIXME: count from time actual_level hits mode_min,
197
                    //   not from beginning of button hold
198
                    * ramp_speed
199
                    #endif
200
                    ) && (actual_level <= mode_min)) {
483.1.44 by Selene Scriven
reduced rom 34 bytes by merging ramp-up code with ramp-down code
201
            blink_once();
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
202
            set_state(lockout_state, 0);
203
        }
204
        #endif
205
        memorized_level = nearest_level((int16_t)actual_level \
206
                          + (step_size * ramp_direction));
207
        #if defined(BLINK_AT_RAMP_CEIL) || defined(BLINK_AT_RAMP_MIDDLE)
208
        // only blink once for each threshold
483.12.128 by Selene ToyKeeper
fixed bug: smooth ramp from turbo down to ceiling caused flickering
209
        // FIXME: blinks at beginning of smooth_steps animation instead
210
        // of the end, so it should blink when actual_level reaches a
211
        // threshold, instead of when memorized_level does
212
        // (one possible fix is to just remove mid-ramp blinks entirely,
213
        //  and just blink only when it hits the top while going up)
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
214
        if ((memorized_level != actual_level) && (
215
                0  // for easier syntax below
216
                #ifdef BLINK_AT_RAMP_MIDDLE_1
217
                || (memorized_level == BLINK_AT_RAMP_MIDDLE_1)
218
                #endif
219
                #ifdef BLINK_AT_RAMP_MIDDLE_2
220
                || (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
221
                #endif
222
                #ifdef BLINK_AT_RAMP_CEIL
483.12.128 by Selene ToyKeeper
fixed bug: smooth ramp from turbo down to ceiling caused flickering
223
                // FIXME: only blink at top when going up, not down
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
224
                || (memorized_level == mode_max)
225
                #endif
483.1.44 by Selene Scriven
reduced rom 34 bytes by merging ramp-up code with ramp-down code
226
                #ifdef BLINK_AT_RAMP_FLOOR
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
227
                || (memorized_level == mode_min)
228
                #endif
229
                )) {
230
            blip();
231
        }
232
        #endif
233
        #if defined(BLINK_AT_STEPS)
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
234
        uint8_t foo = cfg.ramp_style;
235
        cfg.ramp_style = 1;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
236
        uint8_t nearest = nearest_level((int16_t)actual_level);
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
237
        cfg.ramp_style = foo;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
238
        // only blink once for each threshold
239
        if ((memorized_level != actual_level) &&
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
240
                    (cfg.ramp_style == 0) &&
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
241
                    (memorized_level == nearest)
242
                    )
243
        {
244
            blip();
245
        }
246
        #endif
247
        set_level_and_therm_target(memorized_level);
483.1.30 by Selene Scriven
fixed bug: ramp mode acted strange if ramp level was changed after turning on sunset timer
248
        #ifdef USE_SUNSET_TIMER
483.12.10 by Selene ToyKeeper
adapted smooth-sunset patch from SammysHP
249
        reset_sunset_timer();
483.1.30 by Selene Scriven
fixed bug: ramp mode acted strange if ramp level was changed after turning on sunset timer
250
        #endif
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
251
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
252
    }
253
    // reverse ramp direction on hold release
483.1.44 by Selene Scriven
reduced rom 34 bytes by merging ramp-up code with ramp-down code
254
    else if ((event == EV_click1_hold_release)
255
             || (event == EV_click2_hold_release)) {
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
256
        ramp_direction = -ramp_direction;
257
        #ifdef START_AT_MEMORIZED_LEVEL
258
        save_config_wl();
259
        #endif
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
260
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
261
    }
262
263
    else if (event == EV_tick) {
483.1.46 by Selene Scriven
added support for 1-step ramps in stepped ramp mode
264
        // un-reverse after 1 second
483.7.7 by Selene Scriven
reduced length of auto-reverse timing window from ~1000 ms to ~660ms, because it was too fast
265
        if (arg == AUTO_REVERSE_TIME) ramp_direction = 1;
483.1.46 by Selene Scriven
added support for 1-step ramps in stepped ramp mode
266
483.1.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
267
        #ifdef USE_SUNSET_TIMER
268
        // reduce output if shutoff timer is active
269
        if (sunset_timer) {
483.12.10 by Selene ToyKeeper
adapted smooth-sunset patch from SammysHP
270
            uint8_t dimmed_level = sunset_timer_orig_level * sunset_timer / sunset_timer_peak;
271
            uint8_t dimmed_level_next = sunset_timer_orig_level * (sunset_timer-1) / sunset_timer_peak;
272
            uint8_t dimmed_level_delta = dimmed_level - dimmed_level_next;
273
            dimmed_level -= dimmed_level_delta * (sunset_ticks/TICKS_PER_SECOND) / 60;
483.1.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
274
            if (dimmed_level < 1) dimmed_level = 1;
483.12.10 by Selene ToyKeeper
adapted smooth-sunset patch from SammysHP
275
483.1.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
276
            #ifdef USE_SET_LEVEL_GRADUALLY
277
            set_level_gradually(dimmed_level);
278
            target_level = dimmed_level;
279
            #else
280
            set_level_and_therm_target(dimmed_level);
281
            #endif
282
        }
483.1.46 by Selene Scriven
added support for 1-step ramps in stepped ramp mode
283
        #endif  // ifdef USE_SUNSET_TIMER
284
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
285
        #ifdef USE_SET_LEVEL_GRADUALLY
286
        int16_t diff = gradual_target - actual_level;
287
        static uint16_t ticks_since_adjust = 0;
288
        ticks_since_adjust++;
289
        if (diff) {
290
            uint16_t ticks_per_adjust = 256;
291
            if (diff < 0) {
292
                //diff = -diff;
293
                if (actual_level > THERM_FASTER_LEVEL) {
294
                    #ifdef THERM_HARD_TURBO_DROP
295
                    ticks_per_adjust >>= 2;
296
                    #endif
297
                    ticks_per_adjust >>= 2;
298
                }
299
            } else {
300
                // rise at half speed
301
                ticks_per_adjust <<= 1;
302
            }
303
            while (diff) {
304
                ticks_per_adjust >>= 1;
305
                //diff >>= 1;
306
                diff /= 2;  // because shifting produces weird behavior
307
            }
308
            if (ticks_since_adjust > ticks_per_adjust)
309
            {
310
                gradual_tick();
311
                ticks_since_adjust = 0;
312
            }
313
        }
314
        #endif  // ifdef USE_SET_LEVEL_GRADUALLY
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
315
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
316
    }
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
317
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
318
    #ifdef USE_THERMAL_REGULATION
319
    // overheating: drop by an amount proportional to how far we are above the ceiling
320
    else if (event == EV_temperature_high) {
321
        #if 0
322
        blip();
323
        #endif
324
        #ifdef THERM_HARD_TURBO_DROP
325
        //if (actual_level > THERM_FASTER_LEVEL) {
326
        if (actual_level == MAX_LEVEL) {
327
            #ifdef USE_SET_LEVEL_GRADUALLY
328
            set_level_gradually(THERM_FASTER_LEVEL);
329
            target_level = THERM_FASTER_LEVEL;
330
            #else
331
            set_level_and_therm_target(THERM_FASTER_LEVEL);
332
            #endif
333
        } else
334
        #endif
335
        if (actual_level > MIN_THERM_STEPDOWN) {
336
            int16_t stepdown = actual_level - arg;
337
            if (stepdown < MIN_THERM_STEPDOWN) stepdown = MIN_THERM_STEPDOWN;
338
            else if (stepdown > MAX_LEVEL) stepdown = MAX_LEVEL;
339
            #ifdef USE_SET_LEVEL_GRADUALLY
340
            set_level_gradually(stepdown);
341
            #else
342
            set_level(stepdown);
343
            #endif
344
        }
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
345
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
346
    }
347
    // underheating: increase slowly if we're lower than the target
348
    //               (proportional to how low we are)
349
    else if (event == EV_temperature_low) {
350
        #if 0
351
        blip();
352
        #endif
353
        if (actual_level < target_level) {
354
            //int16_t stepup = actual_level + (arg>>1);
355
            int16_t stepup = actual_level + arg;
356
            if (stepup > target_level) stepup = target_level;
357
            else if (stepup < MIN_THERM_STEPDOWN) stepup = MIN_THERM_STEPDOWN;
358
            #ifdef USE_SET_LEVEL_GRADUALLY
359
            set_level_gradually(stepup);
360
            #else
361
            set_level(stepup);
362
            #endif
363
        }
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
364
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
365
    }
366
    #ifdef USE_SET_LEVEL_GRADUALLY
367
    // temperature is within target window
368
    // (so stop trying to adjust output)
369
    else if (event == EV_temperature_okay) {
370
        // if we're still adjusting output...  stop after the current step
371
        if (gradual_target > actual_level)
372
            gradual_target = actual_level + 1;
373
        else if (gradual_target < actual_level)
374
            gradual_target = actual_level - 1;
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
375
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
376
    }
377
    #endif  // ifdef USE_SET_LEVEL_GRADUALLY
378
    #endif  // ifdef USE_THERMAL_REGULATION
379
380
    ////////// Every action below here is blocked in the simple UI //////////
483.3.14 by Gabriel Hart
Add config option to allow 3C smooth/stepped selection in Simple UI, add that option to the Sofirn configs
381
    // That is, unless we specifically want to enable 3C for smooth/stepped selection in Simple UI
382
    #if defined(USE_SIMPLE_UI) && !defined(USE_SIMPLE_UI_RAMPING_TOGGLE)
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
383
    if (cfg.simple_ui_active) {
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
384
        return EVENT_NOT_HANDLED;
385
    }
386
    #endif
387
388
    // 3 clicks: toggle smooth vs discrete ramping
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
389
    // (and/or 6 clicks when there are multiple channel modes)
390
    // (handle 3C here anyway, when all but 1 mode is disabled)
391
    else if ((event == EV_3clicks)
392
        #if NUM_CHANNEL_MODES > 1
393
             || (event == EV_6clicks)
394
        ) {
395
            // detect if > 1 channel mode is enabled,
396
            // and if so, fall through so channel mode code can handle it
397
            // otherwise, change the ramp style
398
            if (event == EV_3clicks) {
399
                uint8_t enabled = 0;
400
                for (uint8_t m=0; m<NUM_CHANNEL_MODES; m++)
401
                    enabled += channel_mode_enabled(m);
402
                if (enabled > 1)
403
                    return EVENT_NOT_HANDLED;
404
            }
405
        #else
406
        ) {
407
        #endif
408
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
409
        cfg.ramp_style = !cfg.ramp_style;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
410
        save_config();
411
        #ifdef START_AT_MEMORIZED_LEVEL
412
        save_config_wl();
413
        #endif
414
        blip();
415
        memorized_level = nearest_level(actual_level);
416
        set_level_and_therm_target(memorized_level);
483.1.30 by Selene Scriven
fixed bug: ramp mode acted strange if ramp level was changed after turning on sunset timer
417
        #ifdef USE_SUNSET_TIMER
483.12.10 by Selene ToyKeeper
adapted smooth-sunset patch from SammysHP
418
        reset_sunset_timer();
483.1.30 by Selene Scriven
fixed bug: ramp mode acted strange if ramp level was changed after turning on sunset timer
419
        #endif
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
420
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
421
    }
422
483.3.14 by Gabriel Hart
Add config option to allow 3C smooth/stepped selection in Simple UI, add that option to the Sofirn configs
423
    // If we allowed 3C in Simple UI, now block further actions
424
    #if defined(USE_SIMPLE_UI) && defined(USE_SIMPLE_UI_RAMPING_TOGGLE)
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
425
    if (cfg.simple_ui_active) {
483.3.14 by Gabriel Hart
Add config option to allow 3C smooth/stepped selection in Simple UI, add that option to the Sofirn configs
426
        return EVENT_NOT_HANDLED;
427
    }
428
    #endif
429
483.1.93 by Selene Scriven
added ramp 3H -> momentary turbo
430
    // 3H: momentary turbo (on lights with no tint ramping)
483.12.16 by Selene ToyKeeper
made "Ramp 3H" do momentary turbo if current channel mode has no args
431
    // (or 4H when tint ramping is available)
432
    else if ((event == EV_click3_hold)
433
            #ifdef USE_CHANNEL_MODE_ARGS
434
            || (event == EV_click4_hold)
435
            #endif
436
        ) {
437
        #ifdef USE_CHANNEL_MODE_ARGS
438
            // ramp tint if tint exists in this mode
439
            if ((event == EV_click3_hold)
483.12.96 by Selene ToyKeeper
added channel mode per strobe mode, and made FSM channel mode more flexible,
440
                && (channel_has_args(channel_mode)))
483.12.16 by Selene ToyKeeper
made "Ramp 3H" do momentary turbo if current channel mode has no args
441
                return EVENT_NOT_HANDLED;
442
        #endif
483.1.93 by Selene Scriven
added ramp 3H -> momentary turbo
443
        if (! arg) {  // first frame only, to allow thermal regulation to work
483.1.155 by Selene ToyKeeper
fixed Ramp 3H with Anduril 2 style turbo
444
            #ifdef USE_2C_STYLE_CONFIG
445
            uint8_t tl = style_2c ? MAX_LEVEL : turbo_level;
446
            set_level_and_therm_target(tl);
447
            #else
483.7.5 by Selene Scriven
made 2C ceiling/turbo behavior configurable in Simple UI
448
            set_level_and_therm_target(turbo_level);
483.1.155 by Selene ToyKeeper
fixed Ramp 3H with Anduril 2 style turbo
449
            #endif
483.1.93 by Selene Scriven
added ramp 3H -> momentary turbo
450
        }
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
451
        return EVENT_HANDLED;
483.1.93 by Selene Scriven
added ramp 3H -> momentary turbo
452
    }
483.12.16 by Selene ToyKeeper
made "Ramp 3H" do momentary turbo if current channel mode has no args
453
    else if ((event == EV_click3_hold_release)
454
            #ifdef USE_CHANNEL_MODE_ARGS
455
            || (event == EV_click4_hold_release)
456
            #endif
457
        ) {
458
        #ifdef USE_CHANNEL_MODE_ARGS
459
            // ramp tint if tint exists in this mode
460
            if ((event == EV_click3_hold_release)
483.12.96 by Selene ToyKeeper
added channel mode per strobe mode, and made FSM channel mode more flexible,
461
                && (channel_has_args(channel_mode)))
483.12.16 by Selene ToyKeeper
made "Ramp 3H" do momentary turbo if current channel mode has no args
462
                return EVENT_NOT_HANDLED;
463
        #endif
483.1.93 by Selene Scriven
added ramp 3H -> momentary turbo
464
        set_level_and_therm_target(memorized_level);
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
465
        return EVENT_HANDLED;
483.1.93 by Selene Scriven
added ramp 3H -> momentary turbo
466
    }
467
483.1.53 by Selene Scriven
many button remappings:
468
    #ifdef USE_MOMENTARY_MODE
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
469
    // 5 clicks: shortcut to momentary mode
483.1.53 by Selene Scriven
many button remappings:
470
    else if (event == EV_5clicks) {
471
        set_level(0);
472
        set_state(momentary_state, 0);
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
473
        return EVENT_HANDLED;
483.1.53 by Selene Scriven
many button remappings:
474
    }
475
    #endif
476
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
477
    #ifdef USE_RAMP_CONFIG
483.1.79 by Selene Scriven
changed menu style and moved menus from "click" events to "hold" events
478
    // 7H: configure this ramp mode
479
    else if (event == EV_click7_hold) {
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
480
        push_state(ramp_config_state, 0);
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
481
        return EVENT_HANDLED;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
482
    }
483
    #endif
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
484
485
    #ifdef USE_MANUAL_MEMORY
486
    else if (event == EV_10clicks) {
483.1.79 by Selene Scriven
changed menu style and moved menus from "click" events to "hold" events
487
        // turn on manual memory and save current brightness
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
488
        manual_memory_save();
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
489
        save_config();
490
        blink_once();
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
491
        return EVENT_HANDLED;
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
492
    }
493
    else if (event == EV_click10_hold) {
483.7.2 by Selene Scriven
added runtime option to select whether to ramp up after hold-from-off (default) or stay at floor
494
        #ifdef USE_RAMP_EXTRAS_CONFIG
495
        // let user configure a bunch of extra ramp options
496
        push_state(ramp_extras_config_state, 0);
483.1.75 by Selene Scriven
added hybrid memory option (a.k.a. manual memory timer)
497
        #else  // manual mem, but no timer
498
        // turn off manual memory; go back to automatic
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
499
        if (0 == arg) {
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
500
            cfg.manual_memory = 0;
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
501
            save_config();
502
            blink_once();
503
        }
483.1.75 by Selene Scriven
added hybrid memory option (a.k.a. manual memory timer)
504
        #endif
483.12.66 by Selene ToyKeeper
Removed references to Harry Potter,
505
        return EVENT_HANDLED;
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
506
    }
483.1.75 by Selene Scriven
added hybrid memory option (a.k.a. manual memory timer)
507
    #endif  // ifdef USE_MANUAL_MEMORY
483.1.56 by Selene Scriven
shuffled functions around to make 4C lockout and 3C battcheck again
508
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
509
    return EVENT_NOT_HANDLED;
510
}
511
512
513
#ifdef USE_RAMP_CONFIG
483.1.79 by Selene Scriven
changed menu style and moved menus from "click" events to "hold" events
514
void ramp_config_save(uint8_t step, uint8_t value) {
515
483.1.36 by Selene Scriven
made simple UI's ramp configurable, and fixed bug where it wouldn't remember if simple UI was enabled
516
    // 0 = smooth ramp, 1 = stepped ramp, 2 = simple UI's ramp
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
517
    uint8_t style = cfg.ramp_style;
483.1.108 by Selene Scriven
added missing ifdefs for compiling without USE_SIMPLE_UI
518
    #ifdef USE_SIMPLE_UI
483.1.36 by Selene Scriven
made simple UI's ramp configurable, and fixed bug where it wouldn't remember if simple UI was enabled
519
    if (current_state == simple_ui_config_state)  style = 2;
483.1.108 by Selene Scriven
added missing ifdefs for compiling without USE_SIMPLE_UI
520
    #endif
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
521
483.7.5 by Selene Scriven
made 2C ceiling/turbo behavior configurable in Simple UI
522
    #if defined(USE_SIMPLE_UI) && defined(USE_2C_STYLE_CONFIG)
523
    // simple UI config is weird...
524
    // has some ramp extras after floor/ceil/steps
525
    if (4 == step) {
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
526
        cfg.ramp_2c_style_simple = value;
483.7.5 by Selene Scriven
made 2C ceiling/turbo behavior configurable in Simple UI
527
    }
528
    else
529
    #endif
530
483.1.79 by Selene Scriven
changed menu style and moved menus from "click" events to "hold" events
531
    // save adjusted value to the correct slot
532
    if (value) {
483.1.81 by Selene Scriven
fixed bug: zero clicks in ceiling config set ceiling to nonsense value
533
        // ceiling value is inverted
534
        if (step == 2) value = MAX_LEVEL + 1 - value;
535
483.1.79 by Selene Scriven
changed menu style and moved menus from "click" events to "hold" events
536
        // which option are we configuring?
537
        // TODO? maybe rearrange definitions to avoid the need for this table
538
        //       (move all ramp values into a single array?)
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
539
        uint8_t *steps[] = { cfg.ramp_floors, cfg.ramp_ceils, cfg.ramp_stepss };
483.1.79 by Selene Scriven
changed menu style and moved menus from "click" events to "hold" events
540
        uint8_t *option;
541
        option = steps[step-1];
542
        option[style] = value;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
543
    }
544
}
545
546
uint8_t ramp_config_state(Event event, uint16_t arg) {
483.7.1 by Selene Scriven
added runtime config option for smooth ramp speed
547
    #ifdef USE_RAMP_SPEED_CONFIG
548
    const uint8_t num_config_steps = 3;
549
    #else
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
550
    uint8_t num_config_steps = cfg.ramp_style + 2;
483.7.1 by Selene Scriven
added runtime config option for smooth ramp speed
551
    #endif
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
552
    return config_state_base(event, arg,
553
                             num_config_steps, ramp_config_save);
554
}
483.1.36 by Selene Scriven
made simple UI's ramp configurable, and fixed bug where it wouldn't remember if simple UI was enabled
555
556
#ifdef USE_SIMPLE_UI
557
uint8_t simple_ui_config_state(Event event, uint16_t arg) {
483.7.5 by Selene Scriven
made 2C ceiling/turbo behavior configurable in Simple UI
558
    #if defined(USE_2C_STYLE_CONFIG)
559
    #define SIMPLE_UI_NUM_MENU_ITEMS 4
560
    #else
561
    #define SIMPLE_UI_NUM_MENU_ITEMS 3
562
    #endif
563
    return config_state_base(event, arg,
564
                             SIMPLE_UI_NUM_MENU_ITEMS,
565
                             ramp_config_save);
483.1.36 by Selene Scriven
made simple UI's ramp configurable, and fixed bug where it wouldn't remember if simple UI was enabled
566
}
567
#endif
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
568
#endif  // #ifdef USE_RAMP_CONFIG
569
483.7.2 by Selene Scriven
added runtime option to select whether to ramp up after hold-from-off (default) or stay at floor
570
#ifdef USE_RAMP_EXTRAS_CONFIG
571
void ramp_extras_config_save(uint8_t step, uint8_t value) {
483.1.79 by Selene Scriven
changed menu style and moved menus from "click" events to "hold" events
572
    // item 1: disable manual memory, go back to automatic
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
573
    if (manual_memory_config_step == step) {
574
        cfg.manual_memory = 0;
575
    }
483.7.2 by Selene Scriven
added runtime option to select whether to ramp up after hold-from-off (default) or stay at floor
576
577
    #ifdef USE_MANUAL_MEMORY_TIMER
483.1.79 by Selene Scriven
changed menu style and moved menus from "click" events to "hold" events
578
    // item 2: set manual memory timer duration
483.1.116 by Selene Scriven
document the actual maximum for manual memory timer (~140 minutes, not 255)
579
    // FIXME: should be limited to (65535 / SLEEP_TICKS_PER_MINUTE)
580
    //   to avoid overflows or impossibly long timeouts
581
    //   (by default, the effective limit is 145, but it allows up to 255)
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
582
    else if (manual_memory_timer_config_step == step) {
583
        cfg.manual_memory_timer = value;
584
    }
483.7.2 by Selene Scriven
added runtime option to select whether to ramp up after hold-from-off (default) or stay at floor
585
    #endif
586
483.7.3 by Selene Scriven
added runtime option to choose Anduril 1 or Anduril 2 double-click turbo style
587
    #ifdef USE_RAMP_AFTER_MOON_CONFIG
483.7.2 by Selene Scriven
added runtime option to select whether to ramp up after hold-from-off (default) or stay at floor
588
    // item 3: ramp up after hold-from-off for moon?
589
    // 0 = yes, ramp after moon
590
    // 1+ = no, stay at moon
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
591
    else if (dont_ramp_after_moon_config_step == step) {
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
592
        cfg.dont_ramp_after_moon = value;
483.7.2 by Selene Scriven
added runtime option to select whether to ramp up after hold-from-off (default) or stay at floor
593
    }
594
    #endif
483.7.3 by Selene Scriven
added runtime option to choose Anduril 1 or Anduril 2 double-click turbo style
595
596
    #ifdef USE_2C_STYLE_CONFIG
597
    // item 4: Anduril 1 2C turbo, or Anduril 2 2C ceiling?
598
    // 1 = Anduril 1, 2C turbo
599
    // 2+ = Anduril 2, 2C ceiling
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
600
    else if (ramp_2c_style_config_step == step) {
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
601
        cfg.ramp_2c_style = value;
483.7.3 by Selene Scriven
added runtime option to choose Anduril 1 or Anduril 2 double-click turbo style
602
    }
603
    #endif
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
604
605
    #ifdef USE_SMOOTH_STEPS
606
    else if (smooth_steps_style_config_step == step) {
607
        cfg.smooth_steps_style = value;
608
    }
609
    #endif
483.1.75 by Selene Scriven
added hybrid memory option (a.k.a. manual memory timer)
610
}
611
483.7.2 by Selene Scriven
added runtime option to select whether to ramp up after hold-from-off (default) or stay at floor
612
uint8_t ramp_extras_config_state(Event event, uint16_t arg) {
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
613
    return config_state_base(event, arg,
614
        ramp_extras_config_num_steps - 1,
615
        ramp_extras_config_save);
483.1.75 by Selene Scriven
added hybrid memory option (a.k.a. manual memory timer)
616
}
617
#endif
618
483.1.132 by Selene Scriven
made jump start level configurable at runtime, and made it activate in more places
619
#ifdef USE_GLOBALS_CONFIG
620
void globals_config_save(uint8_t step, uint8_t value) {
621
    if (0) {}
483.12.18 by Selene ToyKeeper
added stepped tint ramping
622
    #if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
623
    else if (step == tint_style_config_step) { cfg.tint_ramp_style = value; }
483.1.151 by Selene ToyKeeper
added runtime config to choose tint-ramping or tint-toggle
624
    #endif
483.1.133 by Selene Scriven
moved jump start into FSM so it'll be more universal and the app won't need special clauses
625
    #ifdef USE_JUMP_START
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
626
    else if (step == jump_start_config_step) { cfg.jump_start_level = value; }
483.1.132 by Selene Scriven
made jump start level configurable at runtime, and made it activate in more places
627
    #endif
628
}
629
630
uint8_t globals_config_state(Event event, uint16_t arg) {
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
631
    return config_state_base(event, arg,
632
        globals_config_num_steps - 1,
633
        globals_config_save);
483.1.132 by Selene Scriven
made jump start level configurable at runtime, and made it activate in more places
634
}
635
#endif
636
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
637
// find the ramp level closest to the target,
638
// using only the levels which are allowed in the current state
639
uint8_t nearest_level(int16_t target) {
483.1.83 by Selene Scriven
documentation improvements:
640
    // using int16_t here saves us a bunch of logic elsewhere,
641
    // by allowing us to correct for numbers < 0 or > 255 in one central place
642
643
    // ensure all globals are correct
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
644
    ramp_update_config();
645
646
    // bounds check
647
    uint8_t mode_min = ramp_floor;
648
    uint8_t mode_max = ramp_ceil;
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
649
    uint8_t num_steps = cfg.ramp_stepss[1
483.1.108 by Selene Scriven
added missing ifdefs for compiling without USE_SIMPLE_UI
650
    #ifdef USE_SIMPLE_UI
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
651
        + cfg.simple_ui_active
483.1.108 by Selene Scriven
added missing ifdefs for compiling without USE_SIMPLE_UI
652
    #endif
653
        ];
483.1.46 by Selene Scriven
added support for 1-step ramps in stepped ramp mode
654
    // special case for 1-step ramp... use halfway point between floor and ceiling
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
655
    if (cfg.ramp_style && (1 == num_steps)) {
483.1.46 by Selene Scriven
added support for 1-step ramps in stepped ramp mode
656
        uint8_t mid = (mode_max + mode_min) >> 1;
657
        return mid;
658
    }
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
659
    if (target < mode_min) return mode_min;
660
    if (target > mode_max) return mode_max;
661
    // the rest isn't relevant for smooth ramping
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
662
    if (! cfg.ramp_style) return target;
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
663
664
    uint8_t ramp_range = mode_max - mode_min;
665
    ramp_discrete_step_size = ramp_range / (num_steps-1);
666
    uint8_t this_level = mode_min;
667
668
    for(uint8_t i=0; i<num_steps; i++) {
669
        this_level = mode_min + (i * (uint16_t)ramp_range / (num_steps-1));
670
        int16_t diff = target - this_level;
671
        if (diff < 0) diff = -diff;
672
        if (diff <= (ramp_discrete_step_size>>1))
673
            return this_level;
674
    }
675
    return this_level;
676
}
677
678
// ensure ramp globals are correct
679
void ramp_update_config() {
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
680
    uint8_t which = cfg.ramp_style;
483.1.108 by Selene Scriven
added missing ifdefs for compiling without USE_SIMPLE_UI
681
    #ifdef USE_SIMPLE_UI
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
682
    if (cfg.simple_ui_active) { which = 2; }
483.1.108 by Selene Scriven
added missing ifdefs for compiling without USE_SIMPLE_UI
683
    #endif
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
684
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
685
    ramp_floor = cfg.ramp_floors[which];
686
    ramp_ceil  = cfg.ramp_ceils[which];
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
687
}
688
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
689
#if defined(USE_THERMAL_REGULATION) || defined(USE_SMOOTH_STEPS)
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
690
void set_level_and_therm_target(uint8_t level) {
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
691
    #ifdef USE_THERMAL_REGULATION
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
692
    target_level = level;
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
693
    #endif
694
    #ifdef USE_SMOOTH_STEPS
483.12.117 by Selene ToyKeeper
smooth steps: fixed a few corner cases
695
        // if adjusting by more than 1 ramp level,
696
        // animate the step change (if smooth steps enabled)
697
        uint8_t diff = (level > actual_level)
698
            ? (level - actual_level) : (actual_level - level);
483.12.107 by Selene ToyKeeper
fixed soft start animation when using smooth ramp on turbo
699
        if (smooth_steps_in_progress
483.12.117 by Selene ToyKeeper
smooth steps: fixed a few corner cases
700
            || (cfg.smooth_steps_style && (diff > 1)))
483.12.104 by Selene ToyKeeper
added "smooth steps" a.k.a. "soft start", to make brightness steps smoother
701
            set_level_smooth(level, 4);
702
        else
703
    #endif
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
704
    set_level(level);
705
}
483.1.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
706
#else
707
#define set_level_and_therm_target(level) set_level(level)
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
708
#endif
709
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
710
void manual_memory_restore() {
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
711
    memorized_level = cfg.manual_memory;
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
712
    #if NUM_CHANNEL_MODES > 1
483.12.96 by Selene ToyKeeper
added channel mode per strobe mode, and made FSM channel mode more flexible,
713
        channel_mode = cfg.channel_mode = cfg.manual_memory_channel_mode;
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
714
    #endif
715
    #ifdef USE_CHANNEL_MODE_ARGS
716
        for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
717
          cfg.channel_mode_args[i] = cfg.manual_memory_channel_args[i];
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
718
    #endif
719
}
720
721
void manual_memory_save() {
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
722
    cfg.manual_memory = actual_level;
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
723
    #if NUM_CHANNEL_MODES > 1
483.12.96 by Selene ToyKeeper
added channel mode per strobe mode, and made FSM channel mode more flexible,
724
        cfg.manual_memory_channel_mode = channel_mode;
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
725
    #endif
726
    #ifdef USE_CHANNEL_MODE_ARGS
727
        for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
483.12.13 by Selene ToyKeeper
reduced ROM by ~600 bytes by moving all eeprom config values to a "cfg" struct
728
          cfg.manual_memory_channel_args[i] = cfg.channel_mode_args[i];
483.12.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
729
    #endif
730
}
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
731
483.12.10 by Selene ToyKeeper
adapted smooth-sunset patch from SammysHP
732
#ifdef USE_SUNSET_TIMER
733
void reset_sunset_timer() {
734
    if (sunset_timer) {
735
        sunset_timer_orig_level = actual_level;
736
        sunset_timer_peak = sunset_timer;
737
        sunset_ticks = 0;
738
    }
739
}
483.1.11 by Selene Scriven
more progress on refactoring Anduril into separate files... nearly done with the initial split
740
#endif
741