~toykeeper/flashlight-firmware/trunk

« back to all changes in this revision

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

  • Committer: Selene Scriven
  • Date: 2020-07-06 20:24:28 UTC
  • mfrom: (188.1.294 fsm)
  • Revision ID: bzr@toykeeper.net-20200706202428-7pyen2ow9q2rtd9p
merged nearly a year of updates from the fsm branch, including the new product map

Show diffs side-by-side

added added

removed removed

Lines of Context:
22
22
// Anduril config file name (set it here or define it at the gcc command line)
23
23
//#define CONFIGFILE cfg-blf-q8.h
24
24
 
25
 
#define USE_LVP  // FIXME: won't build when this option is turned off
 
25
#define USE_LVP
26
26
 
27
27
// parameters for this defined below or per-driver
28
28
#define USE_THERMAL_REGULATION
35
35
// (currently incompatible with factory reset)
36
36
//#define START_AT_MEMORIZED_LEVEL
37
37
 
 
38
// include a function to blink out the firmware version
 
39
#define USE_VERSION_CHECK
38
40
 
39
41
// short blip when crossing from "click" to "hold" from off
40
42
// (helps the user hit moon mode exactly, instead of holding too long
77
79
// enable beacon mode
78
80
#define USE_BEACON_MODE
79
81
 
 
82
// enable momentary mode
 
83
#define USE_MOMENTARY_MODE
 
84
 
80
85
//Muggle mode for easy UI
81
86
#define USE_MUGGLE_MODE
82
87
 
88
93
//  so don't enable them at the same time as any of the above strobes)
89
94
//#define USE_POLICE_STROBE_MODE
90
95
//#define USE_SOS_MODE
 
96
//#define USE_SOS_MODE_IN_FF_GROUP  // put SOS in the "boring strobes" mode
 
97
//#define USE_SOS_MODE_IN_BLINKY_GROUP  // put SOS in the blinkies mode group
 
98
 
 
99
// cut clock speed at very low modes for better efficiency
 
100
// (defined here so config files can override it)
 
101
#define USE_DYNAMIC_UNDERCLOCKING
91
102
 
92
103
/***** specific settings for known driver types *****/
93
104
#include "tk.h"
128
139
#endif
129
140
#endif
130
141
#define USE_IDLE_MODE  // reduce power use while awake and no tasks are pending
131
 
#define USE_DYNAMIC_UNDERCLOCKING  // cut clock speed at very low modes for better efficiency
132
142
 
133
143
// full FET strobe can be a bit much...  use max regulated level instead,
134
144
// if there's a bright enough regulated level
144
154
#define USE_STROBE_STATE
145
155
#endif
146
156
 
147
 
#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE)
 
157
#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE_IN_FF_GROUP)
148
158
#define USE_BORING_STROBE_STATE
149
159
#endif
150
160
 
268
278
uint8_t battcheck_state(Event event, uint16_t arg);
269
279
#endif
270
280
#ifdef USE_THERMAL_REGULATION
 
281
#define USE_BLINK_NUM
271
282
uint8_t tempcheck_state(Event event, uint16_t arg);
272
283
uint8_t thermal_config_state(Event event, uint16_t arg);
273
284
#endif
280
291
uint8_t beacon_state(Event event, uint16_t arg);
281
292
uint8_t beacon_config_state(Event event, uint16_t arg);
282
293
#endif
 
294
#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
 
295
// automatic SOS emergency signal
 
296
uint8_t sos_state(Event event, uint16_t arg);
 
297
#endif
283
298
// soft lockout
284
299
#define MOON_DURING_LOCKOUT_MODE
285
300
// if enabled, 2nd lockout click goes to the other ramp's floor level
286
301
#define LOCKOUT_MOON_FANCY
287
302
uint8_t lockout_state(Event event, uint16_t arg);
 
303
#ifdef USE_MOMENTARY_MODE
288
304
// momentary / signalling mode
289
305
uint8_t momentary_state(Event event, uint16_t arg);
290
306
uint8_t momentary_mode = 0;  // 0 = ramping, 1 = strobe
291
307
uint8_t momentary_active = 0;  // boolean, true if active *right now*
 
308
#endif
292
309
#ifdef USE_MUGGLE_MODE
293
310
// muggle mode, super-simple, hard to exit
294
311
uint8_t muggle_state(Event event, uint16_t arg);
306
323
void indicator_blink(uint8_t arg);
307
324
#endif
308
325
#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
 
326
uint8_t setting_rgb_mode_now = 0;
309
327
void rgb_led_update(uint8_t mode, uint8_t arg);
 
