If the code hasn't been touched in this long, its probably release-worthy.
This commit is contained in:
333
src/main.cpp
333
src/main.cpp
@@ -5,8 +5,7 @@
|
||||
|
||||
#ifndef PLATFORM_PHOTON
|
||||
#include <ArduinoLog.h>
|
||||
#include <NTP.h>
|
||||
#endif
|
||||
#endif // !PLATFORM_PHOTON
|
||||
|
||||
#include "Platform.h"
|
||||
|
||||
@@ -16,29 +15,22 @@
|
||||
#include "Sequencer.h"
|
||||
#include "LogService.h"
|
||||
|
||||
#include "animations/Power.cpp"
|
||||
#include "animations/SolidAnimation.cpp"
|
||||
#include "animations/Chimes.cpp"
|
||||
#include "animations/Flashlight.cpp"
|
||||
#include "animations/Drain.cpp"
|
||||
#include <time.h>
|
||||
|
||||
#include "animations/Power.h"
|
||||
#include "animations/SolidAnimation.h"
|
||||
#include "animations/Chimes.h"
|
||||
#include "animations/Flashlight.h"
|
||||
#include "animations/Drain.h"
|
||||
#include "animations/UpdateStatus.h"
|
||||
|
||||
#include "inputs/ColorCycle.h"
|
||||
#include "inputs/Buttons.h"
|
||||
#include "inputs/MPU6050.h"
|
||||
|
||||
#ifdef PLATFORM_PHOTON
|
||||
#include "platform/particle/inputs/Photon.h"
|
||||
#include "platform/particle/inputs/CloudStatus.h"
|
||||
#include "platform/particle/PhotonTelemetry.h"
|
||||
#include "platform/particle/WebTelemetry.cpp"
|
||||
#include "platform/particle/MDNSService.cpp"
|
||||
#else
|
||||
#include "WiFiTask.h"
|
||||
#include "platform/arduino/BluetoothSerialTelemetry.h"
|
||||
#include "platform/arduino/MQTTTelemetry.h"
|
||||
#include <ArduinoOTA.h>
|
||||
#endif
|
||||
#endif // PLATFORM_PHOTON
|
||||
|
||||
//SerialLogHandler logHandler;
|
||||
|
||||
@@ -51,6 +43,7 @@
|
||||
// Enable system thread, so rendering happens while booting
|
||||
//SYSTEM_THREAD(ENABLED);
|
||||
|
||||
|
||||
// Setup FastLED and the display
|
||||
CRGB leds[HardwareConfig::MAX_LED_NUM];
|
||||
Display dpy(leds, HardwareConfig::MAX_LED_NUM, Static<ConfigService>::instance()->coordMap());
|
||||
@@ -58,7 +51,9 @@ Display dpy(leds, HardwareConfig::MAX_LED_NUM, Static<ConfigService>::instance()
|
||||
// Setup power management
|
||||
Power<MAX_BRIGHTNESS, PSU_MILLIAMPS> power;
|
||||
|
||||
FigmentFunc configDisplay([](Display* dpy) {
|
||||
REGISTER_TASK(power);
|
||||
|
||||
/*FigmentFunc configDisplay([](Display* dpy) {
|
||||
uint8_t brightness = brighten8_video(beatsin8(60));
|
||||
auto coords = Static<ConfigService>::instance()->coordMap();
|
||||
for(int i = 0; i < HardwareConfig::MAX_LED_NUM; i++) {
|
||||
@@ -68,7 +63,7 @@ FigmentFunc configDisplay([](Display* dpy) {
|
||||
dpy->pixelAt(i) += CRGB(255 - brightness, 255 - brightness, 255 - brightness);
|
||||
}
|
||||
}
|
||||
});
|
||||
});*/
|
||||
|
||||
class InputBlip: public Figment {
|
||||
public:
|
||||
@@ -94,42 +89,7 @@ private:
|
||||
};
|
||||
|
||||
InputBlip inputBlip;
|
||||
|
||||
class ArduinoOTAUpdater : public BufferedInputSource {
|
||||
public:
|
||||
ArduinoOTAUpdater() : BufferedInputSource("ArduinoOTA") {
|
||||
ArduinoOTA.onStart(&ArduinoOTAUpdater::s_onStart);
|
||||
ArduinoOTA.onProgress(&ArduinoOTAUpdater::s_onProgress);
|
||||
}
|
||||
|
||||
void loop() override {
|
||||
if (m_online) {
|
||||
ArduinoOTA.handle();
|
||||
}
|
||||
BufferedInputSource::loop();
|
||||
}
|
||||
|
||||
void handleEvent(const InputEvent& evt) {
|
||||
if (evt.intent == InputEvent::NetworkStatus && evt.asInt()) {
|
||||
Log.notice("Booting OTA");
|
||||
m_online = true;
|
||||
ArduinoOTA.begin();
|
||||
}
|
||||
}
|
||||
private:
|
||||
bool m_online = false;
|
||||
static void s_onStart() {
|
||||
Log.notice("OTA Start!");
|
||||
Static<ArduinoOTAUpdater>::instance()->setEvent(InputEvent::FirmwareUpdate);
|
||||
}
|
||||
|
||||
static void s_onProgress(unsigned int progress, unsigned int total) {
|
||||
Log.notice("OTA Progress! %d / %d", progress, total);
|
||||
Static<ArduinoOTAUpdater>::instance()->setEvent(InputEvent{InputEvent::FirmwareUpdate, progress});
|
||||
}
|
||||
};
|
||||
|
||||
STATIC_ALLOC(ArduinoOTAUpdater);
|
||||
REGISTER_TASK(inputBlip);
|
||||
|
||||
InputFunc randomPulse([]() {
|
||||
static unsigned int pulse = 0;
|
||||
@@ -145,7 +105,9 @@ InputFunc randomPulse([]() {
|
||||
}
|
||||
}
|
||||
return InputEvent{};
|
||||
}, "Pulse", Task::Running);
|
||||
}, "Pulse", Task::Stopped);
|
||||
|
||||
REGISTER_TASK(randomPulse);
|
||||
|
||||
InputMapper keyMap([](const InputEvent& evt) {
|
||||
if (evt.intent == InputEvent::UserInput) {
|
||||
@@ -167,38 +129,96 @@ InputMapper keyMap([](const InputEvent& evt) {
|
||||
return InputEvent::None;
|
||||
}, "Keymap");
|
||||
|
||||
ChimesAnimation chimes{Task::Stopped};
|
||||
SolidAnimation solid{Task::Running};
|
||||
DrainAnimation drain{Task::Stopped};
|
||||
Flashlight flashlight{Task::Stopped};
|
||||
REGISTER_TASK(keyMap);
|
||||
|
||||
|
||||
#ifndef DEFAULT_PATTERN_INDEX
|
||||
#define DEFAULT_PATTERN_INDEX 0
|
||||
#endif
|
||||
|
||||
Sequencer sequencer{{
|
||||
{"Idle", {"Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"}},
|
||||
{"Acid", {"Chimes", "Pulse", "MPU5060", "IdleColors", "Rainbow"}},
|
||||
{"Solid", {"Solid", "MPU5060", "Pulse", "CircadianRhythm"}},
|
||||
{"Interactive", {"Drain", "MPU5060", "CircadianRhythm"}},
|
||||
{"Flashlight", {"Flashlight"}},
|
||||
{"Gay", {"Solid", "Pulse", "Rainbow", "Rainbow"}},
|
||||
{"Acid", {"Chimes", "Pulse", "MPU5060", "IdleColors", "Rainbow"}},
|
||||
}};
|
||||
{"Gay", {"Solid", "Pulse", "Rainbow"}},
|
||||
}, DEFAULT_PATTERN_INDEX};
|
||||
|
||||
REGISTER_TASK(sequencer);
|
||||
|
||||
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<uint16_t, 7> 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);
|
||||
|
||||
// Render all layers to the displays
|
||||
Renderer renderer{
|
||||
{&dpy},
|
||||
{
|
||||
&chimes,
|
||||
&drain,
|
||||
&solid,
|
||||
&flashlight,
|
||||
Static<ChimesAnimation>::instance(),
|
||||
Static<DrainAnimation>::instance(),
|
||||
Static<SolidAnimation>::instance(),
|
||||
Static<Flashlight>::instance(),
|
||||
Static<UpdateStatus>::instance(),
|
||||
&inputBlip,
|
||||
&power,
|
||||
}
|
||||
};
|
||||
|
||||
REGISTER_TASK(renderer);
|
||||
|
||||
Renderer configRenderer{
|
||||
{&dpy},
|
||||
{&drain, &configDisplay, &inputBlip, &power}
|
||||
{Static<DrainAnimation>::instance(), /*&configDisplay,*/ &inputBlip, &power}
|
||||
};
|
||||
|
||||
// Cycle some random colors
|
||||
@@ -209,7 +229,9 @@ ColorSequenceInput<9> idleCycle{{
|
||||
CRGB(128, 0, 128), // Purple
|
||||
CRGB(255, 255, 255), // White
|
||||
CRGB(0, 255, 255), // Cyan
|
||||
}, "IdleColors", Task::Running};
|
||||
}, "IdleColors", Task::Stopped};
|
||||
|
||||
REGISTER_TASK(idleCycle);
|
||||
|
||||
ColorSequenceInput<7> rainbowCycle{{
|
||||
CRGB(255, 0, 0), // Red
|
||||
@@ -219,7 +241,9 @@ ColorSequenceInput<7> rainbowCycle{{
|
||||
CRGB(128, 0, 128), // Purple
|
||||
}, "Rainbow", Task::Stopped};
|
||||
|
||||
struct ConfigInputTask: public BufferedInputSource {
|
||||
REGISTER_TASK(rainbowCycle);
|
||||
|
||||
/*struct ConfigInputTask: public BufferedInputSource {
|
||||
public:
|
||||
ConfigInputTask() : BufferedInputSource("ConfigInput") {}
|
||||
|
||||
@@ -292,7 +316,7 @@ private:
|
||||
return InputEvent::None;
|
||||
}
|
||||
}
|
||||
};
|
||||
};*/
|
||||
|
||||
struct ScheduleEntry {
|
||||
uint8_t hour;
|
||||
@@ -368,9 +392,13 @@ class CircadianRhythm : public InputSource {
|
||||
uint8_t minute = 0;
|
||||
needsUpdate = false;
|
||||
struct tm timeinfo;
|
||||
Platform::getLocalTime(&timeinfo);
|
||||
hour = timeinfo.tm_hour;
|
||||
minute = timeinfo.tm_min;
|
||||
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)};
|
||||
}
|
||||
@@ -379,20 +407,16 @@ class CircadianRhythm : public InputSource {
|
||||
};
|
||||
|
||||
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{{
|
||||
/*MainLoop configApp{{
|
||||
Static<Platform>::instance(),
|
||||
|
||||
// Manage read/write of configuration data
|
||||
Static<ConfigService>::instance(),
|
||||
|
||||
#ifdef PLATFORM_PHOTON
|
||||
// Update photon telemetry
|
||||
Static<PhotonTelemetry>::instance(),
|
||||
#endif
|
||||
|
||||
// Read hardware inputs
|
||||
Static<Buttons>::instance(),
|
||||
|
||||
@@ -408,12 +432,14 @@ MainLoop configApp{{
|
||||
&inputBlip,
|
||||
// Render it all
|
||||
&configRenderer,
|
||||
}};
|
||||
}};*/
|
||||
|
||||
MainLoop configApp{std::vector<Task*>()};
|
||||
|
||||
TaskFunc safeModeNag([]{
|
||||
static uint8_t frame = 0;
|
||||
EVERY_N_SECONDS(30) {
|
||||
Log.notice("I am running in safe mode!");
|
||||
Log.fatal("I am running in safe mode!");
|
||||
}
|
||||
EVERY_N_MILLISECONDS(16) {
|
||||
frame++;
|
||||
@@ -430,149 +456,72 @@ TaskFunc safeModeNag([]{
|
||||
}
|
||||
});
|
||||
|
||||
MainLoop safeModeApp({
|
||||
#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<Platform>::instance(),
|
||||
// System logging
|
||||
Static<LogService>::instance(),
|
||||
&safeModeNag,
|
||||
#ifdef CONFIG_WIFI
|
||||
// ESP Wifi
|
||||
Static<WiFiTask>::instance(),
|
||||
// System logging
|
||||
Static<LogService>::instance(),
|
||||
#endif // CONFIG_WIFI
|
||||
#ifdef CONFIG_MQTT
|
||||
// MQTT
|
||||
Static<MQTTTelemetry>::instance(),
|
||||
#endif // CONFIG_MQTT
|
||||
#ifdef CONFIG_OTA
|
||||
// OTA Updates
|
||||
Static<ArduinoOTAUpdater>::instance(),
|
||||
|
||||
&safeModeNag,
|
||||
});
|
||||
|
||||
// Turn on,
|
||||
MainLoop renderbugApp{{
|
||||
|
||||
Static<Platform>::instance(),
|
||||
|
||||
// Load/update graphics configuration from EEPROM
|
||||
Static<ConfigService>::instance(),
|
||||
|
||||
// Platform inputs
|
||||
// TODO: Merge cloud and esp wifi tasks into a common networking base
|
||||
#ifdef PLATFORM_PHOTON
|
||||
// Particle cloud status
|
||||
Static<CloudStatus>::instance(),
|
||||
|
||||
// Monitor network state and provide particle API events
|
||||
Static<PhotonInput>::instance(),
|
||||
#else
|
||||
// ESP Wifi
|
||||
//Static<WiFiTask>::instance(),
|
||||
#endif
|
||||
|
||||
#ifdef BOARD_ESP32
|
||||
// ESP32 Bluetooth
|
||||
Static<BluetoothSerialTelemetry>::instance(),
|
||||
#endif
|
||||
|
||||
// System logging
|
||||
Static<LogService>::instance(),
|
||||
|
||||
#ifdef CONFIG_MPU5060
|
||||
// Hardware drivers
|
||||
Static<MPU5060>::instance(),
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_BUTTONS
|
||||
Static<Buttons>::instance(),
|
||||
|
||||
// Map buttons to events
|
||||
&keyMap,
|
||||
#endif
|
||||
|
||||
// Pattern sequencer
|
||||
&sequencer,
|
||||
|
||||
// Daily rhythm activities
|
||||
Static<CircadianRhythm>::instance(),
|
||||
|
||||
// Periodic motion input
|
||||
//&randomPulse,
|
||||
|
||||
// Periodic color inputs
|
||||
&idleCycle,
|
||||
&rainbowCycle,
|
||||
|
||||
// Animations
|
||||
&chimes,
|
||||
&drain,
|
||||
&solid,
|
||||
&flashlight,
|
||||
|
||||
// Update UI layer
|
||||
&power,
|
||||
Static<UpdateStatus>::instance(),
|
||||
&inputBlip,
|
||||
|
||||
// Render everything
|
||||
&renderer,
|
||||
|
||||
// Platform telemetry
|
||||
// TODO: Combine some of these services into a unified telemetry API with
|
||||
// platform-specific backends?
|
||||
// Or at least, just the MQTT and watchdog ones.
|
||||
#ifdef PLATFORM_PHOTON
|
||||
// Update photon telemetry
|
||||
Static<PhotonTelemetry>::instance(),
|
||||
|
||||
// Web telemetry UI
|
||||
Static<WebTelemetry>::instance(),
|
||||
|
||||
// MQTT telemetry
|
||||
Static<MQTTTelemetry>::instance(),
|
||||
|
||||
// Network discovery
|
||||
Static<MDNSService>::instance(),
|
||||
|
||||
//Watchdog
|
||||
Static<Watchdog>::instance(),
|
||||
#else
|
||||
// MQTT
|
||||
Static<MQTTTelemetry>::instance(),
|
||||
|
||||
// OTA Updates
|
||||
Static<ArduinoOTAUpdater>::instance(),
|
||||
#endif
|
||||
#endif // CONFIG_OTA
|
||||
}};
|
||||
|
||||
MainLoop &runner = renderbugApp;
|
||||
MainLoop* runner = &safeModeApp;
|
||||
|
||||
// Tune in,
|
||||
void setup() {
|
||||
// Turn on,
|
||||
Platform::preSetup();
|
||||
#ifdef CONFIG_MQTT
|
||||
Static<MQTTTelemetry>::instance()->setSequencer(&sequencer);
|
||||
#endif // CONFIG_MQTT
|
||||
Log.notice(u8"🐛 Booting Renderbug!");
|
||||
Log.notice(u8"🐞 I am built for %d LEDs running on %dmA", HardwareConfig::MAX_LED_NUM, PSU_MILLIAMPS);
|
||||
Log.notice(u8"📡 Platform %s version %s", Platform::name(), Platform::version());
|
||||
Platform::bootSplash();
|
||||
|
||||
Log.notice(u8"Setting timezone to -7 (PST)");
|
||||
Platform::setTimezone(-7);
|
||||
|
||||
Log.notice(u8" Setting up platform...");
|
||||
Platform::setup();
|
||||
Platform::bootSplash();
|
||||
|
||||
Log.notice(u8"💡 Starting FastLED...");
|
||||
Platform::addLEDs(leds, HardwareConfig::MAX_LED_NUM);
|
||||
runner = new MainLoop{std::vector<Task*>{Platform::beginTasks(), Platform::endTasks()}};
|
||||
|
||||
// Tune in,
|
||||
if (Platform::bootopts.isSafeMode) {
|
||||
Log.notice(u8"⚠️ Starting Figment in safe mode!!!");
|
||||
runner = safeModeApp;
|
||||
runner = &safeModeApp;
|
||||
FastLED.showColor(CRGB(5, 0, 0));
|
||||
FastLED.show();
|
||||
} else if (Platform::bootopts.isSetup) {
|
||||
Log.notice(u8"🔧 Starting Figment in configuration mode...");
|
||||
runner = configApp;
|
||||
//runner = &configApp;
|
||||
} else {
|
||||
Log.notice(u8"🌌 Starting Figment...");
|
||||
}
|
||||
Serial.flush();
|
||||
runner.start();
|
||||
runner->start();
|
||||
|
||||
//Log.info(u8"💽 %lu bytes of free RAM", System.freeMemory());
|
||||
Log.notice(u8"🚀 Setup complete! Ready to rock and roll.");
|
||||
@@ -581,6 +530,8 @@ void setup() {
|
||||
|
||||
// Drop out.
|
||||
void loop() {
|
||||
//Platform::loop();
|
||||
runner.loop();
|
||||
EVERY_N_SECONDS(5) {
|
||||
Log.notice("FPS: %d", FastLED.getFPS());
|
||||
}
|
||||
runner->loop();
|
||||
}
|
||||
|
Reference in New Issue
Block a user