Compare commits

..

No commits in common. "9a3186edbd96d4f4514126ad34f59fcd7e9545a1" and "74c2ddb405e645046d8dbbd5ae4a70b74f24ac4d" have entirely different histories.

42 changed files with 132 additions and 847 deletions

View File

@ -1,24 +0,0 @@
{
"version": 1,
"rotation": 3,
"strides": [
{"x": 0, "y": 0, "pixels": 17},
{"x": 0, "y": 1, "pixels": 17},
{"x": 0, "y": 2, "pixels": 17},
{"x": 0, "y": 3, "pixels": 17},
{"x": 0, "y": 4, "pixels": 16},
{"x": 0, "y": 5, "pixels": 17},
{"x": 0, "y": 6, "pixels": 17},
{"x": 0, "y": 7, "pixels": 17},
{"x": 0, "y": 8, "pixels": 17},
{"x": 0, "y": 9, "pixels": 17},
{"x": 0, "y": 10, "pixels": 17},
{"x": 0, "y": 11, "pixels": 17},
{"x": 0, "y": 12, "pixels": 18},
{"x": 0, "y": 13, "pixels": 17},
{"x": 0, "y": 14, "pixels": 18},
{"x": 0, "y": 15, "pixels": 17},
{"x": 0, "y": 16, "pixels": 17},
{"x": 0, "y": 17, "pixels": 17}
]
}

View File

@ -4,8 +4,7 @@
"Renderer", "Renderer",
"SerialInput", "SerialInput",
"BPM", "BPM",
"Bluetooth", "Bluetooth"
"Serial"
], ],
"scenes": { "scenes": {
"Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"], "Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"],

View File

@ -7,8 +7,7 @@
"WiFi", "WiFi",
"MQTT", "MQTT",
"ArduinoOTA", "ArduinoOTA",
"BPM", "BPM"
"Serial"
], ],
"scenes": { "scenes": {
"Idle": ["Solid", "MPU5060", "IdleColors", "CircadianRhythm"], "Idle": ["Solid", "MPU5060", "IdleColors", "CircadianRhythm"],

View File

@ -6,8 +6,7 @@
"WiFi", "WiFi",
"MQTT", "MQTT",
"ArduinoOTA", "ArduinoOTA",
"UpdateStatusAnimation", "UpdateStatusAnimation"
"Serial"
], ],
"scenes": { "scenes": {
"Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"], "Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"],

View File

@ -1,25 +0,0 @@
{
"version": 1,
"tasks": [
"Bluetooth",
"WiFi",
"Renderer",
"Power",
"BPM",
"MQTT",
"ArduinoOTA",
"UpdateStatusAnimation",
"Serial"
],
"scenes": {
"Rain": ["Rain", "Rainbow"],
"Test": ["Test"],
"Idle": ["Solid", "Pulse", "Rainbow", "CircadianRhythm"],
"Acid": ["Chimes", "Pulse", "IdleColors", "Rainbow"],
"Flashlight": ["Flashlight"]
},
"surfaceMap": "ponder",
"defaults": {
"mqtt.ip": "10.0.0.2"
}
}

View File

@ -4,8 +4,7 @@
"Renderer", "Renderer",
"ConfigInput", "ConfigInput",
"ConfigDisplay", "ConfigDisplay",
"InputBlip", "InputBlip"
"Serial"
], ],
"scenes": [], "scenes": [],
"surfaceMap": "default" "surfaceMap": "default"

View File

@ -1,7 +0,0 @@
#include "./Command.h"
void
doNothing(Args& args, Print& printer)
{}
Command::Command() : func(doNothing) {}

View File

@ -1,33 +0,0 @@
#pragma once
#include <Arduino.h>
class Args {
private:
String *str;
public:
Args(String *str) : str(str) {}
String operator[](int pos) {
char buf[64];
strncpy(buf, str->c_str(), sizeof(buf));
char *args = strtok(buf, " ");
while (pos > 0 && args != NULL) {
args = strtok(NULL, " ");
pos--;
}
if (args == NULL) {
return String();
}
return String(args);
}
};
struct CommandList;
struct Command {
using Executor = std::function<void(Args&, Print& output)>;
Executor func;
const char* name = NULL;
Command();
Command(const char* name, Executor func) : name(name), func(func) {}
};

View File