328
void rgb_led_voltage_readout(uint8_t bright);
310
329
/*
311
330
 * 0: R
312
331
 * 1: RG
318
337
 * 7: rainbow
319
338
 * 8: voltage
320
339
 */
 
340
const PROGMEM uint8_t rgb_led_colors[] = {
 
341
    0b00000001,  // 0: red
 
342
    0b00000101,  // 1: yellow
 
343
    0b00000100,  // 2: green
 
344
    0b00010100,  // 3: cyan
 
345
    0b00010000,  // 4: blue
 
346
    0b00010001,  // 5: purple
 
347
    0b00010101,  // 6: white
 
348
};
321
349
#define RGB_LED_NUM_COLORS 10
322
350
#define RGB_LED_NUM_PATTERNS 4
323
351
#ifndef RGB_LED_OFF_DEFAULT
327
355
#ifndef RGB_LED_LOCKOUT_DEFAULT
328
356
#define RGB_LED_LOCKOUT_DEFAULT 0x37  // blinking, rainbow
329
357
#endif
 
358
#ifndef RGB_RAINBOW_SPEED
 
359
#define RGB_RAINBOW_SPEED 0x0f  // change color every 16 frames
 
360
#endif
330
361
uint8_t rgb_led_off_mode = RGB_LED_OFF_DEFAULT;
331
362
uint8_t rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT;
332
363
#endif
462
493
 
463
494
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
464
495
// party / tactical strobe timing
465
 
volatile uint8_t strobe_delays[] = { 40, 67 };  // party strobe, tactical strobe
 
496
volatile uint8_t strobe_delays[] = { 41, 67 };  // party strobe 24 Hz, tactical strobe 10 Hz
466
497
#endif
467
498
 
468
499
// bike mode config options
483
514
volatile uint8_t beacon_seconds = 2;
484
515
#endif
485
516
 
 
517
#ifdef USE_VERSION_CHECK
 
518
#define USE_BLINK_DIGIT
 
519
#include "version.h"
 
520
const PROGMEM uint8_t version_number[] = VERSION_NUMBER;
 
521
uint8_t version_check_state(Event event, uint16_t arg);
 
522
#endif
486
523
 
487
524
uint8_t off_state(Event event, uint16_t arg) {
488
525
    // turn emitter off when entering state
617
654
        set_state(lockout_state, 0);
618
655
        return MISCHIEF_MANAGED;
619
656
    }
 
657
    #ifdef USE_MOMENTARY_MODE
620
658
    // 5 clicks: momentary mode
621
659
    else if (event == EV_5clicks) {
622
660
        blink_confirm(1);
623
661
        set_state(momentary_state, 0);
624
662
        return MISCHIEF_MANAGED;
625
663
    }
 
664
    #endif
626
665
    #ifdef USE_MUGGLE_MODE
627
666
    // 6 clicks: muggle mode
628
667
    else if (event == EV_6clicks) {
661
700
    }
662
701
    // 7 clicks (hold last): change RGB aux LED color
