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
7
#include "config-mode.h"
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;
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);
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(
36
uint8_t num_config_steps,
37
void (*savefunc)(uint8_t step, uint8_t value)) {
39
static uint8_t config_step;
40
#ifdef USE_CONFIG_COLORS
41
static uint8_t orig_channel;
43
if (event == EV_enter_state) {
44
#if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
45
orig_channel = channel_mode;
49
// if button isn't held, configure first menu item
50
if (! button_last_state) {
52
push_state(number_entry_state, 0);
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);
68
if ((TICKS_PER_SECOND/10) == (arg % (TICKS_PER_SECOND*3/2))) {
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);
75
set_level(RAMP_SIZE * 3 / 8);
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);
83
set_level(RAMP_SIZE * 1 / 8);
86
// turn off when end of menu is reached
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);
99
push_state(number_entry_state, 0);
101
// exit after falling out of end of menu
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
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);
123
// eat all other events; don't pass any through to parent
124
return EVENT_HANDLED;
127
uint8_t number_entry_state(Event event, uint16_t arg) {
128
static uint8_t entry_step;
130
if (event == EV_enter_state) {
131
number_entry_value = 0;
133
set_level(0); // initial pause should be dark
136
// advance through the process:
138
// 1: "buzz" while counting clicks
140
else if (event == EV_tick) {
142
if (entry_step == 0) {
143
if (arg > TICKS_PER_SECOND/2) {
145
empty_event_sequence(); // reset tick counter to 0
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) {
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))
159
#ifdef CONFIG_WAITING_CHANNEL
160
set_chan_if(1, CONFIG_WAITING_CHANNEL);
162
set_level( (RAMP_SIZE/8)
166
// all done, save result and return to parent state
170
return EVENT_HANDLED;
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)
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;
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);
189
set_level(RAMP_SIZE/2); // flash briefly
190
return EVENT_HANDLED;
193
// eat all other events; don't pass any through to parent
194
return EVENT_HANDLED;