From 1494dd6405c65da5b4afd37bbe5b2f02f8d71528 Mon Sep 17 00:00:00 2001 From: Torrie Fischer Date: Sat, 18 Feb 2023 17:15:21 +0100 Subject: [PATCH] main: move more tiny objects out of main.cpp --- src/Config.cpp | 13 +- src/Config.h | 2 + src/SafeMode.cpp | 56 ++++++ src/SafeMode.h | 7 + src/inputs/BPM.cpp | 5 + src/inputs/BPM.h | 52 ++++++ src/inputs/CircadianRhythm.cpp | 5 + src/inputs/CircadianRhythm.h | 90 ++++++++++ src/inputs/ConfigInput.cpp | 5 + src/inputs/ConfigInput.h | 77 ++++++++ src/main.cpp | 319 ++------------------------------- 11 files changed, 321 insertions(+), 310 deletions(-) create mode 100644 src/SafeMode.cpp create mode 100644 src/SafeMode.h create mode 100644 src/inputs/BPM.cpp create mode 100644 src/inputs/BPM.h create mode 100644 src/inputs/CircadianRhythm.cpp create mode 100644 src/inputs/CircadianRhythm.h create mode 100644 src/inputs/ConfigInput.cpp create mode 100644 src/inputs/ConfigInput.h diff --git a/src/Config.cpp b/src/Config.cpp index b06c0a3..93ed57f 100644 --- a/src/Config.cpp +++ b/src/Config.cpp @@ -80,7 +80,18 @@ ConfigService::onStart() if (strlen(m_config.data.loadedProfile) == 0) { strcpy(m_config.data.loadedProfile, "default"); } - loadProfile(m_config.data.loadedProfile); + + if (m_overrideProfile != nullptr) { + loadProfile(m_overrideProfile); + } else { + loadProfile(m_config.data.loadedProfile); + } +} + +void +ConfigService::overrideProfile(const char* profileName) +{ + m_overrideProfile = profileName; } void diff --git a/src/Config.h b/src/Config.h index e98ac1a..63f3e5a 100644 --- a/src/Config.h +++ b/src/Config.h @@ -36,10 +36,12 @@ struct ConfigService: public Task { void handleEvent(const InputEvent &evt) override; const CoordinateMapping* coordMap() const { return &m_jsonMap; } const char* loadedProfile() const; + void overrideProfile(const char* profileName); private: HardwareConfig m_config; JsonCoordinateMapping m_jsonMap; + const char* m_overrideProfile = nullptr; void loadProfile(const char* name); void loadMap(const String& mapName); diff --git a/src/SafeMode.cpp b/src/SafeMode.cpp new file mode 100644 index 0000000..1d9517c --- /dev/null +++ b/src/SafeMode.cpp @@ -0,0 +1,56 @@ +#include "./SafeMode.h" +#include "./LogService.h" +#include "./Platform.h" +#include "./Static.h" +#include "./Config.h" + +TaskFunc safeModeNag([]{ + static uint8_t frame = 0; + static CRGB* leds = FastLED.leds(); + EVERY_N_SECONDS(30) { + Log.fatal("I am running in safe mode!"); + } + EVERY_N_MILLISECONDS(16) { + frame++; + for(int i = 0; i < HardwareConfig::MAX_LED_NUM; i++) { + leds[i] = CRGB(0, 0, 0); + } + for(int idx = 0; idx < 3; idx++) { + uint8_t length = beatsin8(5, 3, HardwareConfig::MAX_LED_NUM, 0, idx * 5); + for(int i = 0; i < length; i++) { + leds[i] += CRGB(scale8(5, beatsin8(5 + i * 7, 0, 255, 0, i*3)), 0, 0); + } + } + FastLED.show(); + } +}); + +#ifdef CONFIG_WIFI +#include "platform/arduino/WiFiTask.h" +#endif // CONFIG_WIFI +#ifdef CONFIG_OTA +#include "platform/arduino/OTA.h" +#endif // CONFIG_OTA +#ifdef CONFIG_MQTT +#include "platform/arduino/MQTTTelemetry.h" +#endif // CONFIG_MQTT + +MainLoop +SafeMode::safeModeApp{{ + Static::instance(), + // System logging + Static::instance(), + &safeModeNag, +#ifdef CONFIG_WIFI + // ESP Wifi + Static::instance(), +#endif // CONFIG_WIFI +#ifdef CONFIG_MQTT + // MQTT + Static::instance(), +#endif // CONFIG_MQTT +#ifdef CONFIG_OTA + // OTA Updates + Static::instance(), +#endif // CONFIG_OTA +}}; diff --git a/src/SafeMode.h b/src/SafeMode.h new file mode 100644 index 0000000..b10585a --- /dev/null +++ b/src/SafeMode.h @@ -0,0 +1,7 @@ +#pragma once +#include + +class SafeMode { + public: + static MainLoop safeModeApp; +}; diff --git a/src/inputs/BPM.cpp b/src/inputs/BPM.cpp new file mode 100644 index 0000000..c92ed1c --- /dev/null +++ b/src/inputs/BPM.cpp @@ -0,0 +1,5 @@ +#include "./BPM.h" +#include "../Static.h" + +STATIC_ALLOC(BPM); +STATIC_TASK(BPM); diff --git a/src/inputs/BPM.h b/src/inputs/BPM.h new file mode 100644 index 0000000..a7ba0fc --- /dev/null +++ b/src/inputs/BPM.h @@ -0,0 +1,52 @@ +#pragma once +#include + +class BPM : public InputSource { +public: + BPM() : InputSource("BPM") {} + void handleEvent(const InputEvent& evt) override { + if (evt.intent == InputEvent::BeatDetect) { + m_nextBpm = millis(); + m_timings.insert(millis()); + Log.notice("%d timings", m_timings.size()); + if (m_timings.size() >= 5) { + updateBPM(); + } + } + } + + InputEvent read() override { + if (m_bpm > 0) { + uint16_t now = millis(); + if (now >= m_nextBpm) { + m_nextBpm += m_bpm; + return InputEvent{InputEvent::Beat, m_bpm}; + } + if (now >= m_nextLearn && m_nextLearn != 0) { + m_timings.clear(); + m_nextLearn = 0; + } + } + return InputEvent{}; + } + +private: + uint16_t m_bpm = 0; + uint16_t m_nextBpm = 0; + uint16_t m_nextLearn = 0; + Ringbuf m_timings; + + void updateBPM() { + uint16_t avgDelta = 0; + for(uint8_t i = 0; i < m_timings.size() - 1; i++) { + uint16_t delta = m_timings.peek(i+1) - m_timings.peek(i); + Log.notice("Timing %d Delta %d", m_timings.peek(i), delta); + avgDelta += delta; + } + m_bpm = avgDelta / 4; + m_nextLearn = m_bpm * 5 + millis(); + Log.notice("BPM is now %d", m_bpm); + uint16_t trash; + m_timings.take(trash); + } +}; diff --git a/src/inputs/CircadianRhythm.cpp b/src/inputs/CircadianRhythm.cpp new file mode 100644 index 0000000..21c0125 --- /dev/null +++ b/src/inputs/CircadianRhythm.cpp @@ -0,0 +1,5 @@ +#include "./CircadianRhythm.h" +#include "../Static.h" + +STATIC_ALLOC(CircadianRhythm); +STATIC_TASK(CircadianRhythm); diff --git a/src/inputs/CircadianRhythm.h b/src/inputs/CircadianRhythm.h new file mode 100644 index 0000000..26456ca --- /dev/null +++ b/src/inputs/CircadianRhythm.h @@ -0,0 +1,90 @@ +#pragma once +#include + +struct ScheduleEntry { + uint8_t hour; + uint8_t brightness; +}; + +std::array schedule{{ + {0, 0}, + {5, 0}, + {6, 0}, + {7, 10}, + {8, 80}, + {11, 120}, + {18, 200}, + {19, 255}, + {22, 120}, + {23, 20} +}}; + +class CircadianRhythm : public InputSource { + private: + bool needsUpdate = true; + public: + CircadianRhythm() : InputSource("CircadianRhythm") {} + + void onStart() { + needsUpdate = true; + } + + uint8_t brightnessForTime(uint8_t hour, uint8_t minute) const { + ScheduleEntry start = schedule.back(); + ScheduleEntry end = schedule.front(); + for(ScheduleEntry cur : schedule) { + // Find the last hour that is prior to or equal to now + if (cur.hour <= hour) { + start = cur; + } else { + break; + } + } + for(ScheduleEntry cur : schedule) { + // Find the first hour that is after now + // If no such hour exists, we should automatically wrap back to hour 0 + if (cur.hour > hour) { + end = cur; + break; + } + } + + if (start.hour > end.hour) { + end.hour += 24; + } + + uint16_t startTime = start.hour * 60; + uint16_t endTime = end.hour * 60; + uint16_t nowTime = hour * 60 + minute; + + uint16_t duration = endTime - startTime; + uint16_t curDuration = nowTime - startTime; + + uint8_t frac = map8(curDuration, 0, duration); + + return lerp8by8(start.brightness, end.brightness, frac); + } + + + InputEvent read() { + EVERY_N_SECONDS(60) { + needsUpdate = true; + } + if (needsUpdate) { + uint8_t hour = 0; + uint8_t minute = 0; + needsUpdate = false; + struct tm timeinfo; + if (Platform::getLocalTime(&timeinfo)) { + hour = timeinfo.tm_hour; + minute = timeinfo.tm_min; + } else { + hour = 0; + minute = 0; + } + Log.notice("Current time: %d:%d", hour, minute); + return InputEvent{InputEvent::SetBrightness, brightnessForTime(hour, minute)}; + } + return InputEvent{}; + } +}; diff --git a/src/inputs/ConfigInput.cpp b/src/inputs/ConfigInput.cpp new file mode 100644 index 0000000..67ae8e8 --- /dev/null +++ b/src/inputs/ConfigInput.cpp @@ -0,0 +1,5 @@ +#include "./ConfigInput.h" +#include "./Static.h" + +STATIC_ALLOC(ConfigInput); +STATIC_TASK(ConfigInput); diff --git a/src/inputs/ConfigInput.h b/src/inputs/ConfigInput.h new file mode 100644 index 0000000..a7d074b --- /dev/null +++ b/src/inputs/ConfigInput.h @@ -0,0 +1,77 @@ +#pragma once +#include + +class ConfigInputTask : public BufferedInputSource { + public: + ConfigInputTask() : BufferedInputSource("ConfigInput") {} + + void handleEvent(const InputEvent& evt) override { + if (evt.intent == InputEvent::UserInput) { + Buttons::Chord chord = (Buttons::Chord) evt.asInt(); + switch (chord) { + case Buttons::Circle: + m_currentIntent = nextIntent(); + //Log.info("Next setting... (%d)", m_currentIntent); + break; + case Buttons::CircleTriangle: + //Log.info("Increment..."); + increment(); + break; + case Buttons::CircleCross: + //Log.info("Decrement..."); + decrement(); + break; + case Buttons::Triangle: + //Log.info("Save..."); + setEvent(InputEvent::SaveConfigurationRequest); + break; + default: + break; + } + } + } + +private: + InputEvent::Intent m_currentIntent = InputEvent::SetDisplayLength; + + void decrement() { + int current = 0; + switch (m_currentIntent) { + case InputEvent::SetDisplayLength: + current = Static::instance()->coordMap()->pixelCount; + break; + case InputEvent::SetDisplayOffset: + current = Static::instance()->coordMap()->startPixel; + break; + default: + break; + } + setEvent(InputEvent{m_currentIntent, current - 1}); + } + + void increment() { + int current = 0; + switch (m_currentIntent) { + case InputEvent::SetDisplayLength: + current = Static::instance()->coordMap()->pixelCount; + break; + case InputEvent::SetDisplayOffset: + current = Static::instance()->coordMap()->startPixel; + break; + default: + break; + } + setEvent(InputEvent{m_currentIntent, current + 1}); + } + + InputEvent::Intent nextIntent() { + switch (m_currentIntent) { + case InputEvent::SetDisplayLength: + return InputEvent::SetDisplayOffset; + case InputEvent::SetDisplayOffset: + return InputEvent::SetDisplayLength; + default: + return InputEvent::None; + } + } +}; diff --git a/src/main.cpp b/src/main.cpp index facb15a..66e2473 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,11 +14,10 @@ #include #include "animations/Power.h" -#include "animations/Drain.h" -#include "animations/InputBlip.h" #include "inputs/ColorCycle.h" #include "inputs/Buttons.h" +#include "SafeMode.h" #define MAX_BRIGHTNESS 255 //#define PSU_MILLIAMPS 4800 @@ -36,9 +35,9 @@ Display dpy(leds, HardwareConfig::MAX_LED_NUM, Static::instance() // Setup power management Power power; - REGISTER_TASK(power); +// FIXME: rewrite as static task /*FigmentFunc configDisplay([](Display* dpy) { uint8_t brightness = brighten8_video(beatsin8(60)); auto coords = Static::instance()->coordMap(); @@ -91,63 +90,6 @@ InputMapper keyMap([](const InputEvent& evt) { REGISTER_TASK(keyMap); -class BPM : public InputSource { -public: - BPM() : InputSource("BPM") {} - void handleEvent(const InputEvent& evt) override { - if (evt.intent == InputEvent::BeatDetect) { - m_nextBpm = millis(); - m_timings.insert(millis()); - Log.notice("%d timings", m_timings.size()); - if (m_timings.size() >= 5) { - updateBPM(); - } - } - } - - InputEvent read() override { - if (m_bpm > 0) { - uint16_t now = millis(); - if (now >= m_nextBpm) { - m_nextBpm += m_bpm; - return InputEvent{InputEvent::Beat, m_bpm}; - } - if (now >= m_nextLearn && m_nextLearn != 0) { - m_timings.clear(); - m_nextLearn = 0; - } - } - return InputEvent{}; - } - -private: - uint16_t m_bpm = 0; - uint16_t m_nextBpm = 0; - uint16_t m_nextLearn = 0; - Ringbuf m_timings; - - void updateBPM() { - uint16_t avgDelta = 0; - for(uint8_t i = 0; i < m_timings.size() - 1; i++) { - uint16_t delta = m_timings.peek(i+1) - m_timings.peek(i); - Log.notice("Timing %d Delta %d", m_timings.peek(i), delta); - avgDelta += delta; - } - m_bpm = avgDelta / 4; - m_nextLearn = m_bpm * 5 + millis(); - Log.notice("BPM is now %d", m_bpm); - uint16_t trash; - m_timings.take(trash); - } -}; - -STATIC_ALLOC(BPM); -STATIC_TASK(BPM); - -Renderer configRenderer{ - {&dpy}, - {Static::instance(), /*&configDisplay,*/ Static::instance(), &power} -}; // Cycle some random colors ColorSequenceInput<9> idleCycle{{ @@ -171,249 +113,7 @@ ColorSequenceInput<7> rainbowCycle{{ REGISTER_TASK(rainbowCycle); -/*struct ConfigInputTask: public BufferedInputSource { -public: - ConfigInputTask() : BufferedInputSource("ConfigInput") {} - - void handleEvent(const InputEvent& evt) override { - if (evt.intent == InputEvent::UserInput) { - Buttons::Chord chord = (Buttons::Chord) evt.asInt(); - switch (chord) { - case Buttons::Circle: - m_currentIntent = nextIntent(); - //Log.info("Next setting... (%d)", m_currentIntent); - break; - case Buttons::CircleTriangle: - //Log.info("Increment..."); - increment(); - break; - case Buttons::CircleCross: - //Log.info("Decrement..."); - decrement(); - break; - case Buttons::Triangle: - //Log.info("Save..."); - setEvent(InputEvent::SaveConfigurationRequest); - break; - default: - break; - } - } - } - -private: - InputEvent::Intent m_currentIntent = InputEvent::SetDisplayLength; - - void decrement() { - int current = 0; - switch (m_currentIntent) { - case InputEvent::SetDisplayLength: - current = Static::instance()->coordMap()->pixelCount; - break; - case InputEvent::SetDisplayOffset: - current = Static::instance()->coordMap()->startPixel; - break; - default: - break; - } - setEvent(InputEvent{m_currentIntent, current - 1}); - } - - void increment() { - int current = 0; - switch (m_currentIntent) { - case InputEvent::SetDisplayLength: - current = Static::instance()->coordMap()->pixelCount; - break; - case InputEvent::SetDisplayOffset: - current = Static::instance()->coordMap()->startPixel; - break; - default: - break; - } - setEvent(InputEvent{m_currentIntent, current + 1}); - } - - InputEvent::Intent nextIntent() { - switch (m_currentIntent) { - case InputEvent::SetDisplayLength: - return InputEvent::SetDisplayOffset; - case InputEvent::SetDisplayOffset: - return InputEvent::SetDisplayLength; - default: - return InputEvent::None; - } - } -};*/ - -struct ScheduleEntry { - uint8_t hour; - uint8_t brightness; -}; - -std::array schedule{{ - {0, 0}, - {5, 0}, - {6, 0}, - {7, 10}, - {8, 80}, - {11, 120}, - {18, 200}, - {19, 255}, - {22, 120}, - {23, 20} -}}; - -class CircadianRhythm : public InputSource { - private: - bool needsUpdate = true; - public: - CircadianRhythm() : InputSource("CircadianRhythm") {} - - void onStart() { - needsUpdate = true; - } - - uint8_t brightnessForTime(uint8_t hour, uint8_t minute) const { - ScheduleEntry start = schedule.back(); - ScheduleEntry end = schedule.front(); - for(ScheduleEntry cur : schedule) { - // Find the last hour that is prior to or equal to now - if (cur.hour <= hour) { - start = cur; - } else { - break; - } - } - for(ScheduleEntry cur : schedule) { - // Find the first hour that is after now - // If no such hour exists, we should automatically wrap back to hour 0 - if (cur.hour > hour) { - end = cur; - break; - } - } - - if (start.hour > end.hour) { - end.hour += 24; - } - - uint16_t startTime = start.hour * 60; - uint16_t endTime = end.hour * 60; - uint16_t nowTime = hour * 60 + minute; - - uint16_t duration = endTime - startTime; - uint16_t curDuration = nowTime - startTime; - - uint8_t frac = map8(curDuration, 0, duration); - - return lerp8by8(start.brightness, end.brightness, frac); - } - - - InputEvent read() { - EVERY_N_SECONDS(60) { - needsUpdate = true; - } - if (needsUpdate) { - uint8_t hour = 0; - uint8_t minute = 0; - needsUpdate = false; - struct tm timeinfo; - if (Platform::getLocalTime(&timeinfo)) { - hour = timeinfo.tm_hour; - minute = timeinfo.tm_min; - } else { - hour = 0; - minute = 0; - } - Log.notice("Current time: %d:%d", hour, minute); - return InputEvent{InputEvent::SetBrightness, brightnessForTime(hour, minute)}; - } - return InputEvent{}; - } -}; - -STATIC_ALLOC(CircadianRhythm); -STATIC_TASK(CircadianRhythm); - -// A special mainloop app for configuring hardware settings that reboots the -// device when the user is finished. -/*MainLoop configApp{{ - Static::instance(), - - // Manage read/write of configuration data - Static::instance(), - - // Read hardware inputs - Static::instance(), - - // Map input buttons to configuration commands - new ConfigInputTask(), - - // System logging - Static::instance(), - - // Fill the entire display with a color, to see size - &configDisplay, - // Render some basic input feedback - &inputBlip, - // Render it all - &configRenderer, -}};*/ - -MainLoop configApp{std::vector()}; - -TaskFunc safeModeNag([]{ - static uint8_t frame = 0; - EVERY_N_SECONDS(30) { - Log.fatal("I am running in safe mode!"); - } - EVERY_N_MILLISECONDS(16) { - frame++; - for(int i = 0; i < HardwareConfig::MAX_LED_NUM; i++) { - leds[i] = CRGB(0, 0, 0); - } - for(int idx = 0; idx < 3; idx++) { - uint8_t length = beatsin8(5, 3, HardwareConfig::MAX_LED_NUM, 0, idx * 5); - for(int i = 0; i < length; i++) { - leds[i] += CRGB(scale8(5, beatsin8(5 + i * 7, 0, 255, 0, i*3)), 0, 0); - } - } - FastLED.show(); - } -}); - -#ifdef CONFIG_WIFI -#include "platform/arduino/WiFiTask.h" -#endif // CONFIG_WIFI -#ifdef CONFIG_OTA -#include "platform/arduino/OTA.h" -#endif // CONFIG_OTA -#ifdef CONFIG_MQTT -#include "platform/arduino/MQTTTelemetry.h" -#endif // CONFIG_MQTT - -MainLoop safeModeApp{{ - Static::instance(), - // System logging - Static::instance(), - &safeModeNag, -#ifdef CONFIG_WIFI - // ESP Wifi - Static::instance(), -#endif // CONFIG_WIFI -#ifdef CONFIG_MQTT - // MQTT - Static::instance(), -#endif // CONFIG_MQTT -#ifdef CONFIG_OTA - // OTA Updates - Static::instance(), -#endif // CONFIG_OTA -}}; - -MainLoop* runner = &safeModeApp; +MainLoop* runner = &SafeMode::safeModeApp; void setup() { // Turn on, @@ -436,18 +136,19 @@ void setup() { // Tune in, if (Platform::bootopts.isSafeMode) { Log.notice(u8"⚠️ Starting Figment in safe mode!!!"); - runner = &safeModeApp; + runner = &SafeMode::safeModeApp; FastLED.showColor(CRGB(5, 0, 0)); FastLED.show(); - } else if (Platform::bootopts.isSetup) { - Log.notice(u8"🔧 Starting Figment in configuration mode..."); - FastLED.showColor(CRGB(0, 5, 0)); - //runner = &configApp; } else { Log.notice(u8"🌌 Starting Figment..."); + + if (Platform::bootopts.isSetup) { + Log.notice(u8"🔧 Booting up into setup profile!!!"); + Static::instance()->overrideProfile("setup"); + } + // Render all layers to the displays Renderer* renderer = new Renderer({&dpy}, std::vector{Platform::beginFigments(), Platform::endFigments()}); - std::vector defaultTasks{Platform::beginTasks(), Platform::endTasks()}; defaultTasks.push_back(renderer); runner = new MainLoop{std::vector{defaultTasks.begin(), defaultTasks.end()}};