663
702
    else if (event == EV_click7_hold) {
 
703
        setting_rgb_mode_now = 1;
664
704
        if (0 == (arg & 0x3f)) {
665
705
            uint8_t mode = (rgb_led_off_mode & 0x0f) + 1;
666
706
            mode = mode % RGB_LED_NUM_COLORS;
671
711
        return MISCHIEF_MANAGED;
672
712
    }
673
713
    else if (event == EV_click7_hold_release) {
 
714
        setting_rgb_mode_now = 0;
674
715
        save_config();
675
716
        return MISCHIEF_MANAGED;
676
717
    }
677
718
    #endif  // end 7 clicks
678
 
    #ifdef USE_TENCLICK_THERMAL_CONFIG
 
719
    #if defined(USE_TENCLICK_THERMAL_CONFIG) && defined(USE_THERMAL_REGULATION)
679
720
    // 10 clicks: thermal config mode
680
721
    else if (event == EV_10clicks) {
681
722
        push_state(thermal_config_state, 0);
682
723
        return MISCHIEF_MANAGED;
683
724
    }
684
725
    #endif
 
726
    #ifdef USE_VERSION_CHECK
 
727
    // 15+ clicks: show the version number
 
728
    else if (event == EV_15clicks) {
 
729
        set_state(version_check_state, 0);
 
730
        return MISCHIEF_MANAGED;
 
731
    }
 
732
    #endif
685
733
    #if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET)
686
734
    // 13 clicks and hold the last click: invoke factory reset (reboot)
687
735
    else if (event == EV_click13_hold) {
713
761
 
714
762
    // turn LED on when we first enter the mode
715
763
    if ((event == EV_enter_state) || (event == EV_reenter_state)) {
 
764
        #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE)
716
765
        momentary_mode = 0;  // 0 = ramping, 1 = strobes
 
766
        #endif
717
767
        // if we just got back from config mode, go back to memorized level
718
768
        if (event == EV_reenter_state) {
719
769
            arg = memorized_level;
792
842
            // (off->hold->stepped_min->release causes this state)
793
843
            else if (actual_level <= mode_min) { ramp_direction = 1; }
794
844
        }
 
845
        // if the button is stuck, err on the side of safety and ramp down
 
846
        else if ((arg > TICKS_PER_SECOND * 5) && (actual_level >= mode_max)) {
 
847
            ramp_direction = -1;
 
848
        }
 
849
        // if the button is still stuck, lock the light
 
850
        else if ((arg > TICKS_PER_SECOND * 10) && (actual_level <= mode_min)) {
 
851
            blip();
 
852
            set_state(lockout_state, 0);
 
853
        }
795
854
        memorized_level = nearest_level((int16_t)actual_level \
796
855
                          + (ramp_step_size * ramp_direction));
797
856
        #else
919
978
        if (arg == TICKS_PER_SECOND) ramp_direction = 1;
920
979
        #endif
921
980
        #ifdef USE_SET_LEVEL_GRADUALLY
922
 
        // make thermal adjustment speed scale with magnitude
923
 
        // also, adjust slower when going up
924
 
        if ((arg & 1) &&
925
 
            ((actual_level < THERM_FASTER_LEVEL) ||
926
 
             (actual_level < gradual_target))) {
927
 
            return MISCHIEF_MANAGED;  // adjust slower when not a high mode
928
 
        }
929
 
        #ifdef THERM_HARD_TURBO_DROP
930
 
        else if ((! (actual_level < THERM_FASTER_LEVEL))
931
 
                && (actual_level > gradual_target)) {
932
 
            gradual_tick();
933
 
        }
934
 
        else {
935
 
        #endif
936
 
        // [int(62*4 / (x**0.8)) for x in (1,2,4,8,16,32,64,128)]
937
 
        //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5};
938
 
        // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)]
939
 
        //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3};
940
 
        // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)]
941
 
        uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2};
942
 
        uint8_t diff;
943
 
        static uint8_t ticks_since_adjust = 0;
944
 
        if (gradual_target > actual_level) {
945
 
            // rise at half speed (skip half the frames)
946
 
            if (arg & 2) return MISCHIEF_MANAGED;
947
 
            diff = gradual_target - actual_level;
948
 
        } else {
949
 
            diff = actual_level - gradual_target;
950
 
        }
951
 
        ticks_since_adjust ++;
952
 
        // if there's any adjustment to be made, make it
 
981
        int16_t diff = gradual_target - actual_level;
 
982
        static uint16_t ticks_since_adjust = 0;
 
983
        ticks_since_adjust++;
953
984
        if (diff) {
954
 
            uint8_t magnitude = 0;
955
 
            #ifndef THERM_HARD_TURBO_DROP
956
 
            // if we're on a really high mode, drop faster
957
 
            if ((actual_level >= THERM_FASTER_LEVEL)
958
 
                && (actual_level > gradual_target)) { magnitude ++; }
959
 
            #endif
 
985
            uint16_t ticks_per_adjust = 256;
 
986
            if (diff < 0) {
 
987
                //diff = -diff;
 
988
                if (actual_level > THERM_FASTER_LEVEL) {
 
989
                    #ifdef THERM_HARD_TURBO_DROP
 
990
                    ticks_per_adjust >>= 2;
 
991
                    #endif
 
992
                    ticks_per_adjust >>= 2;
 
993
                }
 
994
            } else {
 
995
                // rise at half speed
 
996
                ticks_per_adjust <<= 1;
 
997
            }
960
998
            while (diff) {
961
 
                magnitude ++;
962
 
                diff >>= 1;
 
999
                ticks_per_adjust >>= 1;
 
1000
                //diff >>= 1;
 
1001
                diff /= 2;  // because shifting produces weird behavior
963
1002
            }
964
 
            uint8_t ticks_per_adjust = intervals[magnitude];
965
1003
            if (ticks_since_adjust > ticks_per_adjust)
966
1004
            {
967
1005
                gradual_tick();
968
1006
                ticks_since_adjust = 0;
969
1007
            }
970
 
            //if (!(arg % ticks_per_adjust)) gradual_tick();
971
 
        }
972
 
        #ifdef THERM_HARD_TURBO_DROP
973
 
        }
974
 
        #endif
 
1008
        }
975
1009
        #endif  // ifdef USE_SET_LEVEL_GRADUALLY
976
1010
        return MISCHIEF_MANAGED;
977
1011
    }
