/* * 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) { }