diff --git a/Makefile b/Makefile index b10af2d9e..fecd9ecf5 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ BOARD_TAG=Proffieboard-L433CC # This should only need to be set if arduino is installed in a nonstandard location. # ARDUINO_DIR:=/home/hubbe/lib/arduino-1.8.3 -ifeq ($(strip $(BOARD_TAG)),Proffieboard-L433CC) +ifeq ($(findstring Proffieboard, $(BOARD_TAG)),Proffieboard) USB_TYPE=USB_TYPE_CDC_MSC_WEBUSB ARDUINO_LIBS=SPI Wire include Proffieboard.mk @@ -37,6 +37,9 @@ test1: test1V: $(MAKE) all TESTFLAGS=-DCONFIG_FILE_TEST=\\\"config/proffieboard_v1_verification_config.h\\\" BOARD_TAG=Proffieboard-L433CC OBJDIR=test-proffieboard-v1-verification +test2V: + $(MAKE) all TESTFLAGS=-DCONFIG_FILE_TEST=\\\"config/proffieboard_v2_verification_config.h\\\" BOARD_TAG=ProffieboardV2-L433CC OBJDIR=test-proffieboard-v2-verification + test2: $(MAKE) all TESTFLAGS=-DCONFIG_FILE_TEST=\\\"config/default_v3_config.h\\\" BOARD_TAG=teensy36 OBJDIR=test-teensy36-default-v3 @@ -68,7 +71,7 @@ testB: $(MAKE) all TESTFLAGS=-DCONFIG_FILE_TEST=\\\"config/td_proffieboard_config.h\\\" BOARD_TAG=Proffieboard-L433CC OBJDIR=test-proffieboard-td -test: style-test common-test blades-test sound-test test1 test2 test3 test4 test5 test6 test7 test8 test9 testA testB test1V +test: style-test common-test blades-test sound-test test1 test2 test3 test4 test5 test6 test7 test8 test9 testA testB test1V test2V @echo Tests pass # Check that there are no uncommitted changes diff --git a/ProffieOS.ino b/ProffieOS.ino index 069c8678b..5f8643205 100644 --- a/ProffieOS.ino +++ b/ProffieOS.ino @@ -184,6 +184,7 @@ SnoozeBlock snooze_config(snooze_touch, snooze_digital, snooze_timer); #endif const char version[] = "$Id: ce12a06a1e236b5101ec60c950530a9a4719a74d $"; +const char install_time[] = __DATE__ " " __TIME__; #include "common/state_machine.h" #include "common/monitoring.h" @@ -1016,6 +1017,8 @@ class Commands : public CommandParser { if (!strcmp(cmd, "version")) { STDOUT.println(version); + STDOUT.print("Installed: "); + STDOUT.println(install_time); return true; } if (!strcmp(cmd, "reset")) { diff --git a/Proffieboard.mk b/Proffieboard.mk index 04c7e6f9a..cd69b0122 100644 --- a/Proffieboard.mk +++ b/Proffieboard.mk @@ -401,12 +401,10 @@ endif CPPFLAGS += -DMD -DUSB_TYPE=$(USB_TYPE) '-DUSB_PRODUCT=$(USB_PRODUCT)' '-DUSB_MANUFACTURER=$(USB_MANUFACTURER)' # Get extra define flags from boards.txt -EXFLAGS := $(shell echo $(call PARSE_BOARD,$(BOARD_TAG),build.extra_flags) | grep -oE '(-D)\w+') +EXFLAGS := $(shell echo $(call PARSE_BOARD,$(BOARD_TAG),build.extra_flags) | grep -oE '(-D)[^ ]+') +$(call show_config_variable,EXTRA FLAGS,[COMPUTED]) XFLAGS=-ffast-math -fsingle-precision-constant -D__FPU_PRESENT=1 -march=armv7e-m -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16 -mabi=aapcs -mslow-flash-data -DDOSFS_SDCARD=1 -DUSB_DID=0xffff -D_SYSTEM_CORE_CLOCK_=80000000L -D_ARDUINO_STM32L4 -# TODO: Makefile selects proffieboard version -CPPFLAGS += -DPROFFIEBOARD_VERSION=1 - # Strip only defines from extra flags as boards file appends user {build.usb} CPPFLAGS += $(EXFLAGS) $(XFLAGS) CPPFLAGS += -DUSB_VID=$(USB_VID) diff --git a/blades/blade_base.h b/blades/blade_base.h index e06096457..6357d51f2 100644 --- a/blades/blade_base.h +++ b/blades/blade_base.h @@ -53,7 +53,6 @@ DEFINE_ALL_EFFECTS(); HANDLED_FEATURE_DRAG = 1 << 3, HANDLED_FEATURE_MELT = 1 << 4, HANDLED_FEATURE_LIGHTNING_BLOCK = 1 << 5, - HANDLED_FEATURE_PREON = 1 << 6, }; #include "../styles/blade_style.h" @@ -110,11 +109,6 @@ class BladeBase { handled_features_ = (HandledFeature) ((int)handled_features_ | (int)feature); } - // Returns true if the current style handles a particular effect type. - static bool IsHandled(HandledFeature effect) { - return (handled_features_ & effect) != 0; - } - static HandledFeature GetHandledTypes() { return handled_features_; } @@ -137,9 +131,6 @@ class OneshotEffectDetector { case EFFECT_STAB: BladeBase::HandleFeature(HANDLED_FEATURE_STAB); break; - case EFFECT_PREON: - BladeBase::HandleFeature(HANDLED_FEATURE_PREON); - break; default: break; } diff --git a/blades/fastled_blade.h b/blades/fastled_blade.h index 655b046f6..54a1dc7e6 100644 --- a/blades/fastled_blade.h +++ b/blades/fastled_blade.h @@ -103,11 +103,8 @@ class FASTLED_Blade : public AbstractBlade, CommandParser, Looper { } void SB_PreOn(float* d) override { AbstractBlade::SB_PreOn(d); - // This blade uses EFFECT_PREON, so we need to turn the power on now. - if (IsHandled(HANDLED_FEATURE_PREON)) { - Power(true); - delay(10); - } + Power(true); + delay(10); } void SB_Off(OffType off_type) override { AbstractBlade::SB_Off(off_type); diff --git a/blades/simple_blade.h b/blades/simple_blade.h index 5071063c9..3a26064f6 100644 --- a/blades/simple_blade.h +++ b/blades/simple_blade.h @@ -214,10 +214,8 @@ class Simple_Blade : public AbstractBlade, CommandParser, Looper { void SB_PreOn(float* delay) override { AbstractBlade::SB_PreOn(delay); // This blade uses EFFECT_PREON, so we need to turn the power on now. - if (IsHandled(HANDLED_FEATURE_PREON)) { - battery_monitor.SetLoad(true); - Power(true); - } + battery_monitor.SetLoad(true); + Power(true); } void SB_Off(OffType off_type) override { AbstractBlade::SB_Off(off_type); diff --git a/blades/spiled_pin.h b/blades/spiled_pin.h index 427f662e6..20b04b634 100644 --- a/blades/spiled_pin.h +++ b/blades/spiled_pin.h @@ -2,7 +2,7 @@ #define BLADES_SPILEDPIN_H template -class SpiLedPin : public Looper { +class SpiLedPin { public: SpiLedPin(int pin, int num_leds, @@ -57,7 +57,6 @@ class SpiLedPin : public Looper { } void Set(int led, Color16 color) { - uint8_t *output = ((uint8_t *)displayMemory) + (led + 1) * (1 + Color8::num_bytes(byteorder_)); int MAX = std::max(color.r, std::max(color.g, color.b)); int output_level = (MAX + 2113) / 2117; int mult = output_level == 0 ? 0 : 7930 / output_level; diff --git a/blades/ws2811_blade.h b/blades/ws2811_blade.h index 8c7fcdb88..a6b59f09e 100644 --- a/blades/ws2811_blade.h +++ b/blades/ws2811_blade.h @@ -82,7 +82,7 @@ class WS2811_Blade : public AbstractBlade, CommandParser, Looper { STDOUT.print("WS2811 Blade with "); STDOUT.print(pin_.num_leds()); STDOUT.println(" leds."); - Power(true); + run_ = true; CommandParser::Link(); Looper::Link(); AbstractBlade::Activate(); @@ -107,14 +107,14 @@ class WS2811_Blade : public AbstractBlade, CommandParser, Looper { } void set(int led, Color16 c) override { color_buffer[led] = c; - } + } void allow_disable() override { if (!on_) allow_disable_ = true; } void SetStyle(BladeStyle* style) override{ TRACE("SetStyle"); AbstractBlade::SetStyle(style); - Power(true); + run_ = true; } BladeStyle* UnSetStyle() override { TRACE("UnSetStyle"); @@ -127,17 +127,15 @@ class WS2811_Blade : public AbstractBlade, CommandParser, Looper { void SB_On() override { TRACE("SB_On"); AbstractBlade::SB_On(); - Power(true); + run_ = true; on_ = true; power_off_requested_ = false; } void SB_PreOn(float* delay) override { AbstractBlade::SB_PreOn(delay); // This blade uses EFFECT_PREON, so we need to turn the power on now. - if (IsHandled(HANDLED_FEATURE_PREON)) { - Power(true); - power_off_requested_ = false; - } + run_ = true; + power_off_requested_ = false; } void SB_Off(OffType off_type) override { TRACE("SB_Off"); @@ -171,61 +169,82 @@ class WS2811_Blade : public AbstractBlade, CommandParser, Looper { void Help() override { STDOUT.println(" blade on/off - turn ws2811 blade on off"); } + void PowerOff() { + if (!poweroff_delay_start_) { + poweroff_delay_start_ = millis(); + } + if (millis() - poweroff_delay_start_ < poweroff_delay_ms_) { + return; + } + Power(false); + run_ = false; + power_off_requested_ = false; + } +#define BLADE_YIELD() do { \ + YIELD(); \ + /* If Power() was called.... */ \ + if (current_blade != this) goto retry; \ +} while(0) + protected: void Loop() override { STATE_MACHINE_BEGIN(); while (true) { retry: - while (!powered_ || !current_style_) { + if (current_blade == this) current_blade = NULL; + YIELD(); + if (!current_style_ || !run_) { loop_counter_.Reset(); - YIELD(); + continue; } // Wait until it's our turn. - while (current_blade) YIELD(); - if (allow_disable_ || power_off_requested_) { - if (!on_) { - if (!poweroff_delay_start_) { - poweroff_delay_start_ = millis(); - } - if (millis() - poweroff_delay_start_ < poweroff_delay_ms_) { - YIELD(); - continue; - } - poweroff_delay_start_ = 0; - } - Power(on_); + if (current_blade) { continue; } current_blade = this; + if (power_off_requested_) { + PowerOff(); + continue; + } + + allow_disable_ = false; current_style_->run(this); - while (!pin_.IsReadyForBeginFrame()) { - YIELD(); - // If Power() was called.... - if (current_blade != this) goto retry; + + if (!powered_) { + if (allow_disable_) continue; + Power(true); } + + // Update pixels + while (!pin_.IsReadyForBeginFrame()) BLADE_YIELD(); pin_.BeginFrame(); for (int i = 0; i < pin_.num_leds(); i++) { pin_.Set(i, color_buffer[i]); if (!(i & 0x1f)) Looper::DoHFLoop(); } - while (!pin_.IsReadyForEndFrame()) { - YIELD(); - // If Power() was called.... - if (current_blade != this) goto retry; - } + while (!pin_.IsReadyForEndFrame()) BLADE_YIELD(); pin_.EndFrame(); loop_counter_.Update(); - current_blade = NULL; - YIELD(); + + if (powered_ && allow_disable_) { + PowerOff(); + run_ = false; + } } STATE_MACHINE_END(); } private: + // Loop should run. + bool run_ = false; + // Blade is "on" bool on_ = false; + // Blade has power bool powered_ = false; + // Style has indicated that it's ok to shutd down until the next wakeup event. bool allow_disable_ = false; + // We should power off and stop running, even if the blade is on. bool power_off_requested_ = false; uint32_t poweroff_delay_ms_; uint32_t poweroff_delay_start_ = 0; diff --git a/buttons/button_base.h b/buttons/button_base.h index d3efd93d7..a0c2606d1 100644 --- a/buttons/button_base.h +++ b/buttons/button_base.h @@ -47,18 +47,21 @@ class ButtonBase : public Looper, // the event itself, and the event with a count, dependinging on how // may double-presses there has been. The count starts at FIRST, that way // the first event can be distinguished from the rest. - void Send(uint32_t event) { + bool Send(uint32_t event) { if (!prop.Event(button_, (EVENT)(event + (EVENT_SECOND_PRESSED - EVENT_FIRST_PRESSED) * press_count_))) { // Only send the second event if the first event didn't trigger a response. - prop.Event(button_, (EVENT)event); + return prop.Event(button_, (EVENT)event); } + return true; } // We send the click immediately, but we also hold a "saved" event // which is sent a little bit later unless a double-click occurs. void SendClick(uint32_t event) { - Send(event); - saved_event_ = event + (EVENT_SAVED_CLICK_SHORT - EVENT_CLICK_SHORT); + if (!Send(event)) { + // Don't save the event if it's already been handled. + saved_event_ = event + (EVENT_SAVED_CLICK_SHORT - EVENT_CLICK_SHORT); + } } void Loop() override { diff --git a/common/battery_monitor.h b/common/battery_monitor.h index 1c1c9a1dc..d191497af 100644 --- a/common/battery_monitor.h +++ b/common/battery_monitor.h @@ -47,30 +47,31 @@ BatteryMonitor() : reader_(batteryLevelPin, SetPinHigh(false); } void Loop() override { - if (reading_) { - if (!reader_.Done()) return; + if (monitor.ShouldPrint(Monitoring::MonitorBattery) || + millis() - last_print_millis_ > 20000) { + STDOUT.print("Battery voltage: "); + STDOUT.println(battery()); + last_print_millis_ = millis(); + } + + STATE_MACHINE_BEGIN(); + last_voltage_read_time_ = micros(); + while (true) { + while (micros() - last_voltage_read_time_ < 1000) YIELD(); + while (!reader_.Start()) YIELD(); + while (!reader_.Done()) YIELD(); float v = battery_now(); - last_voltage_ = last_voltage_ * 0.997 + v * 0.003; - reading_ = false; + uint32_t now = micros(); + float mul = powf(0.05, (now - last_voltage_read_time_) / 1000000.0); + last_voltage_read_time_ = now; + last_voltage_ = last_voltage_ * mul + v * (1 - mul); if (IsLow()) { low_count_++; } else { low_count_ = 0; } } - uint32_t now = micros(); - if (now - last_voltage_read_time_ >= 1000) { - if (!reader_.Start()) - return; - reading_ = true; - last_voltage_read_time_ = now; - } - if (monitor.ShouldPrint(Monitoring::MonitorBattery) || - millis() - last_print_millis_ > 20000) { - STDOUT.print("Battery voltage: "); - STDOUT.println(battery()); - last_print_millis_ = millis(); - } + STATE_MACHINE_END(); } bool IsLow() { @@ -103,8 +104,6 @@ BatteryMonitor() : reader_(batteryLevelPin, } #if 0 if (!strcmp(cmd, "bstate")) { - STDOUT.print("reading = "); - STDOUT.println(reading_); STDOUT.print("Next state: "); STDOUT.println(reader_.state_machine_.next_state_); STDOUT.print("ADC SMP: "); @@ -143,7 +142,6 @@ BatteryMonitor() : reader_(batteryLevelPin, uint32_t last_print_millis_; uint32_t low_count_ = 0; AnalogReader reader_; - bool reading_ = false; }; BatteryMonitor battery_monitor; diff --git a/common/current_preset.h b/common/current_preset.h index 3f58ff98b..ff2824d31 100644 --- a/common/current_preset.h +++ b/common/current_preset.h @@ -55,7 +55,7 @@ class CurrentPreset { bool Read(FileReader* f) { int preset_count = 0; int current_style = 0; - if (f->Tell() == 0) preset_num = -1; + if (f->Tell() <= sizeof(install_time) + 11) preset_num = -1; preset_type = PRESET_DISK; for (; f->Available(); f->skipline()) { @@ -76,6 +76,7 @@ class CurrentPreset { } continue; } + if (!strcmp(variable, "installed")) continue; if (!preset_count) return false; if (f->Peek() != '=') continue; f->Read(); @@ -116,6 +117,7 @@ class CurrentPreset { #define SET_PRESET_STYLE(N) if (current_style == N) { current_style##N = tmp; tmp = 0; } ONCEPERBLADE(SET_PRESET_STYLE); if (tmp) free(tmp); + continue; } } if (preset_count == 1) { @@ -157,6 +159,21 @@ class CurrentPreset { return false; if (f->FileSize() < 4) return false; + int pos = 0; +#ifndef KEEP_SAVEFILES_WHEN_PROGRAMMING + char variable[33]; + f->readVariable(variable); + if (strcmp(variable, "installed")) return false; + if (f->Read() != '=') return false; + const char* tmp = install_time; + while (*tmp) { + if (f->Read() != *tmp) return false; + tmp++; + } + if (f->Read() != '\n') return false; + pos = f->Tell(); +#endif + int p = f->FileSize() - 1; while (p > 0) { f->Seek(p); if (!isSpace(f->Read())) break; p--; } f->Seek(p - 3); @@ -164,7 +181,7 @@ class CurrentPreset { if (toLower(f->Read()) != 'e') return false; if (toLower(f->Read()) != 'n') return false; if (toLower(f->Read()) != 'd') return false; - f->Seek(0); + f->Seek(pos); return true; } @@ -177,6 +194,7 @@ class CurrentPreset { // Found valid tmp file LSFS::Remove(ini_fn); f.Create(ini_fn); + f.write_key_value("installed", install_time); while (f2.Available()) { int to_copy = std::min(f2.Available(), sizeof(buf)); if (f2.Read(buf, to_copy) != to_copy || @@ -198,6 +216,7 @@ class CurrentPreset { FileReader f; PathHelper ini_fn(GetSaveDir(), "presets.ini"); f.Create(ini_fn); + f.write_key_value("installed", install_time); CurrentPreset tmp; for (size_t i = 0; i < current_config->num_presets; i++) { tmp.Set(i); @@ -251,6 +270,7 @@ class CurrentPreset { PathHelper tmp_fn(GetSaveDir(), "presets.tmp"); LSFS::Remove(tmp_fn); out.Create(tmp_fn); + out.write_key_value("installed", install_time); CurrentPreset tmp; int opos = 0; if (position == 0) { diff --git a/common/tests.cpp b/common/tests.cpp index 78b0a9427..8dc0eb365 100644 --- a/common/tests.cpp +++ b/common/tests.cpp @@ -17,6 +17,8 @@ #define PROFFIE_TEST #define ENABLE_SD +const char install_time[] = __DATE__ " " __TIME__; + const char* GetSaveDir() { return NULL; } float fract(float x) { return x - floor(x); } @@ -171,6 +173,7 @@ BladeConfig* current_config; void create_test_presets_ini(const char* filename, int presets, bool finish) { FILE* f = fopen(filename, "wct"); CHECK(f); + fprintf(f, "installed=%s\n", install_time); for (int i = 0; i < presets; i++) { fprintf(f, "new_preset\n"); fprintf(f, "FONT=font%d\n", i); diff --git a/props/prop_base.h b/props/prop_base.h index 5f895e1a8..61480fcda 100644 --- a/props/prop_base.h +++ b/props/prop_base.h @@ -1057,7 +1057,8 @@ class PropBase : CommandParser, Looper, protected SaberBase { if (track_player_) { STDOUT.print("Playing "); STDOUT.println(arg); - track_player_->Play(arg); + if (!track_player_->PlayInCurrentDir(arg)) + track_player_->Play(arg); } else { STDOUT.println("No available WAV players."); } diff --git a/props/saber_sa22c_buttons.h b/props/saber_sa22c_buttons.h index 4258baa03..cb7eb9967 100644 --- a/props/saber_sa22c_buttons.h +++ b/props/saber_sa22c_buttons.h @@ -1,27 +1,53 @@ -// sa22c props file, includes the following changes +// sa22c props file, includes 1,2 and 3 button modes. Incorporates multi-blast +// by fett263, gesture ignition by ShtokyD, on-the-fly volume controls and +// full access to all features with 1,2 or 3 button sabers // // New #define SA22C_NO_LOCKUP_HOLD -// reverts to lockup being triggered only by clash + pow or aux +// reverts to lockup being triggered only by clash + aux in 2-button mode +// Also sets multi-blast to trigger while holding aux and swinging, rather than +// double click and hold +// +// Gesture ignition +// if you add #define SHTOK_GESTURE_IGNITION to your config file, you can +// turn on the saber using the stab motion, and turn off with a twist. +// +// Tightened click timings +// I've shortened the timeout for short and double click detection from 500ms +// to 300ms. I think it feels more responsive this way but still gives enough +// timeout to ensure all button actions can be achieved consistently +// I've included all button timings so they can be easily tweaked to suit +// individual tastes. +// // Button configs: // // 1 Button: -// Activate Muted - fast double click while OFF +// Activate Muted - double click and hold while OFF // Activate - short click while OFF -// Play/Stop Music - hold and release while ON +// Play/Stop Music - double click while OFF // Turn off blade - hold and wait till blade is off while ON // Next Preset - hold and release while OFF // Prev Preset - hold and wait while OFF // Lockup - hold + hit clash while ON +// Stab - thrust forward clash while ON +// Lightning Block - double click and hold while ON +// Melt - hold + thust forward clash while ON // Drag - hold + hit clash while ON pointing the blade tip down -// Blaster Blocks - short click while ON +// Blaster Blocks - short click/double click/triple click while on +// Multi-Blast - hold while swinging for one second and release +// to trigger blaster block, swing saber while in multi-blast mode +// to exit, hold while swinging for one second and release // Force Effects - hold + twist the hilt while ON (while pointing up) // Color Change mode - hold + twist the hilt while ON (pointing down) -// Melt - hold while stabbing (clash with forward motion, horizontal) -// +// Enter Volume - Menu hold + clash while OFF +// Volume UP - hold and release while in Volume Menu +// Volume DOWN - click while in Volume Menu +// Exit Volume Menu - Menu hold + clash while OFF +// Battery Level - triple click while OFF +// // // 2 Button: // POWER -// Activate Muted - fast double click while OFF +// Activate Muted - double click and hold while OFF // Activate - short click while OFF // Play/Stop Music - hold and release while OFF // Turn off blade - hold and wait till blade is off while ON @@ -29,10 +55,13 @@ // Volume UP - short click while OFF and in VOLUME MENU // Prev Preset - hold and wait while OFF // Color Change mode - hold + toggle AUX while ON -// Lightning Block - hold + CLASH when ON +// Lightning Block - double click and hold while ON // Melt - hold while stabbing (clash with forward motion, horizontal) // AUX -// Blaster blocks - short click while ON +// Blaster blocks - short click/double click/triple click while ON +// Multi-Blast - double-click and hold for half a second +// to trigger blaster block, swing saber while in multi-blast mode +// to exit, double-click and hold for half a second // Next Preset - short click while OFF // Lockup - hold while ON // Drag - hold while ON pointing the blade tip down @@ -40,22 +69,42 @@ // Volume down - short click while OFF and in VOLUME MENU // Battery level - hold while off // -// 3 Button: Same as two button except for VOLUME MENU -// AUX -// Volume UP - short click while OFF and in VOLUME MENU +// 3 Button: Same as two button except for the following +// // AUX2 -// Lockup - hold while ON +// Lightning Block - hold while ON // Previous Preset - short click while OFF -// Volume DOWN - short click while OFF and in VOLUME MENU + #ifndef PROPS_SABER_SA22C_BUTTONS_H #define PROPS_SABER_SA22C_BUTTONS_H #include "prop_base.h" +#include "../sound/hybrid_font.h" #undef PROP_TYPE #define PROP_TYPE SaberSA22CButtons +#ifndef BUTTON_DOUBLE_CLICK_TIMEOUT +#define BUTTON_DOUBLE_CLICK_TIMEOUT 300 +#endif + +#ifndef BUTTON_SHORT_CLICK_TIMEOUT +#define BUTTON_SHORT_CLICK_TIMEOUT 300 +#endif + +#ifndef BUTTON_HELD_TIMEOUT +#define BUTTON_HELD_TIMEOUT 300 +#endif + +#ifndef BUTTON_HELD_MEDIUM_TIMEOUT +#define BUTTON_HELD_MEDIUM_TIMEOUT 1000 +#endif + +#ifndef BUTTON_HELD_LONG_TIMEOUT +#define BUTTON_HELD_LONG_TIMEOUT 2000 +#endif + // The Saber class implements the basic states and actions // for the saber. class SaberSA22CButtons : public PropBase { @@ -63,6 +112,19 @@ class SaberSA22CButtons : public PropBase { SaberSA22CButtons() : PropBase() {} const char* name() override { return "SaberSA22CButtons"; } + // EVENT_SWING + bool swinging_ = false; + void Loop() override { + PropBase::Loop(); + if (!swinging_ && fusor.swing_speed() > 250) { + swinging_ = true; + Event(BUTTON_NONE, EVENT_SWING); + } + if (swinging_ && fusor.swing_speed() < 100) { + swinging_ = false; + } + } + void ChangeVolume(bool up) { if (up) { STDOUT.println("Volume up"); @@ -103,236 +165,252 @@ class SaberSA22CButtons : public PropBase { } return true; -#if NUM_BUTTONS == 0 - case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_OFF): -#endif - case EVENTID(BUTTON_POWER, EVENT_LATCH_ON, MODE_OFF): - case EVENTID(BUTTON_AUX, EVENT_LATCH_ON, MODE_OFF): - case EVENTID(BUTTON_AUX2, EVENT_LATCH_ON, MODE_OFF): - case EVENTID(BUTTON_POWER, EVENT_CLICK_SHORT, MODE_OFF): - #if NUM_BUTTONS == 2 - if (mode_volume_) { - ChangeVolume(true); - } +// Saber ON AND Volume Up + case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_OFF): +#ifdef SHTOK_GESTURE_IGNITION + case EVENTID(BUTTON_NONE, EVENT_STAB, MODE_OFF): #endif - if (!mode_volume_) { - aux_on_ = false; - On(); - } - return true; + if (!mode_volume_) { + On(); + } else { + ChangeVolume(true); + } + return true; - case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_OFF): -#if NUM_BUTTONS == 2 - if (mode_volume_) { - ChangeVolume(false); - } + case EVENTID(BUTTON_POWER, EVENT_FIRST_CLICK_LONG, MODE_OFF): + // 1-button: Next Preset AND Volume Down +#if NUM_BUTTONS == 1 + if (!mode_volume_) { + next_preset(); + } else { + ChangeVolume(false); + } + return true; +#else + // 2 and 3 button: Start or Stop Track + StartOrStopTrack(); + return true; #endif + + // 2 and 3 button: Next Preset and Volume Down + case EVENTID(BUTTON_AUX, EVENT_FIRST_CLICK_SHORT, MODE_OFF): + if (!mode_volume_) { + next_preset(); + } else { + ChangeVolume(false); + } + return true; + #if NUM_BUTTONS == 3 - if (mode_volume_) { - ChangeVolume(true); - } -#endif - if (!mode_volume_) { -#ifdef DUAL_POWER_BUTTONS - aux_on_ = true; - On(); + // 3 button: Previous Preset + case EVENTID(BUTTON_AUX2, EVENT_FIRST_CLICK_SHORT, MODE_OFF): #else - if (!mode_volume_) { - next_preset(); - } - + // 1 and 2 button: Previous Preset + case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_LONG, MODE_OFF): #endif - } - return true; + if (!mode_volume_) { + previous_preset(); + } + return true; + +// Activate Muted + case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD, MODE_OFF): + if (SetMute(true)) { + unmute_on_deactivation_ = true; + On(); + } + return true; +// Turn Blade OFF #if NUM_BUTTONS > 1 - case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_ON): - SaberBase::DoBlast(); - return true; -#endif -#if NUM_BUTTONS == 1 - case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON | BUTTON_POWER): -#ifndef DISABLE_COLOR_CHANGE - if (accel_.x < -0.15) { - ToggleColorChangeMode(); - } else { - SaberBase::DoForce(); - } +// 2 and 3 button + case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_MEDIUM, MODE_ON): #else - SaberBase::DoForce(); -#endif - return true; - case EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_ON): - if (!SaberBase::Lockup() || - SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) { - StartOrStopTrack(); - } - return true; - case EVENTID(BUTTON_POWER, EVENT_CLICK_SHORT, MODE_ON): - SaberBase::DoBlast(); - return true; +// 1 button + case EVENTID(BUTTON_POWER, EVENT_FIRST_HELD_LONG, MODE_ON): #endif - case EVENTID(BUTTON_POWER, EVENT_DOUBLE_CLICK, MODE_ON): - if (millis() - activated_ < 500) { - if (SetMute(true)) { - unmute_on_deactivation_ = true; - } - } -#if NUM_BUTTONS > 1 - else { - SaberBase::DoForce(); - } +#ifdef SHTOK_GESTURE_IGNITION + case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON): #endif - return true; - -#if NUM_BUTTONS == 1 - case EVENTID(BUTTON_POWER, EVENT_HELD_LONG, MODE_ON): - if (!SaberBase::Lockup()) { + if (!SaberBase::Lockup()) { #ifndef DISABLE_COLOR_CHANGE - if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) { - // Just exit color change mode. - // Don't turn saber off. - ToggleColorChangeMode(); - return true; - } -#endif - Off(); - } + if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) { + // Just exit color change mode. + // Don't turn saber off. + ToggleColorChangeMode(); return true; + } #endif + Off(); + } + swing_blast_ = false; + return true; +// 1 button Force and Color Change mode +#if NUM_BUTTONS == 1 + case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON | BUTTON_POWER): #ifndef DISABLE_COLOR_CHANGE -#ifdef SA22C_NO_LOCKUP_HOLD - case EVENTID(BUTTON_POWER, EVENT_CLICK_SHORT, MODE_ON | BUTTON_AUX): + if (accel_.x < -0.15) { ToggleColorChangeMode(); - break; -#else //SA22C_NO_LOCKUP_HOLD - case EVENTID(BUTTON_AUX, EVENT_CLICK_SHORT, MODE_ON | BUTTON_POWER): - ToggleColorChangeMode(); - break; -#endif //SA22C_NO_LOCKUP_HOLD -#endif //DISABLE_COLOR_CHANGE -#if NUM_BUTTONS > 1 - case EVENTID(BUTTON_POWER, EVENT_HELD_MEDIUM, MODE_ON): - case EVENTID(BUTTON_POWER, EVENT_LATCH_OFF, MODE_ON): - case EVENTID(BUTTON_AUX, EVENT_LATCH_OFF, MODE_ON): - case EVENTID(BUTTON_AUX2, EVENT_LATCH_OFF, MODE_ON): -#endif -#if NUM_BUTTONS == 0 - case EVENTID(BUTTON_NONE, EVENT_TWIST, MODE_ON): + } else { + SaberBase::DoForce(); + } +#else + SaberBase::DoForce(); #endif -#if NUM_BUTTONS != 1 - if (!SaberBase::Lockup()) { + return true; +#else + // 2 and 3 button Force effect + case EVENTID(BUTTON_POWER, EVENT_SECOND_CLICK_SHORT, MODE_ON): + SaberBase::DoForce(); + return true; + // 2 and 3 button color change #ifndef DISABLE_COLOR_CHANGE - if (SaberBase::GetColorChangeMode() != SaberBase::COLOR_CHANGE_MODE_NONE) { - // Just exit color change mode. - // Don't turn saber off. - ToggleColorChangeMode(); - return true; - } + case EVENTID(BUTTON_AUX, EVENT_FIRST_CLICK_SHORT, MODE_ON | BUTTON_POWER): + ToggleColorChangeMode(); + return true; #endif - Off(); - } #endif - return true; - case EVENTID(BUTTON_NONE, EVENT_STAB, MODE_ON | BUTTON_POWER): - if (!SaberBase::Lockup()) { - SaberBase::SetLockup(SaberBase::LOCKUP_MELT); - SaberBase::DoBeginLockup(); - return true; - } - break; -#if NUM_BUTTONS > 1 - case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_POWER): - SaberBase::SetLockup(SaberBase::LOCKUP_LIGHTNING_BLOCK); - SaberBase::DoBeginLockup(); - return true; -#else - case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_POWER): -#endif -#ifndef SA22C_NO_LOCKUP_HOLD - case EVENTID(BUTTON_AUX, EVENT_HELD, MODE_ON): +// Blaster Deflection +#if NUM_BUTTONS == 1 + // 1 button + case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_POWER, EVENT_SECOND_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_POWER, EVENT_THIRD_CLICK_SHORT, MODE_ON): #else - case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_AUX): + // 2 and 3 button + case EVENTID(BUTTON_AUX, EVENT_FIRST_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_AUX, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_ON): + case EVENTID(BUTTON_AUX, EVENT_THIRD_CLICK_SHORT, MODE_ON): #endif - if (!SaberBase::Lockup()) { - if (pointing_down_) { - SaberBase::SetLockup(SaberBase::LOCKUP_DRAG); - } else { - SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL); - } - SaberBase::DoBeginLockup(); - return true; - } + swing_blast_ = false; + SaberBase::DoBlast(); + return true; - // Off functions - case EVENTID(BUTTON_POWER, EVENT_CLICK_LONG, MODE_OFF): +// Multi-Blaster Deflection mode #if NUM_BUTTONS == 1 - if (!mode_volume_); - next_preset(); - return true; + // 1 button + case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_ON | BUTTON_POWER): +#else + // 2 and 3 button +#ifndef SA22C_NO_LOCKUP_HOLD + case EVENTID(BUTTON_AUX, EVENT_SECOND_HELD, MODE_ON): +#else + // in SA22C_NO_LOCKUP_HOLD mode, multi-blaster is triggered by holding aux + // while swinging + case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_ON | BUTTON_AUX): #endif -#if NUM_BUTTONS > 1 - StartOrStopTrack(); #endif - return true; - - case EVENTID(BUTTON_AUX, EVENT_CLICK_LONG, MODE_OFF): - if (mode_volume_){ - mode_volume_ = false; - beeper.Beep(0.5, 3000); - STDOUT.println("Exit Volume Menu"); - } - else{ - mode_volume_ = true; - beeper.Beep(0.5, 3000); - STDOUT.println("Enter Volume Menu"); + if (!swing_blast_) { + swing_blast_ = true; + hybrid_font.SB_Blast(); + } else { + swing_blast_ = false; + hybrid_font.SB_Blast(); } return true; - case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_OFF): - SaberBase::RequestMotion(); - return true; + case EVENTID(BUTTON_NONE, EVENT_SWING, MODE_ON): + if (swing_blast_) { + SaberBase::DoBlast(); + } + return true; - case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_OFF | BUTTON_POWER): - if (!mode_volume_) { - next_preset(); +// Lockup +#if NUM_BUTTONS == 1 + // 1 button lockup + case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_POWER): +#else +#ifndef SA22C_NO_LOCKUP_HOLD + // 2 and 3 button lockup + case EVENTID(BUTTON_AUX, EVENT_FIRST_HELD, MODE_ON): +#else + case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_ON | BUTTON_AUX): +#endif +#endif + if (!SaberBase::Lockup()) { + if (pointing_down_) { + SaberBase::SetLockup(SaberBase::LOCKUP_DRAG); + } else { + SaberBase::SetLockup(SaberBase::LOCKUP_NORMAL); } + swing_blast_ = false; + SaberBase::DoBeginLockup(); return true; + } + break; +// Lightning Block #if NUM_BUTTONS < 3 - case EVENTID(BUTTON_POWER, EVENT_HELD_LONG, MODE_OFF): + // 1 and 2 button + case EVENTID(BUTTON_POWER, EVENT_SECOND_HELD, MODE_ON): +#else + // 3 button + case EVENTID(BUTTON_AUX2, EVENT_FIRST_HELD, MODE_ON): #endif - if (!mode_volume_) { - previous_preset(); - } - return true; - case EVENTID(BUTTON_AUX, EVENT_HELD_LONG, MODE_OFF): - talkie.SayDigit((int)floorf(battery_monitor.battery())); - talkie.Say(spPOINT); - talkie.SayDigit(((int)floorf(battery_monitor.battery() * 10)) % 10); - talkie.SayDigit(((int)floorf(battery_monitor.battery() * 100)) % 10); - talkie.Say(spVOLTS); + SaberBase::SetLockup(SaberBase::LOCKUP_LIGHTNING_BLOCK); + swing_blast_ = false; + SaberBase::DoBeginLockup(); + return true; + +// Melt + case EVENTID(BUTTON_NONE, EVENT_STAB, MODE_ON | BUTTON_POWER): + if (!SaberBase::Lockup()) { + SaberBase::SetLockup(SaberBase::LOCKUP_MELT); + swing_blast_ = false; + SaberBase::DoBeginLockup(); return true; + } + break; - case EVENTID(BUTTON_AUX2, EVENT_CLICK_SHORT, MODE_OFF): -#if NUM_BUTTONS == 3 - if (mode_volume_) { - ChangeVolume(false); - } +// Start or Stop Track +#if NUM_BUTTONS == 1 + // 1 button + case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, MODE_OFF): + StartOrStopTrack(); + return true; #endif -#ifdef DUAL_POWER_BUTTONS - if (!mode_volume_) { - next_preset(); - } + case EVENTID(BUTTON_POWER, EVENT_PRESSED, MODE_OFF): + case EVENTID(BUTTON_AUX, EVENT_PRESSED, MODE_OFF): + case EVENTID(BUTTON_AUX2, EVENT_PRESSED, MODE_OFF): + SaberBase::RequestMotion(); + return true; + +// Enter Volume MENU +#if NUM_BUTTONS == 1 + // 1 button + case EVENTID(BUTTON_NONE, EVENT_CLASH, MODE_OFF | BUTTON_POWER): #else - if (!mode_volume_) { - previous_preset(); - } + // 2 and 3 button + case EVENTID(BUTTON_AUX, EVENT_FIRST_CLICK_LONG, MODE_OFF): #endif - return true; + if (!mode_volume_) { + mode_volume_ = true; + beeper.Beep(0.5, 3000); + STDOUT.println("Enter Volume Menu"); + } else { + mode_volume_ = false; + beeper.Beep(0.5, 3000); + STDOUT.println("Exit Volume Menu"); + } + return true; + +// Battery level +#if NUM_BUTTONS == 1 + // 1 button + case EVENTID(BUTTON_POWER, EVENT_THIRD_SAVED_CLICK_SHORT, MODE_OFF): +#else + // 2 and 3 button + case EVENTID(BUTTON_AUX, EVENT_FIRST_HELD_LONG, MODE_OFF): +#endif + talkie.SayDigit((int)floorf(battery_monitor.battery())); + talkie.Say(spPOINT); + talkie.SayDigit(((int)floorf(battery_monitor.battery() * 10)) % 10); + talkie.SayDigit(((int)floorf(battery_monitor.battery() * 100)) % 10); + talkie.Say(spVOLTS); + return true; #ifdef BLADE_DETECT_PIN case EVENTID(BUTTON_BLADE_DETECT, EVENT_LATCH_ON, MODE_ANY_BUTTON | MODE_ON): @@ -366,9 +444,9 @@ class SaberSA22CButtons : public PropBase { return false; } private: - bool aux_on_ = true; bool pointing_down_ = false; bool mode_volume_ = false; + bool swing_blast_ = false; }; #endif diff --git a/styles/responsive_styles.h b/styles/responsive_styles.h index ae7fd0995..cc3f33f49 100644 --- a/styles/responsive_styles.h +++ b/styles/responsive_styles.h @@ -15,7 +15,7 @@ template,Int<4000>,Int<28000>>, + class TOP = Scale,Int<4000>,Int<26000>>, class BOTTOM = Int<6000>, class SIZE = Scale,Int<9000>,Int<14000>>> using ResponsiveLockupL = @@ -81,7 +81,7 @@ using ResponsiveLightningBlockL = template, - class TOP = Scale,Int<4000>,Int<28000>>, + class TOP = Scale,Int<4000>,Int<26000>>, class BOTTOM = Int<6000>, class SIZE = Int<10000>> using ResponsiveClashL =