1024
1058
        }
1025
1059
        return MISCHIEF_MANAGED;
1026
1060
    }
 
1061
    #ifdef USE_SET_LEVEL_GRADUALLY
 
1062
    // temperature is within target window
 
1063
    // (so stop trying to adjust output)
 
1064
    else if (event == EV_temperature_okay) {
 
1065
        // if we're still adjusting output...  stop after the current step
 
1066
        if (gradual_target > actual_level)
 
1067
            gradual_target = actual_level + 1;
 
1068
        else if (gradual_target < actual_level)
 
1069
            gradual_target = actual_level - 1;
 
1070
        return MISCHIEF_MANAGED;
 
1071
    }
 
1072
    #endif  // ifdef USE_SET_LEVEL_GRADUALLY
1027
1073
    #endif  // ifdef USE_THERMAL_REGULATION
1028
1074
    return EVENT_NOT_HANDLED;
1029
1075
}
1100
1146
    // (maybe I should just make it nonvolatile?)
1101
1147
    strobe_mode_te st = strobe_type;
1102
1148
 
 
1149
    #ifdef USE_MOMENTARY_MODE
1103
1150
    momentary_mode = 1;  // 0 = ramping, 1 = strobes
 
1151
    #endif
1104
1152
 
1105
1153
    #ifdef USE_CANDLE_MODE
1106
1154
    // pass all events to candle mode, when it's active
1232
1280
    if (0) {}  // placeholde0
1233
1281
    #ifdef USE_PARTY_STROBE_MODE
1234
1282
    else if (st == party_strobe_e) {  // party strobe
 
1283
        #ifdef PARTY_STROBE_ONTIME
 
1284
        nice_delay_ms(PARTY_STROBE_ONTIME);
 
1285
        #else
1235
1286
        if (del < 42) delay_zero();
1236
1287
        else nice_delay_ms(1);
 
1288
        #endif
1237
1289
    }
1238
1290
    #endif
1239
1291
    #ifdef USE_TACTICAL_STROBE_MODE
1484
1536
    }
1485
1537
}
1486
1538
#endif
 
1539
#endif  // #ifdef USE_BORING_STROBE_STATE
1487
1540
 
1488
1541
#ifdef USE_SOS_MODE
 
1542
#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
 
1543
uint8_t sos_state(Event event, uint16_t arg) {
 
1544
    // 1 click: off
 
1545
    if (event == EV_1click) {
 
1546
        set_state(off_state, 0);
 
1547
        return MISCHIEF_MANAGED;
 
1548
    }
 
1549
    // 2 clicks: next mode
 
1550
    else if (event == EV_2clicks) {
 
1551
        #ifdef USE_THERMAL_REGULATION
 
1552
        set_state(tempcheck_state, 0);
 
1553
        #else
 
1554
        set_state(battcheck_state, 0);
 
1555
        #endif
 
1556
        return MISCHIEF_MANAGED;
 
1557
    }
 
1558
    return EVENT_NOT_HANDLED;
 
1559
}
 
1560
#endif
 
1561
 
1489
1562
void sos_blink(uint8_t num, uint8_t dah) {
1490
1563
    #define DIT_LENGTH 200
1491
1564
    for (; num > 0; num--) {
1499
1572
        nice_delay_ms(DIT_LENGTH);
1500
1573
    }
1501
1574
    // three "off" dits (or one "dah") between letters
1502
 
    nice_delay_ms(DIT_LENGTH*2);
 
1575
    // (except for SOS, which is collectively treated as a single "letter")
 
1576
    //nice_delay_ms(DIT_LENGTH*2);
1503
1577
}
1504
1578
 
1505
1579
inline void sos_mode_iter() {
1506
1580
    // one iteration of main loop()
1507
 
    nice_delay_ms(1000);
 
1581
    //nice_delay_ms(1000);
1508
1582
    sos_blink(3, 0);  // S
1509
1583
    sos_blink(3, 1);  // O
1510
1584
    sos_blink(3, 0);  // S
1511
 
    nice_delay_ms(1000);
 
1585
    nice_delay_ms(2000);
1512
1586
}
1513
1587
#endif  // #ifdef USE_SOS_MODE
1514
 
#endif  // #ifdef USE_BORING_STROBE_STATE
1515
1588
 
1516
1589
 
1517
1590
#ifdef USE_BATTCHECK
1521
1594
        set_state(off_state, 0);
