~toykeeper/flashlight-firmware/fsm

« back to all changes in this revision

Viewing changes to ToyKeeper/spaghetti-monster/anduril/config-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
// config-mode.c: Config mode base 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 "config-mode.h"
 
8
 
 
9
// general helper function for config modes
 
10
uint8_t number_entry_state(Event event, uint16_t arg);
 
11
// return value from number_entry_state()
 
12
volatile uint8_t number_entry_value;
 
13
 
 
14
 
 
15
#if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
 
16
// TODO: promote this to fsm-channels.c ?
 
17
void set_chan_if(bool cond, uint8_t chan) {
 
18
    if ((cond) && (chan != channel_mode))
 
19
        set_channel_mode(chan);
 
20
}
 
21
#endif
 
22
 
 
23
// allow the user to set a new value for a config option
 
24
// can be called two ways:
 
25
//   - with a "click" action: Configures first menu item only.
 
26
//   - with a "hold" action: Sets user select a menu item and then
 
27
//     choose a new value for it.  User should hold button until light
 
28
//     blinks N times, to choose menu item N.  Then let go, and light
 
29
//     should give a buzzing prompt to enter a number.  Click N times
 
30
//     at the prompt to set the new value to N.
 
31
// after completing this process, config state calls the savefunc callback
 
32
// and then returns to caller/parent state
 
33
uint8_t config_state_base(
 
34
        Event event,
 
35
        uint16_t arg,
 
36
        uint8_t num_config_steps,
 
37
        void (*savefunc)(uint8_t step, uint8_t value)) {
 
38
 
 
39
    static uint8_t config_step;
 
40
    #ifdef USE_CONFIG_COLORS
 
41
    static uint8_t orig_channel;
 
42
    #endif
 
43
    if (event == EV_enter_state) {
 
44
        #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
 
45
        orig_channel = channel_mode;
 
46
        #endif
 
47
        config_step = 0;
 
48
        set_level(0);
 
49
        // if button isn't held, configure first menu item
 
50
        if (! button_last_state) {
 
51
            config_step ++;
 
52
            push_state(number_entry_state, 0);
 
53
        }
 
54
    }
 
55
 
 
56
    // if initial "hold" event still active
 
57
    // blink while holding to indicate option number
 
58
    #define B_CLICK_FLAGS      (B_CLICK|B_HOLD|B_PRESS|B_RELEASE|B_TIMEOUT)
 
59
    #define B_ANY_HOLD         (B_CLICK|B_HOLD|B_PRESS)
 
60
    #define B_ANY_HOLD_RELEASE (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT)
 
61
    else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD) {
 
62
        if (config_step <= num_config_steps) {
 
63
            #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
 
64
                uint8_t chan = config_step - 1;
 
65
                if (chan < NUM_CHANNEL_MODES)
 
66
                    set_chan_if(config_color_per_step, chan);
 
67
            #endif
 
68
            if ((TICKS_PER_SECOND/10) == (arg % (TICKS_PER_SECOND*3/2))) {
 
69
                config_step ++;
 
70
                // blink when config step advances
 
71
                if (config_step <= num_config_steps) {
 
72
                    #ifdef CONFIG_BLINK_CHANNEL
 
73
                    set_chan_if(!config_color_per_step, CONFIG_BLINK_CHANNEL);
 
74
                    #endif
 
75
                    set_level(RAMP_SIZE * 3 / 8);
 
76
                }
 
77
            }
 
78
            else {
 
79
                // stay on at a low level to indicate menu is active
 
80
                #ifdef CONFIG_WAITING_CHANNEL
 
81
                set_chan_if(!config_color_per_step, CONFIG_WAITING_CHANNEL);
 
82
                #endif
 
83
                set_level(RAMP_SIZE * 1 / 8);
 
84
            }
 
85
        } else {
 
86
            // turn off when end of menu is reached
 
87
            set_level(0);
 
88
        }
 
89
    }
 
90
 
 
91
    // button release: activate number entry for one menu item
 
