diff options
| author | Apexo | 2026-03-27 22:51:23 +0100 |
|---|---|---|
| committer | Apexo | 2026-03-27 22:51:23 +0100 |
| commit | 3ac85dfa1d09feac1f92f98bd9e5194b8185f1cd (patch) | |
| tree | 1f556407df86c36850716a8518636756d1fe1b9f /ui/simple/simple.c | |
| parent | Merge branch 'wurkkos-ts26' into trunk (diff) | |
| download | anduril-simple-ui.tar.gz anduril-simple-ui.tar.bz2 anduril-simple-ui.zip | |
simple UIsimple-ui
Diffstat (limited to 'ui/simple/simple.c')
| -rw-r--r-- | ui/simple/simple.c | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/ui/simple/simple.c b/ui/simple/simple.c new file mode 100644 index 0000000..885f8a8 --- /dev/null +++ b/ui/simple/simple.c @@ -0,0 +1,210 @@ +/* + * simple.c: Yet another UI for the Emisar D3AA + * Copyright (C) 2026 git@apexo.de + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +/* + * States: + * - Off : all lights off, standby + * - Aux : aux RGB shows color-coded battery voltage + * - On : main LED on (one of 4 different brightness levels), + * aux RGB shows color-coded battery voltage + * + * Transitions: + * - Off + Click -> Aux + * - Aux + Click -> On + * - Aux + 5s -> Off + * - On + Click -> Aux + * - On + Hold -> cycle brightness levels, store on release + */ + +#include "arch/mcu.h" + +#include incfile(CFG_H) + +#ifdef HWDEF_H +#include incfile(HWDEF_H) +#endif + +#include "fsm/spaghetti-monster.h" + +#ifdef HWDEF_C +#include incfile(HWDEF_C) +#endif + +#define NUM_LEVELS 4 +#define CEIL SIMPLE_UI_CEIL + +static const uint8_t brightness_levels[NUM_LEVELS] = { + 1, + 1 * CEIL / 10, + 3 * CEIL / 10, + CEIL, +}; + +#define HOLD_TICKS_PER_LEVEL 47 // cycle time ca 0.75s (47/62 Hz) +#define AUX_TIMEOUT_TICKS 310 // ca. 5s + +uint8_t off_state(Event event, uint16_t arg); +uint8_t aux_state(Event event, uint16_t arg); +uint8_t on_state(Event event, uint16_t arg); + +uint8_t current_level_idx = 0; +uint8_t current_level = 0; +uint8_t thermal_limit = SIMPLE_UI_CEIL; + +static const uint8_t voltage_levels[] = { + 0, 0, // black + #ifdef DUAL_VOLTAGE_FLOOR + // NiMH + 9*dV, 0x01, // R + 10*dV, 0x05, // R+G + 11*dV, 0x04, // G + 12*dV, 0x14, // G+B + 13*dV, 0x10, // B + 14*dV, 0x11, // R + B + 16*dV, 0x15, // R+G+B + 20*dV, 0, // black + #endif + // li-ion + 29*dV, 0x01, // R + 33*dV, 0x05, // R+G + 35*dV, 0x04, // G + 37*dV, 0x14, // G+B + 39*dV, 0x10, // B + 41*dV, 0x11, // R + B + 44*dV, 0x15, // R+G+B + 255, 0x15, // R+G+B +}; + +static uint8_t voltage_color(void) { + uint8_t v = voltage; + if (v == 0) return 0; + v -= 1; + uint8_t i; + for (i = 0; v > voltage_levels[i]; i += 2) {} + return voltage_levels[i - 1]; +} + +void set_level_therm(uint8_t value, uint16_t thermal_headroom, uint16_t thermal_overshoot) { + if (current_level == thermal_limit && thermal_headroom > 0) { + if (thermal_headroom >= CEIL - thermal_limit) { + thermal_limit = CEIL; + } else { + thermal_limit += thermal_headroom; + } + } + + if (thermal_overshoot > 0) { + if (thermal_overshoot >= current_level) { + thermal_limit = 1; + } else { + thermal_limit = current_level - thermal_overshoot; + } + } + + current_level = value <= thermal_limit ? value : thermal_limit; + set_level(current_level); +} + +uint8_t off_state(Event event, uint16_t arg) { + if (event == EV_enter_state) { + set_level(0); + rgb_led_set(0); + button_led_set(0); + go_to_standby = 1; + return EVENT_HANDLED; + } + + if (event == EV_1click) { + set_state(aux_state, 0); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + +uint8_t aux_state(Event event, uint16_t arg) { + if (event == EV_enter_state) { + set_level(0); + rgb_led_set(voltage_color() << 1); + button_led_set(1); + return EVENT_HANDLED; + } + + if (event == EV_1click) { + set_state(on_state, 0); + return EVENT_HANDLED; + } + + if (event == EV_tick) { + if (arg >= AUX_TIMEOUT_TICKS) { + set_state(off_state, 0); + } + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + +uint8_t on_state(Event event, uint16_t arg) { + if (event == EV_enter_state) { + set_level_therm(brightness_levels[current_level_idx], 0, 0); + rgb_led_set(voltage_color() << 1); + button_led_set(2); + return EVENT_HANDLED; + } + + if (event == EV_tick) { + if ((arg & 63) == 0) { + rgb_led_set(voltage_color() << 1); + } + return EVENT_HANDLED; + } + + if (event == EV_temperature_high) { + set_level_therm(brightness_levels[current_level_idx], 0, arg); + return EVENT_HANDLED; + } + + if (event == EV_temperature_low) { + set_level_therm(brightness_levels[current_level_idx], arg, 0); + return EVENT_HANDLED; + } + + if (event == EV_1click) { + set_state(aux_state, 0); + return EVENT_HANDLED; + } + + if (event == EV_click1_hold) { + if (arg % HOLD_TICKS_PER_LEVEL == 0) { + current_level_idx = (current_level_idx + 1) % NUM_LEVELS; + set_level_therm(brightness_levels[current_level_idx], 0, 0); + } + return EVENT_HANDLED; + } + + if (event == EV_click1_hold_release) { + eeprom[0] = current_level_idx; + save_eeprom(); + return EVENT_HANDLED; + } + + return EVENT_NOT_HANDLED; +} + +void low_voltage(void) { + set_state(off_state, 0); +} + +void setup(void) { + if (load_eeprom()) { + if (eeprom[0] < NUM_LEVELS) + current_level_idx = eeprom[0]; + } + push_state(off_state, 0); +} + +void loop(void) { } |
