From 8d2f442317fd3678213a14307f95d2f5b54c5623 Mon Sep 17 00:00:00 2001 From: Selene ToyKeeper Date: Sat, 19 Aug 2017 12:17:58 -0600 Subject: Renamed from RoundTable to SpaghettiMonster (FSM). --- RoundTable/momentary.c | 85 ------ RoundTable/round-table.c | 736 ----------------------------------------------- RoundTable/rt-ramping.h | 26 -- 3 files changed, 847 deletions(-) delete mode 100644 RoundTable/momentary.c delete mode 100644 RoundTable/round-table.c delete mode 100644 RoundTable/rt-ramping.h (limited to 'RoundTable') diff --git a/RoundTable/momentary.c b/RoundTable/momentary.c deleted file mode 100644 index fcf0780..0000000 --- a/RoundTable/momentary.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Momentary: Very simple example UI for RoundTable. - * Is intended to be the simplest possible RT e-switch UI. - * The light is on while the button is held; off otherwise. - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#define RT_EMISAR_D4_LAYOUT -#define USE_LVP -#define USE_DEBUG_BLINK -#include "round-table.c" - -volatile uint8_t brightness; -volatile uint8_t on_now; - -void light_on() { - on_now = 1; - PWM1_LVL = brightness; - PWM2_LVL = 0; -} - -void light_off() { - on_now = 0; - PWM1_LVL = 0; - PWM2_LVL = 0; -} - -uint8_t momentary_state(EventPtr event, uint16_t arg) { - - if (event == EV_click1_press) { - brightness = 255; - light_on(); - // don't attempt to parse multiple clicks - empty_event_sequence(); - return 0; - } - - else if (event == EV_release) { - light_off(); - // don't attempt to parse multiple clicks - empty_event_sequence(); - return 0; - } - - else if (event == EV_debug) { - //PWM1_LVL = arg&0xff; - DEBUG_FLASH; - return 0; - } - - // event not handled - return 1; -} - -// LVP / low-voltage protection -void low_voltage() { - debug_blink(3); - if (brightness > 0) { - brightness >>= 1; - if (on_now) light_on(); - } else { - light_off(); - standby_mode(); - } -} - -void setup() { - debug_blink(2); - - push_state(momentary_state); -} diff --git a/RoundTable/round-table.c b/RoundTable/round-table.c deleted file mode 100644 index b18228d..0000000 --- a/RoundTable/round-table.c +++ /dev/null @@ -1,736 +0,0 @@ -/* - * RoundTable: Generic foundation code for e-switch flashlights. - * Other possible names: - * - Mostly Harmless - * - FSM / SpaghettiMonster - * - ... - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include "tk-attiny.h" - -#include -#include -#include -#include -#include -#include -#include - -// typedefs -typedef PROGMEM const uint8_t Event; -typedef Event * EventPtr; -typedef uint8_t (*EventCallbackPtr)(EventPtr event, uint16_t arg); -typedef uint8_t EventCallback(EventPtr event, uint16_t arg); -typedef uint8_t State(EventPtr event, uint16_t arg); -typedef State * StatePtr; -typedef struct Emission { - EventPtr event; - uint16_t arg; -} Emission; - -volatile StatePtr current_state; -#define EV_MAX_LEN 16 -uint8_t current_event[EV_MAX_LEN]; -// at 0.016 ms per tick, 255 ticks = 4.08 s -// TODO: 16 bits? -static volatile uint8_t ticks_since_last_event = 0; - -#ifdef USE_LVP -// volts * 10 -#define VOLTAGE_LOW 30 -// MCU sees voltage 0.X volts lower than actual, add X to readings -#define VOLTAGE_FUDGE_FACTOR 2 -volatile uint8_t voltage; -void low_voltage(); -#endif -#ifdef USE_THERMAL_REGULATION -volatile int16_t temperature; -void low_temperature(); -void high_temperature(); -#endif - -#ifdef USE_DEBUG_BLINK -#define OWN_DELAY -#define USE_DELAY_4MS -#include "tk-delay.h" -#define DEBUG_FLASH PWM1_LVL = 64; delay_4ms(2); PWM1_LVL = 0; -void debug_blink(uint8_t num) { - for(; num>0; num--) { - PWM1_LVL = 32; - delay_4ms(100/4); - PWM1_LVL = 0; - delay_4ms(100/4); - } -} -#endif - -#define A_ENTER_STATE 1 -#define A_LEAVE_STATE 2 -#define A_TICK 3 -#define A_PRESS 4 -#define A_HOLD_START 5 -#define A_HOLD_TICK 6 -#define A_RELEASE 7 -#define A_RELEASE_TIMEOUT 8 -// TODO: add events for over/under-heat conditions (with parameter for severity) -#define A_OVERHEATING 9 -#define A_UNDERHEATING 10 -// TODO: add events for low voltage conditions -#define A_VOLTAGE_LOW 11 -//#define A_VOLTAGE_CRITICAL 12 -#define A_DEBUG 255 // test event for debugging - -// TODO: maybe compare events by number instead of pointer? -// (number = index in event types array) -// (comparison would use full event content, but send off index to callbacks) -// (saves space by using uint8_t instead of a pointer) -// (also eliminates the need to duplicate single-entry events like for voltage or timer tick) - -// Event types -Event EV_debug[] = { - A_DEBUG, - 0 } ; -Event EV_enter_state[] = { - A_ENTER_STATE, - 0 } ; -Event EV_leave_state[] = { - A_LEAVE_STATE, - 0 } ; -Event EV_tick[] = { - A_TICK, - 0 } ; -#ifdef USE_LVP -Event EV_voltage_low[] = { - A_VOLTAGE_LOW, - 0 } ; -#endif -#ifdef USE_THERMAL_REGULATION -Event EV_temperature_high[] = { - A_OVERHEATING, - 0 } ; -Event EV_temperature_low[] = { - A_UNDERHEATING, - 0 } ; -#endif -Event EV_click1_press[] = { - A_PRESS, - 0 }; -// shouldn't normally happen, but UI might reset event while button is down -// so a release with no recorded prior hold could be possible -Event EV_release[] = { - A_RELEASE, - 0 }; -Event EV_click1_release[] = { - A_PRESS, - A_RELEASE, - 0 }; -#define EV_1click EV_click1_complete -Event EV_click1_complete[] = { - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -#define EV_hold EV_click1_hold -// FIXME: Should holds use "start+tick" or just "tick" with a tick number? -// Or "start+tick" with a tick number? -Event EV_click1_hold[] = { - A_PRESS, - A_HOLD_START, - 0 }; -Event EV_click1_hold_release[] = { - A_PRESS, - A_HOLD_START, - A_RELEASE, - 0 }; -Event EV_click2_press[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - 0 }; -Event EV_click2_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - 0 }; -#define EV_2clicks EV_click2_complete -Event EV_click2_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -Event EV_click3_press[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - 0 }; -Event EV_click3_release[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - 0 }; -#define EV_3clicks EV_click3_complete -Event EV_click3_complete[] = { - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_PRESS, - A_RELEASE, - A_RELEASE_TIMEOUT, - 0 }; -// ... and so on - -// A list of event types for easy iteration -EventPtr event_sequences[] = { - EV_click1_press, - EV_release, - EV_click1_release, - EV_click1_complete, - EV_click1_hold, - EV_click1_hold_release, - EV_click2_press, - EV_click2_release, - EV_click2_complete, - EV_click3_press, - EV_click3_release, - EV_click3_complete, - // ... -}; - -#define events_match(a,b) compare_event_sequences(a,b) -// return 1 if (a == b), 0 otherwise -uint8_t compare_event_sequences(uint8_t *a, const uint8_t *b) { - for(uint8_t i=0; (i=0; i--) { - uint8_t err = state_stack[i](event, arg); - if (! err) return 0; - } - return 1; // event not handled -} - -void emit(EventPtr event, uint16_t arg) { - // add this event to the queue for later, - // so we won't use too much time during an interrupt - append_emission(event, arg); -} - -// Search the pre-defined event list for one matching what the user just did, -// and emit it if one was found. -void emit_current_event(uint16_t arg) { - //uint8_t err = 1; - for (uint8_t i=0; i<(sizeof(event_sequences)/sizeof(EventPtr)); i++) { - if (events_match(current_event, event_sequences[i])) { - //DEBUG_FLASH; - //err = emit(event_sequences[i], arg); - //return err; - emit(event_sequences[i], arg); - return; - } - } - //return err; -} - -void _set_state(StatePtr new_state) { - // call old state-exit hook (don't use stack) - if (current_state != NULL) current_state(EV_leave_state, 0); - // set new state - current_state = new_state; - // call new state-enter hook (don't use stack) - if (new_state != NULL) current_state(EV_enter_state, 0); -} - -int8_t push_state(StatePtr new_state) { - if (state_stack_len < STATE_STACK_SIZE) { - // TODO: call old state's exit hook? - // new hook for non-exit recursion into child? - state_stack[state_stack_len] = new_state; - state_stack_len ++; - _set_state(new_state); - return state_stack_len; - } else { - // TODO: um... how is a flashlight supposed to handle a recursion depth error? - return -1; - } -} - -StatePtr pop_state() { - // TODO: how to handle pop from empty stack? - StatePtr old_state = NULL; - StatePtr new_state = NULL; - if (state_stack_len > 0) { - state_stack_len --; - old_state = state_stack[state_stack_len]; - } - if (state_stack_len > 0) { - new_state = state_stack[state_stack_len-1]; - } - _set_state(new_state); - return old_state; -} - -uint8_t set_state(StatePtr new_state) { - // FIXME: this calls exit/enter hooks it shouldn't - pop_state(); - return push_state(new_state); -} - -// TODO? add events to a queue when inside an interrupt -// instead of calling the event functions directly? -// (then empty the queue in main loop?) - -// TODO? new delay() functions which handle queue consumption? -// TODO? new interruptible delay() functions? - - -//static volatile uint8_t button_was_pressed; -#define BP_SAMPLES 16 -uint8_t button_is_pressed() { - // debounce a little - uint8_t highcount = 0; - // measure for 16/64ths of a ms - for(uint8_t i=0; i (BP_SAMPLES/2)); - //button_was_pressed = result; - return result; -} - -//void button_change_interrupt() { -ISR(PCINT0_vect) { - - //DEBUG_FLASH; - - // something happened - //ticks_since_last_event = 0; - - // add event to current sequence - if (button_is_pressed()) { - push_event(A_PRESS); - } else { - push_event(A_RELEASE); - } - - // check if sequence matches any defined sequences - // if so, send event to current state callback - emit_current_event(0); -} - -// TODO: implement -ISR(WDT_vect) { - /* - // TODO? safety net for PCINT, in case it misses a press or release - uint8_t bp = button_is_pressed(); - if (bp != button_was_pressed) { - // TODO: handle missed button event - if (bp) { - push_event(A_PRESS); - } else { - push_event(A_RELEASE); - } - emit_current_event(0); - } - */ - - //timer ++; // Is this needed at all? - - /* - if (ticks_since_last_event & 0b00000111 ) { - DEBUG_FLASH; - } - */ - - //if (ticks_since_last_event < 0xff) ticks_since_last_event ++; - // increment, but loop from 255 back to 128 - ticks_since_last_event = (ticks_since_last_event + 1) \ - | (ticks_since_last_event & 0x80); - - //static uint8_t hold_ticks = 0; // TODO: 16 bits? - - // callback on each timer tick - emit(EV_tick, ticks_since_last_event); - - // if time since last event exceeds timeout, - // append timeout to current event sequence, then - // send event to current state callback - // //hold_event(ticks) - // //emit(EV_press_hold, hold_ticks); - // emit_current_event(hold_ticks); - // or - // //release_timeout() - // //emit(EV_press_release_timeout, 0); - // emit_current_event(0); - - #if defined(USE_LVP) || defined(USE_THERMAL_REGULATION) - // start a new ADC measurement every 4 ticks - static uint8_t adc_trigger = 0; - adc_trigger ++; - if (adc_trigger > 3) { - adc_trigger = 0; - ADCSRA |= (1 << ADSC) | (1 << ADIE); - } - #endif -} - -// TODO: implement? (or is it better done in main()?) -ISR(ADC_vect) { - static uint8_t adc_step = 0; - #ifdef USE_LVP - #ifdef USE_LVP_AVG - #define NUM_VOLTAGE_VALUES 4 - static int16_t voltage_values[NUM_VOLTAGE_VALUES]; - #endif - static uint8_t lvp_timer = 0; - static uint8_t lvp_lowpass = 0; - #define LVP_TIMER_START 50 // ticks between LVP warnings - #define LVP_LOWPASS_STRENGTH 4 - #endif - - #ifdef USE_THERMAL_REGULATION - #define NUM_THERMAL_VALUES 4 - #define ADC_STEPS 4 - static int16_t temperature_values[NUM_THERMAL_VALUES]; - #else - #define ADC_STEPS 2 - #endif - - uint16_t measurement = ADC; // latest 10-bit ADC reading - - adc_step = (adc_step + 1) & (ADC_STEPS-1); - - #ifdef USE_LVP - // voltage - if (adc_step == 1) { - #ifdef USE_LVP_AVG - // prime on first execution - if (voltage == 0) { - for(uint8_t i=0; i> 2; - - voltage = (uint16_t)(1.1*1024*10)/total + VOLTAGE_FUDGE_FACTOR; - } - #else // no USE_LVP_AVG - // calculate actual voltage: volts * 10 - // ADC = 1.1 * 1024 / volts - // volts = 1.1 * 1024 / ADC - voltage = (uint16_t)(1.1*1024*10)/measurement + VOLTAGE_FUDGE_FACTOR; - #endif - // if low, callback EV_voltage_low / EV_voltage_critical - // (but only if it has been more than N ticks since last call) - if (lvp_timer) { - lvp_timer --; - } else { // it has been long enough since the last warning - if (voltage < VOLTAGE_LOW) { - if (lvp_lowpass < LVP_LOWPASS_STRENGTH) { - lvp_lowpass ++; - } else { - // try to send out a warning - //uint8_t err = emit(EV_voltage_low, 0); - //uint8_t err = emit_now(EV_voltage_low, 0); - emit(EV_voltage_low, 0); - //if (!err) { - // on successful warning, reset counters - lvp_timer = LVP_TIMER_START; - lvp_lowpass = 0; - //} - } - } else { - // voltage not low? reset count - lvp_lowpass = 0; - } - } - } - #endif // ifdef USE_LVP - - // TODO: temperature - - // start another measurement for next time - #ifdef USE_THERMAL_REGULATION - #ifdef USE_LVP - if (adc_step < 2) ADMUX = ADMUX_VCC; - else ADMUX = ADMUX_THERM; - #else - ADMUX = ADMUX_THERM; - #endif - #else - #ifdef USE_LVP - ADMUX = ADMUX_VCC; - #endif - #endif -} - -inline void ADC_on() -{ - // read voltage on VCC by default - // disable digital input on VCC pin to reduce power consumption - //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin - // VCC / 1.1V reference - ADMUX = ADMUX_VCC; - // enable, start, prescale - ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL; -} - -inline void ADC_off() { - ADCSRA &= ~(1<= 1 - DDRB |= (1 << PWM1_PIN); - TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...) - TCCR0A = PHASE; - #elif PWM_CHANNELS >= 2 - DDRB |= (1 << PWM2_PIN); - #elif PWM_CHANNELS >= 3 - DDRB |= (1 << PWM3_PIN); - // Second PWM counter is ... weird - TCCR1 = _BV (CS10); - GTCCR = _BV (COM1B1) | _BV (PWM1B); - OCR1C = 255; // Set ceiling value to maximum - #elif PWM_CHANNELS == 4 - // FIXME: How exactly do we do PWM on channel 4? - DDRB |= (1 << PWM4_PIN); - #endif - - // TODO: turn on ADC? - // configure e-switch - PORTB = (1 << SWITCH_PIN); // e-switch is the only input - PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin - - // TODO: configure sleep mode - set_sleep_mode(SLEEP_MODE_PWR_DOWN); - - // Read config values and saved state - // restore_state(); // TODO - - // TODO: handle long press vs short press (or even medium press)? - - #ifdef USE_DEBUG_BLINK - //debug_blink(1); - #endif - - // all booted -- turn interrupts back on - PCINT_on(); - WDT_on(); - ADC_on(); - sei(); - - // fallback for handling a few things - push_state(default_state); - - // call recipe's setup - setup(); - - // main loop - while (1) { - // TODO: update e-switch press state? - // TODO: check voltage? - // TODO: check temperature? - // if event queue not empty, process and pop first item in queue? - if (emissions[0].event != NULL) { - emit_now(emissions[0].event, emissions[0].arg); - delete_first_emission(); - } - } -} diff --git a/RoundTable/rt-ramping.h b/RoundTable/rt-ramping.h deleted file mode 100644 index 61561c4..0000000 --- a/RoundTable/rt-ramping.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * rt-ramping.h: Ramping functions for Round Table. - * Handles 1- to 4-channel smooth ramping on a single LED. - * - * Copyright (C) 2017 Selene ToyKeeper - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -// TODO: ramp tables -// TODO: RAMP_SIZE / MAX_LVL -// TODO: actual_lvl -// TODO: target_lvl -// TODO: set_lvl -// TODO: set_lvl_smooth -- cgit v1.2.3