1522
1595
        return MISCHIEF_MANAGED;
1523
1596
    }
 
1597
    #ifdef USE_GOODNIGHT_MODE
1524
1598
    // 2 clicks: goodnight mode
1525
1599
    else if (event == EV_2clicks) {
1526
1600
        set_state(goodnight_state, 0);
1527
1601
        return MISCHIEF_MANAGED;
1528
1602
    }
 
1603
    #elif defined(USE_BEACON_MODE)
 
1604
    // 2 clicks: beacon mode
 
1605
    else if (event == EV_2clicks) {
 
1606
        set_state(beacon_state, 0);
 
1607
        return MISCHIEF_MANAGED;
 
1608
    }
 
1609
    #elif defined(USE_THERMAL_REGULATION)
 
1610
    // 2 clicks: tempcheck mode
 
1611
    else if (event == EV_2clicks) {
 
1612
        set_state(tempcheck_state, 0);
 
1613
        return MISCHIEF_MANAGED;
 
1614
    }
 
1615
    #endif
1529
1616
    return EVENT_NOT_HANDLED;
1530
1617
}
1531
1618
#endif
1538
1625
        set_state(off_state, 0);
1539
1626
        return MISCHIEF_MANAGED;
1540
1627
    }
 
1628
    #ifdef USE_BATTCHECK
1541
1629
    // 2 clicks: battcheck mode
1542
1630
    else if (event == EV_2clicks) {
1543
1631
        set_state(battcheck_state, 0);
1544
1632
        return MISCHIEF_MANAGED;
1545
1633
    }
 
1634
    #endif
1546
1635
    // 4 clicks: thermal config mode
1547
1636
    else if (event == EV_4clicks) {
1548
1637
        push_state(thermal_config_state, 0);
1562
1651
    }
1563
1652
    // TODO: use sleep ticks to measure time between pulses,
1564
1653
    //       to save power
1565
 
    // 2 clicks: tempcheck mode
 
1654
    // 2 clicks: next mode
1566
1655
    else if (event == EV_2clicks) {
1567
 
        #ifdef USE_THERMAL_REGULATION
 
1656
        #ifdef USE_SOS_MODE_IN_BLINKY_GROUP
 
1657
        set_state(sos_state, 0);
 
1658
        #elif defined(USE_THERMAL_REGULATION)
1568
1659
        set_state(tempcheck_state, 0);
1569
 
        #else
 
1660
        #elif defined(USE_BATTCHECK)
1570
1661
        set_state(battcheck_state, 0);
1571
1662
        #endif
1572
1663
        return MISCHIEF_MANAGED;
1597
1688
        set_state(off_state, 0);
1598
1689
        return MISCHIEF_MANAGED;
1599
1690
    }
1600
 
    // 2 clicks: beacon mode
 
1691
    // 2 clicks: next mode
1601
1692
    else if (event == EV_2clicks) {
 
1693
        #ifdef USE_BEACON_MODE
1602
1694
        set_state(beacon_state, 0);
 
1695
        #elif defined(USE_SOS_MODE_IN_BLINKY_GROUP)
 
1696
        set_state(sos_state, 0);
 
1697
        #elif defined(USE_THERMAL_REGULATION)
 
1698
        set_state(tempcheck_state, 0);
 
1699
        #endif
1603
1700
        return MISCHIEF_MANAGED;
1604
1701
    }
1605
1702
    // tick: step down (maybe) or off (maybe)
1725
1822
    }
1726
1823
    // click, click, hold: change RGB aux LED color
1727
1824
    else if (event == EV_click3_hold) {
 
1825
        setting_rgb_mode_now = 1;
1728
1826
        if (0 == (arg & 0x3f)) {
1729
1827
            uint8_t mode = (rgb_led_lockout_mode & 0x0f) + 1;
1730
1828
            mode = mode % RGB_LED_NUM_COLORS;
1736
1834
    }
1737
1835
    // click, click, hold, release: save new color
1738
1836
    else if (event == EV_click3_hold_release) {
 
1837
        setting_rgb_mode_now = 0;
1739
1838
        save_config();
1740
1839
        return MISCHIEF_MANAGED;
1741
1840
    }
1751
1850
}
1752
1851
 