92
    else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD_RELEASE) {
 
93
        // ask the user for a number, if they selected a menu item
 
94
        if (config_step && config_step <= num_config_steps) {
 
95
            #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
 
96
                // put the colors back how they were
 
97
                set_channel_mode(orig_channel);
 
98
            #endif
 
99
            push_state(number_entry_state, 0);
 
100
        }
 
101
        // exit after falling out of end of menu
 
102
        else {
 
103
            pop_state();
 
104
        }
 
105
    }
 
106
 
 
107
    // an option was set (return from number_entry_state)
 
108
    else if (event == EV_reenter_state) {
 
109
        // process value with parent's callback
 
110
        savefunc(config_step, number_entry_value);
 
111
        // make changes persist in eeprom
 
112
        save_config();
 
113
        pop_state();
 
114
    }
 
115
 
 
116
    #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
 
117
    else if (event == EV_leave_state) {
 
118
        // put the colors back how they were
 
119
        set_channel_mode(orig_channel);
 
120
    }
 
121
    #endif
 
122
 
 
123
    // eat all other events; don't pass any through to parent
 
124
    return EVENT_HANDLED;
 
125
}
 
126
 
 
127
uint8_t number_entry_state(Event event, uint16_t arg) {
 
128
    static uint8_t entry_step;
 
129
 
 
130
    if (event == EV_enter_state) {
 
131
        number_entry_value = 0;
 
132
        entry_step = 0;
 
133
        set_level(0);  // initial pause should be dark
 
134
    }
 
135
 
 
136
    // advance through the process:
 
137
    // 0: wait a moment
 
138
    // 1: "buzz" while counting clicks
 
139
    // 2: save and exit
 
140
    else if (event == EV_tick) {
 
141
        // wait a moment
 
142
        if (entry_step == 0) {
 
143
            if (arg > TICKS_PER_SECOND/2) {
 
144
                entry_step ++;
 
145
                empty_event_sequence();  // reset tick counter to 0
 
146
            }
 
147
        }
 
148
        // buzz while waiting for a number to be entered
 
149
        else if (entry_step == 1) {
 
150
            // time out and exit after 3 seconds
 
151
            if (arg > TICKS_PER_SECOND*3) {
 
152
                entry_step ++;
 
153
                set_level(0);
 
154
            }
 
155
            // buzz for N seconds after last event
 
156
            // (flicker every other frame,
 
157
            //  except first frame (so we can see flashes after each click))
 
158
            else if (arg) {
 
159
                #ifdef CONFIG_WAITING_CHANNEL
 
160
                set_chan_if(1, CONFIG_WAITING_CHANNEL);
 
161
                #endif
 
162
                set_level( (RAMP_SIZE/8)
 
163
                           + ((arg&2)<<2) );
 
164
            }
 
165
        }
 
166
        // all done, save result and return to parent state
 
167
        else {
 
168
            pop_state();
 
169
        }
 
170
        return EVENT_HANDLED;
 
171
    }
 
172
 
 
173
    // count clicks: click = +1, hold = +10
 
174
    else if ((event == EV_click1_release)
 
175
            #ifdef USE_NUMBER_ENTRY_PLUS10
 
176
            || (event == EV_click1_hold_release)
 
177
            #endif
 
178
            ) {
 
179
        entry_step = 1;  // in case user clicked during initial delay
 
180
        #ifdef USE_NUMBER_ENTRY_PLUS10
 
181
            if (event == EV_click1_hold_release) number_entry_value += 10;
 
182
            else
 
183
        #endif
 
184
        number_entry_value ++;  // update the result
 
185
        empty_event_sequence();  // reset FSM's click count
 
186
        #ifdef CONFIG_BLINK_CHANNEL
 
187
        set_channel_mode(CONFIG_BLINK_CHANNEL);
 
188
        #endif
 
189
        set_level(RAMP_SIZE/2);  // flash briefly
 
190
        return EVENT_HANDLED;
 
191
    }
 
192
 
 
193
    // eat all other events; don't pass any through to parent
 
194
    return EVENT_HANDLED;
 
195
}
 
196