~toykeeper/flashlight-firmware/trunk

188.33.14 by Selene ToyKeeper
switched the rest of FSM + Anduril to use SPDX license headers
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
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
5
6
#include "candle-mode.h"
188.22.28 by Selene Scriven
moved sunset bump from 3C to 4H, fixed candle sunset behavior (was overflowing / wrapping around)
7
188.22.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
8
#ifdef USE_SUNSET_TIMER
9
#include "sunset-timer.h"
10
#endif
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
11
12
uint8_t candle_mode_state(Event event, uint16_t arg) {
13
    static int8_t ramp_direction = 1;
188.33.2 by Selene ToyKeeper
refactor progress checkpoint ... got Sofirn LT1S Pro and Emisar D4v2 working
14
    #define MAX_CANDLE_LEVEL (MAX_LEVEL-CANDLE_AMPLITUDE-15)
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
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;
188.22.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
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);
188.33.66 by Selene ToyKeeper
Removed references to Harry Potter,
38
        return EVENT_HANDLED;
188.22.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
39
    }
40
    #endif  // ifdef USE_SUNSET_TIMER
41
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
42
43
    if (event == EV_enter_state) {
44
        ramp_direction = 1;
188.33.66 by Selene ToyKeeper
Removed references to Harry Potter,
45
        return EVENT_HANDLED;
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
46
    }
188.22.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
47
    #ifdef USE_SUNSET_TIMER
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
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
188.22.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
52
        sunset_timer = 0;
188.33.66 by Selene ToyKeeper
Removed references to Harry Potter,
53
        return EVENT_HANDLED;
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
54
    }
188.22.27 by Selene Scriven
replaced "goodnight / sunset mode" with sunset timer, which works in both candle mode and regular ramp mode
55
    #endif  // ifdef USE_SUNSET_TIMER
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
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;
188.33.66 by Selene ToyKeeper
Removed references to Harry Potter,
67
        return EVENT_HANDLED;
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
68
    }
69
    // reverse ramp direction on hold release
70
    else if (event == EV_click1_hold_release) {
71
        ramp_direction = -ramp_direction;
188.33.66 by Selene ToyKeeper
Removed references to Harry Potter,
72
        return EVENT_HANDLED;
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
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 --;
188.33.66 by Selene ToyKeeper
Removed references to Harry Potter,
79
        return EVENT_HANDLED;
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
80
    }
81
    // clock tick: animate candle brightness
82
    else if (event == EV_tick) {
83
        // un-reverse after 1 second
188.28.7 by Selene Scriven
reduced length of auto-reverse timing window from ~1000 ms to ~660ms, because it was too fast
84
        if (arg == AUTO_REVERSE_TIME) ramp_direction = 1;
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
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);
188.22.28 by Selene Scriven
moved sunset bump from 3C to 4H, fixed candle sunset behavior (was overflowing / wrapping around)
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
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
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);
188.33.66 by Selene ToyKeeper
Removed references to Harry Potter,
132
        return EVENT_HANDLED;
188.22.20 by Selene Scriven
moved candle mode to its own file, since it's kinda big
133
    }
134
    return EVENT_NOT_HANDLED;
135
}
136