aboutsummaryrefslogtreecommitdiff
path: root/spaghetti-monster
diff options
context:
space:
mode:
Diffstat (limited to 'spaghetti-monster')
-rw-r--r--spaghetti-monster/anduril/BRANDS1
-rw-r--r--spaghetti-monster/anduril/ChangeLog.md245
-rw-r--r--spaghetti-monster/anduril/MODELS36
-rw-r--r--spaghetti-monster/anduril/anduril-manual.txt350
-rw-r--r--spaghetti-monster/anduril/anduril.c85
-rw-r--r--spaghetti-monster/anduril/aux-leds.c102
-rw-r--r--spaghetti-monster/anduril/aux-leds.h58
-rw-r--r--spaghetti-monster/anduril/battcheck-mode-fsm.h36
-rw-r--r--spaghetti-monster/anduril/battcheck-mode.c60
-rw-r--r--spaghetti-monster/anduril/battcheck-mode.h27
-rw-r--r--spaghetti-monster/anduril/beacon-mode.c40
-rw-r--r--spaghetti-monster/anduril/beacon-mode.h30
-rwxr-xr-xspaghetti-monster/anduril/build-all.sh6
-rw-r--r--spaghetti-monster/anduril/candle-mode.c44
-rw-r--r--spaghetti-monster/anduril/candle-mode.h27
-rw-r--r--spaghetti-monster/anduril/cfg-blf-gt-mini.h7
-rw-r--r--spaghetti-monster/anduril/cfg-blf-gt.h32
-rw-r--r--spaghetti-monster/anduril/cfg-blf-lantern-t1616.h99
-rw-r--r--spaghetti-monster/anduril/cfg-blf-lantern.h104
-rw-r--r--spaghetti-monster/anduril/cfg-blf-q8-t1616.h77
-rw-r--r--spaghetti-monster/anduril/cfg-blf-q8.h19
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-2ch-fet.h113
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-2ch.h103
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1.h6
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d18-219.h4
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d18.h43
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1s.h8
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1v2-7135-fet.h11
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1v2-linear-fet.h9
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h11
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4-219c.h6
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4.h30
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h106
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4s-219c.h4
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4s.h15
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h4
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4sv2-tintramp-fet.h70
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4sv2-tintramp.h87
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4sv2.h82
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2-219.h17
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h28
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2.h79
-rw-r--r--spaghetti-monster/anduril/cfg-ff-e01.h48
-rw-r--r--spaghetti-monster/anduril/cfg-ff-pl47-219.h4
-rw-r--r--spaghetti-monster/anduril/cfg-ff-pl47.h29
-rw-r--r--spaghetti-monster/anduril/cfg-ff-pl47g2.h32
-rw-r--r--spaghetti-monster/anduril/cfg-ff-rot66-219.h4
-rw-r--r--spaghetti-monster/anduril/cfg-ff-rot66.h47
-rw-r--r--spaghetti-monster/anduril/cfg-ff-rot66g2.h7
-rw-r--r--spaghetti-monster/anduril/cfg-fw3a-219.h5
-rw-r--r--spaghetti-monster/anduril/cfg-fw3a-nofet.h17
-rw-r--r--spaghetti-monster/anduril/cfg-fw3a.h42
-rw-r--r--spaghetti-monster/anduril/cfg-fw3x-lume1.h27
-rw-r--r--spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h68
-rw-r--r--spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h42
-rw-r--r--spaghetti-monster/anduril/cfg-mateminco-mf01s.h26
-rw-r--r--spaghetti-monster/anduril/cfg-mateminco-mt35-mini.h53
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-dm11-boost.h (renamed from spaghetti-monster/anduril/cfg-noctigon-dm11-12v.h)81
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-dm11-nofet.h9
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-dm11-sbt90.h59
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-dm11.h63
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k1-boost.h (renamed from spaghetti-monster/anduril/cfg-noctigon-k1-12v.h)62
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h65
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k1.h46
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h13
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k9.3-nofet.h56
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-219.h10
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-fet.h63
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-nofet.h84
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k9.3.c5
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k9.3.h118
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4-12v.h70
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4-219.h4
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h4
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4-2ch.h16
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4-boost.h16
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h12
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4-tintramp.h12
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4.h68
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-m44.h134
-rw-r--r--spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h125
-rw-r--r--spaghetti-monster/anduril/cfg-sofirn-sc21-pro.h14
-rw-r--r--spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h60
-rw-r--r--spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h4
-rw-r--r--spaghetti-monster/anduril/cfg-sofirn-sp36.h4
-rwxr-xr-xspaghetti-monster/anduril/cfg-thefreeman-boost-fwaa.h116
-rw-r--r--spaghetti-monster/anduril/cfg-thefreeman-boost21-6a.h119
-rw-r--r--spaghetti-monster/anduril/cfg-thefreeman-lin16dac.h66
-rw-r--r--spaghetti-monster/anduril/cfg-wurkkos-fc13.h17
-rw-r--r--spaghetti-monster/anduril/cfg-wurkkos-ts10-rgbaux.h85
-rw-r--r--spaghetti-monster/anduril/cfg-wurkkos-ts10.h85
-rw-r--r--spaghetti-monster/anduril/cfg-wurkkos-ts11.h19
-rw-r--r--spaghetti-monster/anduril/cfg-wurkkos-ts25.h87
-rw-r--r--spaghetti-monster/anduril/channel-modes.c237
-rw-r--r--spaghetti-monster/anduril/channel-modes.h26
-rw-r--r--spaghetti-monster/anduril/config-default.h49
-rw-r--r--spaghetti-monster/anduril/config-mode.c82
-rw-r--r--spaghetti-monster/anduril/config-mode.h36
-rw-r--r--spaghetti-monster/anduril/factory-reset-fsm.h26
-rw-r--r--spaghetti-monster/anduril/factory-reset.c49
-rw-r--r--spaghetti-monster/anduril/factory-reset.h26
-rw-r--r--spaghetti-monster/anduril/ff-strobe-modes.c33
-rw-r--r--spaghetti-monster/anduril/ff-strobe-modes.h26
-rw-r--r--spaghetti-monster/anduril/hank-cfg.h15
-rw-r--r--spaghetti-monster/anduril/load-save-config-fsm.h174
-rw-r--r--spaghetti-monster/anduril/load-save-config.c185
-rw-r--r--spaghetti-monster/anduril/load-save-config.h186
-rw-r--r--spaghetti-monster/anduril/lockout-mode-fsm.h26
-rw-r--r--spaghetti-monster/anduril/lockout-mode.c168
-rw-r--r--spaghetti-monster/anduril/lockout-mode.h27
-rw-r--r--spaghetti-monster/anduril/misc.c33
-rw-r--r--spaghetti-monster/anduril/misc.h26
-rw-r--r--spaghetti-monster/anduril/momentary-mode.c33
-rw-r--r--spaghetti-monster/anduril/momentary-mode.h26
-rw-r--r--spaghetti-monster/anduril/off-mode.c278
-rw-r--r--spaghetti-monster/anduril/off-mode.h29
-rw-r--r--spaghetti-monster/anduril/ramp-mode-fsm.h31
-rw-r--r--spaghetti-monster/anduril/ramp-mode.c305
-rw-r--r--spaghetti-monster/anduril/ramp-mode.h117
-rw-r--r--spaghetti-monster/anduril/smooth-steps.c47
-rw-r--r--spaghetti-monster/anduril/smooth-steps.h19
-rw-r--r--spaghetti-monster/anduril/sos-mode.c31
-rw-r--r--spaghetti-monster/anduril/sos-mode.h26
-rw-r--r--spaghetti-monster/anduril/strobe-modes-fsm.h53
-rw-r--r--spaghetti-monster/anduril/strobe-modes.c135
-rw-r--r--spaghetti-monster/anduril/strobe-modes.h66
-rw-r--r--spaghetti-monster/anduril/sunset-timer.c40
-rw-r--r--spaghetti-monster/anduril/sunset-timer.h26
-rw-r--r--spaghetti-monster/anduril/tactical-mode.c44
-rw-r--r--spaghetti-monster/anduril/tactical-mode.h44
-rw-r--r--spaghetti-monster/anduril/tempcheck-mode.c41
-rw-r--r--spaghetti-monster/anduril/tempcheck-mode.h26
-rw-r--r--spaghetti-monster/anduril/tint-ramping.c27
-rw-r--r--spaghetti-monster/anduril/tint-ramping.h26
-rw-r--r--spaghetti-monster/anduril/version-check-mode.c42
-rw-r--r--spaghetti-monster/anduril/version-check-mode.h28
-rw-r--r--spaghetti-monster/anduril/version.h2
-rw-r--r--spaghetti-monster/anduril/wurkkos-cfg.h26
-rw-r--r--spaghetti-monster/baton/baton.c4
-rw-r--r--spaghetti-monster/chan-aux.c11
-rw-r--r--spaghetti-monster/chan-aux.h25
-rw-r--r--spaghetti-monster/chan-rgbaux.c35
-rw-r--r--spaghetti-monster/chan-rgbaux.h72
-rw-r--r--spaghetti-monster/darkhorse/darkhorse.c16
-rw-r--r--spaghetti-monster/fireflies-ui/fireflies-ui.c180
-rw-r--r--spaghetti-monster/fsm-adc.c97
-rw-r--r--spaghetti-monster/fsm-adc.h54
-rw-r--r--spaghetti-monster/fsm-channels.c357
-rw-r--r--spaghetti-monster/fsm-channels.h141
-rw-r--r--spaghetti-monster/fsm-eeprom.c28
-rw-r--r--spaghetti-monster/fsm-eeprom.h32
-rw-r--r--spaghetti-monster/fsm-events.c35
-rw-r--r--spaghetti-monster/fsm-events.h31
-rw-r--r--spaghetti-monster/fsm-main.c116
-rw-r--r--spaghetti-monster/fsm-main.h25
-rw-r--r--spaghetti-monster/fsm-misc.c63
-rw-r--r--spaghetti-monster/fsm-misc.h48
-rw-r--r--spaghetti-monster/fsm-pcint.c26
-rw-r--r--spaghetti-monster/fsm-pcint.h25
-rw-r--r--spaghetti-monster/fsm-ramping.c339
-rw-r--r--spaghetti-monster/fsm-ramping.h230
-rw-r--r--spaghetti-monster/fsm-random.c25
-rw-r--r--spaghetti-monster/fsm-random.h25
-rw-r--r--spaghetti-monster/fsm-standby.c32
-rw-r--r--spaghetti-monster/fsm-standby.h25
-rw-r--r--spaghetti-monster/fsm-states.c25
-rw-r--r--spaghetti-monster/fsm-states.h27
-rw-r--r--spaghetti-monster/fsm-wdt.c34
-rw-r--r--spaghetti-monster/fsm-wdt.h25
-rw-r--r--spaghetti-monster/meteor/meteor.c16
-rw-r--r--spaghetti-monster/ramping-ui/ramping-ui.c62
-rw-r--r--spaghetti-monster/rampingios/rampingiosv3.c100
-rw-r--r--spaghetti-monster/spaghetti-monster.h24
-rw-r--r--spaghetti-monster/werner/werner.c64
174 files changed, 6031 insertions, 4084 deletions
diff --git a/spaghetti-monster/anduril/BRANDS b/spaghetti-monster/anduril/BRANDS
index 4e73002..059f311 100644
--- a/spaghetti-monster/anduril/BRANDS
+++ b/spaghetti-monster/anduril/BRANDS
@@ -7,4 +7,5 @@ Lumintop 0300 - 0399
Fireflies 0400 - 0499
Mateminco 0500 - 0599
Sofirn 0600 - 0699
+Wurkkos 0700 - 0799
gChart 1600 - 1699
diff --git a/spaghetti-monster/anduril/ChangeLog.md b/spaghetti-monster/anduril/ChangeLog.md
new file mode 100644
index 0000000..0aaa392
--- /dev/null
+++ b/spaghetti-monster/anduril/ChangeLog.md
@@ -0,0 +1,245 @@
+# ChangeLog
+
+Items should be sorted roughly in order of most to least important, with
+model-specific changes at the bottom. Most items should use one of these
+formats:
+
+ - Breaking change:
+ - Added
+ - Fixed
+ - Removed
+ - Changed
+ - Documented
+ - @brand-model: Hardware-specific change (NNNN, NNNN, ...)
+
+# Next
+
+# 2023-10-31
+
+General:
+
+- Finally finished converting all lights to the multi-channel API.
+ (but a few are untested due to hardware being unavailable)
+- Enabled smooth steps on almost all supported lights, including old ones.
+ (in some cases, on old t85 lights, some other things may have been removed
+ to make room)
+- Made smooth steps work better, and fixed several bugs.
+- Added/fixed RGB aux voltage on dual-fuel AA/li-ion drivers.
+- Fixed delay timing on all supported MCUs, so 1 "second" is closer to an
+ actual second (like in beacon mode or biking mode).
+ (especially attiny1616, which was 17% too slow)
+- Raised default thermal limit to 50C on attiny1616-based lights, since
+ it doesn't need as big an error margin as older MCUs.
+- Fixed missing item in globals menu on some lights.
+- Misc internal improvements and per-light tweaks.
+- Added a ChangeLog.md, to periodically summarize changes in a format which is
+ more readable than the raw commit logs.
+
+New lights:
+
+- @thefreeman-boost21-6a: Added. (1631)
+ (very nice HDR boost driver which fits into a FW3A)
+- @thefreeman-boost-fwaa: Added. (1632)
+ (very nice AA/li-ion HDR boost driver which fits into a FWAA)
+
+Hardware-specific changes:
+
+- Upgraded several builds to use delta-sigma modulation (DSM), for
+ lower lows and smoother ramping with less flicker or ripple:
+ - @blf-lt1 (0621)
+ - @blf-lt1-t1616 (0622)
+ - @emisar-d4k-3ch (0151)
+ (**dramatically** improves resolution and low modes on its 8-bit channel)
+ - @noctigon-dm11-boost (0273)
+ - @noctigon-kr4-boost (0216)
+ - @noctigon-k1-boost (0253)
+ - @noctigon-m44 (0143)
+
+- Upgraded some builds to use dynamic PWM, for lower lows and smoother ramping:
+ - @blf-q8-t1616, @sofirn-sp36-t1616 (0613, 0614)
+ - @gchart-fet1-t1616 (1618)
+ - @noctigon-k1-sbt90 (0252)
+
+- @wurkkos-ts10, @wurkkos-ts10-rgbaux: Fixed too-high default ceiling. (0713, 0714)
+
+# 2023-10-01
+
+General:
+
+- Updated a bunch of lights to work with the new multi-channel API.
+- Added "smooth steps" a.k.a. "soft start", to make on/off and step changes
+ smooth. (enabled by default, can be turned off in Ramp Extras menu)
+- User can save a different channel mode for each strobe-group mode.
+- Made strobes/blinkies more responsive in Tactical Mode.
+- Fixed bug: Tactical Mode clobbers strobe group memory.
+- Fixed some minor post-off voltage display bugs.
+- Made RGB button brightness update faster in blinky modes.
+- Fixed bug: Wrong channel after colored factory reset.
+- @attiny1616, @attiny1634: Partially fixed oscillating aux LED voltage
+ colors while off. Better than before, but can still happen in some cases.
+- @attiny1616: Fixed spurious voltage warnings in sleep mode. (it could
+ sometimes go from Lockout mode to Off mode by itself)
+- Lots of internal refactoring.
+
+New lights:
+
+- @emisar-2ch-fet: Added. (0136)
+- @emisar-d4k-3ch: Added. (0151)
+- @noctigon-m44: Added. (0143)
+- @wurkkos-ts10-rgbaux: Added. (0713)
+
+Hardware-specific changes:
+
+- @ff-e01, @ff-pl47, @ff-pl47g2: Enabled smooth steps instead of SOS mode.
+ (0421, 0422, 0423, 0441)
+- @emisar-2ch, @noctigon-m44: Added RGB aux channel modes. (0135, 0143)
+- @emisar-2ch-fet, @noctigon-k9.3: New ramps with better-calibrated shape.
+ (0136, 0261)
+- @emisar-d4v2-nofet: New ramp table. (0115)
+- @emisar-d4sv2-tintramp: Removed / renamed. (0135, 0136)
+- @noctigon-k9.3: Fixed years-old mess. Merged builds, converted to
+ multi-channel, removed old builds, generally got K9.3 working quite a bit
+ better. (0261, 0262, 0263, 0265, 0266, 0267)
+- @noctigon-m44: Lower moon, and greatly reduced flicker. (0143)
+- @sofirn-lt1s-pro: Allow configuring the blink channel. (0623)
+- @wurkkos: Raised default temperature limit to 50 C. (07xx)
+- @wurkkos-ts10: Better / smoother ramp. (0713, 0714)
+
+# 2023-06-29
+
+- Fixed red aux blink on 1st frame of post-off voltage display
+- Removed Harry Potter references because its author (J.K. Rowling) spreads
+ hate
+- @noctigon-kr4: Fixed thermal regulation (0211, 0212, 0213, 0214, 0215, 0216)
+
+# 2023-05-30
+
+- Breaking change: Changed version check from YYYYMMDDXXXX to XXXX.YYYY-MM-DD,
+ where the punctuation makes a "buzz" instead of number blinks.
+- Fixed issue where some lights always thought they were overheating (when
+ cold, or after a factory reset)
+- Fixed aux pre-flash
+- Fixed a rare corner case where gradual brightness adjustments stopped at 99%
+ power instead of 100% power while going up from level 149/150 to 150/150 on
+ some lights
+- Changed "0" digit readout to use the same timing as other short blinks, to
+ help ensure it works on all lights.
+- Minor code changes with no runtime impact
+- Documented new version check format
+- @wurkkos: Added red+blue police strobe (0715, 0716, 0717)
+- @noctigon-kr4: Broke thermal regulation (oops) (0211, 0212, 0213, 0214,
+ 0215, 0216)
+- @noctigon-kr4: Use 7 aux channel modes instead of 3 (0211, 0212, 0213, 0214,
+ 0215, 0216)
+- @emisar-d4v2: Changed number blinks from aux to main LEDs by default (0113,
+ 0114, 0115, 0123)
+
+# 2023-05-17
+
+- @noctigon-dm11-12v: Renamed to noctigon-dm11-boost (0273)
+- @noctigon-dm11-boost: Now supported in multi-channel branch (0273)
+
+# 2023-05-02
+
+- Added ability to set channel mode for number readouts (batt check, temp
+ check, version check). Press 3C in batt check mode to change the blink
+ channel.
+- Changed post-off voltage display:
+ - Display for 4s by default, not 5s
+ - Made timing user-configurable in battcheck 7H menu item 2
+ (1 click per second the display should last)
+ - Use low brightness when torch was at moon level before
+ - Skip the voltage display after UI actions which didn't change the mode
+ (like "Off -> 7C" to change aux LED settings)
+- Changed RGB button behavior: While torch is on, use low mode below a
+ configured level for real-time voltage color display, instead of always
+ using high mode
+- Fixed bug: Channel change could stick when activating a config menu from
+ battcheck (the blink function changed the channel, then the config menu
+ saved it, then the blink function restored it, then the config menu restored
+ it to the value it saved, which was wrong)
+- Documented ramp 6C, ramp 4C, ramp 4H, lockout 3H, battcheck 3C, and post-off
+ voltage display config
+- @emisar-d4v2: Added the rest of the aux RGB colors as channel modes, and set
+ aux "white" as the mode it uses to blink out numbers (0113, 0114, 0115,
+ 0123)
+- @wurkkos-ts10: Converted to multi-channel, and gave it a new ramp with
+ better low modes (0714)
+- @wurkkos-ts25: Converted to multi-channel, and gave it a smoother ramp
+ (0715)
+- @wurkkos: Added Wurkkos FC13 and TS11 (0716, 0717)
+
+# 2023-04-29
+
+- Changed lockout mode:
+ - Fixed manual memory timer and tint
+ - Added 3H for next channel (to change channels without unlocking, was
+ possible before but needed to be updated for the new channel system)
+ - Made 3H+ use mem level instead of lowest moon (this is needed for
+ making the channel discernible, and also helps make aux LED controls
+ stand out more)
+- @emisar, @noctigon: Added Extended Simple UI to Hank's config, so a few more
+ features are allowed in simple mode
+- @emisar-d4v2, @noctigon-kr4: Slightly smaller ROM size
+- @emisar-d4sv2: Converted to multi-channel, and updated it to use dynamic PWM
+ for a smoother ramp with lower lows (0133, 0134)
+- @noctigon-kr4: Converted to multi-channel (0211, 0212, 0213, 0214)
+- @noctigon-kr4: Don't blink at top of regulated power (0211, 0213, 0214)
+
+# 2023-04-28
+
+- Added voltage display (by color) on RGB aux LEDs after turning the main LEDs
+ off
+- Changed RGB aux: Always preview in high mode while changing color or pattern
+- Changed default RGB aux color in lockout mode: Uses blinking voltage,
+ instead of blinking disco (unless model-specific config defines a different
+ default)
+- Changed tactical mode default config: only use 2-color strobe if it's on
+ main LEDs, not aux LEDs
+- @emisar-d4v2: Smoother ramp (0113, 0114)
+- @emisar-d4v2: Added hidden channel modes for RGB aux LEDs (0113, 0114, 0115)
+
+# 2023-04-27
+
+- Changed channel mode menu to preview channels during configuration
+- Changed tactical mode default config: use 2-color police strobe if it
+ exists, instead of single-color strobe
+- Fixed unnecessary flickering when changing channel modes from/to the same
+ value
+- Fixed sleep voltage measurement on attiny1616
+- @noctigon-kr4-tintramp: Converted to multi-channel, renamed to
+ noctigon-kr4-2ch (0215)
+
+# 2023-04-25
+
+- Fixed old old bug(s) with voltage measurement while asleep, while also
+ adding other improvements:
+ - Fixed oscillating RGB aux colors while asleep in voltage mode, mostly.
+ Some much smaller oscillations are still possible, but the primary issue
+ has been fixed, I think.
+ - Fixed old old bug which could cause spurious low-voltage warnings while
+ asleep (previously required a messy code workaround).
+ - Reduced avg standby power by ~15uA.
+ - Now measures voltage every 1s instead of 8s while asleep.
+ - Changed RGB aux behavior: Puts aux LEDs in high mode for 3 seconds after
+ light goes to sleep (much easier to see the voltage this way)
+ (this change was obsoleted soon by a better post-off voltage display)
+ - Broke sleep voltage measurement on attiny1616 (oops)
+- Changed internal details for how gradual ramping works
+- @emisar-d4sv2-tintramp: Converted to multi-channel, renamed to emisar-2ch.
+ (0135)
+- @sofirn-lt1s-pro: Updated to use today's new code internals (0623)
+
+# 2023-04-19
+
+- Added stepped tint ramping
+- Documented new channel modes system
+- @sofirn-lt1s-pro: Added white-only auto-tint mode (0623)
+
+# Older: TODO
+
+- Need to import old commit logs as changelog items
+
+
+<!-- vim: set textwidth=78 shiftwidth=2 -->
+
diff --git a/spaghetti-monster/anduril/MODELS b/spaghetti-monster/anduril/MODELS
index 3071b60..f4e9fdf 100644
--- a/spaghetti-monster/anduril/MODELS
+++ b/spaghetti-monster/anduril/MODELS
@@ -14,28 +14,36 @@ Model Name MCU
0132 emisar-d4s-219c attiny85
0133 emisar-d4sv2 attiny1634
0134 emisar-d4sv2-219 attiny1634
-0135 emisar-d4sv2-tintramp attiny1634
-0136 emisar-d4sv2-tintramp-fet attiny1634
+0135 emisar-2ch attiny1634
+0135 emisar-d4sv2-tintramp attiny1634 (old)
+0136 emisar-2ch-fet attiny1634
+0136 emisar-d4sv2-tintramp-fet attiny1634 (old)
0141 emisar-d18 attiny85
0142 emisar-d18-219 attiny85
+0143 noctigon-m44 attiny1634
+0151 emisar-d4k-3ch attiny1634
0211 noctigon-kr4 attiny1634
0212 noctigon-kr4-nofet attiny1634
0213 noctigon-kr4-219 attiny1634
0214 noctigon-kr4-219b attiny1634
-0215 noctigon-kr4-tintramp attiny1634
-0216 noctigon-kr4-12v attiny1634
+0215 noctigon-kr4-2ch attiny1634
+0215 noctigon-kr4-tintramp attiny1634 (old)
+0216 noctigon-kr4-boost attiny1634
+0216 noctigon-kr4-12v attiny1634 (old)
0251 noctigon-k1 attiny1634
0252 noctigon-k1-sbt90 attiny1634
-0253 noctigon-k1-12v attiny1634
+0253 noctigon-k1-boost attiny1634
+0253 noctigon-k1-12v attiny1634 (old)
0261 noctigon-k9.3 attiny1634
0262 noctigon-k9.3-nofet attiny1634
0263 noctigon-k9.3-219 attiny1634
-0265 noctigon-k9.3-tintramp-nofet attiny1634
-0266 noctigon-k9.3-tintramp-fet attiny1634
-0267 noctigon-k9.3-tintramp-219 attiny1634
+0265 noctigon-k9.3-tintramp-nofet attiny1634 (old, use 0262)
+0266 noctigon-k9.3-tintramp-fet attiny1634 (old, use 0261)
+0267 noctigon-k9.3-tintramp-219 attiny1634 (old, use 0263)
0271 noctigon-dm11 attiny1634
0272 noctigon-dm11-nofet attiny1634
-0273 noctigon-dm11-12v attiny1634
+0273 noctigon-dm11-boost attiny1634
+0273 noctigon-dm11-12v attiny1634 (old)
0274 noctigon-dm11-sbt90 attiny1634
0311 fw3a attiny85
0312 fw3a-219 attiny85
@@ -59,8 +67,18 @@ Model Name MCU
0614 sofirn-sp36-t1616 attiny1616
0621 blf-lantern attiny85
0622 blf-lantern-t1616 attiny1616
+0623 sofirn-lt1s-pro attiny1616
0631 sofirn-sp10-pro attiny1616
+0632 sofirn-sc21-pro attiny1616
+0713 wurkkos-ts10-rgbaux attiny1616
+0714 wurkkos-ts10 attiny1616
+0715 wurkkos-ts25 attiny1616
+0716 wurkkos-fc13 attiny1616
+0717 wurkkos-ts11 attiny1616
1618 gchart-fet1-t1616 attiny1616
+1630 thefreeman-lin16dac attiny1616
+1631 thefreeman-boost21-6a attiny1616
+1632 thefreeman-boost-fwaa attiny1616
Duplicates:
diff --git a/spaghetti-monster/anduril/anduril-manual.txt b/spaghetti-monster/anduril/anduril-manual.txt
index 1873b8d..545af0f 100644
--- a/spaghetti-monster/anduril/anduril-manual.txt
+++ b/spaghetti-monster/anduril/anduril-manual.txt
@@ -182,8 +182,13 @@ While the light is on, a few actions are available:
- 2H: Change brightness (down).
- 3C: Switch to the other ramp style. (smooth / stepped)
- - 3H: Tint ramping (on lights which have it).
- - 3H: Momentary turbo (on lights with no tint ramping).
+ (or activate the next channel mode, when more than one is enabled)
+ (then use 6C instead, for smooth / stepped toggle)
+ - 6C: Switch to the other ramp style. (when 3C is mapped to next channel)
+
+ - 3H: Momentary turbo (when current channel has no tint to ramp).
+ - 3H: Tint ramping (only when current channel has adjustable tint).
+ - 4H: Momentary turbo, when 3H is mapped to tint.
- 4C: Go to lockout mode.
@@ -217,6 +222,9 @@ While the light is on, a few actions are available:
2: Anduril 2 style. Ramp -> 2C goes to ceiling,
or goes to full power if user ramped up to ceiling first.
This value also affects momentary turbo in Ramp and Off modes.
+ - Item 5: Configure "smooth steps".
+ 0: Disable smooth steps.
+ 1: Enable smooth steps.
Memory determines which brightness level the light goes to with 1 click
from off. There are three types of brightness memory to choose from:
@@ -247,6 +255,11 @@ To choose a memory style, set the configuration accordingly:
manual on zero
hybrid on non-zero
+If "smooth steps" is enabled, the stepped ramp uses a smooth animation
+between steps, and turning the light on/off has the edges smoothed off
+too. With "smooth steps" turned off, these brightness changes are
+immediate.
+
Sunset Timer
------------
@@ -279,8 +292,9 @@ more than 2 times when the light is off:
- 3H: Strobe modes, starting with the most recently used strobe.
- 4C: Lockout mode.
- 5C: Momentary mode.
+ - 6C: Tactical mode.
- 7C / 7H: Aux LED configuration.
- - 9H: Misc configuration menu. (only on some lights)
+ - 9H: Misc Config menu. (only on some lights)
- 10H: Simple UI configuration menu.
- 13H: Factory reset (on some lights).
- 15C or more: Version check.
@@ -315,6 +329,8 @@ lockout mode has two levels:
- 2H: Light up at the highest floor level.
(or the manual mem level, if there is one)
+ - 3H: Next channel mode (if more than one is enabled).
+
It is also possible to make the light lock itself automatically after
being turned off. To enable this, go to lockout mode and use a 10H
action to activate the auto-lock config menu. Release the button after
@@ -356,70 +372,80 @@ In more detail, this is what each blinky / utility mode does:
Battery check:
- Blinks out the battery voltage per cell. Full is 4.2V, empty is
- about 3.0V. The light blinks the whole-number digit first, pauses,
- then blinks out the "tenths" digit. Then a longer pause, and it
- repeats.
- So for 4.2V, it would be "blink-blink-blink-blink .. blink-blink".
-
- A "zero" digit is represented by a very quick blink.
-
- The voltage config menu has one setting:
-
- 1. Voltage correction factor. This adjusts the battery
- measurement sensor, allowing the user to add or subtract up to
- 0.30V in 0.05V steps. Click N times to enter a value:
-
- 1C: -0.30V
- 2C: -0.25V
- 3C: -0.20V
- 4C: -0.15V
- 5C: -0.10V
- 6C: -0.05V
- 7C: default, 0V
- 8C: +0.05V
- 9C: +0.10V
- 10C: +0.15V
- 11C: +0.20V
- 12C: +0.25V
- 13C: +0.30V
+ Blinks out the battery voltage per cell. Full is 4.2V, empty is
+ about 3.0V. The light blinks the whole-number digit first, pauses,
+ then blinks out the "tenths" digit. Then a longer pause, and it
+ repeats.
+ So for 4.2V, it would be "blink-blink-blink-blink .. blink-blink".
+
+ A "zero" digit is represented by a very quick blink.
+
+ On lights with more than one set of LEDs, pressing 3C during batt
+ check mode can select which set of LEDs (which channel mode) it uses
+ to blink out numbers.
+
+ The voltage config menu has these settings:
+
+ 1. Voltage correction factor. This adjusts the battery
+ measurement sensor, allowing the user to add or subtract up to
+ 0.30V in 0.05V steps. Click N times to enter a value:
+
+ 1C: -0.30V
+ 2C: -0.25V
+ 3C: -0.20V
+ 4C: -0.15V
+ 5C: -0.10V
+ 6C: -0.05V
+ 7C: default, 0V
+ 8C: +0.05V
+ 9C: +0.10V
+ 10C: +0.15V
+ 11C: +0.20V
+ 12C: +0.25V
+ 13C: +0.30V
+
+ 2. Post-off voltage display timeout. (only on lights with RGB aux)
+ This setting determines how many seconds the RGB aux LEDs
+ display the voltage color after the torch goes to sleep. Click
+ once per desired second, or zero times to turn this function
+ off.
Temperature check:
- Blinks out the current temperature in degrees C. This number
- should be pretty close to what a real thermometer says. If not, it
- would be a good idea to enter the thermal config menu and calibrate
- the sensor. Or let the light settle to room temperature, then use
- factory reset to auto-calibrate the sensor.
+ Blinks out the current temperature in degrees C. This number should
+ be pretty close to what a real thermometer says. If not, it would
+ be a good idea to enter the thermal config menu and calibrate the
+ sensor. Or let the light settle to room temperature, then use
+ factory reset to auto-calibrate the sensor.
- The thermal config menu has two settings:
+ The thermal config menu has two settings:
- - Current temperature. Click once per degree C to calibrate the
- sensor. For example, if the ambient temperature is 21 C, then
- click 21 times.
+ - Current temperature. Click once per degree C to calibrate the
+ sensor. For example, if the ambient temperature is 21 C, then
+ click 21 times.
- - Temperature limit. This sets the maximum temperature the light
- can reach before it will start doing thermal regulation to keep
- itself from overheating. Click once per degree C above 30. For
- example, to set the limit to 50 C, click 20 times. The default
- is 45 C, and the highest value it will allow is 70 C.
+ - Temperature limit. This sets the maximum temperature the light
+ can reach before it will start doing thermal regulation to keep
+ itself from overheating. Click once per degree C above 30. For
+ example, to set the limit to 50 C, click 20 times. The default is
+ 45 C, and the highest value it will allow is 70 C.
Beacon mode:
- Blinks at a slow speed. The light stays on for 100ms, and then
- stays off until the next blink. The brightness and the number of
- seconds between pulses are configurable:
+ Blinks at a slow speed. The light stays on for 100ms, and then
+ stays off until the next blink. The brightness and the number of
+ seconds between pulses are configurable:
- - Brightness is the user's memorized ramp level, so set this in
- ramping mode before activating beacon mode. Follows the same
- memory rules as ramping -- automatic, manual, or hybrid.
+ - Brightness is the user's memorized ramp level, so set this in
+ ramping mode before activating beacon mode. Follows the same
+ memory rules as ramping -- automatic, manual, or hybrid.
- - Speed is configured by holding the button. The light should
- blink once per second while holding the button. Release it
- after the desired amount of time has passed, to set a new
- beacon speed.
- For example, to do a 10-second alpine beacon, hold the button
- for 10 seconds.
+ - Speed is configured by holding the button. The light should
+ blink once per second while holding the button. Release it
+ after the desired amount of time has passed, to set a new beacon
+ speed.
+ For example, to do a 10-second alpine beacon, hold the button
+ for 10 seconds.
SOS mode:
@@ -450,6 +476,7 @@ In all of these modes, a few actions are available:
- 2C: Next strobe / mood mode.
- 1H: Increase brightness, or strobe faster. (except lightning)
- 2H: Decrease brightness, or strobe slower. (except lightning)
+ - 4C: Previous strobe / mood mode.
- 5C: Go to momentary mode, for a momentary strobe.
(this is useful for light painting)
@@ -485,6 +512,11 @@ In more detail, here is what each mode does:
Be careful about heat in this mode, if using it for a long time.
+ - Police strobe (on some lights)
+
+ 2-color police style strobe. Only works on lights with 2 or more
+ colors.
+
- Lightning storm mode
Flashes at random brightness and random speed to simulate lightning
@@ -519,6 +551,46 @@ To exit this mode, physically disconnect power by unscrewing the tailcap
or battery tube.
+Tactical Mode
+--------------
+
+Click 6 times from Off to enter Tactical Mode, or 6 times in Tactical
+Mode to exit and go back to "Off".
+
+Tactical Mode provides instant momentary access to high, low, and
+strobe, but each of these is configurable. The inputs are:
+
+ - 1H: High
+ - 2H: Low
+ - 3H: Strobe
+
+Each of these only lasts as long as you hold the button.
+
+Other commands in Tactical Mode are:
+
+ - 6C: exit (go back to Off Mode)
+ - 7H: Tactical Mode config menu
+ - 1st blink: configure tactical slot 1
+ - 2nd blink: configure tactical slot 2
+ - 3rd blink: configure tactical slot 3
+
+To change what is in a tactical slot, press 7H, then release the button
+after the 1st, 2nd, or 3rd blink. Then enter a number. Each click adds
+1, and each hold adds 10. The number can be:
+
+ - 1 to 150: set the brightness level
+ - 0: last-used strobe mode
+ - 151+: go directly to a specific strobe mode
+ 151 = party strobe
+ 152 = tactical strobe
+ 153+ = other strobes, in the same order they're in in the "Off -> 3H"
+ strobe group
+
+This assumes the light has a ramp 150 levels long. Strobe modes start
+at the ramp size plus 1, so it may be different if a light has a
+different ramp size.
+
+
Configuration Menus
-------------------
@@ -597,14 +669,17 @@ Version Check Mode
------------------
This allows people to see which version of the firmware is installed on
-their light. The format for this is (usually) 12 digits -- a date
-followed by a model number. YYYYMMDDBBPP
+their light. The format for this is (usually) 12 digits -- a model
+number and a date. BBPP.YYYY-MM-DD
+ - BB: Brand ID
+ - PP: Product ID
- YYYY: Year
- MM: Month
- DD: Day
- - BB: Brand ID
- - PP: Product ID
+
+Versions before 2023-05-30 used YYYYMMDDBBPP format.
+Anduril 1 used only YYYYMMDD format, or none at all.
The date is when the firmware was compiled. If the vendor did not set
this value, it defaults to 1969-07-20, the date of first human contact
@@ -688,19 +763,26 @@ For lights with front-facing aux LEDs, the aux LEDs typically stay off
when the main emitters are on, and when the light is otherwise awake.
The aux LEDs on most lights only turn on when the light is asleep.
+When a light has a single-color aux LED and no RGB, it fast-blinks the
+aux LED in "off" modes when voltage is low.
+
Misc Config Menu
----------------
Some models may have an extra config menu for settings which don't fit
-anywhere else. These settings are, in order:
+anywhere else. This menu is located at "Off -> 9H" in the advanced UI.
- - Tint ramp style:
+These settings are, in order:
- 0 = smooth (blend channels in any proportion)
- 1 = toggle (only one channel active at a time)
+ - Tint ramp style: (on some lights)
- - Jump Start level:
+ 0 : smooth ramp (blend channels in any proportion)
+ 1 : middle tint only
+ 2 : extreme tints only (only one channel active at a time)
+ 3+: stepped ramp with 3+ steps
+
+ - Jump Start level: (on some lights)
Some lights are prone to starting up slowly at low levels, so they
have an option to "jump start" the engine by pulsing a higher power
@@ -710,36 +792,71 @@ anywhere else. These settings are, in order:
The value can be from 1 to 150, but is usually between 20 and 50.
These settings are hardware-specific and may not be present on all
-lights. The number of settings in the global menu depends on the
+lights. The number of settings in the Misc Config Menu depends on the
hardware model and the firmware version.
-Tint Ramping
-------------
+Channel Modes (a.k.a. Tint Ramping or Multi Channel controls)
+-------------------------------------------------------------
Some lights have more than one set of LEDs which can be adjusted to
-change the color temperature of the light. On these models, there is a
-global button mapping which works at all times unless it's overridden by
-the mode the light is in:
+change the beam color, shape, or other properties. These lights have
+features like tint ramping and channel modes.
+
+On these models, there are some global button mappings which work at all
+times unless they're overridden by the mode the light is in:
+
+ - 3C: Next channel mode
+ - 3H: Adjust current channel mode (ramp tint, for example)
+ - 9H: Channel mode config menu
+
+Details depend on the exact type of light used. For example, if a light
+has LEDs in cool white, warm white, and red... that light might have a
+few channel modes:
+
+ - White blend (adjustable CCT / tint ramping)
+ - Red only
+ - Auto-tint
+
+On a light like this, the user could press 3C to rotate through these
+different channel modes... white, then red, then auto, then back to
+white.
+
+Additionally, in the "white blend" mode, the user could press 3H to
+manually adjust the balance between warm white and cool white.
+
+Finally, if the user decides they don't want all of the modes, they can
+turn some off. Press 9H (while on) to start the channel mode config
+menu. To disable the auto-tint mode, for example, it is the 3rd mode...
+so wait for the 3rd blink, then release the button. Then at the prompt,
+enter a value of 0 (wait for the prompt to time out without clicking
+anything). Afterward, the auto tint mode should no longer be in the
+channel mode rotation. To turn the mode back on later, do the same
+thing, but enter a value of 1 (click 1 time at the prompt).
+
+A light can have many different channel modes, so don't be shy about
+turning off any modes you don't use. It makes all the others easier to
+reach.
+
+If you turn off channel modes until only 1 remains, the "Ramp -> 3C"
+action reverts to its single-channel behavior -- switching between a
+smooth or stepped brightness ramp. Additionally, when a channel mode
+has nothing to adjust with 3H, the 3H action also reverts to its
+single-channel behavior -- momentary turbo.
- - 3H: Tint ramping
+The Misc Config Menu (Off -> 9H) may also have a setting to choose a
+tint ramp style. There are a few styles available, by entering
+different numbers into that config menu:
-So, at almost any time, click 3 times but hold the final press, and it
-can change the color of the light. This is best done with the light at
-a moderately high level, because it makes the changes smooth. Low
-levels have more coarse resolution and will typically change in very
-visible steps.
+ 0: smooth ramp
+ 1: middle tint only
+ 2: extreme tints only
+ 3+: stepped ramp with 3+ steps
-At both ends of the tint ramp, there is an "auto tint" mode. This
-automatically chooses a tint based on the brightness level... so it can
-be warm white while dim, or cool white while bright. Or vice-versa. To
-access this, ramp to the end of the tint range, then keep holding until
-the light blinks a second time.
+This setting only applies to modes with channel ramping (i.e. tint
+ramping), and only when that mode uses the default 3H event handler.
+Custom channel modes may work differently.
-The misc config menu also has a setting to choose a tint ramp style.
-This can be smooth, allowing the user to smoothly blend both channels in
-whatever ratio they desire... or it can be "tint toggle" style, where
-only one channel is active at a time.
UI Reference Table
@@ -758,9 +875,12 @@ Off Any 3C Battcheck mode
Off Full 3H Strobe mode (whichever was used last)
Off Any 4C Lockout mode
Off Full 5C Momentary mode
+Off Full 6C Tactical mode
Off Full 7C Aux LEDs: Next pattern
Off Full 7H Aux LEDs: Next color
-Off Full 9H Misc config menu (varies per light)
+Off Full 9H Misc Config menu (varies per light):
+ ?1: tint ramp style
+ ?2: jump start level
Off Full 10C Enable Simple UI
Off Simple 10H Disable Simple UI
Off Full 10H Simple UI ramp config menu:
@@ -776,48 +896,75 @@ Ramp Any 1H Ramp (up, with reversing)
Ramp Any 2H Ramp (down)
Ramp Any 2C Go to/from ceiling or turbo (configurable)
Ramp Full 3C Change ramp style (smooth / stepped)
-Ramp Any 3H Tint ramping (on some lights)
-Ramp Full 3H Momentary turbo (on lights without tint ramping)
+Ramp Full 6C (same as above, but on multi-channel lights)
+Ramp Full 3H Momentary turbo (when no tint ramping)
+Ramp Full 4H Momentary turbo (on multi channel lights)
Ramp Any 4C Lockout mode
Ramp Full 5C Momentary mode
Ramp Full 5H Sunset timer on, and add 5 minutes
-Ramp Full 7H Ramp config menu (1: floor, 2: ceiling, 3: speed/steps)
+Ramp Full 7H Ramp config menu: (for current ramp)
+ 1: floor
+ 2: ceiling
+ 3: speed / steps
Ramp Full 10C Turn on manual memory and save current brightness
Ramp Full 10H Ramp Extras config menu:
- 1: enable automatic mem
+ 1: switch to automatic mem, not manual mem
2: set manual mem timeout
3: ramp after moon or not
4: advanced UI turbo style
+ 5: smooth steps
+
+Multi-channel lights only:
+Any Any 3C Next channel mode (i.e. next color mode)
+Any Any 3H Tint ramp (if this mode can)
+Any Full 9H Channel mode enable/disable menu:
+ N: click (or not) to enable (disable) mode N
Lockout Any 1C/1H Momentary moon (lowest floor)
Lockout Any 2C/2H Momentary moon (highest floor, or manual mem level)
Lockout Any 3C Unlock (go to "Off" mode)
+Lockout Any 3H Next channel mode (if more than one enabled)
Lockout Any 4C On (ramp mode, memorized level)
Lockout Any 4H On (ramp mode, floor level)
Lockout Any 5C On (ramp mode, ceiling level)
Lockout Full 7C Aux LEDs: Next pattern
Lockout Full 7H Aux LEDs: Next color
-Lockout Full 10H Auto-lock config menu (1: set timeout)
+Lockout Full 10H Auto-lock config menu:
+ 1: set timeout in minutes (0 = no auto-lock)
Strobe (any) Full 1C Off
Strobe (any) Full 2C Next strobe mode
-Strobe (any) Full 3H Tint ramping (on some lights)
+Strobe (any) Full 3C Next channel mode (saved per strobe mode)
+Strobe (any) Full 4C Prev strobe mode
Strobe (any) Full 5C Momentary mode (using current strobe)
-Candle Full 1H/2H Brighter / dimmer
-Candle Full 5H Sunset timer on, add 5 minutes
Party strobe Full 1H/2H Faster / slower
Tactical strobe Full 1H/2H Faster / slower
-Biking Full 1H/2H Brighter / dimmer
+Police strobe - - None (brightness is Ramp Mode's last-used level)
Lightning Full 1H Interrupt current flash or start new one
+Candle Full 1H/2H Brighter / dimmer
+Candle Full 5H Sunset timer on, add 5 minutes
+Biking Full 1H/2H Brighter / dimmer
Batt check Any 1C Off
Batt check Full 2C Next blinky mode (Temp check, Beacon, SOS)
+Batt check Full 3C Next channel mode (for number blinks only)
Batt check Full 7H Voltage config menu
+ 1: voltage correction factor
+ ...
+ 5: -0.10V
+ 6: -0.05V
+ 7: no correction
+ 8: +0.05V
+ 9: +0.10V
+ ...
+ 2: post-off voltage display seconds
Temp check Full 1C Off
Temp check Full 2C Next blinky mode (Beacon, SOS, Batt check)
Temp check Full 7H Thermal config menu
+ 1: set current temperature
+ 2: set temperature limit
Beacon Full 1C Off
Beacon Full 1H Configure beacon timing
@@ -829,8 +976,19 @@ SOS Full 2C Next blinky mode (Batt check, Temp check, Beacon)
Momentary Full Any On (until button is released)
Momentary Full Disconnect power Exit Momentary mode
+Tactical Full 1H High (tactical slot 1)
+Tactical Full 2H Low (tactical slot 2)
+Tactical Full 3H Strobe (tactical slot 3)
+Tactical Full 6C Exit (go back to Off Mode)
+Tactical Full 7H Tactical Mode config menu:
+ 1: tactical slot 1
+ 2: tactical slot 2
+ 3: tactical slot 3
+
Config menus Full Hold Skip current item with no changes
Config menus Full Release Configure current item
+ (goes to Number Entry menu)
Number entry Full Click Add 1 to value for current item
Number entry Full Hold Add 10 to value for current item
+
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c
index 47cd00f..e46eeaf 100644
--- a/spaghetti-monster/anduril/anduril.c
+++ b/spaghetti-monster/anduril/anduril.c
@@ -1,22 +1,7 @@
-/*
- * Anduril: Narsil-inspired UI for SpaghettiMonster.
- * (Anduril is Aragorn's sword, the blade Narsil reforged)
- *
- * Copyright (C) 2017-2020 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 <http://www.gnu.org/licenses/>.
- */
+// Anduril: Narsil-inspired UI for SpaghettiMonster.
+// (Anduril is Aragorn's sword, the blade Narsil reforged)
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
/*
* Usually a program would be structured like this...
@@ -54,10 +39,10 @@
/********* specific settings for known driver types *********/
// Anduril config file name (set it here or define it at the gcc command line)
-//#define CONFIGFILE cfg-blf-q8.h
+//#define CFG_H cfg-blf-q8.h
#include "tk.h"
-#include incfile(CONFIGFILE)
+#include incfile(CFG_H)
/********* Include headers which need to be before FSM *********/
@@ -92,8 +77,11 @@
#include "spaghetti-monster.h"
/********* does this build target have special code to include? *********/
-#ifdef OVERRIDES_FILE
-#include incfile(OVERRIDES_FILE)
+#ifdef HWDEF_C_FILE
+#include incfile(HWDEF_C_FILE)
+#endif
+#ifdef CFG_C_FILE
+#include incfile(CFG_C_FILE)
#endif
@@ -101,7 +89,6 @@
#include "off-mode.h"
#include "ramp-mode.h"
-#include "load-save-config.h"
#include "config-mode.h"
#include "aux-leds.h"
#include "misc.h"
@@ -138,8 +125,10 @@
#include "tactical-mode.h"
#endif
-#ifdef USE_TINT_RAMPING
-#include "tint-ramping.h"
+// allow the channel mode handler even when only 1 mode
+// (so a tint ramp light could still use 3H even if there's no other mode)
+#if defined(USE_CHANNEL_MODES)
+#include "channel-modes.h"
#endif
#ifdef USE_FACTORY_RESET
@@ -153,6 +142,13 @@
#include "sos-mode.h"
#endif
+#ifdef USE_SMOOTH_STEPS
+#include "smooth-steps.h"
+#endif
+
+// this should be last, so other headers have a chance to declare values
+#include "load-save-config.h"
+
/********* Include all the app logic source files *********/
// (is a bit weird to do things this way,
@@ -197,8 +193,8 @@
#include "tactical-mode.c"
#endif
-#ifdef USE_TINT_RAMPING
-#include "tint-ramping.c"
+#if defined(USE_CHANNEL_MODES)
+#include "channel-modes.c"
#endif
#ifdef USE_FACTORY_RESET
@@ -213,6 +209,10 @@
#include "sos-mode.c"
#endif
+#ifdef USE_SMOOTH_STEPS
+#include "smooth-steps.c"
+#endif
+
// runs one time at boot, when power is connected
void setup() {
@@ -234,13 +234,13 @@ void setup() {
#if defined(USE_MANUAL_MEMORY) && defined(USE_MANUAL_MEMORY_TIMER)
// without this, initial boot-up brightness is wrong
// when manual mem is enabled with a non-zero timer
- if (manual_memory) memorized_level = manual_memory;
+ if (cfg.manual_memory) manual_memory_restore();
#endif
- #ifdef USE_TINT_RAMPING
- // add tint ramping underneath every other state
- push_state(tint_ramping_state, 0);
- #endif // ifdef USE_TINT_RAMPING
+ #if defined(USE_CHANNEL_MODES)
+ // add channel mode functions underneath every other state
+ push_state(channel_mode_state, 0);
+ #endif
push_state(off_state, 1);
@@ -250,10 +250,10 @@ void setup() {
// power clicky acts as a momentary mode
load_config();
- #ifdef USE_TINT_RAMPING
- // add tint ramping underneath every other state
- push_state(tint_ramping_state, 0);
- #endif // ifdef USE_TINT_RAMPING
+ #if defined(USE_CHANNEL_MODES)
+ // add channel mode functions underneath every other state
+ push_state(channel_mode_state, 0);
+ #endif
if (button_is_pressed())
// hold button to go to moon
@@ -275,7 +275,8 @@ void loop() {
#ifdef USE_AUX_RGB_LEDS_WHILE_ON
// display battery charge on RGB button during use
- if (! setting_rgb_mode_now) rgb_led_voltage_readout(1);
+ if (state == steady_state)
+ rgb_led_voltage_readout(actual_level > USE_AUX_RGB_LEDS_WHILE_ON);
#endif
if (0) {} // placeholder
@@ -316,7 +317,7 @@ void loop() {
// in simple mode, turn off after one readout
// FIXME: can eat the next button press
// (state changes in loop() act weird)
- if (simple_ui_active) set_state_deferred(off_state, 0);
+ if (cfg.simple_ui_active) set_state_deferred(off_state, 0);
else nice_delay_ms(1000);
#endif
}
@@ -342,6 +343,12 @@ void loop() {
}
#endif
+ #ifdef USE_SMOOTH_STEPS
+ else if (cfg.smooth_steps_style && smooth_steps_in_progress) {
+ smooth_steps_iter();
+ }
+ #endif
+
#ifdef USE_IDLE_MODE
else {
// doze until next clock tick
diff --git a/spaghetti-monster/anduril/aux-leds.c b/spaghetti-monster/anduril/aux-leds.c
index a3b905e..af59aa6 100644
--- a/spaghetti-monster/anduril/aux-leds.c
+++ b/spaghetti-monster/anduril/aux-leds.c
@@ -1,24 +1,7 @@
-/*
- * aux-leds.c: Aux LED functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef AUX_LEDS_C
-#define AUX_LEDS_C
+// aux-leds.c: Aux LED functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
#include "aux-leds.h"
@@ -27,14 +10,21 @@
void indicator_led_update(uint8_t mode, uint8_t tick) {
//uint8_t volts = voltage; // save a few bytes by caching volatile value
// turn off when battery is too low
+ #ifdef DUAL_VOLTAGE_FLOOR
+ if (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR))
+ || (voltage < DUAL_VOLTAGE_LOW_LOW)) {
+ #else
if (voltage < VOLTAGE_LOW) {
+ #endif
indicator_led(0);
}
//#ifdef USE_INDICATOR_LOW_BAT_WARNING
+ #ifndef DUAL_VOLTAGE_FLOOR // this isn't set up for dual-voltage lights like the Sofirn SP10 Pro
// fast blink a warning when battery is low but not critical
else if (voltage < VOLTAGE_RED) {
indicator_led(mode & (((tick & 0b0010)>>1) - 3));
}
+ #endif
//#endif
// normal steady output, 0/1/2 = off / low / high
else if ((mode & 0b00001111) < 3) {
@@ -68,17 +58,30 @@ void indicator_led_update(uint8_t mode, uint8_t tick) {
uint8_t voltage_to_rgb() {
static const uint8_t levels[] = {
// voltage, color
- 0, 0, // 0, R
- 33, 1, // 1, R+G
- 35, 2, // 2, G
- 37, 3, // 3, G+B
- 39, 4, // 4, B
- 41, 5, // 5, R + B
- 44, 6, // 6, R+G+B // skip; looks too similar to G+B
- 255, 6, // 7, R+G+B
+ 0, 0, // black
+ #ifdef DUAL_VOLTAGE_FLOOR
+ // AA / NiMH voltages
+ 9, 1, // R
+ 10, 2, // R+G
+ 11, 3, // G
+ 12, 4, // G+B
+ 13, 5, // B
+ 14, 6, // R + B
+ 15, 7, // R+G+B
+ 20, 0, // black
+ #endif
+ // li-ion voltages
+ 29, 1, // R
+ 33, 2, // R+G
+ 35, 3, // G
+ 37, 4, // G+B
+ 39, 5, // B
+ 41, 6, // R + B
+ 44, 7, // R+G+B // skip; looks too similar to G+B
+ 255, 7, // R+G+B
};
uint8_t volts = voltage;
- if (volts < VOLTAGE_LOW) return 0;
+ //if (volts < VOLTAGE_LOW) return 0;
uint8_t i;
for (i = 0; volts >= levels[i]; i += 2) {}
@@ -89,14 +92,18 @@ uint8_t voltage_to_rgb() {
// do fancy stuff with the RGB aux LEDs
// mode: 0bPPPPCCCC where PPPP is the pattern and CCCC is the color
// arg: time slice number
-void rgb_led_update(uint8_t mode, uint8_t arg) {
+void rgb_led_update(uint8_t mode, uint16_t arg) {
static uint8_t rainbow = 0; // track state of rainbow mode
static uint8_t frame = 0; // track state of animation mode
// turn off aux LEDs when battery is empty
// (but if voltage==0, that means we just booted and don't know yet)
uint8_t volts = voltage; // save a few bytes by caching volatile value
+ #ifdef DUAL_VOLTAGE_FLOOR
+ if ((volts) && (((voltage < VOLTAGE_LOW) && (voltage > DUAL_VOLTAGE_FLOOR)) || (voltage < DUAL_VOLTAGE_LOW_LOW))) {
+ #else
if ((volts) && (volts < VOLTAGE_LOW)) {
+ #endif
rgb_led_set(0);
#ifdef USE_BUTTON_LED
button_led_set(0);
@@ -107,22 +114,26 @@ void rgb_led_update(uint8_t mode, uint8_t arg) {
uint8_t pattern = (mode>>4); // off, low, high, blinking, ... more?
uint8_t color = mode & 0x0f;
- // preview in blinking mode is awkward... use high instead
- if ((! go_to_standby) && (pattern > 2)) { pattern = 2; }
-
+ // always preview in high mode
+ if (setting_rgb_mode_now) { pattern = 2; }
+
+ #ifdef USE_POST_OFF_VOLTAGE
+ // use voltage high mode for a few seconds after initial poweroff
+ // (but not after changing aux LED settings and other similar actions)
+ else if ((arg < (cfg.post_off_voltage * SLEEP_TICKS_PER_SECOND))
+ && (ticks_since_on < (cfg.post_off_voltage * SLEEP_TICKS_PER_SECOND))
+ && (ticks_since_on > 0) // don't blink red on 1st frame
+ ) {
+ // use high mode if regular aux level is high or prev level was high
+ pattern = 1 + ((2 == pattern) | (prev_level >= POST_OFF_VOLTAGE_BRIGHTNESS));
+ // voltage mode
+ color = RGB_LED_NUM_COLORS - 1;
+ }
+ #endif
- const uint8_t *colors = rgb_led_colors;
+ const uint8_t *colors = rgb_led_colors + 1;
uint8_t actual_color = 0;
if (color < 7) { // normal color
- #ifdef USE_K93_LOCKOUT_KLUDGE
- // FIXME: jank alert: this is dumb
- // this clause does nothing; it just uses up clock cycles
- // because without it, the K9.3's lockout mode fails and returns
- // to "off" after ~5 to 15 seconds when configured for a blinking
- // single color, even though there is no code path from lockout to
- // "off", and it doesn't act like a reboot either (no boot-up blink)
- rainbow = (rainbow + 1 + pseudo_rand() % 5) % 6;
- #endif
actual_color = pgm_read_byte(colors + color);
}
else if (color == 7) { // disco
@@ -197,6 +208,3 @@ void rgb_led_voltage_readout(uint8_t bright) {
}
#endif
-
-#endif
-
diff --git a/spaghetti-monster/anduril/aux-leds.h b/spaghetti-monster/anduril/aux-leds.h
index 71a14e3..fa97e6b 100644
--- a/spaghetti-monster/anduril/aux-leds.h
+++ b/spaghetti-monster/anduril/aux-leds.h
@@ -1,31 +1,14 @@
-/*
- * aux-leds.h: Aux LED functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef AUX_LEDS_H
-#define AUX_LEDS_H
+// aux-leds.h: Aux LED functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
#if defined(USE_INDICATOR_LED) && defined(TICK_DURING_STANDBY)
void indicator_led_update(uint8_t mode, uint8_t tick);
#endif
#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
uint8_t setting_rgb_mode_now = 0;
-void rgb_led_update(uint8_t mode, uint8_t arg);
+void rgb_led_update(uint8_t mode, uint16_t arg);
void rgb_led_voltage_readout(uint8_t bright);
/*
* 0: R
@@ -39,13 +22,14 @@ void rgb_led_voltage_readout(uint8_t bright);
* 8: voltage
*/
const PROGMEM uint8_t rgb_led_colors[] = {
- 0b00000001, // 0: red
- 0b00000101, // 1: yellow
- 0b00000100, // 2: green
- 0b00010100, // 3: cyan
- 0b00010000, // 4: blue
- 0b00010001, // 5: purple
- 0b00010101, // 6: white
+ 0b00000000, // 0: black
+ 0b00000001, // 1: red
+ 0b00000101, // 2: yellow
+ 0b00000100, // 3: green
+ 0b00010100, // 4: cyan
+ 0b00010000, // 5: blue
+ 0b00010001, // 6: purple
+ 0b00010101, // 7: white
};
// intentionally 1 higher than total modes, to make "voltage" easier to reach
// (at Hank's request)
@@ -56,13 +40,12 @@ const PROGMEM uint8_t rgb_led_colors[] = {
//#define RGB_LED_OFF_DEFAULT 0x18 // low, rainbow
#endif
#ifndef RGB_LED_LOCKOUT_DEFAULT
-#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, disco
+#define RGB_LED_LOCKOUT_DEFAULT 0x39 // blinking, voltage
+//#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, disco
#endif
#ifndef RGB_RAINBOW_SPEED
#define RGB_RAINBOW_SPEED 0x0f // change color every 16 frames
#endif
-uint8_t rgb_led_off_mode = RGB_LED_OFF_DEFAULT;
-uint8_t rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT;
#endif
//#define USE_OLD_BLINKING_INDICATOR
@@ -71,17 +54,12 @@ uint8_t rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT;
// bits 2-3 control lockout mode
// bits 0-1 control "off" mode
// modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled)
- #ifdef INDICATOR_LED_DEFAULT_MODE
- uint8_t indicator_led_mode = INDICATOR_LED_DEFAULT_MODE;
- #else
+ #ifndef INDICATOR_LED_DEFAULT_MODE
#ifdef USE_INDICATOR_LED_WHILE_RAMPING
- //uint8_t indicator_led_mode = (1<<2) + 2;
- uint8_t indicator_led_mode = (2<<2) + 1;
+ #define INDICATOR_LED_DEFAULT_MODE ((2<<2) + 1)
#else
- uint8_t indicator_led_mode = (3<<2) + 1;
+ #define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
#endif
#endif
#endif
-
-#endif
diff --git a/spaghetti-monster/anduril/battcheck-mode-fsm.h b/spaghetti-monster/anduril/battcheck-mode-fsm.h
index 8f19e12..5f8e8ec 100644
--- a/spaghetti-monster/anduril/battcheck-mode-fsm.h
+++ b/spaghetti-monster/anduril/battcheck-mode-fsm.h
@@ -1,26 +1,18 @@
-/*
- * battcheck-mode-fsm.h: FSM config for battery check mode in Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef BATTCHECK_MODE_FSM_H
-#define BATTCHECK_MODE_FSM_H
+// battcheck-mode-fsm.h: FSM config for battery check mode in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
#define USE_BATTCHECK
-
+#ifdef USE_AUX_RGB_LEDS
+ // show voltage colors for a few seconds after going to standby
+ #define USE_POST_OFF_VOLTAGE
+ #ifndef DEFAULT_POST_OFF_VOLTAGE_SECONDS
+ #define DEFAULT_POST_OFF_VOLTAGE_SECONDS 4
+ #endif
+ #ifndef POST_OFF_VOLTAGE_BRIGHTNESS
+ // level at which to switch from low to high aux brightness
+ #define POST_OFF_VOLTAGE_BRIGHTNESS (RAMP_SIZE/10)
+ #endif
#endif
diff --git a/spaghetti-monster/anduril/battcheck-mode.c b/spaghetti-monster/anduril/battcheck-mode.c
index d34559b..462540e 100644
--- a/spaghetti-monster/anduril/battcheck-mode.c
+++ b/spaghetti-monster/anduril/battcheck-mode.c
@@ -1,31 +1,14 @@
-/*
- * battcheck-mode.c: Battery check mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef BATTCHECK_MODE_C
-#define BATTCHECK_MODE_C
+// battcheck-mode.c: Battery check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
#include "battcheck-mode.h"
uint8_t battcheck_state(Event event, uint16_t arg) {
////////// Every action below here is blocked in the simple UI //////////
#ifdef USE_SIMPLE_UI
- if (simple_ui_active) {
+ if (cfg.simple_ui_active) {
return EVENT_NOT_HANDLED;
}
#endif
@@ -33,7 +16,7 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: next blinky mode
@@ -45,14 +28,23 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
#elif defined(USE_SOS_MODE) && defined(USE_SOS_MODE_IN_BLINKY_GROUP)
set_state(sos_state, 0);
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
+ }
+
+ #ifdef DEFAULT_BLINK_CHANNEL
+ // 3 clicks: next channel mode (specific to number blinky modes)
+ else if (event == EV_3clicks) {
+ cfg.blink_channel = (cfg.blink_channel + 1) % NUM_CHANNEL_MODES;
+ save_config();
+ return EVENT_HANDLED;
}
+ #endif // ifdef DEFAULT_BLINK_CHANNEL
#ifdef USE_VOLTAGE_CORRECTION
// 7H: voltage config mode
else if (event == EV_click7_hold) {
push_state(voltage_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
@@ -69,14 +61,22 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
// ...
// 13 = add 0.30V
void voltage_config_save(uint8_t step, uint8_t value) {
- if (value) voltage_correction = value;
+ #ifdef USE_POST_OFF_VOLTAGE
+ if (2 == step) cfg.post_off_voltage = value;
+ else
+ #endif
+ if (value) cfg.voltage_correction = value;
}
uint8_t voltage_config_state(Event event, uint16_t arg) {
- return config_state_base(event, arg, 1, voltage_config_save);
+ #ifdef USE_POST_OFF_VOLTAGE
+ #define VOLTAGE_CONFIG_STEPS 2
+ #else
+ #define VOLTAGE_CONFIG_STEPS 1
+ #endif
+ return config_state_base(event, arg,
+ VOLTAGE_CONFIG_STEPS,
+ voltage_config_save);
}
#endif // #ifdef USE_VOLTAGE_CORRECTION
-
-#endif
-
diff --git a/spaghetti-monster/anduril/battcheck-mode.h b/spaghetti-monster/anduril/battcheck-mode.h
index 965ffd9..b505b68 100644
--- a/spaghetti-monster/anduril/battcheck-mode.h
+++ b/spaghetti-monster/anduril/battcheck-mode.h
@@ -1,24 +1,7 @@
-/*
- * battcheck-mode.h: Battery check mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef BATTCHECK_MODE_H
-#define BATTCHECK_MODE_H
+// battcheck-mode.h: Battery check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
uint8_t battcheck_state(Event event, uint16_t arg);
@@ -27,5 +10,3 @@ void voltage_config_save(uint8_t step, uint8_t value);
uint8_t voltage_config_state(Event event, uint16_t arg);
#endif
-
-#endif
diff --git a/spaghetti-monster/anduril/beacon-mode.c b/spaghetti-monster/anduril/beacon-mode.c
index e8e5004..5aff508 100644
--- a/spaghetti-monster/anduril/beacon-mode.c
+++ b/spaghetti-monster/anduril/beacon-mode.c
@@ -1,24 +1,7 @@
-/*
- * beacon-mode.c: Beacon mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef BEACON_MODE_C
-#define BEACON_MODE_C
+// beacon-mode.c: Beacon mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
#include "beacon-mode.h"
@@ -28,7 +11,7 @@ inline void beacon_mode_iter() {
set_level(memorized_level);
nice_delay_ms(100);
set_level(0);
- nice_delay_ms(((beacon_seconds) * 1000) - 100);
+ nice_delay_ms(((cfg.beacon_seconds) * 1000) - 100);
}
}
@@ -36,7 +19,7 @@ uint8_t beacon_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// TODO: use sleep ticks to measure time between pulses,
// to save power
@@ -50,24 +33,21 @@ uint8_t beacon_state(Event event, uint16_t arg) {
#elif defined(USE_THERMAL_REGULATION)
set_state(tempcheck_state, 0);
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: configure beacon timing
else if (event == EV_click1_hold) {
if (0 == (arg % TICKS_PER_SECOND)) {
blink_once();
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// release hold: save new timing
else if (event == EV_click1_hold_release) {
- beacon_seconds = 1 + (arg / TICKS_PER_SECOND);
+ cfg.beacon_seconds = 1 + (arg / TICKS_PER_SECOND);
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/beacon-mode.h b/spaghetti-monster/anduril/beacon-mode.h
index 752918e..df047ad 100644
--- a/spaghetti-monster/anduril/beacon-mode.h
+++ b/spaghetti-monster/anduril/beacon-mode.h
@@ -1,31 +1,9 @@
-/*
- * beacon-mode.h: Beacon mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef BEACON_MODE_H
-#define BEACON_MODE_H
-
-// beacon timing
-uint8_t beacon_seconds = 2;
+// beacon-mode.h: Beacon mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
// beacon mode
uint8_t beacon_state(Event event, uint16_t arg);
inline void beacon_mode_iter();
-
-#endif
diff --git a/spaghetti-monster/anduril/build-all.sh b/spaghetti-monster/anduril/build-all.sh
index b9f6d15..b3fc5d3 100755
--- a/spaghetti-monster/anduril/build-all.sh
+++ b/spaghetti-monster/anduril/build-all.sh
@@ -9,7 +9,7 @@ fi
UI=anduril
-date '+#define VERSION_NUMBER "%Y%m%d"' > version.h
+date '+#define VERSION_NUMBER "%Y-%m-%d"' > version.h
PASS=0
FAIL=0
@@ -33,8 +33,8 @@ for TARGET in cfg-*.h ; do
if [ -z "$ATTINY" ]; then ATTINY=85 ; fi
# try to compile
- echo ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
- ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
+ echo ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}"
+ ../../../bin/build.sh $ATTINY "$UI" "-DCFG_H=${TARGET}"
# track result, and rename compiled files
if [ 0 = $? ] ; then
diff --git a/spaghetti-monster/anduril/candle-mode.c b/spaghetti-monster/anduril/candle-mode.c
index d15195e..ab47c34 100644
--- a/spaghetti-monster/anduril/candle-mode.c
+++ b/spaghetti-monster/anduril/candle-mode.c
@@ -1,24 +1,7 @@
-/*
- * candle-mode.c: Candle mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef CANDLE_MODE_C
-#define CANDLE_MODE_C
+// candle-mode.c: Candle mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
#include "candle-mode.h"
@@ -28,7 +11,7 @@
uint8_t candle_mode_state(Event event, uint16_t arg) {
static int8_t ramp_direction = 1;
- #define MAX_CANDLE_LEVEL (RAMP_LENGTH-CANDLE_AMPLITUDE-15)
+ #define MAX_CANDLE_LEVEL (MAX_LEVEL-CANDLE_AMPLITUDE-15)
static uint8_t candle_wave1 = 0;
static uint8_t candle_wave2 = 0;
static uint8_t candle_wave3 = 0;
@@ -52,14 +35,14 @@ uint8_t candle_mode_state(Event event, uint16_t arg) {
// if the timer just expired, shut off
if (sunset_active && (! sunset_timer)) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif // ifdef USE_SUNSET_TIMER
if (event == EV_enter_state) {
ramp_direction = 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_SUNSET_TIMER
// 2 clicks: cancel timer
@@ -67,7 +50,7 @@ uint8_t candle_mode_state(Event event, uint16_t arg) {
// parent state just rotated through strobe/flasher modes,
// so cancel timer... in case any time was left over from earlier
sunset_timer = 0;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif // ifdef USE_SUNSET_TIMER
// hold: change brightness (brighter)
@@ -81,19 +64,19 @@ uint8_t candle_mode_state(Event event, uint16_t arg) {
candle_mode_brightness += ramp_direction;
if (candle_mode_brightness < 1) candle_mode_brightness = 1;
else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// reverse ramp direction on hold release
else if (event == EV_click1_hold_release) {
ramp_direction = -ramp_direction;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: change brightness (dimmer)
else if (event == EV_click2_hold) {
ramp_direction = 1;
if (candle_mode_brightness > 1)
candle_mode_brightness --;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// clock tick: animate candle brightness
else if (event == EV_tick) {
@@ -146,11 +129,8 @@ uint8_t candle_mode_state(Event event, uint16_t arg) {
// random amplitude
//candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2));
candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/candle-mode.h b/spaghetti-monster/anduril/candle-mode.h
index 8859a9c..aab237d 100644
--- a/spaghetti-monster/anduril/candle-mode.h
+++ b/spaghetti-monster/anduril/candle-mode.h
@@ -1,24 +1,7 @@
-/*
- * candle-mode.h: Candle mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef CANDLE_MODE_H
-#define CANDLE_MODE_H
+// candle-mode.h: Candle mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
#ifndef CANDLE_AMPLITUDE
#define CANDLE_AMPLITUDE 25
@@ -28,5 +11,3 @@ uint8_t candle_mode_state(Event event, uint16_t arg);
// moved to fsm-misc.c because it's also used for tint ramping power correction
//uint8_t triangle_wave(uint8_t phase);
-
-#endif
diff --git a/spaghetti-monster/anduril/cfg-blf-gt-mini.h b/spaghetti-monster/anduril/cfg-blf-gt-mini.h
index a647ea5..083f18e 100644
--- a/spaghetti-monster/anduril/cfg-blf-gt-mini.h
+++ b/spaghetti-monster/anduril/cfg-blf-gt-mini.h
@@ -1,5 +1,9 @@
// BLF/Lumintop GT Mini config options for Anduril
-#include "hwdef-BLF_GT_Mini.h"
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "hwdef-blf-gt-mini.h"
// Same as an Emisar D1S, except it has a lighted button
#include "cfg-emisar-d1s.h"
#undef MODEL_NUMBER
@@ -11,6 +15,7 @@
#define USE_INDICATOR_LED_WHILE_RAMPING
// too big, remove stuff to make room
+#undef USE_TACTICAL_MODE
#undef USE_SOS_MODE
//#undef USE_RAMP_AFTER_MOON_CONFIG
//#undef USE_RAMP_SPEED_CONFIG
diff --git a/spaghetti-monster/anduril/cfg-blf-gt.h b/spaghetti-monster/anduril/cfg-blf-gt.h
index 977d877..580318a 100644
--- a/spaghetti-monster/anduril/cfg-blf-gt.h
+++ b/spaghetti-monster/anduril/cfg-blf-gt.h
@@ -1,20 +1,19 @@
// BLF GT config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0321"
-#include "hwdef-BLF_GT.h"
+#include "hwdef-blf-gt.h"
+// ATTINY: 85
// the button lights up
#define USE_INDICATOR_LED
// the button is visible while main LEDs are on
#define USE_INDICATOR_LED_WHILE_RAMPING
-// don't blink during ramp, it's irrelevant and annoying on this light
-#undef BLINK_AT_RAMP_CEIL
-#undef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_FLOOR
-
-//#undef USE_SET_LEVEL_GRADUALLY
+#define RAMP_SIZE 150
-#define RAMP_LENGTH 150
// First 60 values: level_calc.py 1 60 7135 4 5.0 255
// Remainder: all 255 (buck driver at 100% duty cycle)
#define PWM1_LEVELS 4,5,6,6,7,8,9,11,12,13,15,16,18,19,21,23,25,27,30,32,34,37,40,43,46,49,52,55,59,63,66,70,75,79,83,88,93,98,103,108,114,119,125,131,137,144,150,157,164,171,179,186,194,202,210,219,228,236,246,255, \
@@ -25,6 +24,7 @@
26,27,28,29,30,31,32,33,35,36,37,38,40,41,42,44,45,47,48,50,51,53,54,56,58,59,61,63,65,67,69,70,72,74,76,79,81,83,85,87,89,92,94,96,99,101,104,106,109,112,114,117,120,123,125,128,131,134,137,140,143,147,150,153,156,160,163,167,170,174,177,181,184,188,192,196,200,204,208,212,216,220,224,228,233,237,241,246,250,255
#define POWER_80PX 138 // 2.0 Amps out of maximum 2.5 Amps
#define MAX_1x7135 60 // where it switches from PWM to current control
+#define DEFAULT_LEVEL 69 // nice
#define HALFSPEED_LEVEL 17
#define QUARTERSPEED_LEVEL 6
@@ -32,6 +32,7 @@
// start both ramps at the bottom; even moon throws a long way on the GT
#define RAMP_SMOOTH_FLOOR 1
#define RAMP_SMOOTH_CEIL POWER_80PX
+// 1 23 46 [69] 92 115 138
#define RAMP_DISCRETE_FLOOR 1
#define RAMP_DISCRETE_CEIL POWER_80PX
#define RAMP_DISCRETE_STEPS 7
@@ -41,8 +42,21 @@
#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
#define SIMPLE_UI_STEPS 5
+// smoother, more wobbly candle
+#define CANDLE_AMPLITUDE 33
+
+// turbo (i.e. "giggles" mode), low, tactical strobe
+#define TACTICAL_LEVELS 150,30,(RAMP_SIZE+2)
+
// stop panicking at 80% power, this light has plenty of thermal mass
#define THERM_FASTER_LEVEL POWER_80PX // throttle back faster when high
+// don't blink during ramp, it's irrelevant and annoying on this light
+#undef BLINK_AT_RAMP_CEIL
+#undef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_FLOOR
+
// too big, turn off extra features
-#undef USE_TACTICAL_MODE
+//#undef USE_TACTICAL_MODE
+#undef USE_SOS_MODE
+
diff --git a/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h b/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h
index 06d5395..fc02619 100644
--- a/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h
+++ b/spaghetti-monster/anduril/cfg-blf-lantern-t1616.h
@@ -1,75 +1,110 @@
// BLF Lantern config options for Anduril using the Attiny1616
+// Copyright (C) 2021-2023 (original author TBD), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0622"
-#include "hwdef-BLF_LT1-t1616.h"
+#include "hwdef-blf-lt1-t1616.h"
// ATTINY: 1616
// the button lights up
#define USE_INDICATOR_LED
// the button is visible while main LEDs are on
#define USE_INDICATOR_LED_WHILE_RAMPING
-// off mode: high (2)
+// off mode: low (1)
// lockout: blinking (3)
-#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2)
+#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+
+// channel modes...
+// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO
+#define DEFAULT_CHANNEL_MODE CM_AUTO
+#define DEFAULT_BLINK_CHANNEL CM_BOTH
+
+#define CONFIG_WAITING_CHANNEL CM_BOTH
+#define CONFIG_BLINK_CHANNEL CM_BOTH
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_BOTH
-// the lantern has two PWM channels, but they drive different sets of emitters
-// (one channel for warm emitters, one channel for cold)
-// so enable a special ramping mode which changes tint instead of brightness
-#define USE_TINT_RAMPING
// how much to increase total brightness at middle tint
// (0 = 100% brightness, 64 = 200% brightness)
//#define TINT_RAMPING_CORRECTION 26 // prototype, 140%
#define TINT_RAMPING_CORRECTION 10 // production model, 115%
-#ifdef RAMP_LENGTH
-#undef RAMP_LENGTH
-#endif
+#define RAMP_SIZE 150
+// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits)
+// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM)
+// level_calc.py 3.333 1 150 7135 32 0.2 600 --pwm 32640
+#define PWM1_LEVELS 4,35,38,41,45,50,55,61,67,74,82,91,100,110,121,133,146,160,175,192,209,227,247,268,291,314,340,366,395,424,456,489,524,560,599,639,681,726,772,820,871,924,979,1036,1096,1158,1222,1289,1359,1431,1506,1584,1664,1747,1834,1923,2015,2111,2209,2311,2416,2524,2636,2751,2870,2992,3118,3247,3380,3518,3659,3803,3952,4105,4262,4423,4589,4759,4933,5111,5294,5482,5674,5871,6073,6279,6491,6707,6928,7155,7386,7623,7865,8113,8365,8624,8888,9157,9432,9713,10000,10292,10591,10895,11206,11523,11846,12175,12511,12853,13202,13557,13919,14287,14663,15045,15434,15830,16233,16644,17061,17486,17919,18358,18805,19260,19723,20193,20671,21156,21650,22152,22662,23180,23706,24241,24784,25335,25895,26464,27041,27627,28222,28826,29439,30060,30691,31332,31981,32640
-// level_calc.py 1 150 7135 1 30 800
-#define RAMP_LENGTH 150
-#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,11,11,12,13,13,14,15,15,16,17,18,18,19,20,21,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,43,44,45,46,48,49,50,51,53,54,56,57,58,60,61,63,64,66,67,69,70,72,74,75,77,79,80,82,84,85,87,89,91,93,95,97,98,100,102,104,106,108,111,113,115,117,119,121,124,126,128,130,133,135,137,140,142,145,147,150,152,155,157,160,163,165,168,171,173,176,179,182,185,188,190,193,196,199,202,205,209,212,215,218,221,224,228,231,234,238,241,245,248,251,255
-#define MAX_1x7135 65
-#define HALFSPEED_LEVEL 14
-#define QUARTERSPEED_LEVEL 5
+#define DEFAULT_LEVEL 75
+#define MAX_1x7135 75
+#define HALFSPEED_LEVEL 0 // always use tint ramping correction
+#define QUARTERSPEED_LEVEL 2 // quarter speed at level 1, full speed at 2+
+//#undef USE_DYNAMIC_UNDERCLOCKING // makes huge bumps in the ramp
+
+#define USE_SET_LEVEL_GRADUALLY
-// the default of 26 looks a bit flat, so increase it
-#define CANDLE_AMPLITUDE 40
// override default ramp style
#undef RAMP_STYLE
-#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped
+#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped
// set floor and ceiling as far apart as possible
// because this lantern isn't overpowered
-#define RAMP_SMOOTH_FLOOR 1
-#define RAMP_SMOOTH_CEIL 150
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 5
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 150
+#define RAMP_DISCRETE_FLOOR 1
+#define RAMP_DISCRETE_CEIL 150
+#define RAMP_DISCRETE_STEPS 7
// LT1 can handle heat well, so don't limit simple mode
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-#define SIMPLE_UI_STEPS RAMP_DISCRETE_STEPS
+#define SIMPLE_UI_FLOOR 10
+#define SIMPLE_UI_CEIL 150
+#define SIMPLE_UI_STEPS 5
-// Allow 3C in Simple UI for switching between smooth and stepped ramping
+// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping)
#define USE_SIMPLE_UI_RAMPING_TOGGLE
+// allow Aux Config and Strobe Modes in Simple UI
+#define USE_EXTENDED_SIMPLE_UI
+
#define USE_SOS_MODE
#define USE_SOS_MODE_IN_BLINKY_GROUP
-// the sensor (attiny85) is nowhere near the emitters
+// the default of 26 looks a bit flat, so increase it
+#define CANDLE_AMPLITUDE 40
+
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_COLOR_STROBE_CH1 CM_CH1
+#define POLICE_COLOR_STROBE_CH2 CM_CH2
+// aux red + aux blue are the correct colors, but are dim
+//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+#undef TACTICAL_LEVELS
+#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe
+
+// party strobe, tac strobe, police, lightning, candle, bike
+#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO
+
+// the sensor (attiny1616) is nowhere near the emitters
// so thermal regulation can't work
#ifdef USE_THERMAL_REGULATION
#undef USE_THERMAL_REGULATION
#endif
// don't blink while ramping
-#ifdef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_MIDDLE
-#endif
#ifdef BLINK_AT_RAMP_FLOOR
#undef BLINK_AT_RAMP_FLOOR
#endif
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
// except the top... blink at the top
#ifndef BLINK_AT_RAMP_CEIL
#define BLINK_AT_RAMP_CEIL
#endif
+
+// for consistency with other models
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-blf-lantern.h b/spaghetti-monster/anduril/cfg-blf-lantern.h
index 251c0db..53c3203 100644
--- a/spaghetti-monster/anduril/cfg-blf-lantern.h
+++ b/spaghetti-monster/anduril/cfg-blf-lantern.h
@@ -1,68 +1,72 @@
// BLF Lantern config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0621"
-#include "hwdef-BLF_LT1.h"
+#include "hwdef-blf-lt1.h"
// ATTINY: 85
// the button lights up
#define USE_INDICATOR_LED
// the button is visible while main LEDs are on
#define USE_INDICATOR_LED_WHILE_RAMPING
-// off mode: high (2)
+// off mode: low (1)
// lockout: blinking (3)
-#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2)
+#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+
+// channel modes...
+// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO
+#define DEFAULT_CHANNEL_MODE CM_AUTO
+//#define DEFAULT_BLINK_CHANNEL CM_BOTH // takes too much space
-// the lantern has two PWM channels, but they drive different sets of emitters
-// (one channel for warm emitters, one channel for cold)
-// so enable a special ramping mode which changes tint instead of brightness
-#define USE_TINT_RAMPING
// how much to increase total brightness at middle tint
// (0 = 100% brightness, 64 = 200% brightness)
//#define TINT_RAMPING_CORRECTION 26 // prototype, 140%
-#define TINT_RAMPING_CORRECTION 10 // production model, 115%
-//#define TINT_RAMPING_CORRECTION 0 // none
+//#define TINT_RAMPING_CORRECTION 10 // production model, 115%
+#define TINT_RAMPING_CORRECTION 0 // none
-#ifdef RAMP_LENGTH
-#undef RAMP_LENGTH
-#endif
+#define RAMP_SIZE 150
+// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits)
+// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM)
+// level_calc.py 3.333 1 150 7135 32 0.2 600 --pwm 32640
+#define PWM1_LEVELS 32,35,38,41,45,50,55,61,67,74,82,91,100,110,121,133,146,160,175,192,209,227,247,268,291,314,340,366,395,424,456,489,524,560,599,639,681,726,772,820,871,924,979,1036,1096,1158,1222,1289,1359,1431,1506,1584,1664,1747,1834,1923,2015,2111,2209,2311,2416,2524,2636,2751,2870,2992,3118,3247,3380,3518,3659,3803,3952,4105,4262,4423,4589,4759,4933,5111,5294,5482,5674,5871,6073,6279,6491,6707,6928,7155,7386,7623,7865,8113,8365,8624,8888,9157,9432,9713,10000,10292,10591,10895,11206,11523,11846,12175,12511,12853,13202,13557,13919,14287,14663,15045,15434,15830,16233,16644,17061,17486,17919,18358,18805,19260,19723,20193,20671,21156,21650,22152,22662,23180,23706,24241,24784,25335,25895,26464,27041,27627,28222,28826,29439,30060,30691,31332,31981,32640
-// 1-130: 0 to 100% power
-// level_calc.py 3.0 1 130 7135 1 30 800 --pwm 255
-// 131-150: 101% to 200% power
-// level_calc.py 8.69 1 150 7135 1 1 1600 --pwm 510
-#define RAMP_LENGTH 150
-#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,6,7,8,8,9,10,10,11,12,12,13,14,15,16,17,17,18,19,20,21,22,23,24,25,26,27,28,29,31,32,33,34,35,37,38,39,40,42,43,45,46,47,49,50,52,54,55,57,58,60,62,63,65,67,69,70,72,74,76,78,80,82,84,86,88,90,92,95,97,99,101,104,106,108,111,113,115,118,121,123,126,128,131,134,136,139,142,145,148,150,153,156,159,162,166,169,172,175,178,181,185,188,191,195,198,202,205,209,213,216,220,224,227,231,235,239,243,247,251,255,264,274,284,294,305,316,327,339,351,363,376,389,403,417,431,446,461,477,493,510
-#define MAX_1x7135 130
-#define DEFAULT_LEVEL 70
-#define HALFSPEED_LEVEL 14
-#define QUARTERSPEED_LEVEL 5
+#define DEFAULT_LEVEL 75
+#define MAX_1x7135 75
+#define HALFSPEED_LEVEL 44
+#define QUARTERSPEED_LEVEL 34
+#undef USE_DYNAMIC_UNDERCLOCKING // makes huge bumps in the ramp
+
+#define USE_SMOOTH_STEPS
+//#define USE_SET_LEVEL_GRADUALLY
// the default of 26 looks a bit flat, so increase it
#define CANDLE_AMPLITUDE 40
// override default ramp style
#undef RAMP_STYLE
-#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped
+#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped
// set floor and ceiling as far apart as possible
// because this lantern isn't overpowered
-#define RAMP_SMOOTH_FLOOR 1
-#define RAMP_SMOOTH_CEIL 130
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 5
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 150
+#define RAMP_DISCRETE_FLOOR 1
+#define RAMP_DISCRETE_CEIL 150
+#define RAMP_DISCRETE_STEPS 7
+
+// LT1 can handle heat well, so don't limit simple mode
+#define SIMPLE_UI_FLOOR 10
+#define SIMPLE_UI_CEIL 150
+#define SIMPLE_UI_STEPS 5
// Allow 3C in Simple UI for switching between smooth and stepped ramping
#define USE_SIMPLE_UI_RAMPING_TOGGLE
-
-// LT1 can handle heat well, so don't limit simple mode
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-#define SIMPLE_UI_STEPS RAMP_DISCRETE_STEPS
+#define USE_EXTENDED_SIMPLE_UI
// also at Sofirn's request, enable 2 click turbo (Anduril 1 style)
-#define DEFAULT_2C_STYLE 1
+#define DEFAULT_2C_STYLE 1
-#define USE_SOS_MODE
-#define USE_SOS_MODE_IN_BLINKY_GROUP
// the sensor (attiny85) is nowhere near the emitters
// so thermal regulation can't work
@@ -70,18 +74,32 @@
#undef USE_THERMAL_REGULATION
#endif
-// don't blink at floor
+// don't blink while ramping
#ifdef BLINK_AT_RAMP_FLOOR
#undef BLINK_AT_RAMP_FLOOR
#endif
-// blink at 100% power
-#ifndef BLINK_AT_RAMP_MIDDLE
-#define BLINK_AT_RAMP_MIDDLE
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
#endif
-// blink again at the 200% power / ceil / turbo
-#ifndef BLINK_AT_RAMP_CEIL
-#define BLINK_AT_RAMP_CEIL
+#ifdef BLINK_AT_RAMP_CEIL
+#undef BLINK_AT_RAMP_CEIL
#endif
// too big, turn off extra features
+//#undef USE_STEPPED_TINT_RAMPING
+#undef USE_MOMENTARY_MODE
#undef USE_TACTICAL_MODE
+#undef USE_SOS_MODE
+//#undef USE_SIMPLE_UI
+//#undef USE_BEACON_MODE
+//#undef USE_RAMP_SPEED_CONFIG
+#undef USE_RAMP_AFTER_MOON_CONFIG
+#undef USE_2C_STYLE_CONFIG
+#undef USE_VOLTAGE_CORRECTION
+//#undef USE_CHANNEL_PER_STROBE
+// party strobe, tac strobe, lightning, candle, bike
+#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO
+
+// for consistency with other models
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-blf-q8-t1616.h b/spaghetti-monster/anduril/cfg-blf-q8-t1616.h
index 30a3368..9f0402c 100644
--- a/spaghetti-monster/anduril/cfg-blf-q8-t1616.h
+++ b/spaghetti-monster/anduril/cfg-blf-q8-t1616.h
@@ -1,38 +1,50 @@
// BLF Q8 config options for Anduril using the Attiny1616
+// Copyright (C) 2021-2023 gchart, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0613"
-#include "hwdef-BLF_Q8-T1616.h"
+#include "hwdef-blf-q8-t1616.h"
+#include "wurkkos-cfg.h" // Sofirn lights are closely related to Wurkkos
// ATTINY: 1616
// the button lights up
#define USE_INDICATOR_LED
// the button is visible while main LEDs are on
#define USE_INDICATOR_LED_WHILE_RAMPING
-// off mode: high (2)
+// off mode: low (1)
// lockout: blinking (3)
-#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2)
-
-// copied from Emisar D4 ramp
-// ../../bin/level_calc.py 1 65 7135 1 0.8 150
-// ... mixed with this:
-// ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500
-#define RAMP_LENGTH 150
-#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255
-#define MAX_1x7135 65
-#define HALFSPEED_LEVEL 14
+#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+
+// copied from Wurkkos TS25 ramp
+#define RAMP_SIZE 150
+// 7135 at 75/150
+// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3
+// (with some manual tweaks)
+#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+// non-zero part of FET channel calculated with:
+// level_calc.py 3 1 75 7135 1 200 3000
+// (FIXME? there's a visible bump when the FET kicks in, even with just 1/255)
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,7,8,10,11,13,14,16,17,19,21,22,24,26,28,30,32,34,37,39,41,44,46,48,51,54,56,59,62,65,68,71,74,77,81,84,87,91,94,98,102,106,110,114,118,122,126,130,135,139,144,148,153,158,163,168,173,178,184,189,195,200,206,212,218,224,230,236,242,248,255
+#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+
+#define MAX_1x7135 75
+#define DEFAULT_LEVEL 50
+#define MIN_THERM_STEPDOWN 60
+#define HALFSPEED_LEVEL 20
#define QUARTERSPEED_LEVEL 5
-#define RAMP_SMOOTH_FLOOR 1
-#define RAMP_SMOOTH_CEIL 120
-// 10 28 46 [65] 83 101 120
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 150
+// 20 38 56 [75] 93 111 130
+#define RAMP_DISCRETE_FLOOR 20
+#define RAMP_DISCRETE_CEIL 130
+#define RAMP_DISCRETE_STEPS 7
// at Sofirn's request, use max (150) for the Simple UI ceiling
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL 150
-#define SIMPLE_UI_STEPS 5
+#define SIMPLE_UI_FLOOR 1
+#define SIMPLE_UI_CEIL 150
+#define SIMPLE_UI_STEPS 5
// also at Sofirn's request, enable 2 click turbo (Anduril 1 style)
#define DEFAULT_2C_STYLE 1
@@ -44,6 +56,27 @@
// Allow 3C in Simple UI for switching between smooth and stepped ramping
#define USE_SIMPLE_UI_RAMPING_TOGGLE
+// allow Aux Config and Strobe Modes in Simple UI
+#define USE_EXTENDED_SIMPLE_UI
+
// stop panicking at ~75% power or ~3000 lm, this light has high thermal mass
#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the aux LEDs by default
+#define DEFAULT_BLINK_CHANNEL CM_AUX
+
+// the default of 26 looks a bit rough, so increase it to make it smoother
+#define CANDLE_AMPLITUDE 33
+
+// don't blink during the ramp; the button LED brightness is sufficient
+// to indicate which power channel(s) are being used
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// enable factory reset on 13H without loosening tailcap (required)
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-blf-q8.h b/spaghetti-monster/anduril/cfg-blf-q8.h
index a9c48f6..29dbcb7 100644
--- a/spaghetti-monster/anduril/cfg-blf-q8.h
+++ b/spaghetti-monster/anduril/cfg-blf-q8.h
@@ -1,6 +1,12 @@
// BLF Q8 config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0611"
-#include "hwdef-BLF_Q8.h"
+#include "hwdef-blf-q8.h"
+
+#define RAMP_SIZE 150
// the button lights up
#define USE_INDICATOR_LED
@@ -14,10 +20,11 @@
// ../../bin/level_calc.py 1 65 7135 1 0.8 150
// ... mixed with this:
// ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500
-#define RAMP_LENGTH 150
#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255
+
#define MAX_1x7135 65
+#define DEFAULT_LEVEL 65
#define HALFSPEED_LEVEL 14
#define QUARTERSPEED_LEVEL 5
@@ -42,10 +49,18 @@
// Allow 3C in Simple UI for switching between smooth and stepped ramping
#define USE_SIMPLE_UI_RAMPING_TOGGLE
+#define USE_EXTENDED_SIMPLE_UI
// stop panicking at ~75% power or ~3000 lm, this light has high thermal mass
#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high
+// don't blink mid-ramp
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+#define USE_SMOOTH_STEPS
+
// too big, remove stuff to make room
#undef USE_SOS_MODE
//#undef USE_RAMP_AFTER_MOON_CONFIG
diff --git a/spaghetti-monster/anduril/cfg-emisar-2ch-fet.h b/spaghetti-monster/anduril/cfg-emisar-2ch-fet.h
new file mode 100644
index 0000000..4686483
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-emisar-2ch-fet.h
@@ -0,0 +1,113 @@
+// Emisar 2-channel generic (plus FET) config options for Anduril (based on Noctigon K9.3)
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define MODEL_NUMBER "0136"
+#include "hwdef-emisar-2ch-fet.h"
+#include "hank-cfg.h"
+// ATTINY: 1634
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+// the aux LEDs are front-facing, so turn them off while main LEDs are on
+// it also has an independent LED in the button
+#define USE_BUTTON_LED
+// TODO: the whole "indicator LED" thing needs to be refactored into
+// "aux LED(s)" and "button LED(s)" since they work a bit differently
+// enabling this option breaks the button LED on D4v2.5
+#ifdef USE_INDICATOR_LED_WHILE_RAMPING
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+#endif
+
+// channel modes...
+// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO
+// enable max brightness out of the box
+#define DEFAULT_CHANNEL_MODE CM_BOTH
+
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_BOTH
+
+#define POLICE_COLOR_STROBE_CH1 CM_CH1
+#define POLICE_COLOR_STROBE_CH2 CM_CH2
+
+// how much to increase total brightness at middle tint
+// (0 = 100% brightness, 64 = 200% brightness)
+#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it
+
+
+// channel 1 / main LEDs (w/ DD FET)
+// output: unknown, 1750 lm?
+// FET: unknown, 3000 lm?
+// channel 2
+// output: unknown, 1750 lm?
+// combined: 4000 to 5000 lm?
+#define RAMP_SIZE 150
+
+// linear+FET ramp: maxreg at 140/150
+// level_calc.py 5.01 2 150 7135 1 0.1 1924 FET 1 10 2600 --pwm dyn:68:4096:255:3 --clock 8:16:0
+// linear segment
+#define PWM1_LEVELS 1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,8,8,8,9,9,9,10,10,11,12,13,13,14,15,16,17,17,18,19,20,21,22,23,25,26,27,28,30,31,33,34,36,37,39,41,43,44,46,48,50,53,55,57,60,62,65,67,70,73,76,79,82,85,88,92,95,99,103,106,110,114,119,123,127,132,137,142,147,152,157,162,168,174,179,185,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255
+// DD FET segment
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,20,44,68,93,118,144,171,198,226,255
+// PWM TOPS values
+#define PWM3_LEVELS 4096,3831,3570,3314,3063,2817,2577,2342,2115,1894,1682,1477,1281,2237,2005,1784,1576,1380,1196,1111,1026,1442,1348,1215,1091,976,871,774,1031,942,860,784,714,650,591,538,652,602,555,513,473,437,403,372,343,398,370,345,322,299,278,311,292,273,255,278,261,245,263,247,232,246,231,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+
+// linear-only ramp
+// level_calc.py 3.11 1 150 7135 1 0.1 1750 --pwm dyn:64:4096:255:3 --clock 8:16:0
+#define PWM4_LEVELS 1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,13,13,13,14,14,14,15,15,16,17,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,34,35,37,38,39,41,42,44,45,47,48,50,52,53,55,57,59,61,63,65,67,69,71,73,75,77,79,82,84,86,89,91,94,97,99,102,105,107,110,113,116,119,122,125,128,132,135,138,141,145,148,152,156,159,163,167,170,174,178,182,186,190,195,199,203,208,212,217,221,226,230,235,240,245,250,255
+// PWM_TOPS values for linear-only ramp
+#define PWM5_LEVELS 4096,3681,3247,2794,2328,1856,2937,2393,1860,2690,2273,1875,2281,1959,1658,1893,1646,1774,1569,1381,1466,1309,1166,1224,1104,996,1033,942,858,882,810,746,687,634,586,604,561,522,487,454,425,397,409,385,362,341,321,302,311,295,279,286,271,257,263,250,255,258,246,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+
+#define DEFAULT_LEVEL 75
+#define MAX_1x7135 140
+#define MAX_Nx7135 MAX_1x7135
+#define HALFSPEED_LEVEL 16
+#define QUARTERSPEED_LEVEL 8
+
+#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?)
+#define RAMP_SMOOTH_CEIL 140
+// 10, 31, 53, [75], 96, 118, [140]
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
+
+// safe limit highest regulated power (no FET or turbo)
+// 10, 42, [75], 107, 140
+#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
+#define SIMPLE_UI_STEPS 5
+
+// stop panicking at ~1500 lm
+#define THERM_FASTER_LEVEL 130
+#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level
+
+#define USE_POLICE_COLOR_STROBE_MODE
+#undef TACTICAL_LEVELS
+#define TACTICAL_LEVELS 140,30,(RAMP_SIZE+3) // high, low, police strobe
+
+// use the brightest setting for strobe
+#define STROBE_BRIGHTNESS MAX_LEVEL
+// slow down party strobe; this driver can't pulse for 1ms or less
+#define PARTY_STROBE_ONTIME 2
+// TODO: change speed per channel mode
+// (the FET is really fast, but the regulator is not)
+//#undef PARTY_STROBE_ONTIME
+
+// the default of 26 looks a bit flat, so increase it
+#define CANDLE_AMPLITUDE 33
+
+// the power regulator is a bit slow, so push it harder for a quick response from off
+#define DEFAULT_JUMP_START_LEVEL 40
+#define BLINK_BRIGHTNESS 45
+#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow
+
+#define THERM_CAL_OFFSET 5
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-2ch.h b/spaghetti-monster/anduril/cfg-emisar-2ch.h
new file mode 100644
index 0000000..4c3a329
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-emisar-2ch.h
@@ -0,0 +1,103 @@
+// Emisar 2-channel generic config options for Anduril
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define MODEL_NUMBER "0135"
+#include "hwdef-emisar-2ch.h"
+#include "hank-cfg.h"
+// ATTINY: 1634
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+// the aux LEDs are front-facing, so turn them off while main LEDs are on
+// it also has an independent LED in the button
+#define USE_BUTTON_LED
+// TODO: the whole "indicator LED" thing needs to be refactored into
+// "aux LED(s)" and "button LED(s)" since they work a bit differently
+// enabling this option breaks the button LED on D4v2.5
+#ifdef USE_INDICATOR_LED_WHILE_RAMPING
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+#endif
+
+// channel modes...
+// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO
+// enable max brightness out of the box
+#define DEFAULT_CHANNEL_MODE CM_BLEND
+
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_BLEND
+
+#define POLICE_COLOR_STROBE_CH1 CM_CH1
+#define POLICE_COLOR_STROBE_CH2 CM_CH2
+
+// how much to increase total brightness at middle tint
+// (0 = 100% brightness, 64 = 200% brightness)
+#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it
+
+
+// channel 1
+// output: unknown, 2000 lm?
+// FET: absent / unused
+// channel 2
+// output: unknown, 2000 lm?
+#define RAMP_SIZE 150
+
+// abstract ramp (power is split between both sets of LEDs)
+// 1-130: 0 to 100% power
+// level_calc.py 5.01 1 130 7135 2 0.2 2000 --pwm dyn:64:16383:511
+// 131 to 150: 101% to 200% power
+// level_calc.py 6.44 1 150 7135 1 0.2 2000 --pwm dyn:74:16383:1022
+#define PWM1_LEVELS 1,1,1,2,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,15,16,18,19,21,22,24,26,28,29,31,33,35,37,40,42,44,46,48,50,52,54,56,58,59,61,62,63,64,65,66,66,66,66,65,64,62,60,58,54,50,46,41,35,28,20,21,22,24,25,26,27,29,30,32,33,35,37,38,40,42,44,46,48,50,53,55,57,60,63,65,68,71,74,77,80,83,87,90,94,98,102,106,110,114,118,123,128,132,137,142,148,153,159,164,170,176,183,189,196,202,209,216,224,231,239,247,255,263,272,281,290,299,309,318,328,339,349,360,371,382,394,406,418,430,443,456,469,483,497,511
+#define PWM_TOPS 16383,13650,10715,15388,11902,8195,12771,9834,12258,13423,11192,11947,12284,12363,12271,12064,11775,11428,11039,11469,10973,11132,10595,10601,10054,9971,9833,9655,9122,8923,8704,8473,8232,8196,7932,7668,7408,7152,6901,6656,6417,6186,5961,5647,5444,5163,4899,4653,4422,4206,3941,3697,3470,3210,2971,2707,2466,2245,1968,1717,1489,1251,1005,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
+// max "200% power" ramp and tops
+//#define PWM2_LEVELS 2,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,28,30,33,35,38,41,44,47,50,54,57,60,64,67,71,74,78,81,84,88,91,94,97,99,101,103,105,106,107,107,107,106,105,102,99,95,90,84,77,68,58,47,34,36,38,40,42,44,47,49,52,54,57,60,63,66,69,73,76,80,83,87,91,96,100,104,109,114,119,124,130,135,141,147,153,160,166,173,180,187,195,203,211,219,228,236,245,255,264,274,285,295,306,317,329,340,353,365,378,391,405,419,433,448,463,479,495,511,530,550,570,591,612,634,657,680,705,730,755,782,809,837,865,895,925,957,989,1022
+//#define PWM3_LEVELS 16383,13234,9781,13826,9593,13434,9973,12021,12900,13193,13150,12899,12508,12023,12666,11982,12181,11422,11393,11247,11018,10731,10826,10434,10365,9927,9767,9565,9332,9076,8806,8693,8395,8096,7928,7626,7439,7143,6948,6665,6393,6203,5946,5700,5465,5187,4926,4681,4451,4195,3957,3700,3463,3213,2983,2718,2476,2231,1986,1742,1501,1245,997,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
+#define DEFAULT_LEVEL 70
+#define MAX_1x7135 150
+#define HALFSPEED_LEVEL 10
+#define QUARTERSPEED_LEVEL 2
+
+#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?)
+#define RAMP_SMOOTH_CEIL 130
+// 10, 30, 50, [70], 90, 110, [130]
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
+
+// safe limit highest regulated power (no FET or turbo)
+// 10, 40, [70], 100, 130
+#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
+#define SIMPLE_UI_STEPS 5
+
+// stop panicking at ~1500 lm
+#define THERM_FASTER_LEVEL 130
+#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level
+
+#define USE_POLICE_COLOR_STROBE_MODE
+#undef TACTICAL_LEVELS
+#define TACTICAL_LEVELS 130,30,(RAMP_SIZE+3) // high, low, police strobe
+
+// use the brightest setting for strobe
+#define STROBE_BRIGHTNESS MAX_LEVEL
+// slow down party strobe; this driver can't pulse for 1ms or less
+#define PARTY_STROBE_ONTIME 2
+
+// the default of 26 looks a bit flat, so increase it
+#define CANDLE_AMPLITUDE 33
+
+// the power regulator is a bit slow, so push it harder for a quick response from off
+#define DEFAULT_JUMP_START_LEVEL 21
+#define BLINK_BRIGHTNESS 40
+#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow
+
+#define THERM_CAL_OFFSET 5
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1.h b/spaghetti-monster/anduril/cfg-emisar-d1.h
index 2427773..c81171c 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d1.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d1.h
@@ -1,6 +1,10 @@
// Emisar D1 config options for Anduril
-#include "hwdef-Emisar_D1.h"
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// same as Emisar D4, mostly
+#include "hwdef-emisar-d4.h"
#include "cfg-emisar-d4.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0121"
diff --git a/spaghetti-monster/anduril/cfg-emisar-d18-219.h b/spaghetti-monster/anduril/cfg-emisar-d18-219.h
index 1f3a3c2..126e9f4 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d18-219.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d18-219.h
@@ -1,4 +1,8 @@
// Emisar D18 (FET+13+1) reduced-FET config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#include "cfg-emisar-d18.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0142"
diff --git a/spaghetti-monster/anduril/cfg-emisar-d18.h b/spaghetti-monster/anduril/cfg-emisar-d18.h
index 6644513..3e5d3ae 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d18.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d18.h
@@ -1,24 +1,31 @@
// Emisar D18 (FET+13+1) config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0141"
-#include "hwdef-Emisar_D18.h"
+#include "hwdef-emisar-d18.h"
#include "hank-cfg.h"
+#define RAMP_SIZE 150
+
// level_calc.py seventh 3 150 7135 1 1.4 117.99 7135 6 1 1706.86 FET 3 10 13000
// (designed to make 1x hit at level 50, and Nx hit at level 100)
-#define RAMP_LENGTH 150
#define PWM1_LEVELS 1,1,2,2,3,4,4,5,6,7,8,9,10,11,15,16,18,20,22,24,26,28,30,33,36,39,43,47,51,56,61,66,72,78,85,92,99,107,116,125,135,145,156,168,180,194,208,222,238,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,8,10,11,13,14,16,18,19,21,23,26,28,30,33,35,38,41,44,47,51,54,58,62,66,70,75,79,84,90,95,101,106,112,119,126,133,140,147,155,164,172,181,190,200,210,221,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,7,9,11,13,15,18,20,23,25,28,31,34,37,40,44,47,51,54,58,62,66,70,75,79,84,89,94,99,104,110,115,121,127,134,140,147,154,161,168,176,183,191,200,208,217,226,235,245,255
-#define MAX_1x7135 50
-#define MAX_Nx7135 100
-#define HALFSPEED_LEVEL 15
-#define QUARTERSPEED_LEVEL 6
+
+#define DEFAULT_LEVEL 50
+#define MAX_1x7135 50
+#define MAX_Nx7135 100
+#define HALFSPEED_LEVEL 15
+#define QUARTERSPEED_LEVEL 6
// start at ~2000 lm after battery change, not ~150 lm (at Emisar's request)
//#define DEFAULT_LEVEL MAX_Nx7135
// go up to ~4000 lm
-#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_FLOOR 1
#define RAMP_SMOOTH_CEIL 117
// 20 36 52 68 84 [100] 117
#define RAMP_DISCRETE_FLOOR 20
@@ -26,17 +33,29 @@
#define RAMP_DISCRETE_STEPS 7
// safe limit ~20% power / max regulated
-#define SIMPLE_UI_FLOOR 20
-#define SIMPLE_UI_CEIL MAX_Nx7135
-#define SIMPLE_UI_STEPS 5
+// 20 40 60 80 100
+#define SIMPLE_UI_FLOOR 20
+#define SIMPLE_UI_CEIL MAX_Nx7135
+#define SIMPLE_UI_STEPS 5
// only blink at max regulated level and ceiling
-#define BLINK_AT_RAMP_MIDDLE
-#define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
+//#define BLINK_AT_RAMP_MIDDLE
+//#define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
#define BLINK_AT_RAMP_CEIL
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
// stop panicking at about ~40% power or ~5000 lm
#define THERM_FASTER_LEVEL 125
+// enable extra features
+#define USE_SMOOTH_STEPS
+
+// for compatibility with other models
+#define USE_SOFT_FACTORY_RESET
+
// too big, turn off extra features
#undef USE_TACTICAL_MODE
+#undef USE_SOS_MODE
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1s.h b/spaghetti-monster/anduril/cfg-emisar-d1s.h
index 47cecf6..8b70a5d 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d1s.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d1s.h
@@ -1,6 +1,10 @@
// Emisar D1S config options for Anduril
-#include "hwdef-Emisar_D1S.h"
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// same as Emisar D4, mostly
+#include "hwdef-emisar-d4.h"
#include "cfg-emisar-d4.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0122"
@@ -16,4 +20,4 @@
#define THERM_FASTER_LEVEL 144 // throttle back faster when high
// too big, turn off extra features
-#undef USE_TACTICAL_MODE
+//#undef USE_TACTICAL_MODE
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1v2-7135-fet.h b/spaghetti-monster/anduril/cfg-emisar-d1v2-7135-fet.h
index aa9780b..4022ba6 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d1v2-7135-fet.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d1v2-7135-fet.h
@@ -1,7 +1,11 @@
// Emisar D1v2 (7135+FET) config options for Anduril
-// (was only made for a short time, not many people have one)
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// ATTINY: 1634
// same as Emisar D4v2, mostly
+// (was only made for a short time, not many people have one)
#include "cfg-emisar-d4v2.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0123"
@@ -13,7 +17,7 @@
// (early short run had no button LEDs at all, later run uses linear+FET instead,
// so it's unlikely that anyone needs this, but it doesn't hurt anything)
#define USE_AUX_RGB_LEDS
-#define USE_AUX_RGB_LEDS_WHILE_ON
+#define USE_AUX_RGB_LEDS_WHILE_ON 25
#define USE_INDICATOR_LED_WHILE_RAMPING
// safe limit ~50% power
@@ -26,6 +30,3 @@
#endif
#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high
-
-// work around bizarre bug: lockout mode fails when set to solid color blinking
-#define USE_K93_LOCKOUT_KLUDGE
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1v2-linear-fet.h b/spaghetti-monster/anduril/cfg-emisar-d1v2-linear-fet.h
index 5dd5a2e..28c57f8 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d1v2-linear-fet.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d1v2-linear-fet.h
@@ -1,5 +1,9 @@
// Emisar D1v2 (linear+FET) config options for Anduril
// (2022 re-issue / update of old D1)
+// Copyright (C) 2022-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// ATTINY: 1634
// similar to a Noctigon KR4, sort of
#include "cfg-noctigon-kr4.h"
@@ -11,7 +15,7 @@
#define USE_BUTTON_LED
// the aux LEDs are in the button, so use them while main LEDs are on
#define USE_AUX_RGB_LEDS
-#define USE_AUX_RGB_LEDS_WHILE_ON
+#define USE_AUX_RGB_LEDS_WHILE_ON 25
#define USE_INDICATOR_LED_WHILE_RAMPING
// safe limit: max regulated power
@@ -24,6 +28,3 @@
#endif
#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high
-
-// work around bizarre bug: lockout mode fails when set to solid color blinking
-#define USE_K93_LOCKOUT_KLUDGE
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h b/spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h
index 1c7aec9..7f5bcc4 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d1v2-nofet.h
@@ -1,5 +1,9 @@
-// Emisar D1v2 (linear+FET) config options for Anduril
+// Emisar D1v2 (linear only, no DDFET) config options for Anduril
// (2022 re-issue / update of old D1)
+// Copyright (C) 2022-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// ATTINY: 1634
// similar to a Noctigon KR4, sort of
#include "cfg-noctigon-kr4-nofet.h"
@@ -11,13 +15,10 @@
#define USE_BUTTON_LED
// the aux LEDs are in the button, so use them while main LEDs are on
#define USE_AUX_RGB_LEDS
-#define USE_AUX_RGB_LEDS_WHILE_ON
+#define USE_AUX_RGB_LEDS_WHILE_ON 25
#define USE_INDICATOR_LED_WHILE_RAMPING
// safe limit: same as regular ramp
#undef SIMPLE_UI_CEIL
#define SIMPLE_UI_CEIL RAMP_SMOOTH_CEIL
-
-// work around bizarre bug: lockout mode fails when set to solid color blinking
-#define USE_K93_LOCKOUT_KLUDGE
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4-219c.h b/spaghetti-monster/anduril/cfg-emisar-d4-219c.h
index e525d86..65649e3 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4-219c.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4-219c.h
@@ -1,5 +1,9 @@
// Emisar D4-219C config options for Anduril
-// same as D4S but with FET modes limited to 80% power
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// same as D4 but with FET modes limited to 80% power
// to avoid destroying the LEDs
#include "cfg-emisar-d4.h"
#undef MODEL_NUMBER
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4.h b/spaghetti-monster/anduril/cfg-emisar-d4.h
index e77ac38..4b3ae5a 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4.h
@@ -1,16 +1,24 @@
// Emisar D4 config options for Anduril
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0111"
-#include "hwdef-Emisar_D4.h"
+#include "hwdef-emisar-d4.h"
#include "hank-cfg.h"
+// ATTINY: 85
+
+#define RAMP_SIZE 150
// ../../bin/level_calc.py 1 65 7135 1 0.8 150
// ... mixed with this:
// ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500
-#define RAMP_LENGTH 150
#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255
+
#define MAX_1x7135 65
-#define HALFSPEED_LEVEL 14
+#define DEFAULT_LEVEL 65
+#define HALFSPEED_LEVEL 15
#define QUARTERSPEED_LEVEL 6
#define RAMP_SMOOTH_FLOOR 1
@@ -21,12 +29,20 @@
#define RAMP_DISCRETE_STEPS 7
// safe limit ~20% power
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL 95
-#define SIMPLE_UI_STEPS 5
+#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#define SIMPLE_UI_CEIL 100
+#define SIMPLE_UI_STEPS 5
// stop panicking at ~30% power or ~1200 lm
#define THERM_FASTER_LEVEL 105
+// don't blink during ramp
+#undef BLINK_AT_RAMP_MIDDLE
+
+// enable extra features
+#define USE_SMOOTH_STEPS
+
// too big, turn off extra features
-#undef USE_TACTICAL_MODE
+//#undef USE_TACTICAL_MODE
+#undef USE_SOS_MODE
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h b/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h
new file mode 100644
index 0000000..c39ac01
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-emisar-d4k-3ch.h
@@ -0,0 +1,106 @@
+// Emisar D4K 3-channel config options for Anduril
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define MODEL_NUMBER "0151"
+#include "hwdef-emisar-d4k-3ch.h"
+#include "hank-cfg.h"
+// ATTINY: 1634
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+
+// turn on the aux LEDs while main LEDs are on
+// (in case there's a RGB button)
+#define USE_AUX_RGB_LEDS_WHILE_ON 40
+#define USE_INDICATOR_LED_WHILE_RAMPING
+
+// channel modes...
+// CM_MAIN2, CM_LED3, CM_LED4, CM_ALL,
+// CM_BLEND34A, CM_BLEND34B, CM_HSV, CM_AUTO3
+#define DEFAULT_CHANNEL_MODE CM_ALL
+
+#define FACTORY_RESET_WARN_CHANNEL CM_LED4
+#define FACTORY_RESET_SUCCESS_CHANNEL CM_MAIN2
+
+#define CONFIG_WAITING_CHANNEL CM_LED3
+#define CONFIG_BLINK_CHANNEL CM_ALL
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN2
+
+// LEDs 3 and 4 make a nice police strobe
+#define POLICE_COLOR_STROBE_CH1 CM_LED3
+#define POLICE_COLOR_STROBE_CH2 CM_LED4
+// aux red + aux blue are the correct colors, but are dim
+//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// how much to increase total brightness at middle tint
+// (0 = 100% brightness, 64 = 200% brightness)
+#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it
+
+// main 2 LEDs
+// output: unknown, 2000 lm?
+// LED 3 / 4
+// output: unknown, 1000 lm each?
+#define RAMP_SIZE 150
+// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits)
+// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640
+// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM)
+#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640
+
+#define MIN_THERM_STEPDOWN 50
+#define DEFAULT_LEVEL 70
+#define MAX_1x7135 70
+// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit
+// and speed changes make a big visible bump
+#define HALFSPEED_LEVEL 255
+#define QUARTERSPEED_LEVEL 255
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 130
+// 10, 30, 50, [70], 90, 110, 130
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
+
+// 10 40 [70] 100 130
+#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
+#define SIMPLE_UI_STEPS 5
+
+// stop panicking at ~1500 lm
+#define THERM_FASTER_LEVEL 130
+
+#define USE_POLICE_COLOR_STROBE_MODE
+#undef TACTICAL_LEVELS
+#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe
+
+// use the brightest setting for strobe
+#define STROBE_BRIGHTNESS MAX_LEVEL
+// slow down party strobe; this driver can't pulse for 1ms or less
+#define PARTY_STROBE_ONTIME 2
+// #define STROBE_OFF_LEVEL 1 // nope, this makes strobe blurry
+// bike strobe needs a longer pulse too?
+//#define BIKE_STROBE_ONTIME 8
+
+// the default of 26 looks a bit flat, so increase it
+#define CANDLE_AMPLITUDE 33
+
+// the power regulator is a bit slow, so push it harder for a quick response from off
+#define DEFAULT_JUMP_START_LEVEL 21
+#define BLINK_BRIGHTNESS 50
+#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow
+
+#define THERM_CAL_OFFSET 5
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// for consistency with KR4 (not otherwise necessary though)
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s-219c.h b/spaghetti-monster/anduril/cfg-emisar-d4s-219c.h
index 6489b34..f86c1b1 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4s-219c.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4s-219c.h
@@ -1,4 +1,8 @@
// Emisar D4S-219C config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// same as D4S but with FET modes limited to 80% power
// to avoid destroying the LEDs
#include "cfg-emisar-d4s.h"
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s.h b/spaghetti-monster/anduril/cfg-emisar-d4s.h
index 3c82460..e966132 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4s.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4s.h
@@ -1,20 +1,21 @@
// Emisar D4S config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0131"
-#include "hwdef-Emisar_D4S.h"
+#include "hwdef-emisar-d4s.h"
#include "hank-cfg.h"
+// ATTINY: 85
-// the button lights up
+// the button lights up (on some models)
#define USE_INDICATOR_LED
// the aux LEDs are behind the main LEDs
#ifdef USE_INDICATOR_LED_WHILE_RAMPING
#undef USE_INDICATOR_LED_WHILE_RAMPING
#endif
-#ifdef RAMP_LENGTH
-#undef RAMP_LENGTH
-#endif
-
-#define RAMP_LENGTH 150
+#define RAMP_SIZE 150
// 3x7135 + FET
// level_calc.py ninth 2 150 7135 1 11.2 450 FET 1 10 4000
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h
index 54a2e6b..22775cc 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h
@@ -1,4 +1,8 @@
// Emisar D4Sv2-219 config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#include "cfg-emisar-d4sv2.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0134"
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2-tintramp-fet.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2-tintramp-fet.h
deleted file mode 100644
index 657e25d..0000000
--- a/spaghetti-monster/anduril/cfg-emisar-d4sv2-tintramp-fet.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Emisar D4S V2 tint-ramping (plus FET) config options for Anduril (based on Noctigon K9.3)
-#include "cfg-emisar-d4sv2-tintramp.h"
-#undef MODEL_NUMBER
-#define MODEL_NUMBER "0136"
-// ATTINY: 1634
-
-// enable the FET channel, even though it's ... kinda funky
-#undef PWM_CHANNELS
-#define PWM_CHANNELS 2
-
-// main LEDs
-// output: unknown, 2000 lm?
-// FET: unknown, 3000 lm?
-// 2nd LEDs
-// output: unknown, 2000 lm?
-#define RAMP_LENGTH 150
-///// copy non-FET ramp, but add FET to the top 10 levels from 141 to 150
-/* old
-// level_calc.py 5.01 1 140 7135 1 0.2 2000 --pwm dyn:69:16383:511
-// plus a FET segment
-// abstract ramp (power is split between both sets of LEDs)
-// level_calc.py 2 1 10 7135 5 50.0 3000 --pwm 255
-// append: ,500,482,456,420,374,318,252,178,94,0
-*/
-/* also old
-// level_calc.py 3 1 11 7135 511 2000 5000 --pwm 1022
-// append: 549,589,633,679,728,780,836,894,957,1022
-//#undef PWM1_LEVELS
-//#define PWM1_LEVELS 1,1,1,2,2,3,3,4,5,5,6,7,8,9,10,12,13,14,16,18,19,21,23,25,27,30,32,35,37,40,43,45,48,51,54,58,61,64,67,70,74,77,80,83,86,89,92,95,97,99,101,103,105,106,106,107,106,106,104,102,100,96,92,87,81,73,65,56,45,33,35,37,39,41,43,45,47,49,52,54,57,59,62,65,68,71,74,78,81,85,89,92,96,100,105,109,114,118,123,128,133,139,144,150,156,162,168,175,181,188,195,202,210,217,225,233,242,250,259,268,278,287,297,307,318,328,339,351,362,374,386,399,412,425,438,452,466,481,496,511,549,589,633,679,728,780,836,894,957,1022
-// append: ,511,511,511,511,511,511,511,511,511,511
-//#undef PWM_TOPS
-//#define PWM_TOPS 16383,13469,10296,14694,10845,14620,11496,13507,14400,11954,12507,12676,12605,12376,12036,12805,12240,11650,11882,11933,11243,11155,10988,10763,10497,10569,10223,10164,9781,9646,9475,9071,8870,8652,8422,8330,8077,7823,7569,7318,7169,6919,6676,6439,6209,5986,5770,5561,5305,5063,4834,4618,4413,4180,3925,3723,3468,3264,3016,2787,2576,2333,2111,1885,1658,1412,1189,968,734,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
-*/
-
-// prepend: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,14,27,45,68,96,129,166,208,255
-#undef DEFAULT_LEVEL
-#define DEFAULT_LEVEL 70
-#undef MAX_1x7135
-#define MAX_1x7135 130
-
-#undef RAMP_SMOOTH_FLOOR
-#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?)
-#undef RAMP_SMOOTH_CEIL
-#define RAMP_SMOOTH_CEIL 130
-// 10, 30, 50, [70], 90, 110, [130]
-#undef RAMP_DISCRETE_FLOOR
-#define RAMP_DISCRETE_FLOOR 10
-#undef RAMP_DISCRETE_CEIL
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#undef RAMP_DISCRETE_STEPS
-#define RAMP_DISCRETE_STEPS 7
-
-// safe limit highest regulated power (no FET or turbo)
-#undef SIMPLE_UI_FLOOR
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#undef SIMPLE_UI_CEIL
-#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-#undef SIMPLE_UI_STEPS
-#define SIMPLE_UI_STEPS 5
-
-// stop panicking at ~2000 lm
-#undef THERM_FASTER_LEVEL
-#define THERM_FASTER_LEVEL 130
-#undef MIN_THERM_STEPDOWN
-#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level
-
-// speed up party strobe; the FET is really fast
-#undef PARTY_STROBE_ONTIME
-
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2-tintramp.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2-tintramp.h
deleted file mode 100644
index 3e54dca..0000000
--- a/spaghetti-monster/anduril/cfg-emisar-d4sv2-tintramp.h
+++ /dev/null
@@ -1,87 +0,0 @@
-// Emisar D4S V2 tint-ramping config options for Anduril (based on Noctigon K9.3)
-#define MODEL_NUMBER "0135"
-#include "hwdef-Emisar_D4Sv2-tintramp.h"
-#include "hank-cfg.h"
-// ATTINY: 1634
-
-// this light has three aux LED channels: R, G, B
-#define USE_AUX_RGB_LEDS
-// the aux LEDs are front-facing, so turn them off while main LEDs are on
-// it also has an independent LED in the button
-#define USE_BUTTON_LED
-// TODO: the whole "indicator LED" thing needs to be refactored into
-// "aux LED(s)" and "button LED(s)" since they work a bit differently
-// enabling this option breaks the button LED on D4v2.5
-#ifdef USE_INDICATOR_LED_WHILE_RAMPING
-#undef USE_INDICATOR_LED_WHILE_RAMPING
-#endif
-
-// has two channels of independent LEDs
-#define USE_TINT_RAMPING
-// how much to increase total brightness at middle tint
-// (0 = 100% brightness, 64 = 200% brightness)
-#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it
-
-// main LEDs
-// output: unknown, 2000 lm?
-// FET: absent / unused?
-// 2nd LEDs
-// output: unknown, 2000 lm?
-#define RAMP_LENGTH 150
-// abstract ramp (power is split between both sets of LEDs)
-// 1-130: 0 to 100% power
-// level_calc.py 5.01 1 130 7135 2 0.2 2000 --pwm dyn:64:16383:511
-// 131 to 150: 101% to 200% power
-// level_calc.py 6.44 1 150 7135 1 0.2 2000 --pwm dyn:74:16383:1022
-#define PWM1_LEVELS 2,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,28,30,33,35,38,41,44,47,50,54,57,60,64,67,71,74,78,81,84,88,91,94,97,99,101,103,105,106,107,107,107,106,105,102,99,95,90,84,77,68,58,47,34,36,38,40,42,44,47,49,52,54,57,60,63,66,69,73,76,80,83,87,91,96,100,104,109,114,119,124,130,135,141,147,153,160,166,173,180,187,195,203,211,219,228,236,245,255,264,274,285,295,306,317,329,340,353,365,378,391,405,419,433,448,463,479,495,511,530,550,570,591,612,634,657,680,705,730,755,782,809,837,865,895,925,957,989,1022
-#define PWM_TOPS 16383,13234,9781,13826,9593,13434,9973,12021,12900,13193,13150,12899,12508,12023,12666,11982,12181,11422,11393,11247,11018,10731,10826,10434,10365,9927,9767,9565,9332,9076,8806,8693,8395,8096,7928,7626,7439,7143,6948,6665,6393,6203,5946,5700,5465,5187,4926,4681,4451,4195,3957,3700,3463,3213,2983,2718,2476,2231,1986,1742,1501,1245,997,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
-#define DEFAULT_LEVEL 70
-#define MAX_1x7135 150
-#define HALFSPEED_LEVEL 10
-#define QUARTERSPEED_LEVEL 2
-
-#define USE_MANUAL_MEMORY_TIMER_FOR_TINT
-//#define DEFAULT_MANUAL_MEMORY DEFAULT_LEVEL
-//#define DEFAULT_MANUAL_MEMORY_TIMER 10
-
-#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?)
-#define RAMP_SMOOTH_CEIL 130
-// 10, 30, 50, [70], 90, 110, [130]
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
-
-// safe limit highest regulated power (no FET or turbo)
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-#define SIMPLE_UI_STEPS 5
-
-// stop panicking at ~1500 lm
-#define THERM_FASTER_LEVEL 130
-#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level
-
-// use the brightest setting for strobe
-#define STROBE_BRIGHTNESS MAX_LEVEL
-// slow down party strobe; this driver can't pulse for 1ms or less
-#define PARTY_STROBE_ONTIME 2
-
-// the default of 26 looks a bit flat, so increase it
-#define CANDLE_AMPLITUDE 33
-
-// the power regulator is a bit slow, so push it harder for a quick response from off
-#define DEFAULT_JUMP_START_LEVEL 21
-#define BLINK_BRIGHTNESS DEFAULT_LEVEL
-#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow
-
-#define THERM_CAL_OFFSET 5
-
-#ifdef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_MIDDLE
-#endif
-
-// for consistency with KR4 (not otherwise necessary though)
-#define USE_SOFT_FACTORY_RESET
-
-
-// work around bizarre bug: lockout mode fails when set to solid color blinking
-#define USE_K93_LOCKOUT_KLUDGE
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
index 5f18fbc..666b394 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
@@ -1,51 +1,81 @@
// Emisar D4S V2 config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0133"
-#include "hwdef-Emisar_D4Sv2.h"
+#include "hwdef-emisar-d4sv2.h"
#include "hank-cfg.h"
// ATTINY: 1634
-// this light has three aux LED channels: R, G, B
-#define USE_AUX_RGB_LEDS
-// the aux LEDs are front-facing, so turn them off while main LEDs are on
-#ifdef USE_INDICATOR_LED_WHILE_RAMPING
-#undef USE_INDICATOR_LED_WHILE_RAMPING
-#endif
-
+#define RAMP_SIZE 150
// 1x7135 + 3x7135 + FET
-// ../../../bin/level_calc.py seventh 3 150 7135 1 2.3 130 7135 11 5 400.1 FET 2 10 4000
-// (and some manual edits to make the clock speed changes smoother)
-#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,17,18,19,20,21,22,24,26,28,30,33,35,38,41,44,47,50,54,57,61,65,69,74,79,84,89,94,100,106,113,119,126,134,142,150,158,167,176,186,196,207,218,230,242,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,16,21,25,30,35,41,46,52,58,64,71,77,84,92,99,107,115,124,133,142,151,161,172,182,193,205,217,229,242,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
-#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,6,8,10,12,14,16,18,20,23,25,27,30,32,35,38,41,44,47,50,53,57,60,64,67,71,75,79,83,87,92,96,101,106,111,116,121,127,132,138,144,150,156,163,169,176,183,190,197,205,213,221,229,237,246,255
-#define MAX_1x7135 62
-#define MAX_Nx7135 93
-#define HALFSPEED_LEVEL 18
-#define QUARTERSPEED_LEVEL 8
-
-//#define DEFAULT_LEVEL MAX_Nx7135
+
+// level_calc.py 5.7895 3 150 7135 1 0.1 130 7135 1 1 321.43 FET 2 10 3000 --pwm dyn:74:4096:255:3
+#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,12,19,25,32,40,47,55,64,72,81,91,101,111,122,133,144,156,169,182,195,209,224,239,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,7,10,12,15,17,20,22,25,28,31,34,38,41,44,48,52,56,60,64,68,72,77,81,86,91,96,101,107,112,118,124,130,136,143,149,156,163,170,178,186,193,201,210,218,227,236,245,255
+#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+
+#define MIN_THERM_STEPDOWN 70 // should be above highest dyn_pwm level
+#define MAX_1x7135 75
+#define MAX_Nx7135 100
+#define HALFSPEED_LEVEL 20
+#define QUARTERSPEED_LEVEL 5
+
+// old
+//// ../../../bin/level_calc.py seventh 3 150 7135 1 2.3 130 7135 11 5 400.1 FET 2 10 4000
+//// (and some manual edits to make the clock speed changes smoother)
+//#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,17,18,19,20,21,22,24,26,28,30,33,35,38,41,44,47,50,54,57,61,65,69,74,79,84,89,94,100,106,113,119,126,134,142,150,158,167,176,186,196,207,218,230,242,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+//#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,16,21,25,30,35,41,46,52,58,64,71,77,84,92,99,107,115,124,133,142,151,161,172,182,193,205,217,229,242,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+//#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,5,6,8,10,12,14,16,18,20,23,25,27,30,32,35,38,41,44,47,50,53,57,60,64,67,71,75,79,83,87,92,96,101,106,111,116,121,127,132,138,144,150,156,163,169,176,183,190,197,205,213,221,229,237,246,255
+//#define PWM_TOPS 255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+//#define MAX_1x7135 62
+//#define MAX_Nx7135 93
+//#define HALFSPEED_LEVEL 18
+//#define QUARTERSPEED_LEVEL 8
+
+
+#define DEFAULT_LEVEL MAX_Nx7135
#define RAMP_SMOOTH_FLOOR 1
#define RAMP_SMOOTH_CEIL 130
-// 20, 38, 56, 75, [93], 111, 130
+// 20, 38, 56, [75], 93, 111, 130
#define RAMP_DISCRETE_FLOOR 20
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
// safe limit ~35% power, 150% of sustainable thermal power
-// 20 44 69 [93] 118
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL 118
+// 25 50 [75] [100] 125
+#define SIMPLE_UI_FLOOR 25
+#define SIMPLE_UI_CEIL 125
#define SIMPLE_UI_STEPS 5
-#define STROBE_BRIGHTNESS MAX_LEVEL
+#define STROBE_BRIGHTNESS MAX_LEVEL
// stop panicking at ~50% power or ~2000 lm
#define THERM_FASTER_LEVEL 130
+#define THERM_CAL_OFFSET 5
+
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
+// use aux red + aux blue for police strobe
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_STROBE_USES_AUX
+#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// the default of 26 looks a bit rough, so increase it to make it smoother
+#define CANDLE_AMPLITUDE 33
+
+// don't blink while ramping
#ifdef BLINK_AT_RAMP_MIDDLE
#undef BLINK_AT_RAMP_MIDDLE
#endif
-// seems relevant on attiny1634
-#define THERM_CAL_OFFSET 5
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h
index 414971a..e9775ec 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2-219.h
@@ -1,12 +1,17 @@
// Emisar D4v2-219 config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#include "cfg-emisar-d4v2.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0114"
// ATTINY: 1634
-// don't turn off first channel at turbo level
-#undef PWM1_LEVELS
-#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
-// 65% FET power
-#undef PWM2_LEVELS
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,15,16,17,18,19,21,22,23,25,26,27,28,30,32,33,34,36,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,78,80,82,84,87,90,92,94,97,99,102,104,108,110,113,116,119,121,125,127,130,134,136,140,143,146,149,153,156,159,163,166
+// don't turn off the low channel at turbo level
+#undef LOW_PWM_LEVELS
+#define LOW_PWM_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+// 65% DDFET power
+#undef HIGH_PWM_LEVELS
+#define HIGH_PWM_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,13,15,16,17,18,19,21,22,23,25,26,27,28,30,32,33,34,36,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,69,71,73,75,78,80,82,84,87,90,92,94,97,99,102,104,108,110,113,116,119,121,125,127,130,134,136,140,143,146,149,153,156,159,163,166
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h
index 717afcf..6eddb40 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h
@@ -1,21 +1,37 @@
// Emisar D4v2-noFET config options for Anduril
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// switch to 1-channel support functions
+#define HWDEF_C_FILE hwdef-emisar-d4v2-nofet.c
+
#include "cfg-emisar-d4v2.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0115"
// ATTINY: 1634
+// the ramp uses only 1x7135 chip, max ~130 lm
#undef PWM_CHANNELS
#define PWM_CHANNELS 1
+
#undef PWM1_LEVELS
#undef PWM2_LEVELS
-#define PWM1_LEVELS 1,1,1,2,2,2,2,3,3,3,3,4,4,5,5,6,6,6,7,8,8,9,9,10,10,11,12,13,13,14,15,16,16,17,18,19,20,21,22,23,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,41,42,43,45,46,47,49,50,52,53,55,56,58,59,61,62,64,66,67,69,71,72,74,76,78,80,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,116,118,120,122,125,127,129,132,134,136,139,141,144,146,148,151,154,156,159,161,164,166,169,172,174,177,180,183,185,188,191,194,197,200,203,205,208,211,214,217,220,223,226,230,233,236,239,242,245,249,252,255
+#undef PWM_TOPS
+//#define PWM1_LEVELS 1,1,1,2,2,2,2,3,3,3,3,4,4,5,5,6,6,6,7,8,8,9,9,10,10,11,12,13,13,14,15,16,16,17,18,19,20,21,22,23,23,24,26,27,28,29,30,31,32,33,34,36,37,38,39,41,42,43,45,46,47,49,50,52,53,55,56,58,59,61,62,64,66,67,69,71,72,74,76,78,80,81,83,85,87,89,91,93,95,97,99,101,103,105,107,109,111,113,116,118,120,122,125,127,129,132,134,136,139,141,144,146,148,151,154,156,159,161,164,166,169,172,174,177,180,183,185,188,191,194,197,200,203,205,208,211,214,217,220,223,226,230,233,236,239,242,245,249,252,255
+// level_calc.py 3.01 1 150 7135 -1 0.1 140 --pwm dyn:64:4096:255:3 --clock 11:21:8.0
+// (and some manual tweaks to make half/quarter speed levels less bumpy)
+#define PWM1_LEVELS 1,1,2,2,3,4,4,5,6,7,9,10,11,11,12,13,13,14,15,15,18,18,17,18,18,19,19,19,19,19,20,20,20,20,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,19,20,20,20,21,21,22,23,24,24,26,27,28,29,30,31,32,33,34,35,36,38,39,40,42,43,44,46,47,49,50,52,53,55,56,58,60,62,63,65,67,69,71,73,75,77,79,81,83,85,88,90,92,95,97,100,102,105,107,110,112,115,118,121,124,127,129,132,135,139,142,145,148,151,155,158,162,165,169,172,176,179,183,187,191,195,199,203,207,211,215,219,223,228,232,237,241,246,250,255
+#define PWM_TOPS 4094,2719,3280,1954,2599,3032,2342,2548,2626,2635,2246,2261,2244,1964,1956,1929,1743,1733,1763,1697,1492,1362,1245,1231,1132,1118,1034,958,889,826,821,767,717,671,629,591,556,523,493,465,440,416,394,373,354,336,319,304,299,293,279,282,269,257,260,249,251,252,253,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+
#undef MAX_1x7135
#define MAX_1x7135 150
#undef QUARTERSPEED_LEVEL
#undef HALFSPEED_LEVEL
-#define QUARTERSPEED_LEVEL 8
-#define HALFSPEED_LEVEL 16
+#define QUARTERSPEED_LEVEL 11
+#define HALFSPEED_LEVEL 21
+#undef DEFAULT_LEVEL
#define DEFAULT_LEVEL 80
#undef RAMP_SMOOTH_CEIL
@@ -34,9 +50,13 @@
#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
+#undef CANDLE_AMPLITUDE
#define CANDLE_AMPLITUDE 60
#undef THERM_FASTER_LEVEL
#define THERM_FASTER_LEVEL 150
-#undef USE_THERMAL_REGULATION
+// maybe keep this, in case someone uses a higher power channel?
+//#undef USE_THERMAL_REGULATION
+//#undef USE_SET_LEVEL_GRADUALLY
+
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2.h b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
index 4121465..0f0bb17 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4v2.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
@@ -1,45 +1,64 @@
-// Emisar D4 config options for Anduril
+// Emisar D4v2 config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0113"
-#include "hwdef-Emisar_D4v2.h"
+#include "hwdef-emisar-d4v2.h"
#include "hank-cfg.h"
// ATTINY: 1634
-// this light has three aux LED channels: R, G, B
-#define USE_AUX_RGB_LEDS
-// it also has an independent LED in the button
-#define USE_BUTTON_LED
-// the aux LEDs are front-facing, so turn them off while main LEDs are on
-// TODO: the whole "indicator LED" thing needs to be refactored into
-// "aux LED(s)" and "button LED(s)" since they work a bit differently
-#ifdef USE_INDICATOR_LED_WHILE_RAMPING
-#undef USE_INDICATOR_LED_WHILE_RAMPING
-#endif
+#define RAMP_SIZE 150
+// 7135 at 75/150
+// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3
+// (with some manual tweaks)
+// non-zero part of FET channel calculated with:
+// level_calc.py 3 1 75 7135 2 500 3000
+#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,5,7,9,11,13,15,17,19,21,23,25,27,29,32,34,36,39,41,43,46,49,51,54,57,59,62,65,68,71,74,77,80,83,86,90,93,96,100,103,107,110,114,117,121,125,129,133,137,141,145,149,153,157,162,166,170,175,179,184,189,193,198,203,208,213,218,223,228,233,239,244,249,255
+#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
-// copied from original D4, since it's also a FET+1 and has the same host
-// ../../bin/level_calc.py 1 65 7135 1 0.8 150
-// ... mixed with this:
-// ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500
-#define RAMP_LENGTH 150
-#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255
-#define MAX_1x7135 65
-#define HALFSPEED_LEVEL 14
-#define QUARTERSPEED_LEVEL 6
+#define MAX_1x7135 75
+#define DEFAULT_LEVEL 50
+#define MIN_THERM_STEPDOWN 70 // should be above highest dyn_pwm level
+#define HALFSPEED_LEVEL 20
+#define QUARTERSPEED_LEVEL 5
#define RAMP_SMOOTH_FLOOR 1
-#define RAMP_SMOOTH_CEIL 120
-// 10, 28, 46, [65], 83, 101, 120
-#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_SMOOTH_CEIL 130
+// 20, 38, 56, [75], 93, 111, 130
+#define RAMP_DISCRETE_FLOOR 20
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
-// safe limit ~20% power
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL 95
+// 20, 40, 60, 80, 100
+#define SIMPLE_UI_FLOOR 20
+#define SIMPLE_UI_CEIL 100
#define SIMPLE_UI_STEPS 5
-// stop panicking at ~30% power or ~1200 lm
-#define THERM_FASTER_LEVEL 105
+// stop panicking at ~25% power or ~1000 lm
+#define THERM_FASTER_LEVEL 110
#define THERM_CAL_OFFSET 5
+
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
+// use aux red + aux blue for police strobe
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_STROBE_USES_AUX
+#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// the default of 26 looks a bit rough, so increase it to make it smoother
+#define CANDLE_AMPLITUDE 33
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
diff --git a/spaghetti-monster/anduril/cfg-ff-e01.h b/spaghetti-monster/anduril/cfg-ff-e01.h
index f26bfb4..07bda87 100644
--- a/spaghetti-monster/anduril/cfg-ff-e01.h
+++ b/spaghetti-monster/anduril/cfg-ff-e01.h
@@ -1,16 +1,36 @@
// Fireflies E01 SST-40 thrower config options for Anduril
-// most of the good stuff is in the FFUI config; just copy it
-#include "../fireflies-ui/cfg-ff-e01.h"
-#undef MODEL_NUMBER
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0441"
+#include "hwdef-ff-pl47.h"
+// ATTINY: 85
-#ifndef BLINK_AT_RAMP_CEIL
-#define BLINK_AT_RAMP_CEIL
+// disable indicator LED; it's hardwired
+#ifdef USE_INDICATOR_LED
+#undef USE_INDICATOR_LED
#endif
+
+#define RAMP_SIZE 150
+
+// driver is a FET + 3x7135, ~413 lm at highest regulated level
+// ../../../bin/level_calc.py seventh 2 150 7135 1 12 414 FET 2 10 1930
+#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,6,7,8,8,9,10,10,11,12,13,14,15,15,16,17,18,20,21,22,23,24,26,27,28,30,31,33,34,36,38,39,41,43,45,47,49,51,53,56,58,60,63,65,68,71,74,77,80,83,86,89,93,96,100,103,107,111,115,119,124,128,132,137,142,147,152,157,163,168,174,180,186,192,198,204,211,218,225,232,240,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,9,12,14,16,19,22,24,27,30,33,36,39,42,45,48,52,55,58,62,66,69,73,77,81,85,90,94,98,103,107,112,117,122,127,132,137,143,148,154,160,166,172,178,184,191,197,204,211,218,225,232,240,247,255
+#define MAX_1x7135 93
+#define HALFSPEED_LEVEL 14
+#define QUARTERSPEED_LEVEL 7
+
+// ceiling is level 130/150 (50% power)
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 130
+
// 20, 38, 56, 75, [93], 111, 130 (93 is highest regulated)
// (9 / 45 / 116 / 248 / 467 / 742 / 1280 + 2140 lm)
-#undef RAMP_DISCRETE_STEPS
+#define RAMP_DISCRETE_FLOOR 20
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
// safe limit ~50% power
@@ -19,5 +39,19 @@
#define SIMPLE_UI_CEIL 130
#define SIMPLE_UI_STEPS 4
+// thermal regulation parameters
+#define MIN_THERM_STEPDOWN 65 // lowest value it'll step down to
+#define THERM_FASTER_LEVEL 130
+
+#ifndef BLINK_AT_RAMP_CEIL
+#define BLINK_AT_RAMP_CEIL
+#endif
+
+// don't do this
+#undef BLINK_AT_RAMP_MIDDLE
+
+#define USE_SMOOTH_STEPS
+
// too big, turn off extra features
-#undef USE_TACTICAL_MODE
+#undef USE_SOS_MODE
+//#undef USE_TACTICAL_MODE
diff --git a/spaghetti-monster/anduril/cfg-ff-pl47-219.h b/spaghetti-monster/anduril/cfg-ff-pl47-219.h
index efba8d3..11bd5b1 100644
--- a/spaghetti-monster/anduril/cfg-ff-pl47-219.h
+++ b/spaghetti-monster/anduril/cfg-ff-pl47-219.h
@@ -1,4 +1,8 @@
// Fireflies PL47-219B config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// same as PL47 but with FET modes limited to 67% power
// to avoid destroying the LEDs
#include "cfg-ff-pl47.h"
diff --git a/spaghetti-monster/anduril/cfg-ff-pl47.h b/spaghetti-monster/anduril/cfg-ff-pl47.h
index 54827cf..6436477 100644
--- a/spaghetti-monster/anduril/cfg-ff-pl47.h
+++ b/spaghetti-monster/anduril/cfg-ff-pl47.h
@@ -1,6 +1,11 @@
// Fireflies PL47 config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0421"
-#include "hwdef-FF_PL47.h"
+#include "hwdef-ff-pl47.h"
+// ATTINY: 85
// the button lights up
#define USE_INDICATOR_LED
@@ -20,7 +25,7 @@
#define INDICATOR_LED_SKIP_LOW
-#define RAMP_LENGTH 150
+#define RAMP_SIZE 150
// driver is a FET + 3x7135, ~400 lm at highest regulated level
// ramp copied from Emisar D4S ramp
@@ -30,13 +35,8 @@
#define HALFSPEED_LEVEL 13
#define QUARTERSPEED_LEVEL 6
-// thermal regulation parameters
-#ifdef MIN_THERM_STEPDOWN
-#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135
-#endif
-#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to
-
// ceiling is level 120/150
+#define RAMP_SMOOTH_FLOOR 1
#define RAMP_SMOOTH_CEIL 120
// 10, 28, 46, 65, 83, 101, 120 (83 is highest regulated)
@@ -57,12 +57,23 @@
//#define SIMPLE_UI_FLOOR 40
//#define SIMPLE_UI_CEIL 90
+// thermal regulation parameters
+#ifdef MIN_THERM_STEPDOWN
+#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135
+#endif
+#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to
// regulate down faster when the FET is active, slower otherwise
#define THERM_FASTER_LEVEL 135 // throttle back faster when high
+#ifndef BLINK_AT_RAMP_CEIL
+#define BLINK_AT_RAMP_CEIL
+#endif
+
// don't do this
#undef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_CEIL
+
+#define USE_SMOOTH_STEPS
// too big, turn off extra features
+#undef USE_SOS_MODE
#undef USE_TACTICAL_MODE
diff --git a/spaghetti-monster/anduril/cfg-ff-pl47g2.h b/spaghetti-monster/anduril/cfg-ff-pl47g2.h
index c11ba28..830d7e8 100644
--- a/spaghetti-monster/anduril/cfg-ff-pl47g2.h
+++ b/spaghetti-monster/anduril/cfg-ff-pl47g2.h
@@ -1,6 +1,11 @@
// Fireflies PL47 G2 config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0423"
-#include "hwdef-FF_PL47.h"
+#include "hwdef-ff-pl47.h"
+// ATTINY: 85
// the button lights up
#define USE_INDICATOR_LED
@@ -14,7 +19,8 @@
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
-#define RAMP_LENGTH 150
+
+#define RAMP_SIZE 150
// driver is a FET + 3x7135, ~400 lm at highest regulated level
// ramp copied from Emisar D4S ramp
@@ -24,16 +30,11 @@
#define HALFSPEED_LEVEL 13
#define QUARTERSPEED_LEVEL 6
-// thermal regulation parameters
-#ifdef MIN_THERM_STEPDOWN
-#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135
-#endif
-#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to
-
// ceiling is level 120/150
+#define RAMP_SMOOTH_FLOOR 1
#define RAMP_SMOOTH_CEIL 120
-// 10, 28, 46, 65, [83], 101, 120
+// 10, 28, 46, 65, 83, 101, 120 (83 is highest regulated)
#define RAMP_DISCRETE_FLOOR 10
#define RAMP_DISCRETE_CEIL 120
#define RAMP_DISCRETE_STEPS 7
@@ -44,12 +45,23 @@
#define SIMPLE_UI_CEIL 108
#define SIMPLE_UI_STEPS 5
+// thermal regulation parameters
+#ifdef MIN_THERM_STEPDOWN
+#undef MIN_THERM_STEPDOWN // this should be lower, because 3x7135 instead of 1x7135
+#endif
+#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to
// regulate down faster when the FET is active, slower otherwise
#define THERM_FASTER_LEVEL 135 // throttle back faster when high
+#ifndef BLINK_AT_RAMP_CEIL
+#define BLINK_AT_RAMP_CEIL
+#endif
+
// don't do this
#undef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_CEIL
+
+#define USE_SMOOTH_STEPS
// too big, turn off extra features
+#undef USE_SOS_MODE
#undef USE_TACTICAL_MODE
diff --git a/spaghetti-monster/anduril/cfg-ff-rot66-219.h b/spaghetti-monster/anduril/cfg-ff-rot66-219.h
index 086f8ac..4dad4e2 100644
--- a/spaghetti-monster/anduril/cfg-ff-rot66-219.h
+++ b/spaghetti-monster/anduril/cfg-ff-rot66-219.h
@@ -1,4 +1,8 @@
// Fireflies ROT66-219 (7x7135) config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// same as regular ROT66, but calibrated for Nichia 219B with 7x7135 chips
#include "cfg-ff-rot66.h"
#undef MODEL_NUMBER
diff --git a/spaghetti-monster/anduril/cfg-ff-rot66.h b/spaghetti-monster/anduril/cfg-ff-rot66.h
index d386f17..752ddbb 100644
--- a/spaghetti-monster/anduril/cfg-ff-rot66.h
+++ b/spaghetti-monster/anduril/cfg-ff-rot66.h
@@ -1,6 +1,10 @@
// Fireflies ROT66 (14x7135) config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0411"
-#include "hwdef-FF_ROT66.h"
+#include "hwdef-ff-rot66.h"
// the button lights up
#define USE_INDICATOR_LED
@@ -11,28 +15,30 @@
// lockout: blinking (3)
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 2)
+#define RAMP_SIZE 150
// driver is a FET+N+1,
// where N=6 for the 219b version,
// or N=13 for the XP-L HI version (this version)
// calculated to get transition points at level 65 and 125
-#define RAMP_LENGTH 150
// first 65 steps copied from FW3A
#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
// remaining steps from this command:
// level_calc.py ninth 3 150 7135 1 1.4 110.1 7135 15 1 1644.16 FET 1 10 4500
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,16,17,18,19,20,21,22,23,25,26,27,29,30,32,34,35,37,39,41,43,46,48,50,53,55,58,61,64,67,70,73,77,81,84,88,92,97,101,106,110,115,120,126,131,137,143,149,156,162,169,177,184,192,200,208,217,226,235,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,7,13,21,28,35,43,51,60,68,77,87,96,106,117,127,138,150,161,173,186,199,212,226,240,255
-#define MAX_1x7135 65
-#define MAX_Nx7135 125
-#define HALFSPEED_LEVEL 14
-#define QUARTERSPEED_LEVEL 8
+#define MAX_1x7135 65
+#define MAX_Nx7135 125
+#define HALFSPEED_LEVEL 14
+#define QUARTERSPEED_LEVEL 8
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL MAX_Nx7135
// safe limit max regulated power
// 20 46 72 98 [125]
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL MAX_Nx7135
-#define SIMPLE_UI_STEPS 5
+#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#define SIMPLE_UI_CEIL MAX_Nx7135
+#define SIMPLE_UI_STEPS 5
// regulate down faster when the FET is active, slower otherwise
#define THERM_FASTER_LEVEL 130 // throttle back faster when high
@@ -41,18 +47,19 @@
#undef BLINK_AT_RAMP_MIDDLE
#undef BLINK_AT_RAMP_CEIL
-// too big, remove stuff to make room
+// enable extra features
+#define USE_SMOOTH_STEPS
+
+// too big, turn off extra features
+#undef USE_MOMENTARY_MODE
+#undef USE_TACTICAL_MODE
#undef USE_SOS_MODE
-#undef USE_RAMP_AFTER_MOON_CONFIG
-#undef USE_RAMP_SPEED_CONFIG
-#undef USE_VOLTAGE_CORRECTION
+//#undef USE_RAMP_AFTER_MOON_CONFIG
+//#undef USE_RAMP_SPEED_CONFIG
+//#undef USE_VOLTAGE_CORRECTION
//#undef USE_2C_STYLE_CONFIG
//#undef USE_TACTICAL_STROBE_MODE
+//#ifdef USE_LOWPASS_WHILE_ASLEEP
+//#undef USE_LOWPASS_WHILE_ASLEEP
+//#endif
-// reduce size a bit
-#ifdef USE_LOWPASS_WHILE_ASLEEP
-#undef USE_LOWPASS_WHILE_ASLEEP
-#endif
-
-// too big, turn off extra features
-#undef USE_TACTICAL_MODE
diff --git a/spaghetti-monster/anduril/cfg-ff-rot66g2.h b/spaghetti-monster/anduril/cfg-ff-rot66g2.h
index 9737cc4..04aac53 100644
--- a/spaghetti-monster/anduril/cfg-ff-rot66g2.h
+++ b/spaghetti-monster/anduril/cfg-ff-rot66g2.h
@@ -1,4 +1,8 @@
// Fireflies ROT66 G2 config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#include "cfg-ff-rot66.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0413"
@@ -14,6 +18,9 @@
#endif
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+// the button is *not* visible while main LEDs are on
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+
// ramp shape is different than original ROT66
// 1x7135: 150 lm
// Nx7135: 1200 lm
diff --git a/spaghetti-monster/anduril/cfg-fw3a-219.h b/spaghetti-monster/anduril/cfg-fw3a-219.h
index c9bf0c1..0bd5250 100644
--- a/spaghetti-monster/anduril/cfg-fw3a-219.h
+++ b/spaghetti-monster/anduril/cfg-fw3a-219.h
@@ -1,4 +1,8 @@
// FW3A-219 config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#include "cfg-fw3a.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0312"
@@ -9,3 +13,4 @@
#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,13,19,25,30,36,42,48,54,61,67,73,80,86,93,100,107,114,120,128
+
diff --git a/spaghetti-monster/anduril/cfg-fw3a-nofet.h b/spaghetti-monster/anduril/cfg-fw3a-nofet.h
index 7752c10..b5d4166 100644
--- a/spaghetti-monster/anduril/cfg-fw3a-nofet.h
+++ b/spaghetti-monster/anduril/cfg-fw3a-nofet.h
@@ -1,12 +1,16 @@
// FW3A with the FET disabled
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#include "cfg-fw3a.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0313"
// don't use channel 3 (FET)
#undef PWM_CHANNELS
-#undef PWM3_PIN
-#undef PWM3_LVL
+#undef CH3_PIN
+#undef CH3_PWM
#define PWM_CHANNELS 2
// reconfigure the ramp
@@ -18,8 +22,9 @@
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255
#undef MAX_1x7135
#define MAX_1x7135 65
+#undef MAX_Nx7135
#undef HALFSPEED_LEVEL
-#define HALFSPEED_LEVEL 14
+#define HALFSPEED_LEVEL 15
#undef QUARTERSPEED_LEVEL
#define QUARTERSPEED_LEVEL 6
@@ -41,3 +46,9 @@
#undef SIMPLE_UI_CEIL
#define SIMPLE_UI_CEIL 120
+#undef THERM_FASTER_LEVEL
+#define THERM_FASTER_LEVEL 130
+
+// without the 3rd channel, extra features can fit
+#define USE_TACTICAL_MODE
+
diff --git a/spaghetti-monster/anduril/cfg-fw3a.h b/spaghetti-monster/anduril/cfg-fw3a.h
index 24a77c0..53c4135 100644
--- a/spaghetti-monster/anduril/cfg-fw3a.h
+++ b/spaghetti-monster/anduril/cfg-fw3a.h
@@ -1,30 +1,54 @@
// FW3A config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0311"
-#include "hwdef-FW3A.h"
+#include "hwdef-fw3a.h"
+
+#define RAMP_SIZE 150
// ../../bin/level_calc.py 1 65 7135 1 0.8 150
// ... mixed with this:
// ../../../bin/level_calc.py 3 150 7135 1 0.33 150 7135 1 1 850 FET 1 10 1500
-#define RAMP_LENGTH 150
#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,19,31,43,55,67,79,91,104,117,130,143,157,170,184,198,212,226,240,255
-#define MAX_1x7135 65
-#define MAX_Nx7135 130
-#define HALFSPEED_LEVEL 14
-#define QUARTERSPEED_LEVEL 5
+
+#define DEFAULT_LEVEL 50
+#define MAX_1x7135 65
+#define MAX_Nx7135 130
+#define HALFSPEED_LEVEL 15
+#define QUARTERSPEED_LEVEL 6
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL MAX_Nx7135
+// 10, 30, 50, [70], 90, 110, 130
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
// safe limit ~20% power
// 20 40 60 80 100
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL 100
-#define SIMPLE_UI_STEPS 5
+#define SIMPLE_UI_FLOOR 20
+#define SIMPLE_UI_CEIL 100
+#define SIMPLE_UI_STEPS 5
// stop panicking at about 3A or ~1100 lm, this light is a hotrod
#define THERM_FASTER_LEVEL MAX_Nx7135
+// don't blink during ramp
+#define BLINK_AT_RAMP_CEIL
+#undef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_FLOOR
+
+// enable extra features
+#define USE_SMOOTH_STEPS
+
// can't reset the normal way because power is connected before the button
#define USE_SOFT_FACTORY_RESET
// too big, turn off extra features
#undef USE_TACTICAL_MODE
+#undef USE_SOS_MODE
+
diff --git a/spaghetti-monster/anduril/cfg-fw3x-lume1.h b/spaghetti-monster/anduril/cfg-fw3x-lume1.h
index 2f90920..184ab8e 100644
--- a/spaghetti-monster/anduril/cfg-fw3x-lume1.h
+++ b/spaghetti-monster/anduril/cfg-fw3x-lume1.h
@@ -1,4 +1,9 @@
-/* lume1 for FW3x config options for Anduril
+// lume1 for FW3x config options for Anduril
+// Copyright (C) 2020-2023 LoneOceans, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+/*
* Constant current Buck-Boost + FET driver
* For more information: www.loneoceans.com/labs/
* Datasheets:
@@ -6,10 +11,7 @@
* - 85: https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-2586-AVR-8-bit-Microcontroller-ATtiny25-ATtiny45-ATtiny85_Datasheet.pdf
*/
-// TODO: determine if "03" (Lumintop) is the right prefix,
-// or if there should be a brand ID for LoneOceans
#define MODEL_NUMBER "0314"
-
#include "hwdef-fw3x-lume1.h"
// ATTINY: 1634
@@ -31,7 +33,7 @@
// ../../bin/level_calc.py cube 1 149 7135 0 0.5 1000, with 0 appended to the end.
// for FET PWM (PWM2), all values are 0, except for last value of 1023
// (with max_pwm set to 1023)
-#define RAMP_LENGTH 150
+#define RAMP_SIZE 150
//#define PWM1_LEVELS 0,0,0,0,1,1,1,1,2,2,2,3,3,4,4,5,5,6,7,7,8,9,10,11,12,13,14,15,16,17,19,20,22,23,25,26,28,30,32,34,36,38,40,42,45,47,49,52,55,58,60,63,66,70,73,76,80,83,87,91,94,98,102,107,111,115,120,124,129,134,139,144,150,155,160,166,172,178,184,190,196,203,209,216,223,230,237,244,252,259,267,275,283,291,299,308,316,325,334,343,353,362,372,382,392,402,412,423,433,444,455,466,478,489,501,513,525,538,550,563,576,589,602,616,630,644,658,672,687,701,716,731,747,762,778,794,810,827,844,861,878,895,913,930,948,967,985,1004,1023,0
#define PWM1_LEVELS 1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,12,13,14,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,56,58,61,64,67,70,74,77,80,84,88,91,95,99,103,108,112,116,121,125,130,135,140,145,150,156,161,167,173,178,184,191,197,203,210,217,223,230,238,245,252,260,268,275,283,292,300,308,317,326,335,344,353,363,372,382,392,402,413,423,434,445,456,467,478,490,502,514,526,538,551,563,576,589,603,616,630,644,658,672,687,702,717,732,747,763,778,794,811,827,844,861,878,895,913,931,949,967,985,1004,1023,0
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1023
@@ -62,25 +64,18 @@
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
-// Muggle mode: Goal from about ~10 lumens to about ~300+ lumens
-// In this case, ramp number 99 is about 1A to the driver
-#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR
-#define MUGGLE_CEILING 99
+#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#define SIMPLE_UI_CEIL 120
+#define SIMPLE_UI_STEPS 5
// slow down party strobe; this driver can't pulse for too short a time
#define PARTY_STROBE_ONTIME 4
-// optional, makes initial turbo step-down faster so first peak isn't as hot
-// FET mode can run very very hot, so be extra careful
-//#define THERM_HARD_TURBO_DROP
-
// stop panicking at ~85% regulated power or ~750 lm
#define THERM_FASTER_LEVEL 140
#define THERM_CAL_OFFSET 0 // not needed due to external sensor
-// easier access to thermal config mode, similar to Emisar, Noctigon
-#define USE_TENCLICK_THERMAL_CONFIG
-
// can't reset the normal way because power is connected before the button
#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h b/spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h
index 9036d26..4082fca 100644
--- a/spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h
+++ b/spaghetti-monster/anduril/cfg-gchart-fet1-t1616.h
@@ -1,4 +1,8 @@
// gChart's custom FET+1 driver config options for Anduril
+// Copyright (C) 2020-2023 gchart, Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "1618" // Golden Ratio... because I can
#include "hwdef-gchart-fet1-t1616.h"
// ATTINY: 1616
@@ -11,25 +15,49 @@
// lockout: blinking (3)
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+#define RAMP_SIZE 150
+
+// 7135 at 90/150
+// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0
+// (with heavy manual tweaks up to ~15/150)
+#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255
+#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define MAX_1x7135 90
+#define MIN_THERM_STEPDOWN 60
+#define HALFSPEED_LEVEL 11
+#define QUARTERSPEED_LEVEL 5
+#define DEFAULT_LEVEL 50
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 130
+// 10 30 50 70 [90] 110 130
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL 130
+#define RAMP_DISCRETE_STEPS 7
+
+// 10 30 50 70 [90] 110 130
+#define SIMPLE_UI_FLOOR 10
+#define SIMPLE_UI_CEIL 130
+#define SIMPLE_UI_STEPS 7
+
+// stop panicking at ~50% power
+#define THERM_FASTER_LEVEL 130 // throttle back faster when high
+
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the aux LEDs by default
+//#define DEFAULT_BLINK_CHANNEL CM_AUX
+
+// the default of 26 looks a bit rough, so increase it to make it smoother
+#define CANDLE_AMPLITUDE 40
+
+// don't blink mid-ramp
+#ifdef BLINK_AT_RAMP_MIDDLE
#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// enable factory reset on 13H without loosening tailcap (required)
+#define USE_SOFT_FACTORY_RESET
-// Mostly borrowed from the D4 for now
-#define RAMP_LENGTH 150
-#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255
-#define MAX_1x7135 65
-#define HALFSPEED_LEVEL 14
-#define QUARTERSPEED_LEVEL 6
-
-#define RAMP_SMOOTH_FLOOR 1
-#define RAMP_SMOOTH_CEIL 120
-// 10, 28, 46, [65], 83, 101, 120
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
-
-// stop panicking at ~30% power
-#define THERM_FASTER_LEVEL 105
-
-// enable 2 click turbo
-#define DEFAULT_2C_STYLE 1
diff --git a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h
index 894ebaf..fb62ac6 100644
--- a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h
+++ b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h
@@ -1,6 +1,10 @@
// Mateminco/Astrolux MF01-Mini options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0521"
-#include "hwdef-Mateminco_MF01-Mini.h"
+#include "hwdef-mateminco-mf01-mini.h"
// the button lights up
#define USE_INDICATOR_LED
@@ -12,10 +16,7 @@
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
-// don't blink during ramp, it's irrelevant and annoying on this light
-#define BLINK_AT_RAMP_CEIL
-#undef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_FLOOR
+#define RAMP_SIZE 150
// measured brightness with Sofirn 5500mAh cell at 3.97V:
// moon: 0.3 lm
@@ -24,13 +25,13 @@
// channel 3: 3500 lm
// ../../../bin/level_calc.py ninth 3 150 7135 1 2.5 115.65 7135 11 5 708.65 FET 1 10 3500
// (plus some manual tweaks for a smoother ramp)
-#define RAMP_LENGTH 150
#define PWM1_LEVELS 1,1,2,2,3,3,4,5,5,6,7,8,9,9,10,14,15,16,17,18,19,20,21,22,24,26,28,30,32,34,37,39,42,45,48,51,54,58,62,65,69,74,78,83,88,93,98,104,110,116,123,130,137,145,153,161,170,179,188,198,208,219,231,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,11,13,16,18,20,23,25,28,31,34,37,40,43,47,50,54,58,62,66,70,75,80,85,90,95,100,106,112,118,125,131,138,145,153,161,169,177,185,194,204,213,223,233,244,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM3_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,6,10,13,17,21,24,28,33,37,41,46,50,55,60,66,71,76,82,88,94,101,107,114,121,128,135,143,151,159,167,176,185,194,203,213,223,233,244,255
-#define MAX_1x7135 65 // ~113 lm
-#define MAX_Nx7135 110
-#define HALFSPEED_LEVEL 16
+
+#define MAX_1x7135 65 // ~113 lm
+#define MAX_Nx7135 110
+#define HALFSPEED_LEVEL 16
#define QUARTERSPEED_LEVEL 8
#define RAMP_SMOOTH_FLOOR 1 // ~0.3 lm
@@ -51,18 +52,23 @@
#define THERM_FASTER_LEVEL 130 // throttle back faster when high
+// don't blink during ramp
+//#define BLINK_AT_RAMP_CEIL
+#undef BLINK_AT_RAMP_CEIL
+#undef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_FLOOR
+
+// enable extra features
+#define USE_SMOOTH_STEPS
-// too big, remove stuff to make room
+// too big, turn off extra features
+#define USE_SOFT_FACTORY_RESET
+//#undef USE_SIMPLE_UI
+#undef USE_TACTICAL_MODE
#undef USE_SOS_MODE
+#undef USE_BEACON_MODE
#undef USE_RAMP_AFTER_MOON_CONFIG
-#undef USE_RAMP_SPEED_CONFIG
+//#undef USE_RAMP_SPEED_CONFIG
#undef USE_VOLTAGE_CORRECTION
//#undef USE_2C_STYLE_CONFIG
-// reduce size a bit
-#ifdef USE_LOWPASS_WHILE_ASLEEP
-#undef USE_LOWPASS_WHILE_ASLEEP
-#endif
-
-// too big, turn off extra features
-#undef USE_TACTICAL_MODE
diff --git a/spaghetti-monster/anduril/cfg-mateminco-mf01s.h b/spaghetti-monster/anduril/cfg-mateminco-mf01s.h
index 49dbcf0..de92693 100644
--- a/spaghetti-monster/anduril/cfg-mateminco-mf01s.h
+++ b/spaghetti-monster/anduril/cfg-mateminco-mf01s.h
@@ -1,6 +1,11 @@
-// Mateminco/Astrolux MF01S options for Anduril
+// Mateminco MT18S / Astrolux MF01S options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0511"
-#include "hwdef-Mateminco_MF01S.h"
+#include "hwdef-mateminco-mf01s.h"
+// ATTINY: 85
// the button lights up
#define USE_INDICATOR_LED
@@ -11,10 +16,7 @@
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
-// don't blink during ramp, it's irrelevant and annoying on this light
-#define BLINK_AT_RAMP_CEIL
-#undef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_FLOOR
+#define RAMP_SIZE 150
// measured brightness with 4x30Q cells at 4.11V:
// moon: 2.5 lm
@@ -22,9 +24,9 @@
// channel 2: 13500 lm
// ../../../bin/level_calc.py seventh 2 150 7135 1 12 717 FET 1 10 13000
// (with some manual tweaks afterward)
-#define RAMP_LENGTH 150
#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,5,6,7,7,8,9,10,11,12,13,14,15,17,18,19,21,22,24,26,28,30,32,34,36,38,41,44,46,49,52,55,59,62,66,70,74,78,83,87,92,97,102,108,114,120,126,133,139,147,154,162,170,178,187,196,206,215,226,236,248,255,235,255,240,255,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,3,4,4,5,6,7,8,9,10,11,12,13,14,16,17,19,20,22,23,25,26,28,30,31,33,35,37,39,41,43,45,47,49,52,54,57,59,62,65,67,70,73,76,80,83,86,90,93,97,101,105,109,113,117,122,126,131,135,140,145,151,156,161,167,173,179,185,191,197,204,211,218,225,232,239,247,255
+
#define MAX_1x7135 70 // ~626 lm
#define HALFSPEED_LEVEL 23
#define QUARTERSPEED_LEVEL 6
@@ -45,5 +47,15 @@
#define THERM_FASTER_LEVEL 125 // throttle back faster when high (>6000 lm)
#define THERM_HARD_TURBO_DROP // this light is massively overpowered
+// don't blink during ramp, it's irrelevant and annoying on this light
+#define BLINK_AT_RAMP_CEIL
+#undef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_FLOOR
+
+// enable extra features
+#define USE_SMOOTH_STEPS
+
// too big, turn off extra features
#undef USE_TACTICAL_MODE
+#undef USE_SOS_MODE
+
diff --git a/spaghetti-monster/anduril/cfg-mateminco-mt35-mini.h b/spaghetti-monster/anduril/cfg-mateminco-mt35-mini.h
index c5b40e2..1ca5008 100644
--- a/spaghetti-monster/anduril/cfg-mateminco-mt35-mini.h
+++ b/spaghetti-monster/anduril/cfg-mateminco-mt35-mini.h
@@ -1,40 +1,42 @@
// Mateminco MT35 Mini / Astrolux FT03
+// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0531"
-#include "hwdef-Mateminco_MT35-Mini.h"
+#include "hwdef-mateminco-mt35-mini.h"
// ATTINY: 85
-// this light should be fine running a bit hotter than most
-#undef DEFAULT_THERM_CEIL
-#define DEFAULT_THERM_CEIL 55
-
// the button lights up
#define USE_INDICATOR_LED
// the button is visible while main LEDs are on
#define USE_INDICATOR_LED_WHILE_RAMPING
-// enable blinking aux LEDs
-#define TICK_DURING_STANDBY
-#define STANDBY_TICK_SPEED 3 // every 0.128 s
+#define RAMP_SIZE 150
-#define RAMP_LENGTH 150
// level_calc.py fifth 2 150 7135 1 1 120 FET 1 10 2000
#define PWM1_LEVELS 1,2,2,3,4,5,6,7,8,9,11,13,14,16,18,21,23,26,28,31,35,38,41,45,49,53,58,63,67,73,78,84,90,96,102,109,116,124,131,139,147,156,165,174,184,194,204,215,226,237,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,2,3,4,5,6,7,8,9,10,11,12,14,15,16,17,18,20,21,22,24,25,27,28,30,31,33,35,36,38,40,42,43,45,47,49,51,53,55,57,59,62,64,66,68,71,73,75,78,80,83,86,88,91,94,96,99,102,105,108,111,114,117,120,124,127,130,134,137,140,144,148,151,155,159,162,166,170,174,178,182,186,190,195,199,203,208,212,217,221,226,231,235,240,245,250,255
-#define DEFAULT_LEVEL 46
-#define MAX_1x7135 52
-#define HALFSPEED_LEVEL 20
-#define QUARTERSPEED_LEVEL 10
-#define RAMP_SMOOTH_FLOOR 1
-#define RAMP_SMOOTH_CEIL 112
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
+#define DEFAULT_LEVEL 46
+#define MAX_1x7135 52
+#define HALFSPEED_LEVEL 20
+#define QUARTERSPEED_LEVEL 10
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 112
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
// safe limit ~33% power
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL RAMP_LENGTH
-#define SIMPLE_UI_STEPS 5
+#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
+#define SIMPLE_UI_STEPS 5
+
+// this light should be fine running a bit hotter than most
+#undef DEFAULT_THERM_CEIL
+#define DEFAULT_THERM_CEIL 55
// stop panicking at ~40% power
#define THERM_FASTER_LEVEL 120
@@ -50,5 +52,14 @@
// Allow 3C in Simple UI for switching between smooth and stepped ramping
#define USE_SIMPLE_UI_RAMPING_TOGGLE
+// enable extra features
+#define USE_SMOOTH_STEPS
+
// too big, turn off extra features
#undef USE_TACTICAL_MODE
+#undef USE_SOS_MODE
+//#undef USE_RAMP_AFTER_MOON_CONFIG
+//#undef USE_RAMP_SPEED_CONFIG
+//#undef USE_VOLTAGE_CORRECTION
+//#undef USE_2C_STYLE_CONFIG
+
diff --git a/spaghetti-monster/anduril/cfg-noctigon-dm11-12v.h b/spaghetti-monster/anduril/cfg-noctigon-dm11-boost.h
index bd41660..2174fdb 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-dm11-12v.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-dm11-boost.h
@@ -1,41 +1,44 @@
-// Noctigon DM11 (12V) config options for Anduril
+// Noctigon DM11 (boost driver) config options for Anduril
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0273"
-#include "hwdef-Noctigon_DM11-12V.h"
+#include "hwdef-noctigon-dm11-boost.h"
#include "hank-cfg.h"
// ATTINY: 1634
-// this light has three aux LED channels: R, G, B
-#define USE_AUX_RGB_LEDS
-// ... and a single LED in the button
-#define USE_BUTTON_LED
-// don't use aux LEDs while main LED is on
-#ifdef USE_INDICATOR_LED_WHILE_RAMPING
-#undef USE_INDICATOR_LED_WHILE_RAMPING
-#endif
-
+#define RAMP_SIZE 150
// power channels:
-// - linear: 5A?
+// - boost: 8A?
// - DD FET: none (can't do DD on a boost driver)
-#define RAMP_LENGTH 150
-#define USE_DYN_PWM
+#if 0 // old, dynamic PWM method
// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255
// (plus a 0 at the beginning for moon)
#define PWM1_LEVELS 0,1,1,1,2,3,3,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,27,29,31,34,36,39,42,44,47,50,53,57,60,63,67,70,74,77,81,85,88,92,96,99,103,107,110,113,117,120,123,126,128,130,133,134,136,137,137,137,137,136,135,133,130,126,122,117,111,104,96,87,76,65,52,38,22,23,25,26,27,28,29,30,32,33,34,36,37,39,40,42,43,45,47,49,51,53,55,57,59,61,63,66,68,70,73,76,78,81,84,87,90,93,96,99,103,106,110,113,117,121,125,129,133,137,142,146,151,155,160,165,170,175,181,186,192,197,203,209,215,222,228,234,241,248,255
#define PWM_TOPS 16383,16383,12404,8140,11462,14700,11041,12947,13795,14111,14124,13946,13641,13248,12791,13418,12808,13057,12385,12428,12358,12209,12000,11746,11459,11147,11158,10793,10708,10576,10173,9998,9800,9585,9527,9278,9023,8901,8634,8486,8216,8053,7881,7615,7440,7261,7009,6832,6656,6422,6196,6031,5819,5615,5419,5190,4973,4803,4571,4386,4179,3955,3745,3549,3340,3145,2940,2729,2513,2312,2109,1903,1697,1491,1286,1070,871,662,459,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
-
-#define DEFAULT_LEVEL 70
#define MAX_1x7135 150
+#define DEFAULT_LEVEL 70
+#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range
#define HALFSPEED_LEVEL 12
#define QUARTERSPEED_LEVEL 4
-
-// don't blink halfway up
-#ifdef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_MIDDLE
#endif
-#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable
+// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits)
+// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640
+// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM)
+#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640
+#define MIN_THERM_STEPDOWN 50
+#define DEFAULT_LEVEL 70
+#define MAX_1x7135 150
+// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit
+// and speed changes make a big visible bump
+#define HALFSPEED_LEVEL 255
+#define QUARTERSPEED_LEVEL 255
+
+#define RAMP_SMOOTH_FLOOR 1 // low levels may be unreliable
#define RAMP_SMOOTH_CEIL 130
// 10, 30, 50, [70], 90, 110, 130
// Nichia B35 model: (0.56), 1.4, 8.4, 34.5, [102], 250, 500, 860, (1300) lm
@@ -48,18 +51,8 @@
#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
#define SIMPLE_UI_STEPS 5
-// make candle mode wobble more
-#define CANDLE_AMPLITUDE 33
-
// stop panicking at ~70% power or ~600 lm
#define THERM_FASTER_LEVEL 130
-#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range
-
-//#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly
-//#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting
-
-// slow down party strobe; this driver can't pulse for 2ms or less
-#define PARTY_STROBE_ONTIME 3
#define THERM_CAL_OFFSET 5
@@ -70,9 +63,29 @@
#define BLINK_BRIGHTNESS 50
#define BLINK_ONCE_TIME 12
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
+// slow down party strobe; this driver can't pulse for 2ms or less
+#define PARTY_STROBE_ONTIME 3
+
+// use aux red + aux blue for police strobe
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_STROBE_USES_AUX
+#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// the default of 26 looks a bit rough, so increase it to make it smoother
+#define CANDLE_AMPLITUDE 33
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
// added for convenience
#define USE_SOFT_FACTORY_RESET
-
-// work around bizarre bug: lockout mode fails when set to solid color blinking
-#define USE_K93_LOCKOUT_KLUDGE
diff --git a/spaghetti-monster/anduril/cfg-noctigon-dm11-nofet.h b/spaghetti-monster/anduril/cfg-noctigon-dm11-nofet.h
index 5a828d6..b2fdfdb 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-dm11-nofet.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-dm11-nofet.h
@@ -1,4 +1,10 @@
-// Noctigon DM11-noFET config options for Anduril
+// Noctigon DM11 (no DD FET) config options for Anduril
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// same support functions as a KR4
+#define HWDEF_C_FILE hwdef-noctigon-kr4-nofet.c
#include "cfg-noctigon-dm11.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0272"
@@ -7,6 +13,7 @@
// turn off the DD FET
#undef PWM_CHANNELS
#define PWM_CHANNELS 1
+#define RAMP_SIZE 150
// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255
#undef PWM1_LEVELS
diff --git a/spaghetti-monster/anduril/cfg-noctigon-dm11-sbt90.h b/spaghetti-monster/anduril/cfg-noctigon-dm11-sbt90.h
index 92c1ded..9fac446 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-dm11-sbt90.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-dm11-sbt90.h
@@ -1,7 +1,11 @@
// Noctigon DM11-SBT90.2 config options for Anduril
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "cfg-noctigon-kr4.h"
+#undef MODEL_NUMBER
#define MODEL_NUMBER "0274"
-#include "hwdef-Noctigon_DM11-SBT90.h"
-#include "hank-cfg.h"
// ATTINY: 1634
// this light has three aux LED channels: R, G, B
@@ -13,65 +17,30 @@
#undef USE_INDICATOR_LED_WHILE_RAMPING
#endif
-
// power channels:
// - linear: 5A?
// - FET: DD
-#define RAMP_LENGTH 150
-#define USE_DYN_PWM
-
-// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255
-// (plus one extra level at the beginning for moon)
-#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255
-#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#undef DEFAULT_LEVEL
#define DEFAULT_LEVEL 70
-#define MAX_1x7135 130
-#define HALFSPEED_LEVEL 12
-#define QUARTERSPEED_LEVEL 4
-
-// don't blink halfway up
-#ifdef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_MIDDLE
-#endif
+#undef RAMP_SMOOTH_FLOOR
#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable
-#define RAMP_SMOOTH_CEIL 130
// 10, 30, 50, [70], 90, 110, 130
+#undef RAMP_DISCRETE_FLOOR
#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
// safe limit ~75% power
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#undef SIMPLE_UI_CEIL
#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-#define SIMPLE_UI_STEPS 5
-
-// make candle mode wobble more
-#define CANDLE_AMPLITUDE 30
// stop panicking at ~70% power or ~600 lm
+#undef THERM_FASTER_LEVEL
#define THERM_FASTER_LEVEL 130
-#define MIN_THERM_STEPDOWN 66 // must be > end of dynamic PWM range
-
-//#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly
-//#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting
-
-// slow down party strobe; this driver can't pulse for 1ms or less
-// (only needed on no-FET build)
-//#define PARTY_STROBE_ONTIME 2
-#define THERM_CAL_OFFSET 5
-
-// the power regulator is a bit slow, so push it harder for a quick response from off
-#define DEFAULT_JUMP_START_LEVEL 21
+#undef BLINK_BRIGHTNESS
#define BLINK_BRIGHTNESS 50
-#define BLINK_ONCE_TIME 12
-
-// added for convenience
-#define USE_SOFT_FACTORY_RESET
+#undef CANDLE_AMPLITUDE
+#define CANDLE_AMPLITUDE 30
-// work around bizarre bug: lockout mode fails when set to solid color blinking
-#define USE_K93_LOCKOUT_KLUDGE
diff --git a/spaghetti-monster/anduril/cfg-noctigon-dm11.h b/spaghetti-monster/anduril/cfg-noctigon-dm11.h
index 4c746c9..cd6bc9d 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-dm11.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-dm11.h
@@ -1,6 +1,10 @@
// Noctigon DM11 config options for Anduril
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0271"
-#include "hwdef-Noctigon_DM11.h"
+#include "hwdef-noctigon-dm11.h"
#include "hank-cfg.h"
// ATTINY: 1634
@@ -13,29 +17,24 @@
#undef USE_INDICATOR_LED_WHILE_RAMPING
#endif
+#define RAMP_SIZE 150
// power channels:
// - linear: 5A?
// - FET: DD
-#define RAMP_LENGTH 150
-#define USE_DYN_PWM
// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255
// (plus one extra level at the beginning for moon)
-#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255
-#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255
+#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level
-#define DEFAULT_LEVEL 70
#define MAX_1x7135 130
+#define DEFAULT_LEVEL 70
#define HALFSPEED_LEVEL 12
#define QUARTERSPEED_LEVEL 4
-// don't blink halfway up
-#ifdef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_MIDDLE
-#endif
-
#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable
#define RAMP_SMOOTH_CEIL 130
// 10, 30, 50, [70], 90, 110, 130
@@ -48,30 +47,42 @@
#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
#define SIMPLE_UI_STEPS 5
-// make candle mode wobble more
-#define CANDLE_AMPLITUDE 30
-
// stop panicking at ~70% power or ~600 lm
#define THERM_FASTER_LEVEL 130
-#define MIN_THERM_STEPDOWN 66 // must be > end of dynamic PWM range
-
-//#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly
-//#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting
-
-// slow down party strobe; this driver can't pulse for 1ms or less
-// (only needed on no-FET build)
-//#define PARTY_STROBE_ONTIME 2
#define THERM_CAL_OFFSET 5
+
// the power regulator is a bit slow, so push it harder for a quick response from off
#define DEFAULT_JUMP_START_LEVEL 21
#define BLINK_BRIGHTNESS 50
#define BLINK_ONCE_TIME 12
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// there is usually no lighted button, so
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
+// slow down party strobe; this driver can't pulse for 1ms or less
+// (only needed on no-FET build)
+//#define PARTY_STROBE_ONTIME 2
+
+// use aux red + aux blue for police strobe
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_STROBE_USES_AUX
+#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// make candle mode wobble more
+#define CANDLE_AMPLITUDE 30
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
// added for convenience
#define USE_SOFT_FACTORY_RESET
-
-// work around bizarre bug: lockout mode fails when set to solid color blinking
-#define USE_K93_LOCKOUT_KLUDGE
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1-12v.h b/spaghetti-monster/anduril/cfg-noctigon-k1-boost.h
index 7f8f4cf..0684ce7 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-k1-12v.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-k1-boost.h
@@ -1,6 +1,10 @@
-// Noctigon K1 12V config options for Anduril
+// Noctigon K1 boost config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0253"
-#include "hwdef-Noctigon_K1-12V.h"
+#include "hwdef-noctigon-k1-boost.h"
#include "hank-cfg.h"
// ATTINY: 1634
@@ -14,31 +18,39 @@
// this light has three aux LED channels: R, G, B
#define USE_AUX_RGB_LEDS
-#define USE_AUX_RGB_LEDS_WHILE_ON
+#define USE_AUX_RGB_LEDS_WHILE_ON 25
#define USE_INDICATOR_LED_WHILE_RAMPING
-
+#if 0 // old, 10-bit PWM method
// level_calc.py cube 1 150 7135 0 4 1300
// (with max_pwm set to 1023)
// (level 0 is usable on this light)
#define RAMP_LENGTH 150
#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,5,5,6,7,8,9,10,11,12,13,14,15,16,17,19,20,22,23,25,26,28,30,31,33,35,37,39,42,44,46,48,51,53,56,59,61,64,67,70,73,76,80,83,86,90,94,97,101,105,109,113,117,122,126,130,135,140,144,149,154,159,165,170,175,181,187,193,198,204,211,217,223,230,236,243,250,257,264,271,279,286,294,302,310,318,326,334,343,351,360,369,378,387,397,406,416,426,436,446,456,466,477,488,499,510,521,532,544,555,567,579,591,604,616,629,642,655,668,682,695,709,723,737,751,766,780,795,810,825,841,856,872,888,904,921,937,954,971,988,1005,1023
#define MAX_1x7135 50
-
-// the entire ramp is regulated; don't blink halfway up
-#ifdef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_MIDDLE
-#endif
-
// don't slow down at low levels; this isn't that sort of light
// (it needs to stay at full speed for the 10-bit PWM to work)
#ifdef USE_DYNAMIC_UNDERCLOCKING
#undef USE_DYNAMIC_UNDERCLOCKING
#endif
+#endif
+
+#define RAMP_SIZE 150
+// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits)
+// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640
+// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM)
+#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640
+#define MIN_THERM_STEPDOWN 50
+#define DEFAULT_LEVEL 70
+#define MAX_1x7135 150
+// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit
+// and speed changes make a big visible bump
+#define HALFSPEED_LEVEL 255
+#define QUARTERSPEED_LEVEL 255
#define RAMP_SMOOTH_FLOOR 1
#define RAMP_SMOOTH_CEIL 130
-// 10, 30, [50], 70, 90, 110, 130
+// 10, 30, 50, [70], 90, 110, 130
#define RAMP_DISCRETE_FLOOR 10
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
@@ -48,17 +60,37 @@
#define SIMPLE_UI_CEIL 120
#define SIMPLE_UI_STEPS 5
-// make candle mode wobble more
-#define CANDLE_AMPLITUDE 32
-
// stop panicking at ~70% power or ~600 lm
#define THERM_FASTER_LEVEL 130
+#define THERM_CAL_OFFSET 5
+
#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly
#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
// slow down party strobe; this driver can't pulse for 1ms or less
#define PARTY_STROBE_ONTIME 4
-#define THERM_CAL_OFFSET 5
+// use aux red + aux blue for police strobe
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_STROBE_USES_AUX
+#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// make candle mode wobble more
+#define CANDLE_AMPLITUDE 33
+
+// the entire ramp is regulated; don't blink halfway up
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// added for convenience
+#define USE_SOFT_FACTORY_RESET
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h b/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h
index 9207dbc..568ff95 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h
@@ -1,7 +1,11 @@
// Noctigon K1-SBT90.2 config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// (is a K1 host with a KR4-like driver and a really high-powered LED)
#define MODEL_NUMBER "0252"
-#include "hwdef-Noctigon_K1-SBT90.h"
+#include "hwdef-noctigon-k1-sbt90.h"
#include "hank-cfg.h"
// ATTINY: 1634
@@ -11,7 +15,7 @@
// this light has three aux LED channels: R, G, B
#define USE_AUX_RGB_LEDS
-#define USE_AUX_RGB_LEDS_WHILE_ON
+#define USE_AUX_RGB_LEDS_WHILE_ON 10
#define USE_INDICATOR_LED_WHILE_RAMPING
@@ -20,36 +24,73 @@
// 1/1023: 2.56 lm
// max regulated: 1740 lm
// FET: ~3700 lm
+#define RAMP_SIZE 150
+// FIXME: it should probably have max_regulated at 120, not 130
+// ramp copied from noctigon-kr4
+// nice low lows, but might have visible ripple on some lights:
+// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255
+// (plus one extra level at the beginning for moon)
+#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255
+#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level
+
+#define MAX_1x7135 130
+#define DEFAULT_LEVEL 50
+#define HALFSPEED_LEVEL 12
+#define QUARTERSPEED_LEVEL 4
+
+#if 0 // original 10-bit ramp
// maxreg at 130: level_calc.py cube 2 150 7135 0 2.5 1740 FET 1 10 2565
// maxreg at 120: level_calc.py cube 2 150 7135 0 2.5 1740 FET 1 10 3190
-#define RAMP_LENGTH 150
#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,15,17,19,20,22,24,26,28,30,33,35,38,40,43,46,49,52,55,59,62,66,70,74,78,82,86,91,96,100,105,111,116,121,127,133,139,145,151,158,165,172,179,186,193,201,209,217,225,234,243,251,261,270,280,289,299,310,320,331,342,353,364,376,388,400,412,425,438,451,464,478,492,506,521,536,551,566,582,597,614,630,647,664,681,699,717,735,754,772,792,811,831,851,871,892,913,935,956,978,1001,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,0
#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,51,79,109,138,168,198,229,260,292,324,357,390,423,457,492,527,562,598,634,671,708,746,784,822,861,901,941,982,1023
#define DEFAULT_LEVEL 46
#define MAX_1x7135 120
#define HALFSPEED_LEVEL 10
#define QUARTERSPEED_LEVEL 2
+#endif
-#define RAMP_SMOOTH_FLOOR 3 // level 1 is unreliable
-#define RAMP_SMOOTH_CEIL 120
-// 10, 28, [46], 65, 83, 101, [120]
-#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_SMOOTH_FLOOR 11 // low levels may be unreliable
+#define RAMP_SMOOTH_CEIL 130
+// 11 30 [50] 70 90 110 [130]
+#define RAMP_DISCRETE_FLOOR 11
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
// safe limit ~33% power
#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL 110
+#define SIMPLE_UI_CEIL 120
#define SIMPLE_UI_STEPS 5
// stop panicking at ~40% power or ~1700 lm
-#define THERM_FASTER_LEVEL 120
-#define MIN_THERM_STEPDOWN DEFAULT_LEVEL
+#define THERM_FASTER_LEVEL 130
//#define THERM_NEXT_WARNING_THRESHOLD 16 // accumulate less error before adjusting
//#define THERM_RESPONSE_MAGNITUDE 128 // bigger adjustments
-// normal party strobe speed; this driver can pulse very quickly
+#define THERM_CAL_OFFSET 5
+
+// the power regulator is a bit slow, so push it harder for a quick response from off
+// (unsure if necessary, copied from noctigon-kr4)
+//#define DEFAULT_JUMP_START_LEVEL 21
+//#define BLINK_BRIGHTNESS DEFAULT_LEVEL
+//#define BLINK_ONCE_TIME 12
+
+// there is usually no lighted button, so
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
+// normal party strobe speed; this driver can pulse very quickly due to its DD FET
//#define PARTY_STROBE_ONTIME 2
-#define THERM_CAL_OFFSET 5
+// the default of 26 looks a bit rough, so increase it to make it smoother
+#define CANDLE_AMPLITUDE 33
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// for compatibility with other models
+#define USE_SOFT_FACTORY_RESET
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1.h b/spaghetti-monster/anduril/cfg-noctigon-k1.h
index fb2a89c..b2d4697 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-k1.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-k1.h
@@ -1,7 +1,11 @@
// Noctigon K1 config options for Anduril
+// Copyright (C) 2019-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0251"
// (originally known as Emisar D1S v2)
-#include "hwdef-Noctigon_K1.h"
+#include "hwdef-noctigon-k1.h"
#include "hank-cfg.h"
// ATTINY: 1634
@@ -11,22 +15,23 @@
// this light has three aux LED channels: R, G, B
#define USE_AUX_RGB_LEDS
-#define USE_AUX_RGB_LEDS_WHILE_ON
+#define USE_AUX_RGB_LEDS_WHILE_ON 5
#define USE_INDICATOR_LED_WHILE_RAMPING
+#define RAMP_SIZE 150
+
// ../../bin/level_calc.py cube 1 150 7135 1 4 1300
// (with max_pwm set to 1023)
// (level 0 flickers and isn't relevant on a thrower, so it's omitted)
#define RAMP_LENGTH 150
#define PWM1_LEVELS 1,1,2,2,3,3,4,4,5,6,6,7,8,9,10,11,12,13,14,15,16,17,18,20,21,23,24,26,27,29,31,32,34,36,38,40,43,45,47,49,52,54,57,60,62,65,68,71,74,77,81,84,87,91,95,98,102,106,110,114,118,122,127,131,136,141,145,150,155,160,166,171,176,182,188,193,199,205,211,218,224,231,237,244,251,258,265,272,280,287,295,303,310,319,327,335,344,352,361,370,379,388,397,407,416,426,436,446,457,467,477,488,499,510,521,533,544,556,568,580,592,604,617,629,642,655,668,682,695,709,723,737,751,766,781,795,810,826,841,857,872,888,904,921,937,954,971,988,1005,1023
-#define MAX_1x7135 50
-
-// the entire ramp is regulated; don't blink halfway up
-#ifdef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_MIDDLE
-#endif
+#define MAX_1x7135 50
+#define DEFAULT_LEVEL 50
+#define MIN_THERM_STEPDOWN 50 // should be above highest dyn_pwm level
+//#define HALFSPEED_LEVEL 12
+//#define QUARTERSPEED_LEVEL 4
// don't slow down at low levels; this isn't that sort of light
// (it needs to stay at full speed for the 10-bit PWM to work)
#ifdef USE_DYNAMIC_UNDERCLOCKING
@@ -45,17 +50,34 @@
#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
#define SIMPLE_UI_STEPS 5
-// make candle mode wobble more
-#define CANDLE_AMPLITUDE 32
-
// stop panicking at ~70% power or ~600 lm
#define THERM_FASTER_LEVEL 130
+#define THERM_CAL_OFFSET 5
+
#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly
#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting
+// the power regulator is a bit slow, so push it harder for a quick response from off
+#define DEFAULT_JUMP_START_LEVEL 10
+#define BLINK_BRIGHTNESS DEFAULT_LEVEL
+#define BLINK_ONCE_TIME 12
+
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// there is usually no lighted button,
+// so blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
// slow down party strobe; this driver can't pulse for 1ms or less
#define PARTY_STROBE_ONTIME 2
-#define THERM_CAL_OFFSET 5
+// make candle mode wobble more
+#define CANDLE_AMPLITUDE 32
+
+// don't blink while ramping; the entire ramp is regulated
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h b/spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h
index a88ad2c..88abf05 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-k9.3-219.h
@@ -1,18 +1,15 @@
// Noctigon K9.3 (reduced FET) config options for Anduril
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#include "cfg-noctigon-k9.3.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0263"
// ATTINY: 1634
// main LEDs
-#undef PWM1_LEVELS
#undef PWM2_LEVELS
-// don't turn off first channel at turbo level
-#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,15,17,19,20,22,24,26,28,30,33,35,38,40,43,46,49,52,55,59,62,66,70,74,78,82,86,91,96,100,105,111,116,121,127,133,139,145,151,158,165,172,179,186,193,201,209,217,225,234,243,251,261,270,280,289,299,310,320,331,342,353,364,376,388,400,412,425,438,451,464,478,492,506,521,536,551,566,582,597,614,630,647,664,681,699,717,735,754,772,792,811,831,851,871,892,913,935,956,978,1001,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023
// 65% FET power
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,15,34,52,71,90,110,129,149,169,190,211,233,254,275,298,320,343,366,389,413,437,461,485,510,535,560,586,612,639,665
-
-// 2nd LEDs (unchanged)
-//#undef PWM3_LEVELS
-//#define PWM3_LEVELS 0,0,1,1,2,2,3,3,4,4,5,5,6,7,8,9,10,11,12,13,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,55,58,61,63,66,69,72,75,79,82,85,89,92,96,100,104,108,112,116,120,125,129,134,138,143,148,153,158,163,169,174,180,185,191,197,203,209,215,222,228,235,242,248,255,263,270,277,285,292,300,308,316,324,333,341,350,359,368,377,386,395,405,414,424,434,444,454,465,475,486,497,508,519,531,542,554,566,578,590,603,615,628,641,654,667,680,694,708,722,736,750,765,779,794,809,825,840,856,872,888,904,920,937,954,971,988,1005,1023
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,7,11,15,19,23,27,32,37,41,46,51,56,61,66,72,78,83,89,95,102,108,115,121,128,135,143,150,157,165
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3-nofet.h b/spaghetti-monster/anduril/cfg-noctigon-k9.3-nofet.h
index e91ebc4..16504fd 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-k9.3-nofet.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-k9.3-nofet.h
@@ -1,54 +1,12 @@
// Noctigon K9.3 (noFET) config options for Anduril
-#include "cfg-noctigon-k9.3.h"
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// no functional differences from generic emisar-2ch build
+// (just use that one instead, this is only here for legacy reasons)
+#include "cfg-emisar-2ch.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0262"
// ATTINY: 1634
-// don't use the highest power channel
-#define K93_NO_FET
-
-// main LEDs
-#undef PWM1_LEVELS
-#undef PWM2_LEVELS
-#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,4,5,5,6,7,8,9,10,11,12,13,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,55,58,61,63,66,69,72,75,79,82,85,89,92,96,100,104,108,112,116,120,125,129,134,138,143,148,153,158,163,169,174,180,185,191,197,203,209,215,222,228,235,242,248,255,263,270,277,285,292,300,308,316,324,333,341,350,359,368,377,386,395,405,414,424,434,444,454,465,475,486,497,508,519,531,542,554,566,578,590,603,615,628,641,654,667,680,694,708,722,736,750,765,779,794,809,825,840,856,872,888,904,920,937,954,971,988,1005,1023
-// FET channel: unused, all zeroes
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-// 2nd LEDs
-#undef PWM3_LEVELS
-#define PWM3_LEVELS 0,0,1,1,2,2,3,3,4,4,5,5,6,7,8,9,10,11,12,13,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,55,58,61,63,66,69,72,75,79,82,85,89,92,96,100,104,108,112,116,120,125,129,134,138,143,148,153,158,163,169,174,180,185,191,197,203,209,215,222,228,235,242,248,255,263,270,277,285,292,300,308,316,324,333,341,350,359,368,377,386,395,405,414,424,434,444,454,465,475,486,497,508,519,531,542,554,566,578,590,603,615,628,641,654,667,680,694,708,722,736,750,765,779,794,809,825,840,856,872,888,904,920,937,954,971,988,1005,1023
-#undef DEFAULT_LEVEL
-#define DEFAULT_LEVEL 50
-#undef MAX_1x7135
-#undef MAX_Nx7135
-#define MAX_1x7135 150
-#define MAX_Nx7135 MAX_1x7135
-
-#undef RAMP_SMOOTH_FLOOR
-#undef RAMP_SMOOTH_CEIL
-#undef RAMP_DISCRETE_FLOOR
-#undef RAMP_DISCRETE_CEIL
-#undef RAMP_DISCRETE_STEPS
-
-#define RAMP_SMOOTH_FLOOR 3 // level 1 is unreliable(?)
-#define RAMP_SMOOTH_CEIL 130
-// 10, 30, [50], 70, 90, 110, 130 (plus [150] on turbo)
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
-
-// safe limit same as full UI
-#undef SIMPLE_UI_FLOOR
-#undef SIMPLE_UI_CEIL
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-
-
-// make candle mode wobble more
-#define CANDLE_AMPLITUDE 32
-
-// stop panicking at ~90% power or ~1600 lm
-#undef THERM_FASTER_LEVEL
-#define THERM_FASTER_LEVEL 143
-#undef MIN_THERM_STEPDOWN
-#define MIN_THERM_STEPDOWN DEFAULT_LEVEL
-
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-219.h b/spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-219.h
deleted file mode 100644
index 04efa83..0000000
--- a/spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-219.h
+++ /dev/null
@@ -1,10 +0,0 @@
-// Noctigon K9.3 tint-ramping (reduced FET) config options for Anduril
-#include "cfg-noctigon-k9.3-tintramp-fet.h"
-#undef MODEL_NUMBER
-#define MODEL_NUMBER "0267"
-// ATTINY: 1634
-
-// 85% FET power
-#undef PWM2_LEVELS
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,4,11,17,24,33,41,51,60,70,80,91,103,116,129,141,156,170,185,200,216
-
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-fet.h b/spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-fet.h
deleted file mode 100644
index 8535c57..0000000
--- a/spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-fet.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Noctigon K9.3 tint-ramping (plus FET) config options for Anduril
-#include "cfg-noctigon-k9.3-tintramp-nofet.h"
-#undef MODEL_NUMBER
-#define MODEL_NUMBER "0266"
-// ATTINY: 1634
-
-// enable the FET channel, even though it's ... kinda funky
-#undef PWM_CHANNELS
-#define PWM_CHANNELS 2
-
-// main LEDs
-// output: 3000 lm?
-// FET: 5000 to 7500 lm
-// 2nd LEDs
-// output: 1500 lm?
-#define RAMP_LENGTH 150
-// level_calc.py 5.01 1 130 7135 1 0.2 2000 --pwm dyn:64:16383:511
-// plus a FET segment
-// level_calc.py 2 1 20 7135 5 1000.0 7000 --pwm 255
-// abstract ramp (power is split between both sets of LEDs)
-// ','.join([str(511 - int(x*2.005)) for x in fet])
-#undef PWM1_LEVELS
-// append: ,501,485,469,453,433,413,391,369,345,321,295,267,237,207,177,143,108,74,38,0
-#define PWM1_LEVELS 1,1,1,2,2,3,3,4,5,6,7,8,9,10,12,13,15,16,18,20,22,24,27,29,32,34,37,40,43,46,49,53,56,59,63,66,70,73,77,80,83,87,90,93,96,98,100,103,104,105,106,107,106,105,104,101,98,94,89,83,76,67,57,46,33,35,37,39,41,43,46,48,51,53,56,59,62,65,68,72,75,79,83,86,91,95,99,104,108,113,118,123,129,135,140,146,153,159,166,172,179,187,194,202,210,218,227,236,245,254,264,274,284,295,306,317,328,340,352,365,378,391,405,419,433,448,463,479,495,511,501,485,469,453,433,413,391,369,345,321,295,267,237,207,177,143,108,74,38,0
-#undef PWM_TOPS
-// append: ,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
-#define PWM_TOPS 16383,13233,9780,13825,9592,13434,9971,12020,12899,13192,13149,12898,12507,12022,12665,11981,12180,11421,11392,11246,11017,10730,10825,10433,10364,9926,9766,9564,9331,9075,8805,8692,8394,8095,7927,7625,7438,7142,6947,6664,6392,6202,5945,5699,5464,5186,4925,4726,4450,4194,3956,3734,3462,3212,2982,2717,2475,2230,1985,1741,1500,1244,996,755,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
-// prepend: 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,13,21,29,39,49,60,71,83,95,108,122,137,152,167,184,201,218,236,255
-#undef DEFAULT_LEVEL
-#define DEFAULT_LEVEL 70
-#undef MAX_1x7135
-#define MAX_1x7135 130
-
-#undef RAMP_SMOOTH_FLOOR
-#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?)
-#undef RAMP_SMOOTH_CEIL
-#define RAMP_SMOOTH_CEIL 130
-// 10, 30, 50, [70], 90, 110, [130]
-#undef RAMP_DISCRETE_FLOOR
-#define RAMP_DISCRETE_FLOOR 10
-#undef RAMP_DISCRETE_CEIL
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#undef RAMP_DISCRETE_STEPS
-#define RAMP_DISCRETE_STEPS 7
-
-// safe limit highest regulated power (no FET or turbo)
-#undef SIMPLE_UI_FLOOR
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#undef SIMPLE_UI_CEIL
-#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-#undef SIMPLE_UI_STEPS
-#define SIMPLE_UI_STEPS 5
-
-// stop panicking at ~3000 lm
-#undef THERM_FASTER_LEVEL
-#define THERM_FASTER_LEVEL 130
-#undef MIN_THERM_STEPDOWN
-#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level
-
-// speed up party strobe; the FET is really fast
-#undef PARTY_STROBE_ONTIME
-
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-nofet.h b/spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-nofet.h
deleted file mode 100644
index 21ab415..0000000
--- a/spaghetti-monster/anduril/cfg-noctigon-k9.3-tintramp-nofet.h
+++ /dev/null
@@ -1,84 +0,0 @@
-// Noctigon K9.3 noFET tint-ramping config options for Anduril
-#define MODEL_NUMBER "0265"
-#include "hwdef-Emisar_D4Sv2-tintramp.h"
-#include "hank-cfg.h"
-// ATTINY: 1634
-
-// this light has three aux LED channels: R, G, B
-#define USE_AUX_RGB_LEDS
-// the aux LEDs are front-facing, so turn them off while main LEDs are on
-// it also has an independent LED in the button
-#define USE_BUTTON_LED
-// TODO: the whole "indicator LED" thing needs to be refactored into
-// "aux LED(s)" and "button LED(s)" since they work a bit differently
-// enabling this option breaks the button LED on D4v2.5
-#ifdef USE_INDICATOR_LED_WHILE_RAMPING
-#undef USE_INDICATOR_LED_WHILE_RAMPING
-#endif
-
-// has two channels of independent LEDs
-#define USE_TINT_RAMPING
-// how much to increase total brightness at middle tint
-// (0 = 100% brightness, 64 = 200% brightness)
-#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it
-
-// main LEDs
-// output: 3000 lm?
-// FET: disabled
-// 2nd LEDs
-// output: 1500 lm?
-#define RAMP_LENGTH 150
-// level_calc.py 5.01 1 150 7135 1 0.2 2000 --pwm dyn:74:16383:511
-// abstract ramp (power is split between both sets of LEDs)
-#define PWM1_LEVELS 1,1,1,2,2,2,3,4,4,5,6,6,7,8,9,10,12,13,14,16,17,19,20,22,24,26,28,30,32,35,37,40,42,45,47,50,53,56,59,62,65,68,71,74,77,80,83,86,89,91,94,96,98,100,102,104,105,106,107,107,107,106,105,103,101,98,94,90,84,78,71,63,54,44,33,35,37,38,40,42,44,46,48,50,53,55,57,60,63,65,68,71,74,77,80,83,87,90,94,98,102,106,110,114,118,123,128,132,137,142,148,153,159,164,170,176,183,189,196,202,209,216,224,231,239,247,255,263,272,281,290,299,309,318,328,339,349,360,371,382,394,406,418,430,443,456,469,483,497,511
-#define PWM_TOPS 16383,13673,10738,15435,11908,8123,12779,14756,12240,13447,14013,11907,12263,12351,12261,12048,12926,12464,11972,12278,11704,11789,11180,11134,11013,10837,10620,10371,10100,10113,9793,9718,9376,9248,8898,8738,8560,8369,8168,7961,7749,7535,7321,7107,6895,6686,6480,6278,6080,5823,5639,5403,5178,4965,4763,4570,4346,4134,3936,3714,3507,3283,3074,2853,2648,2433,2211,2006,1776,1564,1351,1137,924,714,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
-#define DEFAULT_LEVEL 70
-#define MAX_1x7135 150
-#define HALFSPEED_LEVEL 10
-#define QUARTERSPEED_LEVEL 2
-
-#define USE_MANUAL_MEMORY_TIMER_FOR_TINT
-//#define DEFAULT_MANUAL_MEMORY DEFAULT_LEVEL
-//#define DEFAULT_MANUAL_MEMORY_TIMER 10
-
-#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?)
-#define RAMP_SMOOTH_CEIL 130
-// 10, 30, 50, [70], 90, 110, [130]
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
-
-// safe limit highest regulated power (no FET or turbo)
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-#define SIMPLE_UI_STEPS 5
-
-// stop panicking at ~2000 lm
-#define THERM_FASTER_LEVEL 140
-#define MIN_THERM_STEPDOWN 75 // should be above highest dyn_pwm level
-
-// use the brightest setting for strobe
-#define STROBE_BRIGHTNESS MAX_LEVEL
-// slow down party strobe; this driver can't pulse for 1ms or less
-#define PARTY_STROBE_ONTIME 2
-
-// the default of 26 looks a bit flat, so increase it
-#define CANDLE_AMPLITUDE 40
-
-// the power regulator is a bit slow, so push it harder for a quick response from off
-#define DEFAULT_JUMP_START_LEVEL 21
-#define BLINK_BRIGHTNESS DEFAULT_LEVEL
-#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow
-
-#define THERM_CAL_OFFSET 9
-
-#ifdef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_MIDDLE
-#endif
-
-// for consistency with KR4 (not otherwise necessary though)
-#define USE_SOFT_FACTORY_RESET
-
-
-// work around bizarre bug: lockout mode fails when set to solid color blinking
-#define USE_K93_LOCKOUT_KLUDGE
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3.c b/spaghetti-monster/anduril/cfg-noctigon-k9.3.c
index d79c03f..d30d397 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-k9.3.c
+++ b/spaghetti-monster/anduril/cfg-noctigon-k9.3.c
@@ -1,4 +1,9 @@
#error This build is broken.
+// Noctigon K9.3 code overrides for Anduril
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
/*
* K9.3 has unusual power channels, so it must override some of FSM's code.
* There are two sets of LEDs:
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k9.3.h b/spaghetti-monster/anduril/cfg-noctigon-k9.3.h
index f3a6cdd..ba24504 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-k9.3.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-k9.3.h
@@ -1,19 +1,16 @@
// Noctigon K9.3 config options for Anduril
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0261"
-#include "hwdef-Noctigon_K9.3.h"
+#include "hwdef-emisar-2ch-fet.h"
#include "hank-cfg.h"
// ATTINY: 1634
-// this model requires some special code
-#define OVERRIDES_FILE cfg-noctigon-k9.3.c
-#define OVERRIDE_SET_LEVEL
-#define OVERRIDE_GRADUAL_TICK
-inline void set_level_override(uint8_t level);
-
// this light has three aux LED channels: R, G, B
#define USE_AUX_RGB_LEDS
// the aux LEDs are front-facing, so turn them off while main LEDs are on
-//#define USE_AUX_RGB_LEDS_WHILE_ON
// it also has an independent LED in the button
#define USE_BUTTON_LED
// TODO: the whole "indicator LED" thing needs to be refactored into
@@ -23,63 +20,92 @@ inline void set_level_override(uint8_t level);
#undef USE_INDICATOR_LED_WHILE_RAMPING
#endif
-// has two channels of independent LEDs
-#define USE_TINT_RAMPING
-// ... but it doesn't make sense to ramp between; only toggle
-#define TINT_RAMP_TOGGLE_ONLY
+// channel modes...
+// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO
+// enable max brightness out of the box
+#define DEFAULT_CHANNEL_MODE CM_BOTH
+
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_BOTH
+
+#define POLICE_COLOR_STROBE_CH1 CM_CH1
+#define POLICE_COLOR_STROBE_CH2 CM_CH2
+
+// how much to increase total brightness at middle tint
+// (0 = 100% brightness, 64 = 200% brightness)
+#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it
+
// main LEDs
-// max regulated: ~1750 lm
-// FET: ~8000 lm
+// max regulated: 1500 to 2000 lm?
+// FET: 5000 to 8000 lm?
// 2nd LEDs
// max regulated: ~1500 lm
-// maxreg at 120: level_calc.py cube 2 150 7135 0 2.5 1740 FET 1 10 3190
-#define RAMP_LENGTH 150
-// main LEDs
-#define PWM1_LEVELS 0,0,1,1,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,15,17,19,20,22,24,26,28,30,33,35,38,40,43,46,49,52,55,59,62,66,70,74,78,82,86,91,96,100,105,111,116,121,127,133,139,145,151,158,165,172,179,186,193,201,209,217,225,234,243,251,261,270,280,289,299,310,320,331,342,353,364,376,388,400,412,425,438,451,464,478,492,506,521,536,551,566,582,597,614,630,647,664,681,699,717,735,754,772,792,811,831,851,871,892,913,935,956,978,1001,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,1023,0
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,22,51,79,109,138,168,198,229,260,292,324,357,390,423,457,492,527,562,598,634,671,708,746,784,822,861,901,941,982,1023
-// 2nd LEDs
-#define PWM3_LEVELS 0,0,1,1,2,2,3,3,4,4,5,5,6,7,8,9,10,11,12,13,15,16,17,18,20,21,23,24,26,27,29,31,33,35,37,39,41,43,45,48,50,53,55,58,61,63,66,69,72,75,79,82,85,89,92,96,100,104,108,112,116,120,125,129,134,138,143,148,153,158,163,169,174,180,185,191,197,203,209,215,222,228,235,242,248,255,263,270,277,285,292,300,308,316,324,333,341,350,359,368,377,386,395,405,414,424,434,444,454,465,475,486,497,508,519,531,542,554,566,578,590,603,615,628,641,654,667,680,694,708,722,736,750,765,779,794,809,825,840,856,872,888,904,920,937,954,971,988,1005,1023
-#define DEFAULT_LEVEL 46
-#define MAX_1x7135 120
-#define MAX_Nx7135 MAX_1x7135
-#define HALFSPEED_LEVEL 10
-#define QUARTERSPEED_LEVEL 2
-
-#define USE_MANUAL_MEMORY_TIMER_FOR_TINT
-//#define DEFAULT_MANUAL_MEMORY DEFAULT_LEVEL
-//#define DEFAULT_MANUAL_MEMORY_TIMER 10
-
-#define RAMP_SMOOTH_FLOOR 3 // level 1 is unreliable (?)
-#define RAMP_SMOOTH_CEIL 120
-// 10, 28, [46], 65, 83, 101, [120]
+#define RAMP_SIZE 150
+
+// linear+FET ramp: maxreg at 120/150
+// level_calc.py 5.01 2 150 7135 1 0.1 1872 FET 1 10 5000 --pwm dyn:63:4096:255:3 --clock 8:16:0
+// linear segment
+#define PWM1_LEVELS 1,1,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,8,8,8,8,9,9,10,10,10,11,12,12,13,14,14,16,17,18,19,20,21,22,24,25,26,28,29,31,33,35,36,38,40,42,45,47,49,52,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,108,112,117,122,127,133,138,144,150,156,162,169,175,182,189,197,204,212,220,229,237,246,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+// DD FET segment
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,11,17,24,30,36,43,50,57,64,71,79,87,95,103,111,120,129,138,147,157,167,177,187,198,209,220,231,243,255
+// PWM TOPS values
+#define PWM3_LEVELS 4096,3798,3504,3214,2926,2645,2368,2098,1834,1579,1333,2304,2017,1744,1486,1242,1174,1700,1505,1323,1155,1037,1299,1163,1038,923,819,967,874,789,712,641,577,655,598,545,497,453,413,456,419,386,355,326,352,326,301,278,296,275,287,267,249,258,265,248,253,256,240,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+
+// linear-only ramp
+// level_calc.py 3.11 1 150 7135 1 0.1 1750 --pwm dyn:64:4096:255:3 --clock 8:16:0
+#define PWM4_LEVELS 1,1,1,1,1,1,2,2,2,3,3,3,4,4,4,5,5,6,6,6,7,7,7,8,8,8,9,9,9,10,10,10,10,10,10,11,11,11,11,11,11,11,12,12,12,12,12,12,13,13,13,14,14,14,15,15,16,17,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,34,35,37,38,39,41,42,44,45,47,48,50,52,53,55,57,59,61,63,65,67,69,71,73,75,77,79,82,84,86,89,91,94,97,99,102,105,107,110,113,116,119,122,125,128,132,135,138,141,145,148,152,156,159,163,167,170,174,178,182,186,190,195,199,203,208,212,217,221,226,230,235,240,245,250,255
+// PWM_TOPS values for linear-only ramp
+#define PWM5_LEVELS 4096,3681,3247,2794,2328,1856,2937,2393,1860,2690,2273,1875,2281,1959,1658,1893,1646,1774,1569,1381,1466,1309,1166,1224,1104,996,1033,942,858,882,810,746,687,634,586,604,561,522,487,454,425,397,409,385,362,341,321,302,311,295,279,286,271,257,263,250,255,258,246,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+
+#define DEFAULT_LEVEL 65
+#define MAX_1x7135 120
+#define MAX_Nx7135 MAX_1x7135
+#define HALFSPEED_LEVEL 16
+#define QUARTERSPEED_LEVEL 8
+
+#define RAMP_SMOOTH_FLOOR 10 // level 1 is unreliable (?)
+#define RAMP_SMOOTH_CEIL 120
+// 10, 28, 46, [65], 83, 101, 120
#define RAMP_DISCRETE_FLOOR 10
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
-// safe limit highest regulated power (no FET or turbo)
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-#define SIMPLE_UI_STEPS 5
+// 10, 37, [65], 92, 120
+#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
+#define SIMPLE_UI_STEPS 5
// stop panicking at ~25% power or ~1000 lm
#define THERM_FASTER_LEVEL 100
-#define MIN_THERM_STEPDOWN DEFAULT_LEVEL
-//#define THERM_NEXT_WARNING_THRESHOLD 16 // accumulate less error before adjusting
-//#define THERM_RESPONSE_MAGNITUDE 128 // bigger adjustments
+#define MIN_THERM_STEPDOWN 60 // should be above highest dyn_pwm level
+
+#define USE_POLICE_COLOR_STROBE_MODE
+#undef TACTICAL_LEVELS
+#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe
// use the brightest setting for strobe
#define STROBE_BRIGHTNESS MAX_LEVEL
// slow down party strobe; this driver can't pulse for 1ms or less
#define PARTY_STROBE_ONTIME 2
+// TODO: change speed per channel mode
+// (the FET is really fast, but the regulator is not)
+//#undef PARTY_STROBE_ONTIME
+
+// the default of 26 looks a bit flat, so increase it
+#define CANDLE_AMPLITUDE 33
+// the power regulator is a bit slow, so push it harder for a quick response from off
+#define DEFAULT_JUMP_START_LEVEL 35
+#define BLINK_BRIGHTNESS 30
#define BLINK_ONCE_TIME 12 // longer blink, since main LEDs are slow
#define THERM_CAL_OFFSET 5
-// for consistency with KR4 (not otherwise necessary though)
-#define USE_SOFT_FACTORY_RESET
-
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
-// work around bizarre bug: lockout mode fails when set to solid color blinking
-#define USE_K93_LOCKOUT_KLUDGE
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-12v.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-12v.h
deleted file mode 100644
index a513b3f..0000000
--- a/spaghetti-monster/anduril/cfg-noctigon-kr4-12v.h
+++ /dev/null
@@ -1,70 +0,0 @@
-// Noctigon KR4 (12V) config options for Anduril
-// (and Noctigon KR1)
-#define MODEL_NUMBER "0216"
-#include "hwdef-Noctigon_KR4-12V.h"
-// ATTINY: 1634
-
-// this light has three aux LED channels: R, G, B
-#define USE_AUX_RGB_LEDS
-// ... and a single LED in the button (D4v2.5 only)
-#define USE_BUTTON_LED
-// don't use aux LEDs while main LED is on
-#ifdef USE_INDICATOR_LED_WHILE_RAMPING
-#undef USE_INDICATOR_LED_WHILE_RAMPING
-#endif
-
-
-// power channels:
-// - linear: 5A?
-// - DD FET: none (can't do DD on a boost driver)
-#define RAMP_LENGTH 150
-#define USE_DYN_PWM
-
-// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255
-// (plus a 0 at the beginning for moon)
-#define PWM1_LEVELS 0,1,1,1,2,3,3,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,27,29,31,34,36,39,42,44,47,50,53,57,60,63,67,70,74,77,81,85,88,92,96,99,103,107,110,113,117,120,123,126,128,130,133,134,136,137,137,137,137,136,135,133,130,126,122,117,111,104,96,87,76,65,52,38,22,23,25,26,27,28,29,30,32,33,34,36,37,39,40,42,43,45,47,49,51,53,55,57,59,61,63,66,68,70,73,76,78,81,84,87,90,93,96,99,103,106,110,113,117,121,125,129,133,137,142,146,151,155,160,165,170,175,181,186,192,197,203,209,215,222,228,234,241,248,255
-#define PWM_TOPS 16383,16383,12404,8140,11462,14700,11041,12947,13795,14111,14124,13946,13641,13248,12791,13418,12808,13057,12385,12428,12358,12209,12000,11746,11459,11147,11158,10793,10708,10576,10173,9998,9800,9585,9527,9278,9023,8901,8634,8486,8216,8053,7881,7615,7440,7261,7009,6832,6656,6422,6196,6031,5819,5615,5419,5190,4973,4803,4571,4386,4179,3955,3745,3549,3340,3145,2940,2729,2513,2312,2109,1903,1697,1491,1286,1070,871,662,459,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
-
-#define DEFAULT_LEVEL 70
-#define MAX_1x7135 150
-#define HALFSPEED_LEVEL 12
-#define QUARTERSPEED_LEVEL 4
-
-// don't blink halfway up
-#ifdef BLINK_AT_RAMP_MIDDLE
-#undef BLINK_AT_RAMP_MIDDLE
-#endif
-
-#define RAMP_SMOOTH_FLOOR 10 // low levels may be unreliable
-#define RAMP_SMOOTH_CEIL 130
-// 10, 30, 50, [70], 90, 110, 130
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
-
-// safe limit ~75% power
-#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
-#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-#define SIMPLE_UI_STEPS 5
-
-// make candle mode wobble more
-#define CANDLE_AMPLITUDE 33
-
-// stop panicking at ~70% power or ~600(?) lm
-#define THERM_FASTER_LEVEL 130
-#define MIN_THERM_STEPDOWN 80 // must be > end of dynamic PWM range
-
-// slow down party strobe; this driver can't pulse for 2ms or less
-#define PARTY_STROBE_ONTIME 3
-
-#define THERM_CAL_OFFSET 5
-
-// the power regulator seems to "jump start" the LEDs all on its own,
-// so the firmware doesn't have to
-// (and unfortunately the power regulator jumps it a bit too hard)
-#define DEFAULT_JUMP_START_LEVEL 1
-#define BLINK_BRIGHTNESS 50
-#define BLINK_ONCE_TIME 12
-
-// can't reset the normal way because power is connected before the button
-#define USE_SOFT_FACTORY_RESET
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h
index 28fc595..5d106ef 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h
@@ -1,4 +1,8 @@
// Noctigon KR4 (reduced FET) config options for Anduril
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#include "cfg-noctigon-kr4.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0213"
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h
index 39ac57c..b242048 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-kr4-219b.h
@@ -1,4 +1,8 @@
// Noctigon KR4 (reduced FET) config options for Anduril
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#include "cfg-noctigon-kr4.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0214"
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-2ch.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-2ch.h
new file mode 100644
index 0000000..09dd604
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-noctigon-kr4-2ch.h
@@ -0,0 +1,16 @@
+// Noctigon KR4 2-channel config options for Anduril
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// (basically the same as Emisar generic 2-channel build,
+// but switch on a different pin, and no lighted button)
+// ATTINY: 1634
+#include "hwdef-noctigon-kr4-2ch.h"
+#include "cfg-emisar-2ch.h"
+#undef MODEL_NUMBER
+#define MODEL_NUMBER "0215"
+
+// the button doesn't light up
+#undef USE_BUTTON_LED
+
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-boost.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-boost.h
new file mode 100644
index 0000000..1603acf
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-noctigon-kr4-boost.h
@@ -0,0 +1,16 @@
+// Noctigon KR4 (12V) config options for Anduril
+// (and Noctigon KR1)
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// the only things different from dm11-boost are:
+// - e-switch is on a different pin (defined in hwdef)
+// - different model number
+#include "cfg-noctigon-dm11-boost.h"
+#include "hwdef-noctigon-kr4-boost.h"
+#undef MODEL_NUMBER
+#define MODEL_NUMBER "0216"
+#include "hank-cfg.h"
+// ATTINY: 1634
+
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h
index e4879ef..6d49a8b 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h
@@ -1,6 +1,11 @@
-// Noctigon KR4 (fetless) config options for Anduril
+// Noctigon KR4 (no DD FET) config options for Anduril
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// (and Noctigon KR1)
// (and Emisar D4v2 E21A, a.k.a. "D4v2.5")
+#define HWDEF_C_FILE hwdef-noctigon-kr4-nofet.c
#include "cfg-noctigon-kr4.h"
#undef MODEL_NUMBER
#define MODEL_NUMBER "0212"
@@ -12,7 +17,7 @@
// max regulated: 1740 lm
#undef PWM_CHANNELS
#define PWM_CHANNELS 1
-#define RAMP_LENGTH 150
+#define RAMP_SIZE 150
// prioritize low lows, at risk of visible ripple
// level_calc.py 5.01 1 149 7135 1 0.3 1740 --pwm dyn:78:16384:255
#undef PWM1_LEVELS
@@ -45,9 +50,6 @@
#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
-// make candle mode wobble more
-#define CANDLE_AMPLITUDE 32
-
// slow down party strobe; this driver can't pulse for 1ms or less
// (only needed on no-FET build)
#define PARTY_STROBE_ONTIME 2
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-tintramp.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-tintramp.h
deleted file mode 100644
index f19744d..0000000
--- a/spaghetti-monster/anduril/cfg-noctigon-kr4-tintramp.h
+++ /dev/null
@@ -1,12 +0,0 @@
-// Noctigon KR4 tint-ramping config options for Anduril
-// (basically the same as Emisar D4S V2 tint-ramping,
-// but switch on a different pin, and no lighted button)
-// ATTINY: 1634
-#include "hwdef-Noctigon_KR4-tintramp.h"
-#include "cfg-emisar-d4sv2-tintramp.h"
-#undef MODEL_NUMBER
-#define MODEL_NUMBER "0215"
-
-// the button doesn't light up
-#undef USE_BUTTON_LED
-
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4.h b/spaghetti-monster/anduril/cfg-noctigon-kr4.h
index 8071457..5b24ef5 100644
--- a/spaghetti-monster/anduril/cfg-noctigon-kr4.h
+++ b/spaghetti-monster/anduril/cfg-noctigon-kr4.h
@@ -1,43 +1,36 @@
// Noctigon KR4 config options for Anduril
// (and Emisar D4v2.5, which uses KR4 driver plus a button LED)
+// Copyright (C) 2020-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0211"
-#include "hwdef-Noctigon_KR4.h"
+#include "hwdef-noctigon-kr4.h"
#include "hank-cfg.h"
// ATTINY: 1634
-// this light has three aux LED channels: R, G, B
-#define USE_AUX_RGB_LEDS
-// the aux LEDs are front-facing, so turn them off while main LEDs are on
-//#define USE_AUX_RGB_LEDS_WHILE_ON
-// it also has an independent LED in the button (D4v2.5 titanium/brass only)
-#define USE_BUTTON_LED
-// TODO: the whole "indicator LED" thing needs to be refactored into
-// "aux LED(s)" and "button LED(s)" since they work a bit differently
-// enabling this option breaks the button LED on D4v2.5
-#ifdef USE_INDICATOR_LED_WHILE_RAMPING
-#undef USE_INDICATOR_LED_WHILE_RAMPING
-#endif
-
-
// brightness w/ SST-20 4000K LEDs:
// 0/1023: 0.35 lm
// 1/1023: 2.56 lm
// max regulated: 1740 lm
// FET: ~3700 lm
-#define RAMP_LENGTH 150
-#define USE_DYN_PWM
+#define RAMP_SIZE 150
+
// nice low lows, but might have visible ripple on some lights:
// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:16384:255
// (plus one extra level at the beginning for moon)
-#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255
-#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,7,8,9,11,12,14,16,17,19,22,24,26,29,31,34,37,40,43,46,49,53,56,60,63,67,71,74,78,82,86,89,93,96,99,103,105,108,110,112,114,115,116,116,115,114,112,109,106,101,95,89,81,71,60,48,34,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255
+#define PWM_TOPS 16383,16383,11750,14690,9183,12439,13615,13955,13877,13560,13093,12529,13291,12513,12756,12769,11893,11747,12085,11725,11329,11316,10851,10713,10518,10282,10016,9729,9428,9298,8971,8794,8459,8257,8043,7715,7497,7275,7052,6753,6538,6260,5994,5798,5501,5271,5006,4758,4525,4268,4030,3775,3508,3263,3010,2752,2517,2256,1998,1763,1512,1249,994,749,497,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define MIN_THERM_STEPDOWN 66 // should be above highest dyn_pwm level
+
// less ripple, but lows are a bit higher than ideal:
// maxreg at 130, dynamic PWM: level_calc.py 5.01 2 149 7135 1 0.3 1740 FET 1 10 3190 --pwm dyn:64:4096:255
// (plus one extra level at the beginning for moon)
//#define PWM1_LEVELS 0,1,1,1,1,1,1,2,2,2,2,3,3,3,4,4,5,5,6,6,7,8,8,9,10,11,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,32,33,33,34,34,34,34,34,34,33,32,31,30,28,26,24,21,19,20,21,22,23,24,26,27,28,30,31,32,34,36,37,39,41,43,45,47,49,51,53,56,58,61,63,66,69,72,75,78,81,84,88,91,95,99,103,107,111,115,119,124,129,133,138,143,149,154,159,165,171,177,183,189,196,203,210,217,224,231,239,247,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
//#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,20,30,41,52,63,75,87,99,112,125,138,151,165,179,194,208,224,239,255
//#define PWM_TOPS 4095,4095,3760,3403,3020,2611,2176,3582,3062,2515,1940,3221,2761,2283,2998,2584,3004,2631,2899,2555,2735,2836,2538,2606,2636,2638,2387,2382,2361,2328,2286,2238,2185,2129,2070,2010,1949,1887,1826,1766,1706,1648,1591,1536,1482,1429,1379,1329,1242,1199,1122,1084,1016,953,895,842,791,723,659,602,549,482,422,367,302,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+
#define MAX_1x7135 130
#define DEFAULT_LEVEL 50
#define HALFSPEED_LEVEL 12
@@ -57,21 +50,40 @@
// stop panicking at ~1300 lm
#define THERM_FASTER_LEVEL 120
-#define MIN_THERM_STEPDOWN 66 // must be > end of dynamic PWM range
-// no longer needed, after switching to dynamic PWM
-//#define THERM_NEXT_WARNING_THRESHOLD 16 // accumulate less error before adjusting
-//#define THERM_RESPONSE_MAGNITUDE 128 // bigger adjustments
-
-// slow down party strobe; this driver can't pulse for 1ms or less
-// (only needed on no-FET build)
-//#define PARTY_STROBE_ONTIME 2
#define THERM_CAL_OFFSET 5
+
// the power regulator is a bit slow, so push it harder for a quick response from off
#define DEFAULT_JUMP_START_LEVEL 21
#define BLINK_BRIGHTNESS DEFAULT_LEVEL
#define BLINK_ONCE_TIME 12
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// there is usually no lighted button, so
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
+// slow down party strobe; this driver can't pulse for 1ms or less
+// (only needed on no-FET build)
+//#define PARTY_STROBE_ONTIME 2
+
+// use aux red + aux blue for police strobe
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_STROBE_USES_AUX
+#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// the default of 26 looks a bit rough, so increase it to make it smoother
+#define CANDLE_AMPLITUDE 33
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
// can't reset the normal way because power is connected before the button
#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-noctigon-m44.h b/spaghetti-monster/anduril/cfg-noctigon-m44.h
new file mode 100644
index 0000000..88bf628
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-noctigon-m44.h
@@ -0,0 +1,134 @@
+// Noctigon M44 config options for Anduril
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define MODEL_NUMBER "0143"
+#include "hwdef-noctigon-m44.h"
+#include "hank-cfg.h"
+// ATTINY: 1634
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+// the aux LEDs are front-facing, so turn them off while main LEDs are on
+// it also has an independent LED in the button
+#define USE_BUTTON_LED
+// TODO: the whole "indicator LED" thing needs to be refactored into
+// "aux LED(s)" and "button LED(s)" since they work a bit differently
+// enabling this option breaks the button LED on D4v2.5
+#ifdef USE_INDICATOR_LED_WHILE_RAMPING
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+#endif
+
+// channel modes...
+// CM_CH1, CM_CH2, CM_BOTH, CM_BLEND, CM_AUTO
+// enable max brightness out of the box
+#define DEFAULT_CHANNEL_MODE CM_BOTH
+
+//#define FACTORY_RESET_WARN_CHANNEL CM_CH2
+//#define FACTORY_RESET_SUCCESS_CHANNEL CM_BOTH
+
+#define USE_CONFIG_COLORS
+//#define CONFIG_WAITING_CHANNEL CM_CH2
+//#define CONFIG_BLINK_CHANNEL CM_BOTH
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_BOTH
+
+#define POLICE_COLOR_STROBE_CH1 CM_CH1
+#define POLICE_COLOR_STROBE_CH2 CM_CH2
+// aux red + aux blue are the correct colors, but are dim
+//#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+//#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// how much to increase total brightness at middle tint
+// (0 = 100% brightness, 64 = 200% brightness)
+#define TINT_RAMPING_CORRECTION 0 // none, linear regulator doesn't need it
+
+// channel 1
+// output: unknown, 6000 lm?
+// channel 2
+// output: unknown, 6000 lm?
+#define RAMP_SIZE 150
+#if 0 // optimized hand-tweaked ramp from before PWM+DSM update
+// "100% power" ramp
+// level_calc.py 5.01 1 150 7135 0 2.0 5000 --pwm dyn:64:16384:511:5
+// (with manual tweaks)
+#define PWM1_LEVELS 0,1,1,2,2,3,4,5,6,6,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,9,9,9,10,10,11,11,12,12,13,14,15,16,17,18,19,20,21,22,23,24,26,27,28,29,31,32,34,35,37,39,40,42,44,46,48,50,52,54,57,59,62,64,67,69,72,75,78,81,84,87,91,94,98,101,105,109,113,117,121,126,130,135,140,145,150,155,160,165,171,177,183,189,195,201,208,214,221,228,236,243,251,258,266,274,283,291,300,309,318,328,337,347,357,368,378,389,400,411,423,435,447,459,472,484,498,511
+#define PWM_TOPS 16383,16383,10002,11695,8083,9374,9793,7993,8291,7017,7180,6235,5431,5556,4927,4385,3916,3511,3159,2852,2585,2349,2142,1958,1795,1648,1517,1398,1290,1193,1104,1023,950,883,822,765,714,667,623,582,632,610,588,566,544,512,535,505,523,494,509,480,492,502,509,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
+// max "200% power" ramp and tops
+// 1-130: 0 to 100% power
+// level_calc.py 5.01 1 130 7135 2 0.2 2000 --pwm dyn:64:16383:511
+// 131 to 150: 101% to 200% power
+// level_calc.py 6.44 1 150 7135 1 0.2 2000 --pwm dyn:74:16383:1022
+//#define PWM2_LEVELS 2,2,2,3,3,4,4,5,6,7,8,9,10,11,13,14,16,17,19,21,23,25,28,30,33,35,38,41,44,47,50,54,57,60,64,67,71,74,78,81,84,88,91,94,97,99,101,103,105,106,107,107,107,106,105,102,99,95,90,84,77,68,58,47,34,36,38,40,42,44,47,49,52,54,57,60,63,66,69,73,76,80,83,87,91,96,100,104,109,114,119,124,130,135,141,147,153,160,166,173,180,187,195,203,211,219,228,236,245,255,264,274,285,295,306,317,329,340,353,365,378,391,405,419,433,448,463,479,495,511,530,550,570,591,612,634,657,680,705,730,755,782,809,837,865,895,925,957,989,1022
+//#define PWM3_LEVELS 16383,13234,9781,13826,9593,13434,9973,12021,12900,13193,13150,12899,12508,12023,12666,11982,12181,11422,11393,11247,11018,10731,10826,10434,10365,9927,9767,9565,9332,9076,8806,8693,8395,8096,7928,7626,7439,7143,6948,6665,6393,6203,5946,5700,5465,5187,4926,4681,4451,4195,3957,3700,3463,3213,2983,2718,2476,2231,1986,1742,1501,1245,997,756,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511,511
+#define MIN_THERM_STEPDOWN 65 // should be above highest dyn_pwm level
+#define DEFAULT_LEVEL 70
+#define MAX_1x7135 150
+#define HALFSPEED_LEVEL 10
+#define QUARTERSPEED_LEVEL 2
+#endif // end old ramp config
+
+// delta-sigma modulated PWM (0b0HHHHHHHHLLLLLLL = 0, 8xHigh, 7xLow bits)
+// level_calc.py 5.01 1 150 7135 0 0.2 2000 --pwm 32640
+// (max is (255 << 7), because it's 8-bit PWM plus 7 bits of DSM)
+#define PWM1_LEVELS 0,1,2,3,4,5,6,7,9,10,12,14,17,19,22,25,28,32,36,41,45,50,56,62,69,76,84,92,101,110,121,132,143,156,169,184,199,215,232,251,270,291,313,336,360,386,414,442,473,505,539,574,612,651,693,736,782,829,880,932,987,1045,1105,1168,1233,1302,1374,1449,1527,1608,1693,1781,1873,1969,2068,2172,2279,2391,2507,2628,2753,2883,3018,3158,3303,3454,3609,3771,3938,4111,4289,4475,4666,4864,5068,5280,5498,5724,5957,6197,6445,6701,6965,7237,7518,7808,8106,8413,8730,9056,9392,9737,10093,10459,10835,11223,11621,12031,12452,12884,13329,13786,14255,14737,15232,15741,16262,16798,17347,17911,18489,19082,19691,20314,20954,21609,22281,22969,23674,24397,25137,25895,26671,27465,28279,29111,29963,30835,31727,32640
+
+#define MIN_THERM_STEPDOWN 50
+#define DEFAULT_LEVEL 70
+#define MAX_1x7135 150
+// always run at 1/4th speed, because 4 kHz PWM is enough for this circuit
+// and speed changes make a big visible bump
+#define HALFSPEED_LEVEL 255
+#define QUARTERSPEED_LEVEL 255
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 130
+// 10, 30, 50, [70], 90, 110, 130
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
+
+// 10 40 [70] 100 130
+#define SIMPLE_UI_FLOOR RAMP_DISCRETE_FLOOR
+#define SIMPLE_UI_CEIL RAMP_DISCRETE_CEIL
+#define SIMPLE_UI_STEPS 5
+
+// stop panicking at ~???? lm
+#define THERM_FASTER_LEVEL 130
+
+#define USE_POLICE_COLOR_STROBE_MODE
+#undef TACTICAL_LEVELS
+#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe
+
+// use the brightest setting for strobe
+#define STROBE_BRIGHTNESS MAX_LEVEL
+// slow down party strobe; this driver takes a while to start making any light
+#define PARTY_STROBE_ONTIME 12
+//#define STROBE_OFF_LEVEL 1 // nope, this makes strobe blurry
+// bike strobe needs a longer pulse too? nope
+#define BIKE_STROBE_ONTIME 8
+
+// the default of 26 looks a bit flat, so increase it
+#define CANDLE_AMPLITUDE 33
+
+// party strobe, tac strobe, police, lightning, candle, bike
+#define DEFAULT_STROBE_CHANNELS CM_BOTH,CM_BOTH,CM_BOTH,CM_AUTO,CM_AUTO,CM_AUTO
+
+// the power regulator is a bit slow, so push it harder for a quick response from off
+//#define DEFAULT_JUMP_START_LEVEL 50
+//#define JUMP_START_TIME 50
+#define BLINK_BRIGHTNESS 30
+#define BLINK_ONCE_TIME 16 // longer blink, since the boost driver is slow
+
+#define THERM_CAL_OFFSET 5
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// for consistency with KR4 (not otherwise necessary though)
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h b/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h
new file mode 100644
index 0000000..440a03d
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-sofirn-lt1s-pro.h
@@ -0,0 +1,125 @@
+// Sofirn LT1S Pro config file for Anduril
+// Copyright (C) 2022-2023 (FIXME)
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define MODEL_NUMBER "0623"
+#include "hwdef-sofirn-lt1s-pro.h"
+// ATTINY: 1616
+
+// off mode: low (1)
+// lockout: blinking (3)
+// Standby power usage:
+// - aux high: 6.9 mA (30 days)
+// - aux low: 0.16 mA (3.5 years)
+// - red moon: 2.17 mA (96 days)
+// - white moon: 1.47 mA (141 days)
+// Low mode isn't bright enough to be useful on this light,
+// but at least it doesn't drain the battery 3X faster than moon mode.
+// (it seriously would be more practical to just use moon instead)
+#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+
+// channel modes...
+// CM_WHITE, CM_AUTO2, CM_AUTO3, CM_RED, CM_WHITE_RED
+#define DEFAULT_CHANNEL_MODE CM_AUTO3
+
+#define FACTORY_RESET_WARN_CHANNEL CM_RED
+#define FACTORY_RESET_SUCCESS_CHANNEL CM_WHITE
+
+#define CONFIG_WAITING_CHANNEL CM_RED
+#define CONFIG_BLINK_CHANNEL CM_WHITE
+
+// blink numbers on the main LEDs by default (but allow user to change it)
+#define DEFAULT_BLINK_CHANNEL CM_RED
+
+// how much to increase total brightness at middle tint
+// (0 = 100% brightness, 64 = 200% brightness)
+// seems unnecessary on this light
+#define TINT_RAMPING_CORRECTION 0
+
+#define RAMP_SIZE 150
+// use dynamic PWM instead of plain 8-bit
+// (so we can get lower lows and a smoother ramp)
+// (also, red LEDs use a QX7138 chip which has max PWM speed of 10 kHz,
+// and it behaves erratically at full speed,
+// so PWM here is 576 clock cycles long to keep the speed low enough)
+//
+// This first ramp seems a bit too low: 0.2 / 1.9 / 10 / 37 / 109 / 272 / 600 lm
+// level_calc.py 5.99 1 150 7135 1 0.2 600 --pwm dyn:77:16383:575:3
+//#define PWM_LEVELS 1,1,2,2,3,4,4,5,6,6,7,8,9,9,10,11,11,12,13,13,14,15,15,16,16,17,18,18,19,19,19,20,20,21,21,21,21,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,22,23,23,23,23,24,24,24,25,26,26,27,28,29,30,32,33,34,36,38,40,42,44,46,48,50,52,55,57,59,62,65,68,70,73,77,80,83,86,90,94,97,101,105,110,114,118,123,128,133,138,143,148,154,160,166,172,178,185,191,198,205,213,220,228,236,244,252,261,270,279,289,298,308,319,329,340,351,363,374,386,399,411,424,438,452,466,480,495,510,526,542,558,575
+//#define PWM_TOPS 16383,10869,13246,8043,11458,12772,10093,11043,11450,9664,9991,10091,10048,8868,8838,8730,7814,7724,7589,6864,6748,6604,6024,5899,5398,5287,5159,4754,4638,4287,3963,3876,3594,3511,3265,3038,2829,2770,2586,2417,2260,2115,1981,1857,1742,1636,1537,1445,1360,1281,1207,1138,1073,1013,957,904,855,848,803,760,720,714,677,643,637,630,599,592,585,577,569,579,570,560,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575
+//
+// This ramp is a bit higher: 0.2 / 3 / 19 / 61 / 152 / 320 / 600 lm
+// level_calc.py 4.001 1 150 7135 1 0.2 600 --pwm dyn:78:16383:575:3.333
+#define PWM1_LEVELS 1,1,2,4,5,6,7,8,9,10,12,13,14,15,17,18,19,21,22,23,24,25,26,27,28,29,30,31,32,33,33,34,34,35,35,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,36,37,37,38,38,39,40,40,41,43,44,45,47,48,50,52,54,56,59,62,64,67,69,72,75,78,81,84,87,90,94,97,101,104,108,112,116,120,124,128,133,137,142,147,151,156,161,167,172,177,183,189,194,200,206,213,219,226,232,239,246,253,260,268,275,283,291,299,307,316,324,333,342,351,361,370,380,390,400,410,420,431,442,453,464,476,487,499,511,523,536,549,562,575
+#define PWM_TOPS 16383,8174,7823,14429,13603,12806,12046,11328,10652,10017,10402,9742,9134,8575,8615,8089,7605,7536,7093,6684,6307,5959,5636,5337,5060,4802,4562,4337,4127,3929,3633,3468,3216,3077,2862,2744,2559,2390,2234,2091,1960,1838,1727,1623,1528,1439,1357,1280,1209,1143,1081,1024,970,919,872,828,787,770,732,716,682,668,654,624,611,613,600,587,587,574,573,571,569,566,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575,575
+// TODO? 200% power at top of ramp on white blend mode
+// 2nd table handles "200% power" turbo
+//#define PWM2_LEVELS ...
+// tops for PWM2
+//#define PWM3_LEVELS ...
+#define MAX_1x7135 75
+#define MIN_THERM_STEPDOWN 75 // should be above highest dyn_pwm level
+#define HALFSPEED_LEVEL 12
+#define QUARTERSPEED_LEVEL 5
+
+// the default of 26 looks a bit flat, so increase it
+#define CANDLE_AMPLITUDE 40
+
+// override default ramp style
+#undef RAMP_STYLE
+#define RAMP_STYLE 1 // 0 = smooth, 1 = stepped
+// set floor and ceiling as far apart as possible
+// because this lantern isn't overpowered
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 150
+//#define RAMP_DISCRETE_FLOOR 17 // 17 50 83 116 150
+#define RAMP_DISCRETE_FLOOR 1 // 1 25 50 75 100 125 150
+#define RAMP_DISCRETE_CEIL 150
+#define RAMP_DISCRETE_STEPS 7
+
+// LT1S can handle heat well, so don't limit simple mode
+//#define SIMPLE_UI_FLOOR 10 // 10 45 80 115 150
+#define SIMPLE_UI_FLOOR 1
+#define SIMPLE_UI_CEIL 150
+#define SIMPLE_UI_STEPS 7
+
+// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping)
+#define USE_SIMPLE_UI_RAMPING_TOGGLE
+
+// allow Aux Config and Strobe Modes in Simple UI
+#define USE_EXTENDED_SIMPLE_UI
+
+// turn on at med-low brightness by default (level 50/150, or ramp step 3/7)
+// (also sets lockout mode 2H to a useful level)
+#define DEFAULT_MANUAL_MEMORY 50
+// reset to default after being off for 10 minutes
+#define DEFAULT_MANUAL_MEMORY_TIMER 10
+
+// enable 2 click turbo (Anduril 1 style)
+#define DEFAULT_2C_STYLE 1
+
+#define USE_SOS_MODE
+#define USE_SOS_MODE_IN_BLINKY_GROUP
+
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_COLOR_STROBE_CH1 CM_RED
+#define POLICE_COLOR_STROBE_CH2 CM_WHITE
+
+#undef TACTICAL_LEVELS
+#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3) // high, low, police strobe
+
+// don't blink while ramping
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+#ifdef BLINK_AT_RAMP_FLOOR
+#undef BLINK_AT_RAMP_FLOOR
+#endif
+#ifdef BLINK_AT_RAMP_CEIL
+#undef BLINK_AT_RAMP_CEIL
+#endif
+// without this, it's really hard to tell when ramping up stops
+#define BLINK_AT_RAMP_CEIL
+
+#define USE_SOFT_FACTORY_RESET
diff --git a/spaghetti-monster/anduril/cfg-sofirn-sc21-pro.h b/spaghetti-monster/anduril/cfg-sofirn-sc21-pro.h
new file mode 100644
index 0000000..8fd2dee
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-sofirn-sc21-pro.h
@@ -0,0 +1,14 @@
+// Sofirn SC21 Pro - same setup as a Wurkkos TS10, but with the aux indicator on while ramping
+// Copyright (C) 2022-2023 (FIXME)
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#include "cfg-wurkkos-ts10.h"
+#undef MODEL_NUMBER
+#define MODEL_NUMBER "0632"
+// ATTINY: 1616
+
+// turn on the aux LED while main LED is on
+#ifndef USE_INDICATOR_LED_WHILE_RAMPING
+#define USE_INDICATOR_LED_WHILE_RAMPING
+#endif
diff --git a/spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h b/spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h
index bcfc80e..0e2f28d 100644
--- a/spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h
+++ b/spaghetti-monster/anduril/cfg-sofirn-sp10-pro.h
@@ -1,41 +1,40 @@
// Sofirn SP10 Pro config options for Anduril
+// Copyright (C) 2022-2023 (original author TBD), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
#define MODEL_NUMBER "0631"
-#include "hwdef-Sofirn_SP10-Pro.h"
+#include "hwdef-sofirn-sp10-pro.h"
// ATTINY: 1616
-#undef BLINK_AT_RAMP_MIDDLE
-
-#define USE_DYNAMIC_UNDERCLOCKING
-
// 1....15: level_calc.py 3.01 1 15 7135 1 0.1 2 --pwm dyn:15:64:64
// 16..150: level_calc.py 5.01 1 135 7135 1 2 800 --pwm dyn:49:3072:255:3.0
-#define RAMP_LENGTH 150
-#define USE_DYN_PWM
-#define _PWM1_LEVELS_ 1,2,4,6,9,12,15,19,23,28,34,41,48,55,64
-#define _PWM1_TOPS_ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64
-#define _PWM2_LEVELS_ 1,1,2,2,3,3,4,4,5,5,6,6,6,7,7,7,7,7,7,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,9,9,9,10,10,10,11,11,12,13,13,14,15,15,16,17,18,18,19,20,21,22,23,24,25,26,27,28,29,30,31,33,34,35,37,38,39,41,42,44,46,47,49,51,53,55,57,59,61,63,65,67,70,72,74,77,79,82,85,88,90,93,96,99,103,106,109,113,116,120,123,127,131,135,139,143,147,151,156,160,165,170,175,180,185,190,195,201,206,212,218,223,230,236,242,248,255
-#define _PWM2_TOPS_ 3072,1960,2372,1476,2097,1572,1920,1570,1777,1524,1646,1454,1286,1369,1234,1115,1011,918,837,894,823,759,702,650,603,560,522,487,455,425,398,374,351,330,310,292,275,259,280,265,251,266,253,240,252,240,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
-#define PWM1_LEVELS _PWM1_LEVELS_,_PWM2_TOPS_
-#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,_PWM2_LEVELS_
-#define PWM_TOPS _PWM1_TOPS_,_PWM2_TOPS_
-
-#define MAX_1x7135 15
-#define HALFSPEED_LEVEL 15
+#define RAMP_SIZE 150
+#define _PWM1_LEVELS_ 1, 2, 4, 6, 9,12,15,19,23,28,34,41,48,55,64
+#define _PWM1_TOPS_ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64
+#define _PWM2_LEVELS_ 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 10, 10, 10, 11, 11, 12, 13, 13, 14, 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 33, 34, 35, 37, 38, 39, 41, 42, 44, 46, 47, 49, 51, 53, 55, 57, 59, 61, 63, 65, 67, 70, 72, 74, 77, 79, 82, 85, 88, 90, 93, 96, 99,103,106,109,113,116,120,123,127,131,135,139,143,147,151,156,160,165,170,175,180,185,190,195,201,206,212,218,223,230,236,242,248,255
+#define _PWM2_TOPS_ 3072,1960,2372,1476,2097,1572,1920,1570,1777,1524,1646,1454,1286,1369,1234,1115,1011,918,837,894,823,759,702,650,603,560,522,487,455,425,398,374,351,330,310,292,275,259,280,265,251,266,253,240,252,240,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define PWM1_LEVELS _PWM1_LEVELS_,_PWM2_TOPS_
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,_PWM2_LEVELS_
+#define PWM_TOPS _PWM1_TOPS_,_PWM2_TOPS_
+
+#define MAX_1x7135 15
+#define HALFSPEED_LEVEL 15
#define QUARTERSPEED_LEVEL 15
-#define DEFAULT_LEVEL 75
+#define DEFAULT_LEVEL 50
-#define RAMP_SMOOTH_FLOOR 1
-#define RAMP_SMOOTH_CEIL 150
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 150
// 1 25 50 [75] 100 125 150
-#define RAMP_DISCRETE_FLOOR 1
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
+#define RAMP_DISCRETE_FLOOR 1
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
// at Sofirn's request, use max (150) for the Simple UI ceiling
// 15 48 [82] 116 150
-#define SIMPLE_UI_FLOOR MAX_1x7135
-#define SIMPLE_UI_CEIL 150
-#define SIMPLE_UI_STEPS 5
+#define SIMPLE_UI_FLOOR MAX_1x7135
+#define SIMPLE_UI_CEIL 150
+#define SIMPLE_UI_STEPS 5
// turn on at ~6 lm by default (level 50/150, or ramp step 2/5 or 3/7)
// (also sets lockout mode 2H to a useful level)
@@ -68,6 +67,15 @@
// enable 2 click turbo (replaces USE_2C_MAX_TURBO)
#define DEFAULT_2C_STYLE 1
+// don't blink during the ramp or at the ceiling
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+#ifdef BLINK_AT_RAMP_CEIL
+#undef BLINK_AT_RAMP_CEIL
+#endif
+
+
// enable factory reset on 13H without loosening tailcap
#define USE_SOFT_FACTORY_RESET
diff --git a/spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h b/spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h
index ce1c04a..cb29e4f 100644
--- a/spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h
+++ b/spaghetti-monster/anduril/cfg-sofirn-sp36-t1616.h
@@ -1,4 +1,8 @@
// Sofirn SP36 (small Q8) config options for Anduril using the Attiny1616
+// Copyright (C) 2021-2023 (FIXME)
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// same as the BLF Q8, mostly
#include "cfg-blf-q8-t1616.h"
#undef MODEL_NUMBER
diff --git a/spaghetti-monster/anduril/cfg-sofirn-sp36.h b/spaghetti-monster/anduril/cfg-sofirn-sp36.h
index af8c18b..3661686 100644
--- a/spaghetti-monster/anduril/cfg-sofirn-sp36.h
+++ b/spaghetti-monster/anduril/cfg-sofirn-sp36.h
@@ -1,4 +1,8 @@
// Sofirn SP36 (small Q8) config options for Anduril
+// Copyright (C) 2018-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
// same as the BLF Q8, mostly
#include "cfg-blf-q8.h"
#undef MODEL_NUMBER
diff --git a/spaghetti-monster/anduril/cfg-thefreeman-boost-fwaa.h b/spaghetti-monster/anduril/cfg-thefreeman-boost-fwaa.h
new file mode 100755
index 0000000..460346e
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-thefreeman-boost-fwaa.h
@@ -0,0 +1,116 @@
+// thefreeman's BCK-FWAA-MP3432 (li-ion / AA)
+// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define MODEL_NUMBER "1632"
+#include "hwdef-thefreeman-boost-fwaa-mp3432-hdr-dac-rgb.h"
+// ATTINY: 1616
+
+// HPRsense : 4.2+0.3+20 = 24.5mR (DMN1004UFDF+trace resistance+20mR)
+// R1=165k Vsense=49.02 Iout=2001mA
+// LPRsense : 2R
+// transition DAC lvl 14, ramp lvl 51
+// fifth power ramp 0.06mA to 2001mA
+
+#define RAMP_SIZE 150
+
+// 4 ramp segments:
+// - low 0.55V
+// - low 2.5V
+// - high 0.55V
+// - high 2.5V
+// PWM1: DAC Data
+#define PWM1_LEVELS 2, 3, 4, 5, 6, 8, 9, 11, 14, 16, 19, 23, 26, 31, 35, 41, 47, 54, 61, 69, 78, 89,100,112,125,140,155,173,191,212,234, \
+ 56, 62, 68, 74, 82, 89, 97,106,115,125,136,147,159,172,186,200,215,232,249, \
+ 14, 15, 17, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 38, 40, 43, 45, 48, 51, 54, 57, 60, 63, 66, 70, 74, 77, 81, 86, 90, 95, 99,104,109,114,120,126,131,138,144,150,157,164,171,179,187,195,203,212,221,230,239,249, \
+ 57, 59, 61, 64, 66, 69, 72, 74, 77, 80, 83, 86, 90, 93, 96,100,103,107,111,115,119,123,127,132,136,141,145,150,155,160,166,171,176,182,188,194,200,206,213,219,226,233,240,247,255
+// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20)
+#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18
+
+#define MAX_1x7135 50
+#define DEFAULT_LEVEL 44
+#define HDR_ENABLE_LEVEL_MIN 51 // when HDR FET turns ON
+
+// no PWM, so MCU clock speed can be slow
+#define HALFSPEED_LEVEL 46
+#define QUARTERSPEED_LEVEL 45 // seems to run fine at 10kHz/4, try reducing more
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 130 // ~50% power, ~??? mA / ??? lm
+#define RAMP_DISCRETE_FLOOR 1
+#define RAMP_DISCRETE_CEIL 130
+#define RAMP_DISCRETE_STEPS 7
+
+// 20 [45] 70 95 120
+#define SIMPLE_UI_FLOOR 20
+#define SIMPLE_UI_CEIL 120 // ~37% power, ~??? mA / ??? lm
+#define SIMPLE_UI_STEPS 5
+
+// don't blink mid-ramp
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// thermal config
+
+// temperature limit
+#define THERM_FASTER_LEVEL 130
+#define MIN_THERM_STEPDOWN MAX_1x7135
+
+//#define THERM_LOOKAHEAD 2 // 4 by default -> decrease for longer turbo
+//#define THERM_NEXT_WARNING_THRESHOLD 48 // 24 by default -> increase for fewer adjustments (more stable output on large time scale)
+//#define THERM_RESPONSE_MAGNITUDE 16 // 64 by default -> decrease for smaller adjustments (removes dip post turbo)
+//#define THERM_WINDOW_SIZE 1 // 2 by default -> decrease for tighter temperature regulation
+
+
+// UI
+
+//#define SIMPLE_UI_ACTIVE 0 // advanced UI by default
+
+// allow Aux Config and Strobe Modes in Simple UI
+#define USE_EXTENDED_SIMPLE_UI
+
+// Allow 3C in Simple UI for switching between smooth and stepped ramping
+#define USE_SIMPLE_UI_RAMPING_TOGGLE
+
+#define DEFAULT_2C_STYLE 1 // enable 2 click turbo
+
+
+// AUX
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
+// use aux red + aux blue for police strobe
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_STROBE_USES_AUX
+#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// the aux LEDs are front-facing, so turn them off while main LEDs are on
+#ifdef USE_INDICATOR_LED_WHILE_RAMPING
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+#endif
+
+
+// Misc
+
+#define PARTY_STROBE_ONTIME 1 // slow down party strobe
+#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses
+
+// smoother candle mode with bigger oscillations
+#define CANDLE_AMPLITUDE 40
+
+// enable 13H factory reset so it can be used on tail e-switch lights
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-thefreeman-boost21-6a.h b/spaghetti-monster/anduril/cfg-thefreeman-boost21-6a.h
new file mode 100644
index 0000000..66cc2d0
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-thefreeman-boost21-6a.h
@@ -0,0 +1,119 @@
+// thefreeman's BST21 BST20-FWxA (no button LED)
+// Copyright (C) 2023 TBD (thefreeman), Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define MODEL_NUMBER "1631"
+#include "hwdef-thefreeman-boost21-mp3431-hdr-dac-argb.h"
+// ATTINY: 1616
+
+// HPRsense : 1.7+0.3+5 = 7mR (DMN22M5UFG+trace resistance+5mR)
+// Vsense=42.46mV, R1= 191k
+// LPRsense : 1R
+// transition DAC level 8, ramp level 45
+// fifth power ramp 0.1mA to 6066mA
+
+#define RAMP_SIZE 150
+
+// 4 ramp segments:
+// - low 0.55V
+// - low 2.5V
+// - high 0.55V
+// - high 2.5V
+// PWM1: DAC Data
+#define PWM1_LEVELS 2, 3, 4, 5, 7, 9, 11, 13, 16, 19, 23, 28, 33, 39, 45, 53, 61, 71, 81, 93,106,121,137,155,175,196,220,246, \
+ 60, 67, 74, 82, 91,100,110,121,133,146,159,174,190,207,224,244, \
+ 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 20, 21, 23, 24, 26, 27, 29, 31, 33, 35, 37, 40, 42, 45, 47, 50, 53, 56, 59, 62, 66, 69, 73, 77, 81, 85, 90, 94, 99,104,109,114,120,126,132,138,144,151,158,165,173,180,188,196,205,214,223,232,242,252, \
+ 57, 60, 62, 65, 67, 70, 73, 76, 78, 82, 85, 88, 91, 95, 98,102,105,109,113,117,121,126,130,135,139,144,149,154,159,164,170,175,181,187,193,199,206,212,219,225,232,240,247,255
+// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20)
+#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18
+
+#define MAX_1x7135 44
+#define DEFAULT_LEVEL 44
+#define HDR_ENABLE_LEVEL_MIN 45 // when HDR FET turns ON
+
+// no PWM, so MCU clock speed can be slow
+#define HALFSPEED_LEVEL 41
+#define QUARTERSPEED_LEVEL 40 // seems to run fine at 10kHz/4, try reducing more
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 130 // 50% / 3A / 1000 lm
+// 1 22 [44] 65 87 108 130
+#define RAMP_DISCRETE_FLOOR 1
+#define RAMP_DISCRETE_CEIL 130
+#define RAMP_DISCRETE_STEPS 7
+
+// 20 [45] 70 95 120
+#define SIMPLE_UI_FLOOR 20
+#define SIMPLE_UI_CEIL 120 // ~2.25A / ~750 lm
+#define SIMPLE_UI_STEPS 5
+
+// don't blink mid-ramp
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// thermal config
+
+// temperature limit
+#define THERM_FASTER_LEVEL 130 // stop panicking at 50%/3A
+#define MIN_THERM_STEPDOWN MAX_1x7135
+
+//#define THERM_LOOKAHEAD 4 // 4 by default -> decrease for longer turbo
+#define THERM_NEXT_WARNING_THRESHOLD 48 // 24 by default -> increase for fewer adjustments (more stable output on large time scale)
+#define THERM_RESPONSE_MAGNITUDE 32 // 64 by default -> decrease for smaller adjustments (removes dip post turbo)
+//#define THERM_WINDOW_SIZE 1 // 2 by default -> decrease for tighter temperature regulation
+
+
+// UI
+
+//#define SIMPLE_UI_ACTIVE 0 // advanced UI by default
+
+// allow Aux Config and Strobe Modes in Simple UI
+#define USE_EXTENDED_SIMPLE_UI
+
+// Allow 3C in Simple UI for switching between smooth and stepped ramping
+#define USE_SIMPLE_UI_RAMPING_TOGGLE
+
+#define DEFAULT_2C_STYLE 1 // enable 2 click turbo
+
+
+// AUX
+
+//#define USE_BUTTON_LED
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+
+// use aux red + aux blue for police strobe
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_STROBE_USES_AUX
+#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// the aux LEDs are front-facing, so turn them off while main LEDs are on
+#ifdef USE_INDICATOR_LED_WHILE_RAMPING
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+#endif
+
+
+// Misc
+
+#define PARTY_STROBE_ONTIME 1 // slow down party strobe
+#define STROBE_OFF_LEVEL 1 // keep the regulator chip on between pulses
+
+// smoother candle mode with bigger oscillations
+#define CANDLE_AMPLITUDE 40
+
+// enable 13H factory reset so it can be used on tail e-switch lights
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-thefreeman-lin16dac.h b/spaghetti-monster/anduril/cfg-thefreeman-lin16dac.h
index 64dcd8c..20d2bf5 100644
--- a/spaghetti-monster/anduril/cfg-thefreeman-lin16dac.h
+++ b/spaghetti-monster/anduril/cfg-thefreeman-lin16dac.h
@@ -1,5 +1,9 @@
// thefreeman's Linear 16 driver using DAC control
-#define MODEL_NUMBER "0000" // TBD
+// Copyright (C) 2021-2023 (FIXME)
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define MODEL_NUMBER "1630"
#include "hwdef-thefreeman-lin16dac.h"
// ATTINY: 1616
@@ -11,35 +15,47 @@
// lockout: blinking (3)
#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
-#undef BLINK_AT_RAMP_MIDDLE
-
-// We're abusing the Dynamic PWM functionality to set the VREF instead of PWM TOP.
-// We don't want the Gradual functionality to mess with the PWM_TOP value.
-#ifdef USE_SET_LEVEL_GRADUALLY
-#undef USE_SET_LEVEL_GRADUALLY
-#endif
-
// level_calc.py ninth 2 150 7135 1 0.03 6.4 7135 1 6.3 1600
-#define RAMP_LENGTH 150
-#define USE_DYN_PWM
-
-// PWM1: DAC Data, PWM Tops: VREF selector
-#define PWM1_LEVELS 25,25,33,41,41,50,58,66,75,83,92,108,117,133,150,167,192,209,234,58,64,71,80,90,99,110,121,134,149,163,180,198,218,241,1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,5,5,6,6,7,7,8,8,9,10,11,11,12,13,14,15,16,18,19,20,22,23,25,26,28,30,32,34,36,39,41,44,47,50,53,56,59,63,67,71,75,79,84,89,94,100,105,112,118,124,131,139,146,154,163,172,181,191,201,212,223,234,246,57,60,63,66,69,73,76,80,84,88,93,97,102,107,112,117,123,129,135,141,147,154,161,169,176,184,193,201,210,220,229,239,250,255
-#define PWM_TOPS 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18,18
-
-#define MAX_1x7135 34
-#define HALFSPEED_LEVEL 14
-#define QUARTERSPEED_LEVEL 6
-
-#define RAMP_SMOOTH_FLOOR 1
-#define RAMP_SMOOTH_CEIL 120
+#define RAMP_SIZE 150
+
+// 4 ramp segments:
+// - low 0.55V
+// - low 2.5V
+// - high 0.55V
+// - high 2.5V
+// PWM1: DAC Data
+// FIXME: ramp stalls with 8 duplicate levels in a row
+// (maybe use 1.1V Vref during that part of the ramp?)
+#define PWM1_LEVELS 25, 25, 33, 41, 41, 50, 58, 66, 75, 83, 92,108,117,133,150,167,192,209,234, \
+ 58, 64, 71, 80, 90, 99,110,121,134,149,163,180,198,218,241, \
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 10, 11, 11, 12, 13, 14, 15, 16, 18, 19, 20, 22, 23, 25, 26, 28, 30, 32, 34, 36, 39, 41, 44, 47, 50, 53, 56, 59, 63, 67, 71, 75, 79, 84, 89, 94,100,105,112,118,124,131,139,146,154,163,172,181,191,201,212,223,234,246, \
+ 57, 60, 63, 66, 69, 73, 76, 80, 84, 88, 93, 97,102,107,112,117,123,129,135,141,147,154,161,169,176,184,193,201,210,220,229,239,250,255
+// PWM Tops: VREF selector (0.55V=16,1.1V=17, 2.5V=18, 4.3V=19, 1.5V=20)
+#define PWM_TOPS 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, \
+ 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, \
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18
+
+#define MAX_1x7135 34
+#define HDR_ENABLE_LEVEL_MIN 35 // bottom level of top half of the ramp
+#define HALFSPEED_LEVEL 255 // always run at 1/4th speed
+#define QUARTERSPEED_LEVEL 255 // because DAC doesn't use PWM
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 120
// 10, 28, 46, [65], 83, 101, 120
-#define RAMP_DISCRETE_FLOOR 10
-#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
-#define RAMP_DISCRETE_STEPS 7
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
// stop panicking at ~30% power
#define THERM_FASTER_LEVEL 123
// enable 2 click turbo
#define DEFAULT_2C_STYLE 1
+
+// don't blink mid-ramp
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
diff --git a/spaghetti-monster/anduril/cfg-wurkkos-fc13.h b/spaghetti-monster/anduril/cfg-wurkkos-fc13.h
new file mode 100644
index 0000000..56161b4
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-wurkkos-fc13.h
@@ -0,0 +1,17 @@
+// Wurkkos FC13, like a TS25 but with a RGB button and no front aux
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// ATTINY: 1616
+#include "cfg-wurkkos-ts25.h"
+#undef MODEL_NUMBER
+#define MODEL_NUMBER "0716"
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+
+// turn on the aux LEDs while main LEDs are on
+#define USE_AUX_RGB_LEDS_WHILE_ON 20
+#define USE_INDICATOR_LED_WHILE_RAMPING
+
diff --git a/spaghetti-monster/anduril/cfg-wurkkos-ts10-rgbaux.h b/spaghetti-monster/anduril/cfg-wurkkos-ts10-rgbaux.h
new file mode 100644
index 0000000..6770c47
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-wurkkos-ts10-rgbaux.h
@@ -0,0 +1,85 @@
+// Wurkkos TS10 (RGB aux version) config options for Anduril
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// most things are the same as TS25
+#include "cfg-wurkkos-ts25.h"
+#undef MODEL_NUMBER
+#define MODEL_NUMBER "0713"
+// ATTINY: 1616
+
+///// apply some config from the TS10 /////
+
+// use the TS10 ramp, not the TS25 ramp
+#undef PWM1_LEVELS
+#undef PWM2_LEVELS
+#undef PWM_TOPS
+#undef MAX_1x7135
+#undef MIN_THERM_STEPDOWN
+#undef HALFSPEED_LEVEL
+#undef QUARTERSPEED_LEVEL
+#undef DEFAULT_LEVEL
+#undef RAMP_SMOOTH_FLOOR
+#undef RAMP_SMOOTH_CEIL
+#undef RAMP_DISCRETE_FLOOR
+#undef RAMP_DISCRETE_CEIL
+#undef RAMP_DISCRETE_STEPS
+#undef SIMPLE_UI_FLOOR
+#undef SIMPLE_UI_CEIL
+#undef SIMPLE_UI_STEPS
+#undef THERM_FASTER_LEVEL
+
+// 7135 at 90/150
+// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0
+// (with heavy manual tweaks up to ~15/150)
+#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255
+#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define MAX_1x7135 90
+#define MIN_THERM_STEPDOWN 60
+#define HALFSPEED_LEVEL 11
+#define QUARTERSPEED_LEVEL 5
+#define DEFAULT_LEVEL 50
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 130
+// 20 38 56 [75] 93 111 130
+// 10 30 50 70 [90] 110 130
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL 130
+#define RAMP_DISCRETE_STEPS 7
+
+// at Wurkkos's request, reduce the Simple UI ceiling a little bit
+// (i.e. not 150; original config had it at 144/150, or DD FET 204/255)
+// 20 47 [75] 102 130
+// 10 30 50 70 [90] 110 130
+#define SIMPLE_UI_FLOOR 10
+#define SIMPLE_UI_CEIL 130
+#define SIMPLE_UI_STEPS 7
+
+// stop panicking at ~50% power
+#define THERM_FASTER_LEVEL 130 // throttle back faster when high
+
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the aux LEDs by default
+#undef DEFAULT_BLINK_CHANNEL
+#define DEFAULT_BLINK_CHANNEL CM_AUXWHT
+
+// the aux LEDs are pretty bright; set the high-mode threshold a bit higher
+// (default is 15)
+#define POST_OFF_VOLTAGE_BRIGHTNESS 25
+
+// the default of 26 looks a bit rough, so increase it to make it smoother
+#define CANDLE_AMPLITUDE 33
+
+// don't blink mid-ramp
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// enable factory reset on 13H without loosening tailcap (required)
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-wurkkos-ts10.h b/spaghetti-monster/anduril/cfg-wurkkos-ts10.h
new file mode 100644
index 0000000..90839e7
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-wurkkos-ts10.h
@@ -0,0 +1,85 @@
+// Wurkkos TS10 (originally used Sofirn SP36-t1616 firmware) config options for Anduril using the Attiny1616
+// Copyright (C) 2022-2023 (FIXME)
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// same as the BLF Q8 T1616, mostly (added Dynamic PWM)
+#define MODEL_NUMBER "0714"
+#include "hwdef-wurkkos-ts10.h"
+#include "wurkkos-cfg.h"
+// ATTINY: 1616
+
+// uses forward-facing aux LEDs
+#define USE_INDICATOR_LED
+// don't turn on the aux LEDs while main LEDs are on
+#ifdef USE_INDICATOR_LED_WHILE_RAMPING
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+#endif
+// the high button LED mode on this light uses too much power, default to low
+// off mode: low (1)
+// lockout: blinking (3)
+#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+
+// voltage readings were a little high with the Q8 value
+#undef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V
+
+#define RAMP_SIZE 150
+
+#if 0 // 2022 version by SammysHP, smooth but only level 1/150 is underclocked
+// level 1 by hand, for the rest
+// level_calc.py 7.01 2 149 7135 3 0.5 125 FET 1 10 1200 --pwm dyn:63:2048:255
+#define PWM1_LEVELS 1,3,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,24,25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,64,67,70,72,75,77,80,82,85,87,89,91,93,95,96,98,99,100,100,101,100,100,99,97,95,93,90,86,82,87,91,96,100,106,111,116,122,128,134,141,147,155,162,169,177,186,194,203,213,222,232,243,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,6,7,9,11,12,14,16,18,20,22,24,27,29,31,34,37,39,42,45,48,51,54,57,61,64,68,72,75,79,83,88,92,97,101,106,111,116,121,126,132,138,144,150,156,162,169,176,183,190,197,205,213,221,229,237,246,255
+#define PWM_TOPS 2047,2047,1198,1322,1584,1676,1701,1691,1662,1622,1774,1703,1631,1692,1613,1639,1558,1564,1559,1478,1464,1444,1420,1392,1361,1329,1331,1293,1256,1246,1207,1192,1152,1133,1094,1074,1035,1013,991,954,932,897,875,842,820,790,760,731,704,678,646,622,593,566,534,510,478,452,423,393,364,338,310,280,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define MAX_1x7135 90
+#define HALFSPEED_LEVEL 2
+#define QUARTERSPEED_LEVEL 2
+#endif
+
+// 7135 at 90/150
+// level_calc.py 5.7895 2 150 7135 0 0.1 125.25 FET 1 10 1200 --pwm dyn:61:4096:255:2.5 --clock 5:11:2.0
+// (with heavy manual tweaks up to ~15/150)
+#define PWM1_LEVELS 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 8, 9, 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 19, 20, 21, 22, 23, 23, 24, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 32, 32, 33, 33, 34, 35, 36, 37, 38, 40, 41, 43, 45, 47, 50, 53, 56, 60, 63, 67, 71, 75, 79, 84, 89, 94, 99,104,110,116,122,129,136,143,150,158,166,174,183,192,202,211,222,232,243,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 5, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 29, 31, 34, 37, 39, 42, 45, 48, 51, 54, 58, 61, 65, 68, 72, 76, 80, 84, 88, 93, 97,102,107,112,117,122,127,133,139,145,151,157,163,170,177,183,191,198,205,213,221,229,238,246,255
+#define PWM_TOPS 4095,2893,3917,2806,3252,2703,2684,2660,2640,2370,3000,2900,2630,2549,2246,2193,2030,1961,1889,1716,1642,1569,1497,1428,1290,1232,1176,1122,1070,976,932,890,849,779,745,685,656,605,579,536,514,476,457,424,407,379,364,340,327,314,302,291,280,276,266,262,257,253,253,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define MAX_1x7135 90
+#define MIN_THERM_STEPDOWN 60
+#define HALFSPEED_LEVEL 11
+#define QUARTERSPEED_LEVEL 5
+#define DEFAULT_LEVEL 50
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 130
+// 10 30 50 70 [90] 110 130
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL 130
+#define RAMP_DISCRETE_STEPS 7
+
+// at Wurkkos's request, reduce the Simple UI ceiling a little bit
+// (i.e. not 150; original config had it at 144/150, or DD FET 204/255)
+// 10 30 50 70 [90] 110 130
+#define SIMPLE_UI_FLOOR 10
+#define SIMPLE_UI_CEIL 130
+#define SIMPLE_UI_STEPS 7
+
+// stop panicking at ~50% power
+#define THERM_FASTER_LEVEL 130 // throttle back faster when high
+
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the aux LEDs by default
+#define DEFAULT_BLINK_CHANNEL CM_AUX
+
+// the default of 26 looks a bit rough, so increase it to make it smoother
+#define CANDLE_AMPLITUDE 33
+
+// don't blink mid-ramp
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
+// enable factory reset on 13H without loosening tailcap (required)
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/cfg-wurkkos-ts11.h b/spaghetti-monster/anduril/cfg-wurkkos-ts11.h
new file mode 100644
index 0000000..8955731
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-wurkkos-ts11.h
@@ -0,0 +1,19 @@
+// Wurkkos TS11, like a TS25 but with a RGB button and RGB front aux
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// ATTINY: 1616
+#include "cfg-wurkkos-ts25.h"
+#undef MODEL_NUMBER
+#define MODEL_NUMBER "0717"
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+
+// turn on the aux LEDs while main LEDs are on
+// (but not until the main LEDs are bright enough to overpower the aux)
+// (setting this lower makes an annoying effect on some levels)
+#define USE_AUX_RGB_LEDS_WHILE_ON 50
+#define USE_INDICATOR_LED_WHILE_RAMPING
+
diff --git a/spaghetti-monster/anduril/cfg-wurkkos-ts25.h b/spaghetti-monster/anduril/cfg-wurkkos-ts25.h
new file mode 100644
index 0000000..852e91b
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-wurkkos-ts25.h
@@ -0,0 +1,87 @@
+// Wurkkos TS25, modelled after the TS10 but with RGB Aux
+// Copyright (C) 2022-2023 (FIXME)
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define MODEL_NUMBER "0715"
+#include "hwdef-wurkkos-ts25.h"
+#include "wurkkos-cfg.h"
+// ATTINY: 1616
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+
+// don't turn on the aux LEDs while main LEDs are on
+#ifdef USE_INDICATOR_LED_WHILE_RAMPING
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+#endif
+
+// voltage readings were a little high with the Q8 value
+#undef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V, not 0.35V
+
+#define RAMP_SIZE 150
+
+#if 0 // 2022 version
+// level 1 by hand, for the rest
+// level_calc.py 7.01 2 149 7135 3 0.5 125 FET 1 10 1200 --pwm dyn:63:2048:255
+#define PWM1_LEVELS 1,3,3,4,5,6,7,8,9,10,12,13,14,16,17,19,20,22,24,25,27,29,31,33,35,37,40,42,44,47,49,52,54,57,59,62,64,67,70,72,75,77,80,82,85,87,89,91,93,95,96,98,99,100,100,101,100,100,99,97,95,93,90,86,82,87,91,96,100,106,111,116,122,128,134,141,147,155,162,169,177,186,194,203,213,222,232,243,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,6,7,9,11,12,14,16,18,20,22,24,27,29,31,34,37,39,42,45,48,51,54,57,61,64,68,72,75,79,83,88,92,97,101,106,111,116,121,126,132,138,144,150,156,162,169,176,183,190,197,205,213,221,229,237,246,255
+#define PWM_TOPS 2047,2047,1198,1322,1584,1676,1701,1691,1662,1622,1774,1703,1631,1692,1613,1639,1558,1564,1559,1478,1464,1444,1420,1392,1361,1329,1331,1293,1256,1246,1207,1192,1152,1133,1094,1074,1035,1013,991,954,932,897,875,842,820,790,760,731,704,678,646,622,593,566,534,510,478,452,423,393,364,338,310,280,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+#define MAX_1x7135 90
+#define HALFSPEED_LEVEL 2
+#define QUARTERSPEED_LEVEL 2
+#endif
+
+// 7135 at 75/150
+// level_calc.py 5.7895 2 150 7135 1 0.1 130 FET 1 10 3000 --pwm dyn:74:4096:255:3
+// (with some manual tweaks)
+#define PWM1_LEVELS 1,1,2,3,3,4,5,6,7,8,9,11,12,13,15,16,18,19,21,23,26,27,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,71,74,76,78,80,82,85,87,90,93,96,100,103,107,112,116,122,127,133,140,147,154,163,171,182,192,203,215,228,241,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0
+// non-zero part of FET channel calculated with:
+// level_calc.py 3 1 75 7135 1 200 3000
+// (FIXME? there's a visible bump when the FET kicks in, even with just 1/255)
+#define PWM2_LEVELS 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,6,7,8,10,11,13,14,16,17,19,21,22,24,26,28,30,32,34,37,39,41,44,46,48,51,54,56,59,62,65,68,71,74,77,81,84,87,91,94,98,102,106,110,114,118,122,126,130,135,139,144,148,153,158,163,168,173,178,184,189,195,200,206,212,218,224,230,236,242,248,255
+#define PWM_TOPS 4095,2701,3200,3586,2518,2778,2834,2795,2705,2587,2455,2582,2412,2247,2256,2091,2062,1907,1860,1802,1737,1605,1542,1477,1412,1347,1284,1222,1162,1105,1050,997,946,898,853,810,768,730,693,658,625,594,564,536,503,485,462,439,418,398,384,366,353,340,327,319,307,298,292,284,280,273,269,266,263,260,258,256,256,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
+
+#define MAX_1x7135 75
+#define DEFAULT_LEVEL 50
+#define MIN_THERM_STEPDOWN 60
+#define HALFSPEED_LEVEL 20
+#define QUARTERSPEED_LEVEL 5
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 150
+// 20 38 56 [75] 93 111 130
+#define RAMP_DISCRETE_FLOOR 20
+#define RAMP_DISCRETE_CEIL 130
+#define RAMP_DISCRETE_STEPS 7
+
+// at Wurkkos's request, reduce the Simple UI ceiling a little bit
+// 25 50 [75] 100 125
+#define SIMPLE_UI_FLOOR 25
+#define SIMPLE_UI_CEIL 125
+#define SIMPLE_UI_STEPS 5
+
+// stop panicking at ~50% power
+#define THERM_FASTER_LEVEL 120 // throttle back faster when high
+
+// show each channel while it scroll by in the menu
+#define USE_CONFIG_COLORS
+
+// blink numbers on the main LEDs by default
+// (so battcheck will be visible while charging)
+#define DEFAULT_BLINK_CHANNEL CM_MAIN
+// blink numbers on the aux LEDs by default
+//#define DEFAULT_BLINK_CHANNEL CM_AUXWHT
+
+// use aux red + aux blue for police strobe
+#define USE_POLICE_COLOR_STROBE_MODE
+#define POLICE_STROBE_USES_AUX
+#define POLICE_COLOR_STROBE_CH1 CM_AUXRED
+#define POLICE_COLOR_STROBE_CH2 CM_AUXBLU
+
+// don't blink mid-ramp
+#ifdef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_MIDDLE
+#endif
+
diff --git a/spaghetti-monster/anduril/channel-modes.c b/spaghetti-monster/anduril/channel-modes.c
new file mode 100644
index 0000000..b2fc8d1
--- /dev/null
+++ b/spaghetti-monster/anduril/channel-modes.c
@@ -0,0 +1,237 @@
+// channel-modes.c: Multi-channel functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "channel-modes.h"
+
+uint8_t channel_mode_state(Event event, uint16_t arg) {
+ #ifdef USE_CHANNEL_MODE_ARGS
+ static int8_t tint_ramp_direction = 1;
+ static uint8_t prev_tint = 0;
+ // don't activate auto-tint modes unless the user hits the edge
+ // and keeps pressing for a while
+ static uint8_t past_edge_counter = 0;
+ // bugfix: click-click-hold from off to strobes would invoke tint ramping
+ // in addition to changing state... so ignore any tint-ramp events which
+ // don't look like they were meant to be here
+ static uint8_t active = 0;
+ uint8_t tint = cfg.channel_mode_args[channel_mode];
+ #endif
+
+ // it's possible that a light may need 3H but not 3C,
+ // so try to detect if 3C is needed
+ #if NUM_CHANNEL_MODES > 1
+ // 3 clicks: next channel mode
+ if (event == EV_3clicks) {
+ uint8_t next = channel_mode;
+ // go to next channel mode until we find one which is enabled
+ // (and don't do any infinite loops if the user disabled them all)
+ uint8_t count = 0;
+ do {
+ count ++;
+ next = (next + 1) % NUM_CHANNEL_MODES;
+ } while ((! channel_mode_enabled(next)) && count < NUM_CHANNEL_MODES);
+ //} while ((! channel_modes_enabled[next]) && count < NUM_CHANNEL_MODES);
+
+ // undo change if infinite loop detected (redundant?)
+ //if (NUM_CHANNEL_MODES == count) next = channel_mode;
+
+ // if mode hasn't changed, abort
+ if (channel_mode == next)
+ return EVENT_NOT_HANDLED;
+
+ set_channel_mode(next);
+
+ // remember after battery changes
+ cfg.channel_mode = channel_mode;
+ save_config();
+ return EVENT_HANDLED;
+ } else
+ #endif // if NUM_CHANNEL_MODES > 1
+
+ #ifdef USE_CUSTOM_CHANNEL_3H_MODES
+ // defer to mode-specific function if defined
+ if (channel_3H_modes[channel_mode]) {
+ StatePtr tint_func = channel_3H_modes[channel_mode];
+ uint8_t err = tint_func(event, arg);
+ if (EVENT_HANDLED == err) return EVENT_HANDLED;
+ // else let the default handler run
+ }
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ #ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE
+ // click, click, hold: change the current channel's arg (like tint)
+ if (event == EV_click3_hold) {
+ ///// adjust value from 0 to 255
+ // reset at beginning of movement
+ if (! arg) {
+ active = 1; // first frame means this is for us
+ past_edge_counter = 0; // doesn't start until user hits the edge
+ }
+ // ignore event if we weren't the ones who handled the first frame
+ if (! active) return EVENT_NOT_HANDLED;
+
+ #ifdef USE_STEPPED_TINT_RAMPING
+ if ((tint_ramp_direction > 0 && tint < 255) ||
+ (tint_ramp_direction < 0 && tint > 0)) {
+ // ramp slower in stepped mode
+ if (cfg.tint_ramp_style && (arg % HOLD_TIMEOUT != 0))
+ return EVENT_HANDLED;
+
+ const uint8_t step_size = (cfg.tint_ramp_style < 2)
+ ? 1 : 254 / (cfg.tint_ramp_style-1);
+ tint = nearest_tint_value(
+ tint + ((int16_t)step_size * tint_ramp_direction)
+ );
+ }
+ #else // smooth tint ramping only
+ if ((tint_ramp_direction > 0) && (tint < 255)) { tint ++; }
+ else
+ if ((tint_ramp_direction < 0) && (tint > 0)) { tint --; }
+ #endif // ifdef USE_STEPPED_TINT_RAMPING
+
+ // if tint change stalled, let user know we hit the edge
+ else if (prev_tint == tint) {
+ if (past_edge_counter == 0) blip();
+ past_edge_counter = 1;
+ }
+ prev_tint = tint;
+ cfg.channel_mode_args[channel_mode] = tint;
+ set_level(actual_level);
+ return EVENT_HANDLED;
+ }
+
+ // click, click, hold, release: reverse direction for next ramp
+ else if (event == EV_click3_hold_release) {
+ active = 0; // ignore next hold if it wasn't meant for us
+ // reverse
+ tint_ramp_direction = -tint_ramp_direction;
+ if (0 == tint) tint_ramp_direction = 1;
+ else if (255 == tint) tint_ramp_direction = -1;
+ // remember tint after battery change
+ cfg.channel_mode_args[channel_mode] = tint;
+ save_config();
+ // bug?: for some reason, brightness can seemingly change
+ // from 1/150 to 2/150 without this next line... not sure why
+ set_level(actual_level);
+ return EVENT_HANDLED;
+ }
+ #endif // ifndef DONT_USE_DEFAULT_CHANNEL_ARG_MODE
+ #endif // ifdef USE_CHANNEL_MODE_ARGS
+
+ #if defined(USE_SIMPLE_UI)
+ // remaining mappings aren't "simple", so stop here
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif
+
+ #if NUM_CHANNEL_MODES > 1
+ // channel toggle menu on ... 9H?
+ else if (event == EV_click9_hold) {
+ push_state(channel_mode_config_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ return EVENT_NOT_HANDLED;
+}
+
+
+#if NUM_CHANNEL_MODES > 1
+void channel_mode_config_save(uint8_t step, uint8_t value) {
+ // 1 menu item per channel mode, to enable or disable that mode
+ step --; // step is 1-based, channel modes are 0-based
+ if (value) channel_mode_enable(step);
+ else channel_mode_disable(step);
+}
+
+uint8_t channel_mode_config_state(Event event, uint16_t arg) {
+ uint8_t ret;
+ // make config steps match channel modes
+ config_color_per_step = true;
+ // 1 menu item per channel mode, to enable or disable that mode
+ ret = config_state_base(
+ event, arg,
+ NUM_CHANNEL_MODES,
+ channel_mode_config_save
+ );
+ // no other menu needs this
+ config_color_per_step = false;
+ return ret;
+}
+#endif
+
+
+#if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
+uint8_t nearest_tint_value(const int16_t target) {
+ // const symbols for more readable code, will be removed by the compiler
+ const uint8_t tint_min = 0;
+ const uint8_t tint_max = 255;
+ const uint8_t tint_range = tint_max - tint_min;
+
+ // only equal mix of both channels
+ if (1 == cfg.tint_ramp_style) return (tint_min + tint_max) >> 1;
+
+ if (target < tint_min) return tint_min;
+ if (target > tint_max) return tint_max;
+ if (0 == cfg.tint_ramp_style) return target; // smooth ramping
+
+ const uint8_t step_size = tint_range / (cfg.tint_ramp_style-1);
+
+ uint8_t tint_result = tint_min;
+ for (uint8_t i=0; i<cfg.tint_ramp_style; i++) {
+ tint_result = tint_min
+ + (i * (uint16_t)tint_range / (cfg.tint_ramp_style-1));
+ int16_t diff = target - tint_result;
+ if (diff <= (step_size>>1)) return tint_result;
+ }
+ return tint_result;
+}
+#endif
+
+#ifdef USE_CIRCULAR_TINT_3H
+uint8_t circular_tint_3h(Event event, uint16_t arg) {
+ static int8_t tint_ramp_direction = 1;
+ // bugfix: click-click-hold from off to strobes would invoke tint ramping
+ // in addition to changing state... so ignore any tint-ramp events which
+ // don't look like they were meant to be here
+ static uint8_t active = 0;
+ uint8_t tint = cfg.channel_mode_args[channel_mode];
+
+ // click, click, hold: change the current channel's arg (like tint)
+ if (event == EV_click3_hold) {
+ ///// adjust value from 0 to 255 in a circle
+ // reset at beginning of movement
+ if (! arg) {
+ active = 1; // first frame means this is for us
+ }
+ // ignore event if we weren't the ones who handled the first frame
+ if (! active) return EVENT_NOT_HANDLED;
+
+ // smooth tint ramping only
+ tint += tint_ramp_direction;
+
+ cfg.channel_mode_args[channel_mode] = tint;
+ set_level(actual_level);
+ return EVENT_HANDLED;
+ }
+
+ // click, click, hold, release: reverse direction for next ramp
+ else if (event == EV_click3_hold_release) {
+ active = 0; // ignore next hold if it wasn't meant for us
+ // reverse
+ tint_ramp_direction = -tint_ramp_direction;
+ // remember tint after battery change
+ save_config();
+ // bug?: for some reason, brightness can seemingly change
+ // from 1/150 to 2/150 without this next line... not sure why
+ set_level(actual_level);
+ return EVENT_HANDLED;
+ }
+
+ return EVENT_NOT_HANDLED;
+}
+#endif
diff --git a/spaghetti-monster/anduril/channel-modes.h b/spaghetti-monster/anduril/channel-modes.h
new file mode 100644
index 0000000..b51721d
--- /dev/null
+++ b/spaghetti-monster/anduril/channel-modes.h
@@ -0,0 +1,26 @@
+// channel-modes.h: Multi-channel functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// not actually a mode, more of a fallback under other modes
+uint8_t channel_mode_state(Event event, uint16_t arg);
+
+#if NUM_CHANNEL_MODES > 1
+uint8_t channel_mode_config_state(Event event, uint16_t arg);
+#endif
+
+#if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
+// calculate the nearest tint value which would be valid at the moment
+uint8_t nearest_tint_value(const int16_t target);
+#endif
+
+#ifdef USE_CUSTOM_CHANNEL_3H_MODES
+StatePtr channel_3H_modes[NUM_CHANNEL_MODES];
+#endif
+
+#ifdef USE_CIRCULAR_TINT_3H
+uint8_t circular_tint_3h(Event event, uint16_t arg);
+#endif
+
diff --git a/spaghetti-monster/anduril/config-default.h b/spaghetti-monster/anduril/config-default.h
index 171c8b3..899bc4a 100644
--- a/spaghetti-monster/anduril/config-default.h
+++ b/spaghetti-monster/anduril/config-default.h
@@ -1,24 +1,8 @@
-/*
- * config-default.h: Default configuration for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// config-default.h: Default configuration for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef CONFIG_DEFAULT_H
-#define CONFIG_DEFAULT_H
+#pragma once
/*
* This file specifies the default settings for Anduril.
@@ -36,7 +20,13 @@
// overheat protection
#define USE_THERMAL_REGULATION
+#if (ATTINY==85) || (ATTINY==1634)
+// sloppy temperature sensor needs bigger error margin
#define DEFAULT_THERM_CEIL 45 // try not to get hotter than this (in C)
+#else
+// more accurate temperature sensor can regulate higher safely
+#define DEFAULT_THERM_CEIL 50 // try not to get hotter than this (in C)
+#endif
// Comment out to disable automatic calibration on factory reset
// - If so, be sure to set THERM_CAL_OFFSET to the correct calibration offset
// - Calibration can still be overridden in temperature check mode
@@ -195,6 +185,23 @@
// if the aux LEDs oscillate between "full battery" and "empty battery"
// while in "voltage" mode, enable this to reduce the amplitude of
// those oscillations
-//#define USE_LOWPASS_WHILE_ASLEEP
+#if (ATTINY==1616) || (ATTINY==1634)
+#define USE_LOWPASS_WHILE_ASLEEP
+#endif
+
+// if there's tint ramping, allow user to set it smooth or stepped
+#define USE_STEPPED_TINT_RAMPING
+#define DEFAULT_TINT_RAMP_STYLE 0 // smooth
+// Use "smooth steps" to soften on/off and step changes
+// on MCUs with enough room for extra stuff like this
+#if (ATTINY==1616) || (ATTINY==1634)
+#define USE_SMOOTH_STEPS
#endif
+// 0 = none, 1 = smooth, 2+ = undefined
+#define DEFAULT_SMOOTH_STEPS_STYLE 1
+
+// by default, allow user to set the channel for each strobe-group mode
+// (but allow disabling this feature per build)
+#define USE_CHANNEL_PER_STROBE
+
diff --git a/spaghetti-monster/anduril/config-mode.c b/spaghetti-monster/anduril/config-mode.c
index 3af2a1c..71b0d69 100644
--- a/spaghetti-monster/anduril/config-mode.c
+++ b/spaghetti-monster/anduril/config-mode.c
@@ -1,24 +1,8 @@
-/*
- * config-mode.c: Config mode base functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef CONFIG_MODE_C
-#define CONFIG_MODE_C
+// config-mode.c: Config mode base functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include "config-mode.h"
@@ -28,6 +12,14 @@ uint8_t number_entry_state(Event event, uint16_t arg);
volatile uint8_t number_entry_value;
+#if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+// TODO: promote this to fsm-channels.c ?
+void set_chan_if(bool cond, uint8_t chan) {
+ if ((cond) && (chan != channel_mode))
+ set_channel_mode(chan);
+}
+#endif
+
// allow the user to set a new value for a config option
// can be called two ways:
// - with a "click" action: Configures first menu item only.
@@ -45,7 +37,13 @@ uint8_t config_state_base(
void (*savefunc)(uint8_t step, uint8_t value)) {
static uint8_t config_step;
+ #ifdef USE_CONFIG_COLORS
+ static uint8_t orig_channel;
+ #endif
if (event == EV_enter_state) {
+ #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+ orig_channel = channel_mode;
+ #endif
config_step = 0;
set_level(0);
// if button isn't held, configure first menu item
@@ -62,14 +60,26 @@ uint8_t config_state_base(
#define B_ANY_HOLD_RELEASE (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT)
else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD) {
if (config_step <= num_config_steps) {
- if (2 == (arg % (TICKS_PER_SECOND*3/2))) {
+ #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+ uint8_t chan = config_step - 1;
+ if (chan < NUM_CHANNEL_MODES)
+ set_chan_if(config_color_per_step, chan);
+ #endif
+ if ((TICKS_PER_SECOND/10) == (arg % (TICKS_PER_SECOND*3/2))) {
config_step ++;
// blink when config step advances
- if (config_step <= num_config_steps)
+ if (config_step <= num_config_steps) {
+ #ifdef CONFIG_BLINK_CHANNEL
+ set_chan_if(!config_color_per_step, CONFIG_BLINK_CHANNEL);
+ #endif
set_level(RAMP_SIZE * 3 / 8);
+ }
}
else {
// stay on at a low level to indicate menu is active
+ #ifdef CONFIG_WAITING_CHANNEL
+ set_chan_if(!config_color_per_step, CONFIG_WAITING_CHANNEL);
+ #endif
set_level(RAMP_SIZE * 1 / 8);
}
} else {
@@ -82,6 +92,10 @@ uint8_t config_state_base(
else if ((event & B_CLICK_FLAGS) == B_ANY_HOLD_RELEASE) {
// ask the user for a number, if they selected a menu item
if (config_step && config_step <= num_config_steps) {
+ #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+ // put the colors back how they were
+ set_channel_mode(orig_channel);
+ #endif
push_state(number_entry_state, 0);
}
// exit after falling out of end of menu
@@ -99,6 +113,13 @@ uint8_t config_state_base(
pop_state();
}
+ #if defined(USE_CONFIG_COLORS) && (NUM_CHANNEL_MODES > 1)
+ else if (event == EV_leave_state) {
+ // put the colors back how they were
+ set_channel_mode(orig_channel);
+ }
+ #endif
+
// eat all other events; don't pass any through to parent
return EVENT_HANDLED;
}
@@ -135,15 +156,18 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
// (flicker every other frame,
// except first frame (so we can see flashes after each click))
else if (arg) {
+ #ifdef CONFIG_WAITING_CHANNEL
+ set_chan_if(1, CONFIG_WAITING_CHANNEL);
+ #endif
set_level( (RAMP_SIZE/8)
- + ((arg&2)<<1) );
+ + ((arg&2)<<2) );
}
}
// all done, save result and return to parent state
else {
pop_state();
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// count clicks: click = +1, hold = +10
@@ -159,14 +183,14 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
#endif
number_entry_value ++; // update the result
empty_event_sequence(); // reset FSM's click count
+ #ifdef CONFIG_BLINK_CHANNEL
+ set_channel_mode(CONFIG_BLINK_CHANNEL);
+ #endif
set_level(RAMP_SIZE/2); // flash briefly
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// eat all other events; don't pass any through to parent
return EVENT_HANDLED;
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/config-mode.h b/spaghetti-monster/anduril/config-mode.h
index 7cbc534..d4a7652 100644
--- a/spaghetti-monster/anduril/config-mode.h
+++ b/spaghetti-monster/anduril/config-mode.h
@@ -1,24 +1,18 @@
-/*
- * config-mode.h: Config mode base functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// config-mode.h: Config mode base functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef CONFIG_MODE_H
-#define CONFIG_MODE_H
+#pragma once
+
+// menus can use 2 colors
+#if defined (CONFIG_WAITING_CHANNEL) || defined(CONFIG_BLINK_CHANNEL)
+#define USE_CONFIG_COLORS
+#endif
+
+#if NUM_CHANNEL_MODES > 1
+// when true, changes the channel mode for each config step
+bool config_color_per_step = false;
+#endif
// config menu
uint8_t config_state_base(
@@ -28,5 +22,3 @@ uint8_t config_state_base(
void (*savefunc)(uint8_t step, uint8_t value)
);
-
-#endif
diff --git a/spaghetti-monster/anduril/factory-reset-fsm.h b/spaghetti-monster/anduril/factory-reset-fsm.h
index a8fb0d9..3cb0875 100644
--- a/spaghetti-monster/anduril/factory-reset-fsm.h
+++ b/spaghetti-monster/anduril/factory-reset-fsm.h
@@ -1,28 +1,10 @@
-/*
- * factory-reset-fsm.h: FSM config options to enable factory reset in Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// factory-reset-fsm.h: FSM config options to enable factory reset in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FACTORY_RESET_FSM_H
-#define FACTORY_RESET_FSM_H
+#pragma once
#ifdef USE_SOFT_FACTORY_RESET
#define USE_REBOOT
#endif
-
-#endif
diff --git a/spaghetti-monster/anduril/factory-reset.c b/spaghetti-monster/anduril/factory-reset.c
index ecb4cc2..f9fb472 100644
--- a/spaghetti-monster/anduril/factory-reset.c
+++ b/spaghetti-monster/anduril/factory-reset.c
@@ -1,27 +1,14 @@
-/*
- * factory-reset.c: Factory reset functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// factory-reset.c: Factory reset functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FACTORY_RESET_C
-#define FACTORY_RESET_C
+#pragma once
#include "factory-reset.h"
+// allows setting channel mode per animation stage,
+// so it can ramp up in red then explode in white (as one example)
+
void factory_reset() {
// display a warning for a few seconds before doing the actual reset,
// so the user has time to abort if they want
@@ -31,6 +18,9 @@ void factory_reset() {
uint8_t bright;
uint8_t reset = 1;
// wind up to an explosion
+ #ifdef FACTORY_RESET_WARN_CHANNEL
+ set_channel_mode(FACTORY_RESET_WARN_CHANNEL);
+ #endif
for (bright=0; bright<SPLODEY_STEPS; bright++) {
set_level(bright);
nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
@@ -43,17 +33,29 @@ void factory_reset() {
}
// explode, if button pressed long enough
if (reset) {
+ #if defined(FACTORY_RESET_WARN_CHANNEL) && defined(DEFAULT_CHANNEL_MODE)
+ // return to default channel before saving
+ set_channel_mode(DEFAULT_CHANNEL_MODE);
+ #endif
+
+ // auto-calibrate temperature
// AVR 1-Series has factory calibrated thermal sensor, always remove the offset on reset
#if defined(USE_THERMAL_REGULATION) && defined(AVRXMEGA3)
- thermal_config_save(1,temperature - therm_cal_offset); // this will cancel out the offset
+ // this will cancel out the offset
+ thermal_config_save(1, temperature - cfg.therm_cal_offset);
#elif defined(USE_THERMAL_REGULATION) && defined(USE_THERM_AUTOCALIBRATE)
- // auto-calibrate temperature... assume current temperature is 21 C
+ // assume current temperature is 21 C
thermal_config_save(1, 21);
#endif
+
// save all settings to eeprom
// (assuming they're all at default because we haven't loaded them yet)
save_config();
+ // explosion animation
+ #ifdef FACTORY_RESET_SUCCESS_CHANNEL
+ set_channel_mode(FACTORY_RESET_SUCCESS_CHANNEL);
+ #endif
bright = MAX_LEVEL;
for (; bright > 0; bright--) {
set_level(bright);
@@ -69,6 +71,3 @@ void factory_reset() {
}
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/factory-reset.h b/spaghetti-monster/anduril/factory-reset.h
index 9f0af38..63c25cd 100644
--- a/spaghetti-monster/anduril/factory-reset.h
+++ b/spaghetti-monster/anduril/factory-reset.h
@@ -1,26 +1,8 @@
-/*
- * factory-reset.h: Factory reset functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// factory-reset.h: Factory reset functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FACTORY_RESET_H
-#define FACTORY_RESET_H
+#pragma once
void factory_reset();
-
-#endif
diff --git a/spaghetti-monster/anduril/ff-strobe-modes.c b/spaghetti-monster/anduril/ff-strobe-modes.c
index 4c12630..b7a7303 100644
--- a/spaghetti-monster/anduril/ff-strobe-modes.c
+++ b/spaghetti-monster/anduril/ff-strobe-modes.c
@@ -1,24 +1,8 @@
-/*
- * ff-strobe-modes.c: Fireflies Flashlights strobe modes for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// ff-strobe-modes.c: Fireflies Flashlights strobe modes for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FF_STROBE_MODES_C
-#define FF_STROBE_MODES_C
+#pragma once
#include "ff-strobe-modes.h"
@@ -28,19 +12,19 @@ uint8_t boring_strobe_state(Event event, uint16_t arg) {
uint8_t st = boring_strobe_type;
if (event == EV_enter_state) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
// reset to police strobe for next time
boring_strobe_type = 0;
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: rotate through strobe/flasher modes
else if (event == EV_2clicks) {
boring_strobe_type = (st + 1) % NUM_BORING_STROBES;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -76,6 +60,3 @@ inline void police_strobe_iter() {
}
#endif
-
-#endif
-
diff --git a/spaghetti-monster/anduril/ff-strobe-modes.h b/spaghetti-monster/anduril/ff-strobe-modes.h
index a3e0a27..d7adfec 100644
--- a/spaghetti-monster/anduril/ff-strobe-modes.h
+++ b/spaghetti-monster/anduril/ff-strobe-modes.h
@@ -1,24 +1,8 @@
-/*
- * ff-strobe-modes.h: Fireflies Flashlights strobe modes for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// ff-strobe-modes.h: Fireflies Flashlights strobe modes for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FF_STROBE_MODES_H
-#define FF_STROBE_MODES_H
+#pragma once
uint8_t boring_strobe_state(Event event, uint16_t arg);
inline void boring_strobe_state_iter();
@@ -29,5 +13,3 @@ inline void police_strobe_iter();
#endif
#define NUM_BORING_STROBES 2
-
-#endif
diff --git a/spaghetti-monster/anduril/hank-cfg.h b/spaghetti-monster/anduril/hank-cfg.h
index 11eb0a1..86ac605 100644
--- a/spaghetti-monster/anduril/hank-cfg.h
+++ b/spaghetti-monster/anduril/hank-cfg.h
@@ -1,5 +1,7 @@
-#ifndef HANK_CFG
-#define HANK_CFG
+// Intl-Outdoor (Hank)'s config options for Anduril
+// Copyright (C) 2021-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
// config preferences for Hank Wang of Intl-Outdoor (Emisar, Noctigon)
@@ -14,8 +16,15 @@
// half a second per color in rainbow mode
//#define RGB_RAINBOW_SPEED 0x03
+// Allow 3C (or 6C) in Simple UI (toggle smooth or stepped ramping)
+#define USE_SIMPLE_UI_RAMPING_TOGGLE
+
+// allow Aux Config and Strobe Modes in Simple UI
+#define USE_EXTENDED_SIMPLE_UI
// double click while on goes to full-power turbo, not ramp ceiling
#define DEFAULT_2C_STYLE 1
-#endif // ifndef HANK_CFG
+// for consistency with KR4 (not otherwise necessary though)
+#define USE_SOFT_FACTORY_RESET
+
diff --git a/spaghetti-monster/anduril/load-save-config-fsm.h b/spaghetti-monster/anduril/load-save-config-fsm.h
index f0de161..d189d3a 100644
--- a/spaghetti-monster/anduril/load-save-config-fsm.h
+++ b/spaghetti-monster/anduril/load-save-config-fsm.h
@@ -1,113 +1,139 @@
-/*
- * load-save-config-fsm.h: FSM config for eeprom configuration in Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LOAD_SAVE_CONFIG_FSM_H
-#define LOAD_SAVE_CONFIG_FSM_H
+// load-save-config-fsm.h: FSM config for eeprom configuration in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
-// auto-detect how many eeprom bytes
#define USE_EEPROM
-typedef enum {
- ramp_style_e,
- #ifdef USE_RAMP_CONFIG
- ramp_smooth_floor_e,
- ramp_smooth_ceil_e,
- #ifdef USE_RAMP_SPEED_CONFIG
- ramp_speed_e,
+// load into a custom RAM location instead of FSM's default byte array
+#define EEPROM_OVERRIDE
+
+#ifdef USE_SIMPLE_UI
+#define NUM_RAMPS 3
+#else
+#define NUM_RAMPS 2
+#endif
+
+// let FSM know this config struct exists
+#define USE_CFG
+
+typedef struct Config {
+
+ ///// ramp vars
+ uint8_t ramp_style;
+ #ifdef USE_2C_STYLE_CONFIG
+ uint8_t ramp_2c_style;
#endif
- ramp_discrete_floor_e,
- ramp_discrete_ceil_e,
- ramp_discrete_steps_e,
+ #ifdef USE_RAMP_CONFIG
+ uint8_t ramp_floors[NUM_RAMPS];
+ uint8_t ramp_ceils [NUM_RAMPS];
+ uint8_t ramp_stepss[NUM_RAMPS];
#endif
#ifdef USE_SIMPLE_UI
- simple_ui_floor_e,
- simple_ui_ceil_e,
- simple_ui_steps_e,
- simple_ui_active_e,
- #ifdef USE_2C_STYLE_CONFIG
- ramp_2c_style_simple_e,
- #endif
+ uint8_t simple_ui_active;
+ #ifdef USE_2C_STYLE_CONFIG
+ uint8_t ramp_2c_style_simple;
+ #endif
#endif
#ifdef USE_RAMP_AFTER_MOON_CONFIG
- dont_ramp_after_moon_e,
- #endif
- #ifdef USE_2C_STYLE_CONFIG
- ramp_2c_style_e,
+ uint8_t dont_ramp_after_moon;
#endif
#ifdef USE_MANUAL_MEMORY
- manual_memory_e,
+ uint8_t manual_memory;
#ifdef USE_MANUAL_MEMORY_TIMER
- manual_memory_timer_e,
+ uint8_t manual_memory_timer;
+ #endif
+ #endif
+
+ ///// channel modes / color modes
+ #if NUM_CHANNEL_MODES > 1
+ uint8_t channel_mode;
+ uint16_t channel_modes_enabled;
+ #ifdef USE_MANUAL_MEMORY
+ uint8_t manual_memory_channel_mode;
#endif
- #ifdef USE_TINT_RAMPING
- manual_memory_tint_e,
+ #ifdef DEFAULT_BLINK_CHANNEL
+ uint8_t blink_channel;
#endif
#endif
- #ifdef USE_TINT_RAMPING
- tint_e,
- tint_style_e,
+ #ifdef USE_CHANNEL_MODE_ARGS
+ // this is an array, needs a few bytes
+ uint8_t channel_mode_args[NUM_CHANNEL_MODES];
+ #ifdef USE_MANUAL_MEMORY
+ uint8_t manual_memory_channel_args[NUM_CHANNEL_MODES];
+ #endif
+ #ifdef USE_STEPPED_TINT_RAMPING
+ uint8_t tint_ramp_style;
+ #endif
#endif
- #ifdef USE_JUMP_START
- jump_start_level_e,
+
+ ///// Smooth animation between steps, and for on/off
+ #ifdef USE_SMOOTH_STEPS
+ uint8_t smooth_steps_style;
#endif
+
+ ///// strobe / blinky mode settings
#ifdef USE_STROBE_STATE
- strobe_type_e,
+ uint8_t strobe_type;
+ #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE)
+ uint8_t strobe_channels[NUM_STROBES];
+ #endif
#endif
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
- strobe_delays_0_e,
- strobe_delays_1_e,
+ uint8_t strobe_delays[2];
#endif
#ifdef USE_BIKE_FLASHER_MODE
- bike_flasher_brightness_e,
+ uint8_t bike_flasher_brightness;
#endif
#ifdef USE_BEACON_MODE
- beacon_seconds_e,
- #endif
- #ifdef USE_THERMAL_REGULATION
- therm_ceil_e,
- therm_cal_offset_e,
+ uint8_t beacon_seconds;
#endif
+
+ ///// voltage and temperature
#ifdef USE_VOLTAGE_CORRECTION
- voltage_correction_e,
+ uint8_t voltage_correction;
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ uint8_t therm_ceil;
+ int8_t therm_cal_offset;
#endif
+
+ ///// aux LEDs
#ifdef USE_INDICATOR_LED
- indicator_led_mode_e,
+ uint8_t indicator_led_mode;
#endif
#ifdef USE_AUX_RGB_LEDS
- rgb_led_off_mode_e,
- rgb_led_lockout_mode_e,
+ uint8_t rgb_led_off_mode;
+ uint8_t rgb_led_lockout_mode;
+ #ifdef USE_POST_OFF_VOLTAGE
+ uint8_t post_off_voltage;
+ #endif
#endif
+
+ ///// misc other mode settings
#ifdef USE_AUTOLOCK
- autolock_time_e,
+ uint8_t autolock_time;
#endif
#ifdef USE_TACTICAL_MODE
- tactical_lvl_1_e,
- tactical_lvl_2_e,
- tactical_lvl_3_e,
+ uint8_t tactical_levels[3];
#endif
- eeprom_indexes_e_END
-} eeprom_indexes_e;
-#define EEPROM_BYTES eeprom_indexes_e_END
+
+ ///// hardware config / globals menu
+ #ifdef USE_JUMP_START
+ uint8_t jump_start_level;
+ #endif
+
+} Config;
+
+// auto-detect how many eeprom bytes
+#define EEPROM_BYTES sizeof(Config)
+
+// declare this so FSM can see it,
+// but define its values in a file which loads later
+Config cfg;
#ifdef START_AT_MEMORIZED_LEVEL
#define USE_EEPROM_WL
#define EEPROM_WL_BYTES 1
#endif
-
-#endif
diff --git a/spaghetti-monster/anduril/load-save-config.c b/spaghetti-monster/anduril/load-save-config.c
index 4101e7a..aa772e1 100644
--- a/spaghetti-monster/anduril/load-save-config.c
+++ b/spaghetti-monster/anduril/load-save-config.c
@@ -1,106 +1,17 @@
-/*
- * load-save-config.c: Load/save/eeprom functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// load-save-config.c: Load/save/eeprom functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef LOAD_SAVE_CONFIG_C
-#define LOAD_SAVE_CONFIG_C
+#pragma once
#include "load-save-config-fsm.h"
#include "load-save-config.h"
void load_config() {
- if (load_eeprom()) {
- ramp_style = eeprom[ramp_style_e];
- #ifdef USE_RAMP_CONFIG
- ramp_floors[0] = eeprom[ramp_smooth_floor_e];
- ramp_ceils[0] = eeprom[ramp_smooth_ceil_e];
- #ifdef USE_RAMP_SPEED_CONFIG
- ramp_speed = eeprom[ramp_speed_e];
- #endif
- ramp_floors[1] = eeprom[ramp_discrete_floor_e];
- ramp_ceils[1] = eeprom[ramp_discrete_ceil_e];
- ramp_stepss[1] = eeprom[ramp_discrete_steps_e];
- #endif
- #ifdef USE_SIMPLE_UI
- ramp_floors[2] = eeprom[simple_ui_floor_e];
- ramp_ceils[2] = eeprom[simple_ui_ceil_e];
- ramp_stepss[2] = eeprom[simple_ui_steps_e];
- simple_ui_active = eeprom[simple_ui_active_e];
- #ifdef USE_2C_STYLE_CONFIG
- ramp_2c_style_simple = eeprom[ramp_2c_style_simple_e];
- #endif
- #endif
- #ifdef USE_RAMP_AFTER_MOON_CONFIG
- dont_ramp_after_moon = eeprom[dont_ramp_after_moon_e];
- #endif
- #ifdef USE_2C_STYLE_CONFIG
- ramp_2c_style = eeprom[ramp_2c_style_e];
- #endif
- #ifdef USE_MANUAL_MEMORY
- manual_memory = eeprom[manual_memory_e];
- #ifdef USE_MANUAL_MEMORY_TIMER
- manual_memory_timer = eeprom[manual_memory_timer_e];
- #endif
- #ifdef USE_TINT_RAMPING
- manual_memory_tint = eeprom[manual_memory_tint_e];
- #endif
- #endif
- #ifdef USE_TINT_RAMPING
- tint = eeprom[tint_e];
- tint_style = eeprom[tint_style_e];
- #endif
- #ifdef USE_JUMP_START
- jump_start_level = eeprom[jump_start_level_e],
- #endif
- #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
- strobe_type = eeprom[strobe_type_e]; // TODO: move this to eeprom_wl?
- strobe_delays[0] = eeprom[strobe_delays_0_e];
- strobe_delays[1] = eeprom[strobe_delays_1_e];
- #endif
- #ifdef USE_BIKE_FLASHER_MODE
- bike_flasher_brightness = eeprom[bike_flasher_brightness_e];
- #endif
- #ifdef USE_BEACON_MODE
- beacon_seconds = eeprom[beacon_seconds_e];
- #endif
- #ifdef USE_THERMAL_REGULATION
- therm_ceil = eeprom[therm_ceil_e];
- therm_cal_offset = eeprom[therm_cal_offset_e];
- #endif
- #ifdef USE_VOLTAGE_CORRECTION
- voltage_correction = eeprom[voltage_correction_e];
- #endif
- #ifdef USE_INDICATOR_LED
- indicator_led_mode = eeprom[indicator_led_mode_e];
- #endif
- #ifdef USE_AUX_RGB_LEDS
- rgb_led_off_mode = eeprom[rgb_led_off_mode_e];
- rgb_led_lockout_mode = eeprom[rgb_led_lockout_mode_e];
- #endif
- #ifdef USE_AUTOLOCK
- autolock_time = eeprom[autolock_time_e];
- #endif
- #ifdef USE_TACTICAL_MODE
- tactical_levels[0] = eeprom[tactical_lvl_1_e];
- tactical_levels[1] = eeprom[tactical_lvl_2_e];
- tactical_levels[2] = eeprom[tactical_lvl_3_e];
- #endif
- }
+ eeprom = (uint8_t *)&cfg;
+
+ if (! load_eeprom()) return;
+
#ifdef START_AT_MEMORIZED_LEVEL
if (load_eeprom_wl()) {
memorized_level = eeprom_wl[0];
@@ -109,82 +20,7 @@ void load_config() {
}
void save_config() {
- eeprom[ramp_style_e] = ramp_style;
- #ifdef USE_RAMP_CONFIG
- eeprom[ramp_smooth_floor_e] = ramp_floors[0];
- eeprom[ramp_smooth_ceil_e] = ramp_ceils[0];
- #ifdef USE_RAMP_SPEED_CONFIG
- eeprom[ramp_speed_e] = ramp_speed;
- #endif
- eeprom[ramp_discrete_floor_e] = ramp_floors[1];
- eeprom[ramp_discrete_ceil_e] = ramp_ceils[1];
- eeprom[ramp_discrete_steps_e] = ramp_stepss[1];
- #endif
- #ifdef USE_SIMPLE_UI
- eeprom[simple_ui_floor_e] = ramp_floors[2];
- eeprom[simple_ui_ceil_e] = ramp_ceils[2];
- eeprom[simple_ui_steps_e] = ramp_stepss[2];
- eeprom[simple_ui_active_e] = simple_ui_active;
- #ifdef USE_2C_STYLE_CONFIG
- eeprom[ramp_2c_style_simple_e] = ramp_2c_style_simple;
- #endif
- #endif
- #ifdef USE_RAMP_AFTER_MOON_CONFIG
- eeprom[dont_ramp_after_moon_e] = dont_ramp_after_moon;
- #endif
- #ifdef USE_2C_STYLE_CONFIG
- eeprom[ramp_2c_style_e] = ramp_2c_style;
- #endif
- #ifdef USE_MANUAL_MEMORY
- eeprom[manual_memory_e] = manual_memory;
- #ifdef USE_MANUAL_MEMORY_TIMER
- eeprom[manual_memory_timer_e] = manual_memory_timer;
- #endif
- #ifdef USE_TINT_RAMPING
- eeprom[manual_memory_tint_e] = manual_memory_tint;
- #endif
- #endif
- #ifdef USE_TINT_RAMPING
- eeprom[tint_e] = tint;
- eeprom[tint_style_e] = tint_style;
- #endif
- #ifdef USE_JUMP_START
- eeprom[jump_start_level_e] = jump_start_level,
- #endif
- #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
- eeprom[strobe_type_e] = strobe_type; // TODO: move this to eeprom_wl?
- eeprom[strobe_delays_0_e] = strobe_delays[0];
- eeprom[strobe_delays_1_e] = strobe_delays[1];
- #endif
- #ifdef USE_BIKE_FLASHER_MODE
- eeprom[bike_flasher_brightness_e] = bike_flasher_brightness;
- #endif
- #ifdef USE_BEACON_MODE
- eeprom[beacon_seconds_e] = beacon_seconds;
- #endif
- #ifdef USE_THERMAL_REGULATION
- eeprom[therm_ceil_e] = therm_ceil;
- eeprom[therm_cal_offset_e] = therm_cal_offset;
- #endif
- #ifdef USE_VOLTAGE_CORRECTION
- eeprom[voltage_correction_e] = voltage_correction;
- #endif
- #ifdef USE_INDICATOR_LED
- eeprom[indicator_led_mode_e] = indicator_led_mode;
- #endif
- #ifdef USE_AUX_RGB_LEDS
- eeprom[rgb_led_off_mode_e] = rgb_led_off_mode;
- eeprom[rgb_led_lockout_mode_e] = rgb_led_lockout_mode;
- #endif
- #ifdef USE_AUTOLOCK
- eeprom[autolock_time_e] = autolock_time;
- #endif
- #ifdef USE_TACTICAL_MODE
- eeprom[tactical_lvl_1_e] = tactical_levels[0];
- eeprom[tactical_lvl_2_e] = tactical_levels[1];
- eeprom[tactical_lvl_3_e] = tactical_levels[2];
- #endif
-
+ eeprom = (uint8_t *)&cfg;
save_eeprom();
}
@@ -195,6 +31,3 @@ void save_config_wl() {
}
#endif
-
-#endif
-
diff --git a/spaghetti-monster/anduril/load-save-config.h b/spaghetti-monster/anduril/load-save-config.h
index 29c1a69..514fcbb 100644
--- a/spaghetti-monster/anduril/load-save-config.h
+++ b/spaghetti-monster/anduril/load-save-config.h
@@ -1,24 +1,8 @@
-/*
- * load-save-config.h: Load/save/eeprom functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LOAD_SAVE_CONFIG_H
-#define LOAD_SAVE_CONFIG_H
+// load-save-config.h: Load/save/eeprom functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
// remember stuff even after battery was changed
void load_config();
@@ -27,5 +11,163 @@ void save_config();
void save_config_wl();
#endif
+// a struct to hold config values
+Config cfg = {
+
+ ///// ramp vars
+
+ // smooth vs discrete ramping
+ .ramp_style = RAMP_STYLE, // 0 = smooth, 1 = discrete
+ #ifdef USE_2C_STYLE_CONFIG
+ // 1 = A1 style, 2 = A2 style
+ .ramp_2c_style = DEFAULT_2C_STYLE,
+ #endif
+
+ // settings for each ramp (smooth, stepped, simple UI)
+ #ifdef USE_RAMP_CONFIG
+ .ramp_floors = {
+ RAMP_SMOOTH_FLOOR,
+ RAMP_DISCRETE_FLOOR,
+ #ifdef USE_SIMPLE_UI
+ SIMPLE_UI_FLOOR,
+ #endif
+ },
+ .ramp_ceils = {
+ RAMP_SMOOTH_CEIL,
+ RAMP_DISCRETE_CEIL,
+ #ifdef USE_SIMPLE_UI
+ SIMPLE_UI_CEIL,
+ #endif
+ },
+ .ramp_stepss = {
+ DEFAULT_RAMP_SPEED,
+ RAMP_DISCRETE_STEPS,
+ #ifdef USE_SIMPLE_UI
+ SIMPLE_UI_STEPS,
+ #endif
+ },
+ #endif
+
+ #ifdef USE_SIMPLE_UI
+ // whether to enable the simplified interface or not
+ .simple_ui_active = SIMPLE_UI_ACTIVE,
+ #ifdef USE_2C_STYLE_CONFIG
+ // 0 = no turbo, 1 = A1 style, 2 = A2 style
+ .ramp_2c_style_simple = DEFAULT_2C_STYLE_SIMPLE,
+ #endif
+ #endif
+
+ #ifdef USE_RAMP_AFTER_MOON_CONFIG
+ .dont_ramp_after_moon = DEFAULT_DONT_RAMP_AFTER_MOON,
+ #endif
+
+ #ifdef USE_MANUAL_MEMORY
+ .manual_memory = DEFAULT_MANUAL_MEMORY,
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ .manual_memory_timer = DEFAULT_MANUAL_MEMORY_TIMER,
+ #endif
+ #endif
+
+ ///// channel modes / color modes
+
+ #if NUM_CHANNEL_MODES > 1
+ // current multi-channel mode
+ .channel_mode = DEFAULT_CHANNEL_MODE,
+ // user can take unwanted modes out of the rotation (bitmask)
+ .channel_modes_enabled = CHANNEL_MODES_ENABLED,
+ #ifdef USE_MANUAL_MEMORY
+ // reset w/ manual memory
+ .manual_memory_channel_mode = DEFAULT_CHANNEL_MODE,
+ #endif
+ #ifdef DEFAULT_BLINK_CHANNEL
+ // blink numbers in a specific channel (user configurable)
+ .blink_channel = DEFAULT_BLINK_CHANNEL,
+ #endif
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ // one byte of extra data per channel mode, like for tint value
+ .channel_mode_args = { CHANNEL_MODE_ARGS },
+ #ifdef USE_MANUAL_MEMORY
+ // remember and reset 1 extra parameter per channel mode (like tint)
+ .manual_memory_channel_args = { CHANNEL_MODE_ARGS },
+ #endif
+ #ifdef USE_STEPPED_TINT_RAMPING
+ .tint_ramp_style = DEFAULT_TINT_RAMP_STYLE,
+ #endif
+ #endif
+
+ ///// Smooth animation between steps, and for on/off
+ #ifdef USE_SMOOTH_STEPS
+ .smooth_steps_style = DEFAULT_SMOOTH_STEPS_STYLE,
+ #endif
+
+ ///// strobe / blinky mode settings
+
+ #ifdef USE_STROBE_STATE
+ .strobe_type = DEFAULT_STROBE,
+ #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE)
+ // channel mode saved per strobe-group mode
+ #ifdef DEFAULT_STROBE_CHANNELS
+ .strobe_channels = { DEFAULT_STROBE_CHANNELS },
+ #endif
+ #endif
+ #endif
+ #if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
+ // party / tactical strobe timing
+ // party strobe 24 Hz, tactical strobe 10 Hz
+ .strobe_delays = { 41, 67 },
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ .bike_flasher_brightness = MAX_1x7135,
+ #endif
+ #ifdef USE_BEACON_MODE
+ // beacon timing
+ .beacon_seconds = 2,
+ #endif
+
+ ///// voltage and temperature
+
+ #ifdef USE_VOLTAGE_CORRECTION
+ // same 0.05V units as fudge factor,
+ // but 7 is neutral, and the expected range is from 1 to 13
+ .voltage_correction = 7,
+ #endif
+ #ifdef USE_THERMAL_REGULATION
+ .therm_ceil = DEFAULT_THERM_CEIL,
+ .therm_cal_offset = 0,
+ #endif
+
+ ///// aux LEDs
+
+ #ifdef USE_INDICATOR_LED
+ // bits 2-3 control lockout mode
+ // bits 0-1 control "off" mode
+ // modes are: 0=off, 1=low, 2=high, 3=blinking (if TICK_DURING_STANDBY enabled)
+ .indicator_led_mode = INDICATOR_LED_DEFAULT_MODE,
+ #endif
+ #ifdef USE_AUX_RGB_LEDS
+ .rgb_led_off_mode = RGB_LED_OFF_DEFAULT,
+ .rgb_led_lockout_mode = RGB_LED_LOCKOUT_DEFAULT,
+ #ifdef USE_POST_OFF_VOLTAGE
+ // display voltage readout for a while after turning off?
+ .post_off_voltage = DEFAULT_POST_OFF_VOLTAGE_SECONDS,
+ #endif
+ #endif
+
+ ///// misc other mode settings
+
+ #ifdef USE_AUTOLOCK
+ .autolock_time = DEFAULT_AUTOLOCK_TIME,
+ #endif
+ #ifdef USE_TACTICAL_MODE
+ .tactical_levels = { TACTICAL_LEVELS },
+ #endif
+
+ ///// hardware config / globals menu
+
+ #ifdef USE_JUMP_START
+ .jump_start_level = DEFAULT_JUMP_START_LEVEL,
+ #endif
+
+};
-#endif
diff --git a/spaghetti-monster/anduril/lockout-mode-fsm.h b/spaghetti-monster/anduril/lockout-mode-fsm.h
index bc18ed3..ede251c 100644
--- a/spaghetti-monster/anduril/lockout-mode-fsm.h
+++ b/spaghetti-monster/anduril/lockout-mode-fsm.h
@@ -1,29 +1,11 @@
-/*
- * lockout-mode-fsm.h: FSM config for lockout mode in Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// lockout-mode-fsm.h: FSM config for lockout mode in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef LOCKOUT_MODE_FSM_H
-#define LOCKOUT_MODE_FSM_H
+#pragma once
// autolock function requires the ability to measure time while "off"
#ifdef USE_AUTOLOCK
#define TICK_DURING_STANDBY
#endif
-
-#endif
diff --git a/spaghetti-monster/anduril/lockout-mode.c b/spaghetti-monster/anduril/lockout-mode.c
index 13d3c0a..422d081 100644
--- a/spaghetti-monster/anduril/lockout-mode.c
+++ b/spaghetti-monster/anduril/lockout-mode.c
@@ -1,24 +1,8 @@
-/*
- * lockout-mode.c: Lockout mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef LOCKOUT_MODE_C
-#define LOCKOUT_MODE_C
+// lockout-mode.c: Lockout mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include "lockout-mode.h"
@@ -33,14 +17,14 @@ uint8_t lockout_state(Event event, uint16_t arg) {
if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
// hold: lowest floor
// click, hold: highest floor (or manual mem level)
- uint8_t lvl = ramp_floors[0];
- if ((event & 0x0f) == 2) { // second click
- if (ramp_floors[1] > lvl) lvl = ramp_floors[1];
+ uint8_t lvl = cfg.ramp_floors[0];
+ if (1 == (event & 0x0f)) { // first click
+ if (cfg.ramp_floors[1] < lvl) lvl = cfg.ramp_floors[1];
+ } else { // 2nd click or later
+ if (cfg.ramp_floors[1] > lvl) lvl = cfg.ramp_floors[1];
#ifdef USE_MANUAL_MEMORY
- if (manual_memory) lvl = manual_memory;
+ if (cfg.manual_memory) lvl = cfg.manual_memory;
#endif
- } else { // anything except second click
- if (ramp_floors[1] < lvl) lvl = ramp_floors[1];
}
set_level(lvl);
}
@@ -55,36 +39,45 @@ uint8_t lockout_state(Event event, uint16_t arg) {
// (allow staying awake long enough to exit, but otherwise
// be persistent about going back to sleep every few seconds
// even if the user keeps pressing the button)
- #ifdef USE_INDICATOR_LED
- // redundant, sleep tick does the same thing
- //if (event == EV_enter_state) {
- // indicator_led_update(indicator_led_mode >> 2, 0);
- //} else
- #elif defined(USE_AUX_RGB_LEDS)
if (event == EV_enter_state) {
- rgb_led_update(rgb_led_lockout_mode, 0);
- } else
- #endif
- if (event == EV_tick) {
+ ticks_since_on = 0;
+ #ifdef USE_INDICATOR_LED
+ // redundant, sleep tick does the same thing
+ // indicator_led_update(cfg.indicator_led_mode >> 2, 0);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(cfg.rgb_led_lockout_mode, 0);
+ #endif
+ }
+
+ else if (event == EV_tick) {
if (arg > HOLD_TIMEOUT) {
go_to_standby = 1;
#ifdef USE_INDICATOR_LED
// redundant, sleep tick does the same thing
- //indicator_led_update(indicator_led_mode >> 2, arg);
+ //indicator_led_update(cfg.indicator_led_mode >> 2, arg);
#elif defined(USE_AUX_RGB_LEDS)
- rgb_led_update(rgb_led_lockout_mode, arg);
+ rgb_led_update(cfg.rgb_led_lockout_mode, arg);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
#if defined(TICK_DURING_STANDBY) && (defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS))
else if (event == EV_sleep_tick) {
+ if (ticks_since_on < 255) ticks_since_on ++;
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ // reset to manual memory level when timer expires
+ if (cfg.manual_memory &&
+ (arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) {
+ manual_memory_restore();
+ }
+ #endif
#if defined(USE_INDICATOR_LED)
- indicator_led_update(indicator_led_mode >> 2, arg);
+ indicator_led_update(cfg.indicator_led_mode >> 2, arg);
#elif defined(USE_AUX_RGB_LEDS)
- rgb_led_update(rgb_led_lockout_mode, arg);
+ rgb_led_update(cfg.rgb_led_lockout_mode, arg);
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
@@ -92,19 +85,22 @@ uint8_t lockout_state(Event event, uint16_t arg) {
else if (event == EV_3clicks) {
blink_once();
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
// 4 clicks: exit and turn on
else if (event == EV_4clicks) {
- #ifdef USE_MANUAL_MEMORY
- // FIXME: memory timer is totally ignored
- if (manual_memory)
- set_state(steady_state, manual_memory);
+ #if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER)
+ // this clause probably isn't used by any configs any more
+ // but is included just in case someone configures it this way
+ if (cfg.manual_memory)
+ set_state(steady_state, cfg.manual_memory);
else
#endif
set_state(steady_state, memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
// 4 clicks, but hold last: exit and start at floor
else if (event == EV_click4_hold) {
//blink_once();
@@ -113,34 +109,38 @@ uint8_t lockout_state(Event event, uint16_t arg) {
current_event = 0;
// ... and back to ramp mode
set_state(steady_state, 1);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
// 5 clicks: exit and turn on at ceiling level
else if (event == EV_5clicks) {
set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
- ////////// Every action below here is blocked in the simple UI //////////
- #ifdef USE_SIMPLE_UI
- if (simple_ui_active) {
- return EVENT_NOT_HANDLED;
+ #if NUM_CHANNEL_MODES > 1
+ // 3H: next channel mode
+ else if (event == EV_click3_hold) {
+ if (0 == (arg % TICKS_PER_SECOND)) {
+ // pretend the user clicked 3 times to change channels
+ return channel_mode_state(EV_3clicks, 0);
+ }
}
#endif
- #ifdef USE_AUTOLOCK
- // 10H: configure the autolock option
- else if (event == EV_click10_hold) {
- push_state(autolock_config_state, 0);
- return MISCHIEF_MANAGED;
+ ////////// Every action below here is blocked in the (non-Extended) Simple UI //////////
+
+ #if defined(USE_SIMPLE_UI) && !defined(USE_EXTENDED_SIMPLE_UI)
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
}
- #endif
+ #endif // if simple UI but not extended simple UI
#if defined(USE_INDICATOR_LED)
// 7 clicks: rotate through indicator LED modes (lockout mode)
else if (event == EV_7clicks) {
#if defined(USE_INDICATOR_LED)
- uint8_t mode = indicator_led_mode >> 2;
+ uint8_t mode = cfg.indicator_led_mode >> 2;
#ifdef TICK_DURING_STANDBY
mode = (mode + 1) & 3;
#else
@@ -149,42 +149,57 @@ uint8_t lockout_state(Event event, uint16_t arg) {
#ifdef INDICATOR_LED_SKIP_LOW
if (mode == 1) { mode ++; }
#endif
- indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03);
+ cfg.indicator_led_mode = (mode << 2) + (cfg.indicator_led_mode & 0x03);
// redundant, sleep tick does the same thing
- //indicator_led_update(indicator_led_mode >> 2, arg);
+ //indicator_led_update(cfg.indicator_led_mode >> 2, arg);
#elif defined(USE_AUX_RGB_LEDS)
#endif
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#elif defined(USE_AUX_RGB_LEDS)
// 7 clicks: change RGB aux LED pattern
else if (event == EV_7clicks) {
- uint8_t mode = (rgb_led_lockout_mode >> 4) + 1;
+ uint8_t mode = (cfg.rgb_led_lockout_mode >> 4) + 1;
mode = mode % RGB_LED_NUM_PATTERNS;
- rgb_led_lockout_mode = (mode << 4) | (rgb_led_lockout_mode & 0x0f);
- rgb_led_update(rgb_led_lockout_mode, 0);
+ cfg.rgb_led_lockout_mode = (mode << 4) | (cfg.rgb_led_lockout_mode & 0x0f);
+ rgb_led_update(cfg.rgb_led_lockout_mode, 0);
save_config();
blink_once();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 7H: change RGB aux LED color
else if (event == EV_click7_hold) {
setting_rgb_mode_now = 1;
if (0 == (arg & 0x3f)) {
- uint8_t mode = (rgb_led_lockout_mode & 0x0f) + 1;
+ uint8_t mode = (cfg.rgb_led_lockout_mode & 0x0f) + 1;
mode = mode % RGB_LED_NUM_COLORS;
- rgb_led_lockout_mode = mode | (rgb_led_lockout_mode & 0xf0);
+ cfg.rgb_led_lockout_mode = mode | (cfg.rgb_led_lockout_mode & 0xf0);
//save_config();
}
- rgb_led_update(rgb_led_lockout_mode, arg);
- return MISCHIEF_MANAGED;
+ rgb_led_update(cfg.rgb_led_lockout_mode, arg);
+ return EVENT_HANDLED;
}
// 7H, release: save new color
else if (event == EV_click7_hold_release) {
setting_rgb_mode_now = 0;
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #if defined(USE_EXTENDED_SIMPLE_UI) && defined(USE_SIMPLE_UI)
+ ////////// Every action below here is blocked in the Extended Simple UI //////////
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif // if extended simple UI
+
+ #ifdef USE_AUTOLOCK
+ // 10H: configure the autolock option
+ else if (event == EV_click10_hold) {
+ push_state(autolock_config_state, 0);
+ return EVENT_HANDLED;
}
#endif
@@ -194,7 +209,7 @@ uint8_t lockout_state(Event event, uint16_t arg) {
#ifdef USE_AUTOLOCK
// set the auto-lock timer to N minutes, where N is the number of clicks
void autolock_config_save(uint8_t step, uint8_t value) {
- autolock_time = value;
+ cfg.autolock_time = value;
}
uint8_t autolock_config_state(Event event, uint16_t arg) {
@@ -202,6 +217,3 @@ uint8_t autolock_config_state(Event event, uint16_t arg) {
}
#endif // #ifdef USE_AUTOLOCK
-
-#endif
-
diff --git a/spaghetti-monster/anduril/lockout-mode.h b/spaghetti-monster/anduril/lockout-mode.h
index 1c9c081..c2703a0 100644
--- a/spaghetti-monster/anduril/lockout-mode.h
+++ b/spaghetti-monster/anduril/lockout-mode.h
@@ -1,24 +1,8 @@
-/*
- * lockout-mode.h: Lockout mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// lockout-mode.h: Lockout mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef LOCKOUT_MODE_H
-#define LOCKOUT_MODE_H
+#pragma once
// soft lockout
uint8_t lockout_state(Event event, uint16_t arg);
@@ -27,9 +11,6 @@ uint8_t lockout_state(Event event, uint16_t arg);
#ifndef DEFAULT_AUTOLOCK_TIME
#define DEFAULT_AUTOLOCK_TIME 0 // autolock time in minutes, 0 = disabled
#endif
-uint8_t autolock_time = DEFAULT_AUTOLOCK_TIME;
uint8_t autolock_config_state(Event event, uint16_t arg);
#endif
-
-#endif
diff --git a/spaghetti-monster/anduril/misc.c b/spaghetti-monster/anduril/misc.c
index 9c7f0dd..1b92d6f 100644
--- a/spaghetti-monster/anduril/misc.c
+++ b/spaghetti-monster/anduril/misc.c
@@ -1,24 +1,8 @@
-/*
- * misc.c: Misc extra functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// misc.c: Misc extra functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef MISC_C
-#define MISC_C
+#pragma once
#include "misc.h"
@@ -38,12 +22,6 @@ void blink_confirm(uint8_t num) {
// make a short, visible pulse
// (either brighter or darker, depending on current brightness)
-#ifndef BLINK_ONCE_TIME
-#define BLINK_ONCE_TIME 10
-#endif
-#ifndef BLINK_BRIGHTNESS
-#define BLINK_BRIGHTNESS (MAX_LEVEL/6)
-#endif
void blink_once() {
uint8_t brightness = actual_level;
uint8_t bump = brightness + BLINK_BRIGHTNESS;
@@ -62,6 +40,3 @@ void blip() {
set_level(temp);
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/misc.h b/spaghetti-monster/anduril/misc.h
index 5febbc7..0f2992a 100644
--- a/spaghetti-monster/anduril/misc.h
+++ b/spaghetti-monster/anduril/misc.h
@@ -1,28 +1,10 @@
-/*
- * misc.h: Misc extra functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// misc.h: Misc extra functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef MISC_H
-#define MISC_H
+#pragma once
//void blink_confirm(uint8_t num); // no longer used
void blink_once();
void blip();
-
-#endif
diff --git a/spaghetti-monster/anduril/momentary-mode.c b/spaghetti-monster/anduril/momentary-mode.c
index 08879a1..a765142 100644
--- a/spaghetti-monster/anduril/momentary-mode.c
+++ b/spaghetti-monster/anduril/momentary-mode.c
@@ -1,24 +1,8 @@
-/*
- * momentary-mode.c: Momentary mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// momentary-mode.c: Momentary mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef MOMENTARY_MODE_C
-#define MOMENTARY_MODE_C
+#pragma once
#include "momentary-mode.h"
@@ -38,14 +22,14 @@ uint8_t momentary_state(Event event, uint16_t arg) {
if (momentary_mode == 0) {
set_level(memorized_level);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// button was released
else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
momentary_active = 0;
set_level(0);
//go_to_standby = 1; // sleep while light is off
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// Sleep, dammit! (but wait a few seconds first)
@@ -75,12 +59,9 @@ uint8_t momentary_state(Event event, uint16_t arg) {
#ifdef USE_STROBE_STATE
}
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/momentary-mode.h b/spaghetti-monster/anduril/momentary-mode.h
index c5ccf0f..d774801 100644
--- a/spaghetti-monster/anduril/momentary-mode.h
+++ b/spaghetti-monster/anduril/momentary-mode.h
@@ -1,29 +1,11 @@
-/*
- * momentary-mode.h: Momentary mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// momentary-mode.h: Momentary mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef MOMENTARY_MODE_H
-#define MOMENTARY_MODE_H
+#pragma once
// momentary / signalling mode
uint8_t momentary_state(Event event, uint16_t arg);
uint8_t momentary_mode = 0; // 0 = ramping, 1 = strobe
uint8_t momentary_active = 0; // boolean, true if active *right now*
-
-#endif
diff --git a/spaghetti-monster/anduril/off-mode.c b/spaghetti-monster/anduril/off-mode.c
index 6781ade..0a381b7 100644
--- a/spaghetti-monster/anduril/off-mode.c
+++ b/spaghetti-monster/anduril/off-mode.c
@@ -1,24 +1,8 @@
-/*
- * off-mode.c: "Off" mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef OFF_MODE_C
-#define OFF_MODE_C
+// off-mode.c: "Off" mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include "off-mode.h"
@@ -26,16 +10,31 @@
#include "sunset-timer.h"
#endif
+// set level smooth maybe
+void off_state_set_level(uint8_t level);
+
+
uint8_t off_state(Event event, uint16_t arg) {
// turn emitter off when entering state
if (event == EV_enter_state) {
- set_level(0);
+ // turn off
+ off_state_set_level(0);
+ #ifdef USE_SMOOTH_STEPS
+ // don't go to sleep while animating
+ arg |= smooth_steps_in_progress;
+ #endif
+ ticks_since_on = 0;
+ #if NUM_CHANNEL_MODES > 1
+ // reset to ramp mode's channel when light turns off
+ channel_mode = cfg.channel_mode;
+ #endif
#ifdef USE_INDICATOR_LED
// redundant, sleep tick does the same thing
- //indicator_led_update(indicator_led_mode & 0x03, 0);
+ //indicator_led_update(cfg.indicator_led_mode & 0x03, 0);
#elif defined(USE_AUX_RGB_LEDS)
- rgb_led_update(rgb_led_off_mode, 0);
+ // redundant, sleep tick does the same thing
+ //rgb_led_update(cfg.rgb_led_off_mode, 0);
#endif
#ifdef USE_SUNSET_TIMER
sunset_timer = 0; // needs a reset in case previous timer was aborted
@@ -43,57 +42,64 @@ uint8_t off_state(Event event, uint16_t arg) {
// sleep while off (lower power use)
// (unless delay requested; give the ADC some time to catch up)
if (! arg) { go_to_standby = 1; }
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
// go back to sleep eventually if we got bumped but didn't leave "off" state
else if (event == EV_tick) {
- if (arg > HOLD_TIMEOUT) {
+ if (arg > HOLD_TIMEOUT
+ #ifdef USE_SMOOTH_STEPS
+ && (! smooth_steps_in_progress)
+ #endif
+ ) {
go_to_standby = 1;
#ifdef USE_INDICATOR_LED
// redundant, sleep tick does the same thing
- //indicator_led_update(indicator_led_mode & 0x03, arg);
+ //indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
#elif defined(USE_AUX_RGB_LEDS)
- rgb_led_update(rgb_led_off_mode, arg);
+ // redundant, sleep tick does the same thing
+ //rgb_led_update(cfg.rgb_led_off_mode, arg);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
#if defined(TICK_DURING_STANDBY)
// blink the indicator LED, maybe
else if (event == EV_sleep_tick) {
+ if (ticks_since_on < 255) ticks_since_on ++;
#ifdef USE_MANUAL_MEMORY_TIMER
// reset to manual memory level when timer expires
- if (manual_memory &&
- (arg >= (manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) {
- memorized_level = manual_memory;
- #ifdef USE_TINT_RAMPING
- tint = manual_memory_tint;
- #endif
+ if (cfg.manual_memory &&
+ (arg >= (cfg.manual_memory_timer * SLEEP_TICKS_PER_MINUTE))) {
+ manual_memory_restore();
}
#endif
#ifdef USE_INDICATOR_LED
- indicator_led_update(indicator_led_mode & 0x03, arg);
+ indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
#elif defined(USE_AUX_RGB_LEDS)
- rgb_led_update(rgb_led_off_mode, arg);
+ rgb_led_update(cfg.rgb_led_off_mode, arg);
#endif
#ifdef USE_AUTOLOCK
// lock the light after being off for N minutes
- uint16_t ticks = autolock_time * SLEEP_TICKS_PER_MINUTE;
- if ((autolock_time > 0) && (arg > ticks)) {
+ uint16_t ticks = cfg.autolock_time * SLEEP_TICKS_PER_MINUTE;
+ if ((cfg.autolock_time > 0) && (arg > ticks)) {
set_state(lockout_state, 0);
}
#endif // ifdef USE_AUTOLOCK
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
+
#if (B_TIMING_ON == B_PRESS_T)
// hold (initially): go to lowest level (floor), but allow abort for regular click
else if (event == EV_click1_press) {
- set_level(nearest_level(1));
- return MISCHIEF_MANAGED;
+ off_state_set_level(nearest_level(1));
+ return EVENT_HANDLED;
}
#endif // B_TIMING_ON == B_PRESS_T
+
// hold: go to lowest level
else if (event == EV_click1_hold) {
#if (B_TIMING_ON == B_PRESS_T)
@@ -104,64 +110,65 @@ uint8_t off_state(Event event, uint16_t arg) {
} else
#endif
#else // B_RELEASE_T or B_TIMEOUT_T
- set_level(nearest_level(1));
+ off_state_set_level(nearest_level(1));
#endif
#ifdef USE_RAMP_AFTER_MOON_CONFIG
- if (dont_ramp_after_moon) {
- return MISCHIEF_MANAGED;
+ if (cfg.dont_ramp_after_moon) {
+ return EVENT_HANDLED;
}
#endif
// don't start ramping immediately;
// give the user time to release at moon level
//if (arg >= HOLD_TIMEOUT) { // smaller
- if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent
+ if (arg >= (!cfg.ramp_style) * HOLD_TIMEOUT) { // more consistent
set_state(steady_state, 1);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
// hold, release quickly: go to lowest level (floor)
else if (event == EV_click1_hold_release) {
set_state(steady_state, 1);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
#if (B_TIMING_ON != B_TIMEOUT_T)
// 1 click (before timeout): go to memorized level, but allow abort for double click
else if (event == EV_click1_release) {
#if defined(USE_MANUAL_MEMORY) && !defined(USE_MANUAL_MEMORY_TIMER)
// this clause probably isn't used by any configs any more
// but is included just in case someone configures it this way
- if (manual_memory) {
- memorized_level = manual_memory;
- #ifdef USE_TINT_RAMPING
- tint = manual_memory_tint;
- #endif
+ if (cfg.manual_memory) {
+ manual_memory_restore();
}
#endif
- set_level(nearest_level(memorized_level));
- return MISCHIEF_MANAGED;
+ off_state_set_level(nearest_level(memorized_level));
+ return EVENT_HANDLED;
}
#endif // if (B_TIMING_ON != B_TIMEOUT_T)
+
// 1 click: regular mode
else if (event == EV_1click) {
#if (B_TIMING_ON != B_TIMEOUT_T)
- // brightness was already set; reuse previous value
- set_state(steady_state, actual_level);
+ set_state(steady_state, memorized_level);
#else
// FIXME: B_TIMEOUT_T breaks manual_memory and manual_memory_timer
// (need to duplicate manual mem logic here, probably)
set_state(steady_state, memorized_level);
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
// click, hold: momentary at ceiling or turbo
else if (event == EV_click2_hold) {
+ ticks_since_on = 0; // momentary turbo is definitely "on"
uint8_t turbo_level; // how bright is "turbo"?
#if defined(USE_2C_STYLE_CONFIG) // user can choose 2C behavior
- uint8_t style_2c = ramp_2c_style;
+ uint8_t style_2c = cfg.ramp_2c_style;
#ifdef USE_SIMPLE_UI
// simple UI has its own turbo config
- if (simple_ui_active) style_2c = ramp_2c_style_simple;
+ if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple;
#endif
// 0 = ceiling
// 1+ = full power
@@ -171,118 +178,109 @@ uint8_t off_state(Event event, uint16_t arg) {
// simple UI: ceiling
// full UI: full power
#ifdef USE_SIMPLE_UI
- if (simple_ui_active) turbo_level = nearest_level(MAX_LEVEL);
+ if (cfg.simple_ui_active) turbo_level = nearest_level(MAX_LEVEL);
else
#endif
turbo_level = MAX_LEVEL;
#endif
- set_level(turbo_level);
- return MISCHIEF_MANAGED;
+ off_state_set_level(turbo_level);
+ return EVENT_HANDLED;
}
else if (event == EV_click2_hold_release) {
- set_level(0);
- return MISCHIEF_MANAGED;
+ off_state_set_level(0);
+ return EVENT_HANDLED;
}
+
// 2 clicks: highest mode (ceiling)
else if (event == EV_2clicks) {
set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+
// 3 clicks (initial press): off, to prep for later events
else if (event == EV_click3_press) {
- set_level(0);
- return MISCHIEF_MANAGED;
+ #ifdef USE_SMOOTH_STEPS
+ // immediately cancel any animations in progress
+ smooth_steps_in_progress = 0;
+ #endif
+ off_state_set_level(0);
+ return EVENT_HANDLED;
}
+
#ifdef USE_BATTCHECK
// 3 clicks: battcheck mode / blinky mode group 1
else if (event == EV_3clicks) {
set_state(battcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
+
#ifdef USE_LOCKOUT_MODE
// 4 clicks: soft lockout
else if (event == EV_4clicks) {
blink_once();
set_state(lockout_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
+
#if defined(USE_FACTORY_RESET) && defined(USE_SOFT_FACTORY_RESET)
// 13 clicks and hold the last click: invoke factory reset (reboot)
else if (event == EV_click13_hold) {
reboot();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
+
#ifdef USE_VERSION_CHECK
// 15+ clicks: show the version number
else if (event == EV_15clicks) {
set_state(version_check_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#ifdef USE_SIMPLE_UI
// 10 clicks, but hold last click: turn simple UI off (or configure it)
else if ((event == EV_click10_hold) && (!arg)) {
- if (simple_ui_active) { // turn off simple UI
+ if (cfg.simple_ui_active) { // turn off simple UI
blink_once();
- simple_ui_active = 0;
+ cfg.simple_ui_active = 0;
save_config();
}
else { // configure simple UI ramp
push_state(simple_ui_config_state, 0);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
- ////////// Every action below here is blocked in the simple UI //////////
- if (simple_ui_active) {
+ ////////// Every action below here is blocked in the (non-Extended) Simple UI //////////
+
+ #ifndef USE_EXTENDED_SIMPLE_UI
+ if (cfg.simple_ui_active) {
return EVENT_NOT_HANDLED;
}
- // 10 clicks: enable simple UI
- else if (event == EV_10clicks) {
- blink_once();
- simple_ui_active = 1;
- save_config();
- return MISCHIEF_MANAGED;
- }
- #endif
+ #endif // ifndef USE_EXTENDED_SIMPLE_UI
+ #endif // ifdef USE_SIMPLE_UI
// click, click, long-click: strobe mode
#ifdef USE_STROBE_STATE
else if (event == EV_click3_hold) {
set_state(strobe_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#elif defined(USE_BORING_STROBE_STATE)
else if (event == EV_click3_hold) {
set_state(boring_strobe_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
- #ifdef USE_MOMENTARY_MODE
- // 5 clicks: momentary mode
- else if (event == EV_5clicks) {
- blink_once();
- set_state(momentary_state, 0);
- return MISCHIEF_MANAGED;
- }
- #endif
- #ifdef USE_TACTICAL_MODE
- // 6 clicks: tactical mode
- else if (event == EV_6clicks) {
- blink_once();
- set_state(tactical_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
+
#ifdef USE_INDICATOR_LED
// 7 clicks: change indicator LED mode
else if (event == EV_7clicks) {
- uint8_t mode = (indicator_led_mode & 3) + 1;
+ uint8_t mode = (cfg.indicator_led_mode & 3) + 1;
#ifdef TICK_DURING_STANDBY
mode = mode & 3;
#else
@@ -291,52 +289,96 @@ uint8_t off_state(Event event, uint16_t arg) {
#ifdef INDICATOR_LED_SKIP_LOW
if (mode == 1) { mode ++; }
#endif
- indicator_led_mode = (indicator_led_mode & 0b11111100) | mode;
+ cfg.indicator_led_mode = (cfg.indicator_led_mode & 0b11111100) | mode;
// redundant, sleep tick does the same thing
- //indicator_led_update(indicator_led_mode & 0x03, arg);
+ //indicator_led_update(cfg.indicator_led_mode & 0x03, arg);
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#elif defined(USE_AUX_RGB_LEDS)
// 7 clicks: change RGB aux LED pattern
else if (event == EV_7clicks) {
- uint8_t mode = (rgb_led_off_mode >> 4) + 1;
+ uint8_t mode = (cfg.rgb_led_off_mode >> 4) + 1;
mode = mode % RGB_LED_NUM_PATTERNS;
- rgb_led_off_mode = (mode << 4) | (rgb_led_off_mode & 0x0f);
- rgb_led_update(rgb_led_off_mode, 0);
+ cfg.rgb_led_off_mode = (mode << 4) | (cfg.rgb_led_off_mode & 0x0f);
+ rgb_led_update(cfg.rgb_led_off_mode, 0);
save_config();
blink_once();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 7 clicks (hold last): change RGB aux LED color
else if (event == EV_click7_hold) {
setting_rgb_mode_now = 1;
if (0 == (arg & 0x3f)) {
- uint8_t mode = (rgb_led_off_mode & 0x0f) + 1;
+ uint8_t mode = (cfg.rgb_led_off_mode & 0x0f) + 1;
mode = mode % RGB_LED_NUM_COLORS;
- rgb_led_off_mode = mode | (rgb_led_off_mode & 0xf0);
+ cfg.rgb_led_off_mode = mode | (cfg.rgb_led_off_mode & 0xf0);
//save_config();
}
- rgb_led_update(rgb_led_off_mode, arg);
- return MISCHIEF_MANAGED;
+ rgb_led_update(cfg.rgb_led_off_mode, arg);
+ return EVENT_HANDLED;
}
else if (event == EV_click7_hold_release) {
setting_rgb_mode_now = 0;
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif // end 7 clicks
+ ////////// Every action below here is blocked in the Extended Simple UI //////////
+
+ #ifdef USE_SIMPLE_UI
+ #ifdef USE_EXTENDED_SIMPLE_UI
+ if (cfg.simple_ui_active) {
+ return EVENT_NOT_HANDLED;
+ }
+ #endif // ifdef USE_EXTENDED_SIMPLE_UI
+
+ // 10 clicks: enable simple UI
+ else if (event == EV_10clicks) {
+ blink_once();
+ cfg.simple_ui_active = 1;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #endif // ifdef USE_SIMPLE_UI
+
+ #ifdef USE_MOMENTARY_MODE
+ // 5 clicks: momentary mode
+ else if (event == EV_5clicks) {
+ blink_once();
+ set_state(momentary_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
+ #ifdef USE_TACTICAL_MODE
+ // 6 clicks: tactical mode
+ else if (event == EV_6clicks) {
+ blink_once();
+ set_state(tactical_state, 0);
+ return EVENT_HANDLED;
+ }
+ #endif
+
#ifdef USE_GLOBALS_CONFIG
// 9 clicks, but hold last click: configure misc global settings
else if ((event == EV_click9_hold) && (!arg)) {
push_state(globals_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
+
return EVENT_NOT_HANDLED;
}
-#endif
+void off_state_set_level(uint8_t level) {
+ // this pattern gets used a few times, so reduce duplication
+ #ifdef USE_SMOOTH_STEPS
+ if (cfg.smooth_steps_style) set_level_smooth(level, 8);
+ else
+ #endif
+ set_level(level);
+}
diff --git a/spaghetti-monster/anduril/off-mode.h b/spaghetti-monster/anduril/off-mode.h
index 776173c..d07fff1 100644
--- a/spaghetti-monster/anduril/off-mode.h
+++ b/spaghetti-monster/anduril/off-mode.h
@@ -1,27 +1,12 @@
-/*
- * off-mode.h: "Off" mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// off-mode.h: "Off" mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef OFF_MODE_H
-#define OFF_MODE_H
+#pragma once
+
+// was the light in an "on" mode within the past second or so?
+uint8_t ticks_since_on = 0;
// when the light is "off" or in standby
uint8_t off_state(Event event, uint16_t arg);
-
-#endif
diff --git a/spaghetti-monster/anduril/ramp-mode-fsm.h b/spaghetti-monster/anduril/ramp-mode-fsm.h
index 1a062e9..edfd6db 100644
--- a/spaghetti-monster/anduril/ramp-mode-fsm.h
+++ b/spaghetti-monster/anduril/ramp-mode-fsm.h
@@ -1,24 +1,8 @@
-/*
- * ramp-mode-fsm.h: FSM config for ramping functions in Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RAMP_MODE_FSM_H
-#define RAMP_MODE_FSM_H
+// ramp-mode-fsm.h: FSM config for ramping functions in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
// enable FSM's ramping features
#define USE_RAMPING
@@ -47,9 +31,8 @@
#endif
// include an extra config mode for random stuff which doesn't fit elsewhere
-#if defined(USE_TINT_RAMPING) || defined(USE_JUMP_START)
+#if defined(USE_JUMP_START) || \
+ (defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING))
#define USE_GLOBALS_CONFIG
#endif
-
-#endif
diff --git a/spaghetti-monster/anduril/ramp-mode.c b/spaghetti-monster/anduril/ramp-mode.c
index 6e8ba88..4611b4f 100644
--- a/spaghetti-monster/anduril/ramp-mode.c
+++ b/spaghetti-monster/anduril/ramp-mode.c
@@ -1,24 +1,8 @@
-/*
- * ramp-mode.c: Ramping functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RAMP_MODE_C
-#define RAMP_MODE_C
+// ramp-mode.c: Ramping functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include "ramp-mode.h"
@@ -26,6 +10,11 @@
#include "sunset-timer.h"
#endif
+#ifdef USE_SMOOTH_STEPS
+#include "smooth-steps.h"
+#endif
+
+
uint8_t steady_state(Event event, uint16_t arg) {
static int8_t ramp_direction = 1;
#if (B_TIMING_OFF == B_RELEASE_T)
@@ -34,6 +23,10 @@ uint8_t steady_state(Event event, uint16_t arg) {
static uint8_t level_before_off = 0;
#endif
+ #if NUM_CHANNEL_MODES > 1
+ channel_mode = cfg.channel_mode;
+ #endif
+
// make sure ramp globals are correct...
// ... but they already are; no need to do it here
//ramp_update_config();
@@ -42,16 +35,16 @@ uint8_t steady_state(Event event, uint16_t arg) {
uint8_t mode_min = ramp_floor;
uint8_t mode_max = ramp_ceil;
uint8_t step_size;
- if (ramp_style) { step_size = ramp_discrete_step_size; }
+ if (cfg.ramp_style) { step_size = ramp_discrete_step_size; }
else { step_size = 1; }
// how bright is "turbo"?
uint8_t turbo_level;
#if defined(USE_2C_STYLE_CONFIG) // user can choose 2C behavior
- uint8_t style_2c = ramp_2c_style;
+ uint8_t style_2c = cfg.ramp_2c_style;
#ifdef USE_SIMPLE_UI
// simple UI has its own turbo config
- if (simple_ui_active) style_2c = ramp_2c_style_simple;
+ if (cfg.simple_ui_active) style_2c = cfg.ramp_2c_style_simple;
#endif
// 0 = no turbo
// 1 = Anduril 1 direct to turbo
@@ -66,7 +59,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
// simple UI: to/from ceiling
// full UI: to/from turbo (Anduril1 behavior)
#ifdef USE_SIMPLE_UI
- if (simple_ui_active) turbo_level = mode_max;
+ if (cfg.simple_ui_active) turbo_level = mode_max;
else
#endif
turbo_level = MAX_LEVEL;
@@ -76,7 +69,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
// or to/from turbo if mem >= ceiling
if ((memorized_level < mode_max)
#ifdef USE_SIMPLE_UI
- || simple_ui_active
+ || cfg.simple_ui_active
#endif
) { turbo_level = mode_max; }
else { turbo_level = MAX_LEVEL; }
@@ -84,18 +77,17 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef USE_SUNSET_TIMER
// handle the shutoff timer first
- static uint8_t timer_orig_level = 0;
uint8_t sunset_active = sunset_timer; // save for comparison
// clock tick
sunset_timer_state(event, arg);
// if the timer was just turned on
if (sunset_timer && (! sunset_active)) {
- timer_orig_level = actual_level;
+ sunset_timer_orig_level = actual_level;
}
// if the timer just expired, shut off
else if (sunset_active && (! sunset_timer)) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif // ifdef USE_SUNSET_TIMER
@@ -115,25 +107,25 @@ uint8_t steady_state(Event event, uint16_t arg) {
arg = nearest_level(arg);
set_level_and_therm_target(arg);
ramp_direction = 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if (B_TIMING_OFF == B_RELEASE_T)
// 1 click (early): off, if configured for early response
else if (event == EV_click1_release) {
level_before_off = actual_level;
set_level_and_therm_target(0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks (early): abort turning off, if configured for early response
else if (event == EV_click2_press) {
set_level_and_therm_target(level_before_off);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif // if (B_TIMING_OFF == B_RELEASE_T)
// 1 click: off
else if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: go to/from highest level
else if (event == EV_2clicks) {
@@ -144,9 +136,9 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level_and_therm_target(memorized_level);
}
#ifdef USE_SUNSET_TIMER
- timer_orig_level = actual_level;
+ reset_sunset_timer();
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_LOCKOUT_MODE
@@ -154,22 +146,28 @@ uint8_t steady_state(Event event, uint16_t arg) {
else if (event == EV_4clicks) {
set_level(0);
set_state(lockout_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// hold: change brightness (brighter, dimmer)
// click, hold: change brightness (dimmer)
else if ((event == EV_click1_hold) || (event == EV_click2_hold)) {
- // ramp slower in discrete mode
- if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
- return MISCHIEF_MANAGED;
- }
+ // ramp infrequently in stepped mode
+ if (cfg.ramp_style && (arg % HOLD_TIMEOUT != 0))
+ return EVENT_HANDLED;
#ifdef USE_RAMP_SPEED_CONFIG
- // ramp slower if user configured things that way
- if ((! ramp_style) && (arg % ramp_speed)) {
- return MISCHIEF_MANAGED;
- }
+ // ramp slower if user configured things that way
+ if ((! cfg.ramp_style) && (arg % ramp_speed))
+ return EVENT_HANDLED;
+ #endif
+ #ifdef USE_SMOOTH_STEPS
+ // if a brightness transition is already happening,
+ // don't interrupt it
+ // (like 2C for full turbo then 1H to smooth ramp down
+ // ... without this clause, it flickers because it trips
+ // the "blink at ramp ceil" clause below, over and over)
+ if (smooth_steps_in_progress) return EVENT_HANDLED;
#endif
// fix ramp direction on first frame if necessary
if (!arg) {
@@ -208,6 +206,11 @@ uint8_t steady_state(Event event, uint16_t arg) {
+ (step_size * ramp_direction));
#if defined(BLINK_AT_RAMP_CEIL) || defined(BLINK_AT_RAMP_MIDDLE)
// only blink once for each threshold
+ // FIXME: blinks at beginning of smooth_steps animation instead
+ // of the end, so it should blink when actual_level reaches a
+ // threshold, instead of when memorized_level does
+ // (one possible fix is to just remove mid-ramp blinks entirely,
+ // and just blink only when it hits the top while going up)
if ((memorized_level != actual_level) && (
0 // for easier syntax below
#ifdef BLINK_AT_RAMP_MIDDLE_1
@@ -217,6 +220,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
|| (memorized_level == BLINK_AT_RAMP_MIDDLE_2)
#endif
#ifdef BLINK_AT_RAMP_CEIL
+ // FIXME: only blink at top when going up, not down
|| (memorized_level == mode_max)
#endif
#ifdef BLINK_AT_RAMP_FLOOR
@@ -227,13 +231,13 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
#endif
#if defined(BLINK_AT_STEPS)
- uint8_t foo = ramp_style;
- ramp_style = 1;
+ uint8_t foo = cfg.ramp_style;
+ cfg.ramp_style = 1;
uint8_t nearest = nearest_level((int16_t)actual_level);
- ramp_style = foo;
+ cfg.ramp_style = foo;
// only blink once for each threshold
if ((memorized_level != actual_level) &&
- (ramp_style == 0) &&
+ (cfg.ramp_style == 0) &&
(memorized_level == nearest)
)
{
@@ -242,9 +246,9 @@ uint8_t steady_state(Event event, uint16_t arg) {
#endif
set_level_and_therm_target(memorized_level);
#ifdef USE_SUNSET_TIMER
- timer_orig_level = actual_level;
+ reset_sunset_timer();
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// reverse ramp direction on hold release
else if ((event == EV_click1_hold_release)
@@ -253,7 +257,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef START_AT_MEMORIZED_LEVEL
save_config_wl();
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
else if (event == EV_tick) {
@@ -263,8 +267,12 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef USE_SUNSET_TIMER
// reduce output if shutoff timer is active
if (sunset_timer) {
- uint8_t dimmed_level = timer_orig_level * (sunset_timer-1) / sunset_timer_peak;
+ uint8_t dimmed_level = sunset_timer_orig_level * sunset_timer / sunset_timer_peak;
+ uint8_t dimmed_level_next = sunset_timer_orig_level * (sunset_timer-1) / sunset_timer_peak;
+ uint8_t dimmed_level_delta = dimmed_level - dimmed_level_next;
+ dimmed_level -= dimmed_level_delta * (sunset_ticks/TICKS_PER_SECOND) / 60;
if (dimmed_level < 1) dimmed_level = 1;
+
#ifdef USE_SET_LEVEL_GRADUALLY
set_level_gradually(dimmed_level);
target_level = dimmed_level;
@@ -304,7 +312,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
}
#endif // ifdef USE_SET_LEVEL_GRADUALLY
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_THERMAL_REGULATION
@@ -334,7 +342,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(stepdown);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// underheating: increase slowly if we're lower than the target
// (proportional to how low we are)
@@ -353,7 +361,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(stepup);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_SET_LEVEL_GRADUALLY
// temperature is within target window
@@ -364,7 +372,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
gradual_target = actual_level + 1;
else if (gradual_target < actual_level)
gradual_target = actual_level - 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif // ifdef USE_SET_LEVEL_GRADUALLY
#endif // ifdef USE_THERMAL_REGULATION
@@ -372,14 +380,33 @@ uint8_t steady_state(Event event, uint16_t arg) {
////////// Every action below here is blocked in the simple UI //////////
// That is, unless we specifically want to enable 3C for smooth/stepped selection in Simple UI
#if defined(USE_SIMPLE_UI) && !defined(USE_SIMPLE_UI_RAMPING_TOGGLE)
- if (simple_ui_active) {
+ if (cfg.simple_ui_active) {
return EVENT_NOT_HANDLED;
}
#endif
// 3 clicks: toggle smooth vs discrete ramping
- else if (event == EV_3clicks) {
- ramp_style = !ramp_style;
+ // (and/or 6 clicks when there are multiple channel modes)
+ // (handle 3C here anyway, when all but 1 mode is disabled)
+ else if ((event == EV_3clicks)
+ #if NUM_CHANNEL_MODES > 1
+ || (event == EV_6clicks)
+ ) {
+ // detect if > 1 channel mode is enabled,
+ // and if so, fall through so channel mode code can handle it
+ // otherwise, change the ramp style
+ if (event == EV_3clicks) {
+ uint8_t enabled = 0;
+ for (uint8_t m=0; m<NUM_CHANNEL_MODES; m++)
+ enabled += channel_mode_enabled(m);
+ if (enabled > 1)
+ return EVENT_NOT_HANDLED;
+ }
+ #else
+ ) {
+ #endif
+
+ cfg.ramp_style = !cfg.ramp_style;
save_config();
#ifdef START_AT_MEMORIZED_LEVEL
save_config_wl();
@@ -388,21 +415,31 @@ uint8_t steady_state(Event event, uint16_t arg) {
memorized_level = nearest_level(actual_level);
set_level_and_therm_target(memorized_level);
#ifdef USE_SUNSET_TIMER
- timer_orig_level = actual_level;
+ reset_sunset_timer();
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// If we allowed 3C in Simple UI, now block further actions
#if defined(USE_SIMPLE_UI) && defined(USE_SIMPLE_UI_RAMPING_TOGGLE)
- if (simple_ui_active) {
+ if (cfg.simple_ui_active) {
return EVENT_NOT_HANDLED;
}
#endif
- #ifndef USE_TINT_RAMPING
// 3H: momentary turbo (on lights with no tint ramping)
- else if (event == EV_click3_hold) {
+ // (or 4H when tint ramping is available)
+ else if ((event == EV_click3_hold)
+ #ifdef USE_CHANNEL_MODE_ARGS
+ || (event == EV_click4_hold)
+ #endif
+ ) {
+ #ifdef USE_CHANNEL_MODE_ARGS
+ // ramp tint if tint exists in this mode
+ if ((event == EV_click3_hold)
+ && (channel_has_args(channel_mode)))
+ return EVENT_NOT_HANDLED;
+ #endif
if (! arg) { // first frame only, to allow thermal regulation to work
#ifdef USE_2C_STYLE_CONFIG
uint8_t tl = style_2c ? MAX_LEVEL : turbo_level;
@@ -411,20 +448,29 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level_and_therm_target(turbo_level);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
- else if (event == EV_click3_hold_release) {
+ else if ((event == EV_click3_hold_release)
+ #ifdef USE_CHANNEL_MODE_ARGS
+ || (event == EV_click4_hold_release)
+ #endif
+ ) {
+ #ifdef USE_CHANNEL_MODE_ARGS
+ // ramp tint if tint exists in this mode
+ if ((event == EV_click3_hold_release)
+ && (channel_has_args(channel_mode)))
+ return EVENT_NOT_HANDLED;
+ #endif
set_level_and_therm_target(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
- #endif // ifndef USE_TINT_RAMPING
#ifdef USE_MOMENTARY_MODE
// 5 clicks: shortcut to momentary mode
else if (event == EV_5clicks) {
set_level(0);
set_state(momentary_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
@@ -432,20 +478,17 @@ uint8_t steady_state(Event event, uint16_t arg) {
// 7H: configure this ramp mode
else if (event == EV_click7_hold) {
push_state(ramp_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#ifdef USE_MANUAL_MEMORY
else if (event == EV_10clicks) {
// turn on manual memory and save current brightness
- manual_memory = actual_level;
- #ifdef USE_TINT_RAMPING
- manual_memory_tint = tint; // remember tint too
- #endif
+ manual_memory_save();
save_config();
blink_once();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
else if (event == EV_click10_hold) {
#ifdef USE_RAMP_EXTRAS_CONFIG
@@ -454,12 +497,12 @@ uint8_t steady_state(Event event, uint16_t arg) {
#else // manual mem, but no timer
// turn off manual memory; go back to automatic
if (0 == arg) {
- manual_memory = 0;
+ cfg.manual_memory = 0;
save_config();
blink_once();
}
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif // ifdef USE_MANUAL_MEMORY
@@ -471,7 +514,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
void ramp_config_save(uint8_t step, uint8_t value) {
// 0 = smooth ramp, 1 = stepped ramp, 2 = simple UI's ramp
- uint8_t style = ramp_style;
+ uint8_t style = cfg.ramp_style;
#ifdef USE_SIMPLE_UI
if (current_state == simple_ui_config_state) style = 2;
#endif
@@ -480,7 +523,7 @@ void ramp_config_save(uint8_t step, uint8_t value) {
// simple UI config is weird...
// has some ramp extras after floor/ceil/steps
if (4 == step) {
- ramp_2c_style_simple = value;
+ cfg.ramp_2c_style_simple = value;
}
else
#endif
@@ -493,7 +536,7 @@ void ramp_config_save(uint8_t step, uint8_t value) {
// which option are we configuring?
// TODO? maybe rearrange definitions to avoid the need for this table
// (move all ramp values into a single array?)
- uint8_t *steps[] = { ramp_floors, ramp_ceils, ramp_stepss };
+ uint8_t *steps[] = { cfg.ramp_floors, cfg.ramp_ceils, cfg.ramp_stepss };
uint8_t *option;
option = steps[step-1];
option[style] = value;
@@ -504,7 +547,7 @@ uint8_t ramp_config_state(Event event, uint16_t arg) {
#ifdef USE_RAMP_SPEED_CONFIG
const uint8_t num_config_steps = 3;
#else
- uint8_t num_config_steps = ramp_style + 2;
+ uint8_t num_config_steps = cfg.ramp_style + 2;
#endif
return config_state_base(event, arg,
num_config_steps, ramp_config_save);
@@ -527,22 +570,26 @@ uint8_t simple_ui_config_state(Event event, uint16_t arg) {
#ifdef USE_RAMP_EXTRAS_CONFIG
void ramp_extras_config_save(uint8_t step, uint8_t value) {
// item 1: disable manual memory, go back to automatic
- if (1 == step) { manual_memory = 0; }
+ if (manual_memory_config_step == step) {
+ cfg.manual_memory = 0;
+ }
#ifdef USE_MANUAL_MEMORY_TIMER
// item 2: set manual memory timer duration
// FIXME: should be limited to (65535 / SLEEP_TICKS_PER_MINUTE)
// to avoid overflows or impossibly long timeouts
// (by default, the effective limit is 145, but it allows up to 255)
- else if (2 == step) { manual_memory_timer = value; }
+ else if (manual_memory_timer_config_step == step) {
+ cfg.manual_memory_timer = value;
+ }
#endif
#ifdef USE_RAMP_AFTER_MOON_CONFIG
// item 3: ramp up after hold-from-off for moon?
// 0 = yes, ramp after moon
// 1+ = no, stay at moon
- else if (3 == step) {
- dont_ramp_after_moon = value;
+ else if (dont_ramp_after_moon_config_step == step) {
+ cfg.dont_ramp_after_moon = value;
}
#endif
@@ -550,35 +597,40 @@ void ramp_extras_config_save(uint8_t step, uint8_t value) {
// item 4: Anduril 1 2C turbo, or Anduril 2 2C ceiling?
// 1 = Anduril 1, 2C turbo
// 2+ = Anduril 2, 2C ceiling
- else if (4 == step) {
- ramp_2c_style = value;
+ else if (ramp_2c_style_config_step == step) {
+ cfg.ramp_2c_style = value;
+ }
+ #endif
+
+ #ifdef USE_SMOOTH_STEPS
+ else if (smooth_steps_style_config_step == step) {
+ cfg.smooth_steps_style = value;
}
#endif
}
uint8_t ramp_extras_config_state(Event event, uint16_t arg) {
- return config_state_base(event, arg, 4, ramp_extras_config_save);
+ return config_state_base(event, arg,
+ ramp_extras_config_num_steps - 1,
+ ramp_extras_config_save);
}
#endif
#ifdef USE_GLOBALS_CONFIG
void globals_config_save(uint8_t step, uint8_t value) {
if (0) {}
- #ifdef USE_TINT_RAMPING
- else if (step == 1+tint_style_config_step) {
- tint_style = !(!(value));
- // set tint to middle or edge depending on style being smooth or toggle
- tint = tint_style ? 1 : 127;
- }
+ #if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
+ else if (step == tint_style_config_step) { cfg.tint_ramp_style = value; }
#endif
#ifdef USE_JUMP_START
- else if (step == 1+jump_start_config_step) { jump_start_level = value; }
+ else if (step == jump_start_config_step) { cfg.jump_start_level = value; }
#endif
}
uint8_t globals_config_state(Event event, uint16_t arg) {
- // TODO: set number of steps based on how many configurable options
- return config_state_base(event, arg, globals_config_num_steps, globals_config_save);
+ return config_state_base(event, arg,
+ globals_config_num_steps - 1,
+ globals_config_save);
}
#endif
@@ -594,20 +646,20 @@ uint8_t nearest_level(int16_t target) {
// bounds check
uint8_t mode_min = ramp_floor;
uint8_t mode_max = ramp_ceil;
- uint8_t num_steps = ramp_stepss[1
+ uint8_t num_steps = cfg.ramp_stepss[1
#ifdef USE_SIMPLE_UI
- + simple_ui_active
+ + cfg.simple_ui_active
#endif
];
// special case for 1-step ramp... use halfway point between floor and ceiling
- if (ramp_style && (1 == num_steps)) {
+ if (cfg.ramp_style && (1 == num_steps)) {
uint8_t mid = (mode_max + mode_min) >> 1;
return mid;
}
if (target < mode_min) return mode_min;
if (target > mode_max) return mode_max;
// the rest isn't relevant for smooth ramping
- if (! ramp_style) return target;
+ if (! cfg.ramp_style) return target;
uint8_t ramp_range = mode_max - mode_min;
ramp_discrete_step_size = ramp_range / (num_steps-1);
@@ -625,24 +677,65 @@ uint8_t nearest_level(int16_t target) {
// ensure ramp globals are correct
void ramp_update_config() {
- uint8_t which = ramp_style;
+ uint8_t which = cfg.ramp_style;
#ifdef USE_SIMPLE_UI
- if (simple_ui_active) { which = 2; }
+ if (cfg.simple_ui_active) { which = 2; }
#endif
- ramp_floor = ramp_floors[which];
- ramp_ceil = ramp_ceils[which];
+ ramp_floor = cfg.ramp_floors[which];
+ ramp_ceil = cfg.ramp_ceils[which];
}
-#ifdef USE_THERMAL_REGULATION
+#if defined(USE_THERMAL_REGULATION) || defined(USE_SMOOTH_STEPS)
void set_level_and_therm_target(uint8_t level) {
+ #ifdef USE_THERMAL_REGULATION
target_level = level;
+ #endif
+ #ifdef USE_SMOOTH_STEPS
+ // if adjusting by more than 1 ramp level,
+ // animate the step change (if smooth steps enabled)
+ uint8_t diff = (level > actual_level)
+ ? (level - actual_level) : (actual_level - level);
+ if (smooth_steps_in_progress
+ || (cfg.smooth_steps_style && (diff > 1)))
+ set_level_smooth(level, 4);
+ else
+ #endif
set_level(level);
}
#else
#define set_level_and_therm_target(level) set_level(level)
#endif
+void manual_memory_restore() {
+ memorized_level = cfg.manual_memory;
+ #if NUM_CHANNEL_MODES > 1
+ channel_mode = cfg.channel_mode = cfg.manual_memory_channel_mode;
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ cfg.channel_mode_args[i] = cfg.manual_memory_channel_args[i];
+ #endif
+}
+
+void manual_memory_save() {
+ cfg.manual_memory = actual_level;
+ #if NUM_CHANNEL_MODES > 1
+ cfg.manual_memory_channel_mode = channel_mode;
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ for (uint8_t i=0; i<NUM_CHANNEL_MODES; i++)
+ cfg.manual_memory_channel_args[i] = cfg.channel_mode_args[i];
+ #endif
+}
+#ifdef USE_SUNSET_TIMER
+void reset_sunset_timer() {
+ if (sunset_timer) {
+ sunset_timer_orig_level = actual_level;
+ sunset_timer_peak = sunset_timer;
+ sunset_ticks = 0;
+ }
+}
#endif
diff --git a/spaghetti-monster/anduril/ramp-mode.h b/spaghetti-monster/anduril/ramp-mode.h
index ed76ed5..59c8db0 100644
--- a/spaghetti-monster/anduril/ramp-mode.h
+++ b/spaghetti-monster/anduril/ramp-mode.h
@@ -1,24 +1,8 @@
-/*
- * ramp-mode.h: Ramping functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef RAMP_MODE_H
-#define RAMP_MODE_H
+// ramp-mode.h: Ramping functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#ifndef RAMP_LENGTH
#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file
@@ -72,6 +56,7 @@
#define B_TIMING_OFF B_TIMEOUT_T
#endif
+
// default ramp options if not overridden earlier per-driver
#ifndef RAMP_STYLE
#define RAMP_STYLE 0 // smooth default
@@ -102,6 +87,7 @@
// mile marker(s) partway up the ramp
// default: blink only at border between regulated and FET
#ifdef BLINK_AT_RAMP_MIDDLE
+ // FIXME: remove PWM_CHANNELS, use some other abstraction
#if PWM_CHANNELS >= 3
#ifndef BLINK_AT_RAMP_MIDDLE_1
#define BLINK_AT_RAMP_MIDDLE_1 MAX_Nx7135
@@ -143,7 +129,7 @@ uint8_t nearest_level(int16_t target);
// ensure ramp globals are correct
void ramp_update_config();
-#ifdef USE_THERMAL_REGULATION
+#if defined(USE_THERMAL_REGULATION) || defined(USE_SMOOTH_STEPS)
// brightness before thermal step-down
uint8_t target_level = 0;
void set_level_and_therm_target(uint8_t level);
@@ -155,78 +141,75 @@ void set_level_and_therm_target(uint8_t level);
// brightness control
uint8_t memorized_level = DEFAULT_LEVEL;
#ifdef USE_MANUAL_MEMORY
-#ifndef DEFAULT_MANUAL_MEMORY
-#define DEFAULT_MANUAL_MEMORY 0
-#endif
-uint8_t manual_memory = DEFAULT_MANUAL_MEMORY;
-#ifdef USE_MANUAL_MEMORY_TIMER
-#ifndef DEFAULT_MANUAL_MEMORY_TIMER
-#define DEFAULT_MANUAL_MEMORY_TIMER 0
-#endif
-uint8_t manual_memory_timer = DEFAULT_MANUAL_MEMORY_TIMER;
-#endif
-#endif
-#ifdef USE_SIMPLE_UI
- // whether to enable the simplified interface or not
- uint8_t simple_ui_active = SIMPLE_UI_ACTIVE;
- #ifdef USE_2C_STYLE_CONFIG
- #ifndef DEFAULT_2C_STYLE_SIMPLE
- #define DEFAULT_2C_STYLE_SIMPLE 0
+ void manual_memory_restore();
+ void manual_memory_save();
+ #ifndef DEFAULT_MANUAL_MEMORY
+ #define DEFAULT_MANUAL_MEMORY 0
+ #endif
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ #ifndef DEFAULT_MANUAL_MEMORY_TIMER
+ #define DEFAULT_MANUAL_MEMORY_TIMER 0
#endif
- uint8_t ramp_2c_style_simple = DEFAULT_2C_STYLE_SIMPLE; // 0 = no turbo, 1 = A1 style, 2 = A2 style
#endif
#endif
-// smooth vs discrete ramping
-uint8_t ramp_style = RAMP_STYLE; // 0 = smooth, 1 = discrete
+
+#ifndef DEFAULT_2C_STYLE_SIMPLE
+ #define DEFAULT_2C_STYLE_SIMPLE 0
+#endif
+
#ifdef USE_2C_STYLE_CONFIG
#ifndef DEFAULT_2C_STYLE
#define DEFAULT_2C_STYLE 2
#endif
-uint8_t ramp_2c_style = DEFAULT_2C_STYLE; // 1 = A1 style, 2 = A2 style
+
#ifdef USE_2C_MAX_TURBO
#error Cannot use USE_2C_MAX_TURBO and USE_2C_STYLE_CONFIG at the same time.
#endif
#endif
#ifdef USE_RAMP_SPEED_CONFIG
-#define ramp_speed (ramp_stepss[0])
+#define ramp_speed (cfg.ramp_stepss[0])
#endif
#ifdef USE_RAMP_AFTER_MOON_CONFIG
#ifndef DEFAULT_DONT_RAMP_AFTER_MOON
#define DEFAULT_DONT_RAMP_AFTER_MOON 0
#endif
-uint8_t dont_ramp_after_moon = DEFAULT_DONT_RAMP_AFTER_MOON;
#endif
// current values, regardless of style
uint8_t ramp_floor = RAMP_SMOOTH_FLOOR;
uint8_t ramp_ceil = RAMP_SMOOTH_CEIL;
-// per style
-uint8_t ramp_floors[] = {
- RAMP_SMOOTH_FLOOR,
- RAMP_DISCRETE_FLOOR,
- #ifdef USE_SIMPLE_UI
- SIMPLE_UI_FLOOR,
+
+uint8_t ramp_discrete_step_size; // don't set this
+
+#ifdef USE_SUNSET_TIMER
+uint8_t sunset_timer_orig_level = 0;
+void reset_sunset_timer();
+#endif
+
+#ifdef USE_RAMP_EXTRAS_CONFIG
+typedef enum {
+ ramp_extras_cfg_zero = 0,
+ manual_memory_config_step,
+ #ifdef USE_MANUAL_MEMORY_TIMER
+ manual_memory_timer_config_step,
#endif
- };
-uint8_t ramp_ceils[] = {
- RAMP_SMOOTH_CEIL,
- RAMP_DISCRETE_CEIL,
- #ifdef USE_SIMPLE_UI
- SIMPLE_UI_CEIL,
+ #ifdef USE_RAMP_AFTER_MOON_CONFIG
+ dont_ramp_after_moon_config_step,
#endif
- };
-uint8_t ramp_stepss[] = {
- DEFAULT_RAMP_SPEED,
- RAMP_DISCRETE_STEPS,
- #ifdef USE_SIMPLE_UI
- SIMPLE_UI_STEPS,
+ #ifdef USE_2C_STYLE_CONFIG
+ ramp_2c_style_config_step,
#endif
- };
-uint8_t ramp_discrete_step_size; // don't set this
+ #ifdef USE_SMOOTH_STEPS
+ smooth_steps_style_config_step,
+ #endif
+ ramp_extras_config_num_steps
+} ramp_extras_config_steps_e;
+#endif
#ifdef USE_GLOBALS_CONFIG
typedef enum {
- #ifdef USE_TINT_RAMPING
+ globals_cfg_zero = 0,
+ #if defined(USE_CHANNEL_MODE_ARGS) && defined(USE_STEPPED_TINT_RAMPING)
tint_style_config_step,
#endif
#ifdef USE_JUMP_START
@@ -239,5 +222,3 @@ void globals_config_save(uint8_t step, uint8_t value);
uint8_t globals_config_state(Event event, uint16_t arg);
#endif
-
-#endif
diff --git a/spaghetti-monster/anduril/smooth-steps.c b/spaghetti-monster/anduril/smooth-steps.c
new file mode 100644
index 0000000..9631e41
--- /dev/null
+++ b/spaghetti-monster/anduril/smooth-steps.c
@@ -0,0 +1,47 @@
+// smooth-steps.c: Smooth step adjustments for Anduril.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "smooth-steps.h"
+
+#ifdef USE_SMOOTH_STEPS
+
+// one iteration of main loop()
+void smooth_steps_iter() {
+ if (actual_level == smooth_steps_target) {
+ set_level(smooth_steps_target);
+ smooth_steps_in_progress = 0;
+ // restore prev_level when animation ends
+ prev_level = smooth_steps_start;
+ }
+ else if (smooth_steps_target > actual_level) {
+ // power-linear(ish) ascent
+ // (jump by ~20% of remaining distance on each frame)
+ uint8_t diff = smooth_steps_target - actual_level;
+ uint8_t this = diff / smooth_steps_speed;
+ if (!this) this = 1;
+ set_level(actual_level + this);
+ nice_delay_ms(10);
+ } else {
+ // ramp-linear descent
+ // (jump by 1 on each frame, frame rate gives constant total time)
+ uint8_t diff = smooth_steps_start - smooth_steps_target;
+ uint16_t delay = 1 + (30 * smooth_steps_speed / diff);
+ set_level(actual_level - 1);
+ // TODO? if delay < one PWM cycle, this can look a little weird
+ nice_delay_ms(delay);
+ }
+}
+
+void set_level_smooth(uint8_t level, uint8_t speed) {
+ smooth_steps_target = level;
+ smooth_steps_speed = speed; // higher = slower
+ smooth_steps_in_progress = 1;
+ // for setting prev_level after animation ends
+ smooth_steps_start = actual_level;
+}
+
+#endif
+
diff --git a/spaghetti-monster/anduril/smooth-steps.h b/spaghetti-monster/anduril/smooth-steps.h
new file mode 100644
index 0000000..a553af2
--- /dev/null
+++ b/spaghetti-monster/anduril/smooth-steps.h
@@ -0,0 +1,19 @@
+// smooth-steps.h: Smooth step adjustments for Anduril.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#ifdef USE_SMOOTH_STEPS
+
+uint8_t smooth_steps_start;
+uint8_t smooth_steps_target;
+uint8_t smooth_steps_in_progress;
+uint8_t smooth_steps_speed;
+
+void smooth_steps_iter();
+
+void set_level_smooth(uint8_t level, uint8_t speed);
+
+#endif
+
diff --git a/spaghetti-monster/anduril/sos-mode.c b/spaghetti-monster/anduril/sos-mode.c
index 97245c7..2a4e97c 100644
--- a/spaghetti-monster/anduril/sos-mode.c
+++ b/spaghetti-monster/anduril/sos-mode.c
@@ -1,24 +1,8 @@
-/*
- * sos-mode.c: SOS mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// sos-mode.c: SOS mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef SOS_MODE_C
-#define SOS_MODE_C
+#pragma once
#include "sos-mode.h"
@@ -27,7 +11,7 @@ uint8_t sos_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: next blinky mode
else if (event == EV_2clicks) {
@@ -38,7 +22,7 @@ uint8_t sos_state(Event event, uint16_t arg) {
#elif defined(USE_BEACON_MODE)
set_state(beacon_state, 0);
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -70,6 +54,3 @@ inline void sos_mode_iter() {
nice_delay_ms(2000);
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/sos-mode.h b/spaghetti-monster/anduril/sos-mode.h
index 397aa3f..5af61be 100644
--- a/spaghetti-monster/anduril/sos-mode.h
+++ b/spaghetti-monster/anduril/sos-mode.h
@@ -1,29 +1,11 @@
-/*
- * sos-mode.h: SOS mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// sos-mode.h: SOS mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef SOS_MODE_H
-#define SOS_MODE_H
+#pragma once
#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
// automatic SOS emergency signal
uint8_t sos_state(Event event, uint16_t arg);
#endif
-
-#endif
diff --git a/spaghetti-monster/anduril/strobe-modes-fsm.h b/spaghetti-monster/anduril/strobe-modes-fsm.h
index 002a951..4d948ed 100644
--- a/spaghetti-monster/anduril/strobe-modes-fsm.h
+++ b/spaghetti-monster/anduril/strobe-modes-fsm.h
@@ -1,24 +1,8 @@
-/*
- * strobe-modes-fsm.h: FSM config for strobe modes in Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef STROBE_MODES_FSM_H
-#define STROBE_MODES_FSM_H
+// strobe-modes-fsm.h: FSM config for strobe modes in Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
// enable the random number generator if we need it
#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE)
@@ -41,4 +25,31 @@
#define USE_STROBE_STATE
#endif
+// internal numbering for strobe modes
+#ifdef USE_STROBE_STATE
+typedef enum {
+ #ifdef USE_PARTY_STROBE_MODE
+ party_strobe_e,
+ #endif
+ #ifdef USE_TACTICAL_STROBE_MODE
+ tactical_strobe_e,
+ #endif
+ #ifdef USE_POLICE_COLOR_STROBE_MODE
+ police_color_strobe_e,
+ #endif
+ #ifdef USE_LIGHTNING_MODE
+ lightning_storm_e,
+ #endif
+ #ifdef USE_CANDLE_MODE
+ candle_mode_e,
+ #endif
+ #ifdef USE_BIKE_FLASHER_MODE
+ bike_flasher_e,
+ #endif
+ strobe_mode_END
+} strobe_mode_te;
+
+//const int NUM_STROBES = strobe_mode_END;
+#define NUM_STROBES strobe_mode_END
#endif
+
diff --git a/spaghetti-monster/anduril/strobe-modes.c b/spaghetti-monster/anduril/strobe-modes.c
index 78fe240..ad17964 100644
--- a/spaghetti-monster/anduril/strobe-modes.c
+++ b/spaghetti-monster/anduril/strobe-modes.c
@@ -1,24 +1,8 @@
-/*
- * strobe-modes.c: Strobe modes for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef STROBE_MODES_C
-#define STROBE_MODES_C
+// strobe-modes.c: Strobe modes for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include "strobe-modes.h"
@@ -27,7 +11,7 @@ uint8_t strobe_state(Event event, uint16_t arg) {
static int8_t ramp_direction = 1;
// 'st' reduces ROM size slightly
- strobe_mode_te st = strobe_type;
+ strobe_mode_te st = current_strobe_type;
#ifdef USE_MOMENTARY_MODE
momentary_mode = 1; // 0 = ramping, 1 = strobes
@@ -44,25 +28,36 @@ uint8_t strobe_state(Event event, uint16_t arg) {
if (0) {} // placeholder
// init anything which needs to be initialized
else if (event == EV_enter_state) {
+ current_strobe_type = cfg.strobe_type;
ramp_direction = 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: rotate through strobe/flasher modes
else if (event == EV_2clicks) {
- strobe_type = (st + 1) % NUM_STROBES;
+ current_strobe_type = cfg.strobe_type = (st + 1) % NUM_STROBES;
+ save_config();
+ return EVENT_HANDLED;
+ }
+ #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE)
+ // 3 clicks: rotate through channel modes for the current strobe
+ else if (event == EV_3clicks) {
+ // TODO: maybe skip aux modes?
+ set_channel_mode((channel_mode + 1) % NUM_CHANNEL_MODES);
+ cfg.strobe_channels[st] = channel_mode;
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
+ #endif
// 4 clicks: rotate backward through strobe/flasher modes
else if (event == EV_4clicks) {
- strobe_type = (st - 1 + NUM_STROBES) % NUM_STROBES;
+ current_strobe_type = cfg.strobe_type = (st - 1 + NUM_STROBES) % NUM_STROBES;
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: change speed (go faster)
// or change brightness (brighter)
@@ -77,11 +72,11 @@ uint8_t strobe_state(Event event, uint16_t arg) {
else if (st == party_strobe_e) {
#endif
if ((arg & 1) == 0) {
- uint8_t d = strobe_delays[st];
+ uint8_t d = cfg.strobe_delays[st];
d -= ramp_direction;
if (d < 8) d = 8;
else if (d > 254) d = 254;
- strobe_delays[st] = d;
+ cfg.strobe_delays[st] = d;
}
}
#endif
@@ -92,21 +87,21 @@ uint8_t strobe_state(Event event, uint16_t arg) {
// biking mode brighter
#ifdef USE_BIKE_FLASHER_MODE
else if (st == bike_flasher_e) {
- bike_flasher_brightness += ramp_direction;
- if (bike_flasher_brightness < 2) bike_flasher_brightness = 2;
- else if (bike_flasher_brightness > MAX_BIKING_LEVEL) bike_flasher_brightness = MAX_BIKING_LEVEL;
- set_level(bike_flasher_brightness);
+ cfg.bike_flasher_brightness += ramp_direction;
+ if (cfg.bike_flasher_brightness < 2) cfg.bike_flasher_brightness = 2;
+ else if (cfg.bike_flasher_brightness > MAX_BIKING_LEVEL) cfg.bike_flasher_brightness = MAX_BIKING_LEVEL;
+ set_level(cfg.bike_flasher_brightness);
}
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// reverse ramp direction on hold release
// ... and save new strobe settings
else if (event == EV_click1_hold_release) {
ramp_direction = -ramp_direction;
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: change speed (go slower)
// or change brightness (dimmer)
@@ -123,7 +118,7 @@ uint8_t strobe_state(Event event, uint16_t arg) {
else if (st == party_strobe_e) {
#endif
if ((arg & 1) == 0) {
- if (strobe_delays[st] < 255) strobe_delays[st] ++;
+ if (cfg.strobe_delays[st] < 255) cfg.strobe_delays[st] ++;
}
}
#endif
@@ -134,25 +129,25 @@ uint8_t strobe_state(Event event, uint16_t arg) {
// biking mode dimmer
#ifdef USE_BIKE_FLASHER_MODE
else if (st == bike_flasher_e) {
- if (bike_flasher_brightness > 2)
- bike_flasher_brightness --;
- set_level(bike_flasher_brightness);
+ if (cfg.bike_flasher_brightness > 2)
+ cfg.bike_flasher_brightness --;
+ set_level(cfg.bike_flasher_brightness);
}
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// release hold: save new strobe settings
else if (event == EV_click2_hold_release) {
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_MOMENTARY_MODE
// 5 clicks: go to momentary mode (momentary strobe)
else if (event == EV_5clicks) {
set_state(momentary_state, 0);
set_level(0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE)
@@ -162,7 +157,7 @@ uint8_t strobe_state(Event event, uint16_t arg) {
if (arg == AUTO_REVERSE_TIME) ramp_direction = 1;
pseudo_rand_seed += arg;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -170,7 +165,12 @@ uint8_t strobe_state(Event event, uint16_t arg) {
// runs repeatedly in FSM loop() whenever UI is in strobe_state or momentary strobe
inline void strobe_state_iter() {
- uint8_t st = strobe_type; // can't use switch() on an enum
+ uint8_t st = current_strobe_type; // can't use switch() on an enum
+
+ #if (NUM_CHANNEL_MODES > 1) && defined(USE_CHANNEL_PER_STROBE)
+ // remember channel mode for each strobe
+ channel_mode = cfg.strobe_channels[st];
+ #endif
switch(st) {
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
@@ -184,6 +184,12 @@ inline void strobe_state_iter() {
break;
#endif
+ #ifdef USE_POLICE_COLOR_STROBE_MODE
+ case police_color_strobe_e:
+ police_color_strobe_iter();
+ break;
+ #endif
+
#ifdef USE_LIGHTNING_MODE
case lightning_storm_e:
lightning_storm_iter();
@@ -202,10 +208,10 @@ inline void strobe_state_iter() {
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
inline void party_tactical_strobe_mode_iter(uint8_t st) {
// one iteration of main loop()
- uint8_t del = strobe_delays[st];
+ uint8_t del = cfg.strobe_delays[st];
// TODO: make tac strobe brightness configurable?
set_level(STROBE_BRIGHTNESS);
- if (0) {} // placeholde0
+ if (0) {} // placeholder
#ifdef USE_PARTY_STROBE_MODE
else if (st == party_strobe_e) { // party strobe
#ifdef PARTY_STROBE_ONTIME
@@ -226,6 +232,29 @@ inline void party_tactical_strobe_mode_iter(uint8_t st) {
}
#endif
+#ifdef USE_POLICE_COLOR_STROBE_MODE
+inline void police_color_strobe_iter() {
+ // one iteration of main loop()
+ uint8_t del = 66;
+ // TODO: make police strobe brightness configurable
+ uint8_t bright = memorized_level;
+ //uint8_t channel = channel_mode;
+
+ for (uint8_t i=0; i<10; i++) {
+ if (0 == i) set_channel_mode(POLICE_COLOR_STROBE_CH1);
+ else if (5 == i) set_channel_mode(POLICE_COLOR_STROBE_CH2);
+ set_level(bright);
+ nice_delay_ms(del >> 1);
+ set_level(STROBE_OFF_LEVEL);
+ nice_delay_ms(del);
+ }
+
+ // restore the channel when done
+ //set_channel_mode(channel);
+ channel_mode = cfg.channel_mode;
+}
+#endif
+
#ifdef USE_LIGHTNING_MODE
inline void lightning_storm_iter() {
// one iteration of main loop()
@@ -274,14 +303,17 @@ inline void lightning_storm_iter() {
#endif
#ifdef USE_BIKE_FLASHER_MODE
+#ifndef BIKE_STROBE_ONTIME
+#define BIKE_STROBE_ONTIME 0
+#endif
inline void bike_flasher_iter() {
// one iteration of main loop()
- uint8_t burst = bike_flasher_brightness << 1;
+ uint8_t burst = cfg.bike_flasher_brightness << 1;
if (burst > MAX_LEVEL) burst = MAX_LEVEL;
for(uint8_t i=0; i<4; i++) {
set_level(burst);
- nice_delay_ms(5);
- set_level(bike_flasher_brightness);
+ nice_delay_ms(5 + BIKE_STROBE_ONTIME);
+ set_level(cfg.bike_flasher_brightness);
nice_delay_ms(65);
}
nice_delay_ms(720); // no return check necessary on final delay
@@ -298,6 +330,3 @@ inline void bike_flasher_iter() {
#include "ff-strobe-modes.c"
#endif
-
-#endif
-
diff --git a/spaghetti-monster/anduril/strobe-modes.h b/spaghetti-monster/anduril/strobe-modes.h
index c6cfb53..7dc1df4 100644
--- a/spaghetti-monster/anduril/strobe-modes.h
+++ b/spaghetti-monster/anduril/strobe-modes.h
@@ -1,56 +1,22 @@
-/*
- * strobe-modes.h: Strobe modes for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef STROBE_MODES_H
-#define STROBE_MODES_H
-
-// internal numbering for strobe modes
+// strobe-modes.h: Strobe modes for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
#ifdef USE_STROBE_STATE
-typedef enum {
- #ifdef USE_PARTY_STROBE_MODE
- party_strobe_e,
- #endif
- #ifdef USE_TACTICAL_STROBE_MODE
- tactical_strobe_e,
- #endif
- #ifdef USE_LIGHTNING_MODE
- lightning_storm_e,
- #endif
- #ifdef USE_CANDLE_MODE
- candle_mode_e,
- #endif
- #ifdef USE_BIKE_FLASHER_MODE
- bike_flasher_e,
- #endif
- strobe_mode_END
-} strobe_mode_te;
-
-const int NUM_STROBES = strobe_mode_END;
+
+strobe_mode_te current_strobe_type;
// which strobe mode is active?
#ifdef USE_CANDLE_MODE
-strobe_mode_te strobe_type = candle_mode_e;
+ #define DEFAULT_STROBE candle_mode_e
#else
-strobe_mode_te strobe_type = 0;
-#endif
+ #define DEFAULT_STROBE 0
#endif
+#endif // ifdef USE_STROBE_STATE
+
// full FET strobe can be a bit much... use max regulated level instead,
// if there's a bright enough regulated level
@@ -76,11 +42,13 @@ inline void strobe_state_iter();
#endif
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
-// party / tactical strobe timing
-uint8_t strobe_delays[] = { 41, 67 }; // party strobe 24 Hz, tactical strobe 10 Hz
inline void party_tactical_strobe_mode_iter(uint8_t st);
#endif
+#ifdef USE_POLICE_COLOR_STROBE_MODE
+inline void police_color_strobe_iter();
+#endif
+
#ifdef USE_LIGHTNING_MODE
inline void lightning_storm_iter();
#endif
@@ -88,7 +56,6 @@ inline void lightning_storm_iter();
// bike mode config options
#ifdef USE_BIKE_FLASHER_MODE
#define MAX_BIKING_LEVEL 120 // should be 127 or less
-uint8_t bike_flasher_brightness = MAX_1x7135;
inline void bike_flasher_iter();
#endif
@@ -102,4 +69,3 @@ inline void bike_flasher_iter();
#include "ff-strobe-modes.h"
#endif
-#endif
diff --git a/spaghetti-monster/anduril/sunset-timer.c b/spaghetti-monster/anduril/sunset-timer.c
index d942eba..e4fc512 100644
--- a/spaghetti-monster/anduril/sunset-timer.c
+++ b/spaghetti-monster/anduril/sunset-timer.c
@@ -1,46 +1,31 @@
-/*
- * sunset-timer.c: Sunset / candle auto-shutoff functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// sunset-timer.c: Sunset / candle auto-shutoff functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef SUNSET_TIMER_C
-#define SUNSET_TIMER_C
+#pragma once
#include "sunset-timer.h"
uint8_t sunset_timer_state(Event event, uint16_t arg) {
- #ifdef USE_SIMPLE_UI
+ #if defined(USE_SIMPLE_UI) && !defined(USE_EXTENDED_SIMPLE_UI)
// No timer functions in Simple UI
- if (simple_ui_active) return EVENT_NOT_HANDLED;
+ if (cfg.simple_ui_active) return EVENT_NOT_HANDLED;
#endif
// reset on start
if (event == EV_enter_state) {
sunset_timer = 0;
sunset_ticks = 0;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: maybe "bump" the timer if it's active and almost expired
else if (event == EV_hold) {
// ramping up should "bump" the timer to extend the deadline a bit
if ((sunset_timer > 0) && (sunset_timer < 4)) {
- sunset_timer = 3;
+ sunset_timer = 3; // 3 minutes
sunset_timer_peak = 3;
+ sunset_ticks = 0; // re-start current "minute"
}
}
// 5H: add 5m to timer, per second, until released
@@ -55,7 +40,7 @@ uint8_t sunset_timer_state(Event event, uint16_t arg) {
blink_once();
}
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// tick: count down until time expires
else if (event == EV_tick) {
@@ -68,11 +53,8 @@ uint8_t sunset_timer_state(Event event, uint16_t arg) {
sunset_timer --;
}
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/sunset-timer.h b/spaghetti-monster/anduril/sunset-timer.h
index 8f7840c..963804e 100644
--- a/spaghetti-monster/anduril/sunset-timer.h
+++ b/spaghetti-monster/anduril/sunset-timer.h
@@ -1,24 +1,8 @@
-/*
- * sunset-timer.h: Sunset / candle auto-shutoff functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// sunset-timer.h: Sunset / candle auto-shutoff functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef SUNSET_TIMER_H
-#define SUNSET_TIMER_H
+#pragma once
// how many minutes to add each time the user "bumps" the timer?
#define SUNSET_TIMER_UNIT 5
@@ -31,5 +15,3 @@ uint8_t sunset_timer_peak = 0; // total minutes in countdown
uint16_t sunset_ticks = 0; // counts from 0 to TICKS_PER_MINUTE, then repeats
uint8_t sunset_timer_state(Event event, uint16_t arg);
-
-#endif
diff --git a/spaghetti-monster/anduril/tactical-mode.c b/spaghetti-monster/anduril/tactical-mode.c
index 7e9d66b..0035496 100644
--- a/spaghetti-monster/anduril/tactical-mode.c
+++ b/spaghetti-monster/anduril/tactical-mode.c
@@ -1,29 +1,11 @@
-/*
- * tactical-mode.c: Tactical (ish) mode for Anduril.
- *
- * Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
- */
+// tactical-mode.c: Tactical (ish) mode for Anduril.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef TACTICAL_MODE_C
-#define TACTICAL_MODE_C
+#pragma once
#include "tactical-mode.h"
-// TODO: save these in eeprom
-uint8_t tactical_levels[] = { TACTICAL_LEVELS }; // high, low, strobe
uint8_t tactical_state(Event event, uint16_t arg) {
// momentary(ish) tactical mode
@@ -42,14 +24,18 @@ uint8_t tactical_state(Event event, uint16_t arg) {
if (click <= 3) {
momentary_active = 1;
uint8_t lvl;
- lvl = tactical_levels[click-1];
+ lvl = cfg.tactical_levels[click-1];
if ((1 <= lvl) && (lvl <= RAMP_SIZE)) { // steady output
memorized_level = lvl;
momentary_mode = 0;
+ #if NUM_CHANNEL_MODES > 1
+ // use ramp mode's channel
+ channel_mode = cfg.channel_mode;
+ #endif
} else { // momentary strobe mode
momentary_mode = 1;
if (lvl > RAMP_SIZE) {
- strobe_type = (lvl - RAMP_SIZE - 1) % strobe_mode_END;
+ current_strobe_type = (lvl - RAMP_SIZE - 1) % strobe_mode_END;
}
}
}
@@ -58,6 +44,7 @@ uint8_t tactical_state(Event event, uint16_t arg) {
else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
momentary_active = 0;
set_level(0);
+ interrupt_nice_delays(); // stop animations in progress
}
// delegate to momentary mode while button is pressed
@@ -92,7 +79,7 @@ uint8_t tactical_state(Event event, uint16_t arg) {
// (unnecessary since this entire mode is blocked in simple UI)
/*
#ifdef USE_SIMPLE_UI
- if (simple_ui_active) {
+ if (cfg.simple_ui_active) {
return EVENT_NOT_HANDLED;
}
#endif
@@ -101,7 +88,7 @@ uint8_t tactical_state(Event event, uint16_t arg) {
// 7H: configure tactical mode
else if (event == EV_click7_hold) {
push_state(tactical_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return ret;
@@ -113,13 +100,10 @@ void tactical_config_save(uint8_t step, uint8_t value) {
// each value is 1 to 150, or other:
// - 1..150 is a ramp level
// - other means "strobe mode"
- tactical_levels[step - 1] = value;
+ cfg.tactical_levels[step - 1] = value;
}
uint8_t tactical_config_state(Event event, uint16_t arg) {
return config_state_base(event, arg, 3, tactical_config_save);
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/tactical-mode.h b/spaghetti-monster/anduril/tactical-mode.h
index 4403314..528a796 100644
--- a/spaghetti-monster/anduril/tactical-mode.h
+++ b/spaghetti-monster/anduril/tactical-mode.h
@@ -1,34 +1,22 @@
-/*
- * tactical-mode.h: Tactical mode for Anduril.
- *
- * Copyright (C) 2023 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 <http://www.gnu.org/licenses/>.
- */
+// tactical-mode.h: Tactical mode for Anduril.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef TACTICAL_MODE_H
-#define TACTICAL_MODE_H
-
-// tactical(ish) mode
-uint8_t tactical_state(Event event, uint16_t arg);
+#pragma once
#ifndef TACTICAL_LEVELS
-// high, low, tactical strobe
-#define TACTICAL_LEVELS 120,30,(RAMP_SIZE+2)
+ // high, low, tactical strobe
+ // only do color strobe here if it's main LEDs, not aux LEDs
+ #if defined(USE_POLICE_COLOR_STROBE_MODE) && !defined(POLICE_STROBE_USES_AUX)
+ // 2-color police style strobe
+ #define TACTICAL_LEVELS 120,30,(RAMP_SIZE+3)
+ #else
+ // regular tactical strobe (1 color)
+ #define TACTICAL_LEVELS 120,30,(RAMP_SIZE+2)
+ #endif
#endif
-uint8_t tactical_levels[];
-uint8_t tactical_config_state(Event event, uint16_t arg);
+// tactical(ish) mode
+uint8_t tactical_state(Event event, uint16_t arg);
+uint8_t tactical_config_state(Event event, uint16_t arg);
-#endif
diff --git a/spaghetti-monster/anduril/tempcheck-mode.c b/spaghetti-monster/anduril/tempcheck-mode.c
index f6e1ebe..5d160bd 100644
--- a/spaghetti-monster/anduril/tempcheck-mode.c
+++ b/spaghetti-monster/anduril/tempcheck-mode.c
@@ -1,24 +1,8 @@
-/*
- * tempcheck-mode.c: Temperature check mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// tempcheck-mode.c: Temperature check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef TEMPCHECK_MODE_C
-#define TEMPCHECK_MODE_C
+#pragma once
#include "tempcheck-mode.h"
@@ -26,7 +10,7 @@ uint8_t tempcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: next blinky mode
else if (event == EV_2clicks) {
@@ -37,12 +21,12 @@ uint8_t tempcheck_state(Event event, uint16_t arg) {
#elif defined(USE_BATTCHECK)
set_state(battcheck_state, 0);
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 7H: thermal config mode
else if (event == EV_click7_hold) {
push_state(thermal_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -51,18 +35,18 @@ void thermal_config_save(uint8_t step, uint8_t value) {
if (value) {
// item 1: calibrate room temperature
if (step == 1) {
- int8_t rawtemp = temperature - therm_cal_offset;
- therm_cal_offset = value - rawtemp;
+ int8_t rawtemp = temperature - cfg.therm_cal_offset;
+ cfg.therm_cal_offset = value - rawtemp;
adc_reset = 2; // invalidate all recent temperature data
}
// item 2: set maximum heat limit
else {
- therm_ceil = 30 + value - 1;
+ cfg.therm_ceil = 30 + value - 1;
}
}
- if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
+ if (cfg.therm_ceil > MAX_THERM_CEIL) cfg.therm_ceil = MAX_THERM_CEIL;
}
uint8_t thermal_config_state(Event event, uint16_t arg) {
@@ -70,6 +54,3 @@ uint8_t thermal_config_state(Event event, uint16_t arg) {
2, thermal_config_save);
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/tempcheck-mode.h b/spaghetti-monster/anduril/tempcheck-mode.h
index 83edd9c..15dd03e 100644
--- a/spaghetti-monster/anduril/tempcheck-mode.h
+++ b/spaghetti-monster/anduril/tempcheck-mode.h
@@ -1,24 +1,8 @@
-/*
- * tempcheck-mode.h: Temperature check mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// tempcheck-mode.h: Temperature check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef TEMPCHECK_MODE_H
-#define TEMPCHECK_MODE_H
+#pragma once
#define USE_BLINK_NUM // FIXME: this only matters in an earlier header
@@ -26,5 +10,3 @@ uint8_t tempcheck_state(Event event, uint16_t arg);
uint8_t thermal_config_state(Event event, uint16_t arg);
void thermal_config_save(uint8_t step, uint8_t value);
-
-#endif
diff --git a/spaghetti-monster/anduril/tint-ramping.c b/spaghetti-monster/anduril/tint-ramping.c
index d270d9d..9418113 100644
--- a/spaghetti-monster/anduril/tint-ramping.c
+++ b/spaghetti-monster/anduril/tint-ramping.c
@@ -1,24 +1,8 @@
-/*
- * tint-ramping.c: Tint ramping functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// tint-ramping.c: Tint ramping functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef TINT_RAMPING_C
-#define TINT_RAMPING_C
+#pragma once
#include "tint-ramping.h"
@@ -100,6 +84,3 @@ uint8_t tint_ramping_state(Event event, uint16_t arg) {
return EVENT_NOT_HANDLED;
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/tint-ramping.h b/spaghetti-monster/anduril/tint-ramping.h
index 1c5e22a..19b8dde 100644
--- a/spaghetti-monster/anduril/tint-ramping.h
+++ b/spaghetti-monster/anduril/tint-ramping.h
@@ -1,24 +1,8 @@
-/*
- * tint-ramping.h: Tint ramping functions for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// tint-ramping.h: Tint ramping functions for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef TINT_RAMPING_H
-#define TINT_RAMPING_H
+#pragma once
// 0: smooth tint ramp
// 1: toggle tint only between two extremes
@@ -35,5 +19,3 @@ uint8_t manual_memory_tint;
// not actually a mode, more of a fallback under other modes
uint8_t tint_ramping_state(Event event, uint16_t arg);
-
-#endif
diff --git a/spaghetti-monster/anduril/version-check-mode.c b/spaghetti-monster/anduril/version-check-mode.c
index edb1723..a47706f 100644
--- a/spaghetti-monster/anduril/version-check-mode.c
+++ b/spaghetti-monster/anduril/version-check-mode.c
@@ -1,24 +1,8 @@
-/*
- * version-check-mode.c: Version check mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// version-check-mode.c: Version check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef VERSION_CHECK_MODE_C
-#define VERSION_CHECK_MODE_C
+#pragma once
#include "version-check-mode.h"
@@ -30,18 +14,18 @@ uint8_t version_check_state(Event event, uint16_t arg) {
// this happens in FSM loop()
inline void version_check_iter() {
for (uint8_t i=0; i<sizeof(version_number)-1; i++) {
- blink_digit(pgm_read_byte(version_number + i) - '0');
+ uint8_t digit = pgm_read_byte(version_number + i) - '0';
+ if (digit < 10) blink_digit(digit);
+ else { // "buzz" for non-numeric characters
+ for(uint8_t frame=0; frame<25; frame++) {
+ set_level((frame&1) << 5);
+ nice_delay_ms(16);
+ }
+ nice_delay_ms(BLINK_SPEED * 8 / 12);
+ }
nice_delay_ms(300);
}
- // FIXME: when user interrupts with button, "off" takes an extra click
- // before it'll turn back on, because the click to cancel gets sent
- // to the "off" state instead of version_check_state
- //while (button_is_pressed()) {}
- //empty_event_sequence();
set_state_deferred(off_state, 0);
}
-
-#endif
-
diff --git a/spaghetti-monster/anduril/version-check-mode.h b/spaghetti-monster/anduril/version-check-mode.h
index ce02b73..72bad3f 100644
--- a/spaghetti-monster/anduril/version-check-mode.h
+++ b/spaghetti-monster/anduril/version-check-mode.h
@@ -1,24 +1,8 @@
-/*
- * version-check-mode.h: Version check mode for Anduril.
- *
- * 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 <http://www.gnu.org/licenses/>.
- */
+// version-check-mode.h: Version check mode for Anduril.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef VERSION_CHECK_MODE_H
-#define VERSION_CHECK_MODE_H
+#pragma once
#define USE_BLINK_DIGIT // FIXME: does nothing unless defined earlier
@@ -29,9 +13,7 @@
#endif
#include "version.h"
-const PROGMEM uint8_t version_number[] = VERSION_NUMBER MODEL_NUMBER;
+const PROGMEM uint8_t version_number[] = MODEL_NUMBER "." VERSION_NUMBER;
uint8_t version_check_state(Event event, uint16_t arg);
inline void version_check_iter();
-
-#endif
diff --git a/spaghetti-monster/anduril/version.h b/spaghetti-monster/anduril/version.h
index 8cf3c90..b548e13 100644
--- a/spaghetti-monster/anduril/version.h
+++ b/spaghetti-monster/anduril/version.h
@@ -1,4 +1,4 @@
// this file is replaced automatically by the build script
// set your own date here if you're not using the build script
// otherwise, default to first human contact with the moon
-#define VERSION_NUMBER "19690720"
+#define VERSION_NUMBER "1969-07-20"
diff --git a/spaghetti-monster/anduril/wurkkos-cfg.h b/spaghetti-monster/anduril/wurkkos-cfg.h
new file mode 100644
index 0000000..b9992fb
--- /dev/null
+++ b/spaghetti-monster/anduril/wurkkos-cfg.h
@@ -0,0 +1,26 @@
+// Wurkkos config options for Anduril
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+// allow Aux Config and Strobe Modes in Simple UI
+#define USE_EXTENDED_SIMPLE_UI
+
+// Allow 3C in Simple UI for switching between smooth and stepped ramping
+#define USE_SIMPLE_UI_RAMPING_TOGGLE
+
+// enable 2 click turbo (Anduril 1 style)
+#define DEFAULT_2C_STYLE 1
+
+// enable SOS in the blinkies group
+#define USE_SOS_MODE
+#define USE_SOS_MODE_IN_BLINKY_GROUP
+
+// enable factory reset on 13H without loosening tailcap
+// only really needed on TS10, but doesn't hurt on other models
+#define USE_SOFT_FACTORY_RESET
+
+// higher temperature limit because attiny1616 is more accurate
+#undef DEFAULT_THERM_CEIL
+#define DEFAULT_THERM_CEIL 50
+
diff --git a/spaghetti-monster/baton/baton.c b/spaghetti-monster/baton/baton.c
index 8a80134..e672755 100644
--- a/spaghetti-monster/baton/baton.c
+++ b/spaghetti-monster/baton/baton.c
@@ -152,12 +152,12 @@ uint8_t lockout_state(Event event, uint16_t arg) {
PWM1_LVL = 0; PWM2_LVL = 0; // make sure emitters are off
// sleep 1 second after user stops pressing buttons
if (arg > TICKS_PER_SECOND) { go_to_standby = 1; }
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 4 clicks: exit, and turn on at "low" level
else if (event == EV_4clicks) {
set_state(steady_state, 1);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
diff --git a/spaghetti-monster/chan-aux.c b/spaghetti-monster/chan-aux.c
new file mode 100644
index 0000000..e04e6a2
--- /dev/null
+++ b/spaghetti-monster/chan-aux.c
@@ -0,0 +1,11 @@
+// channel modes for single color aux LEDs
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+void set_level_aux(uint8_t level) {
+ indicator_led(!(!(level)) << 1); // high (or off)
+}
+
+bool gradual_tick_null(uint8_t gt) { return true; } // do nothing
+
diff --git a/spaghetti-monster/chan-aux.h b/spaghetti-monster/chan-aux.h
new file mode 100644
index 0000000..ff599b8
--- /dev/null
+++ b/spaghetti-monster/chan-aux.h
@@ -0,0 +1,25 @@
+// channel modes for single color aux LEDs
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define NUM_AUX_CHANNEL_MODES 1
+
+// include / exclude field based on compile options
+#ifdef USE_CHANNEL_MODE_ARGS
+ #define AUX_HAS_ARGS , .has_args = 0
+#else
+ #define AUX_HAS_ARGS
+#endif
+
+#define AUX_CHANNELS \
+ { \
+ .set_level = set_level_aux, \
+ .gradual_tick = gradual_tick_null \
+ AUX_HAS_ARGS \
+ }
+
+void set_level_aux(uint8_t level);
+
+bool gradual_tick_null(uint8_t gt);
+
diff --git a/spaghetti-monster/chan-rgbaux.c b/spaghetti-monster/chan-rgbaux.c
new file mode 100644
index 0000000..19d18a6
--- /dev/null
+++ b/spaghetti-monster/chan-rgbaux.c
@@ -0,0 +1,35 @@
+// channel modes for RGB aux LEDs
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+void set_level_auxred(uint8_t level) {
+ rgb_led_set(!(!(level)) * 0b000010); // red, high (or off)
+}
+
+void set_level_auxyel(uint8_t level) {
+ rgb_led_set(!(!(level)) * 0b001010); // red+green, high (or off)
+}
+
+void set_level_auxgrn(uint8_t level) {
+ rgb_led_set(!(!(level)) * 0b001000); // green, high (or off)
+}
+
+void set_level_auxcyn(uint8_t level) {
+ rgb_led_set(!(!(level)) * 0b101000); // green+blue, high (or off)
+}
+
+void set_level_auxblu(uint8_t level) {
+ rgb_led_set(!(!(level)) * 0b100000); // blue, high (or off)
+}
+
+void set_level_auxprp(uint8_t level) {
+ rgb_led_set(!(!(level)) * 0b100010); // red+blue, high (or off)
+}
+
+void set_level_auxwht(uint8_t level) {
+ rgb_led_set(!(!(level)) * 0b101010); // red+green+blue, high (or off)
+}
+
+bool gradual_tick_null(uint8_t gt) { return true; } // do nothing
+
diff --git a/spaghetti-monster/chan-rgbaux.h b/spaghetti-monster/chan-rgbaux.h
new file mode 100644
index 0000000..6ef5d89
--- /dev/null
+++ b/spaghetti-monster/chan-rgbaux.h
@@ -0,0 +1,72 @@
+// channel modes for RGB aux LEDs
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
+
+#define RGB_AUX_ENUMS \
+ CM_AUXRED, \
+ CM_AUXYEL, \
+ CM_AUXGRN, \
+ CM_AUXCYN, \
+ CM_AUXBLU, \
+ CM_AUXPRP, \
+ CM_AUXWHT
+
+#define RGB_AUX_CM_ARGS 0,0,0,0,0,0,0
+
+#define NUM_RGB_AUX_CHANNEL_MODES 7
+
+// include / exclude field based on compile options
+#ifdef USE_CHANNEL_MODE_ARGS
+ #define AUX_RGB_HAS_ARGS , .has_args = 0
+#else
+ #define AUX_RGB_HAS_ARGS
+#endif
+
+#define RGB_AUX_CHANNELS \
+ { \
+ .set_level = set_level_auxred, \
+ .gradual_tick = gradual_tick_null \
+ AUX_RGB_HAS_ARGS \
+ }, \
+ { \
+ .set_level = set_level_auxyel, \
+ .gradual_tick = gradual_tick_null \
+ AUX_RGB_HAS_ARGS \
+ }, \
+ { \
+ .set_level = set_level_auxgrn, \
+ .gradual_tick = gradual_tick_null \
+ AUX_RGB_HAS_ARGS \
+ }, \
+ { \
+ .set_level = set_level_auxcyn, \
+ .gradual_tick = gradual_tick_null \
+ AUX_RGB_HAS_ARGS \
+ }, \
+ { \
+ .set_level = set_level_auxblu, \
+ .gradual_tick = gradual_tick_null \
+ AUX_RGB_HAS_ARGS \
+ }, \
+ { \
+ .set_level = set_level_auxprp, \
+ .gradual_tick = gradual_tick_null \
+ AUX_RGB_HAS_ARGS \
+ }, \
+ { \
+ .set_level = set_level_auxwht, \
+ .gradual_tick = gradual_tick_null \
+ AUX_RGB_HAS_ARGS \
+ }
+
+void set_level_auxred(uint8_t level);
+void set_level_auxyel(uint8_t level);
+void set_level_auxgrn(uint8_t level);
+void set_level_auxcyn(uint8_t level);
+void set_level_auxblu(uint8_t level);
+void set_level_auxprp(uint8_t level);
+void set_level_auxwht(uint8_t level);
+
+bool gradual_tick_null(uint8_t gt);
+
diff --git a/spaghetti-monster/darkhorse/darkhorse.c b/spaghetti-monster/darkhorse/darkhorse.c
index 4058c2f..aa37b92 100644
--- a/spaghetti-monster/darkhorse/darkhorse.c
+++ b/spaghetti-monster/darkhorse/darkhorse.c
@@ -166,12 +166,12 @@ uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *sec
// turn on LED when entering the mode
if (event == EV_enter_state) {
set_any_mode(*primary, *secondary, modes);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: change brightness (low, med, hi, always starting at low)
else if (event == EV_click1_hold) {
@@ -187,14 +187,14 @@ uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *sec
set_state(hi_mode_state, 0);
break;
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: toggle primary/secondary level
else if (event == EV_2clicks) {
*primary ^= 1;
set_any_mode(*primary, *secondary, modes);
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click-release-hold: change secondary level
else if (event == EV_click2_hold) {
@@ -204,7 +204,7 @@ uint8_t any_mode_state(Event event, uint16_t arg, uint8_t *primary, uint8_t *sec
*primary = 0;
set_any_mode(*primary, *secondary, modes);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold, release: save secondary level
else if (event == EV_click2_hold_release) {
@@ -259,20 +259,20 @@ uint8_t strobe_beacon_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click (initially): cancel current blink
// FIXME: this is no longer necessary; FSM does this automatically now
if (event == EV_click1_release) {
interrupt_nice_delays();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: rotate through blinky modes
else if (event == EV_2clicks) {
strobe_beacon_mode = (strobe_beacon_mode + 1) & 3;
save_config();
interrupt_nice_delays();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
diff --git a/spaghetti-monster/fireflies-ui/fireflies-ui.c b/spaghetti-monster/fireflies-ui/fireflies-ui.c
index 93a3180..34f8293 100644
--- a/spaghetti-monster/fireflies-ui/fireflies-ui.c
+++ b/spaghetti-monster/fireflies-ui/fireflies-ui.c
@@ -460,7 +460,7 @@ uint8_t off_state(Event event, uint16_t arg) {
#endif
// sleep while off (lower power use)
go_to_standby = 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// go back to sleep eventually if we got bumped but didn't leave "off" state
else if (event == EV_tick) {
@@ -470,7 +470,7 @@ uint8_t off_state(Event event, uint16_t arg) {
indicator_led(indicator_led_mode & 0x03);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
// blink the indicator LED, maybe
@@ -478,13 +478,13 @@ uint8_t off_state(Event event, uint16_t arg) {
if ((indicator_led_mode & 0b00000011) == 0b00000011) {
indicator_blink(arg);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// hold (initially): go to lowest level (floor), but allow abort for regular click
else if (event == EV_click1_press) {
set_level(nearest_level(1));
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: go to lowest level
else if (event == EV_click1_hold) {
@@ -500,75 +500,75 @@ uint8_t off_state(Event event, uint16_t arg) {
if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent
set_state(steady_state, 1);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold, release quickly: go to lowest level (floor)
else if (event == EV_click1_hold_release) {
set_state(steady_state, 1);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click (before timeout): go to memorized level, but allow abort for double click
else if (event == EV_click1_release) {
set_level(nearest_level(memorized_level));
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: regular mode
else if (event == EV_1click) {
set_state(steady_state, memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: go to highest level (ceiling) (for ramping down)
else if (event == EV_click2_hold) {
set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: highest mode (ceiling)
else if (event == EV_2clicks) {
set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 3 clicks (initial press): off, to prep for later events
else if (event == EV_click3_press) {
set_level(0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_BATTCHECK
// 3 clicks: battcheck mode / blinky mode group 1
else if (event == EV_3clicks) {
set_state(battcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// click, click, long-click: strobe mode
#ifdef USE_STROBE_STATE
else if (event == EV_click3_hold) {
set_state(strobe_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#elif defined(USE_BORING_STROBE_STATE)
else if (event == EV_click3_hold) {
set_state(boring_strobe_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// 4 clicks: soft lockout
else if (event == EV_4clicks) {
blink_confirm(2);
set_state(lockout_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 5 clicks: momentary mode
else if (event == EV_5clicks) {
blink_confirm(1);
set_state(momentary_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_MUGGLE_MODE
// 6 clicks: muggle mode
else if (event == EV_6clicks) {
blink_confirm(1);
set_state(muggle_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#ifdef USE_INDICATOR_LED
@@ -586,19 +586,19 @@ uint8_t off_state(Event event, uint16_t arg) {
indicator_led_mode = (indicator_led_mode & 0b11111100) | mode;
indicator_led(mode);
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// 8 clicks: temperature check
else if (event == EV_8clicks) {
set_state(tempcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_TENCLICK_THERMAL_CONFIG
// 10 clicks: thermal config mode
else if (event == EV_10clicks) {
push_state(thermal_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -637,12 +637,12 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef USE_REVERSING
ramp_direction = 1;
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: go to/from highest level
else if (event == EV_2clicks) {
@@ -659,7 +659,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#endif
set_level(memorized_level);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 3 clicks: toggle smooth vs discrete ramping
else if (event == EV_3clicks) {
@@ -677,20 +677,20 @@ uint8_t steady_state(Event event, uint16_t arg) {
#endif
blip();
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_RAMP_CONFIG
// 4 clicks: configure this ramp mode
else if (event == EV_4clicks) {
push_state(ramp_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// hold: change brightness (brighter)
else if (event == EV_click1_hold) {
// ramp slower in discrete mode
if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_REVERSING
// fix ramp direction on first frame if necessary
@@ -744,7 +744,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
#endif
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(USE_REVERSING) || defined(START_AT_MEMORIZED_LEVEL)
// reverse ramp direction on hold release
@@ -755,7 +755,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef START_AT_MEMORIZED_LEVEL
save_config_wl();
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// click, hold: change brightness (dimmer)
@@ -765,7 +765,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#endif
// ramp slower in discrete mode
if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// TODO? make it ramp up instead, if already at min?
memorized_level = nearest_level((int16_t)actual_level - ramp_step_size);
@@ -804,13 +804,13 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
#endif
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef START_AT_MEMORIZED_LEVEL
// click, release, hold, release: save new ramp level (if necessary)
else if (event == EV_click2_hold_release) {
save_config_wl();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING)
@@ -822,7 +822,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef USE_SET_LEVEL_GRADUALLY
// make thermal adjustment speed scale with magnitude
if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) {
- return MISCHIEF_MANAGED; // adjust slower when not a high mode
+ return EVENT_HANDLED; // adjust slower when not a high mode
}
#ifdef THERM_HARD_TURBO_DROP
else if ((! (actual_level < THERM_FASTER_LEVEL))
@@ -864,7 +864,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
#endif
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#ifdef USE_THERMAL_REGULATION
@@ -893,7 +893,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(stepdown);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// underheating: increase slowly if we're lower than the target
// (proportional to how low we are)
@@ -912,7 +912,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(stepup);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -1004,18 +1004,18 @@ uint8_t strobe_state(Event event, uint16_t arg) {
// init anything which needs to be initialized
else if (event == EV_enter_state) {
ramp_direction = 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: rotate through strobe/flasher modes
else if (event == EV_2clicks) {
strobe_type = (st + 1) % NUM_STROBES;
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: change speed (go faster)
// or change brightness (brighter)
@@ -1052,14 +1052,14 @@ uint8_t strobe_state(Event event, uint16_t arg) {
}
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// reverse ramp direction on hold release
// ... and save new strobe settings
else if (event == EV_click1_hold_release) {
ramp_direction = -ramp_direction;
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: change speed (go slower)
// or change brightness (dimmer)
@@ -1093,12 +1093,12 @@ uint8_t strobe_state(Event event, uint16_t arg) {
}
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// release hold: save new strobe settings
else if (event == EV_click2_hold_release) {
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(USE_LIGHTNING_MODE) || defined(USE_CANDLE_MODE)
// clock tick: bump the random seed
@@ -1107,7 +1107,7 @@ uint8_t strobe_state(Event event, uint16_t arg) {
if (arg == TICKS_PER_SECOND) ramp_direction = 1;
pseudo_rand_seed += arg;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -1223,14 +1223,14 @@ uint8_t candle_mode_state(Event event, uint16_t arg) {
if (event == EV_enter_state) {
candle_mode_timer = 0; // in case any time was left over from earlier
ramp_direction = 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: cancel timer
else if (event == EV_2clicks) {
// parent state just rotated through strobe/flasher modes,
// so cancel timer... in case any time was left over from earlier
candle_mode_timer = 0;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: change brightness (brighter)
else if (event == EV_click1_hold) {
@@ -1243,19 +1243,19 @@ uint8_t candle_mode_state(Event event, uint16_t arg) {
candle_mode_brightness += ramp_direction;
if (candle_mode_brightness < 1) candle_mode_brightness = 1;
else if (candle_mode_brightness > MAX_CANDLE_LEVEL) candle_mode_brightness = MAX_CANDLE_LEVEL;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// reverse ramp direction on hold release
else if (event == EV_click1_hold_release) {
ramp_direction = -ramp_direction;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: change brightness (dimmer)
else if (event == EV_click2_hold) {
ramp_direction = 1;
if (candle_mode_brightness > 1)
candle_mode_brightness --;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 3 clicks: add 30m to candle timer
else if (event == EV_3clicks) {
@@ -1266,7 +1266,7 @@ uint8_t candle_mode_state(Event event, uint16_t arg) {
set_level(actual_level + 32);
delay_4ms(2);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// clock tick: animate candle brightness
else if (event == EV_tick) {
@@ -1328,7 +1328,7 @@ uint8_t candle_mode_state(Event event, uint16_t arg) {
// random amplitude
//candle_wave3_depth = 2 + (pseudo_rand() % ((CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100) - 2));
candle_wave3_depth = pseudo_rand() % (CANDLE_WAVE3_MAXDEPTH * CANDLE_AMPLITUDE / 100);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -1345,19 +1345,19 @@ uint8_t boring_strobe_state(Event event, uint16_t arg) {
momentary_mode = 1; // 0 = ramping, 1 = strobes
if (event == EV_enter_state) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
// reset to police strobe for next time
boring_strobe_type = 0;
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: rotate through strobe/flasher modes
else if (event == EV_2clicks) {
boring_strobe_type = (st + 1) % NUM_BORING_STROBES;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -1411,7 +1411,7 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(USE_GOODNIGHT_MODE) || defined(USE_BEACON_MODE)
// 2 clicks: next mode
@@ -1421,7 +1421,7 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
#elif defined(USE_BEACON_MODE)
set_state(beacon_state, 0);
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -1434,19 +1434,19 @@ uint8_t tempcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if 0 // not part of a loop in this UI
// 2 clicks: battcheck mode
else if (event == EV_2clicks) {
set_state(battcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// 4 clicks: thermal config mode
else if (event == EV_4clicks) {
push_state(thermal_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -1458,7 +1458,7 @@ uint8_t beacon_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// TODO: use sleep ticks to measure time between pulses,
// to save power
@@ -1469,12 +1469,12 @@ uint8_t beacon_state(Event event, uint16_t arg) {
#else
set_state(battcheck_state, 0);
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 4 clicks: beacon config mode
else if (event == EV_4clicks) {
push_state(beacon_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -1490,12 +1490,12 @@ uint8_t goodnight_state(Event event, uint16_t arg) {
ticks_since_stepdown = 0;
blink_confirm(2);
set_level(GOODNIGHT_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: beacon mode
else if (event == EV_2clicks) {
@@ -1504,7 +1504,7 @@ uint8_t goodnight_state(Event event, uint16_t arg) {
#elif defined(USE_TEMPCHECK_MODE)
set_state(tempcheck_state, 0);
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// tick: step down (maybe) or off (maybe)
else if (event == EV_tick) {
@@ -1520,7 +1520,7 @@ uint8_t goodnight_state(Event event, uint16_t arg) {
set_state(off_state, 0);
}
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -1572,14 +1572,14 @@ uint8_t lockout_state(Event event, uint16_t arg) {
indicator_led(indicator_led_mode >> 2);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
else if (event == EV_sleep_tick) {
if ((indicator_led_mode & 0b00001100) == 0b00001100) {
indicator_blink(arg);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#ifdef USE_INDICATOR_LED
@@ -1597,7 +1597,7 @@ uint8_t lockout_state(Event event, uint16_t arg) {
indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03);
indicator_led(mode);
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if 0 // old method, deprecated in favor of "7 clicks from off"
// click, click, hold: rotate through indicator LED modes (off mode)
@@ -1624,12 +1624,12 @@ uint8_t lockout_state(Event event, uint16_t arg) {
indicator_led(mode);
#endif
//save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, click, hold, release: save indicator LED mode (off mode)
else if (event == EV_click3_hold_release) {
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#endif
@@ -1637,7 +1637,7 @@ uint8_t lockout_state(Event event, uint16_t arg) {
else if (event == EV_4clicks) {
blink_confirm(1);
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
@@ -1660,14 +1660,14 @@ uint8_t momentary_state(Event event, uint16_t arg) {
if (momentary_mode == 0) {
set_level(memorized_level);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// button was released
else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
momentary_active = 0;
set_level(0);
//go_to_standby = 1; // sleep while light is off
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// Sleep, dammit! (but wait a few seconds first)
@@ -1688,7 +1688,7 @@ uint8_t momentary_state(Event event, uint16_t arg) {
// TODO: lighted button should use lockout config?
}
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
@@ -1721,7 +1721,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
//memorized_level = MAX_1x7135;
memorized_level = (MUGGLE_FLOOR + MUGGLE_CEILING) / 2;
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// initial press: moon hint
else if (event == EV_click1_press) {
@@ -1749,12 +1749,12 @@ uint8_t muggle_state(Event event, uint16_t arg) {
set_level(memorized_level);
}
*/
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: change brightness
else if (event == EV_click1_hold) {
// ramp at half speed
- if (arg & 1) return MISCHIEF_MANAGED;
+ if (arg & 1) return EVENT_HANDLED;
// if off, start at bottom
if (muggle_off_mode) {
@@ -1776,7 +1776,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
memorized_level = m;
set_level(m);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// reverse ramp direction on hold release
else if (event == EV_click1_hold_release) {
@@ -1784,7 +1784,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
#ifdef START_AT_MEMORIZED_LEVEL
save_config_wl(); // momentary use should retain brightness level
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
/*
// click, hold: change brightness (dimmer)
@@ -1793,7 +1793,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
if (memorized_level > MUGGLE_FLOOR)
memorized_level = actual_level - 1;
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
*/
// 6 clicks: exit muggle mode
@@ -1802,7 +1802,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
muggle_mode_active = 0;
save_config();
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// tick: housekeeping
else if (event == EV_tick) {
@@ -1815,7 +1815,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
go_to_standby = 1; // sleep while light is off
}
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_THERMAL_REGULATION
// overheating is handled specially in muggle mode
@@ -1827,7 +1827,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
uint8_t new = actual_level - arg;
if (new < MUGGLE_FLOOR) { new = MUGGLE_FLOOR; }
set_level(new);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// low voltage is handled specially in muggle mode
@@ -1838,7 +1838,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
} else {
muggle_off_mode = 1;
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
@@ -1854,7 +1854,7 @@ uint8_t config_state_base(Event event, uint16_t arg,
if (event == EV_enter_state) {
config_step = 0;
set_level(0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// advance forward through config steps
else if (event == EV_tick) {
@@ -1868,13 +1868,13 @@ uint8_t config_state_base(Event event, uint16_t arg,
//set_state(retstate, retval);
pop_state();
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// an option was set (return from number_entry_state)
else if (event == EV_reenter_state) {
config_state_values[config_step] = number_entry_value;
config_step ++;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
//return EVENT_NOT_HANDLED;
// eat all other events; don't pass any through to parent
@@ -1978,7 +1978,7 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
blinks_left = arg;
entry_step = 0;
wait_ticks = 0;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// advance through the process:
// 0: wait a moment
@@ -2035,7 +2035,7 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
number_entry_value = value;
pop_state();
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// count clicks
else if (event == EV_click1_release) {
@@ -2048,7 +2048,7 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
delay_4ms(8/2);
set_level(0);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c
index 60eb843..31b250f 100644
--- a/spaghetti-monster/fsm-adc.c
+++ b/spaghetti-monster/fsm-adc.c
@@ -1,24 +1,8 @@
-/*
- * fsm-adc.c: ADC (voltage, temperature) functions for 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_ADC_C
-#define FSM_ADC_C
+// fsm-adc.c: ADC (voltage, temperature) functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
// override onboard temperature sensor definition, if relevant
#ifdef USE_EXTERNAL_TEMP_SENSOR
@@ -28,6 +12,8 @@
#define ADMUX_THERM ADMUX_THERM_EXTERNAL_SENSOR
#endif
+#include <avr/sleep.h>
+
static inline void set_admux_therm() {
#if (ATTINY == 1634)
@@ -86,6 +72,24 @@ inline void set_admux_voltage() {
ADC_start_measurement();
}
+
+#ifdef TICK_DURING_STANDBY
+inline void adc_sleep_mode() {
+ // needs a special sleep mode to get accurate measurements quickly
+ // ... full power-down ends up using more power overall, and causes
+ // some weird issues when the MCU doesn't stay awake enough cycles
+ // to complete a reading
+ #ifdef SLEEP_MODE_ADC
+ // attiny1634
+ set_sleep_mode(SLEEP_MODE_ADC);
+ #elif defined(AVRXMEGA3) // ATTINY816, 817, etc
+ set_sleep_mode(SLEEP_MODE_STANDBY);
+ #else
+ #error No ADC sleep mode defined for this hardware.
+ #endif
+}
+#endif
+
inline void ADC_start_measurement() {
#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 841) || (ATTINY == 1634)
ADCSRA |= (1 << ADSC) | (1 << ADIE);
@@ -124,10 +128,13 @@ inline void ADC_on()
ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL;
//ADCSRA |= (1 << ADSC); // start measuring
#elif defined(AVRXMEGA3) // ATTINY816, 817, etc
- set_admux_voltage();
VREF.CTRLA |= VREF_ADC0REFSEL_1V1_gc; // Set Vbg ref to 1.1V
- ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm; // Enabled, free-running (aka, auto-retrigger)
- ADC0.COMMAND |= ADC_STCONV_bm; // Start the ADC conversions
+ // Enabled, free-running (aka, auto-retrigger), run in standby
+ ADC0.CTRLA = ADC_ENABLE_bm | ADC_FREERUN_bm | ADC_RUNSTBY_bm;
+ // set a INITDLY value because the AVR manual says so (section 30.3.5)
+ // (delay 1st reading until Vref is stable)
+ ADC0.CTRLD |= ADC_INITDLY_DLY16_gc;
+ set_admux_voltage();
#else
#error Unrecognized MCU type
#endif
@@ -135,7 +142,7 @@ inline void ADC_on()
inline void ADC_off() {
#ifdef AVRXMEGA3 // ATTINY816, 817, etc
- ADC0.CTRLA &= ~(ADC_ENABLE_bm); // disable the ADC
+ ADC0.CTRLA &= ~(ADC_ENABLE_bm); // disable the ADC
#else
ADCSRA &= ~(1<<ADEN); //ADC off
#endif
@@ -149,7 +156,7 @@ static inline uint8_t calc_voltage_divider(uint16_t value) {
uint8_t result = ((value / adc_per_volt)
+ VOLTAGE_FUDGE_FACTOR
#ifdef USE_VOLTAGE_CORRECTION
- + voltage_correction - 7
+ + VOLT_CORR - 7
#endif
) >> 1;
return result;
@@ -260,6 +267,8 @@ void adc_deferred() {
// (and the usual standby level is only ~20 uA)
if (go_to_standby) {
ADC_off();
+ // if any measurements were in progress, they're done now
+ adc_active_now = 0;
// also, only check the battery while asleep, not the temperature
adc_channel = 0;
}
@@ -314,18 +323,23 @@ static inline void ADC_voltage_handler() {
else if (go_to_standby) { // weaker lowpass while asleep
// occasionally the aux LED color can oscillate during standby,
// while using "voltage" mode ... so try to reduce the oscillation
- uint16_t m = adc_raw[0];
- uint16_t v = adc_smooth[0];
+ uint16_t r = adc_raw[0];
+ uint16_t s = adc_smooth[0];
#if 0
- // fixed-rate lowpass, slow, more stable but takes longer to settle
- if (m < v) { v -= 64; }
- if (m > v) { v += 64; }
+ // fixed-rate lowpass, stable but very slow
+ // (move by only 0.5 ADC units per measurement, 1 ADC unit = 64)
+ if (r < s) { s -= 32; }
+ if (r > s) { s += 32; }
+ #elif 1
+ // 1/8th proportional lowpass, faster but less stable
+ int16_t diff = (r/8) - (s/8);
+ s += diff;
#else
- // weighted lowpass, faster but less stable
- v = (m>>1) + (v>>1);
+ // 50% proportional lowpass, fastest but least stable
+ s = (r>>1) + (s>>1);
#endif
- adc_smooth[0] = v;
- measurement = v;
+ adc_smooth[0] = s;
+ measurement = s;
}
#endif
else measurement = adc_smooth[0];
@@ -349,7 +363,7 @@ static inline void ADC_voltage_handler() {
voltage = ((uint16_t)(2*1.1*1024*10)/(measurement>>6)
+ VOLTAGE_FUDGE_FACTOR
#ifdef USE_VOLTAGE_CORRECTION
- + voltage_correction - 7
+ + VOLT_CORR - 7
#endif
) >> 1;
#endif
@@ -428,10 +442,10 @@ static inline void ADC_temperature_handler() {
// Convert ADC units to Celsius (ish)
#ifndef USE_EXTERNAL_TEMP_SENSOR
// onboard sensor for attiny25/45/85/1634
- temperature = (measurement>>1) + THERM_CAL_OFFSET + (int16_t)therm_cal_offset - 275;
+ temperature = (measurement>>1) + THERM_CAL_OFFSET + (int16_t)TH_CAL - 275;
#else
// external sensor
- temperature = EXTERN_TEMP_FORMULA(measurement>>1) + THERM_CAL_OFFSET + (int16_t)therm_cal_offset;
+ temperature = EXTERN_TEMP_FORMULA(measurement>>1) + THERM_CAL_OFFSET + (int16_t)TH_CAL;
#endif
// how much has the temperature changed between now and a few seconds ago?
@@ -447,10 +461,10 @@ static inline void ADC_temperature_handler() {
pt = measurement + (diff * THERM_LOOKAHEAD);
// convert temperature limit from C to raw 16-bit ADC units
- // C = (ADC>>6) - 275 + THERM_CAL_OFFSET + therm_cal_offset;
+ // C = (ADC>>6) - 275 + THERM_CAL_OFFSET + TH_CAL;
// ... so ...
- // (C + 275 - THERM_CAL_OFFSET - therm_cal_offset) << 6 = ADC;
- uint16_t ceil = (therm_ceil + 275 - therm_cal_offset - THERM_CAL_OFFSET) << 1;
+ // (C + 275 - THERM_CAL_OFFSET - TH_CAL) << 6 = ADC;
+ uint16_t ceil = (TH_CEIL + 275 - TH_CAL - THERM_CAL_OFFSET) << 1;
int16_t offset = pt - ceil;
// bias small errors toward zero, while leaving large errors mostly unaffected
@@ -557,4 +571,3 @@ void battcheck() {
}
#endif
-#endif
diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h
index db2bb7b..1bb67ed 100644
--- a/spaghetti-monster/fsm-adc.h
+++ b/spaghetti-monster/fsm-adc.h
@@ -1,25 +1,8 @@
-/*
- * fsm-adc.h: ADC (voltage, temperature) functions for 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_ADC_H
-#define FSM_ADC_H
+// fsm-adc.h: ADC (voltage, temperature) functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
#if defined(USE_LVP) || defined(USE_THERMAL_REGULATION)
// use raw value instead of lowpassed value for the next N measurements
@@ -49,6 +32,9 @@ volatile uint8_t adc_reset = 2;
#endif
#endif
+#ifdef TICK_DURING_STANDBY
+volatile uint8_t adc_active_now = 0; // sleep LVP needs a different sleep mode
+#endif
volatile uint8_t irq_adc = 0; // ADC interrupt happened?
uint8_t adc_sample_count = 0; // skip the first sample; it's junk
uint8_t adc_channel = 0; // 0=voltage, 1=temperature
@@ -63,9 +49,14 @@ void adc_deferred(); // do the actual ADC-related calculations
static inline void ADC_voltage_handler();
uint8_t voltage = 0;
#ifdef USE_VOLTAGE_CORRECTION
-// same 0.05V units as fudge factor,
-// but 7 is neutral, and the expected range is from 1 to 13
-uint8_t voltage_correction = 7;
+ #ifdef USE_CFG
+ #define VOLT_CORR cfg.voltage_correction
+ #else
+ // same 0.05V units as fudge factor,
+ // but 7 is neutral, and the expected range is from 1 to 13
+ uint8_t voltage_correction = 7;
+ #define VOLT_CORR voltage_correction
+ #endif
#endif
#ifdef USE_LVP
void low_voltage();
@@ -98,8 +89,15 @@ void battcheck();
#endif
// temperature now, in C (ish)
int16_t temperature;
-uint8_t therm_ceil = DEFAULT_THERM_CEIL;
-int8_t therm_cal_offset = 0;
+#ifdef USE_CFG
+ #define TH_CEIL cfg.therm_ceil
+ #define TH_CAL cfg.therm_cal_offset
+#else
+ #define TH_CEIL therm_ceil
+ #define TH_CAL therm_cal_offset
+ uint8_t therm_ceil = DEFAULT_THERM_CEIL;
+ int8_t therm_cal_offset = 0;
+#endif
static inline void ADC_temperature_handler();
#endif // ifdef USE_THERMAL_REGULATION
@@ -108,5 +106,7 @@ inline void ADC_on();
inline void ADC_off();
inline void ADC_start_measurement();
-
+#ifdef TICK_DURING_STANDBY
+inline void adc_sleep_mode();
#endif
+
diff --git a/spaghetti-monster/fsm-channels.c b/spaghetti-monster/fsm-channels.c
new file mode 100644
index 0000000..cc78536
--- /dev/null
+++ b/spaghetti-monster/fsm-channels.c
@@ -0,0 +1,357 @@
+// fsm-channels.c: Channel mode functions for SpaghettiMonster.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+#include "fsm-ramping.h"
+
+
+#if NUM_CHANNEL_MODES > 1
+void set_channel_mode(uint8_t mode) {
+ if (mode == channel_mode) return; // abort if nothing to do
+
+ uint8_t cur_level = actual_level;
+
+ // turn off old LEDs before changing channel
+ set_level(0);
+
+ // change the channel
+ channel_mode = mode;
+
+ // update the LEDs
+ set_level(cur_level);
+}
+#endif // if NUM_CHANNEL_MODES > 1
+
+
+#ifdef USE_CALC_2CH_BLEND
+// calculate a "tint ramp" blend between 2 channels
+// results are placed in *warm and *cool vars
+// brightness : total amount of light units to distribute
+// top : maximum allowed brightness per channel
+// blend : ratio between warm and cool (0 = warm, 128 = 50%, 255 = cool)
+void calc_2ch_blend(
+ PWM_DATATYPE *warm,
+ PWM_DATATYPE *cool,
+ PWM_DATATYPE brightness,
+ PWM_DATATYPE top,
+ uint8_t blend) {
+
+ #ifndef TINT_RAMPING_CORRECTION
+ #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint
+ #endif
+
+ // calculate actual PWM levels based on a single-channel ramp
+ // and a blend value
+ PWM_DATATYPE warm_PWM, cool_PWM;
+ PWM_DATATYPE2 base_PWM = brightness;
+
+ #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
+ uint8_t level = actual_level - 1;
+
+ // middle tints sag, so correct for that effect
+ // by adding extra power which peaks at the middle tint
+ // (correction is only necessary when PWM is fast)
+ if (level > HALFSPEED_LEVEL) {
+ base_PWM = brightness
+ + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64)
+ * triangle_wave(blend) / 255);
+ }
+ // fade the triangle wave out when above 100% power,
+ // so it won't go over 200%
+ if (brightness > top) {
+ base_PWM -= 2 * (
+ ((brightness - top) * TINT_RAMPING_CORRECTION / 64)
+ * triangle_wave(blend) / 255
+ );
+ }
+ // guarantee no more than 200% power
+ if (base_PWM > (top << 1)) { base_PWM = top << 1; }
+ #endif
+
+ cool_PWM = (((PWM_DATATYPE2)blend * (PWM_DATATYPE2)base_PWM) + 127) / 255;
+ warm_PWM = base_PWM - cool_PWM;
+ // when running at > 100% power, spill extra over to other channel
+ if (cool_PWM > top) {
+ warm_PWM += (cool_PWM - top);
+ cool_PWM = top;
+ } else if (warm_PWM > top) {
+ cool_PWM += (warm_PWM - top);
+ warm_PWM = top;
+ }
+
+ *warm = warm_PWM;
+ *cool = cool_PWM;
+}
+#endif // ifdef USE_CALC_2CH_BLEND
+
+
+#ifdef USE_HSV2RGB
+RGB_t hsv2rgb(uint8_t h, uint8_t s, uint16_t v) {
+ RGB_t color;
+
+ if (s == 0) { // grey
+ color.r = color.g = color.b = v;
+ return color;
+ }
+
+ uint8_t region;
+ uint16_t fpart;
+ uint16_t high, low, rising, falling;
+
+ // hue has 6 segments, 0-5
+ region = ((uint16_t)h * 6) >> 8;
+ // find remainder part, make it from 0-255
+ fpart = ((uint16_t)h * 6) - (region << 8);
+
+ // calculate graph segments, doing integer multiplication
+ // TODO: calculate 16-bit results, not 8-bit
+ high = v;
+ low = ((uint32_t)v * (255 - s)) >> 8;
+ // TODO: use a cosine crossfade instead of linear
+ // (because it looks better and feels more natural)
+ falling = ((uint32_t)v * (255 - ((s * fpart) >> 8))) >> 8;
+ rising = ((uint32_t)v * (255 - ((s * (255 - fpart)) >> 8))) >> 8;
+
+ // default floor
+ color.r = low;
+ color.g = low;
+ color.b = low;
+
+ // assign graph shapes based on color cone region
+ switch (region) {
+ case 0:
+ color.r = high;
+ color.g = rising;
+ //color.b = low;
+ break;
+ case 1:
+ color.r = falling;
+ color.g = high;
+ //color.b = low;
+ break;
+ case 2:
+ //color.r = low;
+ color.g = high;
+ color.b = rising;
+ break;
+ case 3:
+ //color.r = low;
+ color.g = falling;
+ color.b = high;
+ break;
+ case 4:
+ color.r = rising;
+ //color.g = low;
+ color.b = high;
+ break;
+ default:
+ color.r = high;
+ //color.g = low;
+ color.b = falling;
+ break;
+ }
+
+ return color;
+}
+#endif // ifdef USE_HSV2RGB
+
+
+///// Common set_level_*() functions shared by multiple lights /////
+// (unique lights should use their own,
+// but these common versions cover most of the common hardware designs)
+
+// TODO: upgrade some older lights to dynamic PWM
+// TODO: 1ch w/ dynamic PWM
+// TODO: 1ch w/ dynamic PWM and opamp enable pins?
+// TODO: 2ch stacked w/ dynamic PWM
+// TODO: 2ch stacked w/ dynamic PWM and opamp enable pins?
+
+
+#ifdef USE_SET_LEVEL_1CH
+// single set of LEDs with 1 power channel
+void set_level_1ch(uint8_t level) {
+ if (level == 0) {
+ LOW_PWM_LVL = 0;
+ } else {
+ level --; // PWM array index = level - 1
+ LOW_PWM_LVL = PWM_GET(low_pwm_levels, level);
+ }
+}
+#endif
+
+
+#ifdef USE_SET_LEVEL_2CH_STACKED
+// single set of LEDs with 2 stacked power channels, DDFET+1 or DDFET+linear
+void set_level_2ch_stacked(uint8_t level) {
+ if (level == 0) {
+ LOW_PWM_LVL = 0;
+ HIGH_PWM_LVL = 0;
+ } else {
+ level --; // PWM array index = level - 1
+ LOW_PWM_LVL = PWM_GET(low_pwm_levels, level);
+ HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level);
+ }
+}
+#endif
+
+
+#ifdef USE_SET_LEVEL_3CH_STACKED
+// single set of LEDs with 3 stacked power channels, like DDFET+N+1
+void set_level_3ch_stacked(uint8_t level) {
+ if (level == 0) {
+ LOW_PWM_LVL = 0;
+ MED_PWM_LVL = 0;
+ HIGH_PWM_LVL = 0;
+ } else {
+ level --; // PWM array index = level - 1
+ LOW_PWM_LVL = PWM_GET(low_pwm_levels, level);
+ MED_PWM_LVL = PWM_GET(med_pwm_levels, level);
+ HIGH_PWM_LVL = PWM_GET(high_pwm_levels, level);
+ }
+}
+#endif
+
+
+#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY))
+void set_level_2ch_blend() {
+ #ifndef TINT_RAMPING_CORRECTION
+ #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint
+ #endif
+
+ // calculate actual PWM levels based on a single-channel ramp
+ // and a global tint value
+ //PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
+ uint16_t brightness = PWM1_LVL;
+ uint16_t warm_PWM, cool_PWM;
+ #ifdef USE_STACKED_DYN_PWM
+ uint16_t top = PWM1_TOP;
+ //uint16_t top = PWM_GET(pwm_tops, actual_level-1);
+ #else
+ const uint16_t top = PWM_TOP;
+ #endif
+
+ // auto-tint modes
+ uint8_t mytint;
+ uint8_t level = actual_level - 1;
+ #if 1
+ // perceptual by ramp level
+ if (tint == 0) { mytint = 255 * (uint16_t)level / RAMP_SIZE; }
+ else if (tint == 255) { mytint = 255 - (255 * (uint16_t)level / RAMP_SIZE); }
+ #else
+ // linear with power level
+ //if (tint == 0) { mytint = brightness; }
+ //else if (tint == 255) { mytint = 255 - brightness; }
+ #endif
+ // stretch 1-254 to fit 0-255 range (hits every value except 98 and 198)
+ else { mytint = (tint * 100 / 99) - 1; }
+
+ PWM_DATATYPE2 base_PWM = brightness;
+ #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
+ // middle tints sag, so correct for that effect
+ // by adding extra power which peaks at the middle tint
+ // (correction is only necessary when PWM is fast)
+ if (level > HALFSPEED_LEVEL) {
+ base_PWM = brightness
+ + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255);
+ }
+ // fade the triangle wave out when above 100% power,
+ // so it won't go over 200%
+ if (brightness > top) {
+ base_PWM -= 2 * (
+ ((brightness - top) * TINT_RAMPING_CORRECTION / 64)
+ * triangle_wave(mytint) / 255
+ );
+ }
+ // guarantee no more than 200% power
+ if (base_PWM > (top << 1)) { base_PWM = top << 1; }
+ #endif
+
+ cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255;
+ warm_PWM = base_PWM - cool_PWM;
+ // when running at > 100% power, spill extra over to other channel
+ if (cool_PWM > top) {
+ warm_PWM += (cool_PWM - top);
+ cool_PWM = top;
+ } else if (warm_PWM > top) {
+ cool_PWM += (warm_PWM - top);
+ warm_PWM = top;
+ }
+
+ TINT1_LVL = warm_PWM;
+ TINT2_LVL = cool_PWM;
+
+ // disable the power channel, if relevant
+ #ifdef LED_ENABLE_PIN
+ if (warm_PWM)
+ LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN);
+ else
+ LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN);
+ #endif
+ #ifdef LED2_ENABLE_PIN
+ if (cool_PWM)
+ LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN);
+ else
+ LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN);
+ #endif
+}
+#endif // ifdef USE_TINT_RAMPING
+
+
+#ifdef USE_GRADUAL_TICK_1CH
+void gradual_tick_1ch() {
+ GRADUAL_TICK_SETUP();
+
+ GRADUAL_ADJUST_1CH(low_pwm_levels, LOW_PWM_LVL);
+
+ // did we go far enough to hit the next defined ramp level?
+ // if so, update the main ramp level tracking var
+ if ((LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt)))
+ {
+ GRADUAL_IS_ACTUAL();
+ }
+}
+#endif
+
+
+#ifdef USE_GRADUAL_TICK_2CH_STACKED
+void gradual_tick_2ch_stacked() {
+ GRADUAL_TICK_SETUP();
+
+ GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP);
+ GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL);
+
+ // did we go far enough to hit the next defined ramp level?
+ // if so, update the main ramp level tracking var
+ if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))
+ && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt))
+ )
+ {
+ GRADUAL_IS_ACTUAL();
+ }
+}
+#endif
+
+
+#ifdef USE_GRADUAL_TICK_3CH_STACKED
+void gradual_tick_3ch_stacked() {
+ GRADUAL_TICK_SETUP();
+
+ GRADUAL_ADJUST(low_pwm_levels, LOW_PWM_LVL, PWM_TOP);
+ GRADUAL_ADJUST(med_pwm_levels, MED_PWM_LVL, PWM_TOP);
+ GRADUAL_ADJUST_1CH(high_pwm_levels, HIGH_PWM_LVL);
+
+ // did we go far enough to hit the next defined ramp level?
+ // if so, update the main ramp level tracking var
+ if ( (LOW_PWM_LVL == PWM_GET(low_pwm_levels, gt))
+ && (MED_PWM_LVL == PWM_GET(med_pwm_levels, gt))
+ && (HIGH_PWM_LVL == PWM_GET(high_pwm_levels, gt))
+ )
+ {
+ GRADUAL_IS_ACTUAL();
+ }
+}
+#endif
+
+
diff --git a/spaghetti-monster/fsm-channels.h b/spaghetti-monster/fsm-channels.h
new file mode 100644
index 0000000..218f4f5
--- /dev/null
+++ b/spaghetti-monster/fsm-channels.h
@@ -0,0 +1,141 @@
+// fsm-channels.h: Channel mode functions for SpaghettiMonster.
+// Copyright (C) 2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
+// always enable channel modes, even if there is only one
+#define USE_CHANNEL_MODES
+
+// typedefs
+typedef void SetLevelFunc(uint8_t level);
+typedef SetLevelFunc * SetLevelFuncPtr;
+
+typedef bool GradualTickFunc(uint8_t gt);
+typedef GradualTickFunc * GradualTickFuncPtr;
+
+// TODO: implement custom 3H handlers
+typedef void ChannelArgFunc();
+typedef ChannelArgFunc * ChannelArgFuncPtr;
+
+typedef struct Channel {
+ SetLevelFuncPtr set_level;
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ GradualTickFuncPtr gradual_tick;
+ #endif
+ #ifdef USE_CUSTOM_3H_HANDLERS
+ // TODO: implement custom 3H handlers
+ ChannelArgFuncPtr ramp_channel_arg;
+ #endif
+ #ifdef USE_CHANNEL_MODE_ARGS
+ bool has_args;
+ //uint8_t arg; // is in the config struct, not here
+ #endif
+} Channel;
+
+Channel channels[]; // values are defined in the hwdef-*.c
+
+// TODO: size-optimize the case with only 1 channel mode?
+// (the arrays and stuff shouldn't be needed)
+
+#if NUM_CHANNEL_MODES > 1
+ #define USE_CHANNEL_MODES
+ // current multi-channel mode
+ uint8_t channel_mode = DEFAULT_CHANNEL_MODE;
+#else
+ #define channel_mode 0
+#endif
+
+#ifdef USE_CUSTOM_CHANNEL_3H_MODES
+// different 3H behavior per channel?
+// TODO: move to progmem
+// TODO: move to Anduril, not FSM
+StatePtr channel_3H_modes[NUM_CHANNEL_MODES];
+#endif
+
+//#ifdef USE_CHANNEL_MODE_TOGGLES
+#if NUM_CHANNEL_MODES > 1
+// user can take unwanted modes out of the rotation
+// bitmask
+#ifdef USE_CFG
+ #define channel_mode_enabled(n) ((cfg.channel_modes_enabled >> n) & 1)
+ #define channel_mode_enable(n) cfg.channel_modes_enabled |= (1 << n)
+ #define channel_mode_disable(n) cfg.channel_modes_enabled &= ((1 << n) ^ 0xff)
+#else
+ uint16_t channel_modes_enabled = CHANNEL_MODES_ENABLED;
+ #define channel_mode_enabled(n) ((channel_modes_enabled >> n) & 1)
+ #define channel_mode_enable(n) channel_modes_enabled |= (1 << n)
+ #define channel_mode_disable(n) channel_modes_enabled &= ((1 << n) ^ 0xff)
+ #endif
+#endif
+
+#ifdef USE_CHANNEL_MODE_ARGS
+ #ifndef USE_CFG
+ // one byte of extra data per channel mode, like for tint value
+ uint8_t channel_mode_args[NUM_CHANNEL_MODES] = { CHANNEL_MODE_ARGS };
+ #endif
+ // which modes respond to their "arg", and which don't?
+ //const uint8_t channel_has_args = CHANNEL_HAS_ARGS;
+ //#define channel_has_args(n) ((CHANNEL_HAS_ARGS >> n) & 1)
+ // struct member
+ #define channel_has_args(n) (channels[n].has_args)
+#endif
+
+#if NUM_CHANNEL_MODES > 1
+void set_channel_mode(uint8_t mode);
+#endif
+
+#ifdef USE_CALC_2CH_BLEND
+void calc_2ch_blend(
+ PWM_DATATYPE *warm,
+ PWM_DATATYPE *cool,
+ PWM_DATATYPE brightness,
+ PWM_DATATYPE top,
+ uint8_t blend);
+#endif
+
+#ifdef USE_HSV2RGB
+typedef struct RGB_t {
+ uint16_t r;
+ uint16_t g;
+ uint16_t b;
+} RGB_t;
+RGB_t hsv2rgb(uint8_t h, uint8_t s, uint16_t v);
+#endif // ifdef USE_HSV2RGB
+
+
+#ifdef USE_SET_LEVEL_1CH
+// TODO: remove this
+void set_level_1ch(uint8_t level);
+#endif
+
+#ifdef USE_SET_LEVEL_2CH_STACKED
+// TODO: remove this
+void set_level_2ch_stacked(uint8_t level);
+#endif
+
+#ifdef USE_SET_LEVEL_3CH_STACKED
+// TODO: remove this
+void set_level_3ch_stacked(uint8_t level);
+#endif
+
+#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY))
+// TODO: remove this
+void set_level_2ch_blend();
+#endif
+
+#ifdef USE_GRADUAL_TICK_1CH
+// TODO: remove this
+void gradual_tick_1ch();
+#endif
+
+#ifdef USE_GRADUAL_TICK_2CH_STACKED
+// TODO: remove this
+void gradual_tick_2ch_stacked();
+#endif
+
+#ifdef USE_GRADUAL_TICK_3CH_STACKED
+// TODO: remove this
+void gradual_tick_3ch_stacked();
+#endif
+
diff --git a/spaghetti-monster/fsm-eeprom.c b/spaghetti-monster/fsm-eeprom.c
index 4c734a2..66cdd78 100644
--- a/spaghetti-monster/fsm-eeprom.c
+++ b/spaghetti-monster/fsm-eeprom.c
@@ -1,24 +1,8 @@
-/*
- * fsm-eeprom.c: EEPROM API for 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_EEPROM_C
-#define FSM_EEPROM_C
+// fsm-eeprom.c: EEPROM API for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include "fsm-eeprom.h"
@@ -126,5 +110,3 @@ void save_eeprom_wl() {
}
#endif
-
-#endif
diff --git a/spaghetti-monster/fsm-eeprom.h b/spaghetti-monster/fsm-eeprom.h
index 3621106..440d2b3 100644
--- a/spaghetti-monster/fsm-eeprom.h
+++ b/spaghetti-monster/fsm-eeprom.h
@@ -1,24 +1,8 @@
-/*
- * fsm-eeprom.h: EEPROM API for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-eeprom.h: EEPROM API for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_EEPROM_H
-#define FSM_EEPROM_H
+#pragma once
#include <avr/eeprom.h>
@@ -33,9 +17,10 @@
#endif
#ifdef USE_EEPROM
-#if EEPROM_BYTES >= (EEPSIZE/2)
-#error Requested EEPROM_BYTES too big.
-#endif
+// this fails when EEPROM_BYTES is a sizeof()
+//#if EEPROM_BYTES >= (EEPSIZE/2)
+//#error Requested EEPROM_BYTES too big.
+//#endif
#ifdef EEPROM_OVERRIDE
uint8_t *eeprom;
#else
@@ -65,4 +50,3 @@ void save_eeprom_wl();
// if this marker isn't found, the eeprom is assumed to be blank
#define EEP_MARKER 0b10100101
-#endif
diff --git a/spaghetti-monster/fsm-events.c b/spaghetti-monster/fsm-events.c
index 3279c14..6987ae2 100644
--- a/spaghetti-monster/fsm-events.c
+++ b/spaghetti-monster/fsm-events.c
@@ -1,24 +1,8 @@
-/*
- * fsm-events.c: Event-handling functions for 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_EVENTS_C
-#define FSM_EVENTS_C
+// fsm-events.c: Event-handling functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include <util/delay_basic.h>
@@ -143,7 +127,7 @@ uint8_t nice_delay_ms(uint16_t ms) {
uint8_t level = actual_level; // volatile, avoid repeat access
if (level < QUARTERSPEED_LEVEL) {
clock_prescale_set(clock_div_4);
- _delay_loop_2(BOGOMIPS*90/100/4);
+ _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100/4);
}
//else if (level < HALFSPEED_LEVEL) {
// clock_prescale_set(clock_div_2);
@@ -151,7 +135,7 @@ uint8_t nice_delay_ms(uint16_t ms) {
//}
else {
clock_prescale_set(clock_div_1);
- _delay_loop_2(BOGOMIPS*90/100);
+ _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100);
}
// restore regular clock speed
clock_prescale_set(clock_div_1);
@@ -159,13 +143,13 @@ uint8_t nice_delay_ms(uint16_t ms) {
// underclock MCU to save power
clock_prescale_set(clock_div_4);
// wait
- _delay_loop_2(BOGOMIPS*90/100/4);
+ _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100/4);
// restore regular clock speed
clock_prescale_set(clock_div_1);
#endif // ifdef USE_RAMPING
#else
// wait
- _delay_loop_2(BOGOMIPS*90/100);
+ _delay_loop_2(BOGOMIPS*DELAY_FACTOR/100);
#endif // ifdef USE_DYNAMIC_UNDERCLOCKING
// run pending system processes while we wait
@@ -212,4 +196,3 @@ uint8_t nice_delay_s() {
}
*/
-#endif
diff --git a/spaghetti-monster/fsm-events.h b/spaghetti-monster/fsm-events.h
index 79a0aff..575af1b 100644
--- a/spaghetti-monster/fsm-events.h
+++ b/spaghetti-monster/fsm-events.h
@@ -1,24 +1,8 @@
-/*
- * fsm-events.h: Event-handling functions for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-events.h: Event-handling functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_EVENTS_H
-#define FSM_EVENTS_H
+#pragma once
#include <avr/pgmspace.h>
@@ -70,6 +54,11 @@ uint8_t push_event(uint8_t ev_type); // only for use by PCINT_inner()
// TODO: Maybe move these to their own file...
// ... this probably isn't the right place for delays.
+#ifndef DELAY_FACTOR
+ // adjust the timing of delays, lower = shorter delays
+ // 90 = 90% delay, 10% for other things
+ #define DELAY_FACTOR 92
+#endif
inline void interrupt_nice_delays();
uint8_t nice_delay_ms(uint16_t ms);
//uint8_t nice_delay_s();
@@ -230,5 +219,3 @@ void delay_4ms(uint8_t ms);
#define EV_click15_hold (B_CLICK|B_HOLD|B_PRESS|15)
#define EV_click15_hold_release (B_CLICK|B_HOLD|B_RELEASE|B_TIMEOUT|15)
-
-#endif
diff --git a/spaghetti-monster/fsm-main.c b/spaghetti-monster/fsm-main.c
index 30b8a67..066188c 100644
--- a/spaghetti-monster/fsm-main.c
+++ b/spaghetti-monster/fsm-main.c
@@ -1,24 +1,8 @@
-/*
- * fsm-main.c: main() function for 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_MAIN_C
-#define FSM_MAIN_C
+// fsm-main.c: main() function for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include "fsm-main.h"
@@ -39,50 +23,55 @@ ISR(TIMER1_COMPA_vect) {
}
#endif
+// FIXME: hw_setup() shouldn't be here ... move it entirely to hwdef files
#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
static inline void hw_setup() {
- // configure PWM channels
- #if PWM_CHANNELS >= 1
- DDRB |= (1 << PWM1_PIN);
- TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
- TCCR0A = PHASE;
- #if (PWM1_PIN == PB4) // Second PWM counter is ... weird
- TCCR1 = _BV (CS10);
- GTCCR = _BV (COM1B1) | _BV (PWM1B);
- OCR1C = 255; // Set ceiling value to maximum
- #endif
- #endif
- // tint ramping needs second channel enabled,
- // despite PWM_CHANNELS being only 1
- #if (PWM_CHANNELS >= 2) || defined(USE_TINT_RAMPING)
- DDRB |= (1 << PWM2_PIN);
- #if (PWM2_PIN == PB4) // Second PWM counter is ... weird
- TCCR1 = _BV (CS10);
- GTCCR = _BV (COM1B1) | _BV (PWM1B);
- OCR1C = 255; // Set ceiling value to maximum
- #endif
- #endif
- #if PWM_CHANNELS >= 3
- DDRB |= (1 << PWM3_PIN);
- #if (PWM3_PIN == PB4) // Second PWM counter is ... weird
- TCCR1 = _BV (CS10);
- GTCCR = _BV (COM1B1) | _BV (PWM1B);
- OCR1C = 255; // Set ceiling value to maximum
- #endif
- #endif
- #if PWM_CHANNELS >= 4
- // 4th PWM channel is ... not actually supported in hardware :(
- DDRB |= (1 << PWM4_PIN);
- //OCR1C = 255; // Set ceiling value to maximum
- TCCR1 = 1<<CTC1 | 1<<PWM1A | 3<<COM1A0 | 2<<CS10;
- GTCCR = (2<<COM1B0) | (1<<PWM1B);
- // set up an interrupt to control PWM4 pin
- TIMSK |= (1<<OCIE1A) | (1<<TOIE1);
- #endif
-
- // configure e-switch
- PORTB = (1 << SWITCH_PIN); // e-switch is the only input
- PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+ #if !defined(USE_GENERIC_HWDEF_SETUP)
+ hwdef_setup();
+ #else
+ // configure PWM channels
+ #if PWM_CHANNELS >= 1
+ DDRB |= (1 << PWM1_PIN);
+ TCCR0B = 0x01; // pre-scaler for timer (1 => 1, 2 => 8, 3 => 64...)
+ TCCR0A = PHASE;
+ #if (PWM1_PIN == PB4) // Second PWM counter is ... weird
+ TCCR1 = _BV (CS10);
+ GTCCR = _BV (COM1B1) | _BV (PWM1B);
+ OCR1C = 255; // Set ceiling value to maximum
+ #endif
+ #endif
+ // tint ramping needs second channel enabled,
+ // despite PWM_CHANNELS being only 1
+ #if (PWM_CHANNELS >= 2) || defined(USE_TINT_RAMPING)
+ DDRB |= (1 << PWM2_PIN);
+ #if (PWM2_PIN == PB4) // Second PWM counter is ... weird
+ TCCR1 = _BV (CS10);
+ GTCCR = _BV (COM1B1) | _BV (PWM1B);
+ OCR1C = 255; // Set ceiling value to maximum
+ #endif
+ #endif
+ #if PWM_CHANNELS >= 3
+ DDRB |= (1 << PWM3_PIN);
+ #if (PWM3_PIN == PB4) // Second PWM counter is ... weird
+ TCCR1 = _BV (CS10);
+ GTCCR = _BV (COM1B1) | _BV (PWM1B);
+ OCR1C = 255; // Set ceiling value to maximum
+ #endif
+ #endif
+ #if PWM_CHANNELS >= 4
+ // 4th PWM channel is ... not actually supported in hardware :(
+ DDRB |= (1 << PWM4_PIN);
+ //OCR1C = 255; // Set ceiling value to maximum
+ TCCR1 = 1<<CTC1 | 1<<PWM1A | 3<<COM1A0 | 2<<CS10;
+ GTCCR = (2<<COM1B0) | (1<<PWM1B);
+ // set up an interrupt to control PWM4 pin
+ TIMSK |= (1<<OCIE1A) | (1<<TOIE1);
+ #endif
+
+ // configure e-switch
+ PORTB = (1 << SWITCH_PIN); // e-switch is the only input
+ PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
+ #endif // ifdef USE_GENERIC_HWDEF_SETUP
}
#elif (ATTINY == 1634) || defined(AVRXMEGA3) // ATTINY816, 817, etc
static inline void hw_setup() {
@@ -220,4 +209,3 @@ void handle_deferred_interrupts() {
}
}
-#endif
diff --git a/spaghetti-monster/fsm-main.h b/spaghetti-monster/fsm-main.h
index 55ae2ff..2e2a111 100644
--- a/spaghetti-monster/fsm-main.h
+++ b/spaghetti-monster/fsm-main.h
@@ -1,27 +1,10 @@
-/*
- * fsm-main.h: main() function for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-main.h: main() function for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_MAIN_H
-#define FSM_MAIN_H
+#pragma once
int main();
// needs to run frequently to execute the logic for WDT and ADC and stuff
void handle_deferred_interrupts();
-#endif
diff --git a/spaghetti-monster/fsm-misc.c b/spaghetti-monster/fsm-misc.c
index 9a10a9c..bc10ea1 100644
--- a/spaghetti-monster/fsm-misc.c
+++ b/spaghetti-monster/fsm-misc.c
@@ -1,25 +1,8 @@
-/*
- * fsm-misc.c: Miscellaneous function for 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_MISC_C
-#define FSM_MISC_C
+// fsm-misc.c: Miscellaneous function for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+#pragma once
#ifdef USE_DYNAMIC_UNDERCLOCKING
void auto_clock_speed() {
@@ -47,14 +30,39 @@ uint8_t blink_digit(uint8_t num) {
// "zero" digit gets a single short blink
uint8_t ontime = BLINK_SPEED * 2 / 12;
- if (!num) { ontime = 8; num ++; }
+ if (!num) { ontime = BLINK_ONCE_TIME; num ++; }
+
+ #ifdef BLINK_CHANNEL
+ // channel is set per blink, to prevent issues
+ // if another mode interrupts us (like a config menu)
+ uint8_t old_channel = channel_mode;
+ #endif
for (; num>0; num--) {
+ // TODO: allow setting a blink channel mode per build target
+ #ifdef BLINK_CHANNEL
+ set_channel_mode(BLINK_CHANNEL);
+ #endif
set_level(BLINK_BRIGHTNESS);
+ #ifdef BLINK_CHANNEL
+ channel_mode = old_channel;
+ #endif
nice_delay_ms(ontime);
+
+ #ifdef BLINK_CHANNEL
+ set_channel_mode(BLINK_CHANNEL);
+ #endif
set_level(0);
+ #ifdef BLINK_CHANNEL
+ channel_mode = old_channel;
+ #endif
nice_delay_ms(BLINK_SPEED * 3 / 12);
}
+
+ #ifdef BLINK_CHANNEL
+ set_channel_mode(old_channel);
+ #endif
+
return nice_delay_ms(BLINK_SPEED * 8 / 12);
}
#endif
@@ -201,7 +209,7 @@ void button_led_set(uint8_t lvl) {
break;
#else
-
+
case 0: // LED off
BUTTON_LED_DDR &= 0xff ^ (1 << BUTTON_LED_PIN);
BUTTON_LED_PUE &= 0xff ^ (1 << BUTTON_LED_PIN);
@@ -231,7 +239,7 @@ void rgb_led_set(uint8_t value) {
uint8_t lvl = (value >> (i<<1)) & 0x03;
uint8_t pin = pins[i];
switch (lvl) {
-
+
#ifdef AVRXMEGA3 // ATTINY816, 817, etc
case 0: // LED off
@@ -249,7 +257,7 @@ void rgb_led_set(uint8_t value) {
break;
#else
-
+
case 0: // LED off
AUXLED_RGB_DDR &= 0xff ^ (1 << pin);
AUXLED_RGB_PUE &= 0xff ^ (1 << pin);
@@ -265,8 +273,8 @@ void rgb_led_set(uint8_t value) {
AUXLED_RGB_PUE |= (1 << pin);
AUXLED_RGB_PORT |= (1 << pin);
break;
-
- #endif // MCU type
+
+ #endif // MCU type
}
}
}
@@ -302,4 +310,3 @@ void reboot() {
}
#endif
-#endif
diff --git a/spaghetti-monster/fsm-misc.h b/spaghetti-monster/fsm-misc.h
index 66d31ba..8de6b29 100644
--- a/spaghetti-monster/fsm-misc.h
+++ b/spaghetti-monster/fsm-misc.h
@@ -1,34 +1,28 @@
-/*
- * fsm-misc.h: Miscellaneous function for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-misc.h: Miscellaneous function for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_MISC_H
-#define FSM_MISC_H
+#pragma once
#ifdef USE_DYNAMIC_UNDERCLOCKING
void auto_clock_speed();
#endif
-#if defined(USE_BLINK_NUM) || defined(USE_BLINK_DIGIT)
-#ifndef BLINK_BRIGHTNESS
-#define BLINK_BRIGHTNESS (MAX_LEVEL/6)
+// shortest time (in ms) the light should blink for to indicate a zero
+#ifndef BLINK_ONCE_TIME
+ #define BLINK_ONCE_TIME 10
#endif
-uint8_t blink_digit(uint8_t num);
+
+#if defined(USE_BLINK_NUM) || defined(USE_BLINK_DIGIT)
+ #ifndef BLINK_BRIGHTNESS
+ #define BLINK_BRIGHTNESS (MAX_LEVEL/6)
+ #endif
+ #if defined(USE_CFG) && defined(DEFAULT_BLINK_CHANNEL)
+ #define BLINK_CHANNEL cfg.blink_channel
+ #elif defined(DEFAULT_BLINK_CHANNEL)
+ #define BLINK_CHANNEL DEFAULT_BLINK_CHANNEL
+ #endif
+ uint8_t blink_digit(uint8_t num);
#endif
#ifdef USE_BLINK_NUM
@@ -53,6 +47,11 @@ void indicator_led(uint8_t lvl);
void button_led_set(uint8_t lvl);
#endif
+// if any type of aux LEDs exist, define a shorthand flag for it
+#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS) || defined(USE_BUTTON_LED)
+#define HAS_AUX_LEDS
+#endif
+
#ifdef USE_AUX_RGB_LEDS
// value: 0b00BBGGRR
// each pair of bits: 0=off, 1=low, 2=high
@@ -67,4 +66,3 @@ uint8_t triangle_wave(uint8_t phase);
void reboot();
#endif
-#endif
diff --git a/spaghetti-monster/fsm-pcint.c b/spaghetti-monster/fsm-pcint.c
index 4ada5b8..131d0c3 100644
--- a/spaghetti-monster/fsm-pcint.c
+++ b/spaghetti-monster/fsm-pcint.c
@@ -1,24 +1,8 @@
-/*
- * fsm-pcint.c: PCINT (Pin Change Interrupt) functions for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-pcint.c: PCINT (Pin Change Interrupt) functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_PCINT_C
-#define FSM_PCINT_C
+#pragma once
#include <avr/interrupt.h>
#include <util/delay_basic.h>
@@ -110,5 +94,3 @@ void PCINT_inner(uint8_t pressed) {
ticks_since_last_event = 0;
}
-
-#endif
diff --git a/spaghetti-monster/fsm-pcint.h b/spaghetti-monster/fsm-pcint.h
index a7f3733..cd7ba02 100644
--- a/spaghetti-monster/fsm-pcint.h
+++ b/spaghetti-monster/fsm-pcint.h
@@ -1,24 +1,8 @@
-/*
- * fsm-pcint.h: PCINT (Pin Change Interrupt) functions for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-pcint.h: PCINT (Pin Change Interrupt) functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_PCINT_H
-#define FSM_PCINT_H
+#pragma once
volatile uint8_t irq_pcint = 0; // pin change interrupt happened?
//static volatile uint8_t button_was_pressed;
@@ -29,4 +13,3 @@ inline void PCINT_on();
inline void PCINT_off();
void PCINT_inner(uint8_t pressed);
-#endif
diff --git a/spaghetti-monster/fsm-ramping.c b/spaghetti-monster/fsm-ramping.c
index 63692c8..adc8acb 100644
--- a/spaghetti-monster/fsm-ramping.c
+++ b/spaghetti-monster/fsm-ramping.c
@@ -1,47 +1,13 @@
-/*
- * fsm-ramping.c: Ramping functions for SpaghettiMonster.
- * 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-ramping.c: Ramping functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_RAMPING_C
-#define FSM_RAMPING_C
+#pragma once
#ifdef USE_RAMPING
-void set_level(uint8_t level) {
- #ifdef USE_JUMP_START
- // maybe "jump start" the engine, if it's prone to slow starts
- // (pulse the output high for a moment to wake up the power regulator)
- // (only do this when starting from off and going to a low level)
- if ((! actual_level)
- && level
- && (level < jump_start_level)) {
- set_level(jump_start_level);
- delay_4ms(JUMP_START_TIME/4);
- }
- #endif // ifdef USE_JUMP_START
-
- actual_level = level;
-
- #ifdef USE_SET_LEVEL_GRADUALLY
- gradual_target = level;
- #endif
-
+#ifdef HAS_AUX_LEDS
+inline void set_level_aux_leds(uint8_t level) {
#ifdef USE_INDICATOR_LED_WHILE_RAMPING
// use side-facing aux LEDs while main LEDs are on
if (! go_to_standby) {
@@ -52,9 +18,6 @@ void set_level(uint8_t level) {
button_led_set((level > 0) + (level > DEFAULT_LEVEL));
#endif
}
- //if (level > MAX_1x7135) indicator_led(2);
- //else if (level > 0) indicator_led(1);
- //else if (! go_to_standby) indicator_led(0);
#else // turn off front-facing aux LEDs while main LEDs are on
#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)
if (! go_to_standby) {
@@ -70,33 +33,89 @@ void set_level(uint8_t level) {
}
#endif
#endif
+}
+#endif // ifdef HAS_AUX_LEDS
+
+#ifdef USE_AUX_RGB_LEDS_WHILE_ON
+// TODO: maybe move this stuff into FSM
+#include "anduril/aux-leds.h" // for rgb_led_voltage_readout()
+inline void set_level_aux_rgb_leds(uint8_t level) {
+ if (! go_to_standby) {
+ if (level > 0) {
+ rgb_led_voltage_readout(level > USE_AUX_RGB_LEDS_WHILE_ON);
+ } else {
+ rgb_led_set(0);
+ }
+ // some drivers can be wired with RGB or single color to button
+ // ... so support both even though only one is connected
+ #ifdef USE_BUTTON_LED
+ button_led_set((level > 0) + (level > DEFAULT_LEVEL));
+ #endif
+ }
+}
+#endif // ifdef USE_AUX_RGB_LEDS_WHILE_ON
- #ifdef OVERRIDE_SET_LEVEL
- set_level_override(level);
- #else
- #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON) || defined(PWM1_PHASE_SYNC)
- static uint8_t prev_level = 0;
- uint8_t api_level = level;
+void set_level(uint8_t level) {
+ #ifdef USE_JUMP_START
+ // maybe "jump start" the engine, if it's prone to slow starts
+ // (pulse the output high for a moment to wake up the power regulator)
+ // (only do this when starting from off and going to a low level)
+ // TODO: allow different jump start behavior per channel mode
+ // FIXME: don't jump-start during factory reset
+ // (it seems to cause some eeprom issues on KR4
+ // when doing a click with a loose tailcap)
+ if ((! actual_level)
+ && level
+ && (level < JUMP_START_LEVEL)) {
+ set_level(JUMP_START_LEVEL);
+ delay_4ms(JUMP_START_TIME/4);
+ }
#endif
- //TCCR0A = PHASE;
+ #ifdef HAS_AUX_LEDS
+ set_level_aux_leds(level);
+ #endif
+
+ #ifdef USE_AUX_RGB_LEDS_WHILE_ON
+ set_level_aux_rgb_leds(level);
+ #endif
+
+ if (0 == level) {
+ set_level_zero();
+ } else {
+ // call the relevant hardware-specific set_level_*()
+ SetLevelFuncPtr set_level_func = channels[channel_mode].set_level;
+ set_level_func(level - 1);
+ }
+
+ if (actual_level != level) prev_level = actual_level;
+ actual_level = level;
+
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ gradual_target = level;
+ #endif
+
+ #ifdef USE_DYNAMIC_UNDERCLOCKING
+ auto_clock_speed();
+ #endif
+}
+
+#ifdef USE_LEGACY_SET_LEVEL
+// (this is mostly just here for reference, temporarily)
+// single set of LEDs with 1 to 3 stacked power channels,
+// like linear, FET+1, and FET+N+1
+// (default set_level_*() function for most lights)
+void set_level_legacy(uint8_t level) {
if (level == 0) {
#if PWM_CHANNELS >= 1
- PWM1_LVL = 0;
+ PWM1_LVL = 0;
#endif
#if PWM_CHANNELS >= 2
- PWM2_LVL = 0;
+ PWM2_LVL = 0;
#endif
#if PWM_CHANNELS >= 3
- PWM3_LVL = 0;
- #endif
- #if PWM_CHANNELS >= 4
- PWM4_LVL = 0;
- #endif
- #ifdef USE_TINT_RAMPING
- TINT1_LVL = 0;
- TINT2_LVL = 0;
+ PWM3_LVL = 0;
#endif
#if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_OFF)
PWM1_CNT = 0;
@@ -121,7 +140,6 @@ void set_level(uint8_t level) {
#endif
} else {
// enable the power channel, if relevant
- #ifndef USE_TINT_RAMPING // update_tint handles this better
#ifdef LED_ENABLE_PIN
#ifdef LED_ON_DELAY
uint8_t led_enable_port_save = LED_ENABLE_PORT;
@@ -161,7 +179,6 @@ void set_level(uint8_t level) {
delay_4ms(LED2_ON_DELAY/4);
#endif
#endif
- #endif // ifndef USE_TINT_RAMPING
// PWM array index = level - 1
level --;
@@ -175,9 +192,6 @@ void set_level(uint8_t level) {
#if PWM_CHANNELS >= 3
PWM3_LVL = PWM_GET(pwm3_levels, level);
#endif
- #if PWM_CHANNELS >= 4
- PWM4_LVL = PWM_GET(pwm4_levels, level);
- #endif
#ifdef USE_DYN_PWM
uint16_t top = PWM_GET(pwm_tops, level);
@@ -190,29 +204,15 @@ void set_level(uint8_t level) {
// (but don't wait when turning on from zero, because
// it'll reset the phase below anyway)
// to be safe, allow at least 32 cycles to update TOP
- while(prev_level && (PWM1_CNT > (top - 32))) {}
+ while(actual_level && (PWM1_CNT > (top - 32))) {}
#endif
// pulse frequency modulation, a.k.a. dynamic PWM
PWM1_TOP = top;
-
- // repeat for other channels if necessary
- #ifdef PMW2_TOP
- #if defined(PWM2_CNT) && defined(PWM2_PHASE_SYNC)
- while(prev_level && (PWM2_CNT > (top - 32))) {}
- #endif
- PWM2_TOP = top;
- #endif
- #ifdef PMW3_TOP
- #if defined(PWM3_CNT) && defined(PWM3_PHASE_SYNC)
- while(prev_level && (PWM3_CNT > (top - 32))) {}
- #endif
- PWM3_TOP = top;
- #endif
#endif // ifdef USE_DYN_PWM
#if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON)
// force reset phase when turning on from zero
// (because otherwise the initial response is inconsistent)
- if (! prev_level) {
+ if (! actual_level) {
PWM1_CNT = 0;
#if defined(PWM2_CNT) && defined(PWM2_PHASE_RESET_ON)
PWM2_CNT = 0;
@@ -223,196 +223,37 @@ void set_level(uint8_t level) {
}
#endif
}
- #ifdef USE_TINT_RAMPING
- update_tint();
- #endif
-
- #if defined(PWM1_CNT) && defined(PWM1_PHASE_RESET_ON) || defined(PWM1_PHASE_SYNC)
- prev_level = api_level;
- #endif
- #endif // ifdef OVERRIDE_SET_LEVEL
#ifdef USE_DYNAMIC_UNDERCLOCKING
auto_clock_speed();
#endif
}
+#endif
+
#ifdef USE_SET_LEVEL_GRADUALLY
inline void set_level_gradually(uint8_t lvl) {
gradual_target = lvl;
}
-#ifndef OVERRIDE_GRADUAL_TICK
+
// call this every frame or every few frames to change brightness very smoothly
void gradual_tick() {
- // go by only one ramp level at a time instead of directly to the target
uint8_t gt = gradual_target;
if (gt < actual_level) gt = actual_level - 1;
else if (gt > actual_level) gt = actual_level + 1;
- /*
- #ifdef LED_ENABLE_PIN_LEVEL_MIN
- // only enable during part of the ramp
- if ((gt >= LED_ENABLE_PIN_LEVEL_MIN)
- && (gt <= LED_ENABLE_PIN_LEVEL_MAX))
- LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN);
- else // disable during other parts of the ramp
- LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN);
- #endif
- */
-
- gt --; // convert 1-based number to 0-based
-
- PWM_DATATYPE target;
+ // call the relevant hardware-specific function
+ GradualTickFuncPtr gradual_tick_func = channels[channel_mode].gradual_tick;
+ bool done = gradual_tick_func(gt - 1);
- #if PWM_CHANNELS >= 1
- target = PWM_GET(pwm1_levels, gt);
- #if PWM_CHANNELS > 1
- if ((gt < actual_level) // special case for FET-only turbo
- && (PWM1_LVL == 0) // (bypass adjustment period for first step)
- && (target == PWM_TOP)) PWM1_LVL = PWM_TOP;
- else
- #endif
- if (PWM1_LVL < target) PWM1_LVL ++;
- else if (PWM1_LVL > target) PWM1_LVL --;
- #endif
- #if PWM_CHANNELS >= 2
- target = PWM_GET(pwm2_levels, gt);
- #if PWM_CHANNELS > 2
- if ((gt < actual_level) // special case for FET-only turbo
- && (PWM2_LVL == 0) // (bypass adjustment period for first step)
- && (target == PWM_TOP)) PWM2_LVL = PWM_TOP;
- else
- #endif
- if (PWM2_LVL < target) PWM2_LVL ++;
- else if (PWM2_LVL > target) PWM2_LVL --;
- #endif
- #if PWM_CHANNELS >= 3
- target = PWM_GET(pwm3_levels, gt);
- if (PWM3_LVL < target) PWM3_LVL ++;
- else if (PWM3_LVL > target) PWM3_LVL --;
- #endif
- #if PWM_CHANNELS >= 4
- target = PWM_GET(pwm4_levels, gt);
- if (PWM4_LVL < target) PWM4_LVL ++;
- else if (PWM4_LVL > target) PWM4_LVL --;
- #endif
-
- // did we go far enough to hit the next defined ramp level?
- // if so, update the main ramp level tracking var
- if ((PWM1_LVL == PWM_GET(pwm1_levels, gt))
- #if PWM_CHANNELS >= 2
- && (PWM2_LVL == PWM_GET(pwm2_levels, gt))
- #endif
- #if PWM_CHANNELS >= 3
- && (PWM3_LVL == PWM_GET(pwm3_levels, gt))
- #endif
- #if PWM_CHANNELS >= 4
- && (PWM4_LVL == PWM_GET(pwm4_levels, gt))
- #endif
- )
- {
- //actual_level = gt + 1;
+ if (done) {
uint8_t orig = gradual_target;
- set_level(gt + 1);
+ set_level(gt);
gradual_target = orig;
}
- // is handled in set_level()
- //#ifdef USE_TINT_RAMPING
- //update_tint();
- //#endif
- // is handled in set_level()
- //#ifdef USE_DYNAMIC_UNDERCLOCKING
- //auto_clock_speed();
- //#endif
}
-#endif // ifdef OVERRIDE_GRADUAL_TICK
#endif // ifdef USE_SET_LEVEL_GRADUALLY
-#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY))
-void update_tint() {
- #ifndef TINT_RAMPING_CORRECTION
- #define TINT_RAMPING_CORRECTION 26 // 140% brightness at middle tint
- #endif
-
- // calculate actual PWM levels based on a single-channel ramp
- // and a global tint value
- //PWM_DATATYPE brightness = PWM_GET(pwm1_levels, level);
- uint16_t brightness = PWM1_LVL;
- uint16_t warm_PWM, cool_PWM;
- #ifdef USE_DYN_PWM
- uint16_t top = PWM1_TOP;
- //uint16_t top = PWM_GET(pwm_tops, actual_level-1);
- #else
- const uint16_t top = PWM_TOP;
- #endif
-
- // auto-tint modes
- uint8_t mytint;
- uint8_t level = actual_level - 1;
- #if 1
- // perceptual by ramp level
- if (tint == 0) { mytint = 255 * (uint16_t)level / RAMP_SIZE; }
- else if (tint == 255) { mytint = 255 - (255 * (uint16_t)level / RAMP_SIZE); }
- #else
- // linear with power level
- //if (tint == 0) { mytint = brightness; }
- //else if (tint == 255) { mytint = 255 - brightness; }
- #endif
- // stretch 1-254 to fit 0-255 range (hits every value except 98 and 198)
- else { mytint = (tint * 100 / 99) - 1; }
-
- PWM_DATATYPE2 base_PWM = brightness;
- #if defined(TINT_RAMPING_CORRECTION) && (TINT_RAMPING_CORRECTION > 0)
- // middle tints sag, so correct for that effect
- // by adding extra power which peaks at the middle tint
- // (correction is only necessary when PWM is fast)
- if (level > HALFSPEED_LEVEL) {
- base_PWM = brightness
- + ((((PWM_DATATYPE2)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255);
- }
- // fade the triangle wave out when above 100% power,
- // so it won't go over 200%
- if (brightness > top) {
- base_PWM -= 2 * (
- ((brightness - top) * TINT_RAMPING_CORRECTION / 64)
- * triangle_wave(mytint) / 255
- );
- }
- // guarantee no more than 200% power
- if (base_PWM > (top << 1)) { base_PWM = top << 1; }
- #endif
-
- cool_PWM = (((PWM_DATATYPE2)mytint * (PWM_DATATYPE2)base_PWM) + 127) / 255;
- warm_PWM = base_PWM - cool_PWM;
- // when running at > 100% power, spill extra over to other channel
- if (cool_PWM > top) {
- warm_PWM += (cool_PWM - top);
- cool_PWM = top;
- } else if (warm_PWM > top) {
- cool_PWM += (warm_PWM - top);
- warm_PWM = top;
- }
-
- TINT1_LVL = warm_PWM;
- TINT2_LVL = cool_PWM;
-
- // disable the power channel, if relevant
- #ifdef LED_ENABLE_PIN
- if (warm_PWM)
- LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN);
- else
- LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN);
- #endif
- #ifdef LED2_ENABLE_PIN
- if (cool_PWM)
- LED2_ENABLE_PORT |= (1 << LED2_ENABLE_PIN);
- else
- LED2_ENABLE_PORT &= ~(1 << LED2_ENABLE_PIN);
- #endif
-}
-#endif // ifdef USE_TINT_RAMPING
-
-
#endif // ifdef USE_RAMPING
-#endif
+
diff --git a/spaghetti-monster/fsm-ramping.h b/spaghetti-monster/fsm-ramping.h
index de090c2..c4b7d48 100644
--- a/spaghetti-monster/fsm-ramping.h
+++ b/spaghetti-monster/fsm-ramping.h
@@ -1,62 +1,83 @@
-/*
- * fsm-ramping.h: Ramping functions for SpaghettiMonster.
- * 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_RAMPING_H
-#define FSM_RAMPING_H
+// fsm-ramping.h: Ramping functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#ifdef USE_RAMPING
// actual_level: last ramp level set by set_level()
uint8_t actual_level = 0;
+// the level used before actual
+uint8_t prev_level = 0;
-#ifdef USE_TINT_RAMPING
-#ifdef TINT_RAMP_TOGGLE_ONLY
-uint8_t tint = 0;
-#else
-uint8_t tint = 128;
-#endif
-#define USE_TRIANGLE_WAVE
-#endif
+void set_level(uint8_t level);
+//void set_level_smooth(uint8_t level);
+void set_level_zero(); // implement this in a hwdef
#ifdef USE_SET_LEVEL_GRADUALLY
// adjust brightness very smoothly
uint8_t gradual_target;
inline void set_level_gradually(uint8_t lvl);
void gradual_tick();
-#endif
-#if defined(USE_TINT_RAMPING) && (!defined(TINT_RAMP_TOGGLE_ONLY))
-void update_tint();
-#endif
+// reduce repetition with macros
+#define GRADUAL_TICK_SETUP() \
+ PWM_DATATYPE target;
+
+// tick to a specific value
+#define GRADUAL_ADJUST_SIMPLE(TARGET,PWM) \
+ if (PWM < TARGET) PWM ++; \
+ else if (PWM > TARGET) PWM --;
+
+// tick to a specific value, except when immediate 0 to 255 is needed
+#define GRADUAL_ADJUST_STACKED(TARGET,PWM,TOP) \
+ if ( ((PWM == 0) && (TARGET == TOP)) \
+ || ((PWM == TOP) && (TARGET == 0))) \
+ PWM = TARGET; \
+ else GRADUAL_ADJUST_SIMPLE(TARGET,PWM)
+
+// tick the top layer of the stack
+#define GRADUAL_ADJUST_1CH(TABLE,PWM) \
+ target = PWM_GET(TABLE, gt); \
+ if (PWM < target) PWM ++; \
+ else if (PWM > target) PWM --;
+
+// tick a base level of the stack
+// (with support for special DD FET behavior
+// like "low=0, high=255" --> "low=255, high=254")
+#define GRADUAL_ADJUST(TABLE,PWM,TOP) \
+ target = PWM_GET(TABLE, gt); \
+ if ((gt < actual_level) \
+ && (PWM == 0) \
+ && (target == TOP)) PWM = TOP; \
+ else \
+ if (PWM < target) PWM ++; \
+ else if (PWM > target) PWM --;
+
+#endif // ifdef USE_SET_LEVEL_GRADUALLY
// auto-detect the data type for PWM tables
-#ifndef PWM_BITS
- #define PWM_BITS 8
- #define PWM_TOP 255
+// FIXME: PWM bits and data type should be per PWM table
+// FIXME: this whole thing is a mess and should be removed
+#ifndef PWM1_BITS
+ #define PWM1_BITS 8
+ #define PWM1_TOP 255
+ #define STACKED_PWM_TOP 255
#endif
#if PWM_BITS <= 8
+ #define STACKED_PWM_DATATYPE uint8_t
#define PWM_DATATYPE uint8_t
#define PWM_DATATYPE2 uint16_t
- #define PWM_TOP 255
+ #ifndef PWM_TOP
+ #define PWM_TOP 255
+ #endif
+ #define STACKED_PWM_TOP 255
+ #ifndef PWM_GET
#define PWM_GET(x,y) pgm_read_byte(x+y)
+ #endif
#else
+ #define STACKED_PWM_DATATYPE uint16_t
#define PWM_DATATYPE uint16_t
#ifndef PWM_DATATYPE2
#define PWM_DATATYPE2 uint32_t
@@ -64,114 +85,83 @@ void update_tint();
#ifndef PWM_TOP
#define PWM_TOP 1023 // 10 bits by default
#endif
+ #ifndef STACKED_PWM_TOP
+ #define STACKED_PWM_TOP 1023
+ #endif
// pointer plus 2*y bytes
//#define PWM_GET(x,y) pgm_read_word(x+(2*y))
// nope, the compiler was already doing the math correctly
+ #ifndef PWM_GET
#define PWM_GET(x,y) pgm_read_word(x+y)
+ #endif
#endif
+#define PWM_GET8(x,y) pgm_read_byte(x+y)
+#define PWM_GET16(x,y) pgm_read_word(x+y)
// use UI-defined ramp tables if they exist
#ifdef PWM1_LEVELS
-PROGMEM const PWM_DATATYPE pwm1_levels[] = { PWM1_LEVELS };
+PROGMEM const PWM1_DATATYPE pwm1_levels[] = { PWM1_LEVELS };
#endif
#ifdef PWM2_LEVELS
-PROGMEM const PWM_DATATYPE pwm2_levels[] = { PWM2_LEVELS };
+PROGMEM const PWM2_DATATYPE pwm2_levels[] = { PWM2_LEVELS };
#endif
#ifdef PWM3_LEVELS
-PROGMEM const PWM_DATATYPE pwm3_levels[] = { PWM3_LEVELS };
+PROGMEM const PWM3_DATATYPE pwm3_levels[] = { PWM3_LEVELS };
#endif
#ifdef PWM4_LEVELS
-PROGMEM const PWM_DATATYPE pwm4_levels[] = { PWM4_LEVELS };
+PROGMEM const PWM4_DATATYPE pwm4_levels[] = { PWM4_LEVELS };
#endif
-
-// pulse frequency modulation, a.k.a. dynamic PWM
-// (different ceiling / frequency at each ramp level)
-#ifdef USE_DYN_PWM
-PROGMEM const PWM_DATATYPE pwm_tops[] = { PWM_TOPS };
+#ifdef PWM5_LEVELS
+PROGMEM const PWM5_DATATYPE pwm5_levels[] = { PWM5_LEVELS };
#endif
-#ifdef USE_JUMP_START
-#ifndef JUMP_START_TIME
-#define JUMP_START_TIME 8 // in ms, should be 4, 8, or 12
+// convenience defs for 1 LED with stacked channels
+// FIXME: remove this, use pwm1/2/3 instead
+#ifdef LOW_PWM_LEVELS
+PROGMEM const PWM_DATATYPE low_pwm_levels[] = { LOW_PWM_LEVELS };
#endif
-#ifndef DEFAULT_JUMP_START_LEVEL
-#define DEFAULT_JUMP_START_LEVEL 10
+#ifdef MED_PWM_LEVELS
+PROGMEM const PWM_DATATYPE med_pwm_levels[] = { MED_PWM_LEVELS };
#endif
-uint8_t jump_start_level = DEFAULT_JUMP_START_LEVEL;
+#ifdef HIGH_PWM_LEVELS
+PROGMEM const PWM_DATATYPE high_pwm_levels[] = { HIGH_PWM_LEVELS };
#endif
-// default / example ramps
-#ifndef PWM1_LEVELS
-#if PWM_CHANNELS == 1
- #if RAMP_LENGTH == 50
- // ../../bin/level_calc.py 1 50 7135 3 0.25 980
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 3,3,3,3,4,4,4,5,5,6,7,8,9,11,12,14,16,18,20,23,25,28,32,35,39,43,47,52,57,62,68,74,80,87,94,102,110,118,127,136,146,156,167,178,189,201,214,227,241,255 };
- #elif RAMP_LENGTH == 75
- // ../../bin/level_calc.py 1 75 7135 3 0.25 980
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 3,3,3,3,3,3,4,4,4,4,5,5,5,6,6,7,8,8,9,10,11,12,13,14,15,17,18,20,21,23,25,27,29,31,33,36,38,41,44,47,50,53,56,59,63,67,71,75,79,83,88,93,98,103,108,113,119,125,131,137,143,150,157,164,171,178,186,194,202,210,219,227,236,246,255 };
- #elif RAMP_LENGTH == 150
- // ../../bin/level_calc.py 1 150 7135 3 0.25 980
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,6,6,6,6,7,7,7,8,8,8,9,9,9,10,10,11,11,12,12,13,13,14,15,15,16,17,17,18,19,19,20,21,22,23,24,24,25,26,27,28,29,31,32,33,34,35,36,38,39,40,42,43,44,46,47,49,50,52,53,55,57,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,89,91,93,96,98,101,103,106,109,111,114,117,120,123,125,128,131,134,138,141,144,147,151,154,157,161,164,168,171,175,179,183,186,190,194,198,202,206,210,215,219,223,228,232,236,241,246,250,255 };
- #endif
-#elif PWM_CHANNELS == 2
- #if RAMP_LENGTH == 50
- // ../../bin/level_calc.py 2 50 7135 4 0.33 150 FET 1 10 1500
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,5,6,8,10,13,17,22,28,35,44,54,65,78,93,109,128,149,171,197,224,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,7,11,15,20,26,31,37,44,51,58,65,73,82,91,100,110,121,132,143,155,168,181,194,209,224,239,255 };
- #define MAX_1x7135 22
- #elif RAMP_LENGTH == 75
- // ../../bin/level_calc.py 2 75 7135 4 0.33 150 FET 1 10 1500
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,4,5,6,7,8,10,12,14,17,20,24,28,32,37,43,49,56,64,72,82,91,102,114,126,139,153,168,184,202,220,239,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,5,7,10,13,16,19,23,26,30,34,38,42,47,51,56,61,66,72,77,83,89,95,101,108,115,122,129,136,144,152,160,168,177,186,195,204,214,224,234,244,255 };
- #define MAX_1x7135 33
- #elif RAMP_LENGTH == 150
- // ../../bin/level_calc.py 1 65 7135 1 0.8 150
- // ... mixed with this:
- // ../../bin/level_calc.py 2 150 7135 4 0.33 150 FET 1 10 1500
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,3,4,5,7,8,9,11,12,14,15,17,19,20,22,24,25,27,29,31,33,35,37,39,41,43,45,48,50,52,55,57,59,62,64,67,70,72,75,78,81,84,87,90,93,96,99,102,105,109,112,115,119,122,126,129,133,137,141,144,148,152,156,160,165,169,173,177,182,186,191,195,200,205,209,214,219,224,229,234,239,244,250,255 };
- #define MAX_1x7135 65
- #define HALFSPEED_LEVEL 14
- #define QUARTERSPEED_LEVEL 5
- #endif
-#elif PWM_CHANNELS == 3
- #if RAMP_LENGTH == 50
- // ../../bin/level_calc.py 3 50 7135 4 0.33 150 7135 4 1 840 FET 1 10 2000
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,5,6,8,11,15,20,26,34,43,54,67,82,99,118,140,165,192,221,254,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,17,25,33,42,52,62,73,85,97,111,125,140,157,174,192,210,230,251,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm3_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,14,34,54,76,98,122,146,172,198,226,255 };
- #define MAX_1x7135 20
- #define MAX_Nx7135 39
- #elif RAMP_LENGTH == 75
- // ../../bin/level_calc.py 3 75 7135 4 0.33 150 7135 4 1 840 FET 1 10 2000
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 4,4,5,6,7,9,11,14,16,20,24,28,34,40,46,54,62,71,81,92,104,117,130,146,162,179,198,218,239,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,9,14,18,23,29,34,40,47,53,60,67,75,83,91,99,108,117,127,137,148,158,170,181,193,206,219,232,246,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm3_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,15,28,42,55,70,84,99,115,131,147,164,181,199,217,236,255 };
- #define MAX_1x7135 30
- #define MAX_Nx7135 59
- #elif RAMP_LENGTH == 150
- // ../../bin/level_calc.py 1 65 7135 1 0.8 150
- // ... mixed with this:
- // ../../../bin/level_calc.py 3 150 7135 1 0.33 150 7135 1 1 850 FET 1 10 1500
- PROGMEM const PWM_DATATYPE pwm1_levels[] = { 1,1,2,2,3,3,4,4,5,6,7,8,9,10,12,13,14,15,17,19,20,22,24,26,29,31,34,36,39,42,45,48,51,55,59,62,66,70,75,79,84,89,93,99,104,110,115,121,127,134,140,147,154,161,168,176,184,192,200,209,217,226,236,245,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm2_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,4,6,8,10,13,15,17,19,22,24,26,29,31,34,37,39,42,45,48,51,54,57,60,64,67,70,74,77,81,85,88,92,96,100,104,108,112,116,121,125,130,134,139,143,148,153,158,163,168,173,179,184,189,195,201,206,212,218,224,230,236,243,249,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0 };
- PROGMEM const PWM_DATATYPE pwm3_levels[] = { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,8,19,31,43,55,67,79,91,104,117,130,143,157,170,184,198,212,226,240,255 };
- #define MAX_1x7135 65
- #define MAX_Nx7135 130
- #define HALFSPEED_LEVEL 14
- #define QUARTERSPEED_LEVEL 5
- #endif
-#elif PWM_CHANNELS == 4
- 4-channel PWM not really supported yet, sorry.
+// 2 channel CCT blending ramp
+#ifdef BLEND_PWM_LEVELS
+// FIXME: remove this, use pwm1/2/3 instead
+PROGMEM const PWM_DATATYPE blend_pwm_levels[] = { BLEND_PWM_LEVELS };
#endif
+
+
+// pulse frequency modulation, a.k.a. dynamic PWM
+// (different ceiling / frequency at each ramp level)
+// FIXME: dynamic PWM should be a per-channel option, not global
+#ifdef PWM_TOPS
+PROGMEM const PWM_DATATYPE pwm_tops[] = { PWM_TOPS };
+#endif
+
+// FIXME: jump start should be per channel / channel mode
+#ifdef USE_JUMP_START
+ #ifndef JUMP_START_TIME
+ #define JUMP_START_TIME 8 // in ms, should be 4, 8, or 12
+ #endif
+ #ifndef DEFAULT_JUMP_START_LEVEL
+ #define DEFAULT_JUMP_START_LEVEL 10
+ #endif
+ #ifdef USE_CFG
+ #define JUMP_START_LEVEL cfg.jump_start_level
+ #else
+ #define JUMP_START_LEVEL jump_start_level
+ uint8_t jump_start_level = DEFAULT_JUMP_START_LEVEL;
+ #endif
#endif
// RAMP_SIZE / MAX_LVL
-#define RAMP_SIZE (sizeof(pwm1_levels)/sizeof(PWM_DATATYPE))
+// cfg-*.h should define RAMP_SIZE
+//#define RAMP_SIZE (sizeof(stacked_pwm1_levels)/sizeof(STACKED_PWM_DATATYPE))
#define MAX_LEVEL RAMP_SIZE
-void set_level(uint8_t level);
-//void set_level_smooth(uint8_t level);
#endif // ifdef USE_RAMPING
-#endif
+
diff --git a/spaghetti-monster/fsm-random.c b/spaghetti-monster/fsm-random.c
index 1f83bce..91fd929 100644
--- a/spaghetti-monster/fsm-random.c
+++ b/spaghetti-monster/fsm-random.c
@@ -1,24 +1,8 @@
-/*
- * fsm-random.c: Random number generator for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-random.c: Random number generator for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_RANDOM_C
-#define FSM_RANDOM_C
+#pragma once
#ifdef USE_PSEUDO_RAND
uint8_t pseudo_rand() {
@@ -30,4 +14,3 @@ uint8_t pseudo_rand() {
}
#endif
-#endif
diff --git a/spaghetti-monster/fsm-random.h b/spaghetti-monster/fsm-random.h
index 720f6f2..49aa0cf 100644
--- a/spaghetti-monster/fsm-random.h
+++ b/spaghetti-monster/fsm-random.h
@@ -1,24 +1,8 @@
-/*
- * fsm-random.h: Random number generator for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-random.h: Random number generator for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_RANDOM_H
-#define FSM_RANDOM_H
+#pragma once
#ifdef USE_PSEUDO_RAND
uint8_t pseudo_rand();
@@ -26,4 +10,3 @@ uint8_t pseudo_rand();
volatile uint8_t pseudo_rand_seed = 0;
#endif
-#endif
diff --git a/spaghetti-monster/fsm-standby.c b/spaghetti-monster/fsm-standby.c
index 0ae6a2f..5def07c 100644
--- a/spaghetti-monster/fsm-standby.c
+++ b/spaghetti-monster/fsm-standby.c
@@ -1,24 +1,8 @@
-/*
- * fsm-standby.c: standby mode functions for 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_STANDBY_C
-#define FSM_STANDBY_C
+// fsm-standby.c: standby mode functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include <avr/interrupt.h>
#include <avr/sleep.h>
@@ -56,6 +40,11 @@ void sleep_until_eswitch_pressed()
#endif
// configure sleep mode
+ #ifdef TICK_DURING_STANDBY
+ // needs a special sleep mode during measurements
+ if (adc_active_now) adc_sleep_mode();
+ else
+ #endif
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
@@ -114,4 +103,3 @@ void idle_mode()
}
#endif
-#endif
diff --git a/spaghetti-monster/fsm-standby.h b/spaghetti-monster/fsm-standby.h
index cd01e72..957e2e1 100644
--- a/spaghetti-monster/fsm-standby.h
+++ b/spaghetti-monster/fsm-standby.h
@@ -1,24 +1,8 @@
-/*
- * fsm-standby.h: standby mode functions for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-standby.h: standby mode functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_STANDBY_H
-#define FSM_STANDBY_H
+#pragma once
// deferred "off" so we won't suspend in a weird state
// (like... during the middle of a strobe pulse)
@@ -82,4 +66,3 @@ void sleep_until_eswitch_pressed();
void idle_mode();
#endif
-#endif
diff --git a/spaghetti-monster/fsm-states.c b/spaghetti-monster/fsm-states.c
index e368277..4b94ce9 100644
--- a/spaghetti-monster/fsm-states.c
+++ b/spaghetti-monster/fsm-states.c
@@ -1,24 +1,8 @@
-/*
- * fsm-states.c: State-handling functions for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-states.c: State-handling functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_STATES_C
-#define FSM_STATES_C
+#pragma once
#include "fsm-states.h"
#include "fsm-adc.h"
@@ -119,4 +103,3 @@ uint8_t default_state(Event event, uint16_t arg) {
}
#endif
-#endif
diff --git a/spaghetti-monster/fsm-states.h b/spaghetti-monster/fsm-states.h
index 9964bc1..156e6cf 100644
--- a/spaghetti-monster/fsm-states.h
+++ b/spaghetti-monster/fsm-states.h
@@ -1,24 +1,8 @@
-/*
- * fsm-states.h: State-handling functions for 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_STATES_H
-#define FSM_STATES_H
+// fsm-states.h: State-handling functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include "fsm-adc.h"
@@ -51,4 +35,3 @@ void set_state_deferred(StatePtr new_state, uint16_t arg);
uint8_t default_state(Event event, uint16_t arg);
#endif
-#endif
diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c
index ea2efac..64f006e 100644
--- a/spaghetti-monster/fsm-wdt.c
+++ b/spaghetti-monster/fsm-wdt.c
@@ -1,24 +1,8 @@
-/*
- * fsm-wdt.c: WDT (Watch Dog Timer) functions for 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 <http://www.gnu.org/licenses/>.
- */
-
-#ifndef FSM_WDT_C
-#define FSM_WDT_C
+// fsm-wdt.c: WDT (Watch Dog Timer) functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
#include <avr/interrupt.h>
#include <avr/wdt.h>
@@ -138,10 +122,13 @@ void WDT_inner() {
#ifndef USE_SLEEP_LVP
return; // no sleep LVP needed if nothing drains power while off
#else
- // stop here, usually... but proceed often enough for sleep LVP to work
- if (0 != (ticks_since_last & 0x3f)) return;
+ // stop here, usually... except during the first few seconds asleep,
+ // and once in a while afterward for sleep LVP
+ if ((ticks_since_last > (8 * SLEEP_TICKS_PER_SECOND))
+ && (0 != (ticks_since_last & 0x0f))) return;
adc_trigger = 0; // make sure a measurement will happen
+ adc_active_now = 1; // use ADC noise reduction sleep mode
ADC_on(); // enable ADC voltage measurement functions temporarily
#endif
}
@@ -208,4 +195,3 @@ void WDT_inner() {
#endif
}
-#endif
diff --git a/spaghetti-monster/fsm-wdt.h b/spaghetti-monster/fsm-wdt.h
index d127551..abf34c5 100644
--- a/spaghetti-monster/fsm-wdt.h
+++ b/spaghetti-monster/fsm-wdt.h
@@ -1,24 +1,8 @@
-/*
- * fsm-wdt.h: WDT (Watch Dog Timer) functions for 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 <http://www.gnu.org/licenses/>.
- */
+// fsm-wdt.h: WDT (Watch Dog Timer) functions for SpaghettiMonster.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
-#ifndef FSM_WDT_H
-#define FSM_WDT_H
+#pragma once
#define TICKS_PER_SECOND 62
@@ -34,4 +18,3 @@ volatile uint8_t irq_wdt = 0; // WDT interrupt happened?
#endif
#endif
-#endif
diff --git a/spaghetti-monster/meteor/meteor.c b/spaghetti-monster/meteor/meteor.c
index 7d854a1..9c1c000 100644
--- a/spaghetti-monster/meteor/meteor.c
+++ b/spaghetti-monster/meteor/meteor.c
@@ -262,7 +262,7 @@ uint8_t base_on_state(Event event, uint16_t arg, uint8_t *mode, uint8_t *group)
// 1 click: off
if (event == EV_1click) {
set_state(base_off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_THERMAL_REGULATION
// overheating: drop by an amount proportional to how far we are above the ceiling
@@ -312,17 +312,17 @@ uint8_t ui1_on_state(Event event, uint16_t arg) {
else if (event == EV_2clicks) {
*mode ^= 1;
set_any_mode(*mode, group);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: turbo
else if (event == EV_hold) {
if (arg == 0) set_level(MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// release: exit turbo
else if (event == EV_click1_hold_release) {
set_any_mode(*mode, group);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return base_on_state(event, arg, mode, group);
}
@@ -361,7 +361,7 @@ uint8_t ui2_on_state(Event event, uint16_t arg) {
else if (event == EV_2clicks) {
*mode ^= 1;
set_any_mode(*mode, group);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: rotate through low/mid/high/turbo
else if (event == EV_hold) {
@@ -371,7 +371,7 @@ uint8_t ui2_on_state(Event event, uint16_t arg) {
else if (arg % HOLD_TIMEOUT == 1) {
set_any_mode(*mode, group);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return base_on_state(event, arg, mode, group);
}
@@ -413,7 +413,7 @@ uint8_t ui3_on_state(Event event, uint16_t arg) {
/*
else if (event == EV_click1_hold) {
set_level(levels[*mode]);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
*/
// hold: turbo
@@ -427,7 +427,7 @@ uint8_t blinky_base_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(base_off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
diff --git a/spaghetti-monster/ramping-ui/ramping-ui.c b/spaghetti-monster/ramping-ui/ramping-ui.c
index 5eb7d8f..583498a 100644
--- a/spaghetti-monster/ramping-ui/ramping-ui.c
+++ b/spaghetti-monster/ramping-ui/ramping-ui.c
@@ -59,43 +59,43 @@ uint8_t off_state(Event event, uint16_t arg) {
set_level(0);
// sleep while off (lower power use)
go_to_standby = 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold (initially): go to lowest level, but allow abort for regular click
else if (event == EV_click1_press) {
set_level(1);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click (before timeout): go to memorized level, but allow abort for double click
else if (event == EV_click1_release) {
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: regular mode
else if (event == EV_1click) {
set_state(steady_state, memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks (initial press): off, to prep for later events
else if (event == EV_click2_press) {
set_level(0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: highest mode
else if (event == EV_2clicks) {
set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 3 clicks: strobe mode
else if (event == EV_3clicks) {
set_state(strobe_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_BATTCHECK
// 4 clicks: battcheck mode
else if (event == EV_4clicks) {
set_state(battcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// hold: go to lowest level
@@ -104,17 +104,17 @@ uint8_t off_state(Event event, uint16_t arg) {
// give the user time to release at moon level
if (arg >= HOLD_TIMEOUT)
set_state(steady_state, 1);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold, release quickly: go to lowest level
else if (event == EV_click1_hold_release) {
set_state(steady_state, 1);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: go to highest level (for ramping down)
else if (event == EV_click2_hold) {
set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -131,12 +131,12 @@ uint8_t steady_state(Event event, uint16_t arg) {
target_level = arg;
#endif
set_level(arg);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: go to/from highest level
else if (event == EV_2clicks) {
@@ -153,12 +153,12 @@ uint8_t steady_state(Event event, uint16_t arg) {
#endif
set_level(memorized_level);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 3 clicks: go to strobe modes
else if (event == EV_3clicks) {
set_state(strobe_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 4 clicks: toggle smooth vs discrete ramping
else if (event == EV_4clicks) {
@@ -167,13 +167,13 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(0);
delay_4ms(20/4);
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: change brightness (brighter)
else if (event == EV_click1_hold) {
// ramp slower in discrete mode
if (arg % ramp_step_size != 0) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// FIXME: make it ramp down instead, if already at max
if (actual_level + ramp_step_size < MAX_LEVEL)
@@ -190,13 +190,13 @@ uint8_t steady_state(Event event, uint16_t arg) {
delay_4ms(8/4);
}
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: change brightness (dimmer)
else if (event == EV_click2_hold) {
// ramp slower in discrete mode
if (arg % ramp_step_size != 0) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// FIXME: make it ramp up instead, if already at min
if (actual_level > ramp_step_size)
@@ -214,7 +214,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
delay_4ms(8/4);
}
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_THERMAL_REGULATION
// TODO: test this on a real light
@@ -225,7 +225,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
if (stepdown < MAX_LEVEL/4) stepdown = MAX_LEVEL/4;
set_level(stepdown);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// underheating: increase slowly if we're lower than the target
// (proportional to how low we are)
@@ -235,7 +235,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
if (stepup > target_level) stepup = target_level;
set_level(stepup);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -244,36 +244,36 @@ uint8_t steady_state(Event event, uint16_t arg) {
uint8_t strobe_state(Event event, uint16_t arg) {
if (event == EV_enter_state) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: toggle party strobe vs tactical strobe
else if (event == EV_2clicks) {
strobe_type ^= 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 3 clicks: go back to regular modes
else if (event == EV_3clicks) {
set_state(steady_state, memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: change speed (go faster)
else if (event == EV_click1_hold) {
if ((arg & 1) == 0) {
if (strobe_delay > 8) strobe_delay --;
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: change speed (go slower)
else if (event == EV_click2_hold) {
if ((arg & 1) == 0) {
if (strobe_delay < 255) strobe_delay ++;
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -284,12 +284,12 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: tempcheck mode
else if (event == EV_2clicks) {
set_state(tempcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -298,7 +298,7 @@ uint8_t tempcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
diff --git a/spaghetti-monster/rampingios/rampingiosv3.c b/spaghetti-monster/rampingios/rampingiosv3.c
index 7b6baee..e990a5a 100644
--- a/spaghetti-monster/rampingios/rampingiosv3.c
+++ b/spaghetti-monster/rampingios/rampingiosv3.c
@@ -247,7 +247,7 @@ uint8_t off_state(Event event, uint16_t arg) {
#endif
// sleep while off (lower power use)
go_to_standby = 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// go back to sleep eventually if we got bumped but didn't leave "off" state
else if (event == EV_tick) {
@@ -257,7 +257,7 @@ uint8_t off_state(Event event, uint16_t arg) {
indicator_led(indicator_led_mode & 0x03);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
// blink the indicator LED, maybe
@@ -265,13 +265,13 @@ uint8_t off_state(Event event, uint16_t arg) {
if ((indicator_led_mode & 0b00000011) == 0b00000011) {
indicator_blink(arg);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// hold (initially): go to lowest level (floor), but allow abort for regular click
else if (event == EV_click1_press) {
set_level(nearest_level(1));
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: go to lowest level
else if (event == EV_click1_hold) {
@@ -290,56 +290,56 @@ uint8_t off_state(Event event, uint16_t arg) {
if (arg >= (!ramp_style) * HOLD_TIMEOUT) { // more consistent
set_state(steady_state, 1);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold, release quickly: go to lowest level (floor)
else if (event == EV_click1_hold_release) {
set_state(steady_state, 1);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click (before timeout): go to memorized level, but allow abort for double click
else if (event == EV_click1_release) {
set_level(nearest_level(memorized_level));
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: regular mode
else if (event == EV_1click) {
set_state(steady_state, memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: go to highest level (ceiling) (for ramping down)
else if (event == EV_click2_hold) {
set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: highest mode (ceiling)
else if (event == EV_2clicks) {
set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 3 clicks (initial press): off, to prep for later events
else if (event == EV_click3_press) {
set_level(0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_BATTCHECK
// 3 clicks: battcheck mode / blinky mode group 1
else if (event == EV_3clicks) {
set_state(battcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// 4 clicks: momentary
else if (event == EV_4clicks) {
blink_confirm(1);
set_state(momentary_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 6 clicks: lockout mode
else if (event == EV_6clicks) {
blink_confirm(2);
set_state(lockout_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_INDICATOR_LED
// 7 clicks: next aux LED mode
@@ -357,19 +357,19 @@ uint8_t off_state(Event event, uint16_t arg) {
indicator_led_mode = (indicator_led_mode & 0b11111100) | mode;
indicator_led(mode);
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// 8 clicks: beacon mode
else if (event == EV_8clicks) {
set_state(beacon_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_TENCLICK_THERMAL_CONFIG
// 10 clicks: thermal config mode
else if (event == EV_10clicks) {
push_state(thermal_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -407,12 +407,12 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef USE_REVERSING
ramp_direction = 1;
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: off
else if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: go to/from highest level
else if (event == EV_2clicks) {
@@ -429,7 +429,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#endif
set_level(memorized_level);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 3 clicks: toggle smooth vs discrete ramping
else if (event == EV_3clicks) {
@@ -445,20 +445,20 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(0);
delay_4ms(20/4);
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_RAMP_CONFIG
// 4 clicks: configure this ramp mode
else if (event == EV_4clicks) {
push_state(ramp_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// hold: change brightness (brighter)
else if (event == EV_click1_hold) {
// ramp slower in discrete mode
if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_REVERSING
// make it ramp down instead, if already at max
@@ -510,7 +510,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
#endif
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(USE_REVERSING)
// reverse ramp direction on hold release
@@ -518,7 +518,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef USE_REVERSING
ramp_direction = -ramp_direction;
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// click, hold: change brightness (dimmer)
@@ -528,7 +528,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#endif
// ramp slower in discrete mode
if (ramp_style && (arg % HOLD_TIMEOUT != 0)) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// TODO? make it ramp up instead, if already at min?
memorized_level = nearest_level((int16_t)actual_level - ramp_step_size);
@@ -569,7 +569,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
#endif
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(USE_SET_LEVEL_GRADUALLY) || defined(USE_REVERSING)
else if (event == EV_tick) {
@@ -580,7 +580,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef USE_SET_LEVEL_GRADUALLY
// make thermal adjustment speed scale with magnitude
if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) {
- return MISCHIEF_MANAGED; // adjust slower when not a high mode
+ return EVENT_HANDLED; // adjust slower when not a high mode
}
#ifdef THERM_HARD_TURBO_DROP
else if ((! (actual_level < THERM_FASTER_LEVEL))
@@ -622,7 +622,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
#endif
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#ifdef USE_THERMAL_REGULATION
@@ -654,7 +654,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(stepdown);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// underheating: increase slowly if we're lower than the target
// (proportional to how low we are)
@@ -676,7 +676,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(stepup);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -688,12 +688,12 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: tempcheck mode
else if (event == EV_2clicks) {
set_state(tempcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -705,12 +705,12 @@ uint8_t tempcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 4 clicks: thermal config mode
else if (event == EV_4clicks) {
push_state(thermal_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -722,14 +722,14 @@ uint8_t beacon_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// TODO: use sleep ticks to measure time between pulses,
// to save power
// 4 clicks: beacon config mode
else if (event == EV_4clicks) {
push_state(beacon_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -781,14 +781,14 @@ uint8_t lockout_state(Event event, uint16_t arg) {
indicator_led(indicator_led_mode >> 2);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(TICK_DURING_STANDBY) && defined(USE_INDICATOR_LED)
else if (event == EV_sleep_tick) {
if ((indicator_led_mode & 0b00001100) == 0b00001100) {
indicator_blink(arg);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#ifdef USE_INDICATOR_LED
@@ -806,14 +806,14 @@ uint8_t lockout_state(Event event, uint16_t arg) {
indicator_led_mode = (mode << 2) + (indicator_led_mode & 0x03);
indicator_led(mode);
save_config();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// 6 clicks: exit
else if (event == EV_6clicks) {
blink_confirm(1);
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
@@ -827,13 +827,13 @@ uint8_t momentary_state(Event event, uint16_t arg) {
// button is being held
if ((event & (B_CLICK | B_PRESS)) == (B_CLICK | B_PRESS)) {
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// button was released
else if ((event & (B_CLICK | B_PRESS)) == (B_CLICK)) {
set_level(0);
//go_to_standby = 1; // sleep while light is off
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// Sleep, dammit! (but wait a few seconds first)
@@ -846,7 +846,7 @@ uint8_t momentary_state(Event event, uint16_t arg) {
go_to_standby = 1; // sleep while light is off
// TODO: lighted button should use lockout config?
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
@@ -861,7 +861,7 @@ uint8_t config_state_base(Event event, uint16_t arg,
if (event == EV_enter_state) {
config_step = 0;
set_level(0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// advance forward through config steps
else if (event == EV_tick) {
@@ -875,13 +875,13 @@ uint8_t config_state_base(Event event, uint16_t arg,
//set_state(retstate, retval);
pop_state();
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// an option was set (return from number_entry_state)
else if (event == EV_reenter_state) {
config_state_values[config_step] = number_entry_value;
config_step ++;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
//return EVENT_NOT_HANDLED;
// eat all other events; don't pass any through to parent
@@ -985,7 +985,7 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
blinks_left = arg;
entry_step = 0;
wait_ticks = 0;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// advance through the process:
// 0: wait a moment
@@ -1042,7 +1042,7 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
number_entry_value = value;
pop_state();
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// count clicks
else if (event == EV_click1_release) {
@@ -1055,7 +1055,7 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
delay_4ms(8/2);
set_level(0);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
diff --git a/spaghetti-monster/spaghetti-monster.h b/spaghetti-monster/spaghetti-monster.h
index 853eac3..77431f8 100644
--- a/spaghetti-monster/spaghetti-monster.h
+++ b/spaghetti-monster/spaghetti-monster.h
@@ -1,3 +1,9 @@
+// spaghetti-monster.h: UI toolkit / microkernel for e-switch flashlights.
+// Copyright (C) 2017-2023 Selene ToyKeeper
+// SPDX-License-Identifier: GPL-3.0-or-later
+
+#pragma once
+
/*
* SpaghettiMonster: Generic foundation code for e-switch flashlights.
* Other possible names:
@@ -5,21 +11,6 @@
* - RoundTable
* - Mostly Harmless
* - ...
- *
- * 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 <http://www.gnu.org/licenses/>.
*/
#include "tk-attiny.h"
@@ -34,6 +25,7 @@
#include "fsm-wdt.h"
#include "fsm-pcint.h"
#include "fsm-standby.h"
+#include "fsm-channels.h"
#include "fsm-ramping.h"
#include "fsm-random.h"
#ifdef USE_EEPROM
@@ -72,6 +64,7 @@ void loop();
#include "fsm-wdt.c"
#include "fsm-pcint.c"
#include "fsm-standby.c"
+#include "fsm-channels.c"
#include "fsm-ramping.c"
#include "fsm-random.c"
#ifdef USE_EEPROM
@@ -79,3 +72,4 @@ void loop();
#endif
#include "fsm-misc.c"
#include "fsm-main.c"
+
diff --git a/spaghetti-monster/werner/werner.c b/spaghetti-monster/werner/werner.c
index 1c3d4ad..f3241ee 100644
--- a/spaghetti-monster/werner/werner.c
+++ b/spaghetti-monster/werner/werner.c
@@ -152,19 +152,19 @@ uint8_t off_state(Event event, uint16_t arg) {
set_level(0);
// sleep while off (lower power use)
go_to_standby = 1;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// go back to sleep eventually if we got bumped but didn't leave "off" state
else if (event == EV_tick) {
if (arg > TICKS_PER_SECOND*2) {
go_to_standby = 1;
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold (initially): go to lowest level, but allow abort for regular click
else if (event == EV_click1_press) {
set_level(nearest_level(1));
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: go to lowest level
else if (event == EV_click1_hold) {
@@ -173,49 +173,49 @@ uint8_t off_state(Event event, uint16_t arg) {
if (arg >= HOLD_TIMEOUT) {
set_state(steady_state, 1);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold, release quickly: go to lowest level
else if (event == EV_click1_hold_release) {
set_state(steady_state, 1);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click (before timeout): go to memorized level, but allow abort for double click
else if (event == EV_click1_release) {
set_level(nearest_level(memorized_level));
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 1 click: regular mode
else if (event == EV_1click) {
set_state(steady_state, memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks (initial press): off, to prep for later events
else if (event == EV_click2_press) {
set_level(0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click, hold: go to highest level (for ramping down)
else if (event == EV_click2_hold) {
set_state(steady_state, MAX_LEVEL);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: highest mode
else if (event == EV_2clicks) {
set_state(steady_state, nearest_level(MAX_LEVEL));
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_BATTCHECK
// 3 clicks: battcheck mode / blinky mode group
else if (event == EV_3clicks) {
set_state(battcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
// 4 clicks: configure ramp
else if (event == EV_4clicks) {
push_state(ramp_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -240,7 +240,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
target_level = arg;
#endif
set_level(nearest_level(arg));
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// click: brighter
else if (event == EV_click1_release) {
@@ -253,25 +253,25 @@ uint8_t steady_state(Event event, uint16_t arg) {
empty_event_sequence();
// remember mode for later
save_config_wl();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// hold: dimmer
else if (event == EV_click1_hold) {
// ramp slower in discrete mode
if (arg % HOLD_TIMEOUT != 0) {
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
memorized_level = nearest_level((int16_t)actual_level - ramp_step_size);
#ifdef USE_THERMAL_REGULATION
target_level = memorized_level;
#endif
set_level(memorized_level);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// reverse ramp direction on hold release
else if (event == EV_click1_hold_release) {
save_config_wl();
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#if defined(USE_SET_LEVEL_GRADUALLY)
// gradual thermal regulation
@@ -279,7 +279,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
#ifdef USE_SET_LEVEL_GRADUALLY
// make thermal adjustment speed scale with magnitude
if ((arg & 1) && (actual_level < THERM_FASTER_LEVEL)) {
- return MISCHIEF_MANAGED; // adjust slower when not a high mode
+ return EVENT_HANDLED; // adjust slower when not a high mode
}
#ifdef THERM_HARD_TURBO_DROP
else if ((! (actual_level < THERM_FASTER_LEVEL))
@@ -317,7 +317,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
#endif
#endif
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
#ifdef USE_THERMAL_REGULATION
@@ -342,7 +342,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(stepdown);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// underheating: increase slowly if we're lower than the target
// (proportional to how low we are)
@@ -358,7 +358,7 @@ uint8_t steady_state(Event event, uint16_t arg) {
set_level(stepup);
#endif
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -370,14 +370,14 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#ifdef USE_THERMAL_REGULATION
// 2 clicks: tempcheck mode
else if (event == EV_2clicks) {
blink_confirm(2);
set_state(tempcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
#endif
return EVENT_NOT_HANDLED;
@@ -389,18 +389,18 @@ uint8_t tempcheck_state(Event event, uint16_t arg) {
// 1 click: off
if (event == EV_1click) {
set_state(off_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 2 clicks: battcheck mode
else if (event == EV_2clicks) {
blink_confirm(1);
set_state(battcheck_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// 4 clicks: thermal config mode
else if (event == EV_4clicks) {
push_state(thermal_config_state, 0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}
@@ -415,7 +415,7 @@ uint8_t config_state_base(Event event, uint16_t arg,
if (event == EV_enter_state) {
config_step = 0;
set_level(0);
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// advance forward through config steps
else if (event == EV_tick) {
@@ -429,13 +429,13 @@ uint8_t config_state_base(Event event, uint16_t arg,
//set_state(retstate, retval);
pop_state();
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// an option was set (return from number_entry_state)
else if (event == EV_reenter_state) {
config_state_values[config_step] = number_entry_value;
config_step ++;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
//return EVENT_NOT_HANDLED;
// eat all other events; don't pass any through to parent
@@ -502,7 +502,7 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
blinks_left = arg;
entry_step = 0;
wait_ticks = 0;
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// advance through the process:
// 0: wait a moment
@@ -559,7 +559,7 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
number_entry_value = value;
pop_state();
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
// count clicks
else if (event == EV_click1_release) {
@@ -572,7 +572,7 @@ uint8_t number_entry_state(Event event, uint16_t arg) {
delay_4ms(8/2);
set_level(0);
}
- return MISCHIEF_MANAGED;
+ return EVENT_HANDLED;
}
return EVENT_NOT_HANDLED;
}