├── .gitattributes ├── blades ├── .cvsignore ├── Makefile ├── led_interface.h ├── README.md ├── blade_wrapper.h ├── abstract_blade.h ├── blades.ino └── dim_blade.h ├── config ├── .cvsignore ├── proffieboard_config.h ├── teensy_audio_shield_audiofx.h ├── proffieboard_v2_testing_config.h ├── hev_config.h ├── teensy_audio_shield_micom.h ├── blaster_v3_config.h ├── default_v3_config.h ├── proffieboard_v1_graflex.h ├── td_proffieboard_config.h ├── owk_v2_config.h ├── proffieboard_v3_verification_config.h ├── graflex_v1_config.h └── toy_saber_config.h ├── common ├── .cvsignore ├── preset.h ├── Makefile ├── blade_config.h ├── saber_base_passthrough.h ├── math.h ├── loop_counter.h ├── common.h ├── cyclint.h ├── scoped_cycle_counter.h ├── circular_buffer.h ├── ref.h ├── profiling.h ├── linked_ptr.h ├── bitfield.h ├── state_machine.h ├── box_filter.h ├── range.h ├── clock_control.h ├── string_piece.h ├── capabilities.h └── resources.h ├── sound ├── .cvsignore ├── audiostream.h ├── waveform_sampler.h ├── Makefile ├── smooth_swing_cfx_config.h ├── smooth_swing_config.h ├── click_avoider_lin.h └── beeper.h ├── doc ├── FET_numbers.jpg ├── Lightsaber_bb.png ├── pl9823_string.png ├── v3_soldered.jpg ├── TeensySaberV3Front.jpg ├── _DSC1630_CROPPED.JPG ├── charging_adapter.png └── led_star_circuit.png ├── pov_tools └── create_POV_data_files-Win.bat ├── buttons ├── Makefile ├── README.md ├── debounced_button.h ├── floating_button.h ├── fast_button.h ├── button.h └── latching_button.h ├── display └── Makefile ├── styles ├── Makefile ├── random_flicker.h ├── random_per_led_flicker.h ├── rainbow.h ├── hump_flicker.h ├── audio_flicker.h ├── colorchange.h ├── brown_noise_flicker.h ├── README.md ├── effect_sequence.h ├── rgb_cycle.h ├── pulsing.h ├── gradient.h ├── random_blink.h ├── on_spark.h ├── rotate_color.h ├── byteorder.h ├── sparkle.h ├── strobe.h ├── blade_shortener.h ├── pixelate.h ├── transition_pulse.h ├── remap.h ├── blinking.h ├── charging.h ├── retraction_delay.h ├── ignition_delay.h ├── rgb_arg.h ├── length_finder.h ├── sequence.h ├── inout_sparktip.h ├── rgb.h └── color_select.h ├── fontconvert └── Makefile ├── pqoi ├── Makefile └── cpqoi.cc ├── transitions ├── instant.h ├── delay.h ├── README.md ├── select.h ├── sequence.h ├── boing.h ├── extend.h ├── blink.h ├── fade.h ├── join.h └── colorcycle.h ├── .github └── workflows │ └── RunTests.yml ├── functions ├── ramp.h ├── battery_level.h ├── center_dist.h ├── volume_level.h ├── wavnum.h ├── int.h ├── layer_functions.h ├── blaster_mode.h ├── random_blink.h ├── variation.h ├── sequence.h ├── linear_section.h ├── random.h ├── on_spark.h ├── subtract.h ├── clash_impact.h ├── svf.h ├── hold_peak.h ├── effect_position.h ├── time_since_effect.h ├── change_slowly.h ├── islessthan.h ├── wavlen.h ├── int_arg.h ├── swing_speed.h ├── smoothstep.h ├── mod.h ├── sound_level.h ├── sum.h ├── blade_angle.h ├── divide.h ├── twist_angle.h ├── brown_noise.h ├── bullet_count.h ├── isbetween.h ├── clamp.h ├── mult.h ├── sparkle.h ├── readpin.h ├── strobe.h ├── blinking.h ├── circular_section.h ├── scale.h └── marble.h ├── videotoblc ├── Makefile └── timing.h ├── ir ├── print.h └── ir.h ├── scripts ├── test_motion_timeout.h ├── clash_recorder_config.h └── v3_test_script.h ├── .cvsignore ├── modes ├── top_menu_wrapper.h ├── menu_base.h ├── select_cancel_mode.h ├── mode.h └── bool_setting.h ├── README.md └── motion └── motion_util.h /.gitattributes: -------------------------------------------------------------------------------- 1 | lightsaber.ino ident 2 | -------------------------------------------------------------------------------- /blades/.cvsignore: -------------------------------------------------------------------------------- 1 | ws2811_serial_blade.h 2 | -------------------------------------------------------------------------------- /config/.cvsignore: -------------------------------------------------------------------------------- 1 | foo_config.h 2 | graflex_v1_config_test.h 3 | -------------------------------------------------------------------------------- /common/.cvsignore: -------------------------------------------------------------------------------- 1 | preset.ini 2 | presets.ini 3 | presets.tmp 4 | tests 5 | -------------------------------------------------------------------------------- /sound/.cvsignore: -------------------------------------------------------------------------------- 1 | bank_open_failure.wav 2 | dac.h.broken 3 | talkie_test 4 | zero.wav 5 | -------------------------------------------------------------------------------- /doc/FET_numbers.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/profezzorn/ProffieOS/HEAD/doc/FET_numbers.jpg -------------------------------------------------------------------------------- /doc/Lightsaber_bb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/profezzorn/ProffieOS/HEAD/doc/Lightsaber_bb.png -------------------------------------------------------------------------------- /doc/pl9823_string.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/profezzorn/ProffieOS/HEAD/doc/pl9823_string.png -------------------------------------------------------------------------------- /doc/v3_soldered.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/profezzorn/ProffieOS/HEAD/doc/v3_soldered.jpg -------------------------------------------------------------------------------- /doc/TeensySaberV3Front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/profezzorn/ProffieOS/HEAD/doc/TeensySaberV3Front.jpg -------------------------------------------------------------------------------- /doc/_DSC1630_CROPPED.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/profezzorn/ProffieOS/HEAD/doc/_DSC1630_CROPPED.JPG -------------------------------------------------------------------------------- /doc/charging_adapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/profezzorn/ProffieOS/HEAD/doc/charging_adapter.png -------------------------------------------------------------------------------- /doc/led_star_circuit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/profezzorn/ProffieOS/HEAD/doc/led_star_circuit.png -------------------------------------------------------------------------------- /pov_tools/create_POV_data_files-Win.bat: -------------------------------------------------------------------------------- 1 | start powershell -noexit -command "bash create_POV_data_files" 2 | -------------------------------------------------------------------------------- /buttons/Makefile: -------------------------------------------------------------------------------- 1 | test: tests 2 | ./tests 3 | 4 | tests: tests.cpp 5 | g++ -O -g -std=c++11 -MD -MP -o tests tests.cpp -lm 6 | 7 | -include *.d 8 | -------------------------------------------------------------------------------- /display/Makefile: -------------------------------------------------------------------------------- 1 | test: tests 2 | ./tests 3 | 4 | tests: tests.cpp 5 | g++ -O -g -std=c++11 -MD -MP -o tests tests.cpp -lm 6 | 7 | -include *.d 8 | -------------------------------------------------------------------------------- /blades/Makefile: -------------------------------------------------------------------------------- 1 | test: tests 2 | ./tests 3 | 4 | tests: tests.cpp drive_logic.h 5 | g++ -O -g -std=c++11 -MD -MP -o tests tests.cpp -lm 6 | 7 | -include *.d 8 | 9 | -------------------------------------------------------------------------------- /buttons/README.md: -------------------------------------------------------------------------------- 1 | # Buttons 2 | 3 | This directory contains the code that implements buttons in ProffieOS. 4 | For more information on how to configure buttons, go to: 5 | https://github.com/profezzorn/ProffieOS/wiki/The-CONFIG_BUTTONS-section 6 | -------------------------------------------------------------------------------- /styles/Makefile: -------------------------------------------------------------------------------- 1 | test: tests 2 | ./tests 3 | 4 | tests: tests.cpp style_parser.h 5 | # clang++ -std=c++11 -g -fsanitize=address -fno-omit-frame-pointer -MD -MP -o tests tests.cpp -lm 6 | g++ -O -g -std=c++14 -MD -MP -o tests tests.cpp -lm 7 | 8 | -include *.d 9 | 10 | -------------------------------------------------------------------------------- /fontconvert/Makefile: -------------------------------------------------------------------------------- 1 | all: fontconvert 2 | 3 | CC = gcc 4 | CFLAGS = -g -Wall -I/usr/local/include/freetype2 -I/usr/include/freetype2 -I/usr/include 5 | LIBS = -lfreetype 6 | 7 | fontconvert: fontconvert.c 8 | $(CC) $(CFLAGS) $< $(LIBS) -o $@ 9 | 10 | clean: 11 | rm -f fontconvert 12 | -------------------------------------------------------------------------------- /config/proffieboard_config.h: -------------------------------------------------------------------------------- 1 | #if PROFFIEBOARD_VERSION - 0 == 1 2 | #include "proffieboard_v1_config.h" 3 | #elif PROFFIEBOARD_VERSION - 0 == 2 4 | #include "proffieboard_v2_config.h" 5 | #elif PROFFIEBOARD_VERSION - 0 == 3 6 | #include "proffieboard_v3_config.h" 7 | #else 8 | #error UNKNOWN PROFFIEBOARD 9 | #endif 10 | 11 | -------------------------------------------------------------------------------- /blades/led_interface.h: -------------------------------------------------------------------------------- 1 | #ifndef BLADES_LED_INTERFACE_H 2 | #define BLADES_LED_INTERFACE_H 3 | 4 | // LED interface. 5 | class LEDInterface { 6 | public: 7 | // Given a color, return a the right PWM level (0-65535). 8 | virtual int PWM(Color16 c) = 0; 9 | 10 | // Same as PWM(), but ignores battery voltage. 11 | virtual int PWM_overdrive(Color16 c) = 0; 12 | }; 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /pqoi/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS= -pthread -MD -ggdb -O2 2 | CXXFLAGS=$(CFLAGS) 3 | LDFLAGS=-lm -lpthread 4 | 5 | COMMON=pqoiml.h pamstream.h stb_image_write.h 6 | HOST_COMMON= 7 | BINARIES=cpqoi dpqoi 8 | 9 | all: $(BINARIES) 10 | 11 | clean: 12 | rm $(BINARIES) *.o *.d 13 | 14 | $(BINARIES) : % : %.o $(COMMON) $(HOST_COMMON) 15 | g++ $(CXXFLAGS) -o $@ $< $(LDFLAGS) 16 | 17 | -include *.d 18 | 19 | -------------------------------------------------------------------------------- /transitions/instant.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_INSTANT_H 2 | #define TRANSITIONS_INSTANT_H 3 | 4 | // Usage: TrInstant 5 | // return value: TRANSITION 6 | // Instant transition. 7 | 8 | class TrInstant { 9 | public: 10 | bool done() { return true; } 11 | void begin() {} 12 | void run(BladeBase* blade) {} 13 | template 14 | auto getColor(const A& a, const B& b, int led) AUTO_RETURN(b) 15 | }; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /.github/workflows/RunTests.yml: -------------------------------------------------------------------------------- 1 | name: C/C++ CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v1 12 | - name: test styles 13 | run: cd styles && make test 14 | - name: test common 15 | run: cd common && make test 16 | - name: test blades 17 | run: cd blades && make test 18 | - name: test sound 19 | run: cd sound && make test 20 | -------------------------------------------------------------------------------- /sound/audiostream.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_AUDIOSTREAM_H 2 | #define SOUND_AUDIOSTREAM_H 3 | 4 | #include 5 | 6 | class ProffieOSAudioStream { 7 | public: 8 | virtual int read(int16_t* data, int elements) = 0; 9 | // There is no need to call eof() unless read() returns zero elements. 10 | virtual bool eof() const { return false; } 11 | // Cannot be called at the same time as read(). 12 | virtual void StopFromReader() {} 13 | }; 14 | 15 | #endif 16 | -------------------------------------------------------------------------------- /common/preset.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_PRESET_H 2 | #define COMMON_PRESET_H 3 | 4 | #include "malloc_helper.h" 5 | #include "onceperblade.h" 6 | 7 | #define CONFIGARRAY(X) X, NELEM(X) 8 | 9 | 10 | struct Preset { 11 | // Sound font. 12 | const char* font; 13 | 14 | // Sound track 15 | const char* track; 16 | 17 | // Blade config. 18 | #define DEFINE_BLADE_STYLES(N) StyleAllocator style_allocator##N; 19 | ONCEPERBLADE(DEFINE_BLADE_STYLES); 20 | 21 | const char* name; 22 | }; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /functions/ramp.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_RAMP_H 2 | #define FUNCTIONS_RAMP_H 3 | 4 | // Usage: RampF 5 | // Returns 0 at base and 32768 at tip. 6 | // Example: Mix 7 | // This would do the same thing as Gradient 8 | 9 | class RampF { 10 | public: 11 | void run(BladeBase* blade) { 12 | num_leds_ = blade->num_leds(); 13 | } 14 | int getInteger(int led) { return led * 32768 / num_leds_; } 15 | 16 | private: 17 | int num_leds_; 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /common/Makefile: -------------------------------------------------------------------------------- 1 | test: tests test2 2 | ./tests 3 | ./test2 4 | ./unlock_presets_ini.py 5 | 6 | presets.ini: tests 7 | ./tests 8 | 9 | unlock: presets.ini 10 | ./unlock_presets_ini.py 11 | 12 | # clang++ -std=c++11 -O1 -g -fsanitize=address -fno-omit-frame-pointer -MD -MP -o tests tests.cpp -lm 13 | 14 | tests: tests.cpp stdout.h Makefile 15 | g++ -O -ggdb -std=c++11 -MD -MP -o tests tests.cpp -lm 16 | 17 | test2: test2.cpp stdout.h 18 | g++ -O -ggdb -std=c++11 -MD -MP -o test2 test2.cpp -lm 19 | 20 | -include *.d 21 | -------------------------------------------------------------------------------- /functions/battery_level.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_BATTERY_LEVEL_H 2 | #define FUNCTIONS_BATTERY_LEVEL_H 3 | 4 | #include "svf.h" 5 | 6 | // Usage: BatteryLevel 7 | // Returns 0-32768 based on battery level. 8 | // returned value: INTEGER 9 | 10 | class BatteryLevelSVF { 11 | public: 12 | void run(BladeBase* blade) {} 13 | int calculate(BladeBase* blade) { 14 | return clampi32(battery_monitor.battery_percent() * 32768 / 100, 0, 32768); 15 | } 16 | }; 17 | 18 | using BatteryLevel = SingleValueAdapter; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /styles/random_flicker.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_RANDOM_FLICKER_H 2 | #define STYLES_RANDOM_FLICKER_H 3 | 4 | // Usage: RandomFlicker 5 | // Or: RandomL 6 | // A, B: COLOR 7 | // return value: COLOR 8 | // Mixes randomly between A and B. 9 | // mix is even over entire blade. 10 | 11 | #include "../functions/random.h" 12 | #include "alpha.h" 13 | #include "layers.h" 14 | 15 | template 16 | using RandomL = AlphaL; 17 | 18 | template 19 | using RandomFlicker = Layers>; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /videotoblc/Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS= -pthread -MD -ggdb -O2 2 | CXXFLAGS=$(CFLAGS) 3 | LDFLAGS=-lm -lpthread 4 | 5 | COMMON= 6 | HOST_COMMON= 7 | BINARIES=videotoblc 8 | 9 | all: $(BINARIES) 10 | 11 | clean: 12 | rm $(BINARIES) *.o *.d 13 | 14 | $(BINARIES) : % : %.o $(COMMON) $(HOST_COMMON) 15 | g++ $(CXXFLAGS) -o $@ $^ $(LDFLAGS) 16 | 17 | spectrum.blc: videotoblc /home/hubbe/hack/teensy/chaosdisplay/video/the_spectrum_song.mp4 18 | ./videotoblc /home/hubbe/hack/teensy/chaosdisplay/video/the_spectrum_song.mp4 >spectrum.blc 19 | 20 | -include *.d 21 | 22 | -------------------------------------------------------------------------------- /common/blade_config.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_BLADE_CONFIG_H 2 | #define COMMON_BLADE_CONFIG_H 3 | 4 | class BladeBase; 5 | 6 | struct BladeConfig { 7 | // Blade identifier resistor. 8 | int ohm; 9 | 10 | // Blade driver. 11 | #define DEFINE_BLADES(N) BladeBase* blade##N; 12 | ONCEPERBLADE(DEFINE_BLADES); 13 | 14 | // Blade presets 15 | Preset* presets; 16 | size_t num_presets; 17 | 18 | const char* save_dir; 19 | }; 20 | 21 | extern BladeConfig* current_config; 22 | 23 | struct RFID_Command { 24 | uint64_t id; 25 | const char* cmd; 26 | const char* arg; 27 | }; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /ir/print.h: -------------------------------------------------------------------------------- 1 | #ifndef IR_PRINT_H 2 | #define IR_PRINT_H 3 | 4 | class PrintDecoder : public IRDecoder { 5 | public: 6 | const char* name() override { return "PrintDecoder"; } 7 | 8 | void signal(bool high, uint32_t length) override { 9 | bool valid = length > 300 && length < 6000; 10 | if (valid || last_valid_) { 11 | STDOUT.print("IR: "); 12 | STDOUT.print(high ? "hi" : "lo"); 13 | STDOUT.print(" for "); 14 | STDOUT.print(length); 15 | STDOUT.println(" us"); 16 | } 17 | last_valid_ = valid; 18 | } 19 | private: 20 | bool last_valid_ = false; 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /styles/random_per_led_flicker.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_RANDOM_PER_LED_FLICKER_H 2 | #define STYLES_RANDOM_PER_LED_FLICKER_H 3 | 4 | // Usage: RandomPerLEDFlicker 5 | // Or: RandomPerLEDFlickerL 6 | // A, B: COLOR 7 | // return value: COLOR 8 | // Mixes randomly between A and B. 9 | // mix is chosen individually for every LED. 10 | 11 | #include "../functions/random.h" 12 | #include "alpha.h" 13 | #include "layers.h" 14 | 15 | template 16 | using RandomPerLEDFlickerL = AlphaL; 17 | 18 | template 19 | using RandomPerLEDFlicker = Layers>; 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /functions/center_dist.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_CENTER_DIST_H 2 | #define FUNCTIONS_CENTER_DIST_H 3 | 4 | // Usage: CenterDistF
5 | // CENTER : FUNCTION (defaults to Int<16384>) 6 | // Returns distance from center, a full blade length counts as 32768. 7 | 8 | template> 9 | class CenterDistF { 10 | public: 11 | void run(BladeBase* blade) { 12 | center_.run(blade); 13 | num_leds_ = blade->num_leds(); 14 | } 15 | int getInteger(int led) { return abs(led * 32768 / num_leds_ - center_.getInteger(led)); } 16 | 17 | private: 18 | PONUA CENTER center_; 19 | int num_leds_; 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /functions/volume_level.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_VOLUME_LEVEL_H 2 | #define FUNCTIONS_VOLUME_LEVEL_H 3 | 4 | #include "svf.h" 5 | 6 | #ifdef ENABLE_AUDIO 7 | 8 | // Usage: VolumeLevel 9 | // Returns 0-32768 based on volume level. 10 | // returned value: INTEGER 11 | 12 | class VolumeLevelSVF { 13 | public: 14 | void run(BladeBase* blade) {} 15 | int calculate(BladeBase* blade) { 16 | return dynamic_mixer.get_volume() * 32768 / VOLUME; 17 | } 18 | }; 19 | 20 | using VolumeLevel = SingleValueAdapter; 21 | 22 | #else // ENABLE_AUDIO 23 | 24 | using VolumeLevel = Int<0>; 25 | 26 | #endif // ENABLE_AUDIO 27 | #endif 28 | -------------------------------------------------------------------------------- /styles/rainbow.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_RAINBOW_H 2 | #define STYLES_RAINBOW_H 3 | 4 | // Usage: Rainbow 5 | // return value: COLOR 6 | // Basic RGB rainbow. 7 | 8 | class Rainbow { 9 | public: 10 | void run(BladeBase* base) { 11 | m = millis(); 12 | } 13 | SimpleColor getColor(int led) { 14 | Color16 c(std::max(0, (sin_table[((m * 3 + led * 50)) & 0x3ff] << 2)), 15 | std::max(0, (sin_table[((m * 3 + led * 50 + 1024 / 3)) & 0x3ff] << 2)), 16 | std::max(0, (sin_table[((m * 3 + led * 50 + 1024 * 2 / 3)) & 0x3ff] << 2))); 17 | return SimpleColor(c); 18 | } 19 | private: 20 | uint32_t m; 21 | }; 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /videotoblc/timing.h: -------------------------------------------------------------------------------- 1 | #ifndef TIMING_H 2 | #define TIMING_H 3 | #include 4 | #include 5 | 6 | static inline struct timeval Time() { 7 | struct timeval t; 8 | gettimeofday(&t, NULL); 9 | return t; 10 | } 11 | 12 | static inline double operator-(struct timeval a, struct timeval b) { 13 | return (a.tv_sec - b.tv_sec) + (a.tv_usec - b.tv_usec)/1000000.0; 14 | } 15 | 16 | static inline void Sleep(double t) { 17 | if (t > 0.0) { 18 | struct timeval tv; 19 | tv.tv_sec = (int)floor(t); 20 | tv.tv_usec = (int)floor((t - tv.tv_sec) * 1000000); 21 | select(0, NULL, NULL, NULL, &tv); 22 | } 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /sound/waveform_sampler.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_WAVEFORM_SAMPLER_H 2 | #define SOUND_WAVEFORM_SAMPLER_H 3 | 4 | struct WaveForm { 5 | int16_t table_[1024]; 6 | }; 7 | 8 | struct WaveFormSampler { 9 | WaveFormSampler(const WaveForm& waveform) : waveform_(waveform.table_), pos_(0), delta_(0) {} 10 | WaveFormSampler(const int16_t* waveform) : waveform_(waveform), pos_(0), delta_(0) {} 11 | const int16_t *waveform_; 12 | int pos_; 13 | volatile int delta_; 14 | int16_t next() { 15 | pos_ += delta_; 16 | if (pos_ > 1024 * 65536) pos_ -= 1024 * 65536; 17 | // Bilinear lookup here? 18 | return waveform_[pos_ >> 16]; 19 | } 20 | }; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /styles/hump_flicker.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_HUMP_FLICKER_H 2 | #define STYLES_HUMP_FLICKER_H 3 | 4 | // Usage: HumpFlicker 5 | // Or: HumpFlickerL 6 | // A, B: COLOR 7 | // HUMP_WIDTH: a number 8 | // return value: COLOR 9 | // Makes a random "hump" which is about 2xHUMP_WIDTH leds wide. 10 | 11 | class BladeBase; 12 | 13 | #include "../functions/bump.h" 14 | #include "alpha.h" 15 | #include "layers.h" 16 | 17 | template 18 | using HumpFlickerL = AlphaL>; 19 | template 20 | using HumpFlicker = Layers>; 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /scripts/test_motion_timeout.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTS_TEST_MOTION_TIMEOUT_H 2 | #define SCRIPTS_TEST_MOTION_TIMEOUT_H 3 | 4 | class MotionTimeoutScript : Looper, StateMachine { 5 | public: 6 | const char* name() override { return "TestMotionTimeoutScript"; } 7 | void Loop() override { 8 | STATE_MACHINE_BEGIN(); 9 | SLEEP(5000); 10 | while(1) { 11 | SaberBase::last_motion_request_ = millis() - 100000; // 100 seconds ago, should trigger timeout 12 | SLEEP(1000); // Motion should be timed out. 13 | SaberBase::RequestMotion(); // Start up motion again. 14 | SLEEP(1000); 15 | } 16 | STATE_MACHINE_END(); 17 | } 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /blades/README.md: -------------------------------------------------------------------------------- 1 | # Blades 2 | 3 | In ProffieOS, anything that can light up is a "blade". Originally, ProffieOS was used to control lightsabers, and the blade was the only thing that we worried about. Over time, people started adding accent lights, button lights, additional blades and lots of other stuff, and it was easier to just all call them "blades" than to invent new control code for each kind of thing. 4 | 5 | In this directory you will find the code for all the blades, including the underlying hardware drivers. 6 | If you're looking for information about how to use blades, you may want to check out this page: https://github.com/profezzorn/ProffieOS/wiki/The-CONFIG_PRESETS-section 7 | -------------------------------------------------------------------------------- /styles/audio_flicker.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_AUDIO_FLICKER_H 2 | #define STYLES_AUDIO_FLICKER_H 3 | 4 | // Usage: AudioFlicker 5 | // Or: AudioFlickerL 6 | // A, B: COLOR 7 | // return value: COLOR 8 | // Mixes between A and B based on audio. Quiet audio 9 | // means more A, loud audio means more B. 10 | // Based on a single sample instead of an average to make it flicker. 11 | 12 | class BladeBase; 13 | 14 | #include "alpha.h" 15 | #include "../functions/sound_level.h" 16 | #include "layers.h" 17 | 18 | template 19 | using AudioFlickerL = AlphaL; 20 | 21 | template 22 | using AudioFlicker = Layers>; 23 | 24 | #endif 25 | -------------------------------------------------------------------------------- /.cvsignore: -------------------------------------------------------------------------------- 1 | *.zip 2 | Arduino-Makefile-1.6.0 3 | audio 4 | build-Proffieboard-L433CC 5 | build-dragonfly 6 | build-proffieboard 7 | build-teensy31 8 | build-teensy35 9 | build-teensy36 10 | common_presets.h.test 11 | lightsaber.d 12 | lightsaber.ino-1.94 13 | lightsaber.ino.bak2 14 | lightsaber.ino.broken 15 | lightsaber.ino.fixes 16 | lightsaber.ino.funky 17 | lightsaber.ino.junk 18 | lightsaber.ino.reverted 19 | lightsaber.ino2 20 | test-proffieboard-default 21 | test-teensy31-crossguard-v3 22 | test-teensy31-default-v3 23 | test-teensy31-fastled-v1 24 | test-teensy31-graflex-v1 25 | test-teensy31-owk-v2 26 | test-teensy31-test-v3 27 | test-teensy31-toy-v3 28 | test-teensy35-default-v3 29 | test-teensy36-default-v3 30 | -------------------------------------------------------------------------------- /scripts/clash_recorder_config.h: -------------------------------------------------------------------------------- 1 | #ifdef CONFIG_PROP 2 | #ifndef PROP_TYPE 3 | #include "../props/saber.h" 4 | #endif 5 | #include "clash_recorder.h" 6 | using PROP_TYPE_TMP = PROP_TYPE; 7 | #undef PROP_TYPE 8 | #define PROP_TYPE ClashRecorder 9 | #endif 10 | 11 | #ifdef CONFIG_PRESETS 12 | 13 | class ShowLocationF { 14 | public: 15 | void run(BladeBase *blade) {} 16 | int getInteger(int led) { return pos_; } 17 | static void SetPos(int x) { pos_ = x; } 18 | private: 19 | static int pos_; 20 | }; 21 | 22 | int ShowLocationF::pos_ = 0; 23 | 24 | using ClashRecorderStyle = InOutHelper< 25 | EasyBlade< Layers>>>, White>, 26 | 800, 300>; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /styles/colorchange.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_COLORCHANGE_H 2 | #define STYLES_COLORCHANGE_H 3 | 4 | #include "../functions/variation.h" 5 | #include "color_select.h" 6 | 7 | // Usage: ColorChange 8 | // TRANSITION: transition 9 | // COLOR1, COLOR2, ...: COLOR 10 | // Return value: COLOR 11 | // Decides what color to return based on the current variation. 12 | // The returned color will be current_variation % N (where N is the number of colors arguments). 13 | // When the variation changes, the transition will be used to change from the old color to the new color. 14 | 15 | template 16 | using ColorChange = ColorSelect; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /styles/brown_noise_flicker.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_BROWN_NOISE_FLICKER_H 2 | #define STYLES_BROWN_NOISE_FLICKER_H 3 | 4 | // Usage: BrownNoiseFlicker 5 | // Or: BrownNoiseFlickerL 6 | // A, B: COLOR 7 | // grade: int 8 | // return value: COLOR 9 | // Randomly selects between A and B, but keeps nearby 10 | // pixels looking similar. 11 | 12 | class BladeBase; 13 | 14 | #include "alpha.h" 15 | #include "layers.h" 16 | #include "../functions/brown_noise.h" 17 | #include "../functions/int.h" 18 | 19 | template 20 | using BrownNoiseFlickerL = AlphaL>; 21 | 22 | template 23 | using BrownNoiseFlicker = Layers>>; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /styles/README.md: -------------------------------------------------------------------------------- 1 | # Styles 2 | 3 | Style templates go in this directory. Styles are used to calculate the color for each pixel on the blade. 4 | Note that most styles takes other styles as arguments, and even basic colors like "RED" are really 5 | style templates. This means that colors can easily be replaced with much more complicated and dynamic 6 | effects. Each style template needs at least two functions: 7 | 8 | # void run(BladeBase* blade); 9 | 10 | Called to start a frame. 11 | It's ok to put semi-complicated calculations here, but not in getColor. 12 | 13 | # OverDriveColor getColor(int led) 14 | 15 | Called once per LED on the blade to calculate the color for that led. 16 | Generally it's best to avoid complicated calculations in getColor since 17 | it will be called thousands of times per second. 18 | -------------------------------------------------------------------------------- /styles/effect_sequence.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_EFFECT_SEQUENCE_H 2 | #define STYLES_EFFECT_SEQUENCE_H 3 | 4 | //Usage EffectSequence 5 | //Create a sequence of Colors based on number of times the effect is used. 6 | 7 | template 8 | class EffectSequence { 9 | public: 10 | void run(BladeBase* blade) { 11 | SaveLastDetectedBladeEffectScoped save; 12 | colors_.run(blade); 13 | if (effect_.Detect(blade)) { 14 | n_ = (n_ + 1) % sizeof...(COLORS); 15 | } 16 | } 17 | private: 18 | OneshotEffectDetector effect_; 19 | int n_ = -1; 20 | MixHelper colors_; 21 | 22 | public: 23 | auto getColor(int led) -> decltype(colors_.getColor(n_, led)) { 24 | return colors_.getColor(n_, led); 25 | } 26 | }; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /common/saber_base_passthrough.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_SABER_BASE_PASSTHROUGH_H 2 | #define COMMON_SABER_BASE_PASSTHROUGH_H 3 | 4 | class SaberBasePassThrough : public SaberBase { 5 | public: 6 | SaberBasePassThrough() : SaberBase(NOLINK) {} 7 | protected: 8 | void SetDelegate(SaberBase* delegate) { 9 | Unlink(this); 10 | if (delegate_) { 11 | SaberBase::Link(delegate_); 12 | } 13 | delegate_ = delegate; 14 | if (delegate_) { 15 | SaberBase::Unlink(delegate_); 16 | SaberBase::Link(this); 17 | } 18 | } 19 | #define SABERFUN(NAME, TYPED_ARGS, ARGS) \ 20 | void SB_##NAME TYPED_ARGS override { \ 21 | delegate_->SB_##NAME ARGS; \ 22 | } 23 | 24 | SABERBASEFUNCTIONS(); 25 | #undef SABERFUN 26 | 27 | SaberBase* delegate_ = NULL; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /pqoi/cpqoi.cc: -------------------------------------------------------------------------------- 1 | #include "pqoi.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "pamstream.h" 9 | #include "pqoiml.h" 10 | 11 | 12 | int main(int argc, char **argv) { 13 | Pqoiml pqoiml; 14 | std::string filename(argv[1]); 15 | std::string scaling_commands; 16 | if (argc > 2) { 17 | pqoiml.set_default_scaling_commands(argv[2]); 18 | } 19 | 20 | if (endswith(filename, ".pqoiml")) { 21 | pqoiml.parseFile(filename); 22 | } else { 23 | assert(pqoiml.parse("label start")); 24 | assert(pqoiml.parse("file "+filename)); 25 | if (getfps(filename).first != 0) assert(pqoiml.parse("goto start")); 26 | } 27 | 28 | // pqoiml.dump(stderr); 29 | 30 | Generator generator; 31 | pqoiml.Generate(&generator); 32 | generator.Output(stdout); 33 | } 34 | 35 | -------------------------------------------------------------------------------- /modes/top_menu_wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef MODE_TOP_MENU_WRAPPER_H 2 | #define MODE_TOP_MENU_WRAPPER_H 3 | 4 | namespace mode { 5 | 6 | // Just makes it say "exit" when you exit the menu system. 7 | template 8 | class TopMenuWrapper: public MENU { 9 | void mode_activate(bool onreturn) override { 10 | if (!onreturn) { 11 | volHum_ = font_config.volHum; 12 | volEff_ = font_config.volEff; 13 | font_config.volHum = 1; 14 | font_config.volEff = 1; 15 | } 16 | MENU::mode_activate(onreturn); 17 | } 18 | void exit() override { 19 | MENU::exit(); 20 | getSL()->SayExit(); 21 | font_config.volHum = volHum_; 22 | font_config.volEff = volEff_; 23 | 24 | } 25 | private: 26 | int volHum_; 27 | int volEff_; 28 | 29 | }; 30 | 31 | } 32 | 33 | #endif // MODE_TOP_MENU_WRAPPER_H 34 | -------------------------------------------------------------------------------- /functions/wavnum.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_WAVENUM_H 2 | #define FUNCTIONS_WAVENUM_H 3 | 4 | // Usage: WavNum<> 5 | // Or: WavNum 6 | // EFFECT: effect type 7 | // return value: INTEGER 8 | // 9 | // Returns which file was actually played. 10 | // First file returns 0. Even if the file is called 'clash1.wav'. 11 | 12 | template 13 | class WavNum { 14 | public: 15 | void run(BladeBase* blade) { 16 | BladeEffect* effect; 17 | if (T == EFFECT_NONE) { 18 | effect = last_detected_blade_effect; 19 | } else { 20 | OneshotEffectDetector detector; 21 | effect = detector.Detect(blade); 22 | } 23 | if (effect) value_ = effect->wavnum; 24 | } 25 | int getInteger() { return value_; } 26 | int getInteger(int led) { return value_; } 27 | private: 28 | int value_ = 0; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /transitions/delay.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_DELAY_H 2 | #define TRANSITIONS_DELAY_H 3 | 4 | #include "base.h" 5 | 6 | // Usage: TrDelayX 7 | // or: TrDelay 8 | // MILLIS_FUNCTION: FUNCTION 9 | // MILLIS: a number 10 | // return value: TRANSITION 11 | // Waits for the specified number of milliseconds, then transitions 12 | // to second color. Meant to be used with TrConcat 13 | 14 | template 15 | class TrDelayX : public TransitionBaseX { 16 | public: 17 | void run(BladeBase* blade) { 18 | TransitionBaseX::run(blade); 19 | this->update(0); 20 | } 21 | template 22 | auto getColor(const A& a, const B& b, int led) -> decltype(MixColors(a,b,1,1)) { 23 | return this->done() ? b : a; 24 | } 25 | }; 26 | 27 | template using TrDelay = TrDelayX>; 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /sound/Makefile: -------------------------------------------------------------------------------- 1 | test: tests zero.wav 2 | ./tests 3 | 4 | tests: tests.cpp effect.h 5 | g++ -O -ggdb -std=c++11 -MD -MP -o tests tests.cpp -lm 6 | 7 | 8 | talkie_test: talkie_test.cpp talkie.h 9 | g++ -O -g -std=c++11 -MD -MP -o talkie_test talkie_test.cpp -lm 10 | 11 | filter_test: filter_test.cpp filter.h 12 | g++ -O -g -std=c++11 -MD -MP -o filter_test filter_test.cpp -lm 13 | 14 | 15 | zero.wav: talkie_test 16 | ./talkie_test 'const uint8_t spZERO[] PROGMEM = {0x69,0xFB,0x59,0xDD,0x51,0xD5,0xD7,0xB5,0x6F,0x0A,0x78,0xC0,0x52,0x01,0x0F,0x50,0xAC,0xF6,0xA8,0x16,0x15,0xF2,0x7B,0xEA,0x19,0x47,0xD0,0x64,0xEB,0xAD,0x76,0xB5,0xEB,0xD1,0x96,0x24,0x6E,0x62,0x6D,0x5B,0x1F,0x0A,0xA7,0xB9,0xC5,0xAB,0xFD,0x1A,0x62,0xF0,0xF0,0xE2,0x6C,0x73,0x1C,0x73,0x52,0x1D,0x19,0x94,0x6F,0xCE,0x7D,0xED,0x6B,0xD9,0x82,0xDC,0x48,0xC7,0x2E,0x71,0x8B,0xBB,0xDF,0xFF,0x1F};' >zero.wav 17 | 18 | -include *.d 19 | -------------------------------------------------------------------------------- /functions/int.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_INT_H 2 | #define FUNCTIONS_INT_H 3 | 4 | #include "svf.h" 5 | 6 | // Usage: Int 7 | // N: a number 8 | // return value: INTEGER 9 | // Returns N 10 | 11 | template 12 | class IntSVF { 13 | public: 14 | FunctionRunResult run(BladeBase* base) { 15 | switch (N) { 16 | case 0: return FunctionRunResult::ZERO_UNTIL_IGNITION; 17 | case 32768: return FunctionRunResult::ONE_UNTIL_IGNITION; 18 | default: return FunctionRunResult::UNKNOWN; 19 | } 20 | } 21 | int calculate(BladeBase* blade) { return N; } 22 | int getInteger(int led) { return N; } 23 | }; 24 | 25 | // Optimized specialization 26 | template class SingleValueAdapter> : public IntSVF {}; 27 | template class SVFWrapper> : public IntSVF {}; 28 | 29 | template 30 | using Int = SingleValueAdapter>; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /styles/rgb_cycle.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_RGB_CYCLE_H 2 | #define STYLES_RGB_CYCLE_H 3 | 4 | // Usage: RgbCycle 5 | // return value: COLOR 6 | // Very fast Red, Green, Blue cycle, result should essentially be white 7 | // until you start swinging it around. 8 | 9 | class RGBCycle { 10 | public: 11 | void run(BladeBase* base) { 12 | if (millis() == millis_at_last_call_) 13 | return; 14 | millis_at_last_call_ = millis(); 15 | n_++; 16 | if (n_ >= 3) n_ = 0; 17 | } 18 | OverDriveColor getColor(int led) { 19 | OverDriveColor ret; 20 | ret.overdrive = false; 21 | switch (n_) { 22 | case 0: ret.c = Color16(65535, 0, 0); break; 23 | case 1: ret.c = Color16(0, 65535, 0); break; 24 | case 2: ret.c = Color16(0, 0, 65535); break; 25 | } 26 | return ret; 27 | } 28 | private: 29 | uint32_t millis_at_last_call_; 30 | uint8_t n_; 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /common/math.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_MATH_H 2 | #define COMMON_MATH_H 3 | 4 | // Returns the decimals of a number, ie 12.2134 -> 0.2134 5 | float fract(float x) { 6 | return x - floorf(x); 7 | } 8 | 9 | // clamp(x, a, b) makes sure that x is between a and b. 10 | float clamp(float x, float a, float b) { 11 | if (x < a) return a; 12 | if (x > b) return b; 13 | return x; 14 | } 15 | float Fmod(float a, float b) { 16 | return a - floorf(a / b) * b; 17 | } 18 | 19 | int32_t clampi32(int32_t x, int32_t a, int32_t b) { 20 | if (x < a) return a; 21 | if (x > b) return b; 22 | return x; 23 | } 24 | int16_t clamptoi16(int32_t x) { 25 | return clampi32(x, -32768, 32767); 26 | } 27 | int32_t clamptoi24(int32_t x) { 28 | return clampi32(x, -8388608, 8388607); 29 | } 30 | 31 | static inline int32_t MOD(int32_t x, int32_t m) { 32 | if (x >= 0) return x % m; 33 | return m + ~(~x % m); 34 | } 35 | 36 | #endif // COMMON_MATH_H 37 | -------------------------------------------------------------------------------- /common/loop_counter.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_LOOP_COUNTER_H 2 | #define COMMON_LOOP_COUNTER_H 3 | 4 | class LoopCounter { 5 | public: 6 | float LoopsPerSecond() { 7 | if (!millis_sum_) return 0.0; 8 | return updates_ * 1000.0 / millis_sum_; 9 | } 10 | void Print() { 11 | if (millis_sum_) 12 | STDOUT.print(updates_ * 1000.0 / millis_sum_); 13 | } 14 | void Reset() { 15 | updates_ = 0; 16 | millis_sum_ = 0; 17 | last_millis_ = 0; 18 | } 19 | void Update(uint32_t v) { 20 | uint32_t m = millis(); 21 | if (last_millis_) { 22 | millis_sum_ += m - last_millis_; 23 | updates_+=v; 24 | if (millis_sum_ > 10000) { 25 | updates_ /= 2; 26 | millis_sum_ /= 2; 27 | } 28 | } 29 | last_millis_ = m; 30 | } 31 | void Update() { Update(1); } 32 | private: 33 | int updates_ = 0; 34 | int millis_sum_ = 0; 35 | uint32_t last_millis_ = 0; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /styles/pulsing.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_PULSING_H 2 | #define STYLES_PULSING_H 3 | 4 | #include "../functions/int.h" 5 | 6 | // Usage: Pulsing 7 | // or: PulsingX 8 | // or: PulsingL 9 | // A, B: COLOR 10 | // PULSE_MILLIS: a number 11 | // PULSE_MILLIS_FUNC: FUNCTION 12 | // return value: COLOR 13 | // Goes back and forth between COLOR1 and COLOR2. 14 | // A full transition from COLOR1 to COLOR2 and back again takes PULSE_MILLIS milliseconds. 15 | 16 | #include "../functions/sin.h" 17 | 18 | template 19 | using PulsingL = AlphaL>; 20 | 21 | template 22 | using PulsingX = Layers>; 23 | 24 | template 25 | using Pulsing = PulsingX >; 26 | 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ProffieOS 2 | 3 | The open source operating system. Proffie OS is supported on various platforms ranging from Teensy 3.2 development boards to its own dedicated ProffieBoard reference hardware. 4 | 5 | Proffie OS supports: 6 | - :fire: SmoothSwing V1/V2 Algorithm 7 | - :fire: NEC styled lightsaber sound fonts (polyphonic) 8 | - :fire: Plecter styled lightsaber sound fonts ( monophonic) 9 | - :fire: Driving Adressable LED strips 10 | - :fire: Driving Segmented LED strips 11 | - :fire: Quad/Tri LED stars. 12 | 13 | ### Getting started 14 | * ProffieOS Documentation: https://pod.hubbe.net/ 15 | * ProffieOS: https://fredrik.hubbe.net/lightsaber/proffieos.html 16 | * Proffieboard v1.5: https://fredrik.hubbe.net/lightsaber/v4 17 | * Proffieboard v2.2: https://fredrik.hubbe.net/lightsaber/v5 18 | * Proffieboard v3.9: https://fredrik.hubbe.net/lightsaber/v6 19 | * TeensySaber: http://fredrik.hubbe.net/lightsaber/v3/ 20 | * Support forum: http://crucible.hubbe.net -------------------------------------------------------------------------------- /functions/layer_functions.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_LAYER_FUNCTIONS_H 2 | #define FUNCTIONS_LAYER_FUNCTIONS_H 3 | 4 | // Usage: LayerFunctions 5 | // F1, F2: FUNCTIONS 6 | // return value: FUNCTION 7 | // Returns (32768 - (32768 - F1) * (32768 * F2) / 32768) 8 | // This is the same as 1-(1-F1)*(1-F2), but multiplied by 32768. 9 | // Basically Mix, A, B> is the same as Mix, B>. 10 | 11 | template class LayerFunctions {}; 12 | 13 | template class LayerFunctions : public X {}; 14 | 15 | template 16 | class LayerFunctions { 17 | public: 18 | void run(BladeBase* blade) { 19 | a_.run(blade); 20 | b_.run(blade); 21 | } 22 | int getInteger(int led) { 23 | return 32768 - (((32768 - a_.getInteger(led)) * (32768 - b_.getInteger(led))) >> 15); 24 | } 25 | 26 | private: 27 | PONUA A a_; 28 | PONUA LayerFunctions b_; 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /motion/motion_util.h: -------------------------------------------------------------------------------- 1 | #ifndef MOTION_MOTION_UTIL_H 2 | #define MOTION_MOTION_UTIL_H 3 | 4 | #ifndef ORIENTATION 5 | #define ORIENTATION ORIENTATION_NORMAL 6 | #endif 7 | 8 | class MotionUtil { 9 | public: 10 | static int16_t GetShort(const unsigned char *data, int LSB) { 11 | return (data[LSB] << 8) | data[1-LSB]; 12 | } 13 | static Vec3 FromData(const unsigned char* data, float mul, int LSB, 14 | Vec3::Orientation ORIENTATION) { 15 | Vec3 ret; 16 | ret.x = mul * GetShort(data + (0 + (ORIENTATION & 7)) % 6, LSB); 17 | ret.y = mul * GetShort(data + (2 + (ORIENTATION & 7)) % 6, LSB); 18 | ret.z = mul * GetShort(data + (4 + (ORIENTATION & 7)) % 6, LSB); 19 | if (ORIENTATION & 0x10) { 20 | ret.x = - ret.x; 21 | ret.y = - ret.y; 22 | } 23 | 24 | #ifdef ORIENTATION_ROTATION 25 | ret = Quat(Vec3(ORIENTATION_ROTATION) * M_PI / 180.0).rotate_normalized(ret); 26 | #endif 27 | return ret; 28 | } 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /buttons/debounced_button.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTONS_DEBOUNCED_BUTTON_H 2 | #define BUTTONS_DEBOUNCED_BUTTON_H 3 | 4 | // Abstract class which de-bounces a potentially noisy 5 | // Read() function by waiting a certain number of ms 6 | // before letting it switch again. 7 | 8 | class DebouncedButton { 9 | public: 10 | void Update() { 11 | STATE_MACHINE_BEGIN(); 12 | while (true) { 13 | pushed_ = false; 14 | last_off_ = millis(); 15 | YIELD(); 16 | if (!Read()) continue; 17 | YIELD(); 18 | while (Read()) { 19 | pushed_ = millis() - last_off_ > timeout(); 20 | YIELD(); 21 | } 22 | } 23 | STATE_MACHINE_END(); 24 | } 25 | bool DebouncedRead() { 26 | Update(); 27 | return pushed_; 28 | } 29 | 30 | protected: 31 | virtual uint32_t timeout() { return 10; } 32 | virtual bool Read() = 0; 33 | 34 | private: 35 | uint32_t last_off_; 36 | bool pushed_ = false; 37 | StateMachineState state_machine_; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /sound/smooth_swing_cfx_config.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_SMOOTH_SWING_CFX_CONFIG_H 2 | #define SOUND_SMOOTH_SWING_CFX_CONFIG_H 3 | 4 | class SmoothSwingCFXConfigFile : public ConfigFile { 5 | public: 6 | void iterateVariables(VariableOP *op) override { 7 | CONFIG_VARIABLE2(smooth_mode, 1); 8 | CONFIG_VARIABLE2(smooth_sens, 450.0f); 9 | CONFIG_VARIABLE2(smooth_dampen, 75.0f); 10 | CONFIG_VARIABLE2(smooth_sharp, 1.75f); 11 | CONFIG_VARIABLE2(smooth_gate, 20.0f); 12 | CONFIG_VARIABLE2(smooth_width1, 45.0f); 13 | CONFIG_VARIABLE2(smooth_width2, 160.0f); 14 | CONFIG_VARIABLE2(smooth_gain, 100.0f); 15 | CONFIG_VARIABLE2(hswing, 450.0f); 16 | }; 17 | 18 | int smooth_mode; 19 | float smooth_sens; 20 | float smooth_dampen; 21 | float smooth_sharp; 22 | float smooth_gate; 23 | float smooth_width1; 24 | float smooth_width2; 25 | float smooth_gain; 26 | float hswing; 27 | }; 28 | 29 | SmoothSwingCFXConfigFile smooth_swing_cfx_config; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /functions/blaster_mode.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_BLASTER_MODE_H 2 | #define FUNCTIONS_BLASTER_MODE_H 3 | 4 | int prop_GetBlasterMode(); 5 | 6 | // Usage: BlasterModeF 7 | // Returns the current blaster mode as an integer: 8 | // 0 for MODE_STUN, 1 for MODE_KILL, 2 for MODE_AUTO 9 | // This function should only be used when BLASTER_SHOTS_UNTIL_EMPTY is defined, 10 | // indicating that the current prop is a Blaster. 11 | // Example usage in a style for Blue Stun, Red Kill, and Green Auto: 12 | // StylePtr>() 13 | 14 | class BlasterModeSVF { 15 | public: 16 | void run(BladeBase* blade) {} 17 | int getInteger(int led) { return prop_GetBlasterMode(); } 18 | int calculate(BladeBase* blade) { return prop_GetBlasterMode(); } 19 | }; 20 | 21 | template<> 22 | class SingleValueAdapter : public BlasterModeSVF {}; 23 | 24 | using BlasterModeF = SingleValueAdapter; 25 | 26 | #endif // FUNCTIONS_BLASTER_MODE_H 27 | -------------------------------------------------------------------------------- /modes/menu_base.h: -------------------------------------------------------------------------------- 1 | #ifndef MODE_MENU_BASE_H 2 | #define MODE_MENU_BASE_H 3 | 4 | namespace mode { 5 | 6 | // Base class for menues with a known size. 7 | template 8 | struct MenuBase : public SPEC::SteppedMode { 9 | virtual void say() = 0; 10 | virtual uint16_t size() = 0; 11 | 12 | void exit() override { 13 | getSL()->SayCancel(); 14 | SPEC::SteppedMode::exit(); 15 | } 16 | 17 | void select() override { 18 | getSL()->SaySelect(); 19 | SPEC::SteppedMode::select(); 20 | } 21 | 22 | void mode_activate(bool onreturn) override { 23 | SPEC::SteppedMode::mode_activate(onreturn); 24 | say(); 25 | } 26 | 27 | void fadeout(float len) { 28 | getSL()->fadeout(len); 29 | } 30 | 31 | void next() override { 32 | pos_ = MOD(pos_ + 1, size()); 33 | } 34 | void prev() override { 35 | pos_ = MOD(pos_ - 1, size()); 36 | } 37 | 38 | uint16_t pos_; 39 | }; 40 | 41 | } // namespace mode 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /styles/gradient.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_GRADIENT_H 2 | #define STYLES_GRADIENT_H 3 | 4 | // Usage: Gradient 5 | // OR: Gradient 6 | // OR: Gradient 7 | // A, B, C, D: COLOR or LAYER 8 | // return value: COLOR or LAYER (if any of the inputs are layers) 9 | // Gradient, color A at base, B at tip. 10 | // Any number of colors can be put together into a gradient. 11 | 12 | #include "mix.h" 13 | 14 | template 15 | class Gradient { 16 | public: 17 | void run(BladeBase* blade) { 18 | colors_.run(blade); 19 | mul_ = ((sizeof...(COLORS)-1) << 15) / (blade->num_leds() - 1); 20 | } 21 | private: 22 | PONUA MixHelper colors_; 23 | int mul_; 24 | public: 25 | auto getColor(int led) -> decltype(colors_.getColor(1,1)) { 26 | int x = led * mul_; 27 | auto a = colors_.getColor(x >> 15, led); 28 | auto b = colors_.getColor((x >> 15) + 1, led); 29 | return MixColors(a, b, x & 0x7fff, 15); 30 | } 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /styles/random_blink.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_RANDOM_BLINK_H 2 | #define STYLES_RANDOM_BLINK_H 3 | 4 | // Usage: RandomBlink 5 | // or: RandomBlinkX 6 | // or: RandomBlinkL 7 | // MILLIHZ: integer 8 | // MILLHZ_CLASS: NUMBER 9 | // COLOR1: COLOR (defaults to WHITE) 10 | // COLOR2: COLOR (defaults to BLACK) 11 | // return value: COLOR 12 | // Each LED is randomly chosen as COLOR1 or COLOR2, then stays 13 | // that color for 1000/MILLIHZ seconds. 14 | 15 | #include "../functions/random_blink.h" 16 | 17 | template 18 | using RandomBlinkL = AlphaL>; 19 | 20 | template 21 | using RandomBlinkX = Layers>; 22 | 23 | template 24 | using RandomBlink = RandomBlinkX, COLOR1, COLOR2>; 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /functions/random_blink.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_RANDOM_BLINK_H 2 | #define FUNCTIONS_RANDOM_BLINK_H 3 | 4 | // Usage: RandomBlinkF 5 | // MILLHZ: FUNCTION 6 | // Randomly returns either 0 or 32768 for each LED. The returned value 7 | // is held, but changed to a new random value MILLIHZ * 1000 times per 8 | // second. 9 | 10 | template 11 | class RandomBlinkF { 12 | public: 13 | void run(BladeBase* blade) { 14 | millihz_.run(blade); 15 | uint32_t now = micros(); 16 | if (now - last_update_ > 1000000000U / millihz_.calculate(blade)) { 17 | last_update_ = now; 18 | size_t shorts = (blade->num_leds() + 15) / 16; 19 | for (size_t i = 0; i < shorts; i++) bits_[i] = rand(); 20 | } 21 | } 22 | int getInteger(int led) { 23 | return (bits_[led>>4] >> (led & 0xf) & 1) * 32768; 24 | } 25 | 26 | private: 27 | PONUA SVFWrapper millihz_; 28 | unsigned short bits_[(maxLedsPerStrip + 15)/ 16]; 29 | uint32_t last_update_; 30 | }; 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /transitions/README.md: -------------------------------------------------------------------------------- 1 | # Transitions 2 | 3 | Transitions are similar to style templates, but are intended to blend between two different colors and run for a finite time. Transition classes are required to have four functions: 4 | 5 | # void begin() 6 | 7 | Called to start a transition. 8 | Can be called again and and again to re-start a transition, even if the transition is not done yet. 9 | 10 | # bool done() 11 | 12 | Should return true when the transition is done. 13 | When the transition is done getColor() should return the "b" color for all leds. 14 | 15 | # void run(BladeBase* blade); 16 | 17 | Called to start a frame. 18 | It's ok to put semi-complicated calculations here, but not in getColor. 19 | 20 | # OverDriveColor getColor(const OverDriveColor& a, const OverDriveColor& b, int led) 21 | 22 | Called once per LED on the blade to calculate the color for that led. 23 | The inputs are the colors to transition between. 24 | Generally it's best to avoid complicated calculations in getColor since 25 | it will be called thousands of times per second. 26 | -------------------------------------------------------------------------------- /common/common.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_COMMON_H 2 | #define COMMON_COMMON_H 3 | 4 | // For std::min, and other things. 5 | #include 6 | 7 | // For static_assert, and other things. 8 | #include 9 | 10 | // No unique address saves some RAM. 11 | // but it's not available in older compilers. 12 | #if __has_cpp_attribute(no_unique_address) 13 | #define PONUA [[no_unique_address]] 14 | #else 15 | #define PONUA 16 | #endif 17 | 18 | #define NELEM(X) (sizeof(X)/sizeof((X)[0])) 19 | const char install_time[] = __DATE__ " " __TIME__ 20 | #ifdef INSTALL_TIME_EXTRA 21 | INSTALL_TIME_EXTRA 22 | #endif 23 | ; 24 | 25 | #ifdef ENABLE_DEBUG 26 | #define PROFFIEOS_ASSERT(X) do { \ 27 | if (!(X)) { \ 28 | interrupts(); \ 29 | if (!(X)) STDERR << "ASSERT " << #X << " FAILED @ " << __FILE__ << ":" << __LINE__ << "\n"; \ 30 | while(true); \ 31 | } \ 32 | } while(0) 33 | #else 34 | #define PROFFIEOS_ASSERT(X) do {} while(0) 35 | #endif 36 | 37 | template struct ToVoid { typedef void Type; }; 38 | 39 | #endif 40 | 41 | -------------------------------------------------------------------------------- /transitions/select.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_SELECT_H 2 | #define TRANSITIONS_SELECT_H 3 | 4 | #include "random.h" 5 | 6 | // Usage: TrSelect 7 | // SELECTION: FUNCTION 8 | // TR1, TR2: TRANSITION 9 | // return value: TRANSITION 10 | // transition option is picked from the specified list of 11 | // transitions based on Int<> 12 | // with Int<0> representing first transition 13 | 14 | template 15 | class TrSelect : public TrHelper3 { 16 | public: 17 | void begin() { 18 | begin_ = true; 19 | } 20 | void run(BladeBase* blade) { 21 | f_.run(blade); 22 | if (begin_) { 23 | begin_ = false; 24 | this->selected_ = MOD(f_.calculate(blade), sizeof...(TRANSITION)); 25 | TrHelper3::begin(); 26 | } 27 | TrHelper3::run(blade); 28 | } 29 | 30 | bool done() { 31 | if (begin_) return false; 32 | return TrHelper3::done(); 33 | } 34 | 35 | private: 36 | bool begin_ = false; 37 | PONUA SVFWrapper f_; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /styles/on_spark.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_ON_SPARK_H 2 | #define STYLES_ON_SPARK_H 3 | 4 | // Usage: OnSpark 5 | // Or: OnSparX 6 | // Or: OnSparL 7 | // BASE: COLOR 8 | // SPARK_COLOR: COLOR (defaults to white) 9 | // MILLIS: a number (defaults to 200) 10 | // MILLI_CLASS: FUNCTION (defaults to Int<200>) 11 | // return value: COLOR 12 | // When you turn the saber on, it starts with SPARK_COLOR, and then 13 | // fades to BASE over a peariod of MILLIS millseconds. 14 | 15 | #include "alpha.h" 16 | #include "layers.h" 17 | #include "../functions/on_spark.h" 18 | 19 | template, class MILLIS = Int<200> > 20 | using OnSparkL = AlphaL>; 21 | 22 | template, class MILLIS = Int<200> > 23 | using OnSparkX = Layers>; 24 | 25 | template, int MILLIS = 200> 26 | using OnSpark = OnSparkX>; 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /styles/rotate_color.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_ROTATE_COLOR 2 | #define STYLES_ROTATE_COLOR 3 | 4 | // Usage: RotateColorsX 5 | // ROTATION: FUNCTION 6 | // COLOR: COLOR or LAYER 7 | // return value: COLOR or LAYER (same as COLOR) 8 | // 9 | // ROTATION specifies how much to rotate the color in HSV (color wheel) 10 | // space. 0 = none, 32768 = 360degrees 11 | 12 | template 13 | class RotateColorsX { 14 | public: 15 | bool run(BladeBase* blade) { 16 | rotation_.run(blade); 17 | return RunStyle(&color_, blade); 18 | } 19 | 20 | private: 21 | PONUA COLOR color_; 22 | PONUA ROTATION rotation_; 23 | public: 24 | auto getColor(int led) -> decltype(color_.getColor(led)) { 25 | auto c = color_.getColor(led); 26 | c.c = c.c.rotate((rotation_.getInteger(led) & 0x7fff) * 3); 27 | return c; 28 | } 29 | }; 30 | 31 | template using RotateColors = RotateColorsX, COLOR>; 32 | template using HueX = RotateColorsX; 33 | template using Hue = HueX>; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /config/teensy_audio_shield_audiofx.h: -------------------------------------------------------------------------------- 1 | #ifdef CONFIG_TOP 2 | #include "teensy_audio_board_rev_d.h" 3 | #define NUM_BLADES 0 4 | #define NUM_BUTTONS 8 5 | #define VOLUME 2500 6 | const unsigned int maxLedsPerStrip = 144; 7 | #define CLASH_THRESHOLD_G 2.0 8 | #define ENABLE_AUDIO 9 | #define ENABLE_SD 10 | #define ENABLE_DEVELOPER_COMMANDS 11 | #endif 12 | 13 | #ifdef CONFIG_PROP 14 | #include "../props/audiofx.h" 15 | #endif 16 | 17 | #ifdef CONFIG_PRESETS 18 | Preset presets[] = { 19 | { "vader", "tracks/cantina.wav", ""}, 20 | { "TeensySF", "tracks/title.wav", ""}, 21 | }; 22 | BladeConfig blades[] = { { 0, CONFIGARRAY(presets) }, }; 23 | #endif 24 | 25 | #ifdef CONFIG_BUTTONS 26 | Button Bt1(BUTTON_TRIGGER_ONE, trigger1Pin, "b1"); 27 | Button Bt2(BUTTON_TRIGGER_TWO, trigger2Pin, "b2"); 28 | Button Bt3(BUTTON_TRIGGER_THREE, trigger3Pin, "b3"); 29 | Button Bt4(BUTTON_TRIGGER_FOUR, trigger4Pin, "b4"); 30 | Button Bt5(BUTTON_TRIGGER_FIVE, trigger5Pin, "b5"); 31 | Button Bt6(BUTTON_TRIGGER_SIX, trigger6Pin, "b6"); 32 | Button Bt7(BUTTON_TRIGGER_SEVEN, trigger7Pin, "b7"); 33 | Button Bt8(BUTTON_TRIGGER_EIGHT, trigger8Pin, "b8"); 34 | #endif 35 | -------------------------------------------------------------------------------- /common/cyclint.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_CYCLINT_H 2 | #define COMMON_CYCLINT_H 3 | 4 | // Lets you do comparisons on integers that wrap around. 5 | // T should be an unsigned type. 6 | template 7 | struct Cyclint { 8 | Cyclint() : v(0) {} 9 | static const T NMASK = ((T)1) << (sizeof(T)*8 - 1); 10 | explicit Cyclint(T value) : v(value) {} 11 | Cyclint operator+(T o) { return Cyclint(v+o); } 12 | Cyclint operator+(int o) { return Cyclint(v+o); } 13 | Cyclint operator-(T o) { return Cyclint(v-o); } 14 | Cyclint operator-(int o) { return Cyclint(v-o); } 15 | void operator+=(T o) { v += o; } 16 | void operator-=(T o) { v -= o; } 17 | bool operator==(Cyclint o) const { return v == o.v; } 18 | bool operator!=(Cyclint o) const { return v != o.v; } 19 | bool operator<(Cyclint o) const { return !!((v - o.v) & NMASK); } 20 | bool operator<=(Cyclint o) const { return !((o.v - v) & NMASK); } 21 | bool operator>(Cyclint o) const { return !!((o.v - v) & NMASK); } 22 | bool operator>=(Cyclint o) const { return !((v - o.v) & NMASK); } 23 | operator const T() const { return v; } 24 | T v; 25 | }; 26 | 27 | #endif // COMMON_CYCLINT_H 28 | -------------------------------------------------------------------------------- /transitions/sequence.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_SEQUENCE_H 2 | #define TRANSITIONS_SEQUENCE_H 3 | 4 | #include "random.h" 5 | 6 | // Usage: TrSequence 7 | // TR1, TR2: TRANSITION 8 | // return value: TRANSITION 9 | // Each time a new transition is started, a transition is selected 10 | // sequentially, such that the frist time a transition is started 11 | // TR1 will be selected, then TR2, etc. 12 | // When the end of the sequence is reached, the next transition selected 13 | // wraps back around to TR1. 14 | 15 | template 16 | class TrSequence : public TrHelper3{ 17 | public: 18 | void begin() { 19 | begin_ = true; 20 | } 21 | void run(BladeBase* blade) { 22 | if (begin_) { 23 | begin_ = false; 24 | this->selected_ = (this->selected_ + 1) % sizeof...(TRANSITION); 25 | TrHelper3::begin(); 26 | } 27 | TrHelper3::run(blade); 28 | } 29 | 30 | bool done() { 31 | if (begin_) return false; 32 | return TrHelper3::done(); 33 | } 34 | 35 | private: 36 | bool begin_ = false; 37 | int n_ = -1; 38 | }; 39 | 40 | #endif 41 | -------------------------------------------------------------------------------- /common/scoped_cycle_counter.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_SCOPED_CYCLE_COUNTER_H 2 | #define COMMON_SCOPED_CYCLE_COUNTER_H 3 | 4 | class ScopedCycleCounter { 5 | public: 6 | static inline uint32_t getCycles() { 7 | #if defined(TEENSYDUINO) 8 | return ARM_DWT_CYCCNT - counted_cycles_; 9 | #elif defined(ARDUINO_ARCH_STM32L4) 10 | return DWT->CYCCNT - counted_cycles_; 11 | #elif defined(ESP32) 12 | return cpu_hal_get_cycle_count(); 13 | #else 14 | return 0; 15 | #endif 16 | } 17 | ScopedCycleCounter(uint64_t& dest) : 18 | dest_(dest) { 19 | #ifndef DISABLE_DIAGNOSTIC_COMMANDS 20 | noInterrupts(); 21 | cycles_ = getCycles(); 22 | interrupts(); 23 | #endif 24 | } 25 | ~ScopedCycleCounter() { 26 | #ifndef DISABLE_DIAGNOSTIC_COMMANDS 27 | noInterrupts(); 28 | uint32_t cycles; 29 | cycles = getCycles() - cycles_; 30 | counted_cycles_ += cycles; 31 | interrupts(); 32 | dest_ += cycles; 33 | #endif 34 | } 35 | private: 36 | static volatile uint32_t counted_cycles_; 37 | uint32_t cycles_; 38 | uint64_t& dest_; 39 | }; 40 | 41 | volatile uint32_t ScopedCycleCounter::counted_cycles_ = 0; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /config/proffieboard_v2_testing_config.h: -------------------------------------------------------------------------------- 1 | // Proffieboard V2 board testing configuration. 2 | // Read ../scripts/proffieboard_tst_script.h for instructions. 3 | 4 | #ifdef CONFIG_TOP 5 | #include "proffieboard_v2_config.h" 6 | #define NUM_BLADES 1 7 | #define NUM_BUTTONS 0 8 | #define VOLUME 300 9 | const unsigned int maxLedsPerStrip = 144; 10 | #define CLASH_THRESHOLD_G 3.5 11 | #define ENABLE_AUDIO 12 | #define ENABLE_MOTION 13 | #define ENABLE_WS2811 14 | #define ENABLE_SD 15 | #define BLADE_ID_CLASS ExternalPullupBladeID 16 | #define ENABLE_DEVELOPER_COMMANDS 17 | #endif 18 | 19 | #ifdef CONFIG_PRESETS 20 | Preset saber[] = { 21 | { "TeensySF", "", StylePtr(), "testrig"}, 22 | }; 23 | 24 | BladeConfig blades[] = { 25 | { 0, SimpleBladePtr(), CONFIGARRAY(saber) }, 26 | }; 27 | 28 | #endif // CONFIG_PRESETS 29 | 30 | #ifdef CONFIG_BUTTONS 31 | 32 | #include "../scripts/proffieboard_test_script.h" 33 | #warning !!! PROFFIEBOARD TEST SCRIPT ACTIVE !!! 34 | V4TestScript script; 35 | Blinker1 blinker1; 36 | Blinker2 blinker2; 37 | CapTest captest; 38 | 39 | #endif // CONFIG_BUTTONS 40 | -------------------------------------------------------------------------------- /styles/byteorder.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_BYTEORDER_H 2 | #define STYLES_BYTEORDER_H 3 | 4 | // Usage: ByteOrderStyle 5 | // BYTEORDER: Color8::RGB, or one of the other byte orders 6 | // COLOR: COLOR 7 | // return value: COLOR 8 | 9 | // This shuffles the RGB values around. It's meant to be used 10 | // when you are mixing different kind of pixels on one string. 11 | // While it's not recommended to do so, this should make it 12 | // easier to compensate by re-ordering the channels. 13 | 14 | template 15 | class ByteOrderStyle { 16 | public: 17 | bool run(BladeBase* base) { 18 | order_ = Color8::combine_byteorder(Color8::invert_byteorder(base->get_byteorder()), byteorder); 19 | return RunStyle(&color_, base); 20 | } 21 | 22 | COLOR color_; 23 | Color8::Byteorder order_; 24 | 25 | auto getColor(int led) -> decltype(color_.getColor(led)) { 26 | Color16 tmp; 27 | auto c = color_.getColor(led); 28 | tmp.r = c.c.getShort(order_, 2); 29 | tmp.g = c.c.getShort(order_, 1); 30 | tmp.b = c.c.getShort(order_, 0); 31 | c.c = tmp; 32 | return c; 33 | } 34 | }; 35 | 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /config/hev_config.h: -------------------------------------------------------------------------------- 1 | #ifdef CONFIG_TOP 2 | #include "proffieboard_v2_config.h" 3 | #define NUM_BLADES 1 4 | #define NUM_BUTTONS 2 5 | #define VOLUME 50 6 | const unsigned int maxLedsPerStrip = 144; 7 | #define CLASH_THRESHOLD_G 3.0 8 | //If not disabled, Armor Readout play back floats around current Armor value. 9 | #define DISABLE_NO_REPEAT_RANDOM 10 | #endif 11 | 12 | #ifdef CONFIG_PROP 13 | #include "../props/hev.h" 14 | #endif 15 | 16 | // All HALF-LIFE sfx audio can be found in the game's "sound" directory. 17 | // Specifically, HEV Suit voice lines can be found within the "fvox" dir. 18 | // The music tracks can be found in the "media" dir in the root folder. 19 | 20 | #ifdef CONFIG_PRESETS 21 | Preset presets[] = { 22 | { "hev;common_hev", "common_hev/tracks/hl1_ost/10 Valve Theme [Extended].wav", 23 | StyleNormalPtr(), 24 | } 25 | }; 26 | 27 | BladeConfig blades[] = { 28 | { 10000, WS2811BladePtr<125, WS2811_ACTUALLY_800kHz | WS2811_GRB>(), 29 | CONFIGARRAY(presets) }, 30 | }; 31 | 32 | #endif 33 | 34 | #ifdef CONFIG_BUTTONS 35 | Button PowerButton(BUTTON_POWER, powerButtonPin, "pow"); 36 | Button AuxButton(BUTTON_AUX, auxPin, "aux"); 37 | #endif 38 | -------------------------------------------------------------------------------- /transitions/boing.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_BOING_H 2 | #define TRANSITIONS_BOING_H 3 | 4 | #include "base.h" 5 | 6 | // Usage: TrBoingX 7 | // or: TrBoing 8 | // MILLIS_FUNCTION: FUNCTION 9 | // MILLIS: a number 10 | // N: a number 11 | // return value: TRANSITION 12 | // Similar to TrFade, but transitions back and forth between the two 13 | // colors several times. (As specified by N). If N is 0, it's equal to 14 | // TrFade. If N is 1 it transitions A-B-A-B, if N is 2, it is A-B-A-B-A-B, 15 | // and so on. 16 | 17 | template 18 | class TrBoingX : public TransitionBaseX { 19 | public: 20 | void run(BladeBase* blade) { 21 | TransitionBaseX::run(blade); 22 | fade_ = this->update(16384 * (N * 2 + 1)); 23 | if (fade_ & 0x4000) { 24 | fade_ = 0x4000 - (fade_ & 0x3FFF); 25 | } else { 26 | fade_ &= 0x3FFF; 27 | } 28 | } 29 | private: 30 | uint32_t fade_; 31 | public: 32 | template 33 | auto getColor(const A& a, const B& b, int led) AUTO_RETURN(MixColors(a, b, fade_, 14)) 34 | }; 35 | 36 | template using TrBoing = TrBoingX, N>; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /modes/select_cancel_mode.h: -------------------------------------------------------------------------------- 1 | #ifndef MODES_SELECT_CANCEL_MODE_H 2 | #define MODES_SELECT_CANCEL_MODE_H 3 | 4 | #include "mode.h" 5 | 6 | namespace mode { 7 | 8 | struct SelectCancelMode : public ModeInterface { 9 | virtual void select() { popMode(); } 10 | virtual void exit() { popMode(); } 11 | 12 | bool mode_Event2(enum BUTTON button, EVENT event, uint32_t modifiers) override { 13 | switch (EVENTID(button, event, 0)) { 14 | case EVENTID(BUTTON_POWER, EVENT_FIRST_SAVED_CLICK_SHORT, 0): 15 | select(); 16 | return true; 17 | 18 | case EVENTID(BUTTON_POWER, EVENT_SECOND_SAVED_CLICK_SHORT, 0): 19 | case EVENTID(BUTTON_AUX, EVENT_FIRST_SAVED_CLICK_SHORT, 0): 20 | exit(); 21 | return true; 22 | } 23 | return false; 24 | } 25 | 26 | bool mode_Parse(const char *cmd, const char* arg) override { 27 | #ifndef DISABLE_DIAGNOSTIC_COMMANDS 28 | if (!strcmp(cmd, "select") || !strcmp(cmd, "sel")) { 29 | select(); 30 | return true; 31 | } 32 | if (!strcmp(cmd, "cancel") || !strcmp(cmd, "can")) { 33 | exit(); 34 | return true; 35 | } 36 | #endif 37 | return false; 38 | } 39 | }; 40 | 41 | } // namespace mode 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /styles/sparkle.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_SPARKLE_H 2 | #define STYLES_SPARKLE_H 3 | 4 | // Usage: Sparkle 5 | // Or: SparkleL 6 | // BASE: COLOR 7 | // SPARKLE_COLOR: COLOR (defaults to white) 8 | // SPARK_CHANCE_PROMILLE: a number 9 | // SPARK_INTENSITY: a number 10 | // Generally displays BASE, but creates little sparkles of SPARKLE_COLOR 11 | // SPARK_CHANCE_PROMILLE decides how often a spark is generated, defaults to 300 (30%) 12 | // SPARK_INTENSITY specifies how intens the spark is, defaults to 1024 13 | 14 | #include "../functions/sparkle.h" 15 | #include "layers.h" 16 | #include "alpha.h" 17 | 18 | template< 19 | class SPARKLE_COLOR = Rgb<255,255,255>, 20 | int SPARK_CHANCE_PROMILLE = 300, 21 | int SPARK_INTENSITY = 1024> 22 | using SparkleL = AlphaL>; 23 | 24 | template, 26 | int SPARK_CHANCE_PROMILLE = 300, 27 | int SPARK_INTENSITY = 1024> 28 | using Sparkle = Layers>; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /transitions/extend.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_EXTEND_H 2 | #define TRANSITIONS_EXTEND_H 3 | 4 | #include "base.h" 5 | 6 | // Usage: TrExtendX 7 | // or: TrExtend 8 | // MILLIS_FUNCTION: FUNCTION 9 | // TRANSITION: TRANSITION 10 | // MILLIS: a number 11 | // return value: TRANSITION 12 | // Runs the specified transition, then holds the 13 | // last value for some additional time specified by 14 | // MILLIS_FUNCTION. 15 | 16 | template 17 | class TrExtendX : public TRANSITION { 18 | public: 19 | void run(BladeBase* blade) { 20 | TRANSITION::run(blade); 21 | if (!extending_ && TRANSITION::done()) { 22 | extending_ = true; 23 | millis_.begin(); 24 | } 25 | millis_.run(blade); 26 | if (extending_) { 27 | millis_.update(0); 28 | } 29 | } 30 | void begin() { 31 | TRANSITION::begin(); 32 | extending_ = false; 33 | } 34 | bool done() { return extending_ && millis_.done(); } 35 | private: 36 | PONUA TransitionBaseX millis_; 37 | bool extending_ = false; 38 | }; 39 | 40 | template using TrExtend = TrExtendX, TRANSITION>; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /styles/strobe.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_STROBE_H 2 | #define STYLES_STROBE_H 3 | 4 | #include "../functions/int.h" 5 | 6 | // Usage: Strobe 7 | // or: StrobeX 8 | // or: StrobeL 9 | // BASE, STROBE_COLOR: COLOR 10 | // STROBE_FREQUENCY, STROBE_MILLIS: a number 11 | // STROBE_FREQUENCY_FUNC, STROBE_MILLIS_FUNC: FUNCTION 12 | // return value: COLOR 13 | // Stroboscope-like effect, turns the color to STROBE_COLOR for STROBE_MILLIS 14 | // STROBE_FREQUENCY times per second. 15 | 16 | #include "../functions/strobe.h" 17 | 18 | template 19 | using StrobeL = AlphaL>; 20 | 21 | template 22 | using StrobeX = Layers>; 23 | 24 | template 25 | using Strobe = StrobeX, Int >; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /config/teensy_audio_shield_micom.h: -------------------------------------------------------------------------------- 1 | #ifdef CONFIG_TOP 2 | #include "teensy_audio_board_rev_d.h" 3 | #define NUM_BLADES 0 4 | #define NUM_BUTTONS 8 5 | #define VOLUME 2500 6 | const unsigned int maxLedsPerStrip = 144; 7 | #define CLASH_THRESHOLD_G 2.0 8 | #define ENABLE_AUDIO 9 | #define ENABLE_SD 10 | #define ENABLE_DEVELOPER_COMMANDS 11 | #endif 12 | 13 | #ifdef CONFIG_PROP 14 | #include "../props/micom.h" 15 | #endif 16 | 17 | #ifdef CONFIG_PRESETS 18 | Preset presets[] = { 19 | { "vadert~1", "tracks/vader.wav", ""}, 20 | { "micom2", "tracks/cantina.wav", ""}, 21 | { "vader", "tracks/vader.wav", ""}, 22 | { "TeensySF", "tracks/title.wav", ""}, 23 | }; 24 | BladeConfig blades[] = { { 0, CONFIGARRAY(presets) }, }; 25 | #endif 26 | 27 | #ifdef CONFIG_BUTTONS 28 | Button Bt1(BUTTON_TRIGGER_ONE, trigger1Pin, "b1"); 29 | Button Bt2(BUTTON_TRIGGER_TWO, trigger2Pin, "b2"); 30 | Button Bt3(BUTTON_TRIGGER_THREE, trigger3Pin, "b3"); 31 | Button Bt4(BUTTON_TRIGGER_FOUR, trigger4Pin, "b4"); 32 | Button Bt5(BUTTON_TRIGGER_FIVE, trigger5Pin, "b5"); 33 | Button Bt6(BUTTON_TRIGGER_SIX, trigger6Pin, "b6"); 34 | Button Bt7(BUTTON_TRIGGER_SEVEN, trigger7Pin, "b7"); 35 | Button Bt8(BUTTON_TRIGGER_EIGHT, trigger8Pin, "b8"); 36 | #endif 37 | -------------------------------------------------------------------------------- /functions/variation.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_VARIATION_H 2 | #define FUNCTIONS_VARIATION_H 3 | 4 | #include "svf.h" 5 | 6 | // Usage: Variation 7 | // Returns 0-32768 based on current variation. 8 | // returned value: FUNCTION 9 | // Note that using Variation in your style means that the 10 | // the automatic color rotation is turned off. The color wheel 11 | // menu is unaffected, but the style is now responsible for actually 12 | // changing the color. 13 | // Note that if any blade styles are using ColorChange<>, 14 | // the variation will return "ticked" values: 0, 1, 2, etc. 15 | 16 | class VariationSVF { 17 | public: 18 | VariationSVF() { 19 | BladeBase::HandleFeature(HANDLED_FEATURE_CHANGE); 20 | } 21 | void run(BladeBase* blade) { 22 | } 23 | int calculate(BladeBase* blade) { 24 | return SaberBase::GetCurrentVariation() & 0x7fff; 25 | } 26 | int getInteger(int led) { 27 | return SaberBase::GetCurrentVariation() & 0x7fff; 28 | } 29 | }; 30 | 31 | // Optimized specialization 32 | template<> class SingleValueAdapter : public VariationSVF {}; 33 | template<> class SVFWrapper : public VariationSVF {}; 34 | 35 | using Variation = SingleValueAdapter; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /functions/sequence.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_SEQUENCE_H 2 | #define FUNCTIONS_SEQUENCE_H 3 | 4 | // usage: SequenceF 5 | // millis_per_bit: a number, millseconds spent on each bit 6 | // bits: a number, number of bits before we loop around to the beginning 7 | // 0b0000000000000000: 16-bit binary numbers containing the actual sequence. 8 | // 9 | // Returns 32768 if the current bit in the sequence is 1, 0 otherwise. 10 | // The number of 16-bit binary numbers should be at least |bits| / 16, rounded up. 11 | // Note that if not all bits are used within the 16-bit number. 12 | // Example, an SOS pattern: 13 | // SequenceF<100, 37, 0b0001010100011100, 0b0111000111000101, 0b0100000000000000> 14 | 15 | template 16 | class SequenceF { 17 | public: 18 | void run(BladeBase* blade) { 19 | static uint16_t sequence_[] = { sequence... }; 20 | uint32_t now = millis(); 21 | uint32_t bit = (now / millis_per_bit) % std::min(bits, sizeof...(sequence) * 16); 22 | value_ = 32768 * ((sequence_[bit >> 4] >> ((~bit) & 0xf)) & 1); 23 | } 24 | int getInteger(int led) { return value_; } 25 | 26 | private: 27 | int value_; 28 | }; 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /ir/ir.h: -------------------------------------------------------------------------------- 1 | #ifndef IR_IR_H 2 | #define IR_IR_H 3 | 4 | class IRInterface { 5 | public: 6 | virtual void signal(bool high, uint32_t us) = 0; 7 | }; 8 | 9 | class IRSender : public IRInterface { 10 | public: 11 | virtual void send() = 0; 12 | }; 13 | 14 | IRSender* GetIRSender(); 15 | 16 | class IRDecoder; 17 | IRDecoder* ir_decoders = nullptr; 18 | 19 | class IRDecoder : public IRInterface { 20 | public: 21 | IRDecoder() { 22 | next_decoder_ = ir_decoders; 23 | ir_decoders = this; 24 | } 25 | static void DoSignal(bool high, uint32_t us) { 26 | for (IRDecoder *d = ir_decoders; d; d = d->next_decoder_) { 27 | d->signal(high, us); 28 | } 29 | } 30 | protected: 31 | virtual const char* name() = 0; 32 | private: 33 | IRDecoder* next_decoder_; 34 | }; 35 | 36 | class IRDecoderHelper : public IRDecoder { 37 | protected: 38 | enum class IRLength { 39 | ILLEGAL, 40 | SHORT, 41 | LONG, 42 | MARK1, 43 | MARK2, 44 | MARK3 45 | }; 46 | 47 | void signal(bool high, uint32_t us) override { 48 | signal(high, classify(us)); 49 | } 50 | 51 | protected: 52 | virtual void signal(bool high, IRLength len) = 0; 53 | virtual IRLength classify(uint32_t us) = 0; 54 | }; 55 | 56 | #endif 57 | 58 | -------------------------------------------------------------------------------- /functions/linear_section.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTION_LINEAR_SECTION_H 2 | #define FUNCTION_LINEAR_SECTION_H 3 | 4 | #include 5 | 6 | // Usage: LinearSectionF 7 | // POSITION: FUNCTION position on the blade, 0-32768 8 | // FRACTION: FUNCTION how much of the blade to light up, 0 = none 9 | // return value: FUNCTION 10 | // creates a "block" of pixels at POSITION taking up FRACTION of blade 11 | 12 | class BladeBase; 13 | template 14 | class LinearSectionF { 15 | public: 16 | FunctionRunResult run(BladeBase* base) { 17 | pos_.run(base); 18 | FunctionRunResult ret = RunFunction(&fraction_, base); 19 | int num_leds = base->num_leds(); 20 | int fraction = fraction_.calculate(base); 21 | int pos = pos_.calculate(base); 22 | range_ = Range(clampi32((pos - fraction / 2) * num_leds, 0, 32768 * num_leds), clampi32((pos + fraction / 2) * num_leds, 0, 32768 * num_leds)); 23 | return ret; 24 | } 25 | int getInteger(int led) { 26 | Range led_range(led * 32768, led * 32768 + 32768); 27 | return (Range(range_) & led_range).size(); 28 | } 29 | private: 30 | PONUA SVFWrapper pos_; 31 | PONUA SVFWrapper fraction_; 32 | Range range_; 33 | }; 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /functions/random.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_RANDOM_H 2 | #define FUNCTIONS_RANDOM_H 3 | 4 | // Usage: RandomF 5 | // Return value: FUNCTION 6 | // Returns a random number between 0 and 32768. 7 | // All LEDS gets the same value. 8 | 9 | class RandomFSVF { 10 | public: 11 | int calculate(BladeBase* blade) { return random(32768); } 12 | void run(BladeBase* blade) {} 13 | }; 14 | 15 | using RandomF = SingleValueAdapter; 16 | 17 | // Usage: RandomPerLEDF 18 | // Return value: FUNCTION 19 | // Returns a random number between 0 and 32768. 20 | // Each LED gets a different random value. 21 | 22 | class RandomPerLEDF { 23 | public: 24 | void run(BladeBase* blade) { } 25 | int getInteger(int led) { return random(32768); } 26 | }; 27 | 28 | // Usage: EffectRandomF 29 | // EFFECT: BladeEffectType 30 | // return value: INTEGER 31 | // Returns a random value between 0 and 32768 each time EVENT is triggered 32 | 33 | template 34 | class EffectRandomF { 35 | public: 36 | void run(BladeBase* blade) { 37 | if (effect_.Detect(blade)) value_ = random(32768); 38 | } 39 | int getInteger(int led) { return value_; } 40 | 41 | private: 42 | OneshotEffectDetector effect_; 43 | int value_; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /styles/blade_shortener.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLE_BLADE_SHORTENER_H 2 | #define STYLE_BLADE_SHORTENER_H 3 | 4 | class BladeShortenerWrapper : public BladeStyle, public BladeWrapper { 5 | public: 6 | BladeShortenerWrapper(int len, BladeStyle* style) : num_leds_(len), style_(style) { 7 | } 8 | ~BladeShortenerWrapper() { 9 | delete style_; 10 | } 11 | 12 | void run(BladeBase* blade) override { 13 | blade_ = blade; 14 | num_leds_ = std::min(blade->num_leds(), num_leds_); 15 | style_->run(this); 16 | // Fill rest of blade with black. 17 | int blade_length = blade_->num_leds(); 18 | for (int i = num_leds_; i < blade_length; i++) { 19 | blade->set(i, Color16()); 20 | } 21 | } 22 | 23 | void activate() override { style_->activate(); } 24 | void deactivate() override { style_->deactivate(); } 25 | bool NoOnOff() override { return style_->NoOnOff(); } 26 | bool Charging() override { return style_->Charging(); } 27 | bool IsHandled(HandledFeature feature) { return style_->IsHandled(feature); } 28 | 29 | int num_leds() const override { return num_leds_; } 30 | void set_length(int length) { 31 | num_leds_ = length; 32 | } 33 | private: 34 | int num_leds_; 35 | BladeStyle* style_; 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /functions/on_spark.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_ON_SPARK_H 2 | #define FUNCTIONS_ON_SPARK_H 3 | 4 | // Usage: OnsparkF 5 | // MILLIS: FUNCTION (defaults to Int<200>) 6 | // return value: FUNCTION 7 | // When the blade turns on, this function starts returning 8 | // 32768, then fades back to zero over MILLIS milliseconds. 9 | // This is intended to be used with Mix<> or AlphaL<> to 10 | // to create a flash of color or white when the blade ignites. 11 | 12 | template > 13 | class OnSparkFSVF { 14 | public: 15 | void run(BladeBase* blade) { 16 | millis_.run(blade); 17 | } 18 | int calculate(BladeBase* blade) { 19 | uint32_t m = millis(); 20 | if (on_ != blade->is_on()) { 21 | on_ = blade->is_on(); 22 | if (on_) on_millis_ = m; 23 | } 24 | uint32_t t = millis() - on_millis_; 25 | uint32_t fade_millis = millis_.calculate(blade); 26 | if (t < fade_millis) { 27 | return 32768 - 32768 * t / fade_millis; 28 | } else { 29 | return 0; 30 | } 31 | } 32 | private: 33 | bool on_; 34 | uint32_t on_millis_; 35 | PONUA SVFWrapper millis_; 36 | }; 37 | 38 | template > 39 | using OnSparkF = SingleValueAdapter>; 40 | 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /functions/subtract.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_SUBTRACT_H 2 | #define FUNCTIONS_SUBTRACT_H 3 | 4 | // Usage: Subtract 5 | // A, B: FUNCTION 6 | // return value: FUNCTION 7 | // Subtracts B from A (A - B) 8 | 9 | template 10 | class SubtractBase { 11 | public: 12 | void run(BladeBase* blade) { 13 | a_.run(blade); 14 | b_.run(blade); 15 | } 16 | int getInteger(int led) { 17 | return a_.getInteger(led) - b_.getInteger(led); 18 | } 19 | 20 | private: 21 | PONUA A a_; 22 | PONUA B b_; 23 | }; 24 | 25 | template 26 | class SubtractSVF { 27 | public: 28 | void run(BladeBase* blade) { 29 | svfa_.run(blade); 30 | svfb_.run(blade); 31 | } 32 | int calculate(BladeBase* blade) { 33 | return svfa_.calculate(blade) - svfb_.calculate(blade); 34 | } 35 | private: 36 | PONUA SVFA svfa_; 37 | PONUA SVFB svfb_; 38 | }; 39 | 40 | template struct SubtractFinder { typedef SubtractBase SubtractClass; }; 41 | template struct SubtractFinder, SingleValueAdapter> { 42 | typedef SingleValueAdapter> SubtractClass; 43 | }; 44 | template using Subtract = typename SubtractFinder::SubtractClass; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /buttons/floating_button.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTONS_FLOATING_BUTTON_H 2 | #define BUTTONS_FLOATING_BUTTON_H 3 | 4 | // Returns true when the specified pin is not floating. 5 | template 6 | class FloatingButtonBase { 7 | public: 8 | // Requires 10 votes of float/non float agreement 9 | // before changing state. 10 | void Vote(int floating) { 11 | if (floating != floating_) { 12 | if (++votes_for_change_ <= 10) return; 13 | floating_ = floating; 14 | } 15 | votes_for_change_ = 0; 16 | } 17 | 18 | void Update() { 19 | STATE_MACHINE_BEGIN(); 20 | while (true) { 21 | YIELD(); 22 | pinMode(PIN, INPUT_PULLUP); 23 | SLEEP(1); 24 | if (!digitalRead(PIN)) { 25 | Vote(false); 26 | continue; 27 | } 28 | 29 | pinMode(PIN, INPUT_PULLDOWN); 30 | SLEEP(1); 31 | if (digitalRead(PIN)) { 32 | Vote(false); 33 | continue; 34 | } 35 | Vote(true); 36 | } 37 | STATE_MACHINE_END(); 38 | } 39 | 40 | // Returns true if NOT floating. 41 | bool DebouncedRead() { 42 | Update(); 43 | return !floating_; 44 | } 45 | virtual bool Read() = 0; 46 | private: 47 | bool floating_ = true; 48 | uint8_t votes_for_change_ = 0; 49 | StateMachineState state_machine_; 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /functions/clash_impact.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_CLASH_IMPACT_H 2 | #define FUNCTIONS_CLASH_IMPACT_H 3 | 4 | #include "../common/saber_base.h" 5 | 6 | // Usage: ClashImpactFX 7 | // MIN: minimum value Clash is detected (recommended range 0 ~ 500, default is 200) 8 | // MAX: maximum impact to return 32768 (recommended range 1000 ~ 1600, default is 1600) 9 | // returned value: INTEGER 10 | // Returns 0-32768 based on impact strength of clash 11 | 12 | #include "svf.h" 13 | 14 | template, class MAX = Int<1600>> 15 | class ClashImpactFXSVF { 16 | public: 17 | void run(BladeBase* base) { 18 | min_cents_.run(base); 19 | max_cents_.run(base); 20 | } 21 | int calculate(BladeBase* base) { 22 | float i = SaberBase::GetClashStrength() * 100; 23 | int min = min_cents_.calculate(base); 24 | int max = max_cents_.calculate(base); 25 | return clampi32((i - min) * 32768 / max, 0, 32768); 26 | } 27 | 28 | private: 29 | PONUA SVFWrapper min_cents_; 30 | PONUA SVFWrapper max_cents_; 31 | }; 32 | 33 | template, class MAX = Int<1600>> 34 | using ClashImpactFX = SingleValueAdapter>; 35 | 36 | template 37 | using ClashImpactF = ClashImpactFX, Int>; 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /scripts/v3_test_script.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRIPTS_V3_TEST_SCRIPT_H 2 | #define SCRIPTS_V3_TEST_SCRIPT_H 3 | 4 | #ifdef TEENSYDUINO 5 | 6 | class V3TestScript : Looper, StateMachine { 7 | public: 8 | const char* name() override { return "Script"; } 9 | void Loop() override { 10 | STATE_MACHINE_BEGIN(); 11 | SLEEP(2000); 12 | if (fabsf(prop.id() - 125812.5f) > 22687.0f) { 13 | STDOUT.println("ID IS WRONG!!!"); 14 | beeper.Beep(0.5, 2000.0); 15 | SLEEP(1000); 16 | beeper.Beep(0.5, 2000.0); 17 | SLEEP(1000); 18 | beeper.Beep(0.5, 2000.0); 19 | SLEEP(1000); 20 | } 21 | CommandParser::DoParse("on", NULL); 22 | SLEEP(1000); 23 | CommandParser::DoParse("batt", NULL); 24 | SLEEP(2000); 25 | CommandParser::DoParse("play", "cantina.wav"); 26 | #if 0 27 | while (true) { 28 | if (dac.isSilent()) { 29 | SLEEP(2000); 30 | } else { 31 | CommandParser::DoParse("clash", NULL); 32 | STDOUT.print("alloced: "); 33 | STDOUT.println(mallinfo().uordblks); 34 | SLEEP(100); 35 | } 36 | } 37 | #endif 38 | STATE_MACHINE_END(); 39 | } 40 | void Run() { 41 | state_machine_.reset_state_machine(); 42 | run_ = true; 43 | } 44 | bool run_ = false; 45 | }; 46 | 47 | #endif 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /styles/pixelate.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_PIXELATE_H 2 | #define STYLES_PIXELATE_H 3 | 4 | 5 | // Usage: PixelateX 6 | // OR: Pixelate 7 | // COLOR: COLOR - color values to pixelate 8 | // PIXEL_SIZE_FUNC: FUNCTION size of pixelation (defaults to 2) 9 | // PIXEL_SIZE: NUMBER size of pixelation (defaults to 2) 10 | // Returns: COLOR 11 | 12 | // Pixelate a style. 13 | // If you have a blade that has too many LEDs to run fast, you 14 | // can use Pixelate to make it run faster, since it will 15 | // only calculate "COLOR" half as many times. 16 | // It can also be used as an effect of course. 17 | 18 | template> 19 | class PixelateX { 20 | private: 21 | PONUA COLOR color_; 22 | PONUA N n_; 23 | int last_led_; 24 | decltype(color_.getColor(0)) last_color_; 25 | 26 | public: 27 | LayerRunResult run(BladeBase* blade) { 28 | n_.run(blade); 29 | return RunLayer(&color_, blade); 30 | } 31 | 32 | auto getColor(int led) -> decltype(color_.getColor(led)) { 33 | if (abs(led - last_led_) >= n_.getInteger(led)) { 34 | last_led_ = led; 35 | last_color_ = color_.getColor(led); 36 | } 37 | return last_color_; 38 | } 39 | }; 40 | 41 | template 42 | using Pixelate = PixelateX>; 43 | 44 | #endif // STYLES_PIXELATE_H 45 | -------------------------------------------------------------------------------- /transitions/blink.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_BLINK_H 2 | #define TRANSITIONS_BLINK_H 3 | 4 | #include "base.h" 5 | 6 | // Usage: TrBlinkX 7 | // or: TrBlink 8 | // MILLIS_FUNCTION: FUNCTION 9 | // MILLIS: a number 10 | // N: a number 11 | // WIDTH_FUNCTION: FUNCTION, defaults to Int<16384> 12 | // WIDTH: a number, defaults to 16384 13 | // return value: TRANSITION 14 | // Blinks A-B N times in MILLIS, based on WIDTH (0 ~ 32768) 15 | // If WIDTH = 16384 A and B appear equally, lower decreases length of A, higher increases length of A 16 | 17 | template> 18 | class TrBlinkX : public TransitionBaseX { 19 | public: 20 | void run(BladeBase* blade) { 21 | TransitionBaseX::run(blade); 22 | width_.run(blade); 23 | blink_ = (this->update(32768 * N) & 0x7fff) < width_.calculate(blade); 24 | } 25 | private: 26 | PONUA SVFWrapper width_; 27 | bool blink_; 28 | public: 29 | template 30 | auto getColor(const A& a, const B& b, int led) -> decltype(MixColors(a,b,1,1)) { 31 | if (blink_) { 32 | return a; 33 | } else { 34 | return b; 35 | } 36 | } 37 | }; 38 | 39 | template using TrBlink = TrBlinkX, N, Int>; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /sound/smooth_swing_config.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_SMOOTH_SWING_CONFIG_H 2 | #define SOUND_SMOOTH_SWING_CONFIG_H 3 | 4 | class SmoothSwingConfigFile : public ConfigFile { 5 | public: 6 | void iterateVariables(VariableOP *op) override { 7 | CONFIG_VARIABLE2(Version, 2); 8 | CONFIG_VARIABLE2(SwingSensitivity, 450.0f); 9 | CONFIG_VARIABLE2(MaximumHumDucking, 75.0f); 10 | CONFIG_VARIABLE2(SwingSharpness, 1.75f); 11 | CONFIG_VARIABLE2(SwingStrengthThreshold, 20.0f); 12 | CONFIG_VARIABLE2(Transition1Degrees, 45.0f); 13 | CONFIG_VARIABLE2(Transition2Degrees, 160.0f); 14 | CONFIG_VARIABLE2(Low2HighSeparationDegrees, 180.0); 15 | CONFIG_VARIABLE2(High2LowSeparationDegrees, 180.0); 16 | CONFIG_VARIABLE2(MaxSwingVolume, 3.0f); 17 | CONFIG_VARIABLE2(AccentSwingSpeedThreshold, 0.0f); 18 | CONFIG_VARIABLE2(AccentSlashAccelerationThreshold, 260.0f); 19 | }; 20 | 21 | int Version; 22 | float SwingSensitivity; 23 | float MaximumHumDucking; 24 | float SwingSharpness; 25 | float SwingStrengthThreshold; 26 | float Transition1Degrees; 27 | float Transition2Degrees; 28 | float Low2HighSeparationDegrees; 29 | float High2LowSeparationDegrees; 30 | float MaxSwingVolume; 31 | float AccentSwingSpeedThreshold; 32 | float AccentSlashAccelerationThreshold; 33 | }; 34 | 35 | SmoothSwingConfigFile smooth_swing_config; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /modes/mode.h: -------------------------------------------------------------------------------- 1 | #ifndef MODES_MODE_H 2 | #define MODES_MODE_H 3 | 4 | // Basic mode interface 5 | // allows you to temporarily override buttons and other types of events. 6 | struct ModeInterface { 7 | virtual void mode_activate(bool onreturn) {} 8 | virtual void mode_deactivate() {} 9 | virtual bool mode_Event2(enum BUTTON button, EVENT event, uint32_t modifiers) { 10 | return false; 11 | } 12 | virtual void mode_Loop() {} 13 | virtual bool mode_Parse(const char *cmd, const char* arg) { return false; } 14 | 15 | ModeInterface* previous_; 16 | }; 17 | 18 | ModeInterface* current_mode; 19 | 20 | template 21 | void pushMode() { 22 | PVLOG_DEBUG << __PRETTY_FUNCTION__ << "\n"; 23 | ModeInterface* mode = getPtr(); 24 | mode->previous_ = current_mode; 25 | current_mode = mode; 26 | mode->mode_activate(false); 27 | } 28 | 29 | void popMode() { 30 | PVLOG_DEBUG << __PRETTY_FUNCTION__ << "\n"; 31 | current_mode->mode_deactivate(); 32 | current_mode = current_mode->previous_; 33 | current_mode->mode_activate(true); 34 | } 35 | 36 | namespace mode { 37 | 38 | int menu_current_blade = 1; 39 | int menu_current_arg = 0; 40 | int menu_current_style_offset = 2; 41 | 42 | template typename SPEC::SoundLibrary* getSL() { 43 | return getPtr(); 44 | } 45 | 46 | } // namespace mode 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /styles/transition_pulse.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_TRANSITION_PULSE_H 2 | #define STYLES_TRANSITION_PULSE_H 3 | 4 | #include "../transitions/concat.h" 5 | #include "../functions/effect_increment.h" 6 | 7 | // Usage: TransitionPulseL 8 | // TRANSITION: TRANSITION 9 | // PULSE: FUNCTION 10 | // return value: COLOR 11 | // 12 | // When the specified PULSE happens TRANSITION layer will run. 13 | 14 | template 15 | class TransitionPulseL { 16 | public: 17 | LayerRunResult run(BladeBase* blade) { 18 | SaveLastDetectedBladeEffectScoped save; 19 | pulse_.run(blade); 20 | if (pulse_.calculate(blade)) { 21 | transition_.begin(); 22 | run_ = true; 23 | } 24 | if (run_) { 25 | transition_.run(blade); 26 | if (transition_.done()) run_ = false; 27 | } 28 | return LayerRunResult::UNKNOWN; 29 | } 30 | 31 | private: 32 | bool run_ = false; 33 | TRANSITION transition_; 34 | PONUA SVFWrapper pulse_; 35 | 36 | public: 37 | auto getColor(int led) -> decltype(transition_.getColor(RGBA_um_nod::Transparent(), 38 | RGBA_um_nod::Transparent(), 1)) { 39 | if (run_) { 40 | return transition_.getColor(RGBA_um_nod::Transparent(), 41 | RGBA_um_nod::Transparent(), led); 42 | } else { 43 | return RGBA_um_nod::Transparent(); 44 | } 45 | } 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /common/circular_buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_CIRCULAR_BUFFER_H 2 | #define COMMON_CIRCULAR_BUFFER_H 3 | 4 | #include "atomic.h" 5 | 6 | template 7 | class CircularBuffer { 8 | public: 9 | CircularBuffer() : first_(0), last_(0) {} 10 | 11 | T& current() { return data_[last_.get() % SIZE]; } 12 | T* data() { return data_ + last_.get() % SIZE; } 13 | T* space() { return data_ + first_.get() % SIZE; } 14 | T& next() { return data_[first_.get() % SIZE]; } 15 | 16 | size_t size() const { return first_.get() - last_.get(); } 17 | bool empty() const { return size() == 0; } 18 | size_t space_available() const { return SIZE - size(); } 19 | size_t continuous_space() const { 20 | return std::min(space_available(), SIZE - first_.get() % SIZE); 21 | } 22 | size_t continuous_data() const { 23 | return std::min(size(), SIZE - last_.get() % SIZE); 24 | } 25 | 26 | void push() { first_ += 1; } 27 | void push(size_t elements) { first_ += elements; } 28 | T pop() { 29 | T ret = current(); 30 | last_ += 1; 31 | return ret; 32 | } 33 | void pop(size_t elements) { last_ += elements; } 34 | 35 | void clear() { first_ = 0; last_=0; } 36 | size_t pos() { return last_.get(); } 37 | 38 | private: 39 | T data_[SIZE]; 40 | POAtomic first_; 41 | POAtomic last_; 42 | }; 43 | 44 | #endif // COMMON_CIRCULAR_BUFFER_H 45 | -------------------------------------------------------------------------------- /functions/svf.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_SVF_H 2 | #define FUNCTIONS_SVF_H 3 | 4 | // #include "int.h" 5 | 6 | // An SVF is a FUNCTION that always returns the same value for all LEDs. 7 | // By defining these functions as SVFs instead of plain FUNCTIONS, we 8 | // can use the template system to do some optimizations when we also only 9 | // wish to call the function once per update. 10 | // An SVF cannot count on calculate() being called every frame. 11 | 12 | class SingleValueBase { 13 | public: 14 | int getInteger(int led) { return value_; } 15 | int value_; 16 | }; 17 | 18 | // Converts an SVF to a FUNCTION 19 | template 20 | class SingleValueAdapter : public SingleValueBase { 21 | public: 22 | FunctionRunResult run(BladeBase* blade) { 23 | FunctionRunResult ret = RunFunction(&single_value_function_, blade); 24 | value_ = single_value_function_.calculate(blade); 25 | return ret; 26 | } 27 | PONUA SVF single_value_function_; 28 | }; 29 | 30 | // Converts a FUNCTION to an SVF 31 | template 32 | class SVFWrapper { 33 | public: 34 | FunctionRunResult run(BladeBase* blade) { return RunFunction(&f_, blade); } 35 | int calculate(BladeBase* blade) { return f_.getInteger(0); } 36 | private: 37 | PONUA FUNC f_; 38 | }; 39 | 40 | // Optimized specializations 41 | template 42 | class SVFWrapper> : public SVF {}; 43 | 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /common/ref.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_REF_H 2 | #define COMMON_REF_H 3 | 4 | // Refcounted pointers. 5 | // Calls T->AddRef() / T->SubRef(); 6 | // Subref is assumed to handle any freeing needed. 7 | template 8 | class RefPtr { 9 | public: 10 | RefPtr() : ptr_(nullptr) {} 11 | explicit RefPtr(T* p) : ptr_(p) { AddRef(); } 12 | ~RefPtr() { Free(); } 13 | RefPtr(RefPtr const& other) : ptr_(other.ptr_) { AddRef(); } 14 | RefPtr(RefPtr&& other) { ptr_ = other.ptr_; other.ptr_ = nullptr; } 15 | RefPtr& operator=(RefPtr const& other) { 16 | if (other.ptr_ != ptr_) { 17 | Free(); 18 | ptr_ = other.ptr_; 19 | AddRef(); 20 | } 21 | return *this; 22 | } 23 | 24 | void Free() { if (ptr_) { ptr_->SubRef(); ptr_ = nullptr; } } 25 | T* operator->() { 26 | if (!ptr_) { 27 | STDOUT.print("NULL POINTER! @"); 28 | STDOUT.println((long)__builtin_extract_return_addr( 29 | __builtin_return_address(0)), HEX); 30 | delay(100); 31 | } 32 | return ptr_; 33 | } 34 | T* get() { return ptr_; } 35 | T& operator*() { return *ptr_; } 36 | bool operator==(const RefPtr& other) const { return ptr_ == other.ptr_; } 37 | bool operator!=(const RefPtr& other) const { return ptr_ == other.ptr_; } 38 | explicit operator bool() const { return ptr_ != nullptr; } 39 | 40 | private: 41 | void AddRef() { if (ptr_) ptr_->AddRef(); } 42 | T* ptr_; 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /blades/blade_wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef BLADES_BLADE_WRAPPER_H 2 | #define BLADES_BLADE_WRAPPER_H 3 | 4 | #include "blade_base.h" 5 | 6 | class BladeWrapper : public BladeBase { 7 | public: 8 | int num_leds() const override { return blade_->num_leds(); } 9 | Color8::Byteorder get_byteorder() const override { 10 | return blade_->get_byteorder(); 11 | } 12 | bool is_on() const override { 13 | int blade_number = GetBladeNumber(); 14 | if (blade_number) return SaberBase::BladeIsOn(blade_number); 15 | return blade_->is_on(); 16 | } 17 | bool is_powered() const override { return blade_->is_powered(); } 18 | void set(int led, Color16 c) override { return blade_->set(led, c); } 19 | void set_overdrive(int led, Color16 c) override { 20 | return blade_->set_overdrive(led, c); 21 | } 22 | void clear() override { return blade_->clear(); } 23 | void allow_disable() override { blade_->allow_disable(); } 24 | void Activate(int blade_number) override { blade_->Activate(blade_number); } 25 | void Deactivate() override { blade_->Deactivate(); } 26 | void SetStyle(BladeStyle* style) override { blade_->SetStyle(style); } 27 | BladeStyle* UnSetStyle() override { return blade_->UnSetStyle(); } 28 | BladeStyle* current_style() const override { return blade_->current_style(); } 29 | int GetBladeNumber() const override { return blade_->GetBladeNumber(); } 30 | 31 | BladeBase* blade_; 32 | }; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /functions/hold_peak.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_HOLD_PEAK_H 2 | #define FUNCTIONS_HOLD_PEAK_H 3 | 4 | // Usage: HoldPeakF 5 | // F, HOLD_MILLIS and SPEED: FUNCTION 6 | // return value: FUNCTION, same for all LEDs 7 | // Holds Peak value of F for HOLD_MILLIS. 8 | // then transitions down over SPEED to current F 9 | 10 | class BladeBase; 11 | 12 | template 13 | class HoldPeakF { 14 | public: 15 | void run(BladeBase* blade) { 16 | f_.run(blade); 17 | speed_.run(blade); 18 | hold_time_millis_.run(blade); 19 | int current = f_.calculate(blade); 20 | uint32_t hold_millis = hold_time_millis_.calculate(blade); 21 | uint32_t now = micros(); 22 | uint64_t delta = now - last_micros_; 23 | last_micros_ = now; 24 | if (millis() - last_peak_ > hold_millis) { 25 | if (delta > 1000000) delta = 1; 26 | delta *= speed_.calculate(blade); 27 | delta /= 1000000; 28 | value_ -= delta; 29 | } 30 | if (current > value_) { 31 | value_ = current; 32 | last_peak_ = millis(); 33 | } 34 | } 35 | 36 | int getInteger(int led) { 37 | return value_; 38 | } 39 | 40 | private: 41 | PONUA SVFWrapper f_; 42 | PONUA SVFWrapper hold_time_millis_; 43 | PONUA SVFWrapper speed_; 44 | int value_ = 0; 45 | uint32_t last_micros_ = 0; 46 | uint32_t last_peak_ = millis(); 47 | }; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /styles/remap.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_REMAP_H 2 | #define STYLES_REMAP_H 3 | 4 | 5 | // Usage: Remap 6 | // F: FUNCTION - the remapping function 7 | // COLOR: COLOR - color values to remap 8 | // Returns: COLOR 9 | 10 | // Remap LED positions 11 | // F is a function which returns values between 0 and 32768 12 | // For each pixel, we'll call F and read the corresponding 13 | // location COLOR, with interpolation. 14 | // So if F is a ramp from 0 to 32768 (0 at base 32768 at tip) then nothing changes. 15 | // If F is 32768 at base and 0 at tip, the blade is reversed. 16 | // Other suitable functions: bump, smoothstep, sin, etc. 17 | 18 | template 19 | class Remap { 20 | private: 21 | F f_; 22 | COLOR color_; 23 | int num_leds_; 24 | 25 | public: 26 | LayerRunResult run(BladeBase* blade) { 27 | f_.run(blade); 28 | num_leds_ = blade->num_leds(); 29 | return RunLayer(&color_, blade); 30 | } 31 | 32 | auto getColor(int led) -> decltype(MixColors(color_.getColor(led), 33 | color_.getColor(led), 1, 15)) { 34 | int pos = f_.getInteger(led); 35 | led = clamp(pos * num_leds_, 0, num_leds_ * 32768 - 1); 36 | int fraction = led & 0x7fff; 37 | led = clamp(led >> 15, 0, num_leds_); 38 | return MixColors(color_.getColor(led), 39 | color_.getColor(std::min(led + 1, num_leds_ - 1)), 40 | fraction, 41 | 15); 42 | } 43 | }; 44 | 45 | #endif // STYLES_REMAP_H 46 | -------------------------------------------------------------------------------- /functions/effect_position.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_EFFECTPOS_H 2 | #define FUNCTIONS_EFFECTPOS_H 3 | 4 | // Usage: EffectPosition<> 5 | // Or: EffectPosition 6 | // EFFECT: effect type 7 | // return value: INTEGER 8 | // 9 | // EffectPosition returns the position of a particular effect. 0 = base, 32768 = tip. 10 | // For now, this location is random, but may be set explicitly in the future. 11 | // When used as EffectPosition<> inside a TransitionEffectL whose EFFECT is already specified, 12 | // then it will automatically use the right effect. 13 | 14 | #include "svf.h" 15 | 16 | template 17 | class EffectPositionSVF { 18 | public: 19 | void run(BladeBase* blade) { 20 | BladeEffect* effect; 21 | if (T == EFFECT_NONE) { 22 | effect = last_detected_blade_effect; 23 | } else { 24 | OneshotEffectDetector detector; 25 | effect = detector.Detect(blade); 26 | } 27 | if (effect) value_ = effect->location * 32768; 28 | } 29 | int getInteger() { return value_; } 30 | int getInteger(int led) { return value_; } 31 | int calculate(BladeBase* blade) { return value_; } 32 | private: 33 | int value_ = 0; 34 | }; 35 | 36 | template 37 | using EffectPosition = SingleValueAdapter>; 38 | 39 | // optimized specialization 40 | template 41 | class SingleValueAdapter> : public EffectPositionSVF {}; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /functions/time_since_effect.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_TIME_SINCE_EFFECT_H 2 | #define FUNCTIONS_TIME_SINCE_EFFECT_H 3 | 4 | // Usage: TimeSinceEffect<> 5 | // Or: TimeSinceEffect 6 | // EFFECT: effect type 7 | // return value: INTEGER 8 | // 9 | // TimeSinceEffect returns the number of milliseconds since a particular 10 | // effect occured. 11 | // When used as TimeSinceEffect<> inside a TransitionEffectL whose EFFECT is already specified, 12 | // then it will automatically use the right effect. 13 | 14 | #include "svf.h" 15 | 16 | template 17 | class TimeSinceEffectSVF { 18 | public: 19 | void run(BladeBase* blade) {} 20 | int calculate(BladeBase* blade) { 21 | BladeEffect* effect; 22 | if (T == EFFECT_NONE) { 23 | effect = last_detected_blade_effect; 24 | } else { 25 | OneshotEffectDetector detector; 26 | effect = detector.Detect(blade); 27 | } 28 | if (effect) { 29 | last_occurance_us_ = effect->start_micros; 30 | } 31 | uint32_t now = micros(); 32 | uint32_t ret = now - last_occurance_us_; 33 | if (ret > 1000000000) { 34 | // Prevent weird wraparounds. 35 | last_occurance_us_ = now - 1000000000UL; 36 | } 37 | return ret / 1000; // return ms 38 | } 39 | private: 40 | uint32_t last_occurance_us_ = 0; 41 | }; 42 | 43 | template 44 | using TimeSinceEffect = SingleValueAdapter>; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /functions/change_slowly.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_CHANGE_SLOWLY_H 2 | #define FUNCTIONS_CHANGE_SLOWLY_H 3 | 4 | // Usage: ChangeSlowly 5 | // F, SPEED: FUNCTION 6 | // return value: FUNCTION, same for all LEDs 7 | // Changes F by no more than SPEED values per second. 8 | 9 | class BladeBase; 10 | 11 | template 12 | class ChangeSlowlySVF { 13 | public: 14 | void run(BladeBase* blade) { 15 | f_.run(blade); 16 | speed_.run(blade); 17 | uint32_t now = micros(); 18 | uint64_t delta = now - last_micros_; 19 | last_micros_ = now; 20 | 21 | if (delta > 1000000) delta = 1; 22 | delta *= speed_.calculate(blade); 23 | delta /= 1000000; 24 | int target = f_.calculate(blade); 25 | if (delta > abs(value_ - target)) { 26 | value_ = target; 27 | } else if (value_ < target) { 28 | value_ += delta; 29 | } else { 30 | value_ -= delta; 31 | } 32 | } 33 | int getInteger(int led) { return value_; } 34 | int calculate(BladeBase* blade) { return value_; } 35 | private: 36 | PONUA SVFWrapper f_; 37 | PONUA SVFWrapper speed_; 38 | uint32_t last_micros_ = 0; 39 | int value_; 40 | }; 41 | 42 | // optimized specialization 43 | template 44 | class SingleValueAdapter> : public ChangeSlowlySVF {}; 45 | 46 | template 47 | using ChangeSlowly = SingleValueAdapter>; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /styles/blinking.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_BLINKING_H 2 | #define STYLES_BLINKING_H 3 | 4 | #include "../functions/int.h" 5 | 6 | // Usage: Blinking 7 | // or: BlinkingX 8 | // or: BlinkingL 9 | // A, B: COLOR 10 | // BLINK_MILLIS: a number 11 | // BLINK_PROMILLE: a number, defaults to 500 12 | // BLINK_MILLIS_FUNC: FUNCTION 13 | // BLINK_PROMILLE_FUNC: FUNCTION 14 | // return value: COLOR 15 | // Switches between A and B. 16 | // A full cycle from A to B and back again takes BLINK_MILLIS milliseconds. 17 | // If BLINK_PROMILLE is 500, we select A for the first half and B for the 18 | // second half. If BLINK_PROMILLE is smaller, we get less A and more B. 19 | // If BLINK_PROMILLE is 0, we get all B. 20 | // If BLINK_PROMILLE is 1000 we get all A. 21 | // 22 | 23 | #include "../functions/blinking.h" 24 | 25 | template 26 | using BlinkingL = AlphaL>; 27 | 28 | template 29 | using BlinkingX = Layers>; 30 | 31 | template 35 | using Blinking = BlinkingX, Int >; 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /functions/islessthan.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_ISLESSTHAN_H 2 | #define FUNCTIONS_ISLESSTHAN_H 3 | 4 | // Usage: IsLessThan 5 | // F, V: FUNCTION 6 | // return value: FUNCTION 7 | // Returns 0 or 32768 based on V 8 | // If F < V returns 32768, if F >= V returns 0 9 | 10 | class BladeBase; 11 | 12 | template 13 | class IsLessThanBase { 14 | public: 15 | void run(BladeBase* blade) { 16 | f_.run(blade); 17 | v_.run(blade); 18 | } 19 | int getInteger(int led) { 20 | return (f_.getInteger(led) < v_.getInteger(led)) << 15; 21 | } 22 | 23 | private: 24 | PONUA F f_; 25 | PONUA V v_; 26 | }; 27 | 28 | template 29 | class IsLessThanSVF { 30 | public: 31 | void run(BladeBase* blade) { 32 | svfa_.run(blade); 33 | svfb_.run(blade); 34 | } 35 | int calculate(BladeBase* blade) { 36 | return (svfa_.calculate(blade) < svfb_.calculate(blade)) << 15; 37 | } 38 | private: 39 | PONUA SVFA svfa_; 40 | PONUA SVFB svfb_; 41 | }; 42 | 43 | template struct IsLessThanFinder { typedef IsLessThanBase IsLessThanClass; }; 44 | template struct IsLessThanFinder, SingleValueAdapter> { 45 | typedef SingleValueAdapter> IsLessThanClass; 46 | }; 47 | template using IsLessThan = typename IsLessThanFinder::IsLessThanClass; 48 | 49 | template using IsGreaterThan = IsLessThan; 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /config/blaster_v3_config.h: -------------------------------------------------------------------------------- 1 | #ifdef CONFIG_TOP 2 | #include "v3_config.h" 3 | #define NUM_BLADES 2 4 | #define NUM_BUTTONS 2 5 | #define VOLUME 2000 6 | const unsigned int maxLedsPerStrip = 144; 7 | #define CLASH_THRESHOLD_G 1.0 8 | #define ENABLE_AUDIO 9 | #define ENABLE_MOTION 10 | #define ENABLE_WS2811 11 | #define ENABLE_SD 12 | #define ENABLE_BLASTER_AUTO 13 | #define BLASTER_SHOTS_UNTIL_EMPTY 30 14 | #define BLASTER_JAM_PERCENTAGE 3 15 | #endif 16 | 17 | #ifdef CONFIG_PROP 18 | #include "../props/blaster.h" 19 | #endif 20 | 21 | #ifdef CONFIG_PRESETS 22 | Preset presets[] = { 23 | // Default basic blast color with red audio flicker on blast 24 | { "_blstr1", "tracks/mars.wav", 25 | StylePtr,250,EFFECT_FIRE>,AudioFlicker,1500,EFFECT_STUN>,AudioFlicker>>(), 26 | StylePtr,250,EFFECT_FIRE>,AudioFlicker,1500,EFFECT_STUN>,AudioFlicker>>() } 27 | }; 28 | 29 | BladeConfig blades[] = { 30 | { 10000, WS2811BladePtr<125, WS2811_ACTUALLY_800kHz | WS2811_GRB>(), 31 | WS2811BladePtr<14, WS2811_ACTUALLY_800kHz | WS2811_GRB, 7, PowerPINS >(), 32 | CONFIGARRAY(presets) }, 33 | }; 34 | 35 | #endif 36 | 37 | #ifdef CONFIG_BUTTONS 38 | Button FireButton(BUTTON_FIRE, powerButtonPin, "fire"); 39 | Button ModeButton(BUTTON_MODE_SELECT, auxPin, "modeselect"); 40 | #endif 41 | -------------------------------------------------------------------------------- /functions/wavlen.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_WAVELEN_H 2 | #define FUNCTIONS_WAVELEN_H 3 | 4 | // Usage: WavLen<> 5 | // Or: WavLen 6 | // EFFECT: effect type 7 | // return value: INTEGER 8 | // 9 | // WavLen (length of wav file) takes the duration of a wav file sound 10 | // and can be used to replace time integer arguments in a blade style. 11 | // Example: TrFadeX> 12 | // When used as WavLen<> inside a TransitionEffectL whose EFFECT is already specified, 13 | // then it will automatically use the right effect. 14 | // Example: TransitionEffectL>,White,TrWipeX>>,EFFECT_BLAST> 15 | 16 | template 17 | class WavLenSVF { 18 | public: 19 | void run(BladeBase* blade) { 20 | BladeEffect* effect; 21 | if (T == EFFECT_NONE) { 22 | effect = last_detected_blade_effect; 23 | } else { 24 | OneshotEffectDetector detector; 25 | effect = detector.Detect(blade); 26 | } 27 | if (effect) value_ = effect->sound_length * 1000; // ms 28 | } 29 | int getInteger() { return value_; } 30 | int getInteger(int led) { return value_; } 31 | int calculate(BladeBase* blade) { return value_; } 32 | private: 33 | int value_ = 0; 34 | }; 35 | 36 | template 37 | using WavLen = SingleValueAdapter>; 38 | 39 | // optimized specialization 40 | template 41 | class SingleValueAdapter> : public WavLenSVF {}; 42 | 43 | #endif 44 | -------------------------------------------------------------------------------- /functions/int_arg.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_INT_ARG_H 2 | #define FUNCTIONS_INT_ARG_H 3 | 4 | #include "../common/arg_parser.h" 5 | #include "svf.h" 6 | 7 | class IntArgBase { 8 | public: 9 | FunctionRunResult run(BladeBase* base) { 10 | switch (value_) { 11 | case 0: return FunctionRunResult::ZERO_UNTIL_IGNITION; 12 | case 32768: return FunctionRunResult::ONE_UNTIL_IGNITION; 13 | default: return FunctionRunResult::UNKNOWN; 14 | } 15 | } 16 | int getInteger(int led) { return value_; } 17 | int calculate(BladeBase* blade) { return value_; } 18 | protected: 19 | void init(int argnum) { 20 | char default_value[16]; 21 | itoa(value_, default_value, 10); 22 | const char* arg = CurrentArgParser->GetArg(argnum, "INT", default_value); 23 | if (arg) { 24 | value_ = strtol(arg, NULL, 0); 25 | } 26 | } 27 | 28 | int value_; 29 | }; 30 | 31 | template 32 | class IntArgSVF : public IntArgBase { 33 | public: 34 | IntArgSVF() { 35 | value_ = DEFAULT_VALUE; 36 | init(ARG); 37 | } 38 | }; 39 | 40 | // Optimized specialization 41 | template 42 | class SingleValueAdapter> : public IntArgSVF {}; 43 | template 44 | class SVFWrapper> : public IntArgSVF {}; 45 | 46 | template 47 | using IntArg = SingleValueAdapter>; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /functions/swing_speed.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_SWING_SPEED_H 2 | #define FUNCTIONS_SWING_SPEED_H 3 | 4 | #include "../common/fuse.h" 5 | #include "svf.h" 6 | 7 | // Usage: SwingSpeed 8 | // MAX: FUNCTION 9 | // returned value: FUNCTION 10 | // Returns 0-32768 based on swing speed 11 | 12 | template 13 | class SwingSpeedSVF { 14 | public: 15 | void run(BladeBase* blade) { max_.run(blade); } 16 | int calculate(BladeBase* blade) { 17 | float v = fusor.swing_speed() / max_.calculate(blade); 18 | return clampi32(v * 32768, 0, 32768); 19 | } 20 | private: 21 | PONUA SVFWrapper max_; 22 | }; 23 | 24 | template 25 | using SwingSpeedX = SingleValueAdapter>; 26 | 27 | template 28 | using SwingSpeed = SwingSpeedX>; 29 | 30 | // Usage: SwingAcceleration 31 | // MAX: FUNCTION, defaults to 150 32 | // returned value: FUNCTION 33 | // Returns 0-32768 based on swing acceleration 34 | 35 | template> 36 | class SwingAccelerationSVF { 37 | public: 38 | void run(BladeBase* blade) { max_.run(blade); } 39 | int calculate(BladeBase* blade) { 40 | float v = fusor.swing_accel() / max_.calculate(blade); 41 | return clampi32(v * 32768, 0, 32768); 42 | } 43 | private: 44 | PONUA SVFWrapper max_; 45 | }; 46 | 47 | template> 48 | using SwingAccelerationX = SingleValueAdapter>; 49 | 50 | template 51 | using SwingAcceleration = SwingAccelerationX>; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /functions/smoothstep.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_SMOOTHSTEP_H 2 | #define FUNCTIONS_SMOOTHSTEP_H 3 | 4 | // Usage: SmoothStep 5 | // POS, WIDTH: FUNCTION 6 | // return value: FUNCTION 7 | // POS: specifies the middle of the smoothstep, 0 = base of blade, 32768=tip 8 | // WIDTH: witdth of transition, 0 = no transition, 32768 = length of blade 9 | // Example: SmoothStep, Int<16384>> returns 0 up until 25% of the blade. 10 | // From there it has a smooth transition to 32768, which will be reached at 75% of 11 | // the blade. If WIDTH is negative, the transition will go the other way. 12 | 13 | class SmoothStepBase { 14 | public: 15 | int getInteger(int led) { 16 | int x = led * mult_ - location_; 17 | if (x < 0) return 0; 18 | if (x > 32768) return 32768; 19 | return (((x * x) >> 14) * ((3<<14) - x)) >> 15; 20 | } 21 | protected: 22 | int mult_; 23 | int location_; 24 | }; 25 | 26 | template 27 | class SmoothStep : public SmoothStepBase { 28 | public: 29 | void run(BladeBase* blade) { 30 | pos_.run(blade); 31 | width_.run(blade); 32 | int width = width_.calculate(blade); 33 | if (width == 0) { 34 | mult_ = 32768; 35 | location_ = blade->num_leds() * pos_.calculate(blade); 36 | } else { 37 | mult_ = 32768 * 32768 / width / blade->num_leds(); 38 | location_ = 32768 * pos_.calculate(blade) / width - 16384; 39 | } 40 | } 41 | 42 | PONUA SVFWrapper pos_; 43 | PONUA SVFWrapper width_; 44 | }; 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /common/profiling.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_PROFILING_H 2 | #define COMMON_PROFILING_H 3 | 4 | // #define ENABLE_PROFILING 5 | 6 | #ifdef ENABLE_PROFILING 7 | 8 | class ProfileLocation; 9 | ProfileLocation* profile_locations_ = nullptr; 10 | struct ProfileLocation { 11 | ProfileLocation(const char* location, const char *func) : location_(location), func_(func) { 12 | next_ = profile_locations_; 13 | profile_locations_ = this; 14 | } 15 | void Dump(uint64_t total) { 16 | STDOUT << (cycles_ * 100.0 / total) << " @ "<< location_ << ":"; 17 | for (int i = 0; i < 256; i++) { 18 | if (!func_[i]) break; 19 | STDOUT.print(func_[i]); 20 | } 21 | STDOUT.println(""); 22 | cycles_ = 0; 23 | } 24 | const char* location_; 25 | const char* func_; 26 | ProfileLocation* next_; 27 | uint64_t cycles_ = 0; 28 | 29 | }; 30 | void DumpProfileLocations(uint64_t total) { 31 | for (ProfileLocation* p = profile_locations_; p; p = p->next_) p->Dump(total); 32 | } 33 | uint64_t CountProfileCycles() { 34 | uint64_t ret = 0; 35 | for (ProfileLocation* p = profile_locations_; p; p = p->next_) ret += p->cycles_; 36 | return ret; 37 | } 38 | #define SCOPED_PROFILER() \ 39 | static ProfileLocation trace_location_(__FILE__ ":" TOSTRING(__LINE__), __PRETTY_FUNCTION__); \ 40 | ScopedCycleCounter cycle_counter_(trace_location_.cycles_) 41 | 42 | #else 43 | 44 | #define SCOPED_PROFILER() do { } while(0) 45 | #define DumpProfileLocations(X) do { } while(0) 46 | #define CountProfileCycles() 0 47 | 48 | #endif 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /functions/mod.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_MOD_F_H 2 | #define FUNCTIONS_MOD_F_H 3 | 4 | #include "svf.h" 5 | 6 | class BladeBase; 7 | 8 | template 9 | class ModBase { 10 | public: 11 | void run(BladeBase* blade) { 12 | f_.run(blade); 13 | max_.run(blade); 14 | } 15 | int getInteger(int led) { 16 | int max = max_.getInteger(led); 17 | if (max == 0) { 18 | return 0; 19 | } else { 20 | return MOD(f_.getInteger(led), max); 21 | } 22 | } 23 | private: 24 | PONUA F f_; 25 | PONUA MAX max_; 26 | }; 27 | 28 | // Usage: ModF 29 | // F: FUNCTION 30 | // MAX: FUNCTION (not zero) 31 | // When F is greater than MAX, F wraps to 0 32 | // When F is less than 0, F wraps to MAX 33 | // returns Integer 34 | 35 | template 36 | class ModSVF { 37 | public: 38 | void run(BladeBase* blade) { 39 | f_.run(blade); 40 | max_.run(blade); 41 | } 42 | int calculate(BladeBase* blade) { 43 | int max = max_.calculate(blade); 44 | if (max == 0) { 45 | return 0; 46 | } else { 47 | return MOD(f_.calculate(blade), max); 48 | } 49 | } 50 | private: 51 | PONUA F f_; 52 | PONUA MAX max_; 53 | }; 54 | 55 | template struct ModFinder { typedef ModBase ModClass; }; 56 | template struct ModFinder, SingleValueAdapter> { 57 | typedef SingleValueAdapter> ModClass; 58 | }; 59 | template using ModF = typename ModFinder::ModClass; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /buttons/fast_button.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTONS_FAST_BUTTON_H 2 | #define BUTTONS_FAST_BUTTON_H 3 | 4 | template 5 | class FastButtonBase : public Looper { 6 | public: 7 | USE_PIN_INPUT(PIN, FastButtonBase); 8 | FastButtonBase(const char* name, enum BUTTON button) : name_(name), button_(button) {} 9 | const char* name() override { return name_; } 10 | 11 | void Loop() override { 12 | if (pressed) { 13 | pressed = false; 14 | prop.Event(button_, EVENT_CLICK_SHORT); 15 | } 16 | } 17 | 18 | protected: 19 | static volatile bool pressed; 20 | static void irq() { pressed = true; } 21 | 22 | const char* name_; 23 | enum BUTTON button_; 24 | }; 25 | 26 | template volatile bool FastButtonBase::pressed = false; 27 | 28 | 29 | // Like a button, but can respond to very short events. 30 | // No de-bouncing, only generates EVENT_CLICK_SHORT 31 | template 32 | class FastButton : public FastButtonBase { 33 | public: 34 | FastButton(const char* name, enum BUTTON button) : FastButtonBase(name, button) { 35 | pinMode(PIN, INPUT_PULLUP); 36 | attachInterrupt(PIN, &FastButtonBase::irq, FALLING); 37 | } 38 | }; 39 | 40 | // Like a FastButton, but reacts on rising edge. 41 | template 42 | class FastPullDownButton : public FastButtonBase { 43 | public: 44 | FastPullDownButton(const char* name, enum BUTTON button) : FastButtonBase(name, button) { 45 | pinMode(PIN, INPUT_PULLDOWN); 46 | attachInterrupt(PIN, &FastButtonBase::irq, RISING); 47 | } 48 | }; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /functions/sound_level.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_SOUND_LEVEL_H 2 | #define FUNCTIONS_SOUND_LEVEL_H 3 | #ifdef ENABLE_AUDIO 4 | 5 | // Usage: SmoothSoundLevel 6 | // Returns 0-32768 based on sound level. 7 | // returned value: INTEGER 8 | 9 | class SmoothSoundLevelSVF { 10 | public: 11 | void run(BladeBase* blade) {} 12 | int calculate(BladeBase* blade) { 13 | return clampi32(sqrtf(dynamic_mixer.audio_volume()) * 20, 0, 32768); 14 | } 15 | }; 16 | 17 | using SmoothSoundLevel = SingleValueAdapter; 18 | 19 | // Usage: NoisySoundLevel 20 | // Returns 0-32768 based on sound level. 21 | // returned value: INTEGER 22 | 23 | class NoisySoundLevelSVF { 24 | public: 25 | void run(BladeBase* blade) {} 26 | int calculate(BladeBase* blade) { 27 | return clampi32(dynamic_mixer.last_sum() * 3, 0, 32768); 28 | } 29 | }; 30 | 31 | using NoisySoundLevel = SingleValueAdapter; 32 | 33 | // Usage: NoisySoundLevelCompat 34 | // Returns 0-32768 based on sound level. 35 | // This function is now used to implement the 36 | // AudioFlicker<> style, don't change it. 37 | // returned value: INTEGER 38 | 39 | class NoisySoundLevelCompatSVF { 40 | public: 41 | void run(BladeBase* blade) {} 42 | int calculate(BladeBase* blade) { 43 | return clampi32(dynamic_mixer.last_sum() << 3, 0, 32768); 44 | } 45 | }; 46 | 47 | using NoisySoundLevelCompat = SingleValueAdapter; 48 | 49 | #else // ENABLE_AUDIO 50 | 51 | using NoisySoundLevel = Int<0>; 52 | using NoisySoundLevelCompat = Int<0>; 53 | 54 | #endif // ENABLE_AUDIO 55 | #endif 56 | -------------------------------------------------------------------------------- /functions/sum.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_SUM_H 2 | #define FUNCTIONS_SUM_H 3 | 4 | // Usage: Sum 5 | // A, B: FUNCTION 6 | // return value: FUNCTION 7 | // Adds A + B... 8 | 9 | template 10 | class SumBase { 11 | public: 12 | void run(BladeBase* blade) { 13 | a_.run(blade); 14 | b_.run(blade); 15 | } 16 | int getInteger(int led) { 17 | return a_.getInteger(led) + b_.getInteger(led); 18 | } 19 | 20 | private: 21 | PONUA A a_; 22 | PONUA B b_; 23 | }; 24 | 25 | template 26 | class SumSVF { 27 | public: 28 | void run(BladeBase* blade) { 29 | svfa_.run(blade); 30 | svfb_.run(blade); 31 | } 32 | int calculate(BladeBase* blade) { 33 | return (svfa_.calculate(blade) + svfb_.calculate(blade)); 34 | } 35 | private: 36 | PONUA SVFA svfa_; 37 | PONUA SVFB svfb_; 38 | }; 39 | 40 | // SVF promotion. 41 | template struct SumFinder3 { typedef SumBase SumClass; }; 42 | template struct SumFinder3, SingleValueAdapter> { 43 | typedef SingleValueAdapter> SumClass; 44 | }; 45 | template using SumFinder2 = typename SumFinder3::SumClass; 46 | 47 | // Make Sum<> handle arbitrary number of arguments. 48 | template struct SumFinder {}; 49 | template struct SumFinder { typedef A SumClass; }; 50 | template struct SumFinder { typedef SumFinder2::SumClass> SumClass; }; 51 | template using Sum = typename SumFinder::SumClass; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /functions/blade_angle.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_BLADE_ANGLE_H 2 | #define FUNCTIONS_BLADE_ANGLE_H 3 | 4 | #include "../common/fuse.h" 5 | 6 | // Usage: BladeAngleX 7 | // MIN : FUNCTION (defaults to Int<0>) 8 | // MAX : FUNCTION (defaults to Int<32768>) 9 | // Returns: 0-32768 based on angle of blade 10 | // MIN and MAX specifies the range of angles which are used. 11 | // For MIN and MAX 0 means down and 32768 means up and 16384 means 12 | // pointing towards the horizon. 13 | // So if MIN=16484 and MAX=32768, BladeAngle will return zero when you 14 | // point the blade towards the horizon and 32768 when you point it 15 | // straight up. Any angle below the horizon will also return zero. 16 | // returned value: FUNCTION, same for all LEDs. 17 | 18 | #include "svf.h" 19 | 20 | template, class MAX = Int<32768>> 21 | class BladeAngleXSVF { 22 | public: 23 | void run(BladeBase* blade) { 24 | min_.run(blade); 25 | max_.run(blade); 26 | } 27 | int calculate(BladeBase* blade) { 28 | int min = min_.calculate(blade); 29 | int max = max_.calculate(blade); 30 | // v is 0-32768, 0 = straight down, 32768 = straight up 31 | float v = (fusor.angle1() + M_PI / 2) * 32768 / M_PI; 32 | return clampi32((v - min) * 32768.0f / (max - min), 0, 32768); 33 | } 34 | 35 | PONUA SVFWrapper min_; 36 | PONUA SVFWrapper max_; 37 | }; 38 | 39 | template, class MAX = Int<32768>> 40 | using BladeAngleX = SingleValueAdapter>; 41 | 42 | template 43 | using BladeAngle = BladeAngleX, Int>; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /functions/divide.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_DIVIDE_H 2 | #define FUNCTIONS_DIVIDE_H 3 | 4 | // Usage: Divide 5 | // F, V: FUNCTION, 6 | // return value: FUNCTION 7 | // Divide F by V 8 | // If V = 0, returns 0 9 | // Please note that Divide<> isn't an exact inverse of Mult<> because mult uses fixed-point mathematics 10 | // (it divides the result by 32768) while Divide<> doesn't, it just returns F / V 11 | 12 | class BladeBase; 13 | 14 | template 15 | class DivideBase { 16 | public: 17 | void run(BladeBase* blade) { 18 | f_.run(blade); 19 | v_.run(blade); 20 | } 21 | int getInteger(int led) { 22 | int v = v_.getInteger(led); 23 | if (v == 0) { 24 | return 0; 25 | } else { 26 | return f_.getInteger(led) / v; 27 | } 28 | } 29 | private: 30 | PONUA F f_; 31 | PONUA V v_; 32 | }; 33 | 34 | template 35 | class DivideSVF { 36 | public: 37 | void run(BladeBase* blade) { 38 | f_.run(blade); 39 | v_.run(blade); 40 | } 41 | int calculate(BladeBase* blade) { 42 | int v = v_.calculate(blade); 43 | if (v == 0) { 44 | return 0; 45 | } else { 46 | return f_.calculate(blade) / v; 47 | } 48 | } 49 | private: 50 | PONUA F f_; 51 | PONUA V v_; 52 | }; 53 | 54 | template struct DivideFinder { typedef DivideBase DivideClass; }; 55 | template struct DivideFinder, SingleValueAdapter> { 56 | typedef SingleValueAdapter> DivideClass; 57 | }; 58 | template using Divide = typename DivideFinder::DivideClass; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /common/linked_ptr.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_LINKED_PTR_H 2 | #define COMMON_LINKED_PTR_H 3 | 4 | // Like a refcounted pointer, but uses a double-linked list 5 | // to keep track of all references so that no refs are needed. 6 | 7 | template 8 | class LinkedPtr { 9 | public: 10 | LinkedPtr() : ptr_(nullptr), prev_(this), next_(this) {} 11 | explicit LinkedPtr(T* p) : ptr_(p), prev_(this), next_(this) {} 12 | ~LinkedPtr() { leave(); } 13 | LinkedPtr(LinkedPtr const& other) { join(other); } 14 | LinkedPtr& operator=(LinkedPtr const& other) { 15 | if (&other != this) { 16 | leave(); 17 | join(other); 18 | } 19 | return *this; 20 | } 21 | LinkedPtr& operator=(T* other) { 22 | leave(); 23 | ptr_ = other; 24 | return *this; 25 | } 26 | 27 | T* get() { return ptr_; } 28 | T* operator->() { return ptr_; } 29 | T& operator*() { return *ptr_; } 30 | bool operator==(const LinkedPtr& other) const { return ptr_ == other.ptr_; } 31 | bool operator!=(const LinkedPtr& other) const { return ptr_ != other.ptr_; } 32 | explicit operator bool() const { return ptr_ != nullptr; } 33 | 34 | private: 35 | void leave() { 36 | if (next_ == this) { 37 | Free::Free(ptr_); 38 | } else { 39 | next_->prev_ = prev_; 40 | prev_->next_ = next_; 41 | } 42 | } 43 | void join(LinkedPtr const& other) { 44 | ptr_ = other.ptr_; 45 | prev_ = &other; 46 | next_ = other.next_; 47 | next_->prev_ = this; 48 | prev_->next_ = this; 49 | } 50 | 51 | T* ptr_; 52 | mutable LinkedPtr const *prev_; 53 | mutable LinkedPtr const *next_; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /common/bitfield.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_BITFIELD_H 2 | #define COMMON_BITFIELD_H 3 | 4 | // This class describes a bitfield, for examples of how to use it 5 | // see ../buttons/stm32l4_touchbutton.h 6 | 7 | #ifdef __STM32L4_CMSIS_VERSION 8 | // TODO: replace atomics with inline code. 9 | #include 10 | #define USE_ATOMICS 11 | #endif 12 | 13 | template 14 | struct BitField { 15 | template 16 | struct Field { 17 | static const T MASK = (~((~(T)0) << BITS)) << POS; 18 | Field(T v) : value(v << POS), mask(MASK) {} 19 | Field(T v, T m) : value(v), mask(m) {} 20 | static T get(const BitField& reg) { 21 | return (reg.value & MASK) >> POS; 22 | } 23 | static T get(const volatile BitField& reg) { 24 | return (reg.value & MASK) >> POS; 25 | } 26 | static T get(volatile BitField& reg) { 27 | return (reg.value & MASK) >> POS; 28 | } 29 | static T get(BitField& reg) { 30 | return (reg.value & MASK) >> POS; 31 | } 32 | template 33 | Field operator|(const Field& other) const { 34 | return Field(value | other.value, mask | other.mask); 35 | } 36 | T value; 37 | T mask; 38 | }; 39 | 40 | template T get() volatile { return FIELD::get(*this); } 41 | 42 | template 43 | void set(const Field& v) volatile { 44 | #ifdef USE_ATOMICS 45 | armv7m_atomic_modify(&value, v.mask, v.value); 46 | #else 47 | value = (value & ~v.mask) | v.value; 48 | #endif 49 | } 50 | T value; 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /sound/click_avoider_lin.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_CLICK_AVOIDER_LIN_H 2 | #define SOUND_CLICK_AVOIDER_LIN_H 3 | 4 | #include 5 | 6 | // This class is used to control volumes without causing 7 | // clicking noises. Everytime you call advance(), the value 8 | // goes closer towards the target at a preset speed. 9 | class ClickAvoiderLin { 10 | public: 11 | ClickAvoiderLin() : speed_(0) { } 12 | ClickAvoiderLin(uint32_t speed) : speed_(speed) { } 13 | void set_target(uint32_t target) { target_ = target; } 14 | void set_speed(uint32_t speed) { speed_ = speed; } 15 | void set(uint16_t v) { current_ = v; } 16 | void set_dodge(bool do_dodge) { 17 | dodge_i14_ = do_dodge ? (1 << 12) : (1 << 14); 18 | } 19 | uint32_t value() const { return current_; } 20 | uint32_t get_target() const { 21 | return (target_ * dodge_i14_) >> 14; 22 | } 23 | uint32_t get_speed() const { 24 | if (dodge_i14_ != (1 << 14)) { 25 | return 50 * (1 << 14) / AUDIO_RATE; // FIXME: This should use kMaxVolume 26 | } else { 27 | return speed_; 28 | } 29 | } 30 | void advance() { 31 | uint32_t target = get_target(); 32 | uint32_t speed = get_speed(); 33 | if (current_ > target) { 34 | current_ -= std::min(speed, current_ - target); 35 | return; 36 | } 37 | if (current_ < target) { 38 | current_ += std::min(speed, target - current_); 39 | return; 40 | } 41 | } 42 | bool isConstant() const { 43 | return current_ == get_target(); 44 | } 45 | 46 | uint32_t speed_; 47 | uint32_t current_; 48 | uint32_t target_; 49 | uint32_t dodge_i14_ = 1 << 14; 50 | }; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /blades/abstract_blade.h: -------------------------------------------------------------------------------- 1 | #ifndef BLADES_ABSTRACT_BLADE_H 2 | #define BLADES_ABSTRACT_BLADE_H 3 | #include "blade_base.h" 4 | 5 | BladeBase* GetPrimaryBlade(); 6 | int GetBladeNumber(BladeBase *blade); 7 | 8 | class AbstractBlade : public BladeBase, public SaberBase { 9 | public: 10 | AbstractBlade() : SaberBase(NOLINK) {} 11 | void Activate(int blade_number) override { 12 | blade_number_ = blade_number; 13 | SaberBase::Link(this); 14 | } 15 | void Deactivate() override { 16 | SaberBase::Unlink(this); 17 | } 18 | 19 | void SetStyle(BladeStyle* style) override { 20 | // current_style should be nullptr; 21 | current_style_ = style; 22 | if (current_style_) { 23 | current_style_->activate(); 24 | } 25 | } 26 | 27 | BladeStyle* UnSetStyle() override { 28 | BladeStyle *ret = current_style_; 29 | if (ret) { 30 | ret->deactivate(); 31 | } 32 | current_style_ = nullptr; 33 | return ret; 34 | } 35 | 36 | BladeStyle* current_style() const override { 37 | return current_style_; 38 | } 39 | 40 | int GetBladeNumber() const override { 41 | return blade_number_; 42 | } 43 | 44 | bool CheckBlade(EffectLocation location) override { 45 | extern bool PRINT_CHECK_BLADE; 46 | if (PRINT_CHECK_BLADE) 47 | STDERR << "CheckBlade " << GetBladeNumber() << " @ " << location << " = " << location.on_blade(GetBladeNumber()) << "\n"; 48 | return location.on_blade(GetBladeNumber()); 49 | } 50 | 51 | protected: 52 | BladeStyle *current_style_ = nullptr; 53 | private: 54 | int blade_number_; 55 | }; 56 | 57 | #ifdef BLADE_ID_SCAN_MILLIS 58 | bool ScanBladeIdNow(); 59 | #endif 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /functions/twist_angle.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_TWIST_ANGLE_H 2 | #define FUNCTIONS_TWIST_ANGLE_H 3 | 4 | #include "../common/fuse.h" 5 | 6 | // Usage: TwistAngle 7 | // OFFSET: Adjustable offset (0-32768) to make the twistangle values line up with how you hold the hilt. 8 | // N : Number of times the values goes from 0 to 32768 and back per hilt revolution. 9 | // returned value: FUNCTION, same for all leds 10 | // Returns 0-32768 based on angle of twist 11 | 12 | template 13 | class TwistAngleSVF { 14 | public: 15 | void run(BladeBase* blade) {} 16 | int calculate(BladeBase* blade) { 17 | int a = fusor.angle2() * 32768 / M_PI; 18 | int value = ((a + OFFSET) * N) & 0xffff; 19 | if (value >= 0x8000) value = 0x10000 - value; 20 | return value; 21 | } 22 | }; 23 | 24 | template 25 | using TwistAngle = SingleValueAdapter>; 26 | 27 | // Usage: TwistAcceleration 28 | // MAX : Maximum acceleration needed to return 32768 29 | // returned value: FUNCTION, same for all leds 30 | // Returns 0-32768 based on acceleration of twist in one direction 31 | 32 | template 33 | class TwistAccelerationSVF { 34 | public: 35 | void run(BladeBase* blade) { max_.run(blade); } 36 | int calculate(BladeBase* blade) { 37 | float accel = fusor.twist_accel(); 38 | if (fusor.gyro().z < 0) accel = -accel; 39 | float v = (accel * 32768 / 360) / max_.calculate(blade); 40 | return clampi32(v, 0, 32768); 41 | } 42 | private: 43 | PONUA SVFWrapper max_; 44 | }; 45 | 46 | template 47 | using TwistAcceleration = SingleValueAdapter>>; 48 | #endif 49 | -------------------------------------------------------------------------------- /transitions/fade.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_FADE_H 2 | #define TRANSITIONS_FADE_H 3 | 4 | #include "base.h" 5 | 6 | // Usage: TrFadeX 7 | // or: TrFade 8 | // MILLIS_FUNCTION: FUNCTION 9 | // MILLIS: a number 10 | // return value: TRANSITION 11 | // Linear fading between two colors in specified number of milliseconds. 12 | 13 | template 14 | class TrFadeX : public TransitionBaseX { 15 | public: 16 | void run(BladeBase* blade) { 17 | TransitionBaseX::run(blade); 18 | fade_ = this->update(16384); 19 | } 20 | private: 21 | uint32_t fade_; 22 | public: 23 | template 24 | auto getColor(const A& a, const B& b, int led) AUTO_RETURN(MixColors(a, b, fade_, 14)) 25 | }; 26 | 27 | template using TrFade = TrFadeX>; 28 | 29 | // Usage: TrSmoothFadeX 30 | // or: TrSmoothFade 31 | // MILLIS_FUNCTION: FUNCTION 32 | // MILLIS: a number 33 | // return value: TRANSITION 34 | // Similar to TrFade, but uses a cubic fading function 35 | // so fading starts slow, speeds up in the middle, then 36 | // slows down at the end. 37 | 38 | template 39 | class TrSmoothFadeX : public TransitionBaseX { 40 | public: 41 | void run(BladeBase* blade) { 42 | TransitionBaseX::run(blade); 43 | uint32_t x = this->update(32768); 44 | fade_ = (((x * x) >> 14) * ((3<<14) - x)) >> 16; 45 | } 46 | private: 47 | uint32_t fade_; 48 | public: 49 | template 50 | auto getColor(const A& a, const B& b, int led) AUTO_RETURN(MixColors(a, b, fade_, 14)) 51 | }; 52 | 53 | template using TrSmoothFade = TrSmoothFadeX>; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /modes/bool_setting.h: -------------------------------------------------------------------------------- 1 | #ifndef MODES_BOOL_SETTING_H 2 | #define MODES_BOOL_SETTING_H 3 | 4 | namespace mode { 5 | 6 | 7 | struct BoolSetting { 8 | // virtual bool get() = 0; 9 | // virtual void set(bool value) = 0; 10 | // virtual void say() = 0; 11 | }; 12 | 13 | template 14 | class DirectBoolEntry : public MenuEntry { 15 | public: 16 | void say(int entry) { 17 | getPtr()->say(); 18 | getSL()->SayBool(getPtr()->get()); 19 | } 20 | void select(int entry) { 21 | getPtr()->set(!getPtr()->get()); 22 | say(entry); 23 | } 24 | }; 25 | 26 | // Represents two menu entries. 27 | // This is used by the class below and should generally not be used directly. 28 | template 29 | struct IndirectBoolEntries : public MenuEntry { 30 | static const int size = 2; 31 | void say(int entry) { 32 | getPtr()->say(); 33 | getSL()->SayBool(entry == 1); 34 | } 35 | void select(int entry) { 36 | getPtr()->set(entry == 1); 37 | say(entry); 38 | } 39 | }; 40 | 41 | template 42 | class IndirectBoolEntry : public MenuEntry { 43 | public: 44 | static const int size = 1; 45 | void say(int entry) { 46 | getPtr()->say(); 47 | getSL()->SayBool(getPtr()->get()); 48 | } 49 | typedef typename SPEC::template IndirectBoolEntries ENTRIES; 50 | typedef typename SPEC::template MenuEntryMenu MENU; 51 | static void select(int entry) { 52 | pushMode(); 53 | } 54 | }; 55 | 56 | } // namespace mode 57 | 58 | #endif // MODES_BOOL_SETTING_H 59 | -------------------------------------------------------------------------------- /functions/brown_noise.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_BROWN_NOISE_H 2 | #define FUNCTIONS_BROWN_NOISE_H 3 | 4 | // Usage: BrownNoiseF 5 | // return value: FUNCTION 6 | // Returns a value between 0 and 32768 with nearby pixels being similar. 7 | // GRADE controls how similar nearby pixels are. 8 | 9 | class BladeBase; 10 | 11 | template 12 | class BrownNoiseF { 13 | public: 14 | void run(BladeBase* blade) { 15 | grade_.run(blade); 16 | mix_ = random(32768); 17 | } 18 | int getInteger(int led) { 19 | int grade = grade_.getInteger(led); 20 | mix_ = clampi32(mix_ + random(grade * 2 + 1) - grade, 0, 32768); 21 | return mix_; 22 | } 23 | private: 24 | PONUA GRADE grade_; 25 | uint16_t mix_; 26 | }; 27 | 28 | // Usage: SlowNoise 29 | // return value: FUNCTION 30 | // Returns a value between 0 and 32768 which changes randomly up and 31 | // down over time. All pixels gets the same value. 32 | // SPEED controls how quickly the value changes. 33 | 34 | template 35 | class SlowNoise { 36 | public: 37 | SlowNoise() { 38 | value_ = random(32768); 39 | } 40 | void run(BladeBase* blade) { 41 | speed_.run(blade); 42 | uint32_t now = millis(); 43 | uint32_t delta = now - last_millis_; 44 | if (delta > 100) delta = 1; 45 | last_millis_ = now; 46 | int speed = speed_.calculate(blade); 47 | // This makes the random value update exactly 1000 times per second. 48 | while (delta--) 49 | value_ = clampi32(value_ + (random(speed * 2 + 1) - speed), 0, 32768); 50 | } 51 | int getInteger(int led) { return value_ ; } 52 | private: 53 | PONUA SVFWrapper speed_; 54 | uint32_t last_millis_; 55 | int value_; 56 | }; 57 | 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /common/state_machine.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_STATE_MACHINE_H 2 | #define COMMON_STATE_MACHINE_H 3 | 4 | /* 5 | * State machine code. 6 | * This code uses the fact that switch can jump into code to create 7 | * lightweight pseudo-thread. These threads have a lot of limitations, 8 | * but they can make code much easier to read compared to manually 9 | * written state machines. Lots of examples below. 10 | */ 11 | // Note, you cannot have two YIELD() on the same line. 12 | #define YIELD() do { state_machine_.next_state_ = __LINE__; return; case __LINE__: break; } while(0) 13 | #define SLEEP(MILLIS) do { state_machine_.sleep_until_ = millis() + (MILLIS); while (millis() < state_machine_.sleep_until_) YIELD(); } while(0) 14 | #define SLEEP_MICROS(MICROS) do { state_machine_.sleep_until_ = micros() + (MICROS); while (micros() < state_machine_.sleep_until_) YIELD(); } while(0) 15 | #define STATE_MACHINE_BEGIN() switch(state_machine_.next_state_) { case -1: 16 | #define STATE_MACHINE_END() state_machine_.next_state_ = -2; case -2: break; } 17 | 18 | #define CALL(SM, FUN) do { \ 19 | state_machine_.next_state_ = __LINE__; \ 20 | case __LINE__: \ 21 | FUN(); \ 22 | if (SM.next_state == -2) return; \ 23 | SM.reset_state_machine(); \ 24 | } while(0) 25 | 26 | struct StateMachineState { 27 | int next_state_ = -1; 28 | uint32_t sleep_until_ = 0; 29 | void reset_state_machine() { 30 | next_state_ = -1; 31 | } 32 | bool done() const { return next_state_ == -2; } 33 | void stop() { next_state_ = -2; } 34 | }; 35 | 36 | class StateMachine { 37 | protected: 38 | StateMachineState state_machine_; 39 | }; 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /functions/bullet_count.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_BULLETCOUNT_H 2 | #define FUNCTIONS_BULLETCOUNT_H 3 | 4 | #ifdef BLASTER_SHOTS_UNTIL_EMPTY 5 | 6 | int prop_GetBulletCount(); 7 | 8 | // Usage: BulletCount 9 | // Return 0-N (number of bullets in the magazine.) 10 | // Will cause compilation error if your prop doesn't have bullets. 11 | // You must define BLASTER_SHOTS_UNTIL_EMPTY for this to be available. 12 | 13 | class BulletCountSVF { 14 | public: 15 | void run(BladeBase* blade) {} 16 | int getInteger(int led) { return prop_GetBulletCount(); } 17 | int calculate(BladeBase* blade) { return prop_GetBulletCount(); } 18 | }; 19 | 20 | // Optimized specialization 21 | template<> 22 | class SingleValueAdapter : public BulletCountSVF {}; 23 | 24 | using BulletCountF = SingleValueAdapter; 25 | 26 | 27 | // Usage: BlasterCharge 28 | // Return 0-32768 based on the fullness of the clip / magazine. 29 | // Will cause compilation error if your prop doesn't have bullets. 30 | // You must define BLASTER_SHOTS_UNTIL_EMPTY for this to be available. 31 | // Recommended to be used as the EXTENSION argument to InOutHelperL. 32 | 33 | class BlasterChargeSVF { 34 | public: 35 | void run(BladeBase* blade) {} 36 | int calculate(BladeBase* blade) { 37 | return prop_GetBulletCount() * 32768 / BLASTER_SHOTS_UNTIL_EMPTY; 38 | } 39 | int getInteger(int led) { 40 | return prop_GetBulletCount() * 32768 / BLASTER_SHOTS_UNTIL_EMPTY; 41 | } 42 | }; 43 | 44 | // Optimized specialization 45 | template<> 46 | class SingleValueAdapter : public BlasterChargeSVF {}; 47 | 48 | using BlasterChargeF = SingleValueAdapter; 49 | 50 | #endif 51 | 52 | #endif // FUNCTIONS_BULLETCOUNT_H 53 | -------------------------------------------------------------------------------- /styles/charging.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_CHARGING_H 2 | #define STYLES_CHARGING_H 3 | 4 | // Usage: &style_charging 5 | // return value: POINTER 6 | // Charging blade style. 7 | // Slowly pulsating battery indicator. 8 | 9 | #include "blade_style.h" 10 | 11 | class StyleCharging : public BladeStyle { 12 | public: 13 | void activate() override { 14 | STDOUT.println("Charging Style"); 15 | } 16 | void run(BladeBase *blade) override { 17 | int black_mix = 128 + 100 * sinf(millis() / 500.0); 18 | float volts = battery_monitor.battery(); 19 | Color8 colors[] = { 20 | Color8(0,255,0), // Green > 4.0 21 | Color8(0,0,255), 22 | Color8(255,128,0), 23 | Color8(255,0,0), 24 | Color8(255,0,0) 25 | }; 26 | float x = (4.0 - volts) * 2.0; 27 | int i = floorf(x); 28 | i = clampi32(i, 0, NELEM(colors) - 2); 29 | // Blend colors over 0.1 volts. 30 | int blend = (x - i) * 10 * 255; 31 | blend = clampi32(blend, 0, 255); 32 | Color8 c = colors[i].mix(colors[i + 1], blend); 33 | c = c.mix(Color8(), black_mix); 34 | int num_leds = blade->num_leds(); 35 | 36 | float min_volts = 2.7; 37 | float max_volts = 4.2; 38 | float pos = (volts - min_volts) * num_leds / (max_volts - min_volts); 39 | int p = pos * 32; 40 | for (int i = 0; i < num_leds; i++) { 41 | blade->set(i, Color16(Color8().mix(c, std::max(0, 256 - abs(p - i * 32))))); 42 | } 43 | }; 44 | 45 | bool NoOnOff() override { return true; } 46 | bool Charging() override { return true; } 47 | bool IsHandled(HandledFeature effect) override { return false; } 48 | }; 49 | // No need to templetize this one, as there are no arguments. 50 | StyleFactoryImpl style_charging; 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /common/box_filter.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_BOX_FILTER_H 2 | #define COMMON_BOX_FILTER_H 3 | 4 | template 5 | class BoxFilter { 6 | public: 7 | void clear(const T& v) { 8 | for (int i = 1; i < N; i++) { 9 | data[i] = v; 10 | } 11 | } 12 | void push(const T& v) { 13 | data[pos] = v; 14 | pos++; 15 | if (pos == N) pos = 0; 16 | } 17 | T get() const { 18 | T ret = data[0]; 19 | for (int i = 1; i < N; i++) { 20 | ret += data[i]; 21 | } 22 | return ret / N; 23 | } 24 | T filter(const T& v) { 25 | push(v); 26 | return get(); 27 | } 28 | 29 | T data[N]; 30 | int pos = 0; 31 | }; 32 | 33 | template 34 | class BoxFilter { 35 | public: 36 | void push(const T& v) { 37 | data = v; 38 | } 39 | void clear(const T& v) { 40 | data = v; 41 | } 42 | T get() const { 43 | return data; 44 | } 45 | T filter(const T& v) { 46 | push(v); 47 | return get(); 48 | } 49 | T data; 50 | }; 51 | 52 | template 53 | class BoxFilter { 54 | public: 55 | void push(const T& v) { 56 | data = v; 57 | } 58 | void clear(const T& v) { 59 | data = v; 60 | } 61 | T get() const { 62 | return data; 63 | } 64 | T filter(const T& v) { 65 | push(v); 66 | return get(); 67 | } 68 | T data; 69 | }; 70 | 71 | template 72 | class BoxFilter { 73 | public: 74 | void clear(const T& v) { 75 | data[0] = data[1] = v; 76 | } 77 | void push(const T& v) { 78 | data[0] = data[1]; 79 | data[1] = v; 80 | } 81 | T get() const { 82 | return (data[0] + data[1]) / 2; 83 | } 84 | T filter(const T& v) { 85 | push(v); 86 | return get(); 87 | } 88 | T data[2]; 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /common/range.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_RANGE_H 2 | #define COMMON_RANGE_H 3 | 4 | class Range { 5 | public: 6 | uint32_t start; 7 | uint32_t end; 8 | Range() : start(0), end(0) {} 9 | Range(uint32_t s, uint32_t e) : start(s), end(e) {} 10 | uint32_t size() const { 11 | if (start >= end) return 0; 12 | return end - start; 13 | } 14 | Range operator&(const Range& other) { 15 | return Range(std::max(start, other.start), 16 | std::min(end, other.end)); 17 | } 18 | 19 | // Stripes repeat at some interval infinitely many times. 20 | // These functions lets you calculate the intersection between 21 | // stripes and some range. 22 | 23 | // Stripes are Range(N*mod, N*mod + stripe_width) 24 | // Returns filled area from 0...end 25 | static uint32_t intersect_with_stripes(uint32_t stripe_width, uint32_t mod, uint32_t end) { 26 | return (end / mod) * stripe_width + std::min(stripe_width, end % mod); 27 | } 28 | 29 | // Stripes are Range(N*mod, N*mod + stripe_width) 30 | // Returns filled area from start...end 31 | uint32_t intersect_with_stripes(uint32_t stripe_width, uint32_t mod) const { 32 | return intersect_with_stripes(stripe_width, mod, end) - intersect_with_stripes(stripe_width, mod, start); 33 | } 34 | 35 | // Stripes are Range(N*mod + stripe.start, N*mod + stripe.end) (N can be negative) 36 | // Returns filled area from start ... end 37 | uint32_t intersect_with_stripes(Range stripe, uint32_t mod) const { 38 | // Convert into a stripe which starts at 0 and then call function above. 39 | uint32_t shift = mod - stripe.start; 40 | Range tmp(start + shift, end + shift); 41 | return tmp.intersect_with_stripes(stripe.size(), mod); 42 | } 43 | }; 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /buttons/button.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTONS_BUTTON_H 2 | #define BUTTONS_BUTTON_H 3 | 4 | #include "button_base.h" 5 | 6 | // For all setups following the wiring diagram (V2) described on https://fredrik.hubbe.net/lightsaber/v5/ 7 | // 8 | // momentary buttons are expected to be connected to gnd and 1 of the Proffieboard/Teensy input pins 9 | // 10 | // Usage example, put this in the button config section: 11 | // Button PowerButton(BUTTON_POWER, powerButtonPin, "pow"); 12 | // 13 | 14 | class Button : public ButtonBase { 15 | public: 16 | 17 | Button(enum BUTTON button, int pin, const char* name) : ButtonBase(name, button), pin_(pin) { 18 | pinMode(pin, INPUT_PULLUP); 19 | #ifdef ENABLE_SNOOZE 20 | snooze_digital.pinMode(pin, INPUT_PULLUP, RISING); 21 | #endif 22 | } 23 | 24 | protected: 25 | uint8_t pin_; 26 | 27 | bool Read() override { 28 | return digitalRead(pin_) == LOW; 29 | } 30 | }; 31 | 32 | 33 | // For special setups were you want to hook up a momentary button to VCC(3.3V) or Vbat 34 | // and the other end to the inputs of the Proffieboard / Teensy 35 | // 36 | // For Proffieboards V2 all inputs can handle Vbat, except for Data4 / blade4pin 37 | // 38 | // Usage example, put this in the button config section: 39 | // PullDownButton PowerButton(BUTTON_POWER, powerButtonPin, "pow"); 40 | // 41 | 42 | class PullDownButton : public ButtonBase{ 43 | public: 44 | PullDownButton(enum BUTTON button, int pin, const char* name) : ButtonBase(name, button), pin_(pin) { 45 | pinMode(pin, INPUT_PULLDOWN); 46 | #ifdef ENABLE_SNOOZE 47 | snooze_digital.pinMode(pin, INPUT_PULLDOWN, FALLING); 48 | #endif 49 | } 50 | protected: 51 | uint8_t pin_; 52 | 53 | bool Read() override { 54 | return digitalRead(pin_) == HIGH; 55 | } 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /styles/retraction_delay.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_RETRACTION_DELAY_H 2 | #define STYLES_RETRACTION_DELAY_H 3 | 4 | // Usage: RetractionDelay 5 | // DELAY_MILLIS: a number 6 | // BASE: COLOR 7 | // return value: COLOR 8 | // This class renders BASE as normal, but delays retraction by 9 | // the specified number of milliseconds. 10 | 11 | class BladeBase; 12 | template 13 | class RetractionDelayBase : public BladeWrapper { 14 | public: 15 | void run(BladeBase* base) { 16 | millis_.run(base); 17 | blade_ = base; 18 | if (!base->is_on()) { 19 | if (!waiting_) { 20 | waiting_ = true; 21 | wait_start_time_ = millis(); 22 | } 23 | uint32_t waited = millis() - wait_start_time_; 24 | int delay_millis = millis_.calculate(base); 25 | if (waited > delay_millis) { 26 | is_on_ = false; 27 | wait_start_time_ = millis() - delay_millis - 1; 28 | } 29 | } else { 30 | waiting_ = false; 31 | is_on_ = true; 32 | } 33 | } 34 | bool is_on() const override { return is_on_; } 35 | private: 36 | bool is_on_ = false; 37 | bool waiting_ = false; 38 | uint32_t wait_start_time_; 39 | PONUA SVFWrapper millis_; 40 | }; 41 | 42 | template 43 | class RetractionDelayX : public RetractionDelayBase { 44 | public: 45 | bool run(BladeBase* base) __attribute__((warn_unused_result)) { 46 | RetractionDelayBase::run(base); 47 | return RunStyle(&base_, this); 48 | } 49 | private: 50 | BASE base_; 51 | public: 52 | auto getColor(int led) ->decltype(base_.getColor(led)) { return base_.getColor(led); } 53 | }; 54 | 55 | template 56 | using RetractionDelay = RetractionDelayX, BASE>; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /transitions/join.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITIONS_JOIN_H 2 | #define TRANSITIONS_JOIN_H 3 | 4 | // Usage: TrJoin 5 | // TR1, TR2: TRANSITION 6 | // return value: TRANSITION 7 | // A little hard to explain, but all the specified 8 | // transitions are run in parallel. Basically, we 9 | // chain transitions like ((A TR1 B) TR2 B) 10 | 11 | template class TrJoin {}; 12 | 13 | template class TrJoin : public X {}; 14 | 15 | template 16 | class TrJoin { 17 | public: 18 | void begin() { a_.begin(); b_.begin(); } 19 | bool done() { return a_.done() && b_.done(); } 20 | void run(BladeBase* blade) { 21 | a_.run(blade); 22 | b_.run(blade); 23 | } 24 | private: 25 | PONUA A a_; 26 | PONUA TrJoin b_; 27 | public: 28 | template 29 | auto getColor(const X& a, const Y& b, int led) AUTO_RETURN(b_.getColor(a_.getColor(a, b, led), b, led)) 30 | }; 31 | 32 | // Usage: TrJoinR 33 | // TR1, TR2: TRANSITION 34 | // return value: TRANSITION 35 | // Similar to TrJoin, but transitions are chained 36 | // to the right instead of to the left. Like: 37 | // (A TR2 (A TR1 B)) 38 | 39 | template class TrJoinR {}; 40 | template class TrJoinR : public X {}; 41 | 42 | template 43 | class TrJoinR { 44 | public: 45 | void begin() { a_.begin(); b_.begin(); } 46 | bool done() { return a_.done() && b_.done(); } 47 | void run(BladeBase* blade) { 48 | a_.run(blade); 49 | b_.run(blade); 50 | } 51 | private: 52 | PONUA A a_; 53 | PONUA TrJoinR b_; 54 | public: 55 | template 56 | auto getColor(const X& a, const Y& b, int led) AUTO_RETURN(b_.getColor(a, a_.getColor(a, b, led), led)); 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /common/clock_control.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_CLOCK_CONTROL_H 2 | #define COMMON_CLOCK_CONTROL_H 3 | 4 | #if VERSION_MAJOR >= 4 5 | 6 | #include "looper.h" 7 | 8 | class ClockControl : public Looper { 9 | public: 10 | const char* name() override { return "ClockControl"; } 11 | void Loop() override { 12 | bool on = false; 13 | SaberBase::DoIsOn(&on); 14 | if (on 15 | || Serial 16 | || prop.NeedsPower() 17 | || USBD_Connected() 18 | || stm32l4_gpio_pin_read(GPIO_PIN_PB2) 19 | #ifdef ENABLE_AUDIO 20 | || amplifier.Active() 21 | #endif 22 | ) { 23 | last_activity_ = millis(); 24 | } 25 | // These two variables must be read in order. 26 | uint32_t last_activity = last_activity_; 27 | uint32_t now = millis(); 28 | if (now - last_activity > 30000) { 29 | uint32_t pclk1 = stm32l4_system_pclk1(); 30 | uint32_t pclk2 = stm32l4_system_pclk2(); 31 | #if 0 // #ifdef PROFFIEBOARD_VERSION 32 | // This saves power, but also casuses freezing. 33 | // TODO: FIgure out why and re-enable. 34 | stm32l4_system_sysclk_configure(1000000, 500000, 500000); 35 | #else 36 | stm32l4_system_sysclk_configure(16000000, 8000000, 8000000); 37 | #endif 38 | #ifdef COMMON_I2CBUS_H 39 | // Motion and other things might still be going on. 40 | if (i2cbus.used()) 41 | delay(5); 42 | else 43 | #endif 44 | delay(50); 45 | stm32l4_system_sysclk_configure(_SYSTEM_CORE_CLOCK_, pclk1, pclk2); 46 | } 47 | } 48 | 49 | void AvoidSleep() { last_activity_ = millis(); } 50 | 51 | private: 52 | volatile uint32_t last_activity_; 53 | }; 54 | 55 | ClockControl clock_control; 56 | 57 | void ClockControl_AvoidSleep() { clock_control.AvoidSleep(); } 58 | 59 | #else 60 | 61 | void ClockControl_AvoidSleep() { } 62 | 63 | #endif 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /styles/ignition_delay.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_IGNITION_DELAY_H 2 | #define STYLES_IGNITION_DELAY_H 3 | 4 | // Usage: IgnitionDelay 5 | // DELAY_MILLIS: a number 6 | // BASE: COLOR 7 | // return value: COLOR 8 | // This class renders BASE as normal, but delays ignition by 9 | // the specified number of milliseconds. Intended for kylo-style 10 | // quillions. 11 | class BladeBase; 12 | 13 | template 14 | class IgnitionDelayBase : public BladeWrapper { 15 | public: 16 | void run(BladeBase* base) { 17 | millis_.run(base); 18 | blade_ = base; 19 | if (base->is_on()) { 20 | if (!waiting_) { 21 | waiting_ = true; 22 | wait_start_time_ = millis(); 23 | } 24 | int delay_millis = millis_.calculate(base); 25 | uint32_t waited = millis() - wait_start_time_; 26 | if (delay_millis < 0 || (waited > (uint32_t)delay_millis)) { 27 | is_on_ = true; 28 | wait_start_time_ = millis() - delay_millis - 1; 29 | } 30 | } else { 31 | waiting_ = false; 32 | is_on_ = false; 33 | } 34 | } 35 | bool is_on() const override { return is_on_; } 36 | private: 37 | bool is_on_ = false; 38 | bool waiting_ = false; 39 | uint32_t wait_start_time_; 40 | SVFWrapper millis_; 41 | }; 42 | 43 | template 44 | class IgnitionDelayX : public IgnitionDelayBase { 45 | public: 46 | bool run(BladeBase* base) __attribute__((warn_unused_result)) { 47 | IgnitionDelayBase::run(base); 48 | return RunStyle(&base_, this); 49 | } 50 | private: 51 | BASE base_; 52 | public: 53 | auto getColor(int led) ->decltype(base_.getColor(led)) { return base_.getColor(led); } 54 | }; 55 | 56 | template 57 | using IgnitionDelay = IgnitionDelayX, BASE>; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /styles/rgb_arg.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_RGB_ARG_H 2 | #define STYLES_RGB_ARG_H 3 | 4 | #include "../common/arg_parser.h" 5 | 6 | // Usage: RgbArg 7 | // ARG: a number 8 | // DEFAULT_COLOR: Must be Rgb<> or Rgb16<> 9 | // Return value: COLOR 10 | // This is used to create templates that can be configured dynamically. 11 | // These templates can be assigned to presets from WebUSB or bluetooth. 12 | // See style_parser.h for more details. 13 | 14 | // Break out template-invariant functionality to save memory. 15 | class RgbArgBase { 16 | public: 17 | LayerRunResult run(BladeBase* base) { 18 | if (color_.r == 0 && color_.g == 0 && color_.b == 0) 19 | return LayerRunResult::OPAQUE_BLACK_UNTIL_IGNITION; 20 | return LayerRunResult::UNKNOWN; 21 | } 22 | SimpleColor getColor(int led) { 23 | return SimpleColor(color_); 24 | } 25 | protected: 26 | void init(int argnum) { 27 | char default_value[32]; 28 | itoa(color_.r, default_value, 10); 29 | strcat(default_value, ","); 30 | itoa(color_.g, default_value + strlen(default_value), 10); 31 | strcat(default_value, ","); 32 | itoa(color_.b, default_value + strlen(default_value), 10); 33 | 34 | const char* arg = CurrentArgParser->GetArg(argnum, "COLOR", default_value); 35 | if (arg) { 36 | char* tmp; 37 | int r = strtol(arg, &tmp, 0); 38 | int g = strtol(tmp+1, &tmp, 0); 39 | int b = strtol(tmp+1, NULL, 0); 40 | // TODO: color names? 41 | color_ = Color16(r, g, b); 42 | } 43 | } 44 | 45 | Color16 color_; 46 | }; 47 | 48 | template 49 | class RgbArg : public RgbArgBase{ 50 | public: 51 | RgbArg() { 52 | DEFAULT_COLOR default_color; // Note, no run() call for default_color! 53 | color_ = default_color.getColor(0).c; 54 | init(ARG); 55 | } 56 | }; 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /functions/isbetween.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_ISBETWEEN_H 2 | #define FUNCTIONS_ISBETWEEN_H 3 | 4 | // Usage: IsBetween 5 | // F, BOTTOM, TOP: INTEGER 6 | // return value: INTEGER 7 | // Returns 0 or 32768 based F > BOTTOM and < TOP 8 | 9 | class BladeBase; 10 | 11 | template 12 | class IsBetweenBase { 13 | public: 14 | void run(BladeBase* blade) { 15 | f_.run(blade); 16 | bottom_.run(blade); 17 | top_.run(blade); 18 | } 19 | int getInteger(int led) { 20 | int f = f_.getInteger(led); 21 | if (f > bottom_.getInteger(led) && f < top_.getInteger(led)) { 22 | return 32768; 23 | } else { 24 | return 0; 25 | } 26 | } 27 | 28 | private: 29 | PONUA F f_; 30 | PONUA BOTTOM bottom_; 31 | PONUA TOP top_; 32 | }; 33 | 34 | template 35 | class IsBetweenSVF { 36 | public: 37 | void run(BladeBase* blade) { 38 | svf_f_.run(blade); 39 | svf_bottom_.run(blade); 40 | svf_top_.run(blade); 41 | } 42 | int calculate(BladeBase* blade) { 43 | int f = svf_f_.calculate(blade); 44 | if (f > svf_bottom_.calculate(blade) && f < svf_top_.calculate(blade)) { 45 | return 32768; 46 | } else { 47 | return 0; 48 | } 49 | } 50 | private: 51 | PONUA SVF_F svf_f_; 52 | PONUA SVF_BOTTOM svf_bottom_; 53 | PONUA SVF_TOP svf_top_; 54 | }; 55 | 56 | template struct IsBetweenFinder { typedef IsBetweenBase IsBetweenClass; }; 57 | template struct IsBetweenFinder, SingleValueAdapter, SingleValueAdapter> { 58 | typedef SingleValueAdapter> IsBetweenClass; 59 | }; 60 | template using IsBetween = typename IsBetweenFinder::IsBetweenClass; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /functions/clamp.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_CLAMP_H 2 | #define FUNCTIONS_CLAMP_H 3 | 4 | #include "int.h" 5 | #include "svf.h" 6 | 7 | // Usage: ClampF 8 | // Or: ClampFX 9 | // F, MIN, MAX: INTEGER 10 | // MINCLASS, MAXCLASS: FUNCTION 11 | // return value: INTEGER 12 | // Clamps value between MIN and MAX 13 | 14 | template 15 | class ClampBase { 16 | public: 17 | void run(BladeBase* blade) { 18 | f_.run(blade); 19 | min_.run(blade); 20 | max_.run(blade); 21 | } 22 | int getInteger(int led) { 23 | int min = min_.getInteger(led); 24 | int max = max_.getInteger(led); 25 | return clampi32(f_.getInteger(led), min, max); 26 | } 27 | 28 | private: 29 | PONUA F f_; 30 | PONUA MIN min_; 31 | PONUA MAX max_; 32 | }; 33 | 34 | template 35 | class ClampSVF { 36 | public: 37 | void run(BladeBase* blade) { 38 | f_.run(blade); 39 | min_.run(blade); 40 | max_.run(blade); 41 | } 42 | int calculate(BladeBase* blade) { 43 | int min = min_.calculate(blade); 44 | int max = max_.calculate(blade); 45 | return clampi32(f_.calculate(blade), min, max); 46 | } 47 | private: 48 | PONUA SVFWrapper f_; 49 | PONUA SVFWrapper min_; 50 | PONUA SVFWrapper max_; 51 | }; 52 | 53 | template struct ClampFinder { typedef ClampBase ClampClass; }; 54 | template struct ClampFinder, SingleValueAdapter, SingleValueAdapter> { 55 | typedef SingleValueAdapter> ClampClass; 56 | }; 57 | 58 | template, class MAX = Int<32768>> 59 | using ClampFX = typename ClampFinder::ClampClass; 60 | 61 | template 62 | using ClampF = ClampFX, Int>; 63 | 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /styles/length_finder.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_LENGTH_FINDER_H 2 | #define STYLES_LENGTH_FINDER_H 3 | 4 | // Usage: LengthFinder 5 | // or: LengthFinder<> 6 | // BASE, LIGHTUP: COLOR 7 | // Return value: COLOR 8 | // Lights up exactly one led, based on the current color change 9 | // variable. When changed, says what the current color change is 10 | // so that you know which led is lit up. 11 | // Use this when you don't know how many LEDs are in your blade. 12 | // Set the blade length to 144, enter color change mode and 13 | // change it until no LED turns on, go back one and there you go! 14 | template 15 | class LengthFinder { 16 | public: 17 | LengthFinder() { 18 | BladeBase::HandleFeature(HANDLED_FEATURE_CHANGE); 19 | BladeBase::HandleFeature(HANDLED_FEATURE_CHANGE_TICKED); 20 | } 21 | void run(BladeBase* blade) { 22 | base_.run(blade); 23 | lightup_.run(blade); 24 | if (SaberBase::GetCurrentVariation() < 0) { 25 | SaberBase::SetVariation(0); 26 | } 27 | if (SaberBase::GetCurrentVariation() >= blade->num_leds()) { 28 | SaberBase::SetVariation(blade->num_leds() - 1); 29 | } 30 | if (SaberBase::GetCurrentVariation() != led_) { 31 | led_ = SaberBase::GetCurrentVariation(); 32 | say_it_ = true; 33 | } 34 | if (say_it_ && millis() - last_speak_ > 3000) { 35 | last_speak_ = millis(); 36 | say_it_ = false; 37 | #ifdef ENABLE_AUDIO 38 | talkie.SayNumber(led_ + 1); 39 | #endif 40 | STDOUT << "LEN=" << (led_ + 1) << "\n"; 41 | } 42 | } 43 | 44 | OverDriveColor getColor(int led) { 45 | if (led == led_) return lightup_.getColor(led); 46 | return base_.getColor(led); 47 | } 48 | private: 49 | bool say_it_ = true; 50 | int led_= -1; 51 | uint32_t last_speak_ = 0; 52 | BASE base_; 53 | LIGHTUP lightup_; 54 | }; 55 | 56 | #endif // STYLES_LENGTH_FINDER_H 57 | -------------------------------------------------------------------------------- /functions/mult.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_MULT_H 2 | #define FUNCTIONS_MULT_H 3 | 4 | // Usage: Mult 5 | // F, V: FUNCTION 6 | // return value: FUNCTION 7 | // Fixed point multiplication of values F * V, 8 | // fixed point 16.15 arithmetic (32768 = 1.0) 9 | // (2*2 would not result in 4), 10 | // (16384 * 16384 = 8192, representation of 0.5*0.5=0.25) 11 | // most blade functions use this method of fixed point calculations 12 | 13 | class BladeBase; 14 | 15 | template 16 | class MultBase { 17 | public: 18 | void run(BladeBase* blade) { 19 | f_.run(blade); 20 | v_.run(blade); 21 | } 22 | int getInteger(int led) { 23 | return (f_.getInteger(led) * v_.getInteger(led)) >> 15; 24 | } 25 | private: 26 | PONUA F f_; 27 | PONUA V v_; 28 | }; 29 | 30 | template 31 | class MultSVF { 32 | public: 33 | void run(BladeBase* blade) { 34 | f_.run(blade); 35 | v_.run(blade); 36 | } 37 | int calculate(BladeBase* blade) { 38 | return (f_.calculate(blade) * v_.calculate(blade)) >> 15; 39 | } 40 | private: 41 | PONUA F f_; 42 | PONUA V v_; 43 | }; 44 | 45 | template struct MultFinder { typedef MultBase MultClass; }; 46 | template struct MultFinder, SingleValueAdapter> { 47 | typedef SingleValueAdapter> MultClass; 48 | }; 49 | template using Mult = typename MultFinder::MultClass; 50 | 51 | // Usage: Percentage 52 | // F: FUNCTION 53 | // V: INTEGER 54 | // return value: FUNCTION 55 | // Gets Percentage V of value F, 56 | // Percentages over 100% are allowed and will effectively be a multiplier. 57 | // example Percentage,25> 58 | // this will give you 25% of Int<16384> and returns Int<4096> 59 | 60 | template //floats do no work in class templates... 61 | using Percentage = Mult>; 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /sound/beeper.h: -------------------------------------------------------------------------------- 1 | #ifndef SOUND_BEEPER_H 2 | #define SOUND_BEEPER_H 3 | 4 | #include "../common/circular_buffer.h" 5 | 6 | // Beeper class, used for warning beeps and such. 7 | class Beeper : public ProffieOSAudioStream { 8 | public: 9 | Beeper() : ProffieOSAudioStream(), x_(0), value_(0) {} 10 | 11 | int read(int16_t *data, int elements) override { 12 | SCOPED_PROFILER(); 13 | int e = elements; 14 | while (beeps_.size()) { 15 | int s = std::min(elements, beeps_.current().samples_); 16 | if (x_.get() == 0) { 17 | x_.set(beeps_.current().f_); 18 | if (x_.get() == 0) { 19 | x_.set(beeps_.current().samples_); 20 | value_.set(0); 21 | } else { 22 | if (value_.get() <= 0) { 23 | value_.set(200); 24 | } else { 25 | value_.set(-200); 26 | } 27 | } 28 | } 29 | s = std::min(s, (int)x_.get()); 30 | if (s <= 0) return e - elements; 31 | for (int i = 0; i < s; i++) data[i] = value_.get(); 32 | data += s; 33 | elements -= s; 34 | x_ -= s; 35 | beeps_.current().samples_ -= s; 36 | if (beeps_.current().samples_ == 0) { 37 | beeps_.pop(); 38 | } 39 | } 40 | return e - elements; 41 | } 42 | 43 | void Beep(float length, float freq) { 44 | EnableAmplifier(); 45 | if (beeps_.space_available()) { 46 | beeps_.next().f_ = freq == 0.0 ? 0 : AUDIO_RATE / freq / 2.0; 47 | beeps_.next().samples_ = AUDIO_RATE * length; 48 | beeps_.push(); 49 | } 50 | } 51 | void Silence(float length) { 52 | Beep(length, 0.0); 53 | } 54 | 55 | bool isPlaying() { 56 | return beeps_.size() > 0; 57 | } 58 | 59 | bool eof() const override { 60 | return beeps_.size() == 0; 61 | } 62 | 63 | private: 64 | struct Beep { 65 | int f_ = 0; 66 | int samples_ = 0; 67 | }; 68 | CircularBuffer beeps_; 69 | POAtomic x_; 70 | POAtomic value_; 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /styles/sequence.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_SEQUENCE_H 2 | #define STYLES_SEQUENCE_H 3 | 4 | // Usage: Sequence 5 | // COLOR1: COLOR 6 | // COLOR2: COLOR 7 | // millis_per_bit: millseconds spent on each bit 8 | // bits: number of bits before we loop around to the beginning 9 | // 0b0000000000000000: 16-bit binary numbers containing the actual sequence. 10 | // 11 | // Shows COLOR1 if the current bit in the sequence is 1, COLOR2 otherwise. 12 | // The number of 16-bit binary numbers should be at least |bits| / 16, rounded up. 13 | // Note that if not all bits are used within the 16-bit number. 14 | // Example, a red SOS pattern: 15 | // Sequence 16 | 17 | #include "../functions/sequence.h" 18 | 19 | template 20 | using SequenceL = AlphaL>; 21 | 22 | template 23 | using Sequence = Layers>; 24 | 25 | 26 | template 27 | class ColorSequence { 28 | public: 29 | void run(BladeBase* blade) { 30 | colors_.run(blade); 31 | uint32_t now = micros(); 32 | if (now - last_micros_ > millis_per_color * 1000) { 33 | if (now - last_micros_ > millis_per_color * 10000) { 34 | n_ = 0; 35 | last_micros_ = now; 36 | } else { 37 | n_ = (n_ + 1) % sizeof...(COLORS); 38 | last_micros_ += millis_per_color * 1000; 39 | } 40 | } 41 | } 42 | private: 43 | uint32_t last_micros_; 44 | int n_; 45 | MixHelper colors_; 46 | 47 | public: 48 | auto getColor(int led) -> decltype(colors_.getColor(n_, led)) { 49 | return colors_.getColor(n_, led); 50 | } 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /config/default_v3_config.h: -------------------------------------------------------------------------------- 1 | // This is a sample configuration file. 2 | // This saber has: 3 | // o TeensySaber V3 hardware. 4 | // o Two buttons 5 | // o An XP-E2 RGB LED star. 6 | // If you have a saber similar to this one, make a copy and use the copy. 7 | // This is also the default configuration file. Pre-programmed boards will 8 | // use this configuration file. 9 | 10 | #ifdef CONFIG_TOP 11 | #include "v3_config.h" 12 | #define NUM_BLADES 1 13 | #define NUM_BUTTONS 2 14 | #define VOLUME 2200 15 | const unsigned int maxLedsPerStrip = 144; 16 | #define CLASH_THRESHOLD_G 1.0 17 | #define ENABLE_AUDIO 18 | #define ENABLE_MOTION 19 | #define ENABLE_WS2811 20 | #define ENABLE_SD 21 | #endif 22 | 23 | #ifdef CONFIG_PRESETS 24 | Preset presets[] = { 25 | { "TeensySF", "tracks/mars.wav", 26 | StyleNormalPtr(), "cyan"}, 27 | { "TeensySF", "tracks/mars.wav", 28 | StylePtr >(), "blue"}, 29 | { "TeensySF", "tracks/mars.wav", 30 | StyleNormalPtr(), "red"}, 31 | { "TeensySF", "tracks/mars.wav", 32 | StylePtr, WHITE), 300, 800> >(), "green"}, 33 | { "TeensySF", "tracks/mars.wav", 34 | StyleNormalPtr(), "white"}, 35 | { "TeensySF", "tracks/mars.wav", 36 | StyleNormalPtr, BLUE, 300, 800>(), "yellow"}, 37 | { "TeensySF", "tracks/mars.wav", 38 | StylePtr >(), "magenta"}, 39 | { "TeensySF", "tracks/mars.wav", 40 | StyleStrobePtr(), "strobe"} 41 | }; 42 | BladeConfig blades[] = { 43 | { 0, SimpleBladePtr(), CONFIGARRAY(presets) }, 44 | }; 45 | #endif 46 | 47 | #ifdef CONFIG_BUTTONS 48 | Button PowerButton(BUTTON_POWER, powerButtonPin, "pow"); 49 | Button AuxButton(BUTTON_AUX, auxPin, "aux"); 50 | #endif 51 | -------------------------------------------------------------------------------- /blades/blades.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define APPDIR(X) APPDIR2(/home/hubbe/hack/teensy/lightsaber/lightsaber/X) 4 | #define APPDIR2(X) #X 5 | 6 | const unsigned int maxLedsPerStrip = 144; 7 | 8 | int32_t clampi32(int32_t x, int32_t a, int32_t b) { 9 | if (x < a) return a; 10 | if (x > b) return b; 11 | return x; 12 | } 13 | 14 | #include APPDIR(common/color.h) 15 | 16 | #include "stm32l4_ws2811.h" 17 | #include "pwm_pin.h" 18 | 19 | WS2811Pin ws2811_pin; 20 | 21 | SimplePWMPin<10> pwm_pin1; 22 | SimplePWMPin<4> pwm_pin2; 23 | SimplePWMPin<18> pwm_pin3; 24 | 25 | // #define WS2811 26 | #define PWM 27 | 28 | void setup() { 29 | pinMode(0, OUTPUT); 30 | pinMode(1, OUTPUT); 31 | pinMode(2, OUTPUT); 32 | pinMode(9, OUTPUT); 33 | Serial.begin(115200); 34 | for (int i = 0; i < 4; i++) { 35 | Serial.println(i); 36 | delay(1000); 37 | } 38 | #ifdef WS2811 39 | ws2811_pin.begin(8, 1, WS2811Pin::Byteorder::BGR, 800000, 300); 40 | #endif 41 | 42 | #ifdef PWM 43 | Serial.println("INIT PWM"); 44 | pwm_pin1.Activate(); 45 | pwm_pin1.set(32768); 46 | delayMicroseconds(500); 47 | 48 | pwm_pin2.Activate(); 49 | pwm_pin2.set(32768); 50 | 51 | pwm_pin3.Activate(); 52 | pwm_pin1.set(32768); 53 | #else 54 | analogWrite(10, 127); 55 | #endif 56 | Serial.println("BEGIN"); 57 | } 58 | 59 | uint64_t loop_count = 0; 60 | 61 | void loop() { 62 | loop_count++; 63 | #ifdef WS2811 64 | digitalWrite(0, HIGH); 65 | Color8 c(loop_count & 0xff, 0xAA, 0x03); 66 | ws2811_pin.BeginFrame(); 67 | digitalWrite(0, LOW); 68 | 69 | digitalWrite(1, HIGH); 70 | ws2811_pin.Set(0, c); 71 | ws2811_pin.EndFrame(); 72 | digitalWrite(1, LOW); 73 | #endif 74 | 75 | #ifdef PWM 76 | uint32_t i = millis(); 77 | pwm_pin1.set(i % 6000); 78 | pwm_pin2.set(i % 6000); 79 | pwm_pin3.set(i % 6000); 80 | #endif 81 | 82 | if (loop_count < 20) { 83 | Serial.print("loop "); 84 | Serial.println(millis()); 85 | } 86 | } 87 | 88 | -------------------------------------------------------------------------------- /common/string_piece.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_STRING_PIECE_H 2 | #define COMMON_STRING_PIECE_H 3 | 4 | int constexpr toLower(char x) { 5 | return (x >= 'A' && x <= 'Z') ? x - 'A' + 'a' : x; 6 | } 7 | 8 | class Print; 9 | struct StringPiece { 10 | StringPiece(const char* s) : str(s), len(strlen(s)) {} 11 | StringPiece(const char* s, size_t l) : str(s), len(l) {} 12 | StringPiece(const char* begin, const char* end) : str(begin), len(end >= begin ? end - begin : 0) {} 13 | StringPiece() : str(0), len(0) {} 14 | int cmp(const StringPiece& other) const { 15 | const char* a = str; 16 | const char* b = other.str; 17 | size_t l = 0; 18 | while (true) { 19 | if (l == len && l == other.len) return 0; 20 | if (l == len) return -1; 21 | if (l == other.len) return 1; 22 | char A = toLower(*a); 23 | char B = toLower(*b); 24 | if (A != B) return A < B ? -1 : 1; 25 | a++; 26 | b++; 27 | l++; 28 | } 29 | } 30 | bool operator<(const StringPiece& other) const { return cmp(other) < 0; } 31 | bool operator>(const StringPiece& other) const { return cmp(other) > 0; } 32 | bool operator<=(const StringPiece& other) const { return cmp(other) <= 0; } 33 | bool operator>=(const StringPiece& other) const { return cmp(other) <= 0; } 34 | bool operator==(const StringPiece& other) const { return cmp(other) == 0; } 35 | bool operator!=(const StringPiece& other) const { return cmp(other) != 0; } 36 | char operator[](size_t x) const { return str[x]; } 37 | explicit operator bool() const { return str != nullptr && len != 0; } 38 | 39 | void printTo(Print& p) const ; 40 | void paste(char* to) const { memcpy(to, str, len); } 41 | void pasteZ(char* to) const { paste(to); to[len] = 0; } 42 | 43 | bool contains(char c) const { 44 | for (size_t i = 0; i < len; i++) { 45 | if (str[i] == c) return true; 46 | } 47 | return false; 48 | } 49 | 50 | const char* str; 51 | size_t len; 52 | }; 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /styles/inout_sparktip.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_INOUT_SPARKTIP_H 2 | #define STYLES_INOUT_SPARKTIP_H 3 | 4 | // Usage: InOutSparkTip 5 | // BASE, SPARK_COLOR: COLOR 6 | // OUT_MILLIS, IN_MILLIS: a number 7 | // return value: COLOR 8 | // Similar to InOutHelper, but makes the tip a different color 9 | // during extension. 10 | 11 | template, class OFF_COLOR=Rgb<0,0,0>, bool ALLOW_DISABLE=1> 12 | class InOutSparkTipX { 13 | public: 14 | bool run(BladeBase* blade) __attribute__((warn_unused_result)) { 15 | base_.run(blade); 16 | extension_.run(blade); 17 | off_color_.run(blade); 18 | on_ = blade->is_on(); 19 | thres = (extension_.calculate(blade) * (blade->num_leds() + 4)) >> 7; 20 | if (ALLOW_DISABLE && is_same_type >::value && thres == 0) 21 | return false; 22 | return true; 23 | } 24 | 25 | private: 26 | T base_; 27 | bool on_; 28 | int thres = 0; 29 | SPARK_COLOR spark_color_; 30 | SVFWrapper extension_; 31 | OFF_COLOR off_color_; 32 | public: 33 | auto getColor(int led) -> decltype(MixColors(off_color_.getColor(0), MixColors(spark_color_.getColor(0), base_.getColor(0), 1, 8), 1, 8)) { 34 | SCOPED_PROFILER(); 35 | decltype(getColor(0)) ret = base_.getColor(led); 36 | if (on_) { 37 | auto spark = spark_color_.getColor(led); 38 | int spark_mix = clampi32(thres - 1024 - led * 256, 0, 255); 39 | ret = MixColors(spark, ret, spark_mix, 8); 40 | } 41 | int black_mix = clampi32(thres - led * 256, 0, 255); 42 | auto off_color = off_color_.getColor(led); 43 | return MixColors(off_color, ret, black_mix, 8); 44 | } 45 | }; 46 | 47 | template, class OFF_COLOR=Rgb<0,0,0>, bool ALLOW_DISABLE=1> 48 | using InOutSparkTip = InOutSparkTipX, SPARK_COLOR, OFF_COLOR, ALLOW_DISABLE>; 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /functions/sparkle.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_SPARKLE_H 2 | #define FUNCTIONS_SPARKLE_H 3 | 4 | // Usage: SparkleF 5 | // SPARK_CHANCE_PROMILLE: a number 6 | // SPARK_INTENSITY: a number 7 | 8 | // SPARK_CHANCE_PROMILLE decides how often a spark is generated, defaults to 300 (30%) 9 | // SPARK_INTENSITY specifies how intens the spark is, defaults to 1024 10 | 11 | class SparkleBase { 12 | public: 13 | ~SparkleBase() { 14 | delete[] sparks_; 15 | } 16 | 17 | void run(BladeBase* blade, int spark_chance_promille, int spark_intensity) { 18 | uint32_t m = millis(); 19 | if (!sparks_) { 20 | size_t N = blade->num_leds() + 4; 21 | sparks_ = new short[N]; 22 | for (size_t i = 0; i < N; i++) sparks_[i] = 0; 23 | } 24 | if (m - last_update_ >= 10) { 25 | last_update_ = m; 26 | uint16_t fifo[2]; 27 | fifo[0] = 0; 28 | fifo[1] = 0; 29 | int N = blade->num_leds(); 30 | for (int i = 2; i < N + 2; i++) { 31 | #if 0 32 | int32_t x = (sparks_[i - 1] + sparks_[i + 1]) * 3 - (sparks_[i - 2] + sparks_[i + 2]); 33 | sparks_[i-2] = fifo[0]; 34 | fifo[0] = fifo[1]; 35 | fifo[1] = (sparks_[i] * 11 + x) >> 4; 36 | #else 37 | int32_t x = ((sparks_[i-1] + sparks_[i+1]) * 200 + sparks_[i] * 570) >> 10; 38 | sparks_[i-1] = fifo[0]; 39 | fifo[0] = x; 40 | #endif 41 | } 42 | sparks_[N] = fifo[0]; 43 | sparks_[N+1] = fifo[1]; 44 | if (random(1000) < spark_chance_promille) { 45 | sparks_[random(blade->num_leds())+2] += spark_intensity; 46 | } 47 | } 48 | } 49 | 50 | int getInteger(int led) { return clampi32(sparks_[led + 2], 0, 256) << 7; } 51 | 52 | private: 53 | short* sparks_ = 0; 54 | uint32_t last_update_; 55 | }; 56 | 57 | template 58 | class SparkleF : public SparkleBase { 59 | public: 60 | void run(BladeBase* blade) { 61 | SparkleBase::run(blade, SPARK_CHANCE_PROMILLE, SPARK_INTENSITY); 62 | } 63 | }; 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /styles/rgb.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_RGB_H 2 | #define STYLES_RGB_H 3 | 4 | // Usage: Rgb 5 | // R, G, B: a number (0-255) 6 | // return value: COLOR 7 | 8 | // Usage: Rgb16 9 | // R, G, B: a number (0-65536) 10 | // return value: COLOR 11 | 12 | // The two RGB classes are equivialent, but Rgb16<> is much more precise, 13 | // most of the time this doesn't matter as 255 different values is accurate 14 | // enough, but especially for very dim colors, the extra accuracy can be 15 | // very useful. 16 | 17 | // We use these templates instead of the Color class, because 18 | // the compiler can inline everything. That means that we can 19 | // have the flexibility of using a dynamically calculated color 20 | // when we want without slowing anything down when we specify 21 | // a constant color. 22 | // See colors.h for a list of pre-defined colors. 23 | 24 | // Simple solid color. 25 | template 26 | class Rgb { 27 | public: 28 | static constexpr Color16 color() { return Color16(Color8(R,G,B)); } 29 | LayerRunResult run(BladeBase* base) { 30 | if (R == 0 && G == 0 && B == 0) return LayerRunResult::OPAQUE_BLACK_UNTIL_IGNITION; 31 | return LayerRunResult::UNKNOWN; 32 | } 33 | SimpleColor getColor(int led) { 34 | return SimpleColor(color()); 35 | } 36 | }; 37 | 38 | // Simple solid color with 16-bit precision. 39 | template 40 | class Rgb16 { 41 | public: 42 | static Color16 color() { return Color16(R, G, B); } 43 | LayerRunResult run(BladeBase* base) { 44 | if (R == 0 && G == 0 && B == 0) return LayerRunResult::OPAQUE_BLACK_UNTIL_IGNITION; 45 | return LayerRunResult::UNKNOWN; 46 | } 47 | SimpleColor getColor(int led) { return SimpleColor(color()); } 48 | }; 49 | 50 | // Simple semi-transparent color with 16-bit precision. 51 | template 52 | class Rgba16 { 53 | public: 54 | void run(BladeBase* base) {} 55 | RGBA_um_nod getColor(int led) { 56 | return RGBA_um_nod(Color16(R, G, B), A >> 1); 57 | } 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /config/proffieboard_v1_graflex.h: -------------------------------------------------------------------------------- 1 | // This is a sample configuration file. 2 | // This saber has: 3 | // o Proffieboard V1 hardware. 4 | // o A touchbutton and a regular button 5 | // o An 8-pin blade connector 6 | // o All blades and presets are stored in common_config.h 7 | // If you have a saber similar to this one, make a copy and use the copy. 8 | 9 | #ifdef CONFIG_TOP 10 | 11 | // Proffieboard V1 electronics 12 | #include "proffieboard_v1_config.h" 13 | 14 | // Number of simultaneously connected blades. 15 | // (For interchangeable blades, see the blades[] array.) 16 | #define NUM_BLADES 1 17 | 18 | // Number of buttons 19 | #define NUM_BUTTONS 2 20 | 21 | // Dual power buttons means that clicking AUX will also turn the saber on. 22 | // If not defined, AUX will go to next preset when off. 23 | #define DUAL_POWER_BUTTONS 24 | 25 | // Volume, useful range is about 0-2000. 26 | #define VOLUME 1800 27 | 28 | // If you have two 144 LED/m strips in your blade, connect 29 | // both of them to bladePin and drive them in parallel. 30 | const unsigned int maxLedsPerStrip = 144; 31 | 32 | // This defines how sensitive the clash detection is. 33 | #define CLASH_THRESHOLD_G 2.0 34 | 35 | #define BLADE_ID_CLASS EnablePowerBladeID, BridgedPullupBladeID> 36 | 37 | // Feature defines, these let you turn off large blocks of code 38 | // used for debugging. 39 | #define ENABLE_AUDIO 40 | #define ENABLE_MOTION 41 | #define ENABLE_WS2811 42 | #define ENABLE_SD 43 | 44 | #define SPEAK_TOUCH_VALUES 45 | 46 | #endif 47 | 48 | #include "common_presets.h" 49 | 50 | #ifdef CONFIG_BUTTONS 51 | 52 | // There are currently three available button classes: 53 | // Button (standard momentary button) 54 | // TouchButton (similar to momentary button, but reacts to touch). 55 | // LatchingButton (on/off button, always controls ignition) 56 | 57 | TouchButton PowerButton(BUTTON_POWER, powerButtonPin, 1500, "pow"); 58 | Button AuxButton(BUTTON_AUX, auxPin, "aux"); 59 | #endif 60 | 61 | -------------------------------------------------------------------------------- /functions/readpin.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_READPIN_H 2 | #define FUNCTIONS_READPIN_H 3 | 4 | #include "../common/analog_read.h" 5 | 6 | // Usage: ReadPinF 7 | // or: ReadPinF 8 | // PIN: int, pin you want your style to respond to 9 | // PIN_MODE: int, one of INPUT, INPUT_PULLUP or INPUT_PULLDOWN, defaults to INPUT 10 | // returns INTEGER, 0 if pin is low and 32768 if pin is high 11 | 12 | template 13 | class ReadPinSVF { 14 | public: 15 | ReadPinSVF() { 16 | pinMode(pin, pin_mode); 17 | } 18 | void run(BladeBase* blade) {} 19 | int calculate(BladeBase* blade) { 20 | return digitalRead(pin) == HIGH ? 32768 : 0; 21 | } 22 | }; 23 | 24 | template 25 | using ReadPinF = SingleValueAdapter>; 26 | 27 | 28 | // Usage: AnalogReadPinF 29 | // or: AnalogReadPinF 30 | // PIN: int, pin you want your style to respond to 31 | // PIN_MODE: int, one of INPUT, INPUT_PULLUP or INPUT_PULLDOWN, defaults to INPUT 32 | // returns INTEGER, 0-32768 depending on input reading. 33 | // Notes: 34 | // * May cause slowdowns 35 | // * may not update every run() call 36 | // * pin modes other than INPUT may not be supported, 37 | // * Only analog-capable pins will work. 38 | 39 | template 40 | class AnalogReadPinSVF { 41 | public: 42 | AnalogReadPinSVF() : reader_(pin, pin_mode) {} 43 | void run(BladeBase* blade) { 44 | do { 45 | if (!started_) { 46 | if (reader_.Start()) { 47 | started_ = true; 48 | } 49 | } else { 50 | if (reader_.Done()) { 51 | value_ = reader_.Value12() * 8; 52 | started_ = false; 53 | } 54 | } 55 | } while (value_ == -1); 56 | } 57 | int calculate(BladeBase* blade) { 58 | return value_; 59 | } 60 | int value_ = -1; 61 | AnalogReader reader_; 62 | bool started_ = false; 63 | }; 64 | 65 | template 66 | using AnalogReadPinF = SingleValueAdapter>; 67 | 68 | 69 | #endif // FUNCTIONS_READPIN_H 70 | -------------------------------------------------------------------------------- /functions/strobe.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_STROBE_H 2 | #define FUNCTIONS_STROBE_H 3 | 4 | #include "../functions/int.h" 5 | 6 | // Usage: StrobeF 7 | // STROBE_FREQUENCY_FUNC, STROBE_MILLIS_FUNC: FUNCTION 8 | // return value: INTEGER 9 | // Stroboscope-like effect, turns the color to STROBE_COLOR for STROBE_MILLIS 10 | // STROBE_FREQUENCY times per second. 11 | 12 | template 13 | class StrobeSVF { 14 | public: 15 | static_assert(!is_same_type>::value, "Division by zero"); 16 | void run(BladeBase* blade) { 17 | strobe_frequency_.run(blade); 18 | strobe_millis_.run(blade); 19 | uint32_t m = millis(); 20 | uint32_t strobe_millis = strobe_millis_.calculate(0); 21 | uint32_t strobe_frequency = strobe_frequency_.calculate(0); 22 | if (strobe_frequency <= 0) strobe_frequency = 1; 23 | uint32_t timeout = strobe_ ? strobe_millis : (1000 / strobe_frequency); 24 | if (m - strobe_start_ > timeout) { 25 | strobe_start_ += timeout; 26 | if (m - strobe_start_ > strobe_millis + (1000 / strobe_frequency)) 27 | strobe_start_ = m; 28 | strobe_ = !strobe_; 29 | } 30 | } 31 | int calculate(BladeBase* blade) { return strobe_ * 32768; } 32 | int getInteger(int led) { return strobe_ * 32768; } 33 | private: 34 | bool strobe_; 35 | PONUA SVFWrapper strobe_frequency_; 36 | PONUA SVFWrapper strobe_millis_; 37 | uint32_t strobe_start_; 38 | }; 39 | 40 | // Optimized specialization 41 | template 42 | class SingleValueAdapter> : public StrobeSVF {}; 43 | template 44 | class SVFWrapper> : public StrobeSVF {}; 45 | 46 | template 47 | using StrobeF = SingleValueAdapter>; 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /functions/blinking.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_BLINKING_H 2 | #define FUNCTIONS_BLINKING_H 3 | 4 | #include "int.h" 5 | 6 | // Usage: BlinkingF 7 | // BLINK_MILLIS: a number 8 | // BLINK_PROMILLE: a number, defaults to 500 9 | // BLINK_MILLIS_FUNC: FUNCTION 10 | // BLINK_PROMILLE_FUNC: FUNCTION 11 | // return value: FUNCTION 12 | // Switches between 0 and 32768 13 | // A full cycle from 0 to 328768 and back again takes BLINK_MILLIS milliseconds. 14 | // If BLINK_PROMILLE is 500, we select A for the first half and B for the 15 | // second half. If BLINK_PROMILLE is smaller, we get less A and more B. 16 | // If BLINK_PROMILLE is 0, we get all 0. 17 | // If BLINK_PROMILLE is 1000 we get all 32768. 18 | // 19 | 20 | #include "svf.h" 21 | 22 | template 23 | class BlinkingFSVF { 24 | public: 25 | void run(BladeBase* base) { 26 | pulse_millis_.run(base); 27 | pulse_promille_.run(base); 28 | } 29 | 30 | int calculate(BladeBase* base) { 31 | uint32_t now = micros(); 32 | uint32_t pulse_millis = pulse_millis_.calculate(base); 33 | if (pulse_millis <= 0) return 0; 34 | uint32_t pulse_progress_micros = now - pulse_start_micros_; 35 | if (pulse_progress_micros > pulse_millis * 1000) { 36 | // Time to start a new pulse 37 | if (pulse_progress_micros < pulse_millis * 2000) { 38 | pulse_start_micros_ += pulse_millis * 1000; 39 | } else { 40 | pulse_start_micros_ = now; 41 | } 42 | pulse_progress_micros = now - pulse_start_micros_; 43 | } 44 | int32_t pulse_progress_promille = pulse_progress_micros / pulse_millis; 45 | return pulse_progress_promille <= pulse_promille_.calculate(base) ? 0 : 32768; 46 | } 47 | 48 | private: 49 | PONUA SVFWrapper pulse_millis_; 50 | PONUA SVFWrapper pulse_promille_; 51 | uint32_t pulse_start_micros_; 52 | }; 53 | 54 | template 55 | using BlinkingF = SingleValueAdapter>; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /common/capabilities.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_CAPABILITIES_H 2 | #define COMMON_CAPABILITIES_H 3 | 4 | #include "common.h" 5 | 6 | #define CREATE_ENUM_CHECKER(ID) \ 7 | template \ 8 | class EnumContains_##ID { \ 9 | private: \ 10 | template \ 11 | struct test2 { \ 12 | static const bool exists = false; \ 13 | static const int value = -9999; \ 14 | }; \ 15 | template \ 16 | struct test2::Type > { \ 17 | static const bool exists = true; \ 18 | static const int value = E::ID; \ 19 | }; \ 20 | \ 21 | public: \ 22 | static constexpr bool exists = test2::exists; \ 23 | static constexpr int value = test2::value; \ 24 | }; 25 | 26 | // Expand to numbers higher than 10 if required. 27 | CREATE_ENUM_CHECKER(bladePowerPin1); 28 | CREATE_ENUM_CHECKER(bladePowerPin2); 29 | CREATE_ENUM_CHECKER(bladePowerPin3); 30 | CREATE_ENUM_CHECKER(bladePowerPin4); 31 | CREATE_ENUM_CHECKER(bladePowerPin5); 32 | CREATE_ENUM_CHECKER(bladePowerPin6); 33 | CREATE_ENUM_CHECKER(bladePowerPin7); 34 | CREATE_ENUM_CHECKER(bladePowerPin8); 35 | CREATE_ENUM_CHECKER(bladePowerPin9); 36 | CREATE_ENUM_CHECKER(bladePowerPin10); 37 | 38 | constexpr bool IsPWMPin(int pin) { 39 | return 40 | pin == EnumContains_bladePowerPin1::value || 41 | pin == EnumContains_bladePowerPin2::value || 42 | pin == EnumContains_bladePowerPin3::value || 43 | pin == EnumContains_bladePowerPin4::value || 44 | pin == EnumContains_bladePowerPin5::value || 45 | pin == EnumContains_bladePowerPin6::value || 46 | pin == EnumContains_bladePowerPin7::value || 47 | pin == EnumContains_bladePowerPin8::value || 48 | pin == EnumContains_bladePowerPin9::value || 49 | pin == EnumContains_bladePowerPin10::value; 50 | } 51 | 52 | #endif 53 | -------------------------------------------------------------------------------- /config/td_proffieboard_config.h: -------------------------------------------------------------------------------- 1 | // This is a sample configuration file for a Thermal Detonator 2 | // This configuration has 3 | // o Proffieboard V1 hardware. 4 | // o Two buttons 5 | // o Three individual LEDs on 1, 2, 3 6 | // o A button LED on LED4 7 | // If you have a similar configuration, make a copy and use that. 8 | 9 | #ifdef CONFIG_TOP 10 | #include "proffieboard_config.h" 11 | #define NUM_BLADES 2 12 | #define NUM_BUTTONS 2 13 | #define VOLUME 2500 14 | const unsigned int maxLedsPerStrip = 144; 15 | #define CLASH_THRESHOLD_G 2.0 16 | #define ENABLE_AUDIO 17 | #define ENABLE_MOTION 18 | #define ENABLE_WS2811 19 | #define ENABLE_SD 20 | #define DELAYED_OFF 21 | #endif 22 | 23 | #ifdef CONFIG_PROP 24 | #include "../props/detonator.h" 25 | #endif 26 | 27 | #ifdef CONFIG_PRESETS 28 | Preset presets[] = { 29 | { "tdmod", "tracks/laptinek.wav", 30 | StylePtr,Rgb<255,255,0>,Rgb<255,0,255>, 32 | Rgb<0,255,255>,Rgb<0,255,0>,Rgb<0,0,255>, 33 | Rgb<255,255,0>,Rgb<0,255,255> >, RandomFlicker, 6000, EFFECT_BLAST>, 100, 100, 6000, BLACK>>(), 34 | StylePtr, 6000, EFFECT_BLAST>, Pulsing, 300>>, 100, 100, 6000>>(), 35 | ""}, 36 | { "ThermalD", "tracks/cantina.wav", 37 | StyleRainbowPtr<100, 100>(), 38 | StylePtr, 300>>, 100, 100>>(), 39 | ""}, 40 | }; 41 | BladeConfig blades[] = { 42 | { 0, 43 | // Note, I use bladePowerPin5 instead of bladePowerPin2! 44 | SimpleBladePtr(), 46 | SimpleBladePtr(), 48 | CONFIGARRAY(presets) }, 49 | }; 50 | #endif 51 | 52 | #ifdef CONFIG_BUTTONS 53 | InvertedLatchingButton PowerButton(BUTTON_POWER, powerButtonPin, "pow"); 54 | Button AuxButton(BUTTON_AUX2, aux2Pin, "aux"); 55 | #endif 56 | -------------------------------------------------------------------------------- /functions/circular_section.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTION_CIRCULAR_SECTION_H 2 | #define FUNCTION_CIRCULAR_SECTION_H 3 | 4 | #include 5 | 6 | // Usage: CircularSectionF 7 | // POSITION: FUNCTION position on the circle or blade, 0-32768 8 | // FRACTION: FUNCTION how much of the blade to light up, 0 = none, 32768 = all of it 9 | // return value: FUNCTION 10 | // Returns 32768 for LEDs near the position with wrap-around. 11 | // Could be used with MarbleF<> for a marble effect, or with 12 | // Saw<> for a spinning/colorcycle type effect. 13 | // Example: If POSITION = 0 and FRACTION = 16384, then this function 14 | // will return 32768 for the first 25% and the last 25% of the blade 15 | // and 0 for the rest of the LEDs. 16 | 17 | class BladeBase; 18 | template 19 | class CircularSectionF { 20 | public: 21 | FunctionRunResult run(BladeBase* base) { 22 | pos_.run(base); 23 | FunctionRunResult ret = RunFunction(&fraction_, base); 24 | 25 | num_leds_ = base->num_leds();; 26 | int fraction = fraction_.calculate(base); 27 | if (fraction == 32768) { 28 | start_ = 0; 29 | end_ = num_leds_ * 32768; 30 | } else if (fraction == 0) { 31 | start_ = 0; 32 | end_ = 0; 33 | } else { 34 | int pos = pos_.calculate(base); 35 | start_ = ((pos + 32768 - fraction / 2) & 32767) * num_leds_; 36 | end_ = ((pos + fraction / 2) & 32767) * num_leds_; 37 | } 38 | num_leds_ *= 32768; 39 | return ret; 40 | } 41 | int getInteger(int led) { 42 | Range led_range(led * 32768, led * 32768 + 32768); 43 | int black_mix = 0; 44 | if (start_ <= end_) { 45 | black_mix = (Range(start_, end_) & led_range).size(); 46 | } else { 47 | black_mix = (Range(0, end_) & led_range).size() + 48 | (Range(start_, num_leds_) & led_range).size(); 49 | } 50 | return black_mix; 51 | } 52 | private: 53 | PONUA SVFWrapper pos_; 54 | PONUA SVFWrapper fraction_; 55 | 56 | uint32_t start_; 57 | uint32_t end_; 58 | uint32_t num_leds_; 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /config/owk_v2_config.h: -------------------------------------------------------------------------------- 1 | // This is a sample configuration file. 2 | // This saber has: 3 | // o TeensySaber V2 hardware. 4 | // o A single button 5 | // o An 8-pin blade connector 6 | // o All blades and presets are stored in common_config.h 7 | // If you have a saber similar to this one, make a copy and use the copy. 8 | 9 | #ifdef CONFIG_TOP 10 | 11 | // V2 electronics 12 | #include "v2_config.h" 13 | 14 | // Number of simultaneously connected blades. 15 | // (For interchangeable blades, see the blades[] array.) 16 | #define NUM_BLADES 1 17 | 18 | // Number of buttons 19 | #define NUM_BUTTONS 1 20 | 21 | // Dual power buttons means that clicking AUX will also turn the saber on. 22 | // If not defined, AUX will go to next preset when off. 23 | // #define DUAL_POWER_BUTTONS 24 | 25 | // Volume, useful range is about 0-2000. 26 | #define VOLUME 1800 27 | 28 | // If you have two 144 LED/m strips in your blade, connect 29 | // both of them to bladePin and drive them in parallel. 30 | const unsigned int maxLedsPerStrip = 144; 31 | 32 | // This defines how sensitive the clash detection is. 33 | #define CLASH_THRESHOLD_G 1.5 34 | 35 | // If your electonics inverts the bladePin for some reason, define this. 36 | // #define INVERT_WS2811 37 | 38 | // Feature defines, these let you turn off large blocks of code 39 | // used for debugging. 40 | #define ENABLE_AUDIO 41 | #define ENABLE_MOTION 42 | // #define ENABLE_SNOOZE 43 | #define ENABLE_WS2811 44 | 45 | // FASTLED is experimental and untested right now 46 | // #define ENABLE_FASTLED 47 | // #define ENABLE_WATCHDOG 48 | #define ENABLE_SD 49 | // #define ENABLE_SERIALFLASH 50 | 51 | #define ENABLE_POWER_FOR_ID PowerPINS 52 | 53 | #endif 54 | 55 | #include "common_presets.h" 56 | 57 | #ifdef CONFIG_BUTTONS 58 | // There are currently three available button classes: 59 | // Button (standard momentary button) 60 | // TouchButton (similar to momentary button, but reacts to touch). 61 | // LatchingButton (on/off button, always controls ignition) 62 | 63 | Button PowerButton(BUTTON_POWER, powerButtonPin, "pow"); 64 | #endif 65 | -------------------------------------------------------------------------------- /buttons/latching_button.h: -------------------------------------------------------------------------------- 1 | #ifndef BUTTONS_LATCHING_BUTTON_H 2 | #define BUTTONS_LATCHING_BUTTON_H 3 | 4 | #include "debounced_button.h" 5 | 6 | // Latching button 7 | template 8 | class LatchingButtonTemplate : public Looper, 9 | public CommandParser, 10 | public BASE { 11 | public: 12 | LatchingButtonTemplate(enum BUTTON button, int pin, const char* name) 13 | : Looper(), 14 | CommandParser(), 15 | name_(name), 16 | button_(button), 17 | pin_(pin) { 18 | pinMode(pin, INPUT_PULLUP); 19 | #ifdef ENABLE_SNOOZE 20 | snooze_digital.pinMode(pin, INPUT_PULLUP, RISING); 21 | #endif 22 | } 23 | 24 | const char* name() override { return name_; } 25 | 26 | void Warmup() { Loop(); } 27 | 28 | protected: 29 | void Loop() override { 30 | STATE_MACHINE_BEGIN(); 31 | while (true) { 32 | while (!BASE::DebouncedRead()) YIELD(); 33 | prop.Event(button_, EVENT_LATCH_ON); 34 | while (BASE::DebouncedRead()) YIELD(); 35 | prop.Event(button_, EVENT_LATCH_OFF); 36 | } 37 | STATE_MACHINE_END(); 38 | } 39 | 40 | bool Parse(const char* cmd, const char* arg) override { 41 | if (!strcmp(cmd, name_)) { 42 | if (current_modifiers & button_) { 43 | prop.Event(button_, EVENT_LATCH_ON); 44 | } else { 45 | prop.Event(button_, EVENT_LATCH_OFF); 46 | } 47 | return true; 48 | } 49 | return false; 50 | } 51 | 52 | bool Read() override { 53 | return digitalRead(pin_) == LOW; 54 | } 55 | 56 | const char* name_; 57 | enum BUTTON button_; 58 | StateMachineState state_machine_; 59 | uint8_t pin_; 60 | }; 61 | 62 | using LatchingButton = LatchingButtonTemplate<>; 63 | 64 | class InvertedLatchingButton : public LatchingButton { 65 | public: 66 | InvertedLatchingButton(enum BUTTON button, int pin, const char* name) 67 | : LatchingButton(button, pin, name) { 68 | #ifdef ENABLE_SNOOZE 69 | snooze_digital.pinMode(pin, INPUT_PULLUP, FALLING); 70 | #endif 71 | } 72 | 73 | bool Read() override { 74 | return digitalRead(pin_) == HIGH; 75 | } 76 | }; 77 | 78 | #endif 79 | -------------------------------------------------------------------------------- /styles/color_select.h: -------------------------------------------------------------------------------- 1 | #ifndef STYLES_COLOR_SELECT_H 2 | #define STYLES_COLOR_SELECT_H 3 | 4 | #include "../functions/variation.h" 5 | #include "../functions/svf.h" 6 | #include "mix.h" 7 | 8 | // Usage: ColorSelect 9 | // SELECTION: function 10 | // TRANSITION: transition 11 | // COLOR1, COLOR2, ...: COLOR 12 | // Return value: COLOR 13 | // Decides what color to return based on the current selection. 14 | // The returned color will be selection % N (where N is the number of colors arguments). 15 | // When the selection changes, the transition will be used to change from the old color to the new color. 16 | 17 | template 18 | class ColorSelect { 19 | public: 20 | ColorSelect() { 21 | if (is_same_type::value) { 22 | BladeBase::HandleFeature(HANDLED_FEATURE_CHANGE_TICKED); 23 | } 24 | } 25 | void run(BladeBase* blade) { 26 | f_.run(blade); 27 | colors_.run(blade); 28 | int f = f_.calculate(blade); 29 | while(f < 0) f += sizeof...(COLORS) << 8; 30 | uint8_t selection = f % sizeof...(COLORS); 31 | if (selection != selection_) { 32 | // Start a transition 33 | old_selection_ = selection_; 34 | selection_ = f % sizeof...(COLORS); 35 | transition_.begin(); 36 | } 37 | if (selection_ != old_selection_) { 38 | transition_.run(blade); 39 | if (!transition_.done()) return; 40 | old_selection_ = selection_; 41 | } 42 | } 43 | 44 | private: 45 | PONUA SVFWrapper f_; 46 | PONUA TRANSITION transition_; 47 | uint8_t selection_= 0; 48 | uint8_t old_selection_ = 0; 49 | PONUA MixHelper colors_; 50 | public: 51 | auto getColor(int led) -> decltype(transition_.getColor(colors_.getColor(selection_, led), colors_.getColor(selection_, led), led)) { 52 | // SCOPED_PROFILER(); 53 | auto ret = colors_.getColor(selection_, led); 54 | if (selection_ != old_selection_) { 55 | auto old = colors_.getColor(old_selection_, led); 56 | return transition_.getColor(old, ret, led); 57 | } 58 | return ret; 59 | } 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /functions/scale.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTIONS_SCALE_H 2 | #define FUNCTIONS_SCALE_H 3 | 4 | #include "int.h" 5 | 6 | // Usage: Scale 7 | // F, A, B: FUNCTION 8 | // return value: FUNCTION 9 | // Changes values in range 0 - 32768 to A-B 10 | 11 | class BladeBase; 12 | 13 | template 14 | class ScaleBase { 15 | public: 16 | void run(BladeBase* blade) { 17 | f_.run(blade); 18 | a_.run(blade); 19 | b_.run(blade); 20 | int a = a_.calculate(blade); 21 | int b = b_.calculate(blade); 22 | mul_ = (b - a); 23 | add_ = a; 24 | } 25 | int getInteger(int led) { 26 | return (f_.getInteger(led) * mul_ >> 15) + add_; 27 | } 28 | private: 29 | PONUA F f_; 30 | PONUA SVFWrapper a_; 31 | PONUA SVFWrapper b_; 32 | int add_; 33 | int mul_; 34 | }; 35 | 36 | template 37 | class ScaleBase, Int> { 38 | public: 39 | void run(BladeBase* blade) { 40 | f_.run(blade); 41 | } 42 | int getInteger(int led) { 43 | return (f_.getInteger(led) * (B - A) >> 15) + A; 44 | } 45 | private: 46 | PONUA F f_; 47 | }; 48 | 49 | template 50 | class ScaleSVF { 51 | public: 52 | void run(BladeBase* blade) { 53 | svff_.run(blade); 54 | svfa_.run(blade); 55 | svfb_.run(blade); 56 | } 57 | int calculate(BladeBase* blade) { 58 | int a = svfa_.calculate(blade); 59 | int b = svfb_.calculate(blade); 60 | return (svff_.calculate(blade) * (b - a) >> 15) + a; 61 | } 62 | private: 63 | PONUA SVFF svff_; 64 | PONUA SVFWrapper svfa_; 65 | PONUA SVFWrapper svfb_; 66 | }; 67 | 68 | template struct ScaleFinder { typedef ScaleBase ScaleClass; }; 69 | template 70 | struct ScaleFinder, A, B> { typedef SingleValueAdapter> ScaleClass; }; 71 | template 72 | using Scale = typename ScaleFinder::ScaleClass; 73 | 74 | // To simplify inverting a function's returned value 75 | // Example InvertF> will return 0 when up and 32768 when down 76 | template using InvertF = Scale, Int<0>>; 77 | 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /blades/dim_blade.h: -------------------------------------------------------------------------------- 1 | #ifndef BLADES_DIM_BLADE_WRAPPER_H 2 | #define BLADES_DIM_BLADE_WRAPPER_H 3 | 4 | class DimBladeWrapper : public BladeWrapper, BladeStyle { 5 | public: 6 | DimBladeWrapper(BladeBase* blade, int fraction) { 7 | blade_ = blade; 8 | fraction_ = fraction; 9 | } 10 | void set(int led, Color16 c) override { 11 | Color16 ret; 12 | ret.r = clampi32((c.r * fraction_) >> 14, 0, 65535); 13 | ret.g = clampi32((c.g * fraction_) >> 14, 0, 65535); 14 | ret.b = clampi32((c.b * fraction_) >> 14, 0, 65535); 15 | return blade_->set(led, ret); 16 | } 17 | 18 | void SetStyle(BladeStyle* style) override { 19 | // current_style should be nullptr; 20 | current_style_ = style; 21 | if (current_style_) { 22 | current_style_->activate(); 23 | } 24 | blade_->SetStyle(this); 25 | } 26 | BladeStyle* UnSetStyle() override { 27 | blade_->UnSetStyle(); 28 | BladeStyle *ret = current_style_; 29 | if (ret) { 30 | ret->deactivate(); 31 | } 32 | current_style_ = nullptr; 33 | return ret; 34 | } 35 | 36 | BladeStyle* current_style() const override { 37 | return current_style_; 38 | } 39 | 40 | // Bladestyle implementation 41 | virtual void activate() override { 42 | if (current_style_) 43 | current_style_->activate(); 44 | } 45 | virtual void deactivate() override { 46 | if (current_style_) 47 | current_style_->deactivate(); 48 | } 49 | virtual void run(BladeBase* blade) override { 50 | if (current_style_) 51 | current_style_->run(this); 52 | } 53 | bool IsHandled(HandledFeature effect) override { 54 | if (!current_style_) return false; 55 | return current_style_->IsHandled(effect); 56 | } 57 | 58 | bool NoOnOff() override { 59 | if (!current_style_) return false; 60 | return current_style_->NoOnOff(); 61 | } 62 | 63 | private: 64 | BladeStyle *current_style_ = nullptr; 65 | int fraction_; 66 | }; 67 | 68 | // Reduces the brightness of the blade. 69 | // percentage = 0 -> dark 70 | // percentage = 100 -> no change 71 | class BladeBase* DimBlade(float percentage, BladeBase* blade) { 72 | return new DimBladeWrapper(blade, (int)(percentage * 16384 / 100.0)); 73 | } 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /transitions/colorcycle.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSITION_COLOR_CYCLE_H 2 | #define TRANSITION_COLOR_CYCLE_H 3 | 4 | #include 5 | 6 | // Usage: TrColorCycle 7 | // OR: TrColorCycleX 8 | // MILLS: number 9 | // MILLIS_FUNCTION: FUNCTION 10 | // START_RPM: a number (defaults to 0) 11 | // END_RPM: a number (defaults to 6000) 12 | // return value: COLOR 13 | // Tron-like transition. 14 | 15 | class BladeBase; 16 | template 17 | class TrColorCycleX : public TransitionBaseX { 18 | public: 19 | void run(BladeBase* base) { 20 | TransitionBaseX::run(base); 21 | uint32_t now = micros(); 22 | uint32_t delta = now - last_micros_; 23 | last_micros_ = now; 24 | if (delta > 1000000) delta = 1; 25 | 26 | fade_ = this->update(1.0f); 27 | 28 | float current_rpm = start_rpm * (1 - fade_) + end_rpm * fade_; 29 | float current_percentage = 100.0 * fade_; 30 | pos_ = fract(pos_ + delta / 60000000.0 * current_rpm); 31 | num_leds_ = base->num_leds() * 16384; 32 | start_ = pos_ * num_leds_; 33 | if (current_percentage == 100.0) { 34 | start_ = 0; 35 | end_ = num_leds_; 36 | } else if (current_percentage == 0.0) { 37 | start_ = 0; 38 | end_ = 0; 39 | } else { 40 | end_ = fract(pos_ + current_percentage / 100.0) * num_leds_; 41 | } 42 | } 43 | template 44 | auto getColor(const A& a, const B& b, int led) -> decltype(MixColors(a,b,1,1)) { 45 | Range led_range(led * 16384, led * 16384 + 16384); 46 | int mix = 0; 47 | if (start_ <= end_) { 48 | mix = (Range(start_, end_) & led_range).size(); 49 | } else { 50 | mix = (Range(0, end_) & led_range).size() + 51 | (Range(start_, num_leds_) & led_range).size(); 52 | } 53 | return MixColors(a, b, mix, 14); 54 | } 55 | private: 56 | float fade_ = 0.0; 57 | float pos_ = 0.0; 58 | uint32_t start_; 59 | uint32_t end_; 60 | uint32_t num_leds_; 61 | uint32_t last_micros_; 62 | }; 63 | 64 | template using TrColorCycle = TrColorCycleX, start_rpm, end_rpm>; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /functions/marble.h: -------------------------------------------------------------------------------- 1 | #ifndef FUNCTION_MARBLE_H 2 | #define FUNCTION_MARBLE_H 3 | 4 | #include 5 | 6 | // Usage: MarbleF 7 | // OFFSET: FUNCTION 0-32768, adjust until "down" represents is actually down 8 | // FRICTION: FUNCTION, higher values makes the marble slow down, usually a constant 9 | // ACCELERATION: FUNCTION, a function specifying how much speed to add to the marble 10 | // GRAVITY: FUNCTION higher values makes the marble heavier 11 | // return value: FUNCTION 0-32768, representing point on a circle 12 | // This is intended for a small ring of neopixels. 13 | // It runs a simulation of a marble trapped in a circular 14 | // track and returns the position of that marble. 15 | // Meant to be used with CircularSectionF to turn the marble 16 | // position into a lighted up section. 17 | 18 | class BladeBase; 19 | template 23 | class MarbleF { 24 | public: 25 | void run(BladeBase* base) { 26 | offset_.run(base); 27 | friction_.run(base); 28 | acceleration_.run(base); 29 | gravity_.run(base); 30 | SaberBase::RequestMotion(); 31 | 32 | uint32_t now = micros(); 33 | uint32_t delta = now - last_micros_; 34 | last_micros_ = now; 35 | if (delta > 1000000) delta = 1; 36 | float fraction = delta / 1000000.0; 37 | 38 | float rad = (pos_ + offset_.calculate(base)/32768.0) * M_PI * 2.0; 39 | Vec3 down = fusor.accel(); 40 | float gravity = gravity_.calculate(base) / 32768.0; 41 | float accel = (down.y * sinf(rad) + down.z * cosf(rad)) * gravity; 42 | accel += acceleration_.calculate(base) / 32768.0; 43 | accel -= speed_ * friction_.calculate(base) / 32768.0; 44 | speed_ += accel * fraction; 45 | pos_ = fract(pos_ + speed_ * fraction); 46 | value_ = pos_ * 32768; 47 | } 48 | int getInteger(int led) { return value_; } 49 | private: 50 | PONUA SVFWrapper offset_; 51 | PONUA SVFWrapper friction_; 52 | PONUA SVFWrapper acceleration_; 53 | PONUA SVFWrapper gravity_; 54 | 55 | float pos_ = 0.0; 56 | float speed_ = 0.0; 57 | uint32_t last_micros_; 58 | int value_; 59 | }; 60 | 61 | #endif 62 | -------------------------------------------------------------------------------- /config/proffieboard_v3_verification_config.h: -------------------------------------------------------------------------------- 1 | /* Proffieboard V3 light stapler test config. 2 | */ 3 | #ifdef CONFIG_TOP 4 | #include "proffieboard_v3_config.h" 5 | #define NUM_BLADES 3 6 | #define NUM_BUTTONS 2 7 | #define VOLUME 100 8 | const unsigned int maxLedsPerStrip = 144; 9 | #define CLASH_THRESHOLD_G 3.5 10 | #define ENABLE_AUDIO 11 | #define ENABLE_MOTION 12 | #define ENABLE_WS2811 13 | #define ENABLE_SD 14 | #define SHARED_POWER_PINS 15 | #define SAVE_STATE 16 | #define BLADE_DETECT_PIN aux2Pin 17 | #define ENABLE_DEVELOPER_COMMANDS 18 | #define IDLE_OFF_TIME 60 * 2 * 1000 19 | #define MOTION_TIMEOUT 60 * 1 * 1000 20 | 21 | #define BLASTER_SHOTS_UNTIL_EMPTY 15 // (whatever number) 22 | #define BLASTER_JAM_PERCENTAGE 10 // if not defined, random. 23 | 24 | #endif 25 | 26 | #ifdef CONFIG_PROP 27 | #include "../props/dual_prop.h" 28 | #include "../props/saber.h" 29 | #undef PROP_TYPE 30 | #include "../props/blaster.h" 31 | #undef PROP_TYPE 32 | #define PROP_TYPE SaberBlasterProp 33 | #endif 34 | 35 | 36 | #ifdef CONFIG_PRESETS 37 | Preset saber [] = { 38 | { "teensysf", "tracks/mars.wav", 39 | StylePtr,TrWipeInX >>>>(), 42 | StylePtr(), 43 | StylePtr(), 44 | "Teensy"}, 45 | }; 46 | 47 | Preset blaster[] = { 48 | { "blaster/bank1", "tracks/venus.wav", 49 | StylePtr(), 50 | StylePtr(), 51 | StylePtr(), 52 | "blaster"}, 53 | }; 54 | 55 | BladeConfig blades[] = { 56 | { 0, 57 | WS281XBladePtr<144, bladePin, Color8::GRB, PowerPINS >(), 58 | WS281XBladePtr<144, blade2Pin, Color8::GRB, PowerPINS >(), 59 | WS281XBladePtr<4, blade5Pin, Color8::GRB, PowerPINS >(), 60 | CONFIGARRAY(saber), 61 | "SaberSave"}, 62 | { NO_BLADE, 63 | WS281XBladePtr<144, bladePin, Color8::GRB, PowerPINS >(), 64 | WS281XBladePtr<144, blade2Pin, Color8::GRB, PowerPINS >(), 65 | WS281XBladePtr<4, blade5Pin, Color8::GRB, PowerPINS >(), 66 | CONFIGARRAY(blaster), 67 | "blasterSave"} 68 | }; 69 | #endif 70 | 71 | #ifdef CONFIG_BUTTONS 72 | Button PowerButton(BUTTON_POWER, powerButtonPin, "pow"); 73 | Button AuxButton(BUTTON_AUX, auxPin, "aux"); 74 | #endif 75 | 76 | -------------------------------------------------------------------------------- /config/graflex_v1_config.h: -------------------------------------------------------------------------------- 1 | // This is a sample configuration file. 2 | // This saber has: 3 | // o TeensySaber V1 hardware. 4 | // o A touchbutton and a regular button 5 | // o An 8-pin blade connector 6 | // o All blades and presets are stored in common_config.h 7 | // If you have a saber similar to this one, make a copy and use the copy. 8 | 9 | #ifdef CONFIG_TOP 10 | 11 | // V1 electronics 12 | #include "v1_config.h" 13 | 14 | // Number of simultaneously connected blades. 15 | // (For interchangeable blades, see the blades[] array.) 16 | #define NUM_BLADES 1 17 | 18 | // Number of buttons 19 | #define NUM_BUTTONS 2 20 | 21 | // Dual power buttons means that clicking AUX will also turn the saber on. 22 | // If not defined, AUX will go to next preset when off. 23 | #define DUAL_POWER_BUTTONS 24 | 25 | // Volume, useful range is about 0-2000. 26 | #define VOLUME 1800 27 | 28 | // If you have two 144 LED/m strips in your blade, connect 29 | // both of them to bladePin and drive them in parallel. 30 | const unsigned int maxLedsPerStrip = 144; 31 | 32 | // This defines how sensitive the clash detection is. 33 | #define CLASH_THRESHOLD_G 1.0 34 | 35 | // For V1 electronics, there is an external pullup resistor to measure 36 | // battery voltage. This specifies how many Ohms it is. 37 | #define BATTERY_PULLUP_OHMS 23000 38 | 39 | // If your electonics inverts the bladePin for some reason, define this. 40 | // #define INVERT_WS2811 41 | 42 | // Feature defines, these let you turn off large blocks of code 43 | // used for debugging. 44 | #define ENABLE_AUDIO 45 | #define ENABLE_MOTION 46 | // #define ENABLE_SNOOZE 47 | #define ENABLE_WS2811 48 | 49 | // FASTLED is experimental and untested right now 50 | // #define ENABLE_FASTLED 51 | // #define ENABLE_WATCHDOG 52 | #define ENABLE_SD 53 | #define ENABLE_SERIALFLASH 54 | 55 | #endif 56 | 57 | #include "common_presets.h" 58 | 59 | #ifdef CONFIG_BUTTONS 60 | 61 | // There are currently three available button classes: 62 | // Button (standard momentary button) 63 | // TouchButton (similar to momentary button, but reacts to touch). 64 | // LatchingButton (on/off button, always controls ignition) 65 | 66 | TouchButton PowerButton(BUTTON_POWER, powerButtonPin, 1700, "pow"); 67 | Button AuxButton(BUTTON_AUX, auxPin, "aux"); 68 | #endif 69 | 70 | -------------------------------------------------------------------------------- /config/toy_saber_config.h: -------------------------------------------------------------------------------- 1 | // This is a sample configuration file. 2 | // This saber has: 3 | // o TeensySaber V2 hardware. 4 | // o Zero buttons (just a latching on-off switch that toggles the power directly.) 5 | // o A six-segment string blade 6 | // If you have a saber similar to this one, make a copy and use the copy. 7 | 8 | #ifdef CONFIG_TOP 9 | 10 | // V2 electronics 11 | #include "v2_config.h" 12 | 13 | // Number of simultaneously connected blades. 14 | // (For interchangeable blades, see the blades[] array.) 15 | #define NUM_BLADES 1 16 | 17 | // Number of buttons 18 | #define NUM_BUTTONS 0 19 | 20 | // Dual power buttons means that clicking AUX will also turn the saber on. 21 | // If not defined, AUX will go to next preset when off. 22 | // #define DUAL_POWER_BUTTONS 23 | 24 | // Volume, useful range is about 0-2000. 25 | #define VOLUME 1800 26 | 27 | // If you have two 144 LED/m strips in your blade, connect 28 | // both of them to bladePin and drive them in parallel. 29 | const unsigned int maxLedsPerStrip = 144; 30 | 31 | // This defines how sensitive the clash detection is. 32 | #define CLASH_THRESHOLD_G 1.0 33 | 34 | // If your electonics inverts the bladePin for some reason, define this. 35 | // #define INVERT_WS2811 36 | 37 | // Feature defines, these let you turn off large blocks of code 38 | // used for debugging. 39 | #define ENABLE_AUDIO 40 | #define ENABLE_MOTION 41 | // #define ENABLE_SNOOZE 42 | #define ENABLE_WS2811 43 | 44 | // FASTLED is experimental and untested right now 45 | // #define ENABLE_FASTLED 46 | // #define ENABLE_WATCHDOG 47 | #define ENABLE_SD 48 | // #define ENABLE_SERIALFLASH 49 | 50 | #endif 51 | 52 | #ifdef CONFIG_PRESETS 53 | 54 | // Each preset line consists of: 55 | // { "font directory", "sound track", Style }, 56 | Preset presets[] = { 57 | { "TeensySF", "tracks/title.wav", StyleNormalPtr() }, 58 | { "graflex7", "tracks/duel.wav", StyleNormalPtr() }, 59 | { "font03", "tracks/cantina.wav", StyleStrobePtr() }, 60 | }; 61 | 62 | // Each line of configuration should be: 63 | // { blade id resistor ohms, blade, CONFIGARRAY(array of presets) }, 64 | BladeConfig blades[] = { 65 | { 0, StringBladePtr(), CONFIGARRAY(presets) }, 66 | }; 67 | 68 | #endif 69 | 70 | -------------------------------------------------------------------------------- /common/resources.h: -------------------------------------------------------------------------------- 1 | #ifndef COMMON_RESOURCES_H 2 | #define COMMON_RESOURCES_H 3 | 4 | namespace PO_SubSystems { 5 | class PO_SPI {}; 6 | class PO_WS2811 {}; 7 | class PO_BUTTON {}; 8 | class PO_BLADE_DETECT {}; 9 | class PO_DISPLAY {}; 10 | class PO_I2S {}; 11 | class PO_SPDIF {}; 12 | class PO_IR {}; 13 | class PO_PWM {}; 14 | class PO_SERIAL {}; 15 | } 16 | 17 | namespace PO_Resources { 18 | template class InputPin {}; 19 | template class OutputPin {}; 20 | template class SPI {}; 21 | template class TIMER {}; 22 | } 23 | 24 | namespace PO_ResourceTracking { 25 | 26 | using namespace PO_Resources; 27 | 28 | template struct BondedPins { static const int other = -1; }; 29 | 30 | #define PROFFIEOS_BOND_PINS(P1, P2) \ 31 | template<> struct BondedPins { static const int other = P2; }; \ 32 | template<> struct BondedPins { static const int other = P1; } 33 | 34 | template 35 | struct UseResource { 36 | friend USED_BY IsUsing(RESOURCE x) { return {}; } 37 | }; 38 | 39 | template 40 | struct UsePin2 { 41 | static_assert(sizeof(UseResource, USER>)); 42 | static_assert(sizeof(UseResource, USER>)); 43 | }; 44 | 45 | template struct UsePin2<-1, USER, INPUT> {}; 46 | 47 | template 48 | struct UsePin2 { 49 | static_assert(sizeof(UseResource, USER>)); 50 | }; 51 | 52 | 53 | template 54 | struct UseBondedPin { 55 | static_assert(sizeof(UseResource, USER>)); 56 | }; 57 | 58 | template struct UseBondedPin<-1, USER, false> {}; 59 | template struct UseBondedPin {}; 60 | 61 | template 62 | struct UsePin { 63 | static_assert(sizeof(UsePin2)); 64 | static_assert(sizeof(UseBondedPin::other, USER, INPUT>)); 65 | }; 66 | 67 | } 68 | 69 | #define USE_PIN_OUTPUT(PIN, CLASS) static_assert( sizeof( PO_ResourceTracking::UsePin), #CLASS) 70 | #define USE_PIN_INPUT(PIN, CLASS) static_assert( sizeof( PO_ResourceTracking::UsePin), #CLASS) 71 | 72 | #endif // COMMON_RESOURCES_H 73 | --------------------------------------------------------------------------------