aboutsummaryrefslogtreecommitdiff
path: root/spaghetti-monster/spaghetti-monster.txt
diff options
context:
space:
mode:
authorSelene ToyKeeper2023-11-02 17:16:25 -0600
committerSelene ToyKeeper2023-11-02 17:16:25 -0600
commit7cb4fe0944b839f28dfd96a88a772cd6a8b58019 (patch)
tree8d3b203f1650edc28b1f67e1589e3bc870b33fa6 /spaghetti-monster/spaghetti-monster.txt
parentadded LICENSE (GPLv3) (diff)
downloadanduril-7cb4fe0944b839f28dfd96a88a772cd6a8b58019.tar.gz
anduril-7cb4fe0944b839f28dfd96a88a772cd6a8b58019.tar.bz2
anduril-7cb4fe0944b839f28dfd96a88a772cd6a8b58019.zip
reorganized project files (part 1)
(just moved files, didn't change the contents yet, and nothing will work without updating #includes and build scripts and stuff)
Diffstat (limited to 'spaghetti-monster/spaghetti-monster.txt')
-rw-r--r--spaghetti-monster/spaghetti-monster.txt325
1 files changed, 0 insertions, 325 deletions
diff --git a/spaghetti-monster/spaghetti-monster.txt b/spaghetti-monster/spaghetti-monster.txt
deleted file mode 100644
index 434e1bc..0000000
--- a/spaghetti-monster/spaghetti-monster.txt
+++ /dev/null
@@ -1,325 +0,0 @@
-Spaghetti Monster: A UI toolkit library for flashlights
--------------------------------------------------------
-
-This toolkit takes care of most of the obnoxious parts of dealing with
-tiny embedded chips and flashlight hardware, leaving you to focus on the
-interface and user-visible features.
-
-For a quick start, look at the example UIs provided to see how things
-are done. They are probably the most useful reference. However, other
-details can be found here or in the FSM source code.
-
-
-Why is it called Spaghetti Monster?
-
- This toolkit is a finite state machine, or FSM. Another thing FSM
- stands for is Flying Spaghetti Monster. Source code tends to weave
- into intricate knots like spaghetti, called spaghetti code,
- particularly when the code isn't using appropriate abstractions for
- the task it implements.
-
- Prior e-switch light code had a tendency to get pretty spaghetti-like,
- and it made the code difficult to write, understand, and modify. So I
- started from scratch and logically separated the hardware details from
- the UI. This effectively put the spaghetti monster in a box, put it
- on a leash, to make it behave and stay out of the way while we focus
- on the user interface.
-
- Also, it's just kind of a fun name. :)
-
-
-General concept:
-
- Spaghetti Monster (FSM) implements a stack-based finite state machine
- with an event-handling system.
-
- Each FSM program should have a setup() function, a loop() function,
- and at least one State:
-
- - The setup() function runs once each time power is connected.
-
- - The loop() function is called repeatedly whenever the system is
- otherwise idle. Put your long-running tasks here, preferably with
- consideration taken to allow for cooperative multitasking.
-
- - The States on the stack will be called whenever an event happens.
- States are called in top-to-bottom order until a state returns an
- "EVENT_HANDLED" signal. Only do quick tasks here.
-
-
-Finite State Machine:
-
- Each "State" is simply a callback function which handles events. It
- should return EVENT_HANDLED for each event type it does something
- with, or EVENT_NOT_HANDLED otherwise.
-
- Transitions between states typically involve mapping an Event to a new
- State, such as this:
-
- // 3 clicks: go to strobe modes
- else if (event == EV_3clicks) {
- set_state(strobe_state, 0);
- return EVENT_HANDLED;
- }
-
- It is strongly recommended that your State functions never do anything
- which takes more than a few milliseconds... and certainly not longer
- than 16ms. If you do this, the pending events may pile up to the
- point where new events get thrown away. So, do only quick tasks in
- the event handler, and do your longer-running tasks in the loop()
- function instead. Preferably with precautions taken to allow for
- cooperative multitasking.
-
- If your State function takes longer than one WDT tick (16ms) once in a
- while, the system won't break. Several events can be queued. But be
- sure not to do it very often.
-
- Several state management functions are provided:
-
- - set_state(new_state, arg): Replace the current state on the stack.
- Send 'arg' to the new state for its init event.
-
- - push_state(new_state, arg): Add a new state to the stack, leaving
- the current state below it. Send 'arg' to the new state for its
- init event.
-
- - pop_state(): Get rid of (and return) the top-most state. Re-enter
- the state below.
-
-
-Event types:
-
- Event types are defined in fsm-events.h. You may want to adjust these
- to fit your program, but the defaults are:
-
- State transitions:
-
- - EV_enter_state: Sent to each new State once when it goes onto
- the stack. The 'arg' is whatever you define it to be.
-
- - EV_leave_state: Sent to a state immediately before it is removed
- from the stack.
-
- - EV_reenter_state: If a State gets pushed on top of this one, and
- then it pops off, a re-enter Event happens. This should handle
- things like consuming the return value of a nested input handler
- State.
-
- Time passing:
-
- - EV_tick: This happens once per clock tick, which is 16ms or
- 62.5Hz by default. The 'arg' is the number of ticks since
- entering the state. When 'arg' exceeds 65535, it wraps around
- to 32768.
-
- - EV_sleep_tick: This happens every 0.5s during standby, if
- enabled at compile time. The 'arg' is the number of ticks since
- entering the state. When 'arg' exceeds 65535, it wraps around
- to 32768.
-
- LVP and thermal regulation:
-
- - EV_voltage_low: Sent whenever the input power drops below the
- VOLTAGE_LOW threshold. Minimum of VOLTAGE_WARNING_SECONDS
- between events.
-
- - EV_temperature_high: Sent whenever the MCU's projected temperature
- is higher than therm_ceil. Minimum of one second between events.
- The 'arg' indicates how far the temperature exceeds the limit.
-
- - EV_temperature_low: Sent whenever the MCU's projected temperature
- is lower than (therm_ceil - THERMAL_WINDOW_SIZE). Minimum of
- one second between events. The 'arg' indicates how far the
- temperature exceeds the limit.
-
- Button presses:
-
- Button events can be referred to either by pre-defined symbols, or
- by teasing out the flags manually. The structure of a button
- event is as follows:
-
- - Bit 7: 1 for button events, 0 otherwise.
-
- - Bit 6: 1 for a "timeout" event (signals the end of a
- sequence), or 0 otherwise.
-
- - Bit 5: 1 for a "hold" event, 0 otherwise. This flag is only
- necessary because, without it, it would be impossible to
- distinguish between "click, click, timeout" and "click, hold,
- release".
-
- - Bit 4: 1 if button is currently pressed, 0 otherwise. Button
- release events look just like button press events, except this
- is not set.
-
- - Bits 0,1,2,3: Counter for how many clicks there have been.
- The first click is 1, second is 2, and it goes up to 15 clicks
- in a row. Clicks after 15 are coded as 15.
-
- The pre-defined button event symbols are like the following:
-
- - EV_click1_press: The user pressed the button, but no time has
- passed since then.
-
- - EV_click1_release: The user pressed and released the button,
- but no time has passed since then.
-
- - EV_click1_complete: The user clicked the e-switch, released
- it, and enough time passed that no more clicks were detected.
- (a.k.a. EV_1click)
-
- - EV_click1_hold: The user pressed the button, and continued
- holding it long enough to count as a "hold" event. This event
- is sent once per timer tick as long as the button is held, and
- the 'arg' value indicates how many timer ticks since the
- button state went from 'press' to 'hold'.
-
- - EV_click1_hold_release: The button was released at the end of
- a "hold" event. This is the end of the input sequence,
- because no timeout period is used after a hold.
-
- It's worth noting that a "hold" event can only happen at the
- end of an input sequence, and the sequence will reset to empty
- after the hold is released.
-
- If the user pressed the button more than once, events follow the
- same pattern. These are the same as above, except with a full
- short-press and release first.
-
- - EV_click2_press
- - EV_click2_release
- - EV_click2_complete (a.k.a. EV_2clicks)
- - EV_click2_hold
- - EV_click2_hold_release
-
- Each of the above patterns continues up to 15 clicks.
-
- To match entire categories of events, use the bitmasks provided.
- For example, to match button events where the button is down or
- the button is up, the code would look like this:
-
- if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
- // button is down (can be a press event or a hold event)
- }
- else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
- // button was just released
- }
-
- In theory, you could also define your own arbitrary event types, and
- emit() them as necessary, and handle them in State functions the same
- as any other event.
-
-
-Cooperative multitasking:
-
- Since we don't have true preemptive multitasking, the best we can do
- is cooperative multitasking. In practice, this means:
-
- - Declare global variables as volatile if they can be changed by an
- event handler. This keeps the compiler from caching the value and
- causing incorrect behavior.
-
- - Don't put long-running tasks into State functions. Each State
- will get called at least once every 16ms for a clock tick, so they
- should not run for longer than 16ms.
-
- - Put long-running tasks into loop() instead.
-
- - For long delay() calls, use nice_delay_ms(). This allows the MCU
- to process events while we wait. It also automatically aborts if
- it detects a state change, and returns a different value.
-
- In many cases, it shouldn't be necessary to do anything more than
- this, but sometimes it will also be a good idea to check the
- return value and abort the current task:
-
- if (! nice_delay_ms(mydelay)) break;
-
- - In general, try to do small amounts of work and then return
- control to other parts of the program. Keep doing small amounts
- and yielding until a task is done, instead of trying to do it all
- at once.
-
-
-Persistent data in EEPROM:
-
- To save data which lasts after a battery change, use the eeprom
- functions. Define an eeprom style (or two) at the top, define how
- many bytes to allocate, and then use the relevant functions as
- appropriate.
-
- - USE_EEPROM / USE_EEPROM_WL: Enable the eeprom-related functions.
- With "WL", it uses wear-levelling. Without, it does not. Note:
- Wear levelling is not necessarily better -- it uses more ROM, and
- it writes more bytes per save(). So, use it only for a few bytes
- which change frequently -- not for many bytes or infrequent
- changes.
-
- - EEPROM_BYTES N / EEPROM_WL_BYTES N: Allocate N bytes for the
- eeprom data.
-
- - load_eeprom() / load_eeprom_wl(): Load the stored data into the
- eeprom[] or eeprom_wl[] arrays.
- Returns 1 if data was found, 0 otherwise.
-
- - save_eeprom() / save_eeprom_wl(): Save the eeprom[] or eeprom_wl[]
- array data to persistent storage. The WL version erases all old
- values and writes new ones in a different part of the eeprom
- space. The non-WL version updates values in place, and does not
- overwrite values which didn't change.
-
- Note that all interrupts will be disabled during eeprom operations.
-
-
-Useful #defines:
-
- A variety of things can be #defined before including
- spaghetti-monster.h in your program. This allows you to tweak the
- behavior and set options to fit your needs:
-
- - FSM_something_LAYOUT: Select a driver type from tk-attiny.h. This
- controls how many power channels there are, which pins they're on,
- and what other driver features are available.
-
- - USE_LVP: Enable low-voltage protection.
-
- - VOLTAGE_LOW: What voltage should LVP trigger at? Defaults to 29 (2.9V).
-
- - VOLTAGE_FUDGE_FACTOR: Add this much to the voltage measurements,
- to compensate for voltage drop across the reverse-polarity
- diode.
-
- - VOLTAGE_WARNING_SECONDS: How long to wait between LVP events.
-
- - USE_THERMAL_REGULATION: Enable thermal regulation
-
- - DEFAULT_THERM_CEIL: Set the temperature limit to use by default
- when the user hasn't configured anything.
-
- - USE_RAMPING: Enable smooth ramping helpers.
-
- - RAMP_LENGTH: Pick a pre-defined ramp by length. Defined sizes
- are 50, 75, and 150 levels.
-
- - USE_DELAY_4MS, USE_DELAY_MS, USE_DELAY_ZERO: Enable the delay_4ms,
- delay_ms(), and delay_zero() functions. Useful for timing-related
- activities.
-
- - HOLD_TIMEOUT: How many clock ticks before a "press" event becomes
- a "hold" event?
-
- - RELEASE_TIMEOUT: How many clock ticks before a "release" event
- becomes a "click" event? Basically, the maximum time between
- clicks in a double-click or triple-click.
-
- - USE_BATTCHECK: Enable the battcheck function. Also define one of
- the following to select a display style:
-
- - BATTCHECK_VpT: Volts, pause, tenths.
- - BATTCHECK_4bars: Blink up to 4 times.
- - BATTCHECK_6bars: Blink up to 6 times.
- - BATTCHECK_8bars: Blink up to 8 times.
-
- - ... and many others. Will try to document them over time, but
- they can be found by searching for pretty much anything in
- all-caps in the fsm-*.[ch] files.