cleanup main.cpp, split platform code into a Platform object

This commit is contained in:
2021-03-31 11:50:00 -07:00
parent a6534bcb20
commit 10bbcd6786
21 changed files with 768 additions and 408 deletions

View File

@@ -1,5 +1,3 @@
#define RENDERBUG_VERSION 1
#include "Arduino.h"
#include <FastLED.h>
@@ -10,13 +8,14 @@
#include <NTP.h>
#endif
#include "BootOptions.h"
#include "Platform.h"
#include "Static.h"
#include "Config.h"
#include "colors.h"
#include "Sequencer.h"
#include "LogService.h"
#include "animations/Power.cpp"
#include "animations/SolidAnimation.cpp"
@@ -62,16 +61,6 @@ Display neckDisplay(leds, HardwareConfig::MAX_LED_NUM, &neckMap);
// Setup power management
Power<MAX_BRIGHTNESS, PSU_MILLIAMPS> power;
// Clip the display at whatever is configured while still showing over-paints
FigmentFunc displayClip([](Display* dpy) {
auto coords = Static<ConfigService>::instance()->coordMap();
for(int i = 0; i < HardwareConfig::MAX_LED_NUM; i++) {
if (i < coords->startPixel || i > coords->pixelCount + coords->startPixel) {
dpy->pixelAt(i) %= 40;
}
}
});
FigmentFunc configDisplay([](Display* dpy) {
uint8_t brightness = brighten8_video(beatsin8(60));
auto coords = Static<ConfigService>::instance()->coordMap();
@@ -112,23 +101,39 @@ InputBlip inputBlip;
class ArduinoOTAUpdater : public BufferedInputSource {
public:
ArduinoOTAUpdater() : BufferedInputSource("ArduinoOTA") {
ArduinoOTA.onStart(&ArduinoOTAUpdater::s_onStart).onProgress(&ArduinoOTAUpdater::s_onProgress);
ArduinoOTA.onStart(&ArduinoOTAUpdater::s_onStart);
ArduinoOTA.onProgress(&ArduinoOTAUpdater::s_onProgress);
}
void loop() override {
ArduinoOTA.handle();
if (m_online) {
ArduinoOTA.handle();
}
BufferedInputSource::loop();
}
void handleEvent(const InputEvent& evt) {
if (evt.intent == InputEvent::NetworkStatus) {
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);
InputFunc randomPulse([]() {
static unsigned int pulse = 0;
EVERY_N_MILLISECONDS(25) {
@@ -158,6 +163,8 @@ InputMapper keyMap([](const InputEvent& evt) {
case Buttons::Cross:
return InputEvent::UserInput;
break;
default:
break;
}
}
return InputEvent::None;
@@ -189,7 +196,6 @@ Renderer renderer{
&solid,
&flashlight,
Static<UpdateStatus>::instance(),
&displayClip,
&inputBlip,
&power,
}
@@ -249,6 +255,8 @@ public:
//Log.info("Save...");
setEvent(InputEvent::SaveConfigurationRequest);
break;
default:
break;
}
}
}
@@ -265,6 +273,8 @@ private:
case InputEvent::SetDisplayOffset:
current = Static<ConfigService>::instance()->coordMap()->startPixel;
break;
default:
break;
}
setEvent(InputEvent{m_currentIntent, current - 1});
}
@@ -278,6 +288,8 @@ private:
case InputEvent::SetDisplayOffset:
current = Static<ConfigService>::instance()->coordMap()->startPixel;
break;
default:
break;
}
setEvent(InputEvent{m_currentIntent, current + 1});
}
@@ -288,22 +300,12 @@ private:
return InputEvent::SetDisplayOffset;
case InputEvent::SetDisplayOffset:
return InputEvent::SetDisplayLength;
default:
return InputEvent::None;
}
}
};
enum Phase {
Null,
AstronomicalDay,
NauticalDay,
CivilDay,
CivilNight,
NauticalNight,
AstronomicalNight,
Evening,
Bedtime,
};
struct ScheduleEntry {
uint8_t hour;
uint8_t brightness;
@@ -358,28 +360,32 @@ uint8_t brightnessForTime(uint8_t hour, uint8_t minute) {
return lerp8by8(start.brightness, end.brightness, frac);
}
WiFiUDP wifiUdp;
NTP ntp(wifiUdp);
class CircadianRhythm : public InputSource {
private:
bool needsUpdate = true;
public:
CircadianRhythm() : InputSource("CircadianRhythm") {}
InputFunc circadianRhythm([]() {
static bool needsUpdate = true;
InputEvent read() {
EVERY_N_SECONDS(60) {
needsUpdate = true;
}
#ifdef PLATFORM_PHOTON
if (Time.isValid() && needsUpdate) {
needsUpdate = false;
return InputEvent{InputEvent::SetBrightness, brightnessForTime(Time.hour(), Time.minute())};
if (needsUpdate) {
uint8_t hour = 0;
uint8_t minute = 0;
needsUpdate = false;
struct tm timeinfo;
Platform::getLocalTime(&timeinfo);
hour = timeinfo.tm_hour;
minute = timeinfo.tm_min;
Log.notice("Current time: %d:%d", hour, minute);
return InputEvent{InputEvent::SetBrightness, brightnessForTime(hour, minute)};
}
return InputEvent{};
}
#else
ntp.update();
if (needsUpdate) {
needsUpdate = false;
return InputEvent{InputEvent::SetBrightness, brightnessForTime(ntp.hours(), ntp.minutes())};
}
#endif
return InputEvent{};
}, "CircadianRhythm", Task::Running);
};
STATIC_ALLOC(CircadianRhythm);
// A special mainloop app for configuring hardware settings that reboots the
// device when the user is finished.
@@ -393,11 +399,14 @@ MainLoop configApp{{
#endif
// Read hardware inputs
new Buttons(),
Static<Buttons>::instance(),
// Map input buttons to configuration commands
new ConfigInputTask(),
// System logging
Static<LogService>::instance(),
// Fill the entire display with a color, to see size
&configDisplay,
// Render some basic input feedback
@@ -409,10 +418,11 @@ MainLoop configApp{{
// Turn on,
MainLoop renderbugApp{{
// Load/update graphics configuration from EEPROM and Particle
// 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(),
@@ -424,9 +434,12 @@ MainLoop renderbugApp{{
Static<WiFiTask>::instance(),
#endif
// System logging
Static<LogService>::instance(),
// Hardware drivers
//new MPU5060(),
//new Buttons(),
Static<MPU5060>::instance(),
Static<Buttons>::instance(),
// Map buttons to events
&keyMap,
@@ -435,7 +448,7 @@ MainLoop renderbugApp{{
&sequencer,
// Daily rhythm activities
&circadianRhythm,
Static<CircadianRhythm>::instance(),
// Periodic motion input
&randomPulse,
@@ -454,7 +467,6 @@ MainLoop renderbugApp{{
// Update UI layer
&power,
&displayClip,
Static<UpdateStatus>::instance(),
&inputBlip,
@@ -462,6 +474,9 @@ MainLoop renderbugApp{{
&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(),
@@ -480,63 +495,33 @@ MainLoop renderbugApp{{
#else
// MQTT
Static<MQTTTelemetry>::instance(),
// OTA Updates
Static<ArduinoOTAUpdater>::instance(),
#endif
}};
MainLoop &runner = renderbugApp;
#ifdef PLATFORM_PHOTON
STARTUP(BootOptions::initPins());
retained BootOptions bootopts;
#else
BootOptions bootopts;
void printNewline(Print* logOutput) {
logOutput->print("\r\n");
}
#endif
// Tune in,
void setup() {
Serial.begin(115200);
#ifdef PLATFORM_PHOTON
System.enableFeature(FEATURE_RETAINED_MEMORY);
if (bootopts.isFlash) {
System.dfu();
}
if (bootopts.isSerial) {
bootopts.waitForRelease();
while(!Serial.isConnected()) {
Particle.process();
}
//Log.info("\xf0\x9f\x94\x8c Serial connected");
}
#else
Log.begin(LOG_LEVEL_VERBOSE, &Serial, true);
Log.setSuffix(printNewline);
Log.notice("Test log?");
#endif
Platform::preSetup();
Static<MQTTTelemetry>::instance()->setSequencer(&sequencer);
Log.notice(u8"🐛 Booting Renderbug!");
Log.notice(u8"🐞 I am built for %d LEDs running on %dmA", HardwareConfig::MAX_LED_NUM, PSU_MILLIAMPS);
#ifdef PLATFORM_PHOTON
Log.notice(u8"📡 Particle version %s", System.version().c_str());
#endif
Log.notice(u8" Boot pin configuration:");
Log.notice(u8" 2: Setup - %d", bootopts.isSetup);
Log.notice(u8" 3: Serial - %d", bootopts.isSerial);
Log.notice(u8" 4: Flash - %d", bootopts.isFlash);
Log.notice(u8"📡 Platform %s version %s", Platform::name(), Platform::version());
Platform::bootSplash();
//Log.info(u8" Setting timezone to UTC-7");
//Time.zone(-7);
Log.notice(u8"Setting timezone to -7 (PST)");
Platform::setTimezone(-7);
Log.notice(u8" Setting up platform...");
Platform::setup();
Log.notice(u8"💡 Starting FastLED...");
#ifdef PLATFORM_PHOTON
FastLED.addLeds<NEOPIXEL, 6>(leds, HardwareConfig::MAX_LED_NUM);
#else
FastLED.addLeds<WS2812B, 13, RGB>(leds, HardwareConfig::MAX_LED_NUM);
#endif
Platform::addLEDs(leds, HardwareConfig::MAX_LED_NUM);
if (bootopts.isSetup) {
if (Platform::bootopts.isSetup) {
Log.notice(u8"🌌 Starting Figment in configuration mode...");
runner = configApp;
} else {
@@ -548,8 +533,6 @@ void setup() {
//Log.info(u8"💽 %lu bytes of free RAM", System.freeMemory());
Log.notice(u8"🚀 Setup complete! Ready to rock and roll.");
Serial.flush();
ntp.begin();
ArduinoOTA.begin();
}
// Drop out.