~toykeeper/flashlight-firmware/fsm

« back to all changes in this revision

Viewing changes to ToyKeeper/spaghetti-monster/anduril/candle-mode.c

  • Committer: Selene ToyKeeper
  • Date: 2023-11-04 15:09:10 UTC
  • mfrom: (483.1.175 anduril2)
  • Revision ID: bzr@toykeeper.net-20231104150910-ddd3afw4nhfvof2l
merged anduril2 branch -> fsm, with *years* of changes
(this also means this code is now Anduril 2 instead of Anduril 1)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// candle-mode.c: Candle mode for Anduril.
 
2
// Copyright (C) 2017-2023 Selene ToyKeeper
 
3
// SPDX-License-Identifier: GPL-3.0-or-later
 
4
#pragma once
 
5
 
 
6
#include "candle-mode.h"
 
7
 
 
8
#ifdef USE_SUNSET_TIMER
 
9
#include "sunset-timer.h"
 
10
#endif
 
11
 
 
12
uint8_t candle_mode_state(Event event, uint16_t arg) {
 
13
    static int8_t ramp_direction = 1;
 
14
    #define MAX_CANDLE_LEVEL (MAX_LEVEL-CANDLE_AMPLITUDE-15)
 
15
    static uint8_t candle_wave1 = 0;
 
16
    static uint8_t candle_wave2 = 0;
 
17
    static uint8_t candle_wave3 = 0;
 
18
    static uint8_t candle_wave2_speed = 0;
 
19
    // these should add up to 100
 
20
    #define CANDLE_WAVE1_MAXDEPTH 30
 
21
    #define CANDLE_WAVE2_MAXDEPTH 45
 
22
    #define CANDLE_WAVE3_MAXDEPTH 25
 
23
    static const uint8_t candle_wave1_depth = CANDLE_WAVE1_MAXDEPTH * CANDLE_AMPLITUDE / 100;
 
24
    static uint8_t candle_wave2_depth       = CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100;
 
25
    static uint8_t candle_wave3_depth       = CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100;
 
26
    static uint8_t candle_mode_brightness = 24;
 
27
 
 
28
    #ifdef USE_SUNSET_TIMER
 
29
    // let the candle "burn out" and shut itself off
 
30
    // if the user told it to
 
31
    // cache this in case it changes when the timer is called
 
32
    uint8_t sunset_active = sunset_timer;
 
33
    // clock tick
 
34
    sunset_timer_state(event, arg);
 
35
    // if the timer just expired, shut off
 
36
    if (sunset_active  &&  (! sunset_timer)) {
 
37
        set_state(off_state, 0);
 
38
        return EVENT_HANDLED;
 
39
    }
 
40
    #endif  // ifdef USE_SUNSET_TIMER
 
41
 
 
42
 
 
43
    if (event == EV_enter_state) {
 
44
        ramp_direction = 1;
 
45
        return EVENT_HANDLED;
 
46
    }
 
47
    #ifdef USE_SUNSET_TIMER
 
48
    // 2 clicks: cancel timer
 
49
    else if (event == EV_2clicks) {
 
50
        // parent state just rotated through strobe/flasher modes,
 
51
        // so cancel timer...  in case any time was left over from earlier
 
52
        sunset_timer = 0;
 
53
        return EVENT_HANDLED;
 
54
    }
 
55
    #endif  // ifdef USE_SUNSET_TIMER
 
56
    // hold: change brightness (brighter)
 
57
    else if (event == EV_click1_hold) {
 
58
        // ramp away from extremes
 
59
        if (! arg) {
 
60
            if (candle_mode_brightness >= MAX_CANDLE_LEVEL) { ramp_direction = -1; }
 
61
            else if (candle_mode_brightness <= 1) { ramp_direction = 1; }
 
62
        }
 
63
        // change brightness, but not too far
 
64
        candle_mode_brightness += ramp_direction;
 
65
        if (candle_mode_brightness < 1) candle_mode_brightness = 1;
 
66
        else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL;
 
67
        return EVENT_HANDLED;
 
68
    }
 
69
    // reverse ramp direction on hold release
 
70
    else if (event == EV_click1_hold_release) {
 
71
        ramp_direction = -ramp_direction;
 
72
        return EVENT_HANDLED;
 
73
    }
 
74
    // click, hold: change brightness (dimmer)
 
75
    else if (event == EV_click2_hold) {
 
76
        ramp_direction = 1;
 
77
        if (candle_mode_brightness > 1)
 
78
            candle_mode_brightness --;
 
79
        return EVENT_HANDLED;
 
80
    }
 
81
    // clock tick: animate candle brightness
 
82
    else if (event == EV_tick) {
 
83
        // un-reverse after 1 second
 
84
        if (arg == AUTO_REVERSE_TIME) ramp_direction = 1;
 
85
 
 
86
        // 3-oscillator synth for a relatively organic pattern
 
87
        uint8_t add;
 
88
        add = ((triangle_wave(candle_wave1) * candle_wave1_depth) >> 8)
 
89
            + ((triangle_wave(candle_wave2) * candle_wave2_depth) >> 8)
 
90
            + ((triangle_wave(candle_wave3) * candle_wave3_depth) >> 8);
 
91
        uint16_t brightness = candle_mode_brightness + add;
 
92
 
 
93
        // self-timer dims the light during the final minute
 
94
        #ifdef USE_SUNSET_TIMER
 
95
        if (1 == sunset_timer) {
 
96
            brightness = brightness
 
97
                         * ((TICKS_PER_MINUTE>>5) - (sunset_ticks>>5))
 
98
                         / (TICKS_PER_MINUTE>>5);
 
99
        }
 
100
        #endif  // ifdef USE_SUNSET_TIMER
 
101
 
 
102
        set_level(brightness);
 
103
 
 
104
        // wave1: slow random LFO
 
105
        // TODO: make wave slower and more erratic?
 
106
        if ((arg & 1) == 0) candle_wave1 += pseudo_rand() & 1;
 
107
        // wave2: medium-speed erratic LFO
 
108
        candle_wave2 += candle_wave2_speed;
 
109
        // wave3: erratic fast wave
 
110
        candle_wave3 += pseudo_rand() % 37;
 
111
        // S&H on wave2 frequency to make it more erratic
 
112
        if ((pseudo_rand() & 0b00111111) == 0)
 
113
            candle_wave2_speed = pseudo_rand() % 13;
 
114
        // downward sawtooth on wave2 depth to simulate stabilizing
 
115
        if ((candle_wave2_depth > 0) && ((pseudo_rand() & 0b00111111) == 0))
 
116
            candle_wave2_depth --;
 
117
        // random sawtooth retrigger
 
118
        if (pseudo_rand() == 0) {
 
119
            // random amplitude
 
120
            //candle_wave2_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2));
 
121
            candle_wave2_depth = pseudo_rand() % (CANDLE_WAVE2_MAXDEPTH * CANDLE_AMPLITUDE / 100);
 
122
            //candle_wave3_depth = 5;
 
123
            candle_wave2 = 0;
 
124
        }
 
125
        // downward sawtooth on wave3 depth to simulate stabilizing
 
126
        if ((candle_wave3_depth > 2) && ((pseudo_rand() & 0b00011111) == 0))
 
127
            candle_wave3_depth --;
 
128
        if ((pseudo_rand() & 0b01111111) == 0)
 
129
            // random amplitude
 
130
            //candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2));
 
131
            candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100);
 
132
        return EVENT_HANDLED;
 
133
    }
 
134
    return EVENT_NOT_HANDLED;
 
135
}
 
136