1753
1852
 
 
1853
#ifdef USE_MOMENTARY_MODE
1754
1854
uint8_t momentary_state(Event event, uint16_t arg) {
1755
1855
    // TODO: momentary strobe here?  (for light painting)
1756
1856
 
1757
1857
    // init strobe mode, if relevant
 
1858
    #ifdef USE_STROBE_STATE
1758
1859
    if ((event == EV_enter_state) && (momentary_mode == 1)) {
1759
1860
        strobe_state(event, arg);
1760
1861
    }
 
1862
    #endif
1761
1863
 
1762
1864
    // light up when the button is pressed; go dark otherwise
1763
1865
    // button is being held
1783
1885
    //  disconnected for several seconds, so we want to be awake when that
1784
1886
    //  happens to speed up the process)
1785
1887
    else if (event == EV_tick) {
 
1888
        #ifdef USE_STROBE_STATE
1786
1889
        if (momentary_active) {
1787
1890
            // 0 = ramping, 1 = strobes
1788
1891
            if (momentary_mode == 1) {
1790
1893
            }
1791
1894
        }
1792
1895
        else {
1793
 
            if (arg > TICKS_PER_SECOND*15) {  // sleep after 15 seconds
 
1896
        #endif
 
1897
            if (arg > TICKS_PER_SECOND*5) {  // sleep after 5 seconds
1794
1898
                go_to_standby = 1;  // sleep while light is off
1795
 
                // TODO: lighted button should use lockout config?
 
1899
                // turn off lighted button
 
1900
                #ifdef USE_INDICATOR_LED
 
1901
                indicator_led(0);
 
1902
                #elif defined(USE_AUX_RGB_LEDS)
 
1903
                rgb_led_update(0, 0);
 
1904
                #endif
1796
1905
            }
 
1906
        #ifdef USE_STROBE_STATE
1797
1907
        }
 
1908
        #endif
1798
1909
        return MISCHIEF_MANAGED;
1799
1910
    }
1800
1911
 
1801
1912
    return EVENT_NOT_HANDLED;
1802
1913
}
 
1914
#endif
1803
1915
 
1804
1916
 
1805
1917
#ifdef USE_MUGGLE_MODE
1919
2031
        // turn off, but don't go to the main "off" state
1920
2032
        if (muggle_off_mode) {
1921
2033
            if (arg > TICKS_PER_SECOND*1) {  // sleep after 1 second
 
2034
                #ifdef USE_AUX_RGB_LEDS_WHILE_ON
 
2035
                rgb_led_set(0);
 
2036
                #endif
1922
2037
                go_to_standby = 1;  // sleep while light is off
1923
2038
            }
1924
2039
        }
1940
2055
        return MISCHIEF_MANAGED;
1941
2056
    }
1942
2057
    #endif
 
2058
    #ifdef USE_LVP
1943
2059
    // low voltage is handled specially in muggle mode
1944
2060
    else if(event == EV_voltage_low) {
1945
2061
        uint8_t lvl = (actual_level >> 1) + (actual_level >> 2);
1950
2066
        }
1951
2067
        return MISCHIEF_MANAGED;
1952
2068
    }
1953
 
 
 
2069
    #endif
 
2070
 
 
2071
    return EVENT_NOT_HANDLED;
 
2072
}
 
2073
#endif
 
2074
 
 
2075
 
 
2076
#ifdef USE_VERSION_CHECK
 
2077
uint8_t version_check_state(Event event, uint16_t arg) {
1954
2078
    return EVENT_NOT_HANDLED;
1955
2079
}
1956
2080
#endif
2247
2371
#endif
2248
2372
 
2249
2373
#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
 
2374
uint8_t voltage_to_rgb() {
 
2375
    uint8_t levels[] = {
 
2376
    // voltage, color
 
2377
          0, 0, // 0, R
 
2378
         33, 1, // 1, R+G
 
2379
         35, 2, // 2,   G
 
2380
         37, 3, // 3,   G+B
 
2381
         39, 4, // 4,     B
 
2382
         41, 5, // 5, R + B
 
2383
         44, 6, // 6, R+G+B  // skip; looks too similar to G+B
 
2384
        255, 6, // 7, R+G+B
 
2385
    };
 
2386
    uint8_t volts = voltage;
 
2387
    if (volts < 29) return 0;
 
2388
 
 
2389
    uint8_t i;
 
2390
    for (i = 0;  volts >= levels[i];  i += 2) {}
 
2391
    uint8_t color_num = levels[(i - 2) + 1];
 
2392
    return pgm_read_byte(rgb_led_colors + color_num);
 
2393
}
 
2394
 
2250
2395
// do fancy stuff with the RGB aux LEDs
2251
2396
// mode: 0bPPPPCCCC where PPPP is the pattern and CCCC is the color
2252
2397
// arg: time slice number
2257
2402
    // turn off aux LEDs when battery is empty
2258
2403
    // (but if voltage==0, that means we just booted and don't know yet)
