~toykeeper/flashlight-firmware/fsm

« back to all changes in this revision

Viewing changes to ToyKeeper/spaghetti-monster/anduril/channel-modes.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
// channel-modes.c: Multi-channel functions 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 "channel-modes.h"
 
8
 
 
9
uint8_t channel_mode_state(Event event, uint16_t arg) {
 
10
    #ifdef USE_CHANNEL_MODE_ARGS
 
11
    static int8_t tint_ramp_direction = 1;
 
12
    static uint8_t prev_tint = 0;
 
13
    // don't activate auto-tint modes unless the user hits the edge
 
14
    // and keeps pressing for a while
 
15
    static uint8_t past_edge_counter = 0;
 
16
    // bugfix: click-click-hold from off to strobes would invoke tint ramping
 
17
    // in addition to changing state...  so ignore any tint-ramp events which
 
18
    // don't look like they were meant to be here
 
19
    static uint8_t active = 0;
 
20
    uint8_t tint = cfg.channel_mode_args[channel_mode];
 
21
    #endif
 
22
 
 
23
    // it's possible that a light may need 3H but not 3C,
 
24
    // so try to detect if 3C is needed
 
25
    #if NUM_CHANNEL_MODES > 1
 
26
    // 3 clicks: next channel mode
 
27
    if (event == EV_3clicks) {
 
28
        uint8_t next = channel_mode;
 
29
        // go to next channel mode until we find one which is enabled
 
30
        // (and don't do any infinite loops if the user disabled them all)
 
31
        uint8_t count = 0;
 
32
        do {
 
33
            count ++;
 
34
            next = (next + 1) % NUM_CHANNEL_MODES;
 
35
        } while ((! channel_mode_enabled(next)) && count < NUM_CHANNEL_MODES);
 
36
        //} while ((! channel_modes_enabled[next]) && count < NUM_CHANNEL_MODES);
 
37
 
 
38
        // undo change if infinite loop detected (redundant?)
 
39
        //if (NUM_CHANNEL_MODES == count) next = channel_mode;
 
40
 
 
41
        // if mode hasn't changed, abort
 
42
        if (channel_mode == next)
 
43
            return EVENT_NOT_HANDLED;
 
44
 
 
45
        set_channel_mode(next);
 
46
 
 
47
        // remember after battery changes
 
48
        cfg.channel_mode = channel_mode;
 
49
        save_config();
 
50
        return EVENT_HANDLED;
 
51
    } else
 
52
    #endif  // if NUM_CHANNEL_MODES > 1
 
53
 
 
54
    #ifdef USE_CUSTOM_CHANNEL_3H_MODES
 
55
    // defer to mode-specific function if defined
 
56
    if (channel_3H_modes[channel_mode]) {
 
57
        StatePtr tint_func = channel_3H_modes[channel_mode];
 
58
        uint8_t err = tint_func(event, arg);
 
59
        if (EVENT_HANDLED == err) return EVENT_HANDLED;
 
60
        // else let the default handler run
 
61
    }
 
62
    #endif
 
63
    #ifdef USE_CHANNEL_MODE_ARGS
 
64
    #ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE
 
65
    // click, click, hold: change the current channel's arg (like tint)
 
66
    if (event == EV_click3_hold) {
 
67
        ///// adjust value from 0 to 255
 
68
        // reset at beginning of movement
 
69
        if (! arg) {
 
70
            active = 1;  // first frame means this is for us
 
71
            past_edge_counter = 0;  // doesn't start until user hits the edge
 
72
        }
 
73
        // ignore event if we weren't the ones who handled the first frame
 
74
        if (! active) return EVENT_NOT_HANDLED;
 
75
 
 
76
        #ifdef USE_STEPPED_TINT_RAMPING
 
77
            if ((tint_ramp_direction > 0 && tint < 255) ||
 
78
                (tint_ramp_direction < 0 && tint > 0)) {
 
79
                // ramp slower in stepped mode
 
80
                if (cfg.tint_ramp_style && (arg % HOLD_TIMEOUT != 0))
 
81
                    return EVENT_HANDLED;
 
82
 
 
83
                const uint8_t step_size = (cfg.tint_ramp_style < 2)
 
84
                                        ? 1 : 254 / (cfg.tint_ramp_style-1);
 
85
                tint = nearest_tint_value(
 
86
                          tint + ((int16_t)step_size * tint_ramp_direction)
 
87
                          );
 
88
            }
 
89
        #else  // smooth tint ramping only
 
90
            if ((tint_ramp_direction > 0) && (tint < 255)) { tint ++; }
 
91
            else
 
92
            if ((tint_ramp_direction < 0) && (tint >   0)) { tint --; }
 
93
        #endif  // ifdef USE_STEPPED_TINT_RAMPING
 
94
 
 
95
        // if tint change stalled, let user know we hit the edge
 
96
        else if (prev_tint == tint) {
 
97
            if (past_edge_counter == 0) blip();
 
98
            past_edge_counter = 1;
 
99
        }
 
100
        prev_tint = tint;
 
101
        cfg.channel_mode_args[channel_mode] = tint;
 
102
        set_level(actual_level);
 
103
        return EVENT_HANDLED;
 
104
    }
 
105
 
 
106
    // click, click, hold, release: reverse direction for next ramp
 
107
    else if (event == EV_click3_hold_release) {
 
108
        active = 0;  // ignore next hold if it wasn't meant for us
 
109
        // reverse
 
110
        tint_ramp_direction = -tint_ramp_direction;
 
111
        if (0 == tint) tint_ramp_direction = 1;
 
112
        else if (255 == tint) tint_ramp_direction = -1;
 
113
        // remember tint after battery change
 
114
        cfg.channel_mode_args[channel_mode] = tint;
 
115
        save_config();
 
116
        // bug?: for some reason, brightness can seemingly change
 
117
        // from 1/150 to 2/150 without this next line... not sure why
 
118
        set_level(actual_level);
 
119
        return EVENT_HANDLED;
 
120
    }
 
121
    #endif  // ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE
 
122
    #endif  // ifdef USE_CHANNEL_MODE_ARGS
 
123
 
 
124
    #if defined(USE_SIMPLE_UI)
 
125
    // remaining mappings aren't "simple", so stop here
 
126
    if (cfg.simple_ui_active) {
 
127
        return EVENT_NOT_HANDLED;
 
128
    }
 
129
    #endif
 
130
 
 
131
    #if NUM_CHANNEL_MODES > 1
 
132
    // channel toggle menu on ... 9H?
 
133
    else if (event == EV_click9_hold) {
 
134
        push_state(channel_mode_config_state, 0);
 
135
        return EVENT_HANDLED;
 
136
    }
 
137
    #endif
 
138
 
 
139
    return EVENT_NOT_HANDLED;
 
140
}
 
