aboutsummaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorApexo2026-03-27 22:51:23 +0100
committerApexo2026-03-27 22:51:23 +0100
commit3ac85dfa1d09feac1f92f98bd9e5194b8185f1cd (patch)
tree1f556407df86c36850716a8518636756d1fe1b9f /ui
parentMerge branch 'wurkkos-ts26' into trunk (diff)
downloadanduril-simple-ui.tar.gz
anduril-simple-ui.tar.bz2
anduril-simple-ui.zip
simple UIsimple-ui
Diffstat (limited to 'ui')
-rw-r--r--ui/simple/simple.c210
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) { }