aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSelene ToyKeeper2020-07-06 14:24:28 -0600
committerSelene ToyKeeper2020-07-06 14:24:28 -0600
commit24270b394a0119bff8681ed75c1e21876c11439f (patch)
tree432756e4b5bf26bac78c7809128d52e0d531262c
parentadded a compile flag to fix compatibility with GCC 7/8/9's new semantics for ... (diff)
parentmerged in support for Noctigon K1-SBT90.2 (diff)
downloadanduril-24270b394a0119bff8681ed75c1e21876c11439f.tar.gz
anduril-24270b394a0119bff8681ed75c1e21876c11439f.tar.bz2
anduril-24270b394a0119bff8681ed75c1e21876c11439f.zip
merged nearly a year of updates from the fsm branch, including the new product map
-rwxr-xr-xbin/build.sh2
-rwxr-xr-xbin/level_calc.py16
-rw-r--r--hwdef-BLF_GT.h12
-rw-r--r--hwdef-Emisar_D18.h5
-rw-r--r--hwdef-Emisar_D4.h5
-rw-r--r--hwdef-Emisar_D4Sv2.h122
-rw-r--r--hwdef-Emisar_D4v2.h14
-rw-r--r--hwdef-FW3A.h5
-rw-r--r--hwdef-Mateminco_MF01-Mini.h49
-rw-r--r--hwdef-Mateminco_MF01S.h12
-rw-r--r--hwdef-Noctigon_K1-12V.h138
-rw-r--r--hwdef-Noctigon_K1-SBT90.h143
-rw-r--r--hwdef-Noctigon_K1.h134
-rw-r--r--hwdef-Noctigon_KR4.h145
-rw-r--r--hwdef-TK_Saber.h5
-rw-r--r--spaghetti-monster/anduril/anduril-manual.txt2
-rw-r--r--spaghetti-monster/anduril/anduril-ui.pngbin286759 -> 281904 bytes
-rw-r--r--spaghetti-monster/anduril/anduril.c373
-rw-r--r--spaghetti-monster/anduril/anduril.svg238
-rw-r--r--spaghetti-monster/anduril/anduril.txt6
-rwxr-xr-xspaghetti-monster/anduril/build-all.sh2
-rw-r--r--spaghetti-monster/anduril/cfg-blf-lantern.h10
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1.h5
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d18.h2
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1s.h5
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d1v2.h17
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4.h8
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4s.h9
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h10
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4sv2.h53
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h34
-rw-r--r--spaghetti-monster/anduril/cfg-emisar-d4v2.h13
-rw-r--r--spaghetti-monster/anduril/cfg-ff-pl47.h5
-rw-r--r--spaghetti-monster/anduril/cfg-ff-pl47g2.h55
-rw-r--r--spaghetti-monster/anduril/cfg-ff-rot66.h3
-rw-r--r--spaghetti-monster/anduril/cfg-ff-rot66g2.h49
-rw-r--r--spaghetti-monster/anduril/cfg-fw3a-nofet.h36
-rw-r--r--spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h50
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k1-12v.h65
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h61
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-k1.h66
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4-219.h11
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h47
-rw-r--r--spaghetti-monster/anduril/cfg-noctigon-kr4.h58
-rw-r--r--spaghetti-monster/anduril/cfg-sofirn-sp36.h7
-rw-r--r--spaghetti-monster/anduril/version.h4
-rw-r--r--spaghetti-monster/darkhorse/darkhorse.c1
-rw-r--r--spaghetti-monster/fsm-adc.c591
-rw-r--r--spaghetti-monster/fsm-adc.h32
-rw-r--r--spaghetti-monster/fsm-eeprom.c36
-rw-r--r--spaghetti-monster/fsm-events.c19
-rw-r--r--spaghetti-monster/fsm-events.h3
-rw-r--r--spaghetti-monster/fsm-main.c38
-rw-r--r--spaghetti-monster/fsm-main.h2
-rw-r--r--spaghetti-monster/fsm-misc.c70
-rw-r--r--spaghetti-monster/fsm-misc.h6
-rw-r--r--spaghetti-monster/fsm-pcint.c28
-rw-r--r--spaghetti-monster/fsm-pcint.h1
-rw-r--r--spaghetti-monster/fsm-ramping.c53
-rw-r--r--spaghetti-monster/fsm-ramping.h64
-rw-r--r--spaghetti-monster/fsm-standby.c25
-rw-r--r--spaghetti-monster/fsm-wdt.c85
-rw-r--r--spaghetti-monster/fsm-wdt.h4
-rw-r--r--spaghetti-monster/meteor/meteor.c1
-rw-r--r--spaghetti-monster/momentary/momentary.c1
-rw-r--r--spaghetti-monster/ramping-ui/ramping-ui.c3
-rw-r--r--spaghetti-monster/rampingios/rampingiosv3.c12
-rw-r--r--spaghetti-monster/spaghetti-monster.txt12
-rw-r--r--spaghetti-monster/werner/Makefile7
-rwxr-xr-xspaghetti-monster/werner/build-all.sh6
-rw-r--r--spaghetti-monster/werner/werner.c19
-rw-r--r--tk-attiny.h9
-rw-r--r--tk-delay.h2
73 files changed, 2561 insertions, 680 deletions
diff --git a/bin/build.sh b/bin/build.sh
index fbb24ea..3992c38 100755
--- a/bin/build.sh
+++ b/bin/build.sh
@@ -14,7 +14,7 @@ export PROGRAM=$1 ; shift
export MCU=attiny$ATTINY
export CC=avr-gcc
export OBJCOPY=avr-objcopy
-export CFLAGS="-Wall -g -Os -mmcu=$MCU -c -std=gnu99 -fgnu89-inline -DATTINY=$ATTINY -I.. -I../.. -I../../.. -fshort-enums"
+export CFLAGS="-Wall -g -Os -mmcu=$MCU -c -std=gnu99 -fgnu89-inline -fwhole-program -DATTINY=$ATTINY -I.. -I../.. -I../../.. -fshort-enums"
export OFLAGS="-Wall -g -Os -mmcu=$MCU"
export LDFLAGS="-fgnu89-inline"
export OBJCOPYFLAGS='--set-section-flags=.eeprom=alloc,load --change-section-lma .eeprom=0 --no-change-warnings -O ihex'
diff --git a/bin/level_calc.py b/bin/level_calc.py
index 6f07621..a780405 100755
--- a/bin/level_calc.py
+++ b/bin/level_calc.py
@@ -8,6 +8,8 @@ interactive = False
# supported shapes: ninth, seventh, fifth, cube, square, log
#ramp_shape = 'cube'
+max_pwm = 255
+
def main(args):
"""Calculates PWM levels for visually-linear steps.
@@ -22,7 +24,7 @@ def main(args):
(str, 'type', '7135', 'Type of channel - 7135 or FET:'),
(int, 'pwm_min', 6, 'Lowest visible PWM level:'),
(float, 'lm_min', 0.25, 'How bright is the lowest level, in lumens?'),
- #(int, 'pwm_max', 255, 'Highest PWM level:'),
+ #(int, 'pwm_max', max_pwm, 'Highest PWM level:'),
(float, 'lm_max', 1000, 'How bright is the highest level, in lumens?'),
]
@@ -48,7 +50,7 @@ def main(args):
if not args:
print('===== Channel %s =====' % (chan_num+1))
chan = Empty()
- chan.pwm_max = 255
+ chan.pwm_max = max_pwm
ask(questions_per_channel, chan)
chan.type = chan.type.upper()
if chan.type not in ('7135', 'FET'):
@@ -152,6 +154,16 @@ def multi_pwm(answers, channels):
(cnum+1,
','.join([str(int(round(i))) for i in channel.modes])))
+ # Show highest level for each channel before next channel starts
+ for cnum, channel in enumerate(channels[:-1]):
+ prev = 0
+ i = 1
+ while (i < answers.num_levels) \
+ and (channel.modes[i] >= channel.modes[i-1]) \
+ and (channels[cnum+1].modes[i] == 0):
+ i += 1
+ print('Ch%i max: %i (%.2f/%s)' % (cnum, i, channel.modes[i-1], max_pwm))
+
def get_value(text, default, args):
"""Get input from the user, or from the command line args."""
diff --git a/hwdef-BLF_GT.h b/hwdef-BLF_GT.h
index 01dbdbd..51b391d 100644
--- a/hwdef-BLF_GT.h
+++ b/hwdef-BLF_GT.h
@@ -34,24 +34,24 @@
#ifndef VOLTAGE_PIN
#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
#define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds with PB2
-#define VOLTAGE_ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
+#define VOLTAGE_ADC ADC1D // Digital input disable bit corresponding with PB2
+// inherited from tk-attiny.h
+//#define VOLTAGE_ADC_DIDR DIDR0 // DIDR for ADC1
// 1.1V reference, left-adjust, ADC1/PB2
//#define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | (1 << ADLAR) | VOLTAGE_CHANNEL)
// 1.1V reference, no left-adjust, ADC1/PB2
#define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | VOLTAGE_CHANNEL)
#endif
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// Raw ADC readings at 4.4V and 2.2V (in-between, we assume values form a straight line)
#ifndef ADC_44
-#define ADC_44 184
+#define ADC_44 (184*4)
#endif
#ifndef ADC_22
-#define ADC_22 92
+#define ADC_22 (92*4)
#endif
-#define TEMP_CHANNEL 0b00001111
-
#define FAST 0xA3 // fast PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels
diff --git a/hwdef-Emisar_D18.h b/hwdef-Emisar_D18.h
index 638dadb..3a238eb 100644
--- a/hwdef-Emisar_D18.h
+++ b/hwdef-Emisar_D18.h
@@ -33,16 +33,13 @@
#ifndef AUXLED_PIN
#define AUXLED_PIN PB2 // pin 7
#endif
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
#endif
-//#define TEMP_DIDR ADC4D
-#define TEMP_CHANNEL 0b00001111
-
#define FAST 0xA3 // fast PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels
diff --git a/hwdef-Emisar_D4.h b/hwdef-Emisar_D4.h
index be499f1..d062d6f 100644
--- a/hwdef-Emisar_D4.h
+++ b/hwdef-Emisar_D4.h
@@ -34,16 +34,13 @@
//#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
//#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
#endif
-//#define TEMP_DIDR ADC4D
-#define TEMP_CHANNEL 0b00001111
-
#define FAST 0xA3 // fast PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels
diff --git a/hwdef-Emisar_D4Sv2.h b/hwdef-Emisar_D4Sv2.h
new file mode 100644
index 0000000..d68b7ac
--- /dev/null
+++ b/hwdef-Emisar_D4Sv2.h
@@ -0,0 +1,122 @@
+#ifndef HWDEF_EMISAR_D4SV2_H
+#define HWDEF_EMISAR_D4SV2_H
+
+/* Emisar D4Sv2 driver layout (attiny1634)
+ * (same layout as D4v2, except it's a FET+3+1 instead of FET+1)
+ *
+ * Pin / Name / Function
+ * 1 PA6 FET PWM (PWM1B)
+ * 2 PA5 red aux LED (PWM0B)
+ * 3 PA4 green aux LED
+ * 4 PA3 blue aux LED
+ * 5 PA2 e-switch
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 3x7135 PWM (PWM0A)
+ * 16 PB3 1x7135 PWM (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI
+ * 19 PB0 (none)
+ * 20 PA7 (none)
+ * ADC12 thermal sensor
+ */
+
+#ifdef ATTINY
+#undef ATTINY
+#endif
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define SWITCH_PIN PA2 // pin 5
+#define SWITCH_PCINT PCINT2 // pin 5 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
+#define SWITCH_PORT PINA // PINA or PINB or PINC
+
+#define PWM_CHANNELS 3
+
+#define PWM1_PIN PB3 // pin 16, 1x7135 PWM
+#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
+
+#define PWM2_PIN PC0 // pin 15, 3x7135 PWM
+#define PWM2_LVL OCR0A // OCR0A is the output compare register for PC0
+
+#define PWM3_PIN PA6 // pin 1, FET PWM
+#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB1
+
+
+#define ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 4 // add 0.20V (measured 0.22V)
+#endif
+
+// this light has aux LEDs under the optic
+#define AUXLED_R_PIN PA5 // pin 2
+#define AUXLED_G_PIN PA4 // pin 3
+#define AUXLED_B_PIN PA3 // pin 4
+#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC
+#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC
+#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC
+
+// with so many pins, doing this all with #ifdefs gets awkward...
+// ... so just hardcode it in each hwdef file instead
+inline void hwdef_setup() {
+ // enable output ports
+ // FET, aux R/G/B
+ DDRA = (1 << PWM3_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ ;
+ // 1x7135
+ DDRB = (1 << PWM1_PIN);
+ // 3x7135
+ DDRC = (1 << PWM2_PIN);
+
+ // configure PWM
+ // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter
+ // pre-scale for timer: N = 1
+ // WGM1[3:0]: 0,0,0,1: PWM, Phase Correct, 8-bit (DS table 12-5)
+ // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6)
+ // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4)
+ // COM1B[1:0]: 1,0: PWM OC1B in the normal direction (DS table 12-4)
+ TCCR1A = (0<<WGM11) | (1<<WGM10) // 8-bit (TOP=0xFF) (DS table 12-5)
+ | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4)
+ | (1<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4)
+ ;
+ TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6)
+ | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
+ ;
+
+ // WGM0[2:0]: 0,0,1: PWM, Phase Correct (DS table 11-8)
+ // CS0[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 11-9)
+ // COM0A[1:0]: 1,0: PWM OC0A in the normal direction (DS table 11-4)
+ // COM0B[1:0]: 0,0: OC0B disabled (DS table 11-7)
+ // TCCR0A: COM0A1, COM0A0, COM0B1, COM0B0, -, -, WGM01, WGM00
+ TCCR0A = (0<<WGM01) | (1<<WGM00) // PWM, Phase Correct, TOP=0xFF (DS table 11-5)
+ | (1<<COM1A1) | (0<<COM1A0) // PWM 0A in normal direction (DS table 11-4)
+ | (0<<COM1B1) | (0<<COM1B0) // PWM 0B disabled (DS table 11-7)
+ ;
+ // TCCR0B: FOC0A, FOC0B, -, -, WGM02, CS02, CS01, CS00
+ TCCR0B = (0<<CS02) | (0<<CS01) | (1<<CS00) // clk/1 (no prescaling) (DS table 11-9)
+ | (0<<WGM02) // PWM, Phase Correct, TOP=0xFF (DS table 11-8)
+ ;
+
+ // set up e-switch
+ //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
+ PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+#define LAYOUT_DEFINED
+
+#endif
diff --git a/hwdef-Emisar_D4v2.h b/hwdef-Emisar_D4v2.h
index 0460fad..fc95e22 100644
--- a/hwdef-Emisar_D4v2.h
+++ b/hwdef-Emisar_D4v2.h
@@ -9,7 +9,7 @@
* 3 PA4 green aux LED
* 4 PA3 blue aux LED
* 5 PA2 e-switch
- * 6 PA1 (none)
+ * 6 PA1 button LED
* 7 PA0 (none)
* 8 GND GND
* 9 VCC VCC
@@ -48,15 +48,13 @@
#define PWM2_LVL OCR1B // OCR1B is the output compare register for PB1
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
#define VOLTAGE_FUDGE_FACTOR 4 // add 0.20V (measured 0.22V)
#endif
-#define TEMP_CHANNEL 0b00001111
-
// this light has aux LEDs under the optic
#define AUXLED_R_PIN PA5 // pin 2
#define AUXLED_G_PIN PA4 // pin 3
@@ -65,17 +63,23 @@
#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC
#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC
+#define BUTTON_LED_PIN PA1 // pin 6
+#define BUTTON_LED_PORT PORTA // for all "PA" pins
+#define BUTTON_LED_DDR DDRA // for all "PA" pins
+#define BUTTON_LED_PUE PUEA // for all "PA" pins
+
// with so many pins, doing this all with #ifdefs gets awkward...
// ... so just hardcode it in each hwdef file instead
inline void hwdef_setup() {
// enable output ports
// 7135
DDRB = (1 << PWM1_PIN);
- // FET, aux R/G/B
+ // FET, aux R/G/B, button LED
DDRA = (1 << PWM2_PIN)
| (1 << AUXLED_R_PIN)
| (1 << AUXLED_G_PIN)
| (1 << AUXLED_B_PIN)
+ | (1 << BUTTON_LED_PIN)
;
// configure PWM
diff --git a/hwdef-FW3A.h b/hwdef-FW3A.h
index 0b94635..e8875d7 100644
--- a/hwdef-FW3A.h
+++ b/hwdef-FW3A.h
@@ -35,16 +35,13 @@
//#define ADC_CHANNEL 0x01 // MUX 01 corresponds with PB2
//#define ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
#endif
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#ifndef VOLTAGE_FUDGE_FACTOR
#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
#endif
-//#define TEMP_DIDR ADC4D
-#define TEMP_CHANNEL 0b00001111
-
#define FAST 0xA3 // fast PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels
diff --git a/hwdef-Mateminco_MF01-Mini.h b/hwdef-Mateminco_MF01-Mini.h
new file mode 100644
index 0000000..c0de533
--- /dev/null
+++ b/hwdef-Mateminco_MF01-Mini.h
@@ -0,0 +1,49 @@
+#ifndef HWDEF_MF01_MINI_H
+#define HWDEF_MF01_MINI_H
+
+/* MF01-Mini driver layout
+ * ----
+ * Reset -|1 8|- VCC
+ * eswitch -|2 7|- aux LEDs
+ * FET PWM -|3 6|- PWM (7x7135)
+ * GND -|4 5|- PWM (1x7135)
+ * ----
+ */
+
+#define PWM_CHANNELS 3
+
+#ifndef AUXLED_PIN
+#define AUXLED_PIN PB2 // pin 7
+#endif
+
+#ifndef SWITCH_PIN
+#define SWITCH_PIN PB3 // pin 2
+#define SWITCH_PCINT PCINT3 // pin 2 pin change interrupt
+#endif
+
+#ifndef PWM1_PIN
+#define PWM1_PIN PB0 // pin 5, 1x7135 PWM
+#define PWM1_LVL OCR0A // OCR0A is the output compare register for PB0
+#endif
+#ifndef PWM2_PIN
+#define PWM2_PIN PB1 // pin 6, 7x7135 PWM
+#define PWM2_LVL OCR0B // OCR0B is the output compare register for PB1
+#endif
+#ifndef PWM3_PIN
+#define PWM3_PIN PB4 // pin 3, FET PWM
+#define PWM3_LVL OCR1B // OCR1B is the output compare register for PB4
+#endif
+
+#define ADC_PRSCL 0x07 // clk/128
+
+// average drop across diode on this hardware
+#ifndef VOLTAGE_FUDGE_FACTOR
+#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
+#endif
+
+#define FAST 0xA3 // fast PWM both channels
+#define PHASE 0xA1 // phase-correct PWM both channels
+
+#define LAYOUT_DEFINED
+
+#endif
diff --git a/hwdef-Mateminco_MF01S.h b/hwdef-Mateminco_MF01S.h
index ab1c5bf..78a64a6 100644
--- a/hwdef-Mateminco_MF01S.h
+++ b/hwdef-Mateminco_MF01S.h
@@ -34,24 +34,24 @@
#ifndef VOLTAGE_PIN
#define VOLTAGE_PIN PB2 // pin 7, voltage ADC
#define VOLTAGE_CHANNEL 0x01 // MUX 01 corresponds with PB2
-#define VOLTAGE_ADC_DIDR ADC1D // Digital input disable bit corresponding with PB2
+#define VOLTAGE_ADC ADC1D // Digital input disable bit corresponding with PB2
+// inherited from tk-attiny.h
+//#define VOLTAGE_ADC_DIDR DIDR0 // DIDR for ADC1
// 1.1V reference, left-adjust, ADC1/PB2
//#define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | (1 << ADLAR) | VOLTAGE_CHANNEL)
// 1.1V reference, no left-adjust, ADC1/PB2
#define ADMUX_VOLTAGE_DIVIDER ((1 << V_REF) | VOLTAGE_CHANNEL)
#endif
-#define ADC_PRSCL 0x06 // clk/64
+#define ADC_PRSCL 0x07 // clk/128
// Raw ADC readings at 4.4V and 2.2V (in-between, we assume values form a straight line)
#ifndef ADC_44
-#define ADC_44 234
+#define ADC_44 (234*4)
#endif
#ifndef ADC_22
-#define ADC_22 117
+#define ADC_22 (117*4)
#endif
-#define TEMP_CHANNEL 0b00001111
-
#define FAST 0xA3 // fast PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels
diff --git a/hwdef-Noctigon_K1-12V.h b/hwdef-Noctigon_K1-12V.h
new file mode 100644
index 0000000..8781d8e
--- /dev/null
+++ b/hwdef-Noctigon_K1-12V.h
@@ -0,0 +1,138 @@
+#ifndef HWDEF_NOCTIGON_K1_12V_H
+#define HWDEF_NOCTIGON_K1_12V_H
+
+/* Noctigon K1 driver layout (attiny1634)
+ * (originally known as Emisar D1S V2)
+ *
+ * Pin / Name / Function
+ * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 (none) (reserved for L: button LED (on some models))
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 boost PMIC enable (PWM0A not used)
+ * 16 PB3 main LED PWM (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * All brightness control uses the power level pin, with 4 kHz 10-bit PWM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ */
+
+#ifdef ATTINY
+#undef ATTINY
+#endif
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define PWM_CHANNELS 1
+#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz
+#define PWM_TOP 1023
+
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
+#define SWITCH_PORT PINA // PINA or PINB or PINC
+
+#define PWM1_PIN PB3 // pin 16, Opamp reference
+#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
+
+#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
+#define LED_ENABLE_PORT PORTB // control port for PB0
+
+#define LED_ENABLE2_PIN PC0 // pin 15, boost PMIC enable
+#define LED_ENABLE2_PORT PORTC // control port for PC0
+
+
+#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened
+#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6
+// pin to ADC mappings are in DS table 19-4
+#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1
+// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6
+#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D
+// DS tables 19-3, 19-4
+// Bit 7 6 5 4 3 2 1 0
+// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0
+// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1
+// divided by ...
+// REFS[1:0] = 1, 0 for internal 1.1V reference
+// other bits reserved
+#define ADMUX_VOLTAGE_DIVIDER 0b10000110
+#define ADC_PRSCL 0x07 // clk/128
+
+// Raw ADC readings at 4.4V and 2.2V
+// calibrate the voltage readout here
+// estimated / calculated values are:
+// (voltage - D1) * (R2/(R2+R1) * 256 / 1.1)
+// D1, R1, R2 = 0, 330, 100
+#ifndef ADC_44
+//#define ADC_44 981 // raw value at 4.40V
+#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2
+#endif
+#ifndef ADC_22
+//#define ADC_22 489 // raw value at 2.20V
+#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2
+#endif
+
+// this light has aux LEDs under the optic
+#define AUXLED_R_PIN PA5 // pin 2
+#define AUXLED_G_PIN PA4 // pin 3
+#define AUXLED_B_PIN PA3 // pin 4
+#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC
+#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC
+#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC
+
+// with so many pins, doing this all with #ifdefs gets awkward...
+// ... so just hardcode it in each hwdef file instead
+inline void hwdef_setup() {
+ // enable output ports
+ // boost PMIC on/off
+ DDRC = (1 << LED_ENABLE2_PIN);
+ // Opamp level and Opamp on/off
+ DDRB = (1 << PWM1_PIN)
+ | (1 << LED_ENABLE_PIN);
+ // aux R/G/B
+ DDRA = (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ ;
+
+ // configure PWM
+ // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter
+ // pre-scale for timer: N = 1
+ // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (DS table 12-5)
+ // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6)
+ // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4)
+ // COM1B[1:0]: 0,0: PWM OC1B disabled (DS table 12-4)
+ TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5)
+ | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4)
+ | (0<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4)
+ ;
+ TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6)
+ | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
+ ;
+
+ // set up e-switch
+ PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+#define LAYOUT_DEFINED
+
+#endif
diff --git a/hwdef-Noctigon_K1-SBT90.h b/hwdef-Noctigon_K1-SBT90.h
new file mode 100644
index 0000000..c19a4a6
--- /dev/null
+++ b/hwdef-Noctigon_K1-SBT90.h
@@ -0,0 +1,143 @@
+#ifndef HWDEF_NOCTIGON_K1SBT90_H
+#define HWDEF_NOCTIGON_K1SBT90_H
+
+/* Noctigon K1-SBT90.2 driver layout (attiny1634)
+ * (mostly the same as KR4 driver)
+ *
+ * Pin / Name / Function
+ * 1 PA6 FET PWM (direct drive) (PWM1B)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 (none)
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 (none) PWM0A
+ * 16 PB3 main LED PWM (linear) (PWM1A)
+ * 17 PB2 MISO / e-switch (PCINT10)
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 (none)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * Main brightness control uses the power level pin, with 4 kHz 10-bit PWM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ * Also has a direct-drive FET for turbo.
+ */
+
+#ifdef ATTINY
+#undef ATTINY
+#endif
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define PWM_CHANNELS 2
+#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz
+#define PWM_TOP 1023
+
+#define SWITCH_PIN PB2 // pin 17
+#define SWITCH_PCINT PCINT10 // pin 17 pin change interrupt
+#define SWITCH_PCIE PCIE1 // PCIE1 is for PCINT[11:8]
+#define SWITCH_PCMSK PCMSK1 // PCMSK1 is for PCINT[11:8]
+#define SWITCH_PORT PINB // PINA or PINB or PINC
+#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8]
+
+#define PWM1_PIN PB3 // pin 16, Opamp reference
+#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
+
+#define PWM2_PIN PA6 // pin 1, DD FET PWM
+#define PWM2_LVL OCR1B // OCR1B is the output compare register for PA6
+
+#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
+#define LED_ENABLE_PORT PORTB // control port for PB0
+
+
+#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened
+#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6
+// pin to ADC mappings are in DS table 19-4
+#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1
+// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6
+#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D
+// DS tables 19-3, 19-4
+// Bit 7 6 5 4 3 2 1 0
+// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0
+// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1
+// divided by ...
+// REFS[1:0] = 1, 0 for internal 1.1V reference
+// other bits reserved
+#define ADMUX_VOLTAGE_DIVIDER 0b10000110
+#define ADC_PRSCL 0x07 // clk/128
+
+// TODO: calibrate this
+// Raw ADC readings at 4.4V and 2.2V
+// calibrate the voltage readout here
+// estimated / calculated values are:
+// (voltage - D1) * (R2/(R2+R1) * 256 / 1.1)
+// D1, R1, R2 = 0, 330, 100
+#ifndef ADC_44
+//#define ADC_44 981 // raw value at 4.40V
+#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2
+#endif
+#ifndef ADC_22
+//#define ADC_22 489 // raw value at 2.20V
+#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2
+#endif
+
+#define TEMP_CHANNEL 0b00001111
+
+// this light has aux LEDs under the optic
+#define AUXLED_R_PIN PA5 // pin 2
+#define AUXLED_G_PIN PA4 // pin 3
+#define AUXLED_B_PIN PA3 // pin 4
+#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC
+#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC
+#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC
+
+// with so many pins, doing this all with #ifdefs gets awkward...
+// ... so just hardcode it in each hwdef file instead
+inline void hwdef_setup() {
+ // enable output ports
+ // Opamp level and Opamp on/off
+ DDRB = (1 << PWM1_PIN)
+ | (1 << LED_ENABLE_PIN);
+ // DD FET PWM, aux R/G/B
+ DDRA = (1 << PWM2_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ ;
+
+ // configure PWM
+ // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter
+ // pre-scale for timer: N = 1
+ // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (DS table 12-5)
+ // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6)
+ // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4)
+ // COM1B[1:0]: 1,0: PWM OC1B in the normal direction (DS table 12-4)
+ TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5)
+ | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4)
+ | (1<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4)
+ ;
+ TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6)
+ | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
+ ;
+
+ // set up e-switch
+ //PORTB = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
+ PUEB = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+#define LAYOUT_DEFINED
+
+#endif
diff --git a/hwdef-Noctigon_K1.h b/hwdef-Noctigon_K1.h
new file mode 100644
index 0000000..4c0ce05
--- /dev/null
+++ b/hwdef-Noctigon_K1.h
@@ -0,0 +1,134 @@
+#ifndef HWDEF_NOCTIGON_K1_H
+#define HWDEF_NOCTIGON_K1_H
+
+/* Noctigon K1 driver layout (attiny1634)
+ * (originally known as Emisar D1S V2)
+ *
+ * Pin / Name / Function
+ * 1 PA6 (none) (PWM1B) (reserved for DD drivers)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 (none) (reserved for L: button LED (on some models))
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 (none) PWM0A
+ * 16 PB3 main LED PWM (PWM1A)
+ * 17 PB2 MISO
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 e-switch (PCINT7)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * All brightness control uses the power level pin, with 4 kHz 10-bit PWM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ */
+
+#ifdef ATTINY
+#undef ATTINY
+#endif
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define PWM_CHANNELS 1
+#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz
+#define PWM_TOP 1023
+
+#define SWITCH_PIN PA7 // pin 20
+#define SWITCH_PCINT PCINT7 // pin 20 pin change interrupt
+#define SWITCH_PCIE PCIE0 // PCIE0 is for PCINT[7:0]
+#define SWITCH_PCMSK PCMSK0 // PCMSK0 is for PCINT[7:0]
+#define SWITCH_PORT PINA // PINA or PINB or PINC
+
+#define PWM1_PIN PB3 // pin 16, Opamp reference
+#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
+
+#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
+#define LED_ENABLE_PORT PORTB // control port for PB0
+
+
+#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened
+#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6
+// pin to ADC mappings are in DS table 19-4
+#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1
+// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6
+#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D
+// DS tables 19-3, 19-4
+// Bit 7 6 5 4 3 2 1 0
+// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0
+// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1
+// divided by ...
+// REFS[1:0] = 1, 0 for internal 1.1V reference
+// other bits reserved
+#define ADMUX_VOLTAGE_DIVIDER 0b10000110
+#define ADC_PRSCL 0x07 // clk/128
+
+// Raw ADC readings at 4.4V and 2.2V
+// calibrate the voltage readout here
+// estimated / calculated values are:
+// (voltage - D1) * (R2/(R2+R1) * 256 / 1.1)
+// D1, R1, R2 = 0, 330, 100
+#ifndef ADC_44
+//#define ADC_44 981 // raw value at 4.40V
+#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2
+#endif
+#ifndef ADC_22
+//#define ADC_22 489 // raw value at 2.20V
+#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2
+#endif
+
+// this light has aux LEDs under the optic
+#define AUXLED_R_PIN PA5 // pin 2
+#define AUXLED_G_PIN PA4 // pin 3
+#define AUXLED_B_PIN PA3 // pin 4
+#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC
+#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC
+#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC
+
+// with so many pins, doing this all with #ifdefs gets awkward...
+// ... so just hardcode it in each hwdef file instead
+inline void hwdef_setup() {
+ // enable output ports
+ // Opamp level and Opamp on/off
+ DDRB = (1 << PWM1_PIN)
+ | (1 << LED_ENABLE_PIN);
+ // aux R/G/B
+ DDRA = (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ ;
+
+ // configure PWM
+ // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter
+ // pre-scale for timer: N = 1
+ // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (DS table 12-5)
+ // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6)
+ // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4)
+ // COM1B[1:0]: 0,0: PWM OC1B disabled (DS table 12-4)
+ TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5)
+ | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4)
+ | (0<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4)
+ ;
+ TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6)
+ | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
+ ;
+
+ // set up e-switch
+ //PORTA = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
+ PUEA = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+#define LAYOUT_DEFINED
+
+#endif
diff --git a/hwdef-Noctigon_KR4.h b/hwdef-Noctigon_KR4.h
new file mode 100644
index 0000000..073fe27
--- /dev/null
+++ b/hwdef-Noctigon_KR4.h
@@ -0,0 +1,145 @@
+#ifndef HWDEF_NOCTIGON_KR4_H
+#define HWDEF_NOCTIGON_KR4_H
+
+/* Noctigon KR4 driver layout (attiny1634)
+ *
+ * Pin / Name / Function
+ * 1 PA6 FET PWM (direct drive) (PWM1B)
+ * 2 PA5 R: red aux LED (PWM0B)
+ * 3 PA4 G: green aux LED
+ * 4 PA3 B: blue aux LED
+ * 5 PA2 (none)
+ * 6 PA1 (none)
+ * 7 PA0 (none)
+ * 8 GND GND
+ * 9 VCC VCC
+ * 10 PC5 (none)
+ * 11 PC4 (none)
+ * 12 PC3 RESET
+ * 13 PC2 (none)
+ * 14 PC1 SCK
+ * 15 PC0 (none) PWM0A
+ * 16 PB3 main LED PWM (linear) (PWM1A)
+ * 17 PB2 MISO / e-switch (PCINT10)
+ * 18 PB1 MOSI / battery voltage (ADC6)
+ * 19 PB0 Opamp power
+ * 20 PA7 (none)
+ * ADC12 thermal sensor
+ *
+ * Main LED power uses one pin to turn the Opamp on/off,
+ * and one pin to control Opamp power level.
+ * Main brightness control uses the power level pin, with 4 kHz 10-bit PWM.
+ * The on/off pin is only used to turn the main LED on and off,
+ * not to change brightness.
+ * Some models also have a direct-drive FET for turbo.
+ */
+
+#ifdef ATTINY
+#undef ATTINY
+#endif
+#define ATTINY 1634
+#include <avr/io.h>
+
+#define PWM_CHANNELS 2
+#define PWM_BITS 10 // 0 to 1023 at 4 kHz, not 0 to 255 at 16 kHz
+#define PWM_TOP 1023
+
+#define SWITCH_PIN PB2 // pin 17
+#define SWITCH_PCINT PCINT10 // pin 17 pin change interrupt
+#define SWITCH_PCIE PCIE1 // PCIE1 is for PCINT[11:8]
+#define SWITCH_PCMSK PCMSK1 // PCMSK1 is for PCINT[11:8]
+#define SWITCH_PORT PINB // PINA or PINB or PINC
+#define PCINT_vect PCINT1_vect // ISR for PCINT[11:8]
+
+// the button tends to short out the voltage divider,
+// so ignore voltage while the button is being held
+//#define NO_LVP_WHILE_BUTTON_PRESSED
+
+
+#define PWM1_PIN PB3 // pin 16, Opamp reference
+#define PWM1_LVL OCR1A // OCR1A is the output compare register for PB3
+
+#define PWM2_PIN PA6 // pin 1, DD FET PWM
+#define PWM2_LVL OCR1B // OCR1B is the output compare register for PA6
+
+#define LED_ENABLE_PIN PB0 // pin 19, Opamp power
+#define LED_ENABLE_PORT PORTB // control port for PB0
+
+
+#define USE_VOLTAGE_DIVIDER // use a dedicated pin, not VCC, because VCC input is flattened
+#define VOLTAGE_PIN PB1 // Pin 18 / PB1 / ADC6
+// pin to ADC mappings are in DS table 19-4
+#define VOLTAGE_ADC ADC6D // digital input disable pin for PB1
+// DIDR0/DIDR1 mappings are in DS section 19.13.5, 19.13.6
+#define VOLTAGE_ADC_DIDR DIDR1 // DIDR channel for ADC6D
+// DS tables 19-3, 19-4
+// Bit 7 6 5 4 3 2 1 0
+// REFS1 REFS0 REFEN ADC0EN MUX3 MUX2 MUX1 MUX0
+// MUX[3:0] = 0, 1, 1, 0 for ADC6 / PB1
+// divided by ...
+// REFS[1:0] = 1, 0 for internal 1.1V reference
+// other bits reserved
+#define ADMUX_VOLTAGE_DIVIDER 0b10000110
+#define ADC_PRSCL 0x07 // clk/128
+
+// TODO: calibrate this
+// Raw ADC readings at 4.4V and 2.2V
+// calibrate the voltage readout here
+// estimated / calculated values are:
+// (voltage - D1) * (R2/(R2+R1) * 256 / 1.1)
+// D1, R1, R2 = 0, 330, 100
+#ifndef ADC_44
+//#define ADC_44 981 // raw value at 4.40V
+#define ADC_44 967 // manually tweaked so 4.16V will blink out 4.2
+#endif
+#ifndef ADC_22
+//#define ADC_22 489 // raw value at 2.20V
+#define ADC_22 482 // manually tweaked so 4.16V will blink out 4.2
+#endif
+
+// this light has aux LEDs under the optic
+#define AUXLED_R_PIN PA5 // pin 2
+#define AUXLED_G_PIN PA4 // pin 3
+#define AUXLED_B_PIN PA3 // pin 4
+#define AUXLED_RGB_PORT PORTA // PORTA or PORTB or PORTC
+#define AUXLED_RGB_DDR DDRA // DDRA or DDRB or DDRC
+#define AUXLED_RGB_PUE PUEA // PUEA or PUEB or PUEC
+
+// with so many pins, doing this all with #ifdefs gets awkward...
+// ... so just hardcode it in each hwdef file instead
+inline void hwdef_setup() {
+ // enable output ports
+ // Opamp level and Opamp on/off
+ DDRB = (1 << PWM1_PIN)
+ | (1 << LED_ENABLE_PIN);
+ // DD FET PWM, aux R/G/B
+ DDRA = (1 << PWM2_PIN)
+ | (1 << AUXLED_R_PIN)
+ | (1 << AUXLED_G_PIN)
+ | (1 << AUXLED_B_PIN)
+ ;
+
+ // configure PWM
+ // Setup PWM. F_pwm = F_clkio / 2 / N / TOP, where N = prescale factor, TOP = top of counter
+ // pre-scale for timer: N = 1
+ // WGM1[3:0]: 0,0,1,1: PWM, Phase Correct, 10-bit (DS table 12-5)
+ // CS1[2:0]: 0,0,1: clk/1 (No prescaling) (DS table 12-6)
+ // COM1A[1:0]: 1,0: PWM OC1A in the normal direction (DS table 12-4)
+ // COM1B[1:0]: 1,0: PWM OC1B in the normal direction (DS table 12-4)
+ TCCR1A = (1<<WGM11) | (1<<WGM10) // 10-bit (TOP=0x03FF) (DS table 12-5)
+ | (1<<COM1A1) | (0<<COM1A0) // PWM 1A in normal direction (DS table 12-4)
+ | (1<<COM1B1) | (0<<COM1B0) // PWM 1B in normal direction (DS table 12-4)
+ ;
+ TCCR1B = (0<<CS12) | (0<<CS11) | (1<<CS10) // clk/1 (no prescaling) (DS table 12-6)
+ | (0<<WGM13) | (0<<WGM12) // phase-correct PWM (DS table 12-5)
+ ;
+
+ // set up e-switch
+ //PORTB = (1 << SWITCH_PIN); // TODO: configure PORTA / PORTB / PORTC?
+ PUEB = (1 << SWITCH_PIN); // pull-up for e-switch
+ SWITCH_PCMSK = (1 << SWITCH_PCINT); // enable pin change interrupt
+}
+
+#define LAYOUT_DEFINED
+
+#endif
diff --git a/hwdef-TK_Saber.h b/hwdef-TK_Saber.h
index 3f49d30..e6476b8 100644
--- a/hwdef-TK_Saber.h
+++ b/hwdef-TK_Saber.h
@@ -23,14 +23,11 @@
#define SWITCH_PIN PB2 // pin 7
#define SWITCH_PCINT PCINT2 // pin 7 pin change interrupt
-#define ADC_PRSCL 0x06 // clk/64 (no need to be super fast)
+#define ADC_PRSCL 0x07 // clk/128
// average drop across diode on this hardware
#define VOLTAGE_FUDGE_FACTOR 5 // add 0.25V
-//#define TEMP_DIDR ADC4D
-#define TEMP_CHANNEL 0b00001111
-
#define FAST 0xA3 // fast PWM both channels
#define PHASE 0xA1 // phase-correct PWM both channels
diff --git a/spaghetti-monster/anduril/anduril-manual.txt b/spaghetti-monster/anduril/anduril-manual.txt
index 3f17f41..97e6589 100644
--- a/spaghetti-monster/anduril/anduril-manual.txt
+++ b/spaghetti-monster/anduril/anduril-manual.txt
@@ -147,7 +147,7 @@ In more detail, this is what each blinky / utility mode does:
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.
+ is 45 C, and the highest value it will allow is 70 C.
Strobe / Mood Modes
diff --git a/spaghetti-monster/anduril/anduril-ui.png b/spaghetti-monster/anduril/anduril-ui.png
index 3c032bc..70f6be0 100644
--- a/spaghetti-monster/anduril/anduril-ui.png
+++ b/spaghetti-monster/anduril/anduril-ui.png
Binary files differ
diff --git a/spaghetti-monster/anduril/anduril.c b/spaghetti-monster/anduril/anduril.c
index 8ab66f5..01904dc 100644
--- a/spaghetti-monster/anduril/anduril.c
+++ b/spaghetti-monster/anduril/anduril.c
@@ -22,7 +22,7 @@
// Anduril config file name (set it here or define it at the gcc command line)
//#define CONFIGFILE cfg-blf-q8.h
-#define USE_LVP // FIXME: won't build when this option is turned off
+#define USE_LVP
// parameters for this defined below or per-driver
#define USE_THERMAL_REGULATION
@@ -35,6 +35,8 @@
// (currently incompatible with factory reset)
//#define START_AT_MEMORIZED_LEVEL
+// include a function to blink out the firmware version
+#define USE_VERSION_CHECK
// short blip when crossing from "click" to "hold" from off
// (helps the user hit moon mode exactly, instead of holding too long
@@ -77,6 +79,9 @@
// enable beacon mode
#define USE_BEACON_MODE
+// enable momentary mode
+#define USE_MOMENTARY_MODE
+
//Muggle mode for easy UI
#define USE_MUGGLE_MODE
@@ -88,6 +93,12 @@
// so don't enable them at the same time as any of the above strobes)
//#define USE_POLICE_STROBE_MODE
//#define USE_SOS_MODE
+//#define USE_SOS_MODE_IN_FF_GROUP // put SOS in the "boring strobes" mode
+//#define USE_SOS_MODE_IN_BLINKY_GROUP // put SOS in the blinkies mode group
+
+// cut clock speed at very low modes for better efficiency
+// (defined here so config files can override it)
+#define USE_DYNAMIC_UNDERCLOCKING
/***** specific settings for known driver types *****/
#include "tk.h"
@@ -128,7 +139,6 @@
#endif
#endif
#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending
-#define USE_DYNAMIC_UNDERCLOCKING // cut clock speed at very low modes for better efficiency
// full FET strobe can be a bit much... use max regulated level instead,
// if there's a bright enough regulated level
@@ -144,7 +154,7 @@
#define USE_STROBE_STATE
#endif
-#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE)
+#if defined(USE_POLICE_STROBE_MODE) || defined(USE_SOS_MODE_IN_FF_GROUP)
#define USE_BORING_STROBE_STATE
#endif
@@ -268,6 +278,7 @@ void sos_blink(uint8_t num, uint8_t dah);
uint8_t battcheck_state(Event event, uint16_t arg);
#endif
#ifdef USE_THERMAL_REGULATION
+#define USE_BLINK_NUM
uint8_t tempcheck_state(Event event, uint16_t arg);
uint8_t thermal_config_state(Event event, uint16_t arg);
#endif
@@ -280,15 +291,21 @@ uint8_t goodnight_state(Event event, uint16_t arg);
uint8_t beacon_state(Event event, uint16_t arg);
uint8_t beacon_config_state(Event event, uint16_t arg);
#endif
+#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
+// automatic SOS emergency signal
+uint8_t sos_state(Event event, uint16_t arg);
+#endif
// soft lockout
#define MOON_DURING_LOCKOUT_MODE
// if enabled, 2nd lockout click goes to the other ramp's floor level
#define LOCKOUT_MOON_FANCY
uint8_t lockout_state(Event event, uint16_t arg);
+#ifdef USE_MOMENTARY_MODE
// 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
#ifdef USE_MUGGLE_MODE
// muggle mode, super-simple, hard to exit
uint8_t muggle_state(Event event, uint16_t arg);
@@ -306,7 +323,9 @@ void blip();
void indicator_blink(uint8_t arg);
#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_voltage_readout(uint8_t bright);
/*
* 0: R
* 1: RG
@@ -318,6 +337,15 @@ void rgb_led_update(uint8_t mode, uint8_t arg);
* 7: rainbow
* 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
+};
#define RGB_LED_NUM_COLORS 10
#define RGB_LED_NUM_PATTERNS 4
#ifndef RGB_LED_OFF_DEFAULT
@@ -327,6 +355,9 @@ void rgb_led_update(uint8_t mode, uint8_t arg);
#ifndef RGB_LED_LOCKOUT_DEFAULT
#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow
#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
@@ -462,7 +493,7 @@ volatile strobe_mode_te strobe_type = 0;
#if defined(USE_PARTY_STROBE_MODE) || defined(USE_TACTICAL_STROBE_MODE)
// party / tactical strobe timing
-volatile uint8_t strobe_delays[] = { 40, 67 }; // party strobe, tactical strobe
+volatile uint8_t strobe_delays[] = { 41, 67 }; // party strobe 24 Hz, tactical strobe 10 Hz
#endif
// bike mode config options
@@ -483,6 +514,12 @@ uint8_t triangle_wave(uint8_t phase);
volatile uint8_t beacon_seconds = 2;
#endif
+#ifdef USE_VERSION_CHECK
+#define USE_BLINK_DIGIT
+#include "version.h"
+const PROGMEM uint8_t version_number[] = VERSION_NUMBER;
+uint8_t version_check_state(Event event, uint16_t arg);
+#endif
uint8_t off_state(Event event, uint16_t arg) {
// turn emitter off when entering state
@@ -617,12 +654,14 @@ uint8_t off_state(Event event, uint16_t arg) {
set_state(lockout_state, 0);
return MISCHIEF_MANAGED;
}
+ #ifdef USE_MOMENTARY_MODE
// 5 clicks: momentary mode
else if (event == EV_5clicks) {
blink_confirm(1);
set_state(momentary_state, 0);
return MISCHIEF_MANAGED;
}
+ #endif
#ifdef USE_MUGGLE_MODE
// 6 clicks: muggle mode
else if (event == EV_6clicks) {
@@ -661,6 +700,7 @@ uint8_t off_state(Event event, uint16_t arg) {
}
// 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;
mode = mode % RGB_LED_NUM_COLORS;
@@ -671,17 +711,25 @@ uint8_t off_state(Event event, uint16_t arg) {
return MISCHIEF_MANAGED;
}
else if (event == EV_click7_hold_release) {
+ setting_rgb_mode_now = 0;
save_config();
return MISCHIEF_MANAGED;
}
#endif // end 7 clicks
- #ifdef USE_TENCLICK_THERMAL_CONFIG
+ #if defined(USE_TENCLICK_THERMAL_CONFIG) && defined(USE_THERMAL_REGULATION)
// 10 clicks: thermal config mode
else if (event == EV_10clicks) {
push_state(thermal_config_state, 0);
return MISCHIEF_MANAGED;
}
#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;
+ }
+ #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) {
@@ -713,7 +761,9 @@ uint8_t steady_state(Event event, uint16_t arg) {
// turn LED on when we first enter the mode
if ((event == EV_enter_state) || (event == EV_reenter_state)) {
+ #if defined(USE_MOMENTARY_MODE) && defined(USE_STROBE_STATE)
momentary_mode = 0; // 0 = ramping, 1 = strobes
+ #endif
// if we just got back from config mode, go back to memorized level
if (event == EV_reenter_state) {
arg = memorized_level;
@@ -792,6 +842,15 @@ uint8_t steady_state(Event event, uint16_t arg) {
// (off->hold->stepped_min->release causes this state)
else if (actual_level <= mode_min) { ramp_direction = 1; }
}
+ // if the button is stuck, err on the side of safety and ramp down
+ else if ((arg > TICKS_PER_SECOND * 5) && (actual_level >= mode_max)) {
+ ramp_direction = -1;
+ }
+ // if the button is still stuck, lock the light
+ else if ((arg > TICKS_PER_SECOND * 10) && (actual_level <= mode_min)) {
+ blip();
+ set_state(lockout_state, 0);
+ }
memorized_level = nearest_level((int16_t)actual_level \
+ (ramp_step_size * ramp_direction));
#else
@@ -919,59 +978,34 @@ uint8_t steady_state(Event event, uint16_t arg) {
if (arg == TICKS_PER_SECOND) ramp_direction = 1;
#endif
#ifdef USE_SET_LEVEL_GRADUALLY
- // make thermal adjustment speed scale with magnitude
- // also, adjust slower when going up
- if ((arg & 1) &&
- ((actual_level < THERM_FASTER_LEVEL) ||
- (actual_level < gradual_target))) {
- return MISCHIEF_MANAGED; // adjust slower when not a high mode
- }
- #ifdef THERM_HARD_TURBO_DROP
- else if ((! (actual_level < THERM_FASTER_LEVEL))
- && (actual_level > gradual_target)) {
- gradual_tick();
- }
- else {
- #endif
- // [int(62*4 / (x**0.8)) for x in (1,2,4,8,16,32,64,128)]
- //uint8_t intervals[] = {248, 142, 81, 46, 26, 15, 8, 5};
- // [int(62*4 / (x**0.9)) for x in (1,2,4,8,16,32,64,128)]
- //uint8_t intervals[] = {248, 132, 71, 38, 20, 10, 5, 3};
- // [int(62*4 / (x**0.95)) for x in (1,2,4,8,16,32,64,128)]
- uint8_t intervals[] = {248, 128, 66, 34, 17, 9, 4, 2};
- uint8_t diff;
- static uint8_t ticks_since_adjust = 0;
- if (gradual_target > actual_level) {
- // rise at half speed (skip half the frames)
- if (arg & 2) return MISCHIEF_MANAGED;
- diff = gradual_target - actual_level;
- } else {
- diff = actual_level - gradual_target;
- }
- ticks_since_adjust ++;
- // if there's any adjustment to be made, make it
+ int16_t diff = gradual_target - actual_level;
+ static uint16_t ticks_since_adjust = 0;
+ ticks_since_adjust++;
if (diff) {
- uint8_t magnitude = 0;
- #ifndef THERM_HARD_TURBO_DROP
- // if we're on a really high mode, drop faster
- if ((actual_level >= THERM_FASTER_LEVEL)
- && (actual_level > gradual_target)) { magnitude ++; }
- #endif
+ uint16_t ticks_per_adjust = 256;
+ if (diff < 0) {
+ //diff = -diff;
+ if (actual_level > THERM_FASTER_LEVEL) {
+ #ifdef THERM_HARD_TURBO_DROP
+ ticks_per_adjust >>= 2;
+ #endif
+ ticks_per_adjust >>= 2;
+ }
+ } else {
+ // rise at half speed
+ ticks_per_adjust <<= 1;
+ }
while (diff) {
- magnitude ++;
- diff >>= 1;
+ ticks_per_adjust >>= 1;
+ //diff >>= 1;
+ diff /= 2; // because shifting produces weird behavior
}
- uint8_t ticks_per_adjust = intervals[magnitude];
if (ticks_since_adjust > ticks_per_adjust)
{
gradual_tick();
ticks_since_adjust = 0;
}
- //if (!(arg % ticks_per_adjust)) gradual_tick();
}
- #ifdef THERM_HARD_TURBO_DROP
- }
- #endif
#endif // ifdef USE_SET_LEVEL_GRADUALLY
return MISCHIEF_MANAGED;
}
@@ -1024,6 +1058,18 @@ uint8_t steady_state(Event event, uint16_t arg) {
}
return MISCHIEF_MANAGED;
}
+ #ifdef USE_SET_LEVEL_GRADUALLY
+ // temperature is within target window
+ // (so stop trying to adjust output)
+ else if (event == EV_temperature_okay) {
+ // if we're still adjusting output... stop after the current step
+ if (gradual_target > actual_level)
+ gradual_target = actual_level + 1;
+ else if (gradual_target < actual_level)
+ gradual_target = actual_level - 1;
+ return MISCHIEF_MANAGED;
+ }
+ #endif // ifdef USE_SET_LEVEL_GRADUALLY
#endif // ifdef USE_THERMAL_REGULATION
return EVENT_NOT_HANDLED;
}
@@ -1100,7 +1146,9 @@ uint8_t strobe_state(Event event, uint16_t arg) {
// (maybe I should just make it nonvolatile?)
strobe_mode_te st = strobe_type;
+ #ifdef USE_MOMENTARY_MODE
momentary_mode = 1; // 0 = ramping, 1 = strobes
+ #endif
#ifdef USE_CANDLE_MODE
// pass all events to candle mode, when it's active
@@ -1232,8 +1280,12 @@ inline void party_tactical_strobe_mode_iter(uint8_t st) {
if (0) {} // placeholde0
#ifdef USE_PARTY_STROBE_MODE
else if (st == party_strobe_e) { // party strobe
+ #ifdef PARTY_STROBE_ONTIME
+ nice_delay_ms(PARTY_STROBE_ONTIME);
+ #else
if (del < 42) delay_zero();
else nice_delay_ms(1);
+ #endif
}
#endif
#ifdef USE_TACTICAL_STROBE_MODE
@@ -1484,8 +1536,29 @@ inline void police_strobe_iter() {
}
}
#endif
+#endif // #ifdef USE_BORING_STROBE_STATE
#ifdef USE_SOS_MODE
+#ifdef USE_SOS_MODE_IN_BLINKY_GROUP
+uint8_t sos_state(Event event, uint16_t arg) {
+ // 1 click: off
+ if (event == EV_1click) {
+ set_state(off_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ // 2 clicks: next mode
+ else if (event == EV_2clicks) {
+ #ifdef USE_THERMAL_REGULATION
+ set_state(tempcheck_state, 0);
+ #else
+ set_state(battcheck_state, 0);
+ #endif
+ return MISCHIEF_MANAGED;
+ }
+ return EVENT_NOT_HANDLED;
+}
+#endif
+
void sos_blink(uint8_t num, uint8_t dah) {
#define DIT_LENGTH 200
for (; num > 0; num--) {
@@ -1499,19 +1572,19 @@ void sos_blink(uint8_t num, uint8_t dah) {
nice_delay_ms(DIT_LENGTH);
}
// three "off" dits (or one "dah") between letters
- nice_delay_ms(DIT_LENGTH*2);
+ // (except for SOS, which is collectively treated as a single "letter")
+ //nice_delay_ms(DIT_LENGTH*2);
}
inline void sos_mode_iter() {
// one iteration of main loop()
- nice_delay_ms(1000);
+ //nice_delay_ms(1000);
sos_blink(3, 0); // S
sos_blink(3, 1); // O
sos_blink(3, 0); // S
- nice_delay_ms(1000);
+ nice_delay_ms(2000);
}
#endif // #ifdef USE_SOS_MODE
-#endif // #ifdef USE_BORING_STROBE_STATE
#ifdef USE_BATTCHECK
@@ -1521,11 +1594,25 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
set_state(off_state, 0);
return MISCHIEF_MANAGED;
}
+ #ifdef USE_GOODNIGHT_MODE
// 2 clicks: goodnight mode
else if (event == EV_2clicks) {
set_state(goodnight_state, 0);
return MISCHIEF_MANAGED;
}
+ #elif defined(USE_BEACON_MODE)
+ // 2 clicks: beacon mode
+ else if (event == EV_2clicks) {
+ set_state(beacon_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #elif defined(USE_THERMAL_REGULATION)
+ // 2 clicks: tempcheck mode
+ else if (event == EV_2clicks) {
+ set_state(tempcheck_state, 0);
+ return MISCHIEF_MANAGED;
+ }
+ #endif
return EVENT_NOT_HANDLED;
}
#endif
@@ -1538,11 +1625,13 @@ uint8_t tempcheck_state(Event event, uint16_t arg) {
set_state(off_state, 0);
return MISCHIEF_MANAGED;
}
+ #ifdef USE_BATTCHECK
// 2 clicks: battcheck mode
else if (event == EV_2clicks) {
set_state(battcheck_state, 0);
return MISCHIEF_MANAGED;
}
+ #endif
// 4 clicks: thermal config mode
else if (event == EV_4clicks) {
push_state(thermal_config_state, 0);
@@ -1562,11 +1651,13 @@ uint8_t beacon_state(Event event, uint16_t arg) {
}
// TODO: use sleep ticks to measure time between pulses,
// to save power
- // 2 clicks: tempcheck mode
+ // 2 clicks: next mode
else if (event == EV_2clicks) {
- #ifdef USE_THERMAL_REGULATION
+ #ifdef USE_SOS_MODE_IN_BLINKY_GROUP
+ set_state(sos_state, 0);
+ #elif defined(USE_THERMAL_REGULATION)
set_state(tempcheck_state, 0);
- #else
+ #elif defined(USE_BATTCHECK)
set_state(battcheck_state, 0);
#endif
return MISCHIEF_MANAGED;
@@ -1597,9 +1688,15 @@ uint8_t goodnight_state(Event event, uint16_t arg) {
set_state(off_state, 0);
return MISCHIEF_MANAGED;
}
- // 2 clicks: beacon mode
+ // 2 clicks: next mode
else if (event == EV_2clicks) {
+ #ifdef USE_BEACON_MODE
set_state(beacon_state, 0);
+ #elif defined(USE_SOS_MODE_IN_BLINKY_GROUP)
+ set_state(sos_state, 0);
+ #elif defined(USE_THERMAL_REGULATION)
+ set_state(tempcheck_state, 0);
+ #endif
return MISCHIEF_MANAGED;
}
// tick: step down (maybe) or off (maybe)
@@ -1725,6 +1822,7 @@ uint8_t lockout_state(Event event, uint16_t arg) {
}
// click, click, hold: change RGB aux LED color
else if (event == EV_click3_hold) {
+ setting_rgb_mode_now = 1;
if (0 == (arg & 0x3f)) {
uint8_t mode = (rgb_led_lockout_mode & 0x0f) + 1;
mode = mode % RGB_LED_NUM_COLORS;
@@ -1736,6 +1834,7 @@ uint8_t lockout_state(Event event, uint16_t arg) {
}
// click, click, hold, release: save new color
else if (event == EV_click3_hold_release) {
+ setting_rgb_mode_now = 0;
save_config();
return MISCHIEF_MANAGED;
}
@@ -1751,13 +1850,16 @@ uint8_t lockout_state(Event event, uint16_t arg) {
}
+#ifdef USE_MOMENTARY_MODE
uint8_t momentary_state(Event event, uint16_t arg) {
// TODO: momentary strobe here? (for light painting)
// init strobe mode, if relevant
+ #ifdef USE_STROBE_STATE
if ((event == EV_enter_state) && (momentary_mode == 1)) {
strobe_state(event, arg);
}
+ #endif
// light up when the button is pressed; go dark otherwise
// button is being held
@@ -1783,6 +1885,7 @@ uint8_t momentary_state(Event event, uint16_t arg) {
// disconnected for several seconds, so we want to be awake when that
// happens to speed up the process)
else if (event == EV_tick) {
+ #ifdef USE_STROBE_STATE
if (momentary_active) {
// 0 = ramping, 1 = strobes
if (momentary_mode == 1) {
@@ -1790,16 +1893,25 @@ uint8_t momentary_state(Event event, uint16_t arg) {
}
}
else {
- if (arg > TICKS_PER_SECOND*15) { // sleep after 15 seconds
+ #endif
+ if (arg > TICKS_PER_SECOND*5) { // sleep after 5 seconds
go_to_standby = 1; // sleep while light is off
- // TODO: lighted button should use lockout config?
+ // turn off lighted button
+ #ifdef USE_INDICATOR_LED
+ indicator_led(0);
+ #elif defined(USE_AUX_RGB_LEDS)
+ rgb_led_update(0, 0);
+ #endif
}
+ #ifdef USE_STROBE_STATE
}
+ #endif
return MISCHIEF_MANAGED;
}
return EVENT_NOT_HANDLED;
}
+#endif
#ifdef USE_MUGGLE_MODE
@@ -1919,6 +2031,9 @@ uint8_t muggle_state(Event event, uint16_t arg) {
// turn off, but don't go to the main "off" state
if (muggle_off_mode) {
if (arg > TICKS_PER_SECOND*1) { // sleep after 1 second
+ #ifdef USE_AUX_RGB_LEDS_WHILE_ON
+ rgb_led_set(0);
+ #endif
go_to_standby = 1; // sleep while light is off
}
}
@@ -1940,6 +2055,7 @@ uint8_t muggle_state(Event event, uint16_t arg) {
return MISCHIEF_MANAGED;
}
#endif
+ #ifdef USE_LVP
// low voltage is handled specially in muggle mode
else if(event == EV_voltage_low) {
uint8_t lvl = (actual_level >> 1) + (actual_level >> 2);
@@ -1950,7 +2066,15 @@ uint8_t muggle_state(Event event, uint16_t arg) {
}
return MISCHIEF_MANAGED;
}
+ #endif
+
+ return EVENT_NOT_HANDLED;
+}
+#endif
+
+#ifdef USE_VERSION_CHECK
+uint8_t version_check_state(Event event, uint16_t arg) {
return EVENT_NOT_HANDLED;
}
#endif
@@ -2247,6 +2371,27 @@ void indicator_blink(uint8_t arg) {
#endif
#if defined(USE_AUX_RGB_LEDS) && defined(TICK_DURING_STANDBY)
+uint8_t voltage_to_rgb() {
+ 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
+ };
+ uint8_t volts = voltage;
+ if (volts < 29) return 0;
+
+ uint8_t i;
+ for (i = 0; volts >= levels[i]; i += 2) {}
+ uint8_t color_num = levels[(i - 2) + 1];
+ return pgm_read_byte(rgb_led_colors + color_num);
+}
+
// do fancy stuff with the RGB aux LEDs
// mode: 0bPPPPCCCC where PPPP is the pattern and CCCC is the color
// arg: time slice number
@@ -2257,7 +2402,13 @@ void rgb_led_update(uint8_t mode, uint8_t arg) {
// 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
- if ((volts) && (volts < VOLTAGE_LOW)) { rgb_led_set(0); return; }
+ if ((volts) && (volts < VOLTAGE_LOW)) {
+ rgb_led_set(0);
+ #ifdef USE_BUTTON_LED
+ button_led_set(0);
+ #endif
+ return;
+ }
uint8_t pattern = (mode>>4); // off, low, high, blinking, ... more?
uint8_t color = mode & 0x0f;
@@ -2266,36 +2417,31 @@ void rgb_led_update(uint8_t mode, uint8_t arg) {
if ((! go_to_standby) && (pattern > 2)) { pattern = 2; }
- uint8_t colors[] = {
- 0b00000001, // 0: red
- 0b00000101, // 1: yellow
- 0b00000100, // 2: green
- 0b00010100, // 3: cyan
- 0b00010000, // 4: blue
- 0b00010001, // 5: purple
- 0b00010101, // 6: white
- };
+ const uint8_t *colors = rgb_led_colors;
uint8_t actual_color = 0;
if (color < 7) { // normal color
- actual_color = colors[color];
+ actual_color = pgm_read_byte(colors + color);
}
else if (color == 7) { // rainbow
- if (0 == (arg & 0x03)) {
+ uint8_t speed = 0x03; // awake speed
+ if (go_to_standby) speed = RGB_RAINBOW_SPEED; // asleep speed
+ if (0 == (arg & speed)) {
rainbow = (rainbow + 1) % 6;
}
- actual_color = colors[rainbow];
+ actual_color = pgm_read_byte(colors + rainbow);
}
else { // voltage
// show actual voltage while asleep...
if (go_to_standby) {
+ actual_color = voltage_to_rgb();
// choose a color based on battery voltage
- if (volts >= 38) actual_color = colors[4];
- else if (volts >= 33) actual_color = colors[2];
- else actual_color = colors[0];
+ //if (volts >= 38) actual_color = pgm_read_byte(colors + 4);
+ //else if (volts >= 33) actual_color = pgm_read_byte(colors + 2);
+ //else actual_color = pgm_read_byte(colors + 0);
}
// ... but during preview, cycle colors quickly
else {
- actual_color = colors[((arg>>1) % 3) << 1];
+ actual_color = pgm_read_byte(colors + (((arg>>1) % 3) << 1));
}
}
@@ -2307,17 +2453,40 @@ void rgb_led_update(uint8_t mode, uint8_t arg) {
frame = (frame + 1) % sizeof(animation);
pattern = animation[frame];
}
+ uint8_t result;
+ #ifdef USE_BUTTON_LED
+ uint8_t button_led_result;
+ #endif
switch (pattern) {
case 0: // off
- rgb_led_set(0);
+ result = 0;
+ #ifdef USE_BUTTON_LED
+ button_led_result = 0;
+ #endif
break;
case 1: // low
- rgb_led_set(actual_color);
+ result = actual_color;
+ #ifdef USE_BUTTON_LED
+ button_led_result = 1;
+ #endif
break;
- case 2: // high
- rgb_led_set(actual_color << 1);
+ default: // high
+ result = (actual_color << 1);
+ #ifdef USE_BUTTON_LED
+ button_led_result = 2;
+ #endif
break;
}
+ rgb_led_set(result);
+ #ifdef USE_BUTTON_LED
+ button_led_set(button_led_result);
+ #endif
+}
+
+void rgb_led_voltage_readout(uint8_t bright) {
+ uint8_t color = voltage_to_rgb();
+ if (bright) color = color << 1;
+ rgb_led_set(color);
}
#endif
@@ -2326,7 +2495,7 @@ void rgb_led_update(uint8_t mode, uint8_t arg) {
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
- #define SPLODEY_TIME 3000
+ #define SPLODEY_TIME 2500
#define SPLODEY_STEPS 64
#define SPLODEY_TIME_PER_STEP (SPLODEY_TIME/SPLODEY_STEPS)
uint8_t bright;
@@ -2334,9 +2503,9 @@ void factory_reset() {
// wind up to an explosion
for (bright=0; bright<SPLODEY_STEPS; bright++) {
set_level(bright);
- delay_4ms(SPLODEY_TIME_PER_STEP/2/4);
+ nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
set_level(bright>>1);
- delay_4ms(SPLODEY_TIME_PER_STEP/2/4);
+ nice_delay_ms(SPLODEY_TIME_PER_STEP/2);
if (! button_is_pressed()) {
reset = 0;
break;
@@ -2357,14 +2526,14 @@ void factory_reset() {
bright = MAX_LEVEL;
for (; bright > 0; bright--) {
set_level(bright);
- delay_4ms(SPLODEY_TIME_PER_STEP/6/4);
+ nice_delay_ms(SPLODEY_TIME_PER_STEP/8);
}
}
// explosion cancelled, fade away
else {
for (; bright > 0; bright--) {
set_level(bright);
- delay_4ms(SPLODEY_TIME_PER_STEP/3/4);
+ nice_delay_ms(SPLODEY_TIME_PER_STEP/3);
}
}
}
@@ -2556,11 +2725,35 @@ void loop() {
StatePtr state = current_state;
+ #ifdef USE_AUX_RGB_LEDS_WHILE_ON
+ if (! setting_rgb_mode_now) rgb_led_voltage_readout(1);
+ #endif
+
if (0) {}
+ #ifdef USE_VERSION_CHECK
+ else if (state == version_check_state) {
+ for (uint8_t i=0; i<sizeof(version_number)-1; i++) {
+ blink_digit(pgm_read_byte(version_number + i) - '0');
+ 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(off_state, 0);
+ }
+ #endif // #ifdef USE_VERSION_CHECK
+
#ifdef USE_STROBE_STATE
else if ((state == strobe_state)
- || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active)) ) { // also handle momentary strobes
+ #ifdef USE_MOMENTARY_MODE
+ // also handle momentary strobes
+ || ((state == momentary_state) && (momentary_mode == 1) && (momentary_active))
+ #endif
+ ) {
uint8_t st = strobe_type;
switch(st) {
@@ -2600,7 +2793,7 @@ void loop() {
break;
#endif
- #ifdef USE_SOS_MODE
+ #ifdef USE_SOS_MODE_IN_FF_GROUP
default: // SOS
sos_mode_iter();
break;
@@ -2621,6 +2814,12 @@ void loop() {
}
#endif
+ #ifdef USE_SOS_MODE_IN_BLINKY_GROUP
+ else if (state == sos_state) {
+ sos_mode_iter();
+ }
+ #endif
+
#ifdef USE_THERMAL_REGULATION
// TODO: blink out therm_ceil during thermal_config_state?
else if (state == tempcheck_state) {
diff --git a/spaghetti-monster/anduril/anduril.svg b/spaghetti-monster/anduril/anduril.svg
index 36840a4..d58e478 100644
--- a/spaghetti-monster/anduril/anduril.svg
+++ b/spaghetti-monster/anduril/anduril.svg
@@ -22,6 +22,49 @@
inkscape:export-ydpi="109.75774">
<defs
id="defs4">
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="marker19840"
+ style="overflow:visible;"
+ inkscape:isstock="true">
+ <path
+ id="path18419"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+ transform="scale(0.4) rotate(180) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0.0"
+ refX="0.0"
+ id="marker18673"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path18416"
+ d="M 0.0,0.0 L 5.0,-5.0 L -12.5,0.0 L 5.0,5.0 L 0.0,0.0 z "
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:1pt;stroke-opacity:1;fill:#000000;fill-opacity:1"
+ transform="scale(0.4) translate(10,0)" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mstart"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="marker17250"
+ style="overflow:visible"
+ inkscape:isstock="true">
+ <path
+ id="path17248"
+ d="M 0,0 5,-5 -12.5,0 5,5 Z"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
+ transform="matrix(0.4,0,0,0.4,4,0)"
+ inkscape:connector-curvature="0" />
+ </marker>
<linearGradient
inkscape:collect="always"
id="linearGradient7690">
@@ -57,7 +100,7 @@
<path
id="path61710"
d="M 0,0 5,-5 -12.5,0 5,5 Z"
- style="fill:#7777ff;fill-opacity:1;fill-rule:evenodd;stroke:#7777ff;stroke-width:1.00000003pt;stroke-opacity:1"
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.00000003pt;stroke-opacity:1"
transform="matrix(0.4,0,0,0.4,4,0)"
inkscape:connector-curvature="0" />
</marker>
@@ -1896,16 +1939,6 @@
style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
inkscape:connector-curvature="0" />
</marker>
- <linearGradient
- inkscape:collect="always"
- xlink:href="#linearGradient1694"
- id="linearGradient1364-8"
- x1="415.74805"
- y1="495.00006"
- x2="415.74805"
- y2="215.43314"
- gradientUnits="userSpaceOnUse"
- gradientTransform="translate(904.95044,-71.333651)" />
<marker
inkscape:stockid="Arrow1Mend"
orient="auto"
@@ -2773,6 +2806,42 @@
style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
inkscape:connector-curvature="0" />
</marker>
+ <linearGradient
+ inkscape:collect="always"
+ xlink:href="#linearGradient1694"
+ id="linearGradient1364-8-7"
+ x1="415.74805"
+ y1="495.00006"
+ x2="415.74805"
+ y2="215.43314"
+ gradientUnits="userSpaceOnUse"
+ gradientTransform="translate(904.95036,-71.333648)" />
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-597-9-5-1"
+ style="overflow:visible">
+ <path
+ id="path3856-76-6-3-3"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
+ <marker
+ inkscape:stockid="Arrow1Mend"
+ orient="auto"
+ refY="0"
+ refX="0"
+ id="Arrow1Mend-88-34"
+ style="overflow:visible">
+ <path
+ id="path3856-868-0"
+ d="M -4.2666667,0 -6.4,2.1333333 1.0666667,0 -6.4,-2.1333333 Z"
+ style="fill-rule:evenodd;stroke:#000000;stroke-width:0.42666668pt"
+ inkscape:connector-curvature="0" />
+ </marker>
</defs>
<sodipodi:namedview
id="base"
@@ -2786,10 +2855,10 @@
inkscape:cy="568.52482"
inkscape:document-units="px"
inkscape:current-layer="layer1"
- showgrid="false"
+ showgrid="true"
showguides="true"
gridtolerance="1"
- inkscape:window-width="2389"
+ inkscape:window-width="2548"
inkscape:window-height="1415"
inkscape:window-x="0"
inkscape:window-y="0"
@@ -2799,12 +2868,16 @@
fit-margin-right="0.5"
fit-margin-bottom="0.5"
units="px"
- inkscape:snap-global="false"
+ inkscape:snap-global="true"
inkscape:snap-bbox="true"
- inkscape:bbox-paths="true"
- inkscape:bbox-nodes="true"
+ inkscape:bbox-paths="false"
+ inkscape:bbox-nodes="false"
inkscape:snap-bbox-edge-midpoints="true"
- inkscape:snap-bbox-midpoints="true">
+ inkscape:snap-bbox-midpoints="true"
+ inkscape:snap-midpoints="true"
+ inkscape:snap-object-midpoints="true"
+ inkscape:object-paths="true"
+ inkscape:snap-intersection-paths="true">
<inkscape:grid
type="xygrid"
id="grid3065"
@@ -2876,6 +2949,49 @@
inkscape:connector-curvature="0"
sodipodi:nodetypes="ccccc" />
<rect
+ style="opacity:1;fill:url(#linearGradient1364-8-7);fill-opacity:1;stroke:#000000;stroke-width:3.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
+ id="rect1356-0-3"
+ width="37.795277"
+ height="264.56693"
+ x="1320.6984"
+ y="144.09947" />
+ <image
+ y="145.69688"
+ x="1339.1499"
+ id="image21186"
+ xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAMgCAYAAADVw8ezAAAABmJLR0QA/wD/AP+gvaeTAAAACXBI
+WXMAAC4mAAAuJgFVH1qAAAAAB3RJTUUH4wgWFywjmzkvJwAABGZJREFUeNrt08ENg0AQBEFjbf6R
+9vP+kMUKoeoQpjTXOef+6TX9TQBEQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQARE
+QIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABASIg
+QAREQIAICBABASIgQAREQIAICBABASIgQAQEiIAICBABASIgX28qK3iIgAARECACAkRAgAiIgAAR
+ECACAkRAgAiIgAARECACAkRAgAiIgAARECACAkRAgAiIgAARECACAkRAgAiIgAARECACAkRAgAiI
+gAARECACAkRAgAiIgAARECACAkRAgAiIgAARECACAkRAgAgIEAERECACAkRAgAgIEAERECACAkRA
+gGivqazgIQICRECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEiIAIC
+RECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEiIAICRECACAgQAQEi
+IEAERECACAgQAQEiIEAERECACAgQAQGivaaygocICBABASIgQAQEiIAICBABASIgQAQEiIAICBAB
+ASIgQAQEiIAICBABASIgQAQEiIAICBABASIgQAQEiIAICBABASIgQAQEiIAICBABASIgQAQEiIAI
+CBABASIgQAQEiIAICBABASIgQAQEiIAAERABASIgQAQEiIAAERABASIgQAQEiPaaygoeIiBABASI
+gAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBA
+BASIgAARECACIiBABASIgAARECACIiBABASIgAARECACIiBABASIgAARECACAkRABASIgAARECAC
+AkRABASIgAARECDaayoreIiAABEQIAICRECACIiAABEQIAICRECACIiAABEQIAICRECACIiAABEQ
+IAICRECACIiAABEQIAICRECACIiAABEQIAICRECACIiAABEQIAICRECACIiAABEQIAICRECACIiA
+ABEQIAICRECACAgQAREQIAICRECACAgQAREQIAICRECAaK+prOAhAgJEQIAICBABASIgAgJEQIAI
+CBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgAgJE
+QIAICBABASIgAgJEQIAICBABASIgAgJEQIAICBABASIgQAREQIAICBABASIgQAREQIAICBABAaK9
+prKChwgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEB
+IiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAgIEAEBIiBABASIgAAR
+EAEBIiBABASIgAAREAEBIiBABASI9noABIsdhY+pbIsAAAAASUVORK5CYII=
+"
+ preserveAspectRatio="none"
+ height="261.36438"
+ width="17.741749" />
+ <path
+ style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:0.30000001;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:1.80000007,1.80000007;stroke-dashoffset:2.19000029;stroke-opacity:1"
+ d="m 1339.1499,145.69688 0,261.36438"
+ id="path15687-2-6-9"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cc" />
+ <rect
style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
id="rect5159-4"
width="416.73956"
@@ -3113,13 +3229,6 @@
id="path5213-6-8-4-9-9-8-4-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
- <rect
- style="opacity:1;fill:url(#linearGradient1364-8);fill-opacity:1;stroke:#000000;stroke-width:3.20000005;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1"
- id="rect1356-0"
- width="37.795277"
- height="264.56693"
- x="1320.6984"
- y="144.09947" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
@@ -3136,35 +3245,35 @@
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
x="1268.1858"
- y="203.77095"
+ y="201.6805"
id="text3021-8-4-1-8-6-14-2-4-7-7-9"
transform="scale(1.0975814,0.91109416)"><tspan
sodipodi:role="line"
x="1268.1858"
- y="203.77095"
+ y="201.6805"
id="tspan3613-9-7-5-6"
style="font-size:17.06666756px;line-height:1.25;stroke-width:1.06666672">Ceil</tspan></text>
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
x="1263.0579"
- y="426.17856"
+ y="417.30948"
id="text3021-8-4-1-8-6-14-2-4-7-7-3-1"
transform="scale(1.0975814,0.91109416)"><tspan
sodipodi:role="line"
x="1263.0579"
- y="426.17856"
+ y="417.30948"
id="tspan3613-9-7-5-5-0"
style="font-size:17.06666756px;line-height:1.25;stroke-width:1.06666672">Floor</tspan></text>
<path
style="fill:none;stroke:#009d00;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-70)"
- d="M 1419.75,188.60753 V 372.1669"
+ d="m 1419.75,188.60753 0,174.38457"
id="path5213-6-8-2-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
- style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-7-5)"
- d="M 1400.1843,366.71374 V 191.75683"
+ style="fill:none;stroke:#000000;stroke-width:2.1329999;stroke-miterlimit:4;stroke-dasharray:12.79799938,2.1329999;stroke-dashoffset:7.46549964;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-7-5)"
+ d="M 1400.1843,359.40373 V 191.75683"
id="path5213-6-8-2-9-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
@@ -3245,22 +3354,16 @@
</g>
<path
style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:7.30000019;stroke-opacity:1"
- d="m 1319.7047,180.764 h 69.7795"
+ d="m 1319.7047,178.2642 h 69.7795"
id="path15687-2-6"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#009d00;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:12.8, 2.13333333;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-6-0)"
- d="m 1131.0344,258.42719 c 48.1809,-3.99296 64.2076,-2.68523 79.9712,-42.47521 42.8072,-108.05255 133.489,-146.16407 131.2384,-34.5044"
+ d="m 1131.0344,258.42719 c 48.1809,-3.99296 64.2076,-2.68523 79.9712,-42.47521 42.8072,-108.05255 130.8373,-147.401507 128.5867,-35.74184"
id="path5213-6-8-4-9-9-9-29-5-2"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc" />
- <path
- style="opacity:1;fill:#c0c0c0;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4, 4;stroke-dashoffset:0;stroke-opacity:1"
- d="m 1319.7047,381.98134 h 64.5803"
- id="path15687-9-5"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cc" />
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:24px;line-height:0%;font-family:'DejaVu Sans';-inkscape-font-specification:Sans;text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
@@ -3842,19 +3945,19 @@
id="tspan7287-5-5-6-6-1"> 6 Clicks</tspan></textPath></text>
<path
style="fill:none;stroke:none;stroke-width:1.32938921;stroke-miterlimit:4;stroke-dasharray:1.32938933, 1.32938933;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-88-9-4-0-4-9)"
- d="m 1400.1241,365.79536 c 0,0 0.043,-10.40514 -0.024,5.00754"
+ d="m 1400.1843,364.54546 c 0,0 -0.017,-16.32727 -0.084,-0.91459"
id="path5213-6-8-4-9-9-1-3-0-0-6-4-9"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#000000;stroke-width:2.13333344;stroke-miterlimit:4;stroke-dasharray:4.26666689, 4.26666689;stroke-dashoffset:4.05333376;stroke-opacity:1;marker-start:url(#marker14034-2-9);marker-end:url(#Arrow1Mend-597-9-5)"
- d="m 1159.7225,198.62656 c 67.9862,-0.44112 68.743,-36.42258 0.1793,-35.70399"
+ d="m 954.34165,198.62656 c 67.98625,-0.44112 68.74305,-36.42258 0.1793,-35.70399"
id="path5213-6-8-4-9-9-8-7-3-8"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
<g
id="g18567"
- transform="translate(13.62988,7.3367765)">
+ transform="translate(-191.75097,7.3367765)">
<text
id="text56273"
y="160.56241"
@@ -3898,7 +4001,7 @@
</g>
<g
id="g18572"
- transform="translate(-76.57207,43.516866)">
+ transform="translate(-281.95292,43.516866)">
<rect
ry="3"
rx="3"
@@ -4249,7 +4352,7 @@
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-6-0-0)"
- d="m 1131.0344,258.42719 c 48.1809,-3.99296 64.2076,-2.68523 79.9712,-42.47521 42.8072,-108.05255 133.489,-146.164071 131.2384,-34.5044"
+ d="m 1131.0344,258.42719 c 48.1809,-3.99296 64.2076,-2.68523 79.9712,-42.47521 42.8072,-108.05255 130.8373,-147.401511 128.5867,-35.74184"
id="path5213-6-8-4-9-9-9-29-5-2-7"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc" />
@@ -4289,7 +4392,7 @@
sodipodi:nodetypes="cc" />
<path
style="fill:none;stroke:#ffffff;stroke-width:1;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker-end:url(#Arrow1Mend-62-70-9)"
- d="M 1419.75,188.60753 V 372.1669"
+ d="M 1419.75,188.60753 V 362.9921"
id="path5213-6-8-2-4-1"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cc" />
@@ -4639,11 +4742,11 @@
<text
xml:space="preserve"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:14.66666698px;line-height:110.00000238%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
- x="1185.0386"
+ x="979.65771"
y="185.57895"
id="text39623-4-9-0-0"><tspan
sodipodi:role="line"
- x="1185.0386"
+ x="979.65771"
y="185.57895"
id="tspan39625-9-1-3-5">3C</tspan></text>
<text
@@ -4666,8 +4769,8 @@
y="1179.0468"
id="tspan39625-9-1-3-9">3C</tspan></text>
<path
- style="fill:none;stroke:#040404;stroke-width:2.18333006;stroke-miterlimit:4;stroke-dasharray:13.09998035, 2.18333006;stroke-dashoffset:1.30999804;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-6-0-0-2)"
- d="m 1130.3238,305.57915 c 48.1809,3.99296 64.2076,2.68523 79.9712,42.47521 42.8072,108.05255 133.489,146.16407 131.2384,34.5044"
+ style="fill:none;stroke:#040404;stroke-width:2.18300009;stroke-miterlimit:4;stroke-dasharray:13.09800053,2.18300009;stroke-dashoffset:6.76730027;stroke-opacity:1;marker-end:url(#Arrow1Mend-367-6-0-0-2)"
+ d="m 1130.3238,305.57915 c 48.1809,3.99296 64.2076,2.68523 79.9712,42.47521 42.8072,108.05255 131.144,142.50981 129.1171,41.94961"
id="path5213-6-8-4-9-9-9-29-5-2-7-4"
inkscape:connector-curvature="0"
sodipodi:nodetypes="csc" />
@@ -4799,5 +4902,42 @@
x="1091.5458"
y="1212.6874"
id="tspan43995-1">Factory Reset: Loosen tailcap, Hold button, Tighten tailcap, Hold 3s (or 13H from Off)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.29780102px;line-height:110.00000238%;font-family:'DejaVu Sans';-inkscape-font-specification:'DejaVu Sans';text-align:start;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.70212275px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ x="1341.7825"
+ y="220.06078"
+ id="text39623-4-9-0-0-1"
+ transform="scale(0.99329477,1.0067505)"><tspan
+ sodipodi:role="line"
+ x="1341.7825"
+ y="220.06078"
+ id="tspan39625-9-1-3-5-0"
+ style="stroke-width:0.70212275px">3C</tspan></text>
+ <g
+ id="g21114"
+ transform="matrix(0.5165354,0,0,0.5165354,676.70758,110.93007)">
+ <path
+ style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;font-variant-ligatures:normal;font-variant-position:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-alternates:normal;font-feature-settings:normal;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;writing-mode:lr-tb;direction:ltr;text-orientation:mixed;dominant-baseline:auto;baseline-shift:baseline;text-anchor:start;white-space:normal;shape-padding:0;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;vector-effect:none;fill:#000000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.13333344;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4.26666689, 4.26666689;stroke-dashoffset:4.26666689;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
+ d="m 1263.3125,222.54688 h 4.2676 v -2.13282 h -4.2676 z m 8.5332,0 h 4.2676 v -2.13282 h -4.2676 z m 8.5332,0 h 4.2676 v -2.13282 h -4.2676 z m 8.5352,0 h 4.2656 v -2.13282 h -4.2656 z m 8.5332,0 h 4.2656 v -2.13282 h -4.2656 z"
+ id="path5213-6-8-4-9-9-8-3-3"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.85333344pt;stroke-opacity:1"
+ d="m 1267.5798,221.48082 4.2667,-4.26667 -14.9333,4.26667 14.9333,4.26667 z"
+ id="path21120"
+ inkscape:connector-curvature="0" />
+ <path
+ style="fill:#000000;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:0.85333344pt;stroke-opacity:1"
+ d="m 1297.3647,221.48082 -4.2667,4.26667 14.9333,-4.26667 -14.9333,-4.26667 z"
+ id="path21122"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ style="opacity:1;fill:none;fill-opacity:1;stroke:#040000;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:4,4;stroke-dashoffset:3.4;stroke-opacity:1"
+ d="m 1319.7047,390.82553 h 19.4452 l 0,-16.40309 h 28.99 16.1451"
+ id="path15687-9-5-1"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ccccc" />
</g>
</svg>
diff --git a/spaghetti-monster/anduril/anduril.txt b/spaghetti-monster/anduril/anduril.txt
index db73cdb..0a3dc4c 100644
--- a/spaghetti-monster/anduril/anduril.txt
+++ b/spaghetti-monster/anduril/anduril.txt
@@ -162,6 +162,12 @@ Indicator LED / aux LED support:
TODO:
+ - change 6C to 6H for exiting muggle mode?
+ - move muggle mode from 6C to ... 8C or something?
+ - add 4H from lockout to turn light on and start ramping up?
+ - move config modes to 5C instead of 4C, and move manual memory to 4C?
+ - remove beacon config mode, and use a hold to set timing instead?
+ - rewrite muggle mode to split it into on and off states
* save settings in eeprom
* decide on "hold until hot" or "click N times" for thermal config mode
* test thermal regulation on an actual light
diff --git a/spaghetti-monster/anduril/build-all.sh b/spaghetti-monster/anduril/build-all.sh
index 56a88bf..42a36fd 100755
--- a/spaghetti-monster/anduril/build-all.sh
+++ b/spaghetti-monster/anduril/build-all.sh
@@ -2,6 +2,8 @@
UI=anduril
+date '+#define VERSION_NUMBER "%Y%m%d"' > version.h
+
for TARGET in cfg-*.h ; do
NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";')
echo "===== $NAME ====="
diff --git a/spaghetti-monster/anduril/cfg-blf-lantern.h b/spaghetti-monster/anduril/cfg-blf-lantern.h
index 9467397..e12a453 100644
--- a/spaghetti-monster/anduril/cfg-blf-lantern.h
+++ b/spaghetti-monster/anduril/cfg-blf-lantern.h
@@ -27,6 +27,10 @@
// (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
@@ -51,6 +55,12 @@
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 5
+#define MUGGLE_FLOOR 15 // about 20 lm
+#define MUGGLE_CEILING 115 // about 350 lm
+
+#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
#ifdef USE_THERMAL_REGULATION
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1.h b/spaghetti-monster/anduril/cfg-emisar-d1.h
index 35e91db..9276ba3 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d1.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d1.h
@@ -8,8 +8,3 @@
#undef THERM_FASTER_LEVEL
#endif
#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high
-
-// no need to be extra-careful on this light
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
diff --git a/spaghetti-monster/anduril/cfg-emisar-d18.h b/spaghetti-monster/anduril/cfg-emisar-d18.h
index 16fbacd..155a747 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d18.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d18.h
@@ -42,5 +42,3 @@
// stop panicking at about ~40% power or ~5000 lm
#define THERM_FASTER_LEVEL 125
-// optional, makes initial turbo step-down faster so first peak isn't as hot
-//#define THERM_HARD_TURBO_DROP
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1s.h b/spaghetti-monster/anduril/cfg-emisar-d1s.h
index 56200a2..c01c37d 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d1s.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d1s.h
@@ -8,8 +8,3 @@
#undef THERM_FASTER_LEVEL
#endif
#define THERM_FASTER_LEVEL 144 // throttle back faster when high
-
-// no need to be extra-careful on this light
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
diff --git a/spaghetti-monster/anduril/cfg-emisar-d1v2.h b/spaghetti-monster/anduril/cfg-emisar-d1v2.h
new file mode 100644
index 0000000..91be3f3
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-emisar-d1v2.h
@@ -0,0 +1,17 @@
+// Emisar D1v2 config options for Anduril (D1 w/ D4v2 driver)
+// ATTINY: 1634
+// same as Emisar D4v2, mostly
+#include "cfg-emisar-d4v2.h"
+
+// there are no aux LEDs on a D1
+#undef USE_AUX_RGB_LEDS
+// ... and no button LED
+#undef USE_BUTTON_LED
+// no aux LEDs means no need for sleep ticks
+#undef TICK_DURING_STANDBY
+
+// stop panicking at ~75% power or ~1000 lm (D1 has a decent power-to-thermal-mass ratio)
+#ifdef THERM_FASTER_LEVEL
+#undef THERM_FASTER_LEVEL
+#endif
+#define THERM_FASTER_LEVEL (RAMP_SIZE*9/10) // throttle back faster when high
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4.h b/spaghetti-monster/anduril/cfg-emisar-d4.h
index c86a534..7700d88 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4.h
@@ -18,14 +18,6 @@
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
-// optional, makes initial turbo step-down faster so first peak isn't as hot
-// the D4 runs very very hot, so be extra careful
-//#define THERM_HARD_TURBO_DROP
-
// stop panicking at ~30% power or ~1200 lm
#define THERM_FASTER_LEVEL 105
-// respond to thermal changes faster
-#define THERMAL_WARNING_SECONDS 3
-#define THERMAL_UPDATE_SPEED 1
-#define THERM_PREDICTION_STRENGTH 4
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4s.h b/spaghetti-monster/anduril/cfg-emisar-d4s.h
index 230ac7c..f5addb2 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4s.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4s.h
@@ -37,12 +37,3 @@
#endif
#define MIN_THERM_STEPDOWN 60 // lowest value it'll step down to
#define THERM_FASTER_LEVEL (RAMP_SIZE-20) // don't throttle back faster when high
-
-// no need to be extra-careful on this light
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
-
-#define THERMAL_WARNING_SECONDS 3
-#define THERMAL_UPDATE_SPEED 2
-#define THERM_PREDICTION_STRENGTH 4
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h
new file mode 100644
index 0000000..9898246
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-emisar-d4sv2-219.h
@@ -0,0 +1,10 @@
+// Emisar D4Sv2-219 config options for Anduril
+#include "cfg-emisar-d4sv2.h"
+// ATTINY: 1634
+
+#undef PWM1_LEVELS
+#undef PWM2_LEVELS
+#undef PWM3_LEVELS
+#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,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,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,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,2,3,4,6,7,9,10,12,13,15,17,18,20,22,24,26,28,30,33,35,37,40,43,45,48,50,53,56,59,62,65,69,72,76,80,83,87,91,95,99,104,108,113,117,123,127,132,138,143,148,154,160,166,172,178,185,192
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4sv2.h b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
new file mode 100644
index 0000000..c578c4a
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-emisar-d4sv2.h
@@ -0,0 +1,53 @@
+// Emisar D4S V2 config options for Anduril
+#include "hwdef-Emisar_D4Sv2.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
+// enable blinking aux LEDs
+#define TICK_DURING_STANDBY
+#define STANDBY_TICK_SPEED 3 // every 0.128 s
+//#define STANDBY_TICK_SPEED 4 // every 0.256 s
+//#define STANDBY_TICK_SPEED 5 // every 0.512 s
+
+
+// 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 RAMP_SMOOTH_FLOOR 1
+#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
+
+#define DEFAULT_LEVEL MAX_Nx7135
+#define STROBE_BRIGHTNESS MAX_LEVEL
+
+#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR
+#define MUGGLE_CEILING MAX_Nx7135
+
+// stop panicking at ~50% power or ~2000 lm
+#define THERM_FASTER_LEVEL 130
+
+// easier access to thermal config mode, for Emisar
+#define USE_TENCLICK_THERMAL_CONFIG
+
+#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-nofet.h b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h
new file mode 100644
index 0000000..cbb5891
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2-nofet.h
@@ -0,0 +1,34 @@
+// Emisar D4v2-noFET config options for Anduril
+#include "cfg-emisar-d4v2.h"
+// ATTINY: 1634
+
+#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 MAX_1x7135
+#define MAX_1x7135 150
+#undef QUARTERSPEED_LEVEL
+#undef HALFSPEED_LEVEL
+#define QUARTERSPEED_LEVEL 8
+#define HALFSPEED_LEVEL 16
+
+#define DEFAULT_LEVEL 80
+
+#undef RAMP_SMOOTH_CEIL
+#define RAMP_SMOOTH_CEIL 150
+// 10, 45, 80, 115, 150
+#undef RAMP_DISCRETE_FLOOR
+#undef RAMP_DISCRETE_CEIL
+#undef RAMP_DISCRETE_STEPS
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL 150
+#define RAMP_DISCRETE_STEPS 5
+
+#define CANDLE_AMPLITUDE 60
+
+#undef THERM_FASTER_LEVEL
+#define THERM_FASTER_LEVEL 150
+
+#undef USE_THERMAL_REGULATION
diff --git a/spaghetti-monster/anduril/cfg-emisar-d4v2.h b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
index b83c65c..241ca7e 100644
--- a/spaghetti-monster/anduril/cfg-emisar-d4v2.h
+++ b/spaghetti-monster/anduril/cfg-emisar-d4v2.h
@@ -4,7 +4,11 @@
// 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
@@ -33,17 +37,8 @@
#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
#define RAMP_DISCRETE_STEPS 7
-// optional, makes initial turbo step-down faster so first peak isn't as hot
-// the D4 runs very very hot, so be extra careful
-//#define THERM_HARD_TURBO_DROP
-
// stop panicking at ~30% power or ~1200 lm
#define THERM_FASTER_LEVEL 105
-// respond to thermal changes faster
-#define THERMAL_WARNING_SECONDS 3
-#define THERMAL_UPDATE_SPEED 1
-#define THERM_PREDICTION_STRENGTH 4
-//#define THERM_RESPONSE_MAGNITUDE 128
// easier access to thermal config mode, for Emisar
#define USE_TENCLICK_THERMAL_CONFIG
diff --git a/spaghetti-monster/anduril/cfg-ff-pl47.h b/spaghetti-monster/anduril/cfg-ff-pl47.h
index 7a81c25..e6907c1 100644
--- a/spaghetti-monster/anduril/cfg-ff-pl47.h
+++ b/spaghetti-monster/anduril/cfg-ff-pl47.h
@@ -61,11 +61,6 @@
// regulate down faster when the FET is active, slower otherwise
#define THERM_FASTER_LEVEL 135 // throttle back faster when high
-// play it safe, don't try to regulate above the recommended safe level
-#ifdef THERM_HARD_TURBO_DROP
-#undef THERM_HARD_TURBO_DROP
-#endif
-
// don't do this
#undef BLINK_AT_RAMP_MIDDLE
#undef BLINK_AT_RAMP_CEILING
diff --git a/spaghetti-monster/anduril/cfg-ff-pl47g2.h b/spaghetti-monster/anduril/cfg-ff-pl47g2.h
new file mode 100644
index 0000000..cab008c
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-ff-pl47g2.h
@@ -0,0 +1,55 @@
+// Fireflies PL47 G2 config options for Anduril
+#include "hwdef-FF_PL47.h"
+
+// the button lights up
+#define USE_INDICATOR_LED
+// the aux LEDs are in the same place as the main LEDs
+#ifdef USE_INDICATOR_LED_WHILE_RAMPING
+#undef USE_INDICATOR_LED_WHILE_RAMPING
+#endif
+// enable blinking indicator LED while off?
+#define TICK_DURING_STANDBY
+#define STANDBY_TICK_SPEED 3 // every 0.128 s
+#define USE_FANCIER_BLINKING_INDICATOR
+
+// If TICK_DURING_STANDBY is enabled...
+// off mode: low (1)
+// lockout: blinking (3)
+#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+
+
+#define RAMP_LENGTH 150
+
+// driver is a FET + 3x7135, ~400 lm at highest regulated level
+// ramp copied from Emisar D4S ramp
+#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,16,17,18,19,21,22,23,25,26,27,29,31,32,34,36,38,40,42,44,46,49,51,54,56,59,62,65,68,71,74,78,81,85,89,93,97,101,106,110,115,120,125,130,136,141,147,153,160,166,173,180,187,195,202,210,219,227,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,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,1,3,4,5,7,8,10,11,13,14,16,18,19,21,23,25,27,29,31,34,36,38,41,43,46,48,51,54,57,60,63,66,69,72,76,79,83,87,91,95,99,103,107,112,116,121,126,131,136,141,146,152,158,163,169,175,182,188,195,202,209,216,223,231,239,247,255
+#define MAX_1x7135 83
+#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_CEIL 120
+
+// 10, 28, 46, 65, [83], 101, 120
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL 120
+#define RAMP_DISCRETE_STEPS 7
+
+// ~25 lm to ~300 lm
+#define MUGGLE_FLOOR 30
+#define MUGGLE_CEILING MAX_1x7135
+
+// regulate down faster when the FET is active, slower otherwise
+#define THERM_FASTER_LEVEL 135 // throttle back faster when high
+
+// don't do this
+#undef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_CEILING
+
diff --git a/spaghetti-monster/anduril/cfg-ff-rot66.h b/spaghetti-monster/anduril/cfg-ff-rot66.h
index 2a90343..a87b66d 100644
--- a/spaghetti-monster/anduril/cfg-ff-rot66.h
+++ b/spaghetti-monster/anduril/cfg-ff-rot66.h
@@ -38,9 +38,6 @@
// regulate down faster when the FET is active, slower otherwise
#define THERM_FASTER_LEVEL 130 // throttle back faster when high
-// play it safe, don't try to regulate above the recommended safe level
-//#define THERM_HARD_TURBO_DROP
-
// don't do this
#undef BLINK_AT_RAMP_MIDDLE
#undef BLINK_AT_RAMP_CEILING
diff --git a/spaghetti-monster/anduril/cfg-ff-rot66g2.h b/spaghetti-monster/anduril/cfg-ff-rot66g2.h
new file mode 100644
index 0000000..4e353a8
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-ff-rot66g2.h
@@ -0,0 +1,49 @@
+// Fireflies ROT66 G2 config options for Anduril
+#include "cfg-ff-rot66.h"
+
+// if the "low" mode was disabled, turn it back on
+#ifdef INDICATOR_LED_SKIP_LOW
+#undef INDICATOR_LED_SKIP_LOW
+#endif
+// enable blinking indicator LED while off
+#define TICK_DURING_STANDBY
+#define STANDBY_TICK_SPEED 3 // every 0.128 s
+#define USE_FANCIER_BLINKING_INDICATOR
+
+// lockout: blinking (3), off: low (1)
+#ifdef INDICATOR_LED_DEFAULT_MODE
+#undef INDICATOR_LED_DEFAULT_MODE
+#endif
+#define INDICATOR_LED_DEFAULT_MODE ((3<<2) + 1)
+
+// ramp shape is different than original ROT66
+// 1x7135: 150 lm
+// Nx7135: 1200 lm
+// FET: 4500 lm
+// ../../../bin/level_calc.py 7.0 3 150 7135 1 4 180.16 7135 8 1 1374.48 FET 1 10 4000
+// (plus some manual tweaks)
+#undef PWM1_LEVELS
+#undef PWM2_LEVELS
+#undef PWM3_LEVELS
+#define PWM1_LEVELS 1,1,2,3,3,4,4,5,6,7,7,8,9,10,11,12,16,17,18,19,20,21,22,24,26,28,30,32,35,37,40,42,45,48,51,55,58,62,65,69,74,78,83,87,92,98,103,109,115,121,128,134,142,149,157,165,173,182,191,201,211,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,255,255,255,255,255,255,255,255,255,255,255,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,9,11,13,14,16,18,20,22,24,26,28,30,33,35,38,41,43,46,49,52,55,59,62,66,69,73,77,81,85,89,94,98,103,108,113,118,124,129,135,141,147,153,160,166,173,180,188,195,203,211,219,228,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,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,6,11,17,23,29,36,42,49,56,63,71,78,86,94,102,111,119,128,137,147,156,166,176,187,197,208,219,231,243,255
+
+#undef MAX_1x7135
+#undef MAX_Nx7135
+#define MAX_1x7135 65 // ~150 lm
+#define MAX_Nx7135 120 // ~1200 m
+#undef HALFSPEED_LEVEL
+#undef QUARTERSPEED_LEVEL
+#define HALFSPEED_LEVEL 17
+#define QUARTERSPEED_LEVEL 6
+
+// higher floor than default, and stop at highest regulated level
+#define RAMP_SMOOTH_FLOOR 1 // ~0.3 lm
+#define RAMP_SMOOTH_CEIL MAX_Nx7135 // ~1200 lm
+// 10, 28, 46, [65], 83, 101, [120]
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL MAX_Nx7135
+#define RAMP_DISCRETE_STEPS 7
+
+
diff --git a/spaghetti-monster/anduril/cfg-fw3a-nofet.h b/spaghetti-monster/anduril/cfg-fw3a-nofet.h
new file mode 100644
index 0000000..a6be10d
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-fw3a-nofet.h
@@ -0,0 +1,36 @@
+// FW3A with the FET disabled
+#include "cfg-fw3a.h"
+
+// don't use channel 3 (FET)
+#undef PWM_CHANNELS
+#undef PWM3_PIN
+#undef PWM3_LVL
+#define PWM_CHANNELS 2
+
+// reconfigure the ramp
+#undef PWM1_LEVELS
+#undef PWM2_LEVELS
+#undef PWM3_LEVELS
+// copied from Emisar D4, mostly
+#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,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 HALFSPEED_LEVEL
+#define HALFSPEED_LEVEL 14
+#undef QUARTERSPEED_LEVEL
+#define QUARTERSPEED_LEVEL 6
+
+#undef RAMP_SMOOTH_FLOOR
+#undef RAMP_SMOOTH_CEIL
+#undef RAMP_DISCRETE_FLOOR
+#undef RAMP_DISCRETE_CEIL
+#undef RAMP_DISCRETE_STEPS
+
+#define RAMP_SMOOTH_FLOOR 1
+#define RAMP_SMOOTH_CEIL 150
+// 10, 33, 56, 80, 103, 126, 150
+#define RAMP_DISCRETE_FLOOR 10
+#define RAMP_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
+
diff --git a/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h
new file mode 100644
index 0000000..28c77c2
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-mateminco-mf01-mini.h
@@ -0,0 +1,50 @@
+// Mateminco/Astrolux MF01-Mini options for Anduril
+#include "hwdef-Mateminco_MF01-Mini.h"
+
+// the button lights up
+#define USE_INDICATOR_LED
+//#define INDICATOR_LED_SKIP_LOW // low mode doesn't work on this driver
+// the button is visible while main LEDs are on
+//#define USE_INDICATOR_LED_WHILE_RAMPING
+// enable blinking indicator LED while off
+#define TICK_DURING_STANDBY
+#define STANDBY_TICK_SPEED 3 // every 0.128 s
+#define USE_FANCIER_BLINKING_INDICATOR
+// off mode: low (1)
+// lockout: blinking (3)
+#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_CEILING
+#undef BLINK_AT_RAMP_MIDDLE
+#undef BLINK_AT_RAMP_FLOOR
+
+// measured brightness with Sofirn 5500mAh cell at 3.97V:
+// moon: 0.3 lm
+// channel 1: 113 lm
+// channel 2: 718 lm
+// 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 QUARTERSPEED_LEVEL 8
+
+#define RAMP_SMOOTH_FLOOR 1 // ~0.3 lm
+#define RAMP_SMOOTH_CEIL 130 // ~??? lm
+// 14/135/6 = 14, 38, 62, 86, [110], 135
+// 20/110/7 = 20, 35, 50, [65], 80, 95, [110]
+// 15/130/7 = 15, 34, 53, 72, 91, [110], 130 <--
+#define RAMP_DISCRETE_FLOOR 15 // ~?? lm
+#define RAMP_DISCRETE_CEIL 130 // ~??? lm
+#define RAMP_DISCRETE_STEPS 7 // ??, ??, ... lm
+
+
+#define USE_TENCLICK_THERMAL_CONFIG // by request
+#define THERM_FASTER_LEVEL 130 // throttle back faster when high
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1-12v.h b/spaghetti-monster/anduril/cfg-noctigon-k1-12v.h
new file mode 100644
index 0000000..617801f
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-noctigon-k1-12v.h
@@ -0,0 +1,65 @@
+// Noctigon K1 12V config options for Anduril
+#include "hwdef-Noctigon_K1-12V.h"
+// ATTINY: 1634
+
+// this light can safely run a bit hotter than most
+#undef DEFAULT_THERM_CEIL
+#define DEFAULT_THERM_CEIL 55
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+#define USE_AUX_RGB_LEDS_WHILE_ON
+#define USE_INDICATOR_LED_WHILE_RAMPING
+#define RGB_LED_OFF_DEFAULT 0x18 // low, voltage
+#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow
+
+// enable blinking aux LEDs
+#define TICK_DURING_STANDBY
+#define STANDBY_TICK_SPEED 3 // every 0.128 s
+
+
+// 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
+
+#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
+
+#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR
+#define MUGGLE_CEILING 70
+
+// make candle mode wobble more
+#define CANDLE_AMPLITUDE 32
+
+// stop panicking at ~70% power or ~600 lm
+#define THERM_FASTER_LEVEL 130
+
+#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly
+#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting
+
+// easier access to thermal config mode, for Noctigon
+#define USE_TENCLICK_THERMAL_CONFIG
+
+// slow down party strobe; this driver can't pulse for 1ms or less
+#define PARTY_STROBE_ONTIME 4
+
+#define THERM_CAL_OFFSET 5
+
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h b/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h
new file mode 100644
index 0000000..fd98979
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-noctigon-k1-sbt90.h
@@ -0,0 +1,61 @@
+// Noctigon K1-SBT90.2 config options for Anduril
+// (is a K1 host with a KR4-like driver and a really high-powered LED)
+#include "hwdef-Noctigon_K1-SBT90.h"
+// ATTINY: 1634
+
+// this light can safely run a bit hotter than most
+#undef DEFAULT_THERM_CEIL
+#define DEFAULT_THERM_CEIL 55
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+#define USE_AUX_RGB_LEDS_WHILE_ON
+#define USE_INDICATOR_LED_WHILE_RAMPING
+#define RGB_LED_OFF_DEFAULT 0x18 // low, voltage
+#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow
+#define RGB_RAINBOW_SPEED 0x03 // half a second per color
+
+// enable blinking aux LEDs
+#define TICK_DURING_STANDBY
+#define STANDBY_TICK_SPEED 3 // every 0.128 s
+
+
+// brightness:
+// 0/1023: 0.35 lm
+// 1/1023: 2.56 lm
+// max regulated: 1740 lm
+// FET: ~3700 lm
+// 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
+
+#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_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
+
+#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR
+#define MUGGLE_CEILING 65
+
+// stop panicking at ~25% power or ~1000 lm
+#define THERM_FASTER_LEVEL 120
+#define MIN_THERM_STEPDOWN DEFAULT_LEVEL
+//#define THERM_NEXT_WARNING_THRESHOLD 16 // accumulate less error before adjusting
+//#define THERM_RESPONSE_MAGNITUDE 128 // bigger adjustments
+
+// easier access to thermal config mode, for Noctigon
+#define USE_TENCLICK_THERMAL_CONFIG
+
+// slow down party strobe; this driver can't pulse for 1ms or less
+//#define PARTY_STROBE_ONTIME 2
+
+#define THERM_CAL_OFFSET 5
+
diff --git a/spaghetti-monster/anduril/cfg-noctigon-k1.h b/spaghetti-monster/anduril/cfg-noctigon-k1.h
new file mode 100644
index 0000000..4f2c2cc
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-noctigon-k1.h
@@ -0,0 +1,66 @@
+// Noctigon K1 config options for Anduril
+// (originally known as Emisar D1S v2)
+#include "hwdef-Noctigon_K1.h"
+// ATTINY: 1634
+
+// this light can safely run a bit hotter than most
+#undef DEFAULT_THERM_CEIL
+#define DEFAULT_THERM_CEIL 55
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+#define USE_AUX_RGB_LEDS_WHILE_ON
+#define USE_INDICATOR_LED_WHILE_RAMPING
+#define RGB_LED_OFF_DEFAULT 0x18 // low, voltage
+#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow
+
+// enable blinking aux LEDs
+#define TICK_DURING_STANDBY
+#define STANDBY_TICK_SPEED 3 // every 0.128 s
+
+
+// ../../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
+
+// 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
+
+#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
+
+#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR
+#define MUGGLE_CEILING 70
+
+// make candle mode wobble more
+#define CANDLE_AMPLITUDE 32
+
+// stop panicking at ~70% power or ~600 lm
+#define THERM_FASTER_LEVEL 130
+
+#define THERM_RESPONSE_MAGNITUDE 32 // smaller adjustments, this host changes temperature slowly
+#define THERM_NEXT_WARNING_THRESHOLD 32 // more error tolerance before adjusting
+
+// easier access to thermal config mode, for Noctigon
+#define USE_TENCLICK_THERMAL_CONFIG
+
+// slow down party strobe; this driver can't pulse for 1ms or less
+#define PARTY_STROBE_ONTIME 2
+
+#define THERM_CAL_OFFSET 5
+
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h
new file mode 100644
index 0000000..0cfccf2
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-noctigon-kr4-219.h
@@ -0,0 +1,11 @@
+// Noctigon KR4 (75% FET) config options for Anduril
+#include "cfg-noctigon-kr4.h"
+// ATTINY: 1634
+
+// don't turn off first channel at turbo level
+#undef PWM1_LEVELS
+#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
+// 75% 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,17,39,60,82,104,126,149,172,195,219,243,268,293,318,343,369,396,422,449,476,504,531,560,588,617,646,676,706,737,768
+
diff --git a/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h b/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h
new file mode 100644
index 0000000..19cbc23
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-noctigon-kr4-nofet.h
@@ -0,0 +1,47 @@
+// Noctigon KR4 (fetless) config options for Anduril
+#include "cfg-noctigon-kr4.h"
+// ATTINY: 1634
+
+// brightness w/ SST-20 4000K LEDs:
+// 0/1023: 0.35 lm
+// 1/1023: 2.56 lm
+// max regulated: 1740 lm
+// level_calc.py 3.0 1 150 7135 0 5 1740
+#undef PWM_CHANNELS
+#define PWM_CHANNELS 1
+#define RAMP_LENGTH 150
+#undef PWM1_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
+#undef PWM2_LEVELS
+#undef DEFAULT_LEVEL
+#define DEFAULT_LEVEL 50
+#undef MAX_1x7135
+#define MAX_1x7135 150
+
+#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
+
+#undef MUGGLE_FLOOR
+#undef MUGGLE_CEILING
+#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR
+#define MUGGLE_CEILING 70
+
+// 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-kr4.h b/spaghetti-monster/anduril/cfg-noctigon-kr4.h
new file mode 100644
index 0000000..dc71655
--- /dev/null
+++ b/spaghetti-monster/anduril/cfg-noctigon-kr4.h
@@ -0,0 +1,58 @@
+// Noctigon KR4 config options for Anduril
+#include "hwdef-Noctigon_KR4.h"
+// ATTINY: 1634
+
+// this light has three aux LED channels: R, G, B
+#define USE_AUX_RGB_LEDS
+//#define USE_AUX_RGB_LEDS_WHILE_ON
+//#define USE_INDICATOR_LED_WHILE_RAMPING
+#define RGB_LED_OFF_DEFAULT 0x17 // low, rainbow
+#define RGB_LED_LOCKOUT_DEFAULT 0x37 // blinking, rainbow
+#define RGB_RAINBOW_SPEED 0x03 // half a second per color
+
+// enable blinking aux LEDs
+#define TICK_DURING_STANDBY
+#define STANDBY_TICK_SPEED 3 // every 0.128 s
+
+
+// brightness w/ SST-20 4000K LEDs:
+// 0/1023: 0.35 lm
+// 1/1023: 2.56 lm
+// max regulated: 1740 lm
+// FET: ~3700 lm
+// 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
+
+#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_DISCRETE_CEIL RAMP_SMOOTH_CEIL
+#define RAMP_DISCRETE_STEPS 7
+
+#define MUGGLE_FLOOR RAMP_DISCRETE_FLOOR
+#define MUGGLE_CEILING 65
+
+// 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
+
+// easier access to thermal config mode, for Noctigon
+#define USE_TENCLICK_THERMAL_CONFIG
+
+// slow down party strobe; this driver can't pulse for 1ms or less
+#define PARTY_STROBE_ONTIME 2
+
+#define THERM_CAL_OFFSET 5
+
+// 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-sofirn-sp36.h b/spaghetti-monster/anduril/cfg-sofirn-sp36.h
index 494a263..d808e2a 100644
--- a/spaghetti-monster/anduril/cfg-sofirn-sp36.h
+++ b/spaghetti-monster/anduril/cfg-sofirn-sp36.h
@@ -28,10 +28,3 @@
#undef THERM_FASTER_LEVEL
#endif
#define THERM_FASTER_LEVEL 130
-
-// be extra-careful at high levels
-// (or not... this host seems to heat up pretty slowly)
-//#ifndef THERM_HARD_TURBO_DROP
-//#define THERM_HARD_TURBO_DROP
-//#endif
-
diff --git a/spaghetti-monster/anduril/version.h b/spaghetti-monster/anduril/version.h
new file mode 100644
index 0000000..8cf3c90
--- /dev/null
+++ b/spaghetti-monster/anduril/version.h
@@ -0,0 +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"
diff --git a/spaghetti-monster/darkhorse/darkhorse.c b/spaghetti-monster/darkhorse/darkhorse.c
index e613f55..4058c2f 100644
--- a/spaghetti-monster/darkhorse/darkhorse.c
+++ b/spaghetti-monster/darkhorse/darkhorse.c
@@ -21,7 +21,6 @@
#define USE_LVP
#define USE_THERMAL_REGULATION
#define DEFAULT_THERM_CEIL 45
-#define USE_DELAY_4MS
#define USE_RAMPING
#define RAMP_LENGTH 150
#define USE_BATTCHECK
diff --git a/spaghetti-monster/fsm-adc.c b/spaghetti-monster/fsm-adc.c
index 6832e32..45a4297 100644
--- a/spaghetti-monster/fsm-adc.c
+++ b/spaghetti-monster/fsm-adc.c
@@ -21,37 +21,49 @@
#define FSM_ADC_C
-inline void set_admux_therm() {
- #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
+static inline void set_admux_therm() {
+ #if (ATTINY == 1634)
ADMUX = ADMUX_THERM;
- #elif (ATTINY == 841)
+ #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ ADMUX = ADMUX_THERM | (1 << ADLAR);
+ #elif (ATTINY == 841) // FIXME: not tested
ADMUXA = ADMUXA_THERM;
ADMUXB = ADMUXB_THERM;
#else
#error Unrecognized MCU type
#endif
+ adc_channel = 1;
+ adc_sample_count = 0; // first result is unstable
+ ADC_start_measurement();
}
inline void set_admux_voltage() {
- #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
- #ifdef USE_VOLTAGE_DIVIDER
- // 1.1V / pin7
- ADMUX = ADMUX_VOLTAGE_DIVIDER;
- #else
- // VCC / 1.1V reference
- ADMUX = ADMUX_VCC;
+ #if (ATTINY == 1634)
+ #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7
+ ADMUX = ADMUX_VOLTAGE_DIVIDER;
+ #else // VCC / 1.1V reference
+ ADMUX = ADMUX_VCC;
#endif
- #elif (ATTINY == 841)
- #ifdef USE_VOLTAGE_DIVIDER
- ADMUXA = ADMUXA_VOLTAGE_DIVIDER;
- ADMUXB = ADMUXB_VOLTAGE_DIVIDER;
- #else
- ADMUXA = ADMUXA_VCC;
- ADMUXB = ADMUXB_VCC;
+ #elif (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7
+ ADMUX = ADMUX_VOLTAGE_DIVIDER | (1 << ADLAR);
+ #else // VCC / 1.1V reference
+ ADMUX = ADMUX_VCC | (1 << ADLAR);
+ #endif
+ #elif (ATTINY == 841) // FIXME: not tested
+ #ifdef USE_VOLTAGE_DIVIDER // 1.1V / pin7
+ ADMUXA = ADMUXA_VOLTAGE_DIVIDER;
+ ADMUXB = ADMUXB_VOLTAGE_DIVIDER;
+ #else // VCC / 1.1V reference
+ ADMUXA = ADMUXA_VCC;
+ ADMUXB = ADMUXB_VCC;
#endif
#else
#error Unrecognized MCU type
#endif
+ adc_channel = 0;
+ adc_sample_count = 0; // first result is unstable
+ ADC_start_measurement();
}
inline void ADC_start_measurement() {
@@ -68,24 +80,25 @@ inline void ADC_on()
#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
set_admux_voltage();
#ifdef USE_VOLTAGE_DIVIDER
- // disable digital input on divider pin to reduce power consumption
- DIDR0 |= (1 << VOLTAGE_ADC_DIDR);
+ // disable digital input on divider pin to reduce power consumption
+ VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC);
#else
- // disable digital input on VCC pin to reduce power consumption
- //DIDR0 |= (1 << ADC_DIDR); // FIXME: unsure how to handle for VCC pin
+ // disable digital input on VCC pin to reduce power consumption
+ //VOLTAGE_ADC_DIDR |= (1 << VOLTAGE_ADC); // FIXME: unsure how to handle for VCC pin
#endif
#if (ATTINY == 1634)
- ACSRA |= (1 << ACD); // turn off analog comparator to save power
+ //ACSRA |= (1 << ACD); // turn off analog comparator to save power
+ ADCSRB |= (1 << ADLAR); // left-adjust flag is here instead of ADMUX
#endif
- // enable, start, prescale
- ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL;
+ // enable, start, auto-retrigger, prescale
+ ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL;
// end tiny25/45/85
- #elif (ATTINY == 841)
+ #elif (ATTINY == 841) // FIXME: not tested, missing left-adjust
ADCSRB = 0; // Right adjusted, auto trigger bits cleared.
//ADCSRA = (1 << ADEN ) | 0b011; // ADC on, prescaler division factor 8.
set_admux_voltage();
- // enable, start, prescale
- ADCSRA = (1 << ADEN) | (1 << ADSC) | ADC_PRSCL;
+ // enable, start, auto-retrigger, prescale
+ ADCSRA = (1 << ADEN) | (1 << ADSC) | (1 << ADATE) | ADC_PRSCL;
//ADCSRA |= (1 << ADSC); // start measuring
#else
#error Unrecognized MCU type
@@ -99,288 +112,320 @@ inline void ADC_off() {
#ifdef USE_VOLTAGE_DIVIDER
static inline uint8_t calc_voltage_divider(uint16_t value) {
// use 9.7 fixed-point to get sufficient precision
- uint16_t adc_per_volt = ((ADC_44<<7) - (ADC_22<<7)) / (44-22);
- // incoming value is 8.2 fixed-point, so shift it 2 bits less
- uint8_t result = ((value<<5) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR;
+ uint16_t adc_per_volt = ((ADC_44<<5) - (ADC_22<<5)) / (44-22);
+ // shift incoming value into a matching position
+ uint8_t result = ((value>>1) / adc_per_volt) + VOLTAGE_FUDGE_FACTOR;
return result;
}
#endif
-// Each full cycle runs 7.8X per second with just voltage enabled,
-// or 3.9X per second with voltage and temperature.
+// Each full cycle runs ~2X per second with just voltage enabled,
+// or ~1X per second with voltage and temperature.
#if defined(USE_LVP) && defined(USE_THERMAL_REGULATION)
-#define ADC_CYCLES_PER_SECOND 4
+#define ADC_CYCLES_PER_SECOND 1
#else
-#define ADC_CYCLES_PER_SECOND 8
+#define ADC_CYCLES_PER_SECOND 2
#endif
-// TODO: is this better done in main() or WDT()?
+
+// happens every time the ADC sampler finishes a measurement
ISR(ADC_vect) {
- // For some reason, the ADC interrupt is getting called a *lot*
- // more often than it should be, like it's auto-triggering after each
- // measurement, but I don't know why, or how to turn that off...
- // So, skip every call except when explicitly requested.
- if (! adcint_enable) return;
- adcint_enable = 0;
- static uint8_t adc_step = 0;
+ if (adc_sample_count) {
- // LVP declarations
- #ifdef USE_LVP
- #ifdef USE_LVP_AVG
- #define NUM_VOLTAGE_VALUES 4
- static int16_t voltage_values[NUM_VOLTAGE_VALUES];
- #endif
- static uint8_t lvp_timer = 0;
- static uint8_t lvp_lowpass = 0;
- #define LVP_TIMER_START (VOLTAGE_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between LVP warnings
- #define LVP_LOWPASS_STRENGTH ADC_CYCLES_PER_SECOND // lowpass for one second
- #endif
+ uint16_t m; // latest measurement
+ uint16_t s; // smoothed measurement
+ uint8_t channel = adc_channel;
- // thermal declarations
- #ifdef USE_THERMAL_REGULATION
- #ifndef THERMAL_UPDATE_SPEED
- #define THERMAL_UPDATE_SPEED 2
- #endif
- #define NUM_THERMAL_VALUES_HISTORY 8
- #define ADC_STEPS 4
- static uint8_t history_step = 0; // don't update history as often
- static int16_t temperature_history[NUM_THERMAL_VALUES_HISTORY];
- static uint8_t temperature_timer = 0;
- static uint8_t overheat_lowpass = 0;
- static uint8_t underheat_lowpass = 0;
- #define TEMPERATURE_TIMER_START ((THERMAL_WARNING_SECONDS-2)*ADC_CYCLES_PER_SECOND) // N seconds between thermal regulation events
- #define OVERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds
- #define UNDERHEAT_LOWPASS_STRENGTH (ADC_CYCLES_PER_SECOND*2) // lowpass for 2 seconds
- #else
- #define ADC_STEPS 2
- #endif
+ // update the latest value
+ m = ADC;
+ adc_raw[channel] = m;
+
+ // lowpass the value
+ //s = adc_smooth[channel]; // easier to read
+ uint16_t *v = adc_smooth + channel; // compiles smaller
+ s = *v;
+ if (m > s) { s++; }
+ if (m < s) { s--; }
+ //adc_smooth[channel] = s;
+ *v = s;
+
+ // track what woke us up, and enable deferred logic
+ irq_adc = 1;
+
+ }
+
+ // the next measurement isn't the first
+ adc_sample_count = 1;
+ // rollover doesn't really matter
+ //adc_sample_count ++;
- uint16_t measurement = ADC; // latest 10-bit ADC reading
+}
+
+void adc_deferred() {
+ irq_adc = 0; // event handled
#ifdef USE_PSEUDO_RAND
// real-world entropy makes this a true random, not pseudo
- pseudo_rand_seed += measurement;
+ // Why here instead of the ISR? Because it makes the time-critical ISR
+ // code a few cycles faster and we don't need crypto-grade randomness.
+ pseudo_rand_seed += (ADCL >> 6) + (ADCH << 2);
+ #endif
+
+ // the ADC triggers repeatedly when it's on, but we only need to run the
+ // voltage and temperature regulation stuff once in a while...so disable
+ // this after each activation, until it's manually enabled again
+ if (! adc_deferred_enable) return;
+
+ // disable after one iteration
+ adc_deferred_enable = 0;
+
+ // what is being measured? 0 = battery voltage, 1 = temperature
+ uint8_t adc_step;
+
+ #if defined(USE_LVP) && defined(USE_THERMAL_REGULATION)
+ // do whichever one is currently active
+ adc_step = adc_channel;
+ #else
+ // unless there's no temperature sensor... then just do voltage
+ adc_step = 0;
#endif
#if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP)
- // only measure battery voltage while asleep
- if (go_to_standby) adc_step = 1;
- else
+ // in sleep mode, turn off after just one measurement
+ // (having the ADC on raises standby power by about 250 uA)
+ // (and the usual standby level is only ~20 uA)
+ if (go_to_standby) {
+ ADC_off();
+ // also, only check the battery while asleep, not the temperature
+ adc_channel = 0;
+ }
#endif
- adc_step = (adc_step + 1) & (ADC_STEPS-1);
+ if (0) {} // placeholder for easier syntax
#ifdef USE_LVP
- // voltage
- if (adc_step == 1) {
- #ifdef USE_LVP_AVG
- // prime on first execution
- if (voltage == 0) {
- for(uint8_t i=0; i<NUM_VOLTAGE_VALUES; i++)
- voltage_values[i] = measurement;
- voltage = 42; // the answer to life, the universe, and the voltage of a full li-ion cell
- } else {
- uint16_t total = 0;
- uint8_t i;
- for(i=0; i<NUM_VOLTAGE_VALUES-1; i++) {
- voltage_values[i] = voltage_values[i+1];
- total += voltage_values[i];
- }
- voltage_values[i] = measurement;
- total += measurement;
- total = total >> 2;
-
- #ifdef USE_VOLTAGE_DIVIDER
- voltage = calc_voltage_divider(total);
- #else
- voltage = (uint16_t)(1.1*1024*10)/total + VOLTAGE_FUDGE_FACTOR;
- #endif
- }
- #else // no USE_LVP_AVG
- #ifdef USE_VOLTAGE_DIVIDER
- voltage = calc_voltage_divider(measurement);
- #else
- // calculate actual voltage: volts * 10
- // ADC = 1.1 * 1024 / volts
- // volts = 1.1 * 1024 / ADC
- //voltage = (uint16_t)(1.1*1024*10)/measurement + VOLTAGE_FUDGE_FACTOR;
- voltage = ((uint16_t)(2*1.1*1024*10)/measurement + VOLTAGE_FUDGE_FACTOR) >> 1;
- #endif
+ else if (0 == adc_step) { // voltage
+ ADC_voltage_handler();
+ #ifdef USE_THERMAL_REGULATION
+ // set the correct type of measurement for next time
+ if (! go_to_standby) set_admux_therm();
#endif
- // if low, callback EV_voltage_low / EV_voltage_critical
- // (but only if it has been more than N ticks since last call)
- if (lvp_timer) {
- lvp_timer --;
- } else { // it has been long enough since the last warning
- if (voltage < VOLTAGE_LOW) {
- if (lvp_lowpass < LVP_LOWPASS_STRENGTH) {
- lvp_lowpass ++;
- } else {
- // try to send out a warning
- //uint8_t err = emit(EV_voltage_low, 0);
- //uint8_t err = emit_now(EV_voltage_low, 0);
- emit(EV_voltage_low, 0);
- //if (!err) {
- // on successful warning, reset counters
- lvp_timer = LVP_TIMER_START;
- lvp_lowpass = 0;
- //}
- }
- } else {
- // voltage not low? reset count
- lvp_lowpass = 0;
- }
- }
}
- #endif // ifdef USE_LVP
-
+ #endif
#ifdef USE_THERMAL_REGULATION
- // temperature
- else if (adc_step == 3) {
- // Convert ADC units to Celsius (ish)
- int16_t temp = measurement - 275 + THERM_CAL_OFFSET + (int16_t)therm_cal_offset;
-
- // prime on first execution
- if (reset_thermal_history) {
- reset_thermal_history = 0;
- temperature = temp;
- for(uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY; i++)
- temperature_history[i] = temp;
- } else { // update our current temperature estimate
- // crude lowpass filter
- // (limit rate of change to 1 degree per measurement)
- if (temp > temperature) {
- temperature ++;
- } else if (temp < temperature) {
- temperature --;
- }
- }
+ else if (1 == adc_step) { // temperature
+ ADC_temperature_handler();
+ #ifdef USE_LVP
+ // set the correct type of measurement for next time
+ set_admux_voltage();
+ #endif
+ }
+ #endif
+}
- // guess what the temperature will be in a few seconds
- int16_t pt;
- {
- int16_t diff;
- int16_t t = temperature;
-
- // algorithm tweaking; not really intended to be modified
- // how far ahead should we predict?
- #ifndef THERM_PREDICTION_STRENGTH
- #define THERM_PREDICTION_STRENGTH 4
- #endif
- // how proportional should the adjustments be? (not used yet)
- #ifndef THERM_RESPONSE_MAGNITUDE
- #define THERM_RESPONSE_MAGNITUDE 128
- #endif
- // acceptable temperature window size in C
- #define THERM_WINDOW_SIZE 5
- // highest temperature allowed
- #define THERM_CEIL ((int16_t)therm_ceil)
- // bottom of target temperature window
- #define THERM_FLOOR (THERM_CEIL - THERM_WINDOW_SIZE)
-
- // if it's time to rotate the thermal history, do it
- history_step ++;
- #if (THERMAL_UPDATE_SPEED == 4) // new value every 4s
- #define THERM_HISTORY_STEP_MAX 15
- #elif (THERMAL_UPDATE_SPEED == 2) // new value every 2s
- #define THERM_HISTORY_STEP_MAX 7
- #elif (THERMAL_UPDATE_SPEED == 1) // new value every 1s
- #define THERM_HISTORY_STEP_MAX 3
- #elif (THERMAL_UPDATE_SPEED == 0) // new value every 0.5s
- #define THERM_HISTORY_STEP_MAX 1
- #endif
- if (0 == (history_step & THERM_HISTORY_STEP_MAX)) {
- // rotate measurements and add a new one
- for (uint8_t i=0; i<NUM_THERMAL_VALUES_HISTORY-1; i++) {
- temperature_history[i] = temperature_history[i+1];
- }
- temperature_history[NUM_THERMAL_VALUES_HISTORY-1] = t;
- }
-
- // guess what the temp will be several seconds in the future
- // diff = rate of temperature change
- //diff = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] - temperature_history[0];
- diff = t - temperature_history[0];
- // slight bias toward zero; ignore very small changes (noise)
- for (uint8_t z=0; z<3; z++) {
- if (diff < 0) diff ++;
- if (diff > 0) diff --;
- }
- // projected_temperature = current temp extended forward by amplified rate of change
- //projected_temperature = temperature_history[NUM_THERMAL_VALUES_HISTORY-1] + (diff<<THERM_PREDICTION_STRENGTH);
- pt = projected_temperature = t + (diff<<THERM_PREDICTION_STRENGTH);
- }
- // cancel counters if appropriate
- if (pt > THERM_FLOOR) {
- underheat_lowpass = 0; // we're probably not too cold
- }
- if (pt < THERM_CEIL) {
- overheat_lowpass = 0; // we're probably not too hot
- }
+#ifdef USE_LVP
+static inline void ADC_voltage_handler() {
+ // rate-limit low-voltage warnings to a max of 1 per N seconds
+ static uint8_t lvp_timer = 0;
+ #define LVP_TIMER_START (VOLTAGE_WARNING_SECONDS*ADC_CYCLES_PER_SECOND) // N seconds between LVP warnings
- if (temperature_timer) {
- temperature_timer --;
- } else { // it has been long enough since the last warning
-
- // Too hot?
- if (pt > THERM_CEIL) {
- if (overheat_lowpass < OVERHEAT_LOWPASS_STRENGTH) {
- overheat_lowpass ++;
- } else {
- // reset counters
- overheat_lowpass = 0;
- temperature_timer = TEMPERATURE_TIMER_START;
- // how far above the ceiling?
- //int16_t howmuch = (pt - THERM_CEIL) * THERM_RESPONSE_MAGNITUDE / 128;
- int16_t howmuch = pt - THERM_CEIL;
- // try to send out a warning
- emit(EV_temperature_high, howmuch);
- }
- }
-
- // Too cold?
- else if (pt < THERM_FLOOR) {
- if (underheat_lowpass < UNDERHEAT_LOWPASS_STRENGTH) {
- underheat_lowpass ++;
- } else {
- // reset counters
- underheat_lowpass = 0;
- temperature_timer = TEMPERATURE_TIMER_START;
- // how far below the floor?
- //int16_t howmuch = (THERM_FLOOR - pt) * THERM_RESPONSE_MAGNITUDE / 128;
- int16_t howmuch = THERM_FLOOR - pt;
- // try to send out a warning (unless voltage is low)
- // (LVP and underheat warnings fight each other)
- if (voltage > VOLTAGE_LOW)
- emit(EV_temperature_low, howmuch);
- }
- }
- }
- }
- #endif // ifdef USE_THERMAL_REGULATION
+ #ifdef NO_LVP_WHILE_BUTTON_PRESSED
+ // don't run if button is currently being held
+ // (because the button causes a reading of zero volts)
+ if (button_last_state) return;
+ #endif
+ uint16_t measurement;
- // set the correct type of measurement for next time
- #ifdef USE_THERMAL_REGULATION
- #ifdef USE_LVP
- if (adc_step < 2) set_admux_voltage();
- else set_admux_therm();
- #else
- set_admux_therm();
- #endif
+ // latest ADC value
+ if (go_to_standby || (adc_smooth[0] < 255)) {
+ measurement = adc_raw[0];
+ adc_smooth[0] = measurement; // no lowpass while asleep
+ }
+ else measurement = adc_smooth[0];
+
+ // values stair-step between intervals of 64, with random variations
+ // of 1 or 2 in either direction, so if we chop off the last 6 bits
+ // it'll flap between N and N-1... but if we add half an interval,
+ // the values should be really stable after right-alignment
+ // (instead of 99.98, 100.00, and 100.02, it'll hit values like
+ // 100.48, 100.50, and 100.52... which are stable when truncated)
+ //measurement += 32;
+ //measurement = (measurement + 16) >> 5;
+ measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000
+
+ #ifdef USE_VOLTAGE_DIVIDER
+ voltage = calc_voltage_divider(measurement);
#else
- #ifdef USE_LVP
- set_admux_voltage();
- #endif
+ // calculate actual voltage: volts * 10
+ // ADC = 1.1 * 1024 / volts
+ // volts = 1.1 * 1024 / ADC
+ voltage = ((uint16_t)(2*1.1*1024*10)/(measurement>>6) + VOLTAGE_FUDGE_FACTOR) >> 1;
#endif
- #ifdef TICK_DURING_STANDBY
- // if we were asleep, go back to sleep
- if (go_to_standby) ADC_off();
+ // if low, callback EV_voltage_low / EV_voltage_critical
+ // (but only if it has been more than N seconds since last call)
+ if (lvp_timer) {
+ lvp_timer --;
+ } else { // it has been long enough since the last warning
+ if (voltage < VOLTAGE_LOW) {
+ // send out a warning
+ emit(EV_voltage_low, 0);
+ // reset rate-limit counter
+ lvp_timer = LVP_TIMER_START;
+ }
+ }
+}
+#endif
+
+
+#ifdef USE_THERMAL_REGULATION
+// generally happens once per second while awake
+static inline void ADC_temperature_handler() {
+ // coarse adjustment
+ #ifndef THERM_LOOKAHEAD
+ #define THERM_LOOKAHEAD 4
+ #endif
+ // reduce frequency of minor warnings
+ #ifndef THERM_NEXT_WARNING_THRESHOLD
+ #define THERM_NEXT_WARNING_THRESHOLD 24
#endif
+ // fine-grained adjustment
+ // how proportional should the adjustments be?
+ #ifndef THERM_RESPONSE_MAGNITUDE
+ #define THERM_RESPONSE_MAGNITUDE 64
+ #endif
+ // acceptable temperature window size in C
+ #define THERM_WINDOW_SIZE 2
+
+ // TODO? make this configurable per build target?
+ // (shorter time for hosts with a lower power-to-mass ratio)
+ // (because then it'll have smaller responses)
+ #define NUM_TEMP_HISTORY_STEPS 8 // don't change; it'll break stuff
+ static uint8_t history_step = 0;
+ static uint16_t temperature_history[NUM_TEMP_HISTORY_STEPS];
+ static int8_t warning_threshold = 0;
+
+ if (reset_thermal_history) { // wipe out old data
+ // don't keep resetting
+ reset_thermal_history = 0;
+
+ // ignore average, use latest sample
+ uint16_t foo = adc_raw[1];
+ adc_smooth[1] = foo;
+
+ // forget any past measurements
+ for(uint8_t i=0; i<NUM_TEMP_HISTORY_STEPS; i++)
+ temperature_history[i] = (foo + 16) >> 5;
+ }
+
+ // latest 16-bit ADC reading
+ uint16_t measurement = adc_smooth[1];
+
+ // values stair-step between intervals of 64, with random variations
+ // of 1 or 2 in either direction, so if we chop off the last 6 bits
+ // it'll flap between N and N-1... but if we add half an interval,
+ // the values should be really stable after right-alignment
+ // (instead of 99.98, 100.00, and 100.02, it'll hit values like
+ // 100.48, 100.50, and 100.52... which are stable when truncated)
+ //measurement += 32;
+ measurement = (measurement + 16) >> 5;
+ //measurement = (measurement + 16) & 0xffe0; // 1111 1111 1110 0000
+
+ // let the UI see the current temperature in C
+ // Convert ADC units to Celsius (ish)
+ temperature = (measurement>>1) + THERM_CAL_OFFSET + (int16_t)therm_cal_offset - 275;
+
+ // how much has the temperature changed between now and a few seconds ago?
+ int16_t diff;
+ diff = measurement - temperature_history[history_step];
+
+ // update / rotate the temperature history
+ temperature_history[history_step] = measurement;
+ history_step = (history_step + 1) & (NUM_TEMP_HISTORY_STEPS-1);
+
+ // PI[D]: guess what the temperature will be in a few seconds
+ uint16_t pt; // predicted temperature
+ 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;
+ // ... so ...
+ // (C + 275 - THERM_CAL_OFFSET - therm_cal_offset) << 6 = ADC;
+ uint16_t ceil = (therm_ceil + 275 - therm_cal_offset - THERM_CAL_OFFSET) << 1;
+ int16_t offset = pt - ceil;
+
+ // bias small errors toward zero, while leaving large errors mostly unaffected
+ // (a diff of 1 C is 2 ADC units, * 4 for therm lookahead, so it becomes 8)
+ // (but a diff of 1 C should only send a warning of magnitude 1)
+ // (this also makes it only respond to small errors at the time the error
+ // happened, not after the temperature has stabilized)
+ for(uint8_t foo=0; foo<3; foo++) {
+ if (offset > 0) {
+ offset --;
+ } else if (offset < 0) {
+ offset ++;
+ }
+ }
+
+ // Too hot?
+ // (if it's too hot and not getting cooler...)
+ if ((offset > 0) && (diff > -1)) {
+ // accumulated error isn't big enough yet to send a warning
+ if (warning_threshold > 0) {
+ warning_threshold -= offset;
+ } else { // error is big enough; send a warning
+ // how far above the ceiling?
+ // original method works, but is too slow on some small hosts:
+ // (and typically has a minimum response magnitude of 2 instead of 1)
+ // int16_t howmuch = offset;
+ // ... so increase the amount, except for small values
+ // (for example, 1:1, 2:1, 3:3, 4:5, 6:9, 8:13, 10:17, 40:77)
+ // ... and let us tune the response per build target if desired
+ int16_t howmuch = (offset + offset - 3) * THERM_RESPONSE_MAGNITUDE / 128;
+ if (howmuch < 1) howmuch = 1;
+ warning_threshold = THERM_NEXT_WARNING_THRESHOLD - (uint8_t)howmuch;
+
+ // send a warning
+ emit(EV_temperature_high, howmuch);
+ }
+ }
+
+ // Too cold?
+ // (if it's too cold and still getting colder...)
+ // the temperature is this far below the floor:
+ #define BELOW (offset + (THERM_WINDOW_SIZE<<1))
+ else if ((BELOW < 0) && (diff < 0)) {
+ // accumulated error isn't big enough yet to send a warning
+ if (warning_threshold < 0) {
+ warning_threshold -= BELOW;
+ } else { // error is big enough; send a warning
+ warning_threshold = (-THERM_NEXT_WARNING_THRESHOLD) - BELOW;
+
+ // how far below the floor?
+ // int16_t howmuch = ((-BELOW) >> 1) * THERM_RESPONSE_MAGNITUDE / 128;
+ int16_t howmuch = (-BELOW) >> 1;
+ // send a notification (unless voltage is low)
+ // (LVP and underheat warnings fight each other)
+ if (voltage > (VOLTAGE_LOW + 1))
+ emit(EV_temperature_low, howmuch);
+ }
+ }
+ #undef BELOW
+
+ // Goldilocks?
+ // (temperature is within target window, or at least heading toward it)
+ else {
+ // send a notification (unless voltage is low)
+ // (LVP and temp-okay events fight each other)
+ if (voltage > VOLTAGE_LOW)
+ emit(EV_temperature_okay, 0);
+ }
}
+#endif
+
#ifdef USE_BATTCHECK
#ifdef BATTCHECK_4bars
diff --git a/spaghetti-monster/fsm-adc.h b/spaghetti-monster/fsm-adc.h
index 274fb4d..241dee4 100644
--- a/spaghetti-monster/fsm-adc.h
+++ b/spaghetti-monster/fsm-adc.h
@@ -38,9 +38,24 @@
#define VOLTAGE_FUDGE_FACTOR 5
#endif
#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
+uint16_t adc_raw[2]; // last ADC measurements (0=voltage, 1=temperature)
+uint16_t adc_smooth[2]; // lowpassed ADC measurements (0=voltage, 1=temperature)
+// ADC code is split into two parts:
+// - ISR: runs immediately at each interrupt, does the bare minimum because time is critical here
+// - deferred: the bulk of the logic runs later when time isn't so critical
+uint8_t adc_deferred_enable = 0; // stop waiting and run the deferred code
+void adc_deferred(); // do the actual ADC-related calculations
+
+static inline void ADC_voltage_handler();
volatile uint8_t voltage = 0;
-volatile uint8_t adcint_enable; // kludge, because adc auto-retrigger won't turn off
+#ifdef USE_LVP
void low_voltage();
+#endif
+
#ifdef USE_BATTCHECK
void battcheck();
#ifdef BATTCHECK_VpT
@@ -50,14 +65,10 @@ void battcheck();
#define USE_BLINK_DIGIT
#endif
#endif
-#endif
+#endif // ifdef USE_LVP
#ifdef USE_THERMAL_REGULATION
-// default 5 seconds between thermal regulation events
-#ifndef THERMAL_WARNING_SECONDS
-#define THERMAL_WARNING_SECONDS 5
-#endif
// try to keep temperature below 45 C
#ifndef DEFAULT_THERM_CEIL
#define DEFAULT_THERM_CEIL 45
@@ -70,16 +81,13 @@ void battcheck();
#ifndef THERM_CAL_OFFSET
#define THERM_CAL_OFFSET 0
#endif
-// temperature now, in C (ish) * 2 (14.1 fixed-point)
+// temperature now, in C (ish)
volatile int16_t temperature;
-// temperature in a few seconds, in C (ish) * 2 (14.1 fixed-point)
-volatile int16_t projected_temperature; // Fight the future!
uint8_t therm_ceil = DEFAULT_THERM_CEIL;
int8_t therm_cal_offset = 0;
-//void low_temperature();
-//void high_temperature();
volatile uint8_t reset_thermal_history = 1;
-#endif
+static inline void ADC_temperature_handler();
+#endif // ifdef USE_THERMAL_REGULATION
inline void ADC_on();
diff --git a/spaghetti-monster/fsm-eeprom.c b/spaghetti-monster/fsm-eeprom.c
index 277e2b2..0de7e05 100644
--- a/spaghetti-monster/fsm-eeprom.c
+++ b/spaghetti-monster/fsm-eeprom.c
@@ -30,6 +30,10 @@ uint8_t eeprom[EEPROM_BYTES];
#endif
uint8_t load_eeprom() {
+ #ifdef LED_ENABLE_PIN
+ delay_4ms(2); // wait for power to stabilize
+ #endif
+
cli();
// check if eeprom has been initialized; abort if it hasn't
uint8_t marker = eeprom_read_byte((uint8_t *)EEP_START);
@@ -44,6 +48,10 @@ uint8_t load_eeprom() {
}
void save_eeprom() {
+ #ifdef LED_ENABLE_PIN
+ delay_4ms(2); // wait for power to stabilize
+ #endif
+
cli();
// save the actual data
@@ -59,17 +67,21 @@ void save_eeprom() {
#ifdef USE_EEPROM_WL
uint8_t eeprom_wl[EEPROM_WL_BYTES];
-EEP_OFFSET_T eep_wl_prev_offset;
+uint8_t * eep_wl_prev_offset;
uint8_t load_eeprom_wl() {
+ #ifdef LED_ENABLE_PIN
+ delay_4ms(2); // wait for power to stabilize
+ #endif
+
cli();
// check if eeprom has been initialized; abort if it hasn't
uint8_t found = 0;
- EEP_OFFSET_T offset;
+ uint8_t * offset;
for(offset = 0;
- offset < EEP_WL_SIZE - EEPROM_WL_BYTES - 1;
+ offset < (uint8_t *)(EEP_WL_SIZE - EEPROM_WL_BYTES - 1);
offset += (EEPROM_WL_BYTES + 1)) {
- if (eeprom_read_byte((uint8_t *)offset) == EEP_MARKER) {
+ if (eeprom_read_byte(offset) == EEP_MARKER) {
found = 1;
eep_wl_prev_offset = offset;
break;
@@ -79,7 +91,7 @@ uint8_t load_eeprom_wl() {
if (found) {
// load the actual data
for(uint8_t i=0; i<EEPROM_WL_BYTES; i++) {
- eeprom_wl[i] = eeprom_read_byte((uint8_t *)(offset+1+i));
+ eeprom_wl[i] = eeprom_read_byte(offset+1+i);
}
}
sei();
@@ -87,24 +99,28 @@ uint8_t load_eeprom_wl() {
}
void save_eeprom_wl() {
+ #ifdef LED_ENABLE_PIN
+ delay_4ms(2); // wait for power to stabilize
+ #endif
+
cli();
// erase old state
- EEP_OFFSET_T offset = eep_wl_prev_offset;
+ uint8_t * offset = eep_wl_prev_offset;
for (uint8_t i = 0; i < EEPROM_WL_BYTES+1; i ++) {
- eeprom_update_byte((uint8_t *)offset+i, 0xFF);
+ eeprom_update_byte(offset+i, 0xFF);
}
// save new state
offset += EEPROM_WL_BYTES+1;
- if (offset > EEP_WL_SIZE-EEPROM_WL_BYTES-1) offset = 0;
+ if (offset > (uint8_t *)(EEP_WL_SIZE-EEPROM_WL_BYTES-1)) offset = 0;
eep_wl_prev_offset = offset;
// marker byte
// FIXME: write the marker last, to signal completed transaction
- eeprom_update_byte((uint8_t *)offset, EEP_MARKER);
+ eeprom_update_byte(offset, EEP_MARKER);
offset ++;
// user data
for(uint8_t i=0; i<EEPROM_WL_BYTES; i++, offset++) {
- eeprom_update_byte((uint8_t *)(offset), eeprom_wl[i]);
+ eeprom_update_byte(offset, eeprom_wl[i]);
}
sei();
}
diff --git a/spaghetti-monster/fsm-events.c b/spaghetti-monster/fsm-events.c
index 362a5cc..ad869a6 100644
--- a/spaghetti-monster/fsm-events.c
+++ b/spaghetti-monster/fsm-events.c
@@ -20,6 +20,8 @@
#ifndef FSM_EVENTS_C
#define FSM_EVENTS_C
+#include <util/delay_basic.h>
+
void empty_event_sequence() {
current_event = EV_none;
@@ -42,7 +44,7 @@ uint8_t push_event(uint8_t ev_type) {
// set press flag
current_event |= B_PRESS;
// increase click counter
- if ((current_event & B_COUNT) < (B_COUNT-1)) {
+ if ((current_event & B_COUNT) < (B_COUNT)) {
current_event ++;
}
return 1; // event pushed, even if max clicks already reached
@@ -108,7 +110,7 @@ uint8_t nice_delay_ms(uint16_t ms) {
/* // delay_zero() implementation
if (ms == 0) {
CLKPR = 1<<CLKPCE; CLKPR = 0; // full speed
- _delay_loop_2(BOGOMIPS*98/100/3);
+ _delay_loop_2(BOGOMIPS*95/100/3);
return 1;
}
*/
@@ -118,15 +120,15 @@ 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*98/100/4);
+ _delay_loop_2(BOGOMIPS*90/100/4);
}
//else if (level < HALFSPEED_LEVEL) {
// clock_prescale_set(clock_div_2);
- // _delay_loop_2(BOGOMIPS*98/100/2);
+ // _delay_loop_2(BOGOMIPS*95/100/2);
//}
else {
clock_prescale_set(clock_div_1);
- _delay_loop_2(BOGOMIPS*98/100);
+ _delay_loop_2(BOGOMIPS*90/100);
}
// restore regular clock speed
clock_prescale_set(clock_div_1);
@@ -134,15 +136,18 @@ uint8_t nice_delay_ms(uint16_t ms) {
// underclock MCU to save power
clock_prescale_set(clock_div_4);
// wait
- _delay_loop_2(BOGOMIPS*98/100/4);
+ _delay_loop_2(BOGOMIPS*90/100/4);
// restore regular clock speed
clock_prescale_set(clock_div_1);
#endif // ifdef USE_RAMPING
#else
// wait
- _delay_loop_2(BOGOMIPS*98/100);
+ _delay_loop_2(BOGOMIPS*90/100);
#endif // ifdef USE_DYNAMIC_UNDERCLOCKING
+ // run pending system processes while we wait
+ handle_deferred_interrupts();
+
if ((nice_delay_interrupt) || (old_state != current_state)) {
return 0; // state changed; abort
}
diff --git a/spaghetti-monster/fsm-events.h b/spaghetti-monster/fsm-events.h
index 39ad3aa..a132d5a 100644
--- a/spaghetti-monster/fsm-events.h
+++ b/spaghetti-monster/fsm-events.h
@@ -85,6 +85,7 @@ static volatile uint16_t ticks_since_last_event = 0;
#ifdef USE_THERMAL_REGULATION
#define EV_temperature_high (B_SYSTEM|0b00000101)
#define EV_temperature_low (B_SYSTEM|0b00000110)
+#define EV_temperature_okay (B_SYSTEM|0b00000111)
#endif
// Button press events
@@ -217,8 +218,8 @@ void emit(Event event, uint16_t arg);
void emit_current_event(uint16_t arg);
uint8_t nice_delay_ms(uint16_t ms);
-//uint8_t nice_delay_4ms(uint8_t ms);
//uint8_t nice_delay_s();
inline void interrupt_nice_delays();
+void delay_4ms(uint8_t ms);
#endif
diff --git a/spaghetti-monster/fsm-main.c b/spaghetti-monster/fsm-main.c
index e537a9e..790cc68 100644
--- a/spaghetti-monster/fsm-main.c
+++ b/spaghetti-monster/fsm-main.c
@@ -37,7 +37,7 @@ ISR(TIMER1_COMPA_vect) {
#endif
#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
-inline void hw_setup() {
+static inline void hw_setup() {
// configure PWM channels
#if PWM_CHANNELS >= 1
DDRB |= (1 << PWM1_PIN);
@@ -69,7 +69,7 @@ inline void hw_setup() {
PCMSK = (1 << SWITCH_PIN); // pin change interrupt uses this pin
}
#elif (ATTINY == 1634)
-inline void hw_setup() {
+static inline void hw_setup() {
// this gets tricky with so many pins...
// ... so punt it to the hwdef file
hwdef_setup();
@@ -79,22 +79,24 @@ inline void hw_setup() {
#endif
-#ifdef USE_REBOOT
-void prevent_reboot_loop() {
+//#ifdef USE_REBOOT
+static inline void prevent_reboot_loop() {
// prevent WDT from rebooting MCU again
MCUSR &= ~(1<<WDRF); // reset status flag
wdt_disable();
}
-#endif
+//#endif
int main() {
// Don't allow interrupts while booting
cli();
- #ifdef USE_REBOOT
+ //#ifdef USE_REBOOT
+ // prevents cycling after a crash,
+ // whether intentional (like factory reset) or not (bugs)
prevent_reboot_loop();
- #endif
+ //#endif
hw_setup();
@@ -123,7 +125,6 @@ int main() {
#else
delay_4ms(1);
#endif
- empty_event_sequence();
// fallback for handling a few things
#ifndef DONT_USE_DEFAULT_STATE
@@ -160,6 +161,9 @@ int main() {
standby_mode();
}
+ // catch up on interrupts
+ handle_deferred_interrupts();
+
// give the recipe some time slices
loop();
@@ -168,4 +172,22 @@ int main() {
}
}
+
+void handle_deferred_interrupts() {
+ /*
+ if (irq_pcint) { // button pressed or released
+ // nothing to do here
+ // (PCINT only matters during standby)
+ }
+ */
+ if (irq_adc) { // ADC done measuring
+ adc_deferred();
+ // irq_adc = 0; // takes care of itself
+ }
+ if (irq_wdt) { // the clock ticked
+ WDT_inner();
+ // irq_wdt = 0; // takes care of itself
+ }
+}
+
#endif
diff --git a/spaghetti-monster/fsm-main.h b/spaghetti-monster/fsm-main.h
index cc469d7..55ae2ff 100644
--- a/spaghetti-monster/fsm-main.h
+++ b/spaghetti-monster/fsm-main.h
@@ -21,5 +21,7 @@
#define FSM_MAIN_H
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 8e88cbd..82be745 100644
--- a/spaghetti-monster/fsm-misc.c
+++ b/spaghetti-monster/fsm-misc.c
@@ -41,23 +41,46 @@ void auto_clock_speed() {
#endif
#if defined(USE_BLINK_NUM) || defined(USE_BLINK_DIGIT)
+#define BLINK_SPEED 1000
uint8_t blink_digit(uint8_t num) {
//StatePtr old_state = current_state;
// "zero" digit gets a single short blink
- uint8_t ontime = 200;
+ uint8_t ontime = BLINK_SPEED * 2 / 12;
if (!num) { ontime = 8; num ++; }
for (; num>0; num--) {
set_level(BLINK_BRIGHTNESS);
nice_delay_ms(ontime);
set_level(0);
- nice_delay_ms(400);
+ nice_delay_ms(BLINK_SPEED * 3 / 12);
}
- return nice_delay_ms(600);
+ return nice_delay_ms(BLINK_SPEED * 8 / 12);
}
#endif
+#ifdef USE_BLINK_BIG_NUM
+uint8_t blink_big_num(uint16_t num) {
+ uint16_t digits[] = { 10000, 1000, 100, 10, 1 };
+ uint8_t started = 0;
+ for (uint8_t digit=0; digit<sizeof(digits)/sizeof(uint16_t); digit++) {
+ uint16_t scale = digits[digit];
+ if (num >= scale) {
+ started = 1;
+ }
+ if (started) {
+ uint8_t digit = 0;
+ while (num >= scale) {
+ num -= scale;
+ digit ++;
+ }
+ if (! blink_digit(digit)) return 0;
+ }
+ }
+
+ return nice_delay_ms(1000);
+}
+#endif
#ifdef USE_BLINK_NUM
uint8_t blink_num(uint8_t num) {
//StatePtr old_state = current_state;
@@ -147,6 +170,29 @@ void indicator_led_auto() {
*/
#endif // USE_INDICATOR_LED
+#ifdef USE_BUTTON_LED
+// TODO: Refactor this and RGB LED function to merge code and save space
+void button_led_set(uint8_t lvl) {
+ switch (lvl) {
+ case 0: // LED off
+ BUTTON_LED_DDR &= 0xff ^ (1 << BUTTON_LED_PIN);
+ BUTTON_LED_PUE &= 0xff ^ (1 << BUTTON_LED_PIN);
+ BUTTON_LED_PORT &= 0xff ^ (1 << BUTTON_LED_PIN);
+ break;
+ case 1: // LED low
+ BUTTON_LED_DDR &= 0xff ^ (1 << BUTTON_LED_PIN);
+ BUTTON_LED_PUE |= (1 << BUTTON_LED_PIN);
+ BUTTON_LED_PORT |= (1 << BUTTON_LED_PIN);
+ break;
+ default: // LED high
+ BUTTON_LED_DDR |= (1 << BUTTON_LED_PIN);
+ BUTTON_LED_PUE |= (1 << BUTTON_LED_PIN);
+ BUTTON_LED_PORT |= (1 << BUTTON_LED_PIN);
+ break;
+ }
+}
+#endif
+
#ifdef USE_AUX_RGB_LEDS
void rgb_led_set(uint8_t value) {
// value: 0b00BBGGRR
@@ -185,18 +231,20 @@ uint8_t triangle_wave(uint8_t phase) {
#ifdef USE_REBOOT
void reboot() {
- #if 1 // WDT method, safer but larger
+ // put the WDT in hard reset mode, then trigger it
cli();
- WDTCR = 0xD8 | WDTO_15MS;
+ #if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85)
+ WDTCR = 0xD8 | WDTO_15MS;
+ #elif (ATTINY == 1634)
+ // allow protected configuration changes for next 4 clock cycles
+ CCP = 0xD8; // magic number
+ // reset (WDIF + WDE), no WDIE, fastest (16ms) timing (0000)
+ // (DS section 8.5.2 and table 8-4)
+ WDTCSR = 0b10001000;
+ #endif
sei();
wdt_reset();
while (1) {}
- #else // raw assembly method, doesn't reset registers or anything
- __asm__ __volatile__ (
- "cli" "\n\t"
- "rjmp 0x00" "\n\t"
- );
- #endif
}
#endif
diff --git a/spaghetti-monster/fsm-misc.h b/spaghetti-monster/fsm-misc.h
index a39d31a..66d31ba 100644
--- a/spaghetti-monster/fsm-misc.h
+++ b/spaghetti-monster/fsm-misc.h
@@ -43,10 +43,16 @@ uint8_t blink(uint8_t num, uint8_t speed);
*/
#ifdef USE_INDICATOR_LED
+// FIXME: Remove this, replace with button_led()
// lvl: 0=off, 1=low, 2=high
void indicator_led(uint8_t lvl);
#endif
+#ifdef USE_BUTTON_LED
+// lvl: 0=off, 1=low, 2=high
+void button_led_set(uint8_t lvl);
+#endif
+
#ifdef USE_AUX_RGB_LEDS
// value: 0b00BBGGRR
// each pair of bits: 0=off, 1=low, 2=high
diff --git a/spaghetti-monster/fsm-pcint.c b/spaghetti-monster/fsm-pcint.c
index 4928980..d362633 100644
--- a/spaghetti-monster/fsm-pcint.c
+++ b/spaghetti-monster/fsm-pcint.c
@@ -24,19 +24,9 @@
#include <util/delay_basic.h>
uint8_t button_is_pressed() {
- // remember the past 32 measurements
- static uint32_t readings = 0;
- // take at least one new measurement,
- // and wait for measurements to settle to all zeroes or all ones
- do {
- // shift past readings and add current value
- readings = (readings << 1) | ((SWITCH_PORT & (1<<SWITCH_PIN)) == 0);
- // wait a moment
- _delay_loop_2(BOGOMIPS/16); // up to 2ms to stabilize
- }
- while ((readings != 0) && (readings != 0xFFFFFFFF));
- button_last_state = readings;
- return readings;
+ uint8_t value = ((SWITCH_PORT & (1<<SWITCH_PIN)) == 0);
+ button_last_state = value;
+ return value;
}
inline void PCINT_on() {
@@ -75,7 +65,14 @@ inline void PCINT_off() {
//void button_change_interrupt() {
#if (ATTINY == 25) || (ATTINY == 45) || (ATTINY == 85) || (ATTINY == 1634)
-EMPTY_INTERRUPT(PCINT0_vect);
+//EMPTY_INTERRUPT(PCINT0_vect);
+#ifdef PCINT_vect
+ISR(PCINT_vect) {
+#else
+ISR(PCINT0_vect) {
+#endif
+ irq_pcint = 1;
+}
#else
#error Unrecognized MCU type
#endif
@@ -104,8 +101,7 @@ void PCINT_inner(uint8_t pressed) {
pushed = push_event(B_RELEASE);
}
- // check if sequence matches any defined sequences
- // if so, send event to current state callback
+ // send event to the current state callback
if (pushed) {
button_last_state = pressed;
emit_current_event(0);
diff --git a/spaghetti-monster/fsm-pcint.h b/spaghetti-monster/fsm-pcint.h
index ec3ae4b..a7f3733 100644
--- a/spaghetti-monster/fsm-pcint.h
+++ b/spaghetti-monster/fsm-pcint.h
@@ -20,6 +20,7 @@
#ifndef FSM_PCINT_H
#define FSM_PCINT_H
+volatile uint8_t irq_pcint = 0; // pin change interrupt happened?
//static volatile uint8_t button_was_pressed;
#define BP_SAMPLES 32
volatile uint8_t button_last_state;
diff --git a/spaghetti-monster/fsm-ramping.c b/spaghetti-monster/fsm-ramping.c
index efa07e4..bae601e 100644
--- a/spaghetti-monster/fsm-ramping.c
+++ b/spaghetti-monster/fsm-ramping.c
@@ -46,6 +46,9 @@ void set_level(uint8_t level) {
#endif
#ifdef USE_AUX_RGB_LEDS
rgb_led_set(0);
+ #ifdef USE_BUTTON_LED
+ button_led_set((level > 0) + (level > MAX_1x7135));
+ #endif
#endif
}
#endif
@@ -65,13 +68,31 @@ void set_level(uint8_t level) {
#if PWM_CHANNELS >= 4
PWM4_LVL = 0;
#endif
+ // disable the power channel, if relevant
+ #ifdef LED_ENABLE_PIN
+ LED_ENABLE_PORT &= ~(1 << LED_ENABLE_PIN);
+ #endif
+ #ifdef LED_ENABLE2_PIN
+ LED_ENABLE2_PORT &= ~(1 << LED_ENABLE2_PIN);
+ #endif
} else {
level --;
+ // enable the power channel, if relevant
+ #ifdef LED_ENABLE_PIN
+ LED_ENABLE_PORT |= (1 << LED_ENABLE_PIN);
+ #endif
+ #ifdef LED_ENABLE2_PIN
+ LED_ENABLE2_PORT |= (1 << LED_ENABLE2_PIN);
+ #endif
+
#ifdef USE_TINT_RAMPING
+ #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
- uint8_t brightness = pgm_read_byte(pwm1_levels + level);
+ uint8_t brightness = PWM_GET(pwm1_levels, level);
uint8_t warm_PWM, cool_PWM;
// auto-tint modes
@@ -93,7 +114,7 @@ void set_level(uint8_t level) {
// correction is only necessary when PWM is fast
if (level > HALFSPEED_LEVEL) {
base_PWM = brightness
- + ((((uint16_t)brightness) * 26 / 64) * triangle_wave(mytint) / 255);
+ + ((((uint16_t)brightness) * TINT_RAMPING_CORRECTION / 64) * triangle_wave(mytint) / 255);
}
cool_PWM = (((uint16_t)mytint * (uint16_t)base_PWM) + 127) / 255;
@@ -104,16 +125,16 @@ void set_level(uint8_t level) {
#else
#if PWM_CHANNELS >= 1
- PWM1_LVL = pgm_read_byte(pwm1_levels + level);
+ PWM1_LVL = PWM_GET(pwm1_levels, level);
#endif
#if PWM_CHANNELS >= 2
- PWM2_LVL = pgm_read_byte(pwm2_levels + level);
+ PWM2_LVL = PWM_GET(pwm2_levels, level);
#endif
#if PWM_CHANNELS >= 3
- PWM3_LVL = pgm_read_byte(pwm3_levels + level);
+ PWM3_LVL = PWM_GET(pwm3_levels, level);
#endif
#if PWM_CHANNELS >= 4
- PWM4_LVL = pgm_read_byte(pwm4_levels + level);
+ PWM4_LVL = PWM_GET(pwm4_levels, level);
#endif
#endif // ifdef USE_TINT_RAMPING
@@ -137,43 +158,43 @@ void gradual_tick() {
gt --; // convert 1-based number to 0-based
- uint8_t target;
+ PWM_DATATYPE target;
#if PWM_CHANNELS >= 1
- target = pgm_read_byte(pwm1_levels + gt);
+ target = PWM_GET(pwm1_levels, gt);
if ((gt < actual_level) // special case for FET-only turbo
&& (PWM1_LVL == 0) // (bypass adjustment period for first step)
- && (target == 255)) PWM1_LVL = 255;
+ && (target == PWM_TOP)) PWM1_LVL = PWM_TOP;
else if (PWM1_LVL < target) PWM1_LVL ++;
else if (PWM1_LVL > target) PWM1_LVL --;
#endif
#if PWM_CHANNELS >= 2
- target = pgm_read_byte(pwm2_levels + gt);
+ target = PWM_GET(pwm2_levels, gt);
if (PWM2_LVL < target) PWM2_LVL ++;
else if (PWM2_LVL > target) PWM2_LVL --;
#endif
#if PWM_CHANNELS >= 3
- target = pgm_read_byte(pwm3_levels + gt);
+ 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 = pgm_read_byte(pwm4_levels + gt);
+ 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 == pgm_read_byte(pwm1_levels + gt))
+ if ((PWM1_LVL == PWM_GET(pwm1_levels, gt))
#if PWM_CHANNELS >= 2
- && (PWM2_LVL == pgm_read_byte(pwm2_levels + gt))
+ && (PWM2_LVL == PWM_GET(pwm2_levels, gt))
#endif
#if PWM_CHANNELS >= 3
- && (PWM3_LVL == pgm_read_byte(pwm3_levels + gt))
+ && (PWM3_LVL == PWM_GET(pwm3_levels, gt))
#endif
#if PWM_CHANNELS >= 4
- && (PWM4_LVL == pgm_read_byte(pwm4_levels + gt))
+ && (PWM4_LVL == PWM_GET(pwm4_levels, gt))
#endif
)
{
diff --git a/spaghetti-monster/fsm-ramping.h b/spaghetti-monster/fsm-ramping.h
index dcc3b74..c650e21 100644
--- a/spaghetti-monster/fsm-ramping.h
+++ b/spaghetti-monster/fsm-ramping.h
@@ -38,18 +38,36 @@ inline void set_level_gradually(uint8_t lvl);
void gradual_tick();
#endif
+// auto-detect the data type for PWM tables
+#ifndef PWM_BITS
+#define PWM_BITS 8
+#define PWM_TOP 255
+#endif
+#if PWM_BITS <= 8
+#define PWM_DATATYPE uint8_t
+#define PWM_TOP 255
+#define PWM_GET(x,y) pgm_read_byte(x+y)
+#else
+#define PWM_DATATYPE uint16_t
+#define PWM_TOP 1023 // 10 bits by default
+// 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
+#define PWM_GET(x,y) pgm_read_word(x+y)
+#endif
+
// use UI-defined ramp tables if they exist
#ifdef PWM1_LEVELS
-PROGMEM const uint8_t pwm1_levels[] = { PWM1_LEVELS };
+PROGMEM const PWM_DATATYPE pwm1_levels[] = { PWM1_LEVELS };
#endif
#ifdef PWM2_LEVELS
-PROGMEM const uint8_t pwm2_levels[] = { PWM2_LEVELS };
+PROGMEM const PWM_DATATYPE pwm2_levels[] = { PWM2_LEVELS };
#endif
#ifdef PWM3_LEVELS
-PROGMEM const uint8_t pwm3_levels[] = { PWM3_LEVELS };
+PROGMEM const PWM_DATATYPE pwm3_levels[] = { PWM3_LEVELS };
#endif
#ifdef PWM4_LEVELS
-PROGMEM const uint8_t pwm4_levels[] = { PWM4_LEVELS };
+PROGMEM const PWM_DATATYPE pwm4_levels[] = { PWM4_LEVELS };
#endif
// default / example ramps
@@ -57,31 +75,31 @@ PROGMEM const uint8_t pwm4_levels[] = { PWM4_LEVELS };
#if PWM_CHANNELS == 1
#if RAMP_LENGTH == 50
// ../../bin/level_calc.py 1 50 7135 3 0.25 980
- PROGMEM const uint8_t 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 };
+ 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 uint8_t 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 };
+ 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 uint8_t 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 };
+ 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 uint8_t 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 uint8_t 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 };
+ 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 uint8_t 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 uint8_t 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 };
+ 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 uint8_t 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 uint8_t 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 };
+ 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
@@ -89,25 +107,25 @@ PROGMEM const uint8_t pwm4_levels[] = { PWM4_LEVELS };
#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 uint8_t 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 uint8_t 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 uint8_t 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 };
+ 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 uint8_t 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 uint8_t 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 uint8_t 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 };
+ 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 uint8_t 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 uint8_t 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 uint8_t 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 };
+ 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
@@ -119,7 +137,7 @@ PROGMEM const uint8_t pwm4_levels[] = { PWM4_LEVELS };
#endif
// RAMP_SIZE / MAX_LVL
-#define RAMP_SIZE sizeof(pwm1_levels)
+#define RAMP_SIZE (sizeof(pwm1_levels)/sizeof(PWM_DATATYPE))
#define MAX_LEVEL RAMP_SIZE
void set_level(uint8_t level);
diff --git a/spaghetti-monster/fsm-standby.c b/spaghetti-monster/fsm-standby.c
index 44b047a..b002b91 100644
--- a/spaghetti-monster/fsm-standby.c
+++ b/spaghetti-monster/fsm-standby.c
@@ -42,16 +42,19 @@ void sleep_until_eswitch_pressed()
// make sure switch isn't currently pressed
while (button_is_pressed()) {}
empty_event_sequence(); // cancel pending input on suspend
- //PCINT_since_WDT = 0; // ensure PCINT won't ignore itself
PCINT_on(); // wake on e-switch event
#ifdef TICK_DURING_STANDBY
+ // detect which type of event caused a wake-up
+ irq_adc = 0;
+ irq_wdt = 0;
+ irq_pcint = 0;
while (go_to_standby) {
- f_wdt = 0; // detect if WDT was what caused a wake-up
#else
go_to_standby = 0;
#endif
+
// configure sleep mode
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
@@ -65,10 +68,19 @@ void sleep_until_eswitch_pressed()
sleep_disable();
#ifdef TICK_DURING_STANDBY
- // determine what woke us up... WDT or PCINT
- if (! f_wdt) { // PCINT went off; wake up
+ // determine what woke us up...
+ if (irq_pcint) { // button pressed; wake up
go_to_standby = 0;
}
+ if (irq_adc) { // ADC done measuring
+ adc_deferred_enable = 1;
+ adc_deferred();
+ //ADC_off(); // takes care of itself
+ //irq_adc = 0; // takes care of itself
+ }
+ if (irq_wdt) { // generate a sleep tick
+ WDT_inner();
+ }
}
#endif
@@ -78,7 +90,10 @@ void sleep_until_eswitch_pressed()
#endif
// go back to normal running mode
- //PCINT_on(); // should be on already
+ // PCINT not needed any more, and can cause problems if on
+ // (occasional reboots on wakeup-by-button-press)
+ PCINT_off();
+ // restore normal awake-mode interrupts
ADC_on();
WDT_on();
}
diff --git a/spaghetti-monster/fsm-wdt.c b/spaghetti-monster/fsm-wdt.c
index 6e61e87..94266c1 100644
--- a/spaghetti-monster/fsm-wdt.c
+++ b/spaghetti-monster/fsm-wdt.c
@@ -82,46 +82,52 @@ inline void WDT_off()
// clock tick -- this runs every 16ms (62.5 fps)
ISR(WDT_vect) {
+ irq_wdt = 1; // WDT event happened
+}
+
+void WDT_inner() {
+ irq_wdt = 0; // WDT event handled; reset flag
+
static uint8_t adc_trigger = 0;
- #ifdef TICK_DURING_STANDBY
- f_wdt = 1; // WDT event happened
+ // cache this here to reduce ROM size, because it's volatile
+ uint16_t ticks_since_last = ticks_since_last_event;
+ // increment, but loop from max back to half
+ ticks_since_last = (ticks_since_last + 1) \
+ | (ticks_since_last & 0x8000);
+ // copy back to the original
+ ticks_since_last_event = ticks_since_last;
+
+ // detect and emit button change events (even during standby)
+ uint8_t was_pressed = button_last_state;
+ uint8_t pressed = button_is_pressed();
+ if (was_pressed != pressed) {
+ go_to_standby = 0;
+ PCINT_inner(pressed);
+ }
+ // cache again, in case the value changed
+ ticks_since_last = ticks_since_last_event;
- static uint16_t sleep_counter = 0;
+ #ifdef TICK_DURING_STANDBY
// handle standby mode specially
if (go_to_standby) {
- // emit a halfsleep tick, and process it
- emit(EV_sleep_tick, sleep_counter);
- // wrap around from 65535 to 32768, not 0
- sleep_counter = (sleep_counter + 1) | (sleep_counter & 0x8000);
+ // emit a sleep tick, and process it
+ emit(EV_sleep_tick, ticks_since_last);
process_emissions();
- #if defined(USE_SLEEP_LVP)
- // stop here, usually... but proceed often enough for sleep LVP to work
- if (0 != (sleep_counter & 0x7f)) return;
- adc_trigger = 255; // make sure a measurement will happen
- #else
+ #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;
+
+ adc_trigger = 0; // make sure a measurement will happen
+ ADC_on(); // enable ADC voltage measurement functions temporarily
#endif
}
- else { sleep_counter = 0; }
+ else { // button handling should only happen while awake
#endif
- // detect and emit button change events
- uint8_t was_pressed = button_last_state;
- uint8_t pressed = button_is_pressed();
- if (was_pressed != pressed) PCINT_inner(pressed);
-
- // cache this here to reduce ROM size, because it's volatile
- uint16_t ticks_since_last = ticks_since_last_event;
-
- // increment, but loop from max back to half
- //if (ticks_since_last < 0xff) ticks_since_last ++;
- ticks_since_last = (ticks_since_last + 1) \
- | (ticks_since_last & 0x8000);
- // copy back to the original
- ticks_since_last_event = ticks_since_last;
-
// if time since last event exceeds timeout,
// append timeout to current event sequence, then
// send event to current state callback
@@ -141,9 +147,9 @@ ISR(WDT_vect) {
emit_current_event(ticks_since_last);
}
// has button been down long enough to become a "hold"?
+ // (first frame of a "hold" event)
else {
if (ticks_since_last >= HOLD_TIMEOUT) {
- //ticks_since_last_event = 0;
current_event |= B_HOLD;
emit_current_event(0);
}
@@ -156,30 +162,29 @@ ISR(WDT_vect) {
// no timeout required when releasing a long-press
// TODO? move this logic to PCINT() and simplify things here?
if (current_event & B_HOLD) {
- //emit_current_event(0); // should have been emitted by PCINT
+ //emit_current_event(0); // should have been emitted by PCINT_inner()
empty_event_sequence();
}
// end and clear event after release timeout
else if (ticks_since_last >= RELEASE_TIMEOUT) {
current_event |= B_TIMEOUT;
- //ticks_since_last_event = 0;
emit_current_event(0);
empty_event_sequence();
}
}
+ #ifdef TICK_DURING_STANDBY
+ }
+ #endif
+
#if defined(USE_LVP) || defined(USE_THERMAL_REGULATION)
- // start a new ADC measurement every 4 ticks
- adc_trigger ++;
- if (0 == (adc_trigger & 3)) {
- #if defined(TICK_DURING_STANDBY) && defined(USE_SLEEP_LVP)
- // we shouldn't be here unless it woke up for a LVP check...
- // so enable ADC voltage measurement functions temporarily
- if (go_to_standby) ADC_on();
- #endif
+ // enable the deferred ADC handler once in a while
+ if (! adc_trigger) {
ADC_start_measurement();
- adcint_enable = 1;
+ adc_deferred_enable = 1;
}
+ // timing for the ADC handler is every 32 ticks (~2Hz)
+ adc_trigger = (adc_trigger + 1) & 31;
#endif
}
diff --git a/spaghetti-monster/fsm-wdt.h b/spaghetti-monster/fsm-wdt.h
index 78fe791..d127551 100644
--- a/spaghetti-monster/fsm-wdt.h
+++ b/spaghetti-monster/fsm-wdt.h
@@ -25,9 +25,9 @@
void WDT_on();
inline void WDT_off();
-#ifdef TICK_DURING_STANDBY
-volatile uint8_t f_wdt = 0;
+volatile uint8_t irq_wdt = 0; // WDT interrupt happened?
+#ifdef TICK_DURING_STANDBY
#if defined(USE_INDICATOR_LED) || defined(USE_AUX_RGB_LEDS)
// measure battery charge while asleep
#define USE_SLEEP_LVP
diff --git a/spaghetti-monster/meteor/meteor.c b/spaghetti-monster/meteor/meteor.c
index 5e925e2..7d854a1 100644
--- a/spaghetti-monster/meteor/meteor.c
+++ b/spaghetti-monster/meteor/meteor.c
@@ -22,7 +22,6 @@
#define USE_LVP
#define USE_THERMAL_REGULATION
#define DEFAULT_THERM_CEIL 45
-#define USE_DELAY_4MS
#define USE_RAMPING
#define RAMP_LENGTH 150
#define USE_BATTCHECK
diff --git a/spaghetti-monster/momentary/momentary.c b/spaghetti-monster/momentary/momentary.c
index 26acd44..c7a8cf1 100644
--- a/spaghetti-monster/momentary/momentary.c
+++ b/spaghetti-monster/momentary/momentary.c
@@ -22,7 +22,6 @@
#include "hwdef-Emisar_D4.h"
#define USE_LVP
#define USE_DEBUG_BLINK
-#define USE_DELAY_4MS
#include "spaghetti-monster.h"
volatile uint8_t brightness;
diff --git a/spaghetti-monster/ramping-ui/ramping-ui.c b/spaghetti-monster/ramping-ui/ramping-ui.c
index 18f488d..5eb7d8f 100644
--- a/spaghetti-monster/ramping-ui/ramping-ui.c
+++ b/spaghetti-monster/ramping-ui/ramping-ui.c
@@ -22,7 +22,6 @@
#define USE_THERMAL_REGULATION
#define DEFAULT_THERM_CEIL 32
#define USE_DELAY_MS
-#define USE_DELAY_4MS
#define USE_DELAY_ZERO
#define USE_RAMPING
#define USE_BATTCHECK
@@ -353,7 +352,7 @@ void loop() {
battcheck();
}
else if (current_state == tempcheck_state) {
- blink_num(projected_temperature>>2);
+ blink_num(temperature);
nice_delay_ms(1000);
}
#endif
diff --git a/spaghetti-monster/rampingios/rampingiosv3.c b/spaghetti-monster/rampingios/rampingiosv3.c
index d72e971..7f03e77 100644
--- a/spaghetti-monster/rampingios/rampingiosv3.c
+++ b/spaghetti-monster/rampingios/rampingiosv3.c
@@ -123,6 +123,7 @@ uint8_t ramp_config_state(Event event, uint16_t arg);
uint8_t battcheck_state(Event event, uint16_t arg);
#endif
#ifdef USE_THERMAL_REGULATION
+#define USE_BLINK_NUM
uint8_t tempcheck_state(Event event, uint16_t arg);
uint8_t thermal_config_state(Event event, uint16_t arg);
#endif
@@ -930,14 +931,15 @@ void thermal_config_save() {
// calibrate room temperature
val = config_state_values[0];
if (val) {
- int8_t rawtemp = (temperature >> 1) - therm_cal_offset;
+ int8_t rawtemp = temperature - therm_cal_offset;
therm_cal_offset = val - rawtemp;
+ reset_thermal_history = 1; // invalidate all recent temperature data
}
val = config_state_values[1];
if (val) {
// set maximum heat limit
- therm_ceil = 30 + val;
+ therm_ceil = 30 + val - 1;
}
if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
}
@@ -966,9 +968,9 @@ uint8_t beacon_config_state(Event event, uint16_t arg) {
inline void beacon_mode_iter() {
// one iteration of main loop()
set_level(memorized_level);
- nice_delay_ms(500);
+ nice_delay_ms(100);
set_level(0);
- nice_delay_ms(((beacon_seconds) * 1000) - 500);
+ nice_delay_ms(((beacon_seconds) * 1000) - 100);
}
#endif // #ifdef USE_BEACON_MODE
@@ -1235,7 +1237,7 @@ void loop() {
#ifdef USE_THERMAL_REGULATION
// TODO: blink out therm_ceil during thermal_config_state?
else if (state == tempcheck_state) {
- blink_num(temperature>>1);
+ blink_num(temperature);
nice_delay_ms(1000);
}
#endif
diff --git a/spaghetti-monster/spaghetti-monster.txt b/spaghetti-monster/spaghetti-monster.txt
index 9e051f1..434e1bc 100644
--- a/spaghetti-monster/spaghetti-monster.txt
+++ b/spaghetti-monster/spaghetti-monster.txt
@@ -124,14 +124,13 @@ Event types:
between events.
- EV_temperature_high: Sent whenever the MCU's projected temperature
- is higher than therm_ceil. Minimum of THERMAL_WARNING_SECONDS
- between events. The 'arg' indicates how far the temperature
- exceeds the limit.
+ is higher than therm_ceil. Minimum of one second between events.
+ The 'arg' indicates how far the temperature exceeds the limit.
- EV_temperature_low: Sent whenever the MCU's projected temperature
is lower than (therm_ceil - THERMAL_WINDOW_SIZE). Minimum of
- THERMAL_WARNING_SECONDS between events. The 'arg' indicates how
- far the temperature exceeds the limit.
+ one second between events. The 'arg' indicates how far the
+ temperature exceeds the limit.
Button presses:
@@ -297,9 +296,6 @@ Useful #defines:
- DEFAULT_THERM_CEIL: Set the temperature limit to use by default
when the user hasn't configured anything.
- - THERMAL_WARNING_SECONDS: How long to wait between temperature
- events.
-
- USE_RAMPING: Enable smooth ramping helpers.
- RAMP_LENGTH: Pick a pre-defined ramp by length. Defined sizes
diff --git a/spaghetti-monster/werner/Makefile b/spaghetti-monster/werner/Makefile
new file mode 100644
index 0000000..21d85f7
--- /dev/null
+++ b/spaghetti-monster/werner/Makefile
@@ -0,0 +1,7 @@
+all:
+ ./build-all.sh
+
+clean:
+ rm -f cfg-*.h *.hex *~ *.elf *.o
+
+.phony: clean
diff --git a/spaghetti-monster/werner/build-all.sh b/spaghetti-monster/werner/build-all.sh
index 87d4f0d..b114101 100755
--- a/spaghetti-monster/werner/build-all.sh
+++ b/spaghetti-monster/werner/build-all.sh
@@ -7,7 +7,9 @@ UI=werner
for TARGET in cfg-*.h ; do
NAME=$(echo "$TARGET" | perl -ne '/cfg-(.*).h/ && print "$1\n";')
echo "===== $NAME ====="
- echo ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}"
- ../../../bin/build.sh 85 "$UI" "-DCONFIGFILE=${TARGET}"
+ ATTINY=$(grep 'ATTINY:' $TARGET | awk '{ print $3 }')
+ if [ -z "$ATTINY" ]; then ATTINY=85 ; fi
+ echo ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
+ ../../../bin/build.sh $ATTINY "$UI" "-DCONFIGFILE=${TARGET}"
mv -f "$UI".hex "$UI".$NAME.hex
done
diff --git a/spaghetti-monster/werner/werner.c b/spaghetti-monster/werner/werner.c
index 7c47cd7..1c3d4ad 100644
--- a/spaghetti-monster/werner/werner.c
+++ b/spaghetti-monster/werner/werner.c
@@ -33,6 +33,10 @@
//#define BATTCHECK_8bars // FIXME: breaks build
//#define BATTCHECK_4bars // FIXME: breaks build
+// cut clock speed at very low modes for better efficiency
+// (defined here so config files can override it)
+#define USE_DYNAMIC_UNDERCLOCKING
+
/***** specific settings for known driver types *****/
#ifdef CONFIGFILE
#include "tk.h"
@@ -63,7 +67,6 @@
#define RAMP_LENGTH 150 // default, if not overridden in a driver cfg file
#define USE_BATTCHECK
#define USE_IDLE_MODE // reduce power use while awake and no tasks are pending
-#define USE_DYNAMIC_UNDERCLOCKING // cut clock speed at very low modes for better efficiency
// auto-detect how many eeprom bytes
#define USE_EEPROM
@@ -369,12 +372,14 @@ uint8_t battcheck_state(Event event, uint16_t arg) {
set_state(off_state, 0);
return MISCHIEF_MANAGED;
}
+ #ifdef USE_THERMAL_REGULATION
// 2 clicks: tempcheck mode
else if (event == EV_2clicks) {
blink_confirm(2);
set_state(tempcheck_state, 0);
return MISCHIEF_MANAGED;
}
+ #endif
return EVENT_NOT_HANDLED;
}
#endif
@@ -467,14 +472,15 @@ void thermal_config_save() {
// calibrate room temperature
val = config_state_values[0];
if (val) {
- int8_t rawtemp = (temperature >> 1) - therm_cal_offset;
+ int8_t rawtemp = temperature - therm_cal_offset;
therm_cal_offset = val - rawtemp;
+ reset_thermal_history = 1; // invalidate all recent temperature data
}
val = config_state_values[1];
if (val) {
// set maximum heat limit
- therm_ceil = 30 + val;
+ therm_ceil = 30 + val - 1;
}
if (therm_ceil > MAX_THERM_CEIL) therm_ceil = MAX_THERM_CEIL;
}
@@ -589,7 +595,7 @@ uint8_t nearest_level(int16_t target) {
for(uint8_t i=0; i<ramp_discrete_steps; i++) {
this_level = ramp_discrete_floor + (i * (uint16_t)ramp_range / (ramp_discrete_steps-1));
- int8_t diff = target - this_level;
+ int16_t diff = target - this_level;
if (diff < 0) diff = -diff;
if (diff <= (ramp_discrete_step_size>>1))
return this_level;
@@ -684,9 +690,6 @@ void loop() {
StatePtr state = current_state;
- #ifdef USE_DYNAMIC_UNDERCLOCKING
- auto_clock_speed();
- #endif
if (0) {}
#ifdef USE_BATTCHECK
@@ -697,7 +700,7 @@ void loop() {
#ifdef USE_THERMAL_REGULATION
// TODO: blink out therm_ceil during thermal_config_state
else if (state == tempcheck_state) {
- blink_num(temperature>>1);
+ blink_num(temperature);
nice_delay_ms(1000);
}
#endif
diff --git a/tk-attiny.h b/tk-attiny.h
index 597af4d..49f1195 100644
--- a/tk-attiny.h
+++ b/tk-attiny.h
@@ -34,6 +34,7 @@
#define ADMUX_VCC 0b00001100
#define DELAY_ZERO_TIME 252
#define SWITCH_PORT PINB // PINA or PINB or PINC
+ #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR
#elif (ATTINY == 25)
// TODO: Use 6.4 MHz instead of 8 MHz?
#define F_CPU 8000000UL
@@ -44,6 +45,7 @@
#define ADMUX_THERM 0b10001111
#define DELAY_ZERO_TIME 1020
#define SWITCH_PORT PINB // PINA or PINB or PINC
+ #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR
#elif (ATTINY == 85)
// TODO: Use 6.4 MHz instead of 8 MHz?
#define F_CPU 8000000UL
@@ -56,16 +58,19 @@
#define ADMUX_THERM 0b10001111
#define DELAY_ZERO_TIME 1020
#define SWITCH_PORT PINB // PINA or PINB or PINC
+ #define VOLTAGE_ADC_DIDR DIDR0 // this MCU only has one DIDR
#elif (ATTINY == 1634)
#define F_CPU 8000000UL
#define V_REF REFS1
#define BOGOMIPS (F_CPU/4000)
- // (1 << V_REF) | (0 << ADLAR) | (VCC_CHANNEL)
+ // DS table 19-3, 19-4, 1.1V ref / VCC
#define ADMUX_VCC 0b00001101
- // (1 << V_REF) | (0 << ADLAR) | (THERM_CHANNEL)
+ // (1 << V_REF) | (THERM_CHANNEL)
+ // DS table 19-3, 19-4, internal sensor / 1.1V ref
#define ADMUX_THERM 0b10001110
#define DELAY_ZERO_TIME 1020
//#define SWITCH_PORT PINA // set this in hwdef
+ //#define VOLTAGE_ADC_DIDR DIDR0 // set this in hwdef
#else
#error Hey, you need to define ATTINY.
#endif
diff --git a/tk-delay.h b/tk-delay.h
index 7b00ac0..29cf463 100644
--- a/tk-delay.h
+++ b/tk-delay.h
@@ -48,12 +48,14 @@ void _delay_zero() {
}
#endif
#ifdef USE_DELAY_4MS
+#ifndef delay_4ms
#define delay_4ms _delay_4ms
void _delay_4ms(uint8_t n) // because it saves a bit of ROM space to do it this way
{
while(n-- > 0) _delay_loop_2(BOGOMIPS*4);
}
#endif
+#endif
#ifdef USE_DELAY_S
#define delay_s _delay_s
void _delay_s() // because it saves a bit of ROM space to do it this way