141
 
 
142
 
 
143
#if NUM_CHANNEL_MODES > 1
 
144
void channel_mode_config_save(uint8_t step, uint8_t value) {
 
145
    // 1 menu item per channel mode, to enable or disable that mode
 
146
    step --;  // step is 1-based, channel modes are 0-based
 
147
    if (value) channel_mode_enable(step);
 
148
    else channel_mode_disable(step);
 
149
}
 
150
 
 
151
uint8_t channel_mode_config_state(Event event, uint16_t arg) {
 
152
    uint8_t ret;
 
153
    // make config steps match channel modes
 
154
    config_color_per_step = true;
 
155
    // 1 menu item per channel mode, to enable or disable that mode
 
156
    ret = config_state_base(
 
157
        event, arg,
 
158
        NUM_CHANNEL_MODES,
 
159
        channel_mode_config_save
 
160
        );
 
161
    // no other menu needs this
 
162
    config_color_per_step = false;
 
163
    return ret;
 
164
}
 
165
#endif
 
166
 
 
167
 
 
168
#if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
 
169
uint8_t nearest_tint_value(const int16_t target) {
 
170
    // const symbols for more readable code, will be removed by the compiler
 
171
    const uint8_t tint_min = 0;
 
172
    const uint8_t tint_max = 255;
 
173
    const uint8_t tint_range = tint_max - tint_min;
 
174
 
 
175
    // only equal mix of both channels
 
176
    if (1 == cfg.tint_ramp_style) return (tint_min + tint_max) >> 1;
 
177
 
 
178
    if (target < tint_min) return tint_min;
 
179
    if (target > tint_max) return tint_max;
 
180
    if (0 == cfg.tint_ramp_style) return target;  // smooth ramping
 
181
 
 
182
    const uint8_t step_size = tint_range / (cfg.tint_ramp_style-1);
 
183
 
 
184
    uint8_t tint_result = tint_min;
 
185
    for (uint8_t i=0; i<cfg.tint_ramp_style; i++) {
 
186
        tint_result = tint_min
 
187
                    + (i * (uint16_t)tint_range / (cfg.tint_ramp_style-1));
 
188
        int16_t diff = target - tint_result;
 
189
        if (diff <= (step_size>>1)) return tint_result;
 
190
    }
 
191
    return tint_result;
 
192
}
 
193
#endif
 
194
 
 
195
#ifdef USE_CIRCULAR_TINT_3H
 
196
uint8_t circular_tint_3h(Event event, uint16_t arg) {
 
197
    static int8_t tint_ramp_direction = 1;
 
198
    // bugfix: click-click-hold from off to strobes would invoke tint ramping
 
199
    // in addition to changing state...  so ignore any tint-ramp events which
 
200
    // don't look like they were meant to be here
 
201
    static uint8_t active = 0;
 
202
    uint8_t tint = cfg.channel_mode_args[channel_mode];
 
203
 
 
204
    // click, click, hold: change the current channel's arg (like tint)
 
205
    if (event == EV_click3_hold) {
 
206
        ///// adjust value from 0 to 255 in a circle
 
207
        // reset at beginning of movement
 
208
        if (! arg) {
 
209
            active = 1;  // first frame means this is for us
 
210
        }
 
211
        // ignore event if we weren't the ones who handled the first frame
 
212
        if (! active) return EVENT_NOT_HANDLED;
 
213
 
 
214
        // smooth tint ramping only
 
215
        tint += tint_ramp_direction;
 
216
 
 
217
        cfg.channel_mode_args[channel_mode] = tint;
 
218
        set_level(actual_level);
 
219
        return EVENT_HANDLED;
 
220
    }
 
221
 
 
222
    // click, click, hold, release: reverse direction for next ramp
 
223
    else if (event == EV_click3_hold_release) {
 
224
        active = 0;  // ignore next hold if it wasn't meant for us
 
225
        // reverse
 
226
        tint_ramp_direction = -tint_ramp_direction;
 
227
        // remember tint after battery change
 
228
        save_config();
 
229
        // bug?: for some reason, brightness can seemingly change
 
230
        // from 1/150 to 2/150 without this next line... not sure why
 
231
        set_level(actual_level);
 
232
        return EVENT_HANDLED;
 
233
    }
 
234
 
 
235
    return EVENT_NOT_HANDLED;
 
236
}
 
237
#endif