2259
2404
    uint8_t volts = voltage;  // save a few bytes by caching volatile value
2260
 
    if ((volts) && (volts < VOLTAGE_LOW)) { rgb_led_set(0); return; }
 
2405
    if ((volts) && (volts < VOLTAGE_LOW)) {
 
2406
        rgb_led_set(0);
 
2407
        #ifdef USE_BUTTON_LED
 
2408
        button_led_set(0);
 
2409
        #endif
 
2410
        return;
 
2411
    }
2261
2412
 
2262
2413
    uint8_t pattern = (mode>>4);  // off, low, high, blinking, ... more?
2263
2414
    uint8_t color = mode & 0x0f;
2266
2417
    if ((! go_to_standby) && (pattern > 2)) { pattern = 2; }
2267
2418
 
2268
2419
 
2269
 
    uint8_t colors[] = {
2270
 
        0b00000001,  // 0: red
2271
 
        0b00000101,  // 1: yellow
2272
 
        0b00000100,  // 2: green
2273
 
        0b00010100,  // 3: cyan
2274
 
        0b00010000,  // 4: blue
2275
 
        0b00010001,  // 5: purple
2276
 
        0b00010101,  // 6: white
2277
 
    };
 
2420
    const uint8_t *colors = rgb_led_colors;
2278
2421
    uint8_t actual_color = 0;
2279
2422
    if (color < 7) {  // normal color
2280
 
        actual_color = colors[color];
 
2423
        actual_color = pgm_read_byte(colors + color);
2281
2424
    }
2282
2425
    else if (color == 7) {  // rainbow
2283
 
        if (0 == (arg & 0x03)) {
 
2426
        uint8_t speed = 0x03;  // awake speed
 
2427
        if (go_to_standby) speed = RGB_RAINBOW_SPEED;  // asleep speed
 
2428
        if (0 == (arg & speed)) {
2284
2429
            rainbow = (rainbow + 1) % 6;
2285
2430
        }
2286
 
        actual_color = colors[rainbow];
 
2431
        actual_color = pgm_read_byte(colors + rainbow);
2287
2432
    }
2288
2433
    else {  // voltage
2289
2434
        // show actual voltage while asleep...
2290
2435
        if (go_to_standby) {
 
2436
            actual_color = voltage_to_rgb();
2291
2437
            // choose a color based on battery voltage
2292
 
            if (volts >= 38) actual_color = colors[4];
2293
 
            else if (volts >= 33) actual_color = colors[2];
2294
 
            else actual_color = colors[0];
 
2438
            //if (volts >= 38) actual_color = pgm_read_byte(colors + 4);
 
2439
            //else if (volts >= 33) actual_color = pgm_read_byte(colors + 2);
 
2440
            //else actual_color = pgm_read_byte(colors + 0);
2295
2441
        }
2296
2442
        // ... but during preview, cycle colors quickly
2297
2443
        else {
2298
 
            actual_color = colors[((arg>>1) % 3) << 1];
 
2444
            actual_color = pgm_read_byte(colors + (((arg>>1) % 3) << 1));
2299
2445
        }
2300
2446
    }
2301
2447
 
2307
2453
        frame = (frame + 1) % sizeof(animation);
2308
2454
        pattern = animation[frame];
2309
2455
    }
 
2456
    uint8_t result;
 
2457
    #ifdef USE_BUTTON_LED
 
2458
    uint8_t button_led_result;
 
2459
    #endif
2310
2460
    switch (pattern) {
2311
2461
        case 0:  // off
2312
 
            rgb_led_set(0);
 
2462
            result = 0;
 
2463
            #ifdef USE_BUTTON_LED
 
2464
            button_led_result = 0;
 
2465
            #endif
2313
2466
            break;
2314
2467
        case 1:  // low
2315
 
            rgb_led_set(actual_color);
 
2468
            result = actual_color;
 
2469
            #ifdef USE_BUTTON_LED
 
2470
            button_led_result = 1;
 
2471
            #endif
2316
2472
            break;
2317
 
        case 2:  // high
2318
 
            rgb_led_set(actual_color << 1);
 
2473
        default:  // high
 
2474
            result = (actual_color << 1);
 
2475
            #ifdef USE_BUTTON_LED
 
2476
            button_led_result = 2;
 
2477
            #endif
2319
2478
            break;
2320
2479
    }
 
2480
    rgb_led_set(result);
 
2481
    #ifdef USE_BUTTON_LED
 
2482
    button_led_set(button_led_result);
 
2483
    #endif
 
2484
}
 