@ -43,7 +43,6 @@ CRGB&
Display::pixelAt(int idx) Display::pixelAt(int idx)
{ {
const int kx = idx % pixelCount(); const int kx = idx % pixelCount();
assert(abs(kx) < pixelCount());
if (kx < 0) { if (kx < 0) {
return m_pixels[pixelCount() + 1 + kx]; return m_pixels[pixelCount() + 1 + kx];
} else { } else {

View File

@ -1,9 +0,0 @@
#include "./Figment.h"
const std::vector<Command> emptyCommands;
const std::vector<Command>&
Task::commands() const
{
return emptyCommands;
}

View File

@ -2,7 +2,6 @@
#include <Arduino.h> #include <Arduino.h>
#include <functional> #include <functional>
#include <ArduinoLog.h> #include <ArduinoLog.h>
#include "./Command.h"
#define F_LIKELY(x) __builtin_expect(!!(x), true) #define F_LIKELY(x) __builtin_expect(!!(x), true)
#define F_UNLIKELY(x) __builtin_expect(!!(x), false) #define F_UNLIKELY(x) __builtin_expect(!!(x), false)
@ -69,8 +68,6 @@ struct Task : public virtual Loopable {
const char* name = ""; const char* name = "";
State state = Stopped; State state = Stopped;
virtual const std::vector<Command> &commands() const;
}; };
/** /**

View File

@ -114,10 +114,6 @@ struct InputEvent: public Variant {
InputEvent() InputEvent()
: Variant(), intent(None) {} : Variant(), intent(None) {}
bool operator!=(const InputEvent::Intent& otherIntent) {
return intent != otherIntent;
}
Intent intent; Intent intent;
}; };

View File

@ -24,23 +24,17 @@ MainLoop::dispatchSync(const InputEvent& evt)
{ {
if (evt.intent == InputEvent::StartThing || evt.intent == InputEvent::StopThing) { if (evt.intent == InputEvent::StartThing || evt.intent == InputEvent::StopThing) {
const bool jobState = (evt.intent == InputEvent::StartThing); const bool jobState = (evt.intent == InputEvent::StartThing);
bool wasFound = false;
for(auto figmentJob: scheduler.tasks) { for(auto figmentJob: scheduler.tasks) {
if (!strcmp(figmentJob->name, evt.asString())) { if (!strcmp(figmentJob->name, evt.asString())) {
if (jobState) { if (jobState) {
Log.trace("** Starting %s", figmentJob->name); Log.trace("** Starting %s", figmentJob->name);
figmentJob->start(); figmentJob->start();
wasFound = true;
} else { } else {
Log.trace("** Stopping %s", figmentJob->name); Log.trace("** Stopping %s", figmentJob->name);
figmentJob->stop(); figmentJob->stop();
wasFound = true;
} }
} }
} }
if (!wasFound) {
Log.warning("** Unable to find task %s", evt.asString());
}
} }
for(Task* task : scheduler) { for(Task* task : scheduler) {
@ -92,8 +86,7 @@ MainLoop::loop()
} }
} }
frameSpeed = millis() - frameStart; frameSpeed = millis() - frameStart;
if (frameSpeed >= 34) { // TODO: Configure max frame time at build. Default if (frameSpeed >= 23) { // TODO: Configure max frame time at build
// to 30FPS
const char* slowestName = (slowestTask->name ? slowestTask->name : "(Unnamed)"); const char* slowestName = (slowestTask->name ? slowestTask->name : "(Unnamed)");
Log.warning("Slow frame: %dms, %d tasks, longest task %s was %dms", frameSpeed, taskCount, slowestName, slowest); Log.warning("Slow frame: %dms, %d tasks, longest task %s was %dms", frameSpeed, taskCount, slowestName, slowest);
} }

View File

@ -3,18 +3,6 @@
#include <ArduinoLog.h> #include <ArduinoLog.h>
#ifndef __NOINIT_ATTR // Pre-defined on esp32
#define __NOINIT_ATTR __attribute__ ((section (".noinit")))
#endif
__NOINIT_ATTR const char* s_lastFigmentName;
const char*
Renderer::lastFigmentName()
{
return s_lastFigmentName;
}
void void
Renderer::loop() Renderer::loop()
{ {
@ -25,7 +13,6 @@ Renderer::loop()
unsigned int frameStart = ESP.getCycleCount(); unsigned int frameStart = ESP.getCycleCount();
#endif #endif
Log.verbose("Render %s", figment->name); Log.verbose("Render %s", figment->name);
s_lastFigmentName = figment->name;
figment->render(dpy); figment->render(dpy);
#if defined(BOARD_ESP32) or defined(BOARD_ESP8266) #if defined(BOARD_ESP32) or defined(BOARD_ESP8266)
unsigned int runtime = (ESP.getCycleCount() - frameStart) / 160000; unsigned int runtime = (ESP.getCycleCount() - frameStart) / 160000;

View File

@ -10,8 +10,6 @@ public:
void loop() override; void loop() override;
void onStart() override; void onStart() override;
static const char* lastFigmentName();
private: private:
const std::vector<Figment*> m_figments; const std::vector<Figment*> m_figments;
const std::vector<Display*> m_displays; const std::vector<Display*> m_displays;

View File

@ -86,8 +86,6 @@ struct Ringbuf {
size_t size() { size_t size() {
if (m_tail > m_head) { if (m_tail > m_head) {
return m_tail - m_head; return m_tail - m_head;
} else if (m_tail == m_head) {
return 0;
} }
return m_tail + (Size - m_head); return m_tail + (Size - m_head);
} }

View File

@ -54,20 +54,17 @@ void
Surface::paintShader(Surface::Shader shader) Surface::paintShader(Surface::Shader shader)
{ {
PerfCounter _("paintShader"); PerfCounter _("paintShader");
uint8_t startX = min(start.x, end.x); const uint16_t width = end.x - start.x + 1;
uint8_t startY = min(start.y, end.y); const uint16_t height = end.y - start.y + 1;
uint8_t endX = max(start.x, end.x);
uint8_t endY = max(start.y, end.y);
const uint16_t width = endX - startX + 1;
const uint16_t height = endY - startY + 1;
const uint8_t xMod = 255 / width; const uint8_t xMod = 255 / width;
const uint8_t yMod = 255 / height; const uint8_t yMod = 255 / height;
for(auto x = 0; x < width; x++) { for(auto x = 0; x < width; x++) {
for(auto y = 0; y < height; y++) { for(auto y = 0; y < height; y++) {
PhysicalCoordinates coords{x + startX, y + startY}; PhysicalCoordinates coords{x + start.x, y + start.y};
VirtualCoordinates virtCoords{m_display->coordinateMapping()->physicalToVirtualCoords(coords)}; VirtualCoordinates virtCoords{m_display->coordinateMapping()->physicalToVirtualCoords(coords)};
VirtualCoordinates surfaceCoords{xMod * x, yMod * y}; VirtualCoordinates surfaceCoords{xMod * x, yMod * y};
//Log.notice("width=%d height=%d vx=%d vy=%d sx=%d sy=%d x=%d y=%d px=%d py=%d", width, height, startX, startY, x, y, coords.x, coords.y); // 256 = 1.0 //Log.notice("width=%d height=%d vx=%d vy=%d sx=%d sy=%d x=%d y=%d px=%d py=%d", width, height, start.x, start.y, x, y, coords.x, coords.y);
// 256 = 1.0
// 128 = 0.0 // 128 = 0.0
// 0 = 1.0 // 0 = 1.0
shader(m_display->pixelAt(coords), virtCoords, coords, surfaceCoords); shader(m_display->pixelAt(coords), virtCoords, coords, surfaceCoords);

View File

@ -9,7 +9,7 @@
; https://docs.platformio.org/page/projectconf.html ; https://docs.platformio.org/page/projectconf.html
[common_env_data] [common_env_data]
src_filter = "+<*> -<.git/> -<.svn/> -<platform/> -<inputs/> +<inputs/BPM.cpp> +<inputs/Serial.cpp> +<inputs/CircadianRhythm.cpp>" src_filter = "+<*> -<.git/> -<.svn/> -<platform/> -<inputs/> +<inputs/BPM.cpp>"
lib_ldf_mode = chain+ lib_ldf_mode = chain+
extra_scripts = verify-configs.py extra_scripts = verify-configs.py
src_build_flags = src_build_flags =

View File

@ -14,21 +14,10 @@ retained bool LAST_BOOT_WAS_FLASH;
retained bool LAST_BOOT_WAS_SERIAL; retained bool LAST_BOOT_WAS_SERIAL;
#endif #endif
#ifndef __NOINIT_ATTR // Pre-defined on esp32 #ifdef BOARD_ESP32
#define __NOINIT_ATTR __attribute__ ((section (".noinit"))) __NOINIT_ATTR uint8_t s_rebootCount = 0;
#endif #endif
#define SAFE_MODE_MAGIC 6942
__NOINIT_ATTR uint8_t s_rebootCount;
__NOINIT_ATTR uint16_t s_forceSafeMode;
void
BootOptions::forceSafeMode()
{
s_forceSafeMode = SAFE_MODE_MAGIC;
}
void void
BootOptions::initPins() BootOptions::initPins()
{ {
@ -71,20 +60,23 @@ BootOptions::BootOptions()
#ifdef BOARD_ESP8266 #ifdef BOARD_ESP8266
struct rst_info resetInfo = *ESP.getResetInfoPtr(); struct rst_info resetInfo = *ESP.getResetInfoPtr();
resetReason = resetInfo.reason; resetReason = resetInfo.reason;
crashCount = s_rebootCount; EEPROM.begin(sizeof(crashCount));
if (resetInfo.reason == REASON_SOFT_WDT_RST || resetInfo.reason == REASON_WDT_RST || resetInfo.reason == REASON_EXCEPTION_RST) { EEPROM.get(sizeof(HardwareConfig) + 32, crashCount);
EEPROM.end();
if (resetInfo.reason == REASON_WDT_RST || resetInfo.reason == REASON_EXCEPTION_RST) {
if (crashCount++ >= 3) { if (crashCount++ >= 3) {
// Boot into safe mode if the watchdog reset us three times in a row. // Boot into safe mode if the watchdog reset us three times in a row.
isSafeMode = true; isSafeMode = true;
} else {
EEPROM.begin(sizeof(crashCount));
EEPROM.put(sizeof(HardwareConfig) + 32, crashCount);
EEPROM.end();
} }
} else { } else if (crashCount != 0) {
crashCount = 0; crashCount = 0;
} EEPROM.begin(sizeof(crashCount));
s_rebootCount = crashCount; EEPROM.put(sizeof(HardwareConfig) + 32, crashCount);
EEPROM.end();
if (resetInfo.reason > 0 && s_forceSafeMode == SAFE_MODE_MAGIC) {
isSafeMode = true;
s_forceSafeMode = 0;
} }
#endif #endif
} }

View File

@ -7,7 +7,6 @@ struct BootOptions {
BootOptions(); BootOptions();
void waitForRelease(); void waitForRelease();
void forceSafeMode();
bool isSetup = false; bool isSetup = false;
bool isSerial = false; bool isSerial = false;

View File

@ -53,7 +53,7 @@ Configuration::get(const char* key, bool defaultVal) const
} }
} }
StaticJsonDocument<2048> jsonConfig; StaticJsonDocument<1024> jsonConfig;
constexpr uint16_t HardwareConfig::MAX_LED_NUM; constexpr uint16_t HardwareConfig::MAX_LED_NUM;
@ -154,11 +154,6 @@ ConfigService::loadMap(const String& mapName)
deserializeJson(jsonConfig, configFile); deserializeJson(jsonConfig, configFile);
configFile.close(); configFile.close();
JsonArray strideList = jsonConfig["strides"]; JsonArray strideList = jsonConfig["strides"];
if (jsonConfig.containsKey("rotation")) {
m_jsonMap.rotation = jsonConfig["rotation"];
} else {
m_jsonMap.rotation = 0;
}
m_jsonMap.load(strideList); m_jsonMap.load(strideList);
return true; return true;
} else { } else {
@ -218,9 +213,8 @@ ConfigService::loadProfile(const char* profileName)
m_jsonMap.loadDefault(); m_jsonMap.loadDefault();
} }
Log.notice("config: Loaded!"); Log.notice("config: Loaded!");
strcpy(m_config.data.loadedProfile, profileName);
} else { } else {
Log.warning("config: Could not find profile json %s!", fname.c_str()); Log.warning("config: Could not load profile %s!", profileName);
return false; return false;
} }
@ -260,56 +254,5 @@ ConfigService::handleEvent(const InputEvent &evt)
} }
} }
void
doMapList(Args& args, Print& out)
{
static const auto conf = Static<ConfigService>::instance();
out.println("Available maps:");
LittleFS.begin();
for(auto it = conf->mapsBegin();it != conf->mapsEnd(); it++) {
out.println(*it);
}
LittleFS.end();
}
void
doSave(Args& args, Print& print)
{
MainLoop::instance()->dispatch(InputEvent::SaveConfigurationRequest);
}
static String s;
void
doSetProfile(Args& args, Print& out)
{
s = args[1];
MainLoop::instance()->dispatch(InputEvent{InputEvent::LoadConfigurationByName, s.c_str()});
}
void
doCoordMap(Args& args, Print& out)
{
VirtualCoordinates coords{atoi(args[1].c_str()), atoi(args[2].c_str())};
auto map = Static<ConfigService>::instance()->coordMap();
auto pPos = map->virtualToPhysicalCoords(coords);
auto idx = map->physicalCoordsToIndex(pPos);
char buf[32];
sprintf(buf, "(%d, %d) -> (%d, %d) -> %d", coords.x, coords.y, pPos.x, pPos.y, idx);
out.println(buf);
}
const std::vector<Command>&
ConfigService::commands() const
{
static const std::vector<Command> _commands = {
{"save", doSave},
{"profile", doSetProfile},
{"maps", doMapList},
{"coordmap", doCoordMap}
};
return _commands;
}
STATIC_ALLOC(ConfigService); STATIC_ALLOC(ConfigService);
STATIC_TASK(ConfigService); STATIC_TASK(ConfigService);

View File

@ -2,7 +2,6 @@
#include <Figments.h> #include <Figments.h>
#include "JsonCoordinateMapping.h" #include "JsonCoordinateMapping.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <LittleFS.h>
class Configuration { class Configuration {
public: public:
@ -40,7 +39,7 @@ struct HardwareConfig {
void save(); void save();
bool isValid() const; bool isValid() const;
static constexpr uint16_t MAX_LED_NUM = 512; static constexpr uint16_t MAX_LED_NUM = 255;
private: private:
uint8_t getCRC() const; uint8_t getCRC() const;
@ -60,59 +59,6 @@ struct ConfigService: public Task {
const char* loadedProfile() const; const char* loadedProfile() const;
void overrideProfile(const char* profileName); void overrideProfile(const char* profileName);
const char* getConfigValue(const char* key) const; const char* getConfigValue(const char* key) const;
const std::vector<Command>& commands() const override;
struct filename_iterator: public std::iterator<std::input_iterator_tag, const char*> {
Dir dir;
String ret;
bool valid;
const char* suffix;
explicit filename_iterator() : suffix(NULL), valid(false) {}
explicit filename_iterator(const char* path, const char* suffix) : dir(LittleFS.openDir(path)), valid(true), suffix(suffix) {
next();
}
void next() {
if (!valid) {
return;
}
int extPos = -1;
do {
valid = dir.next();
Log.info("valid %F", valid);
if (valid) {
String fname = dir.fileName();
extPos = fname.lastIndexOf(suffix);
Log.info("compare %s %d", fname.c_str(), extPos);
if (extPos != -1) {
ret = fname.substring(0, extPos);
Log.info("found %s", ret.c_str());
}
}
} while (valid && extPos == -1);
}
filename_iterator& operator++() {
next();
return *this;
}
filename_iterator& operator++(int) {filename_iterator ret = *this; ++(*this); return ret;}
bool operator==(const filename_iterator &other) const { return valid == other.valid;}
bool operator!=(const filename_iterator &other) const { return !(*this == other); }
const char* operator*() const {
if (!valid) {
return NULL;
}
return ret.c_str();
}
};
filename_iterator mapsBegin() const { return filename_iterator("/maps/", ".json"); }
filename_iterator mapsEnd() const { return filename_iterator(); }
filename_iterator profilesBegin() const { return filename_iterator("/profiles/", ".json"); }
filename_iterator profilesEnd() const { return filename_iterator(); }
private: private:
HardwareConfig m_config; HardwareConfig m_config;

View File

@ -16,21 +16,21 @@
#include <ctime> #include <ctime>
WiFiUDP wifiUdp; WiFiUDP wifiUdp;
NTPClient timeClient(wifiUdp, "pool.ntp.org", 3600 * -7); //NTPClient timeClient(wifiUdp, "pool.ntp.org", 3600 * -7);
NTPClient timeClient(wifiUdp, "10.0.0.1", 3600 * -7);
#endif #endif
#endif #endif
#ifdef PLATFORM_PHOTON #ifdef PLATFORM_PHOTON
STARTUP(BootOptions::initPins()); STARTUP(BootOptions::initPins());
#else #else
#include "inputs/Serial.h"
#ifdef CONFIG_MQTT #ifdef CONFIG_MQTT
#include "platform/arduino/MQTTTelemetry.h" #include "platform/arduino/MQTTTelemetry.h"
#endif #endif
void printNewline(Print* logOutput, int logLevel) void printNewline(Print* logOutput, int logLevel)
{ {
(void)logLevel; // unused (void)logLevel; // unused
logOutput->print("\n"); logOutput->print("\r\n");
} }
int printEspLog(const char* fmt, va_list args) int printEspLog(const char* fmt, va_list args)
{ {
@ -62,7 +62,7 @@ Platform::version()
{ {
#ifdef PLATFORM_PHOTON #ifdef PLATFORM_PHOTON
return System.version().c_str(); return System.version().c_str();
#elif defined(BOARD_ESP32) || defined(BOARD_ESP8266) #elif defined(BOARD_ESP32)
return ESP.getSdkVersion(); return ESP.getSdkVersion();
#else #else
return "Unknown!"; return "Unknown!";
@ -72,7 +72,10 @@ Platform::version()
int int
Platform::freeRam() Platform::freeRam()
{ {
#if defined(BOARD_ESP8266) || defined(BOARD_ESP32) #ifdef BOARD_ESP8266
return ESP.getFreeHeap();
#endif
#ifdef BOARD_ESP32
return ESP.getFreeHeap(); return ESP.getFreeHeap();
#endif #endif
} }
@ -81,6 +84,7 @@ void
Platform::preSetup() Platform::preSetup()
{ {
Serial.begin(115200); Serial.begin(115200);
delay(5000);
#ifdef PLATFORM_PHOTON #ifdef PLATFORM_PHOTON
System.enableFeature(FEATURE_RETAINED_MEMORY); System.enableFeature(FEATURE_RETAINED_MEMORY);
if (bootopts.isFlash) { if (bootopts.isFlash) {
@ -98,7 +102,7 @@ Platform::preSetup()
Log.begin(LOG_LEVEL_TRACE, Static<MQTTTelemetry>::instance()->logPrinter()); Log.begin(LOG_LEVEL_TRACE, Static<MQTTTelemetry>::instance()->logPrinter());
Static<MQTTTelemetry>::instance()->setSequencer(Static<Sequencer>::instance()); Static<MQTTTelemetry>::instance()->setSequencer(Static<Sequencer>::instance());
#else #else
Log.begin(LOG_LEVEL_TRACE, Static<SerialInput>::instance()->logPrinter()); Log.begin(LOG_LEVEL_TRACE, &Serial);
#endif #endif
Log.setSuffix(printNewline); Log.setSuffix(printNewline);
#endif #endif
@ -110,9 +114,6 @@ Platform::preSetup()
#endif #endif
#ifdef BOARD_ESP8266 #ifdef BOARD_ESP8266
ESP.wdtEnable(0); ESP.wdtEnable(0);
if (!ESP.checkFlashCRC()) {
Log.fatal("Firmware failed CRC check!!!");
}
#endif #endif
} }
@ -141,30 +142,6 @@ Platform::bootSplash()
Log.notice(u8" 4: Flash - %d", bootopts.isFlash); Log.notice(u8" 4: Flash - %d", bootopts.isFlash);
#endif #endif
if (bootopts.crashCount > 0) {
Log.warning(u8"Previous crash detected!!!! We're on attempt %d", bootopts.crashCount);
char lastTaskBuf[16];
strncpy(lastTaskBuf, MainLoop::lastTaskName(), sizeof(lastTaskBuf));
lastTaskBuf[15] = 0;
Log.error(u8"Crash occurred in task %s", lastTaskBuf);
#ifdef BOARD_ESP8266
auto rInfo = ESP.getResetInfoPtr();
if (Platform::bootopts.resetReason == REASON_EXCEPTION_RST) {
Log.error("Fatal exception (%d):", rInfo->exccause);
}
Log.error("epc1=%X, epc2=%X, epc3=%X, excvaddr=%X, depc=%X",
rInfo->epc1, rInfo->epc2, rInfo->epc3, rInfo->excvaddr, rInfo->depc);
#endif
strncpy(lastTaskBuf, Renderer::lastFigmentName(), sizeof(lastTaskBuf));
lastTaskBuf[15] = 0;
Log.error(u8"Last Figment was %s", lastTaskBuf);
}
Log.trace("Startup reason: %d", bootopts.resetReason);
Log.trace("Registered tasks:"); Log.trace("Registered tasks:");
auto it = beginTasks(); auto it = beginTasks();
while (it != endTasks()) { while (it != endTasks()) {
@ -234,8 +211,8 @@ Platform::deviceID()
} }
void void
Platform::addLEDs(CRGB* leds, uint16_t ledCount) { Platform::addLEDs(CRGB* leds, unsigned int ledCount) {
FastLED.addLeds<WS2812, RENDERBUG_LED_PIN, RENDERBUG_LED_PACKING>(leds, ledCount); FastLED.addLeds<WS2812B, RENDERBUG_LED_PIN, RENDERBUG_LED_PACKING>(leds, ledCount);
} }
const String const String
@ -255,78 +232,6 @@ Platform::restart() {
#endif #endif
} }
__attribute__((noreturn))
void
doReboot(Args& args, Print& out)
{
out.println("Rebooting");
Platform::restart();
}
__attribute__((noreturn))
void
doSafeMode(Args& args, Print& out)
{
out.println("Rebooting into safe mode");
Platform::bootopts.forceSafeMode();
Platform::restart();
}
String s;
void
doTaskStart(Args& args, Print& out)
{
s = args[1];
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, s.c_str()});
}
void
doTaskStop(Args& args, Print& out)
{
s = args[1];
MainLoop::instance()->dispatch(InputEvent{InputEvent::StopThing, s.c_str()});
}
void
doTaskList(Args& args, Print& out)
{
auto sched = MainLoop::instance()->scheduler;
auto printer = Static<SerialInput>::instance()->printer();
out.println("Tasks:");
for(auto task : sched.tasks) {
bool isFigment = task->isFigment();
if (task->state == Task::Running) {
out.print("+");
} else {
out.print("-");
}
if (isFigment) {
out.print("F ");
} else {
out.print("T ");
}
out.println(task->name);
}
}
const std::vector<Command> _commands = {
{"tasks", doTaskList},
{"safe-mode", doSafeMode},
{"reboot", doReboot},
{"stop", doTaskStop},
{"start", doTaskStart}
};
const std::vector<Command>&
Platform::commands() const
{
return _commands;
}
BootOptions BootOptions
Platform::bootopts; Platform::bootopts;

View File

@ -11,7 +11,7 @@ class Platform : public Task {
static BootOptions bootopts; static BootOptions bootopts;
static void setTimezone(int tz) { s_timezone = tz; } static void setTimezone(int tz) { s_timezone = tz; }
static int getTimezone() { return s_timezone; } static int getTimezone() { return s_timezone; }
static void addLEDs(CRGB* leds, uint16_t ledCount); static void addLEDs(CRGB* leds, unsigned int ledCount);
static const char* name(); static const char* name();
static const char* version(); static const char* version();
@ -105,6 +105,4 @@ class Platform : public Task {
} }
static void restart(); static void restart();
const std::vector<Command>& commands() const override;
}; };

View File

@ -3,7 +3,6 @@
#include "./Platform.h" #include "./Platform.h"
#include "./Static.h" #include "./Static.h"
#include "./Config.h" #include "./Config.h"
#include "./inputs/Serial.h"
TaskFunc safeModeNag([]{ TaskFunc safeModeNag([]{
static uint8_t frame = 0; static uint8_t frame = 0;
@ -42,7 +41,6 @@ SafeMode::safeModeApp{{
// System logging // System logging
Static<LogService>::instance(), Static<LogService>::instance(),
&safeModeNag, &safeModeNag,
Static<SerialInput>::instance(),
#ifdef CONFIG_WIFI #ifdef CONFIG_WIFI
// ESP Wifi // ESP Wifi
Static<WiFiTask>::instance(), Static<WiFiTask>::instance(),

View File

@ -94,34 +94,5 @@ Sequencer::handleEvent(const InputEvent& evt)
} }
} }
void
doScenes(Args& args, Print& out)
{
out.println("Available scenes: ");
for (auto scene : Static<Sequencer>::instance()->scenes()) {
out.println(scene.name);
}
}
static String s;
void
doScene(Args& args, Print& out)
{
s = args[1];
MainLoop::instance()->dispatch(InputEvent{InputEvent::SetPattern, s.c_str()});
}
const std::vector<Command> _commands = {
{"scene", doScene},
{"scenes", doScenes}
};
const std::vector<Command>&
Sequencer::commands() const
{
return _commands;
}
STATIC_ALLOC(Sequencer); STATIC_ALLOC(Sequencer);
STATIC_TASK(Sequencer); STATIC_TASK(Sequencer);

View File

@ -23,7 +23,6 @@ public:
const char* currentSceneName(); const char* currentSceneName();
const std::vector<Scene> scenes() const; const std::vector<Scene> scenes() const;
const std::vector<Command>& commands() const override;
private: private:
int m_idx; int m_idx;

View File

@ -2,54 +2,6 @@
#include "../Static.h" #include "../Static.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
void
doBrightness(Args& args, Print& out)
{
String nextVal = args[1];
uint8_t newBrightness = (uint8_t)atoi(nextVal.c_str());
MainLoop::instance()->dispatch(InputEvent{InputEvent::SetBrightness, newBrightness});
}
void
doOn(Args& args, Print& out)
{
MainLoop::instance()->dispatch(InputEvent{InputEvent::SetPower, 255});
}
void
doOff(Args& args, Print& out)
{
MainLoop::instance()->dispatch(InputEvent{InputEvent::SetPower, 0});
}
void
doForceBrightness(Args& args, Print& out)
{
String nextVal = args[1];
uint8_t newBrightness = (uint8_t)atoi(nextVal.c_str());
Static<Power>::instance()->forceBrightness(newBrightness);
}
void
Power::forceBrightness(uint8_t v)
{
m_forced = true;
FastLED.setBrightness(v);
}
const std::vector<Command> _commands = {
{"brightness", doBrightness},
{"brightness-force", doForceBrightness},
{"on", doOn},
{"off", doOff}
};
const std::vector<Command>&
Power::commands() const
{
return _commands;
}
void void
Power::handleConfigChange(const Configuration& config) Power::handleConfigChange(const Configuration& config)
{ {

View File

@ -10,17 +10,15 @@ public:
switch (evt.intent) { switch (evt.intent) {
case InputEvent::PowerToggle: case InputEvent::PowerToggle:
m_powerState = m_powerState.value() <= 128 ? 255 : 0; m_powerState = m_powerState.value() <= 128 ? 255 : 0;
m_forced = false; //Log.info("POWER TOGGLE %d", m_powerState.value());
Log.notice("Power toggled to %t", m_powerState);
break; break;
case InputEvent::SetPower: case InputEvent::SetPower:
m_powerState = evt.asInt() == 0 ? 0 : 255; m_powerState = evt.asInt() == 0 ? 0 : 255;
m_forced = false; Log.notice("Power is now %d", m_powerState);
Log.notice("Power state is now %t", m_powerState);
break; break;
case InputEvent::SetBrightness: case InputEvent::SetBrightness:
m_brightness = evt.asInt(); m_brightness = evt.asInt();
m_forced = false; m_brightness = 255;
break; break;
case InputEvent::Beat: case InputEvent::Beat:
m_beatDecay.set(0, 255); m_beatDecay.set(0, 255);
@ -42,7 +40,7 @@ public:
} }
void render(Display* dpy) const override { void render(Display* dpy) const override {
if (F_LIKELY(m_valid && !m_forced)) { if (F_LIKELY(m_valid)) {
const uint8_t decayedBrightness = scale8((uint8_t)m_brightness, m_useBPM ? ease8InOutCubic((uint8_t)m_beatDecay) : 255); const uint8_t decayedBrightness = scale8((uint8_t)m_brightness, m_useBPM ? ease8InOutCubic((uint8_t)m_beatDecay) : 255);
const uint8_t clippedBrightness = std::min(decayedBrightness, (uint8_t)255); const uint8_t clippedBrightness = std::min(decayedBrightness, (uint8_t)255);
const uint8_t scaledBrightness = scale8(m_powerState, clippedBrightness); const uint8_t scaledBrightness = scale8(m_powerState, clippedBrightness);
@ -52,9 +50,6 @@ public:
} }
} }
void forceBrightness(uint8_t v);
const std::vector<Command>& commands() const override;
private: private:
AnimatedNumber m_powerState = 255; AnimatedNumber m_powerState = 255;
@ -64,5 +59,4 @@ private:
uint16_t m_milliamps = 500; uint16_t m_milliamps = 500;
bool m_valid = true; bool m_valid = true;
bool m_useBPM = false; bool m_useBPM = false;
bool m_forced = false;
}; };

View File

@ -1,45 +0,0 @@
#include "Rain.h"
#include "../Static.h"
RainAnimation::RainAnimation() : Figment("Rain")
{
}
void
RainAnimation::render(Display* dpy) const
{
Surface sfc = Surface(dpy, {0, 0}, {255, 255});
uint8_t noiseY = sin8(m_noiseOffset % 255);
uint8_t noiseX = cos8(m_noiseOffset % 255);
sfc.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates, const VirtualCoordinates& surfaceCoords) {
pixel = CHSV(m_hue, inoise8(noiseX + coords.x, coords.y), inoise8(m_noiseOffset + coords.x, noiseY + coords.y));
});
m_drops.render(dpy);
}
void
RainAnimation::loop()
{
EVERY_N_MILLISECONDS(250) {
m_drops.update();
}
EVERY_N_MILLISECONDS(60) {
m_hue.update(1);
m_curColor.h = m_hue;
}
m_noiseOffset += 1;
}
void
RainAnimation::handleEvent(const InputEvent& evt) {
if (evt.intent == InputEvent::SetColor) {
CHSV next = rgb2hsv_approximate(evt.asRGB());
m_hue.set(next.h);
m_drops.forEach([=](Raindrop& drop) {
drop.nextColor = next;
});
}
}
STATIC_ALLOC(RainAnimation);
STATIC_TASK(RainAnimation);

View File

@ -1,48 +0,0 @@
#pragma once
#include <Figments.h>
class RainAnimation: public Figment {
private:
struct Raindrop {
int size = 20;
int x = random(255);
int y = random(255);
CHSV fg{180, 255, 255};
CHSV nextColor{180, 255, 255};
void render(Display* dpy) const {
Surface sfc{dpy, {x - size, y - size}, {x + size, y + size}};
paint(sfc);
}
void paint(Surface& sfc) const {
sfc.paintShader([=](CRGB& pixel, const VirtualCoordinates& coords, const PhysicalCoordinates, const VirtualCoordinates& surfaceCoords) {
int distance = 255 - (min(128, abs(128 - surfaceCoords.x)) + min(128, abs(128 - surfaceCoords.y)));
pixel += CHSV{fg.h, fg.s, scale8_video(fg.v, distance)};
});
}
void update() {
if (random(255) >= 100) {
y++;
if (y >= 255) {
y = 0;
x += 13;
x %= 255;
fg = nextColor;
}
}
}
};
SpriteList<Raindrop, 10> m_drops;
uint16_t m_noiseOffset;
CHSV m_curColor{180, 255, 255};
AnimatedNumber m_hue;
public:
RainAnimation();
//void handleEvent(const InputEvent& evt) override;
void loop() override;
void render(Display* dpy) const override;
void handleEvent(const InputEvent& evt) override;
};

View File

@ -1,32 +1,53 @@
#include "./TestAnimation.h" #include "./TestAnimation.h"
#include "../Static.h"
#include <FastLED.h> #include <FastLED.h>
const char*
TestAnimation::name() const
{
return "Test";
}
void
TestAnimation::handleEvent(const InputEvent& evt)
{
if (evt.intent == InputEvent::Acceleration) {
if (evt.asInt() > 5) {
m_brightness += 15;
}
m_hue += scale8(evt.asInt(), 128);
}
if (evt.intent == InputEvent::UserInput) {
switch(evt.asInt()) {
case 1:
m_brightness.set(255, 0);break;
case 2:
m_saturation.set(255, 128);break;
default:
m_brightness.set(255, 0);
m_saturation.set(255, 128);
}
}
}
void void
TestAnimation::loop() TestAnimation::loop()
{ {
m_x += 1; m_x += 4;
m_y += 1; if (m_x % 12 == 0) {
m_y += 28;
}
m_hue.update();
m_saturation.update();
m_brightness.update();
} }
void void
TestAnimation::render(Display* dpy) const TestAnimation::render(Display* dpy) const
{ {
for(unsigned int i = 0; i < dpy->pixelCount(); i++) { for(uint8_t col = 0; col < 3; col++) {
dpy->pixelAt(i) = CRGB{255, 255, 255}; for (uint8_t i = 0; i < 254; i+=10) {
} dpy->pixelAt(VirtualCoordinates{(uint8_t)(m_x + (col * (254 / 3))), (uint8_t)(i + m_y)}) = CHSV(m_hue, m_saturation + 100, scale8(i, m_brightness));
return; }
// Blank the canvas to white }
Surface{dpy, {0, 0}, {255, 255}} = CRGB{255, 255, 255};
// Draw red line on top row
Surface{dpy, {0, 0}, {255, 0}} = CRGB{255, 0, 0};
// Green line on first column
Surface{dpy, {0, 0}, {0, 255}} = CRGB{0, 255, 0};
//Surface{dpy, {m_x, 0}, {m_x, 255}} = CRGB{255, 0, 0};
///Surface{dpy, {0, m_y}, {255, m_y}} = CRGB{255, 0, 0};
//dpy->pixelAt(VirtualCoordinates{m_x, m_y}) = CRGB{255, 0, 255};
} }
STATIC_ALLOC(TestAnimation);
STATIC_TASK(TestAnimation);

View File

@ -2,11 +2,15 @@
class TestAnimation: public Figment { class TestAnimation: public Figment {
public: public:
TestAnimation() : Figment("Test") {} const char* name() const;
void handleEvent(const InputEvent& evt) override;
void loop() override; void loop() override;
void render(Display* dpy) const override; void render(Display* dpy) const override;
private: private:
AnimatedNumber m_hue;
AnimatedNumber m_saturation;
AnimatedNumber m_brightness;
uint8_t m_x; uint8_t m_x;
uint8_t m_y; uint8_t m_y;
}; };

View File

@ -8,7 +8,7 @@ UpdateStatus::handleEvent(const InputEvent& evt)
if (evt.intent == InputEvent::FirmwareUpdate) { if (evt.intent == InputEvent::FirmwareUpdate) {
static int updateCount = 0; static int updateCount = 0;
updateCount++; updateCount++;
Log.info("Update count %d", updateCount); //Log.info("Update count %d", updateCount);
m_updateReady = true; m_updateReady = true;
} }
} }
@ -22,12 +22,11 @@ UpdateStatus::loop()
void void
UpdateStatus::render(Display* dpy) const UpdateStatus::render(Display* dpy) const
{ {
int pos = m_pos % dpy->pixelCount();
if (m_updateReady) { if (m_updateReady) {
for(int i = 0; i < 12; i+=3) { for(int i = 0; i < 12; i+=3) {
dpy->pixelAt(pos + i) = CRGB(255, 0, 0); dpy->pixelAt(m_pos + i) = CRGB(255, 0, 0);
dpy->pixelAt(pos + i + 1) = CRGB(0, 255, 0); dpy->pixelAt(m_pos + i + 1) = CRGB(0, 255, 0);
dpy->pixelAt(pos + i + 2) = CRGB(0, 0, 255); dpy->pixelAt(m_pos + i + 2) = CRGB(0, 0, 255);
} }
} }
} }

View File

@ -10,5 +10,5 @@ public:
private: private:
bool m_updateReady = false; bool m_updateReady = false;
int m_pos = 0; uint8_t m_pos = 0;
}; };

View File

@ -1,22 +1,5 @@
#include "./BPM.h" #include "./BPM.h"
#include "../Static.h" #include "../Static.h"
void
doBPM(Args& args, Print& out)
{
uint8_t newBPM(atoi(args[1].c_str()));
Static<BPM>::instance()->setBPM(newBPM);
}
const std::vector<Command> _commands = {
{"bpm", doBPM}
};
const std::vector<Command>&
BPM::commands() const
{
return _commands;
}
STATIC_ALLOC(BPM); STATIC_ALLOC(BPM);
STATIC_TASK(BPM); STATIC_TASK(BPM);

View File

@ -18,8 +18,6 @@ public:
ConfigTaskMixin::handleEvent(evt); ConfigTaskMixin::handleEvent(evt);
} }
const std::vector<Command>& commands() const override;
void loop() { void loop() {
InputSource::loop(); InputSource::loop();
ConfigTaskMixin::loop(); ConfigTaskMixin::loop();
@ -31,11 +29,6 @@ public:
Log.notice("bpm: idle BPM set to %d (requested %d)", (int)msToBPM(m_msPerBeat), (int)requestedBPM); Log.notice("bpm: idle BPM set to %d (requested %d)", (int)msToBPM(m_msPerBeat), (int)requestedBPM);
} }
void setBPM(double bpm) {
m_msPerBeat = 60000.0 / (double)bpm;
Log.notice("bpm: Command changed to %d (requested %d)", (int)msToBPM(m_msPerBeat), (int)bpm);
}
InputEvent read() override { InputEvent read() override {
if (m_msPerBeat > 0) { if (m_msPerBeat > 0) {
uint16_t now = millis(); uint16_t now = millis();

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include <Figments.h> #include <Figments.h>
#include "../Platform.h"
struct ScheduleEntry { struct ScheduleEntry {
uint8_t hour; uint8_t hour;
@ -84,9 +83,7 @@ class CircadianRhythm : public InputSource {
minute = 0; minute = 0;
} }
Log.notice("Current time: %d:%d", hour, minute); Log.notice("Current time: %d:%d", hour, minute);
auto brightness = brightnessForTime(hour, minute); return InputEvent{InputEvent::SetBrightness, brightnessForTime(hour, minute)};
Log.notice("Adjusting brightness to %d", brightness);
return InputEvent{InputEvent::SetBrightness, brightness};
} }
return InputEvent{}; return InputEvent{};
} }

View File

@ -1,171 +1,33 @@
#include "Serial.h" #include "Serial.h"
#include "../Static.h"
#include <LittleFS.h>
#include "../Config.h"
#include "../Sequencer.h"
SerialInput::SerialInput() : InputSource("Serial"),
m_state(ParseState::Normal),
m_logPrinter(this)
{
}
void
SerialInput::redrawPrompt()
{
if (m_canRedraw) {
Serial.print((char)8);
Serial.print((char)27);
Serial.print("[2K");
Serial.print((char)8);
Serial.print((char)27);
Serial.print("[G");
Serial.print('\r');
Serial.print("> ");
Serial.print(m_buf);
}
}
InputEvent InputEvent
SerialInput::parseNormal(char nextChar) Serial::read()
{
if (nextChar == 27) {
m_state = ParseState::EscapeSequence;
return InputEvent::None;
}
if (nextChar == 13) {
redrawPrompt();
Serial.println();
if (m_buf.length() > 0) {
m_canRedraw = false;
doCommand();
m_canRedraw = true;
m_history.insert(m_buf);
m_buf = "";
}
m_historyOffset = 0;
redrawPrompt();
return InputEvent{};
}
if (nextChar == 8) {
if (m_buf.length() > 0) {
m_buf.remove(m_buf.length() - 1, 1);
}
Serial.print((char)8);
Serial.print((char)27);
Serial.print("[K");
redrawPrompt();
return InputEvent::None;
}
if (nextChar >= 32 && nextChar <= 126) {
m_buf += nextChar;
Serial.print(nextChar);
}
return InputEvent::None;
}
InputEvent
SerialInput::parseEscape(char nextChar)
{
if (nextChar == '[') {
m_state = ParseState::CSI;
} else {
m_state = ParseState::Normal;
}
return InputEvent::None;
}
InputEvent
SerialInput::parseCSI(char nextChar)
{
if (nextChar == 'A') {
if (m_historyOffset < m_history.size()) {
m_historyOffset += 1;
m_buf = m_history.peek(m_historyOffset);
redrawPrompt();
} else {
Serial.print((char)7);
}
} else if (nextChar == 'B') {
if (m_historyOffset > 0) {
m_historyOffset -= 1;
m_buf = m_history.peek(m_historyOffset);
redrawPrompt();
} else {
Serial.print((char)7);
}
} else {
Serial.print((char)7);
}
m_state = ParseState::Normal;
return InputEvent::None;
}
InputEvent
SerialInput::read()
{ {
while (Serial.available() > 0) { while (Serial.available() > 0) {
char nextChar = Serial.read(); char nextChar = Serial.read();
InputEvent ret = InputEvent::None; if (nextChar == '\n') {
switch (m_state) { doCommand();
case ParseState::Normal: m_buf = "";
ret = parseNormal(nextChar);break; } else {
case ParseState::EscapeSequence: m_buf += nextChar;
ret = parseEscape(nextChar);break;
case ParseState::CSI:
ret = parseCSI(nextChar);break;
}
if (ret != InputEvent::None) {
return ret;
} }
} }
return InputEvent::None;
} }
void void
doHelp(Args& args, Print& out) Serial::doCommand() {
{ if (command == "tasks") {
out.println("Available commands:"); Serial.println("Tasks:");
auto sched = MainLoop::instance()->scheduler; auto sched = MainLoop::instance()->scheduler;
for(auto task : sched.tasks) { for(auto task : sched.tasks) {
for(auto &command : task->commands()) { bool isFigment = task->isFigment();
out.print(command.name); if (isFigment) {
out.print(" "); Serial.println("F " + task->name);
} else {
Serial.println("T " + task->name);
}
} }
} }
out.println();
}
const std::vector<Command> serialCommands = {
{"help", doHelp}
};
const std::vector<Command>&
SerialInput::commands() const
{
return serialCommands;
}
void
SerialInput::doCommand() {
auto sched = MainLoop::instance()->scheduler;
Args args = Args(&m_buf);
const auto cmdName = args[0];
for(auto task : sched.tasks) {
for(auto &command : task->commands()) {
if (cmdName == command.name) {
command.func(args, m_logPrinter);
return;
}
}
}
m_logPrinter.println("Unknown command");
doHelp(args, m_logPrinter);
} }
STATIC_ALLOC(SerialInput); STATIC_ALLOC(SerialInput);

View File

@ -3,60 +3,14 @@
class SerialInput: public InputSource { class SerialInput: public InputSource {
public: public:
SerialInput(); void onStart() override {
InputEvent read() override; //Serial.begin();
class LogPrinter : public Print {
private:
SerialInput* serial;
Ringbuf<char, 512> buf;
public:
LogPrinter(SerialInput* serial) : serial(serial) {};
size_t write(uint8_t byte) {
if (byte == '\n') {
char c;
Serial.print('\r');
while (buf.take(c)) {
Serial.write(c);
}
Serial.println();
serial->redrawPrompt();
} else {
buf.insert(byte);
}
return sizeof(byte);
}
};
void redrawPrompt();
Print* logPrinter() {
return &m_logPrinter;
} }
//static SerialInput::Command *s_root; InputEvent read();
LogPrinter* printer() {
return &m_logPrinter;
}
const std::vector<Command> &commands() const override;
private: private:
enum ParseState {
Normal,
EscapeSequence,
CSI
};
String m_buf; String m_buf;
ParseState m_state;
char m_escapeSeq[3];
void doCommand(); void doCommand();
LogPrinter m_logPrinter;
bool m_canRedraw = true;
Ringbuf<String, 5> m_history;
int m_historyOffset = 0;
InputEvent parseNormal(char nextChar); }
InputEvent parseEscape(char nextChar);
InputEvent parseCSI(char nextChar);
};

View File

@ -87,6 +87,16 @@ void setup() {
Log.notice(u8"🐞 I am built for %d LEDs on pin %d", HardwareConfig::MAX_LED_NUM, RENDERBUG_LED_PIN); Log.notice(u8"🐞 I am built for %d LEDs on pin %d", HardwareConfig::MAX_LED_NUM, RENDERBUG_LED_PIN);
Log.notice(u8"📡 Platform %s version %s", Platform::name(), Platform::version()); Log.notice(u8"📡 Platform %s version %s", Platform::name(), Platform::version());
if (Platform::bootopts.crashCount > 0) {
Log.warning(u8"Previous crash detected!!!! We're on attempt %d", Platform::bootopts.crashCount);
char lastTaskBuf[16];
strncpy(lastTaskBuf, MainLoop::lastTaskName(), sizeof(lastTaskBuf));
lastTaskBuf[15] = 0;
Log.error(u8"Crash occurred in task %s", lastTaskBuf);
}
Log.trace("Startup reason: %d", Platform::bootopts.resetReason);
Log.notice(u8"Setting timezone to +2 (CEST)"); Log.notice(u8"Setting timezone to +2 (CEST)");
Platform::setTimezone(+2); Platform::setTimezone(+2);
@ -95,17 +105,14 @@ void setup() {
Platform::setup(); Platform::setup();
Platform::bootSplash(); Platform::bootSplash();
Log.notice(u8"💡 Starting FastLED on %d LEDs...", HardwareConfig::MAX_LED_NUM); Log.notice(u8"💡 Starting FastLED...");
Platform::addLEDs(leds, HardwareConfig::MAX_LED_NUM); Platform::addLEDs(leds, HardwareConfig::MAX_LED_NUM);
// Tune in, // Tune in,
if (Platform::bootopts.isSafeMode) { if (Platform::bootopts.isSafeMode) {
Log.warning(u8"⚠️ Starting Figment in safe mode!!!"); Log.error(u8"⚠️ Starting Figment in safe mode!!!");
runner = &SafeMode::safeModeApp; runner = &SafeMode::safeModeApp;
for(auto task : runner->scheduler.tasks) { FastLED.showColor(CRGB(5, 0, 0));
task->state = Task::Running;
}
FastLED.showColor(CRGB(255, 0, 0));
FastLED.show(); FastLED.show();
} else { } else {
Log.notice(u8"🌌 Starting Figment..."); Log.notice(u8"🌌 Starting Figment...");
@ -124,7 +131,7 @@ void setup() {
Serial.flush(); Serial.flush();
runner->start(); runner->start();
Log.notice(u8"💽 %l bytes of free RAM", Platform::freeRam()); Log.notice(u8"💽 %lu bytes of free RAM", Platform::freeRam());
Log.notice(u8"🚀 Setup complete! Ready to rock and roll."); Log.notice(u8"🚀 Setup complete! Ready to rock and roll.");
Serial.flush(); Serial.flush();
} }

View File

@ -118,8 +118,6 @@ MQTTTelemetry::handleEventOnline(const InputEvent& evt)
Log.notice("Connecting to MQTT as %s on %s...", Platform::deviceID(), Device.availabilityTopic.c_str()); Log.notice("Connecting to MQTT as %s on %s...", Platform::deviceID(), Device.availabilityTopic.c_str());
if (m_mqtt.connect(Platform::deviceID(), NULL, NULL, Device.availabilityTopic.c_str(), 0, true, "offline")) { if (m_mqtt.connect(Platform::deviceID(), NULL, NULL, Device.availabilityTopic.c_str(), 0, true, "offline")) {
Log.notice("Connected to MQTT"); Log.notice("Connected to MQTT");
String logTopic = m_debugTopic + "/log";
Log.info("MQTT logs are available at %s", logTopic.c_str());
m_needHeartbeat = true; m_needHeartbeat = true;
m_json.clear(); m_json.clear();