2485
 
 
2486
void rgb_led_voltage_readout(uint8_t bright) {
 
2487
    uint8_t color = voltage_to_rgb();
 
2488
    if (bright) color = color << 1;
 
2489
    rgb_led_set(color);
2321
2490
}
2322
2491
#endif
2323
2492
 
2326
2495
void factory_reset() {
2327
2496
    // display a warning for a few seconds before doing the actual reset,
2328
2497
    // so the user has time to abort if they want
2329
 
    #define SPLODEY_TIME 3000
 
2498
    #define SPLODEY_TIME 2500
2330
2499
    #define SPLODEY_STEPS 64
2331
2500
    #define SPLODEY_TIME_PER_STEP (SPLODEY_TIME/SPLODEY_STEPS)
2332
2501
    uint8_t bright;
2334
2503
    // wind up to an explosion
2335
2504
    for (bright=0; bright<SPLODEY_STEPS; bright++) {
2336
2505
        set_level(bright);
2337
 
        delay_4ms(SPLODEY_TIME_PER_STEP/2/4);
 
2506
        nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
2338
2507
        set_level(bright>>1);
2339
 
        delay_4ms(SPLODEY_TIME_PER_STEP/2/4);
 
2508
        nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
2340
2509
        if (! button_is_pressed()) {
2341
2510
            reset = 0;
2342
2511
            break;
2357
2526
        bright = MAX_LEVEL;
2358
2527
        for (; bright > 0; bright--) {
2359
2528
            set_level(bright);
2360
 
            delay_4ms(SPLODEY_TIME_PER_STEP/6/4);
 
2529
            nice_delay_ms(SPLODEY_TIME_PER_STEP/8);
2361
2530
        }
2362
2531
    }
2363
2532
    // explosion cancelled, fade away
2364
2533
    else {
2365
2534
        for (; bright > 0; bright--) {
2366
2535
            set_level(bright);
2367
 
            delay_4ms(SPLODEY_TIME_PER_STEP/3/4);
 
2536
            nice_delay_ms(SPLODEY_TIME_PER_STEP/3);
2368
2537
        }
2369
2538
    }
2370
2539
}
2556
2725
 
2557
2726
    StatePtr state = current_state;
2558
2727
 
 
2728
    #ifdef USE_AUX_RGB_LEDS_WHILE_ON
 
2729
        if (! setting_rgb_mode_now) rgb_led_voltage_readout(1);
 
2730
    #endif
 
2731
 
2559
2732
    if (0) {}
2560
2733
 
 
2734
    #ifdef USE_VERSION_CHECK
 
2735
    else if (state == version_check_state) {
 
2736
        for (uint8_t i=0; i<sizeof(version_number)-1; i++) {
 
2737
            blink_digit(pgm_read_byte(version_number + i) - '0');
 
2738
            nice_delay_ms(300);
 
2739
        }
 
2740
        // FIXME: when user interrupts with button, "off" takes an extra click
 
2741
        //  before it'll turn back on, because the click to cancel gets sent
 
2742
        //  to the "off" state instead of version_check_state
 
2743
        //while (button_is_pressed()) {}
 
2744
        //empty_event_sequence();
 
2745
 
 
2746
        set_state(off_state, 0);
 
2747
    }
 
2748
    #endif  // #ifdef USE_VERSION_CHECK
 
2749
 
2561
2750
    #ifdef USE_STROBE_STATE
2562
2751
    else if ((state == strobe_state)
2563
 
         ||  ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) {  // also handle momentary strobes
 
2752
         #ifdef USE_MOMENTARY_MODE
 
2753
         // also handle momentary strobes
 
2754
         ||  ((state == momentary_state) && (momentary_mode == 1) && (momentary_active))
 
2755
         #endif
 
2756
         ) {
2564
2757
        uint8_t st = strobe_type;
2565
2758
 
2566
2759
        switch(st) {
2600
2793
                break;
2601
2794
            #endif
2602
2795
 
2603
 
            #ifdef USE_SOS_MODE
 
2796
            #ifdef USE_SOS_MODE_IN_FF_GROUP
2604
2797
            default: // SOS
2605
2798
                sos_mode_iter();
2606
2799
                break;
2621
2814
    }
2622
2815
    #endif
2623
2816
 
 
2817
    #ifdef USE_SOS_MODE_IN_BLINKY_GROUP
 
2818
    else if (state == sos_state) {
 
2819
        sos_mode_iter();
 
2820
    }
 
2821
    #endif
 
2822
 
2624
2823
    #ifdef USE_THERMAL_REGULATION
2625
2824
    // TODO: blink out therm_ceil during thermal_config_state?
2626
2825
    else if (state == tempcheck_state) {