Compare commits
No commits in common. "9a3186edbd96d4f4514126ad34f59fcd7e9545a1" and "74c2ddb405e645046d8dbbd5ae4a70b74f24ac4d" have entirely different histories.
9a3186edbd
...
74c2ddb405
@ -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}
|
||||
]
|
||||
}
|
@ -4,8 +4,7 @@
|
||||
"Renderer",
|
||||
"SerialInput",
|
||||
"BPM",
|
||||
"Bluetooth",
|
||||
"Serial"
|
||||
"Bluetooth"
|
||||
],
|
||||
"scenes": {
|
||||
"Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"],
|
||||
|
@ -7,8 +7,7 @@
|
||||
"WiFi",
|
||||
"MQTT",
|
||||
"ArduinoOTA",
|
||||
"BPM",
|
||||
"Serial"
|
||||
"BPM"
|
||||
],
|
||||
"scenes": {
|
||||
"Idle": ["Solid", "MPU5060", "IdleColors", "CircadianRhythm"],
|
||||
|
@ -6,8 +6,7 @@
|
||||
"WiFi",
|
||||
"MQTT",
|
||||
"ArduinoOTA",
|
||||
"UpdateStatusAnimation",
|
||||
"Serial"
|
||||
"UpdateStatusAnimation"
|
||||
],
|
||||
"scenes": {
|
||||
"Idle": ["Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"],
|
||||
|
@ -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"
|
||||
}
|
||||
}
|
@ -4,8 +4,7 @@
|
||||
"Renderer",
|
||||
"ConfigInput",
|
||||
"ConfigDisplay",
|
||||
"InputBlip",
|
||||
"Serial"
|
||||
"InputBlip"
|
||||
],
|
||||
"scenes": [],
|
||||
"surfaceMap": "default"
|
||||
|
@ -1,7 +0,0 @@
|
||||
#include "./Command.h"
|
||||
|
||||
void
|
||||
doNothing(Args& args, Print& printer)
|
||||
{}
|
||||
|
||||
Command::Command() : func(doNothing) {}
|
@ -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) {}
|
||||
};
|
@ -43,7 +43,6 @@ CRGB&
|
||||
Display::pixelAt(int idx)
|
||||
{
|
||||
const int kx = idx % pixelCount();
|
||||
assert(abs(kx) < pixelCount());
|
||||
if (kx < 0) {
|
||||
return m_pixels[pixelCount() + 1 + kx];
|
||||
} else {
|
||||
|
@ -1,9 +0,0 @@
|
||||
#include "./Figment.h"
|
||||
|
||||
const std::vector<Command> emptyCommands;
|
||||
|
||||
const std::vector<Command>&
|
||||
Task::commands() const
|
||||
{
|
||||
return emptyCommands;
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
#include <ArduinoLog.h>
|
||||
#include "./Command.h"
|
||||
|
||||
#define F_LIKELY(x) __builtin_expect(!!(x), true)
|
||||
#define F_UNLIKELY(x) __builtin_expect(!!(x), false)
|
||||
@ -69,8 +68,6 @@ struct Task : public virtual Loopable {
|
||||
|
||||
const char* name = "";
|
||||
State state = Stopped;
|
||||
|
||||
virtual const std::vector<Command> &commands() const;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -114,10 +114,6 @@ struct InputEvent: public Variant {
|
||||
InputEvent()
|
||||
: Variant(), intent(None) {}
|
||||
|
||||
bool operator!=(const InputEvent::Intent& otherIntent) {
|
||||
return intent != otherIntent;
|
||||
}
|
||||
|
||||
Intent intent;
|
||||
};
|
||||
|
||||
|
@ -24,23 +24,17 @@ MainLoop::dispatchSync(const InputEvent& evt)
|
||||
{
|
||||
if (evt.intent == InputEvent::StartThing || evt.intent == InputEvent::StopThing) {
|
||||
const bool jobState = (evt.intent == InputEvent::StartThing);
|
||||
bool wasFound = false;
|
||||
for(auto figmentJob: scheduler.tasks) {
|
||||
if (!strcmp(figmentJob->name, evt.asString())) {
|
||||
if (jobState) {
|
||||
Log.trace("** Starting %s", figmentJob->name);
|
||||
figmentJob->start();
|
||||
wasFound = true;
|
||||
} else {
|
||||
Log.trace("** Stopping %s", figmentJob->name);
|
||||
figmentJob->stop();
|
||||
wasFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!wasFound) {
|
||||
Log.warning("** Unable to find task %s", evt.asString());
|
||||
}
|
||||
}
|
||||
|
||||
for(Task* task : scheduler) {
|
||||
@ -92,8 +86,7 @@ MainLoop::loop()
|
||||
}
|
||||
}
|
||||
frameSpeed = millis() - frameStart;
|
||||
if (frameSpeed >= 34) { // TODO: Configure max frame time at build. Default
|
||||
// to 30FPS
|
||||
if (frameSpeed >= 23) { // TODO: Configure max frame time at build
|
||||
const char* slowestName = (slowestTask->name ? slowestTask->name : "(Unnamed)");
|
||||
Log.warning("Slow frame: %dms, %d tasks, longest task %s was %dms", frameSpeed, taskCount, slowestName, slowest);
|
||||
}
|
||||
|
@ -3,18 +3,6 @@
|
||||
|
||||
#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
|
||||
Renderer::loop()
|
||||
{
|
||||
@ -25,7 +13,6 @@ Renderer::loop()
|
||||
unsigned int frameStart = ESP.getCycleCount();
|
||||
#endif
|
||||
Log.verbose("Render %s", figment->name);
|
||||
s_lastFigmentName = figment->name;
|
||||
figment->render(dpy);
|
||||
#if defined(BOARD_ESP32) or defined(BOARD_ESP8266)
|
||||
unsigned int runtime = (ESP.getCycleCount() - frameStart) / 160000;
|
||||
|
@ -10,8 +10,6 @@ public:
|
||||
void loop() override;
|
||||
void onStart() override;
|
||||
|
||||
static const char* lastFigmentName();
|
||||
|
||||
private:
|
||||
const std::vector<Figment*> m_figments;
|
||||
const std::vector<Display*> m_displays;
|
||||
|
@ -86,8 +86,6 @@ struct Ringbuf {
|
||||
size_t size() {
|
||||
if (m_tail > m_head) {
|
||||
return m_tail - m_head;
|
||||
} else if (m_tail == m_head) {
|
||||
return 0;
|
||||
}
|
||||
return m_tail + (Size - m_head);
|
||||
}
|
||||
|
@ -54,20 +54,17 @@ void
|
||||
Surface::paintShader(Surface::Shader shader)
|
||||
{
|
||||
PerfCounter _("paintShader");
|
||||
uint8_t startX = min(start.x, end.x);
|
||||
uint8_t startY = min(start.y, end.y);
|
||||
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 uint16_t width = end.x - start.x + 1;
|
||||
const uint16_t height = end.y - start.y + 1;
|
||||
const uint8_t xMod = 255 / width;
|
||||
const uint8_t yMod = 255 / height;
|
||||
for(auto x = 0; x < width; x++) {
|
||||
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 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
|
||||
// 0 = 1.0
|
||||
shader(m_display->pixelAt(coords), virtCoords, coords, surfaceCoords);
|
||||
|
@ -9,7 +9,7 @@
|
||||
; https://docs.platformio.org/page/projectconf.html
|
||||
|
||||
[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+
|
||||
extra_scripts = verify-configs.py
|
||||
src_build_flags =
|
||||
|
@ -14,21 +14,10 @@ retained bool LAST_BOOT_WAS_FLASH;
|
||||
retained bool LAST_BOOT_WAS_SERIAL;
|
||||
#endif
|
||||
|
||||
#ifndef __NOINIT_ATTR // Pre-defined on esp32
|
||||
#define __NOINIT_ATTR __attribute__ ((section (".noinit")))
|
||||
#ifdef BOARD_ESP32
|
||||
__NOINIT_ATTR uint8_t s_rebootCount = 0;
|
||||
#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
|
||||
BootOptions::initPins()
|
||||
{
|
||||
@ -71,20 +60,23 @@ BootOptions::BootOptions()
|
||||
#ifdef BOARD_ESP8266
|
||||
struct rst_info resetInfo = *ESP.getResetInfoPtr();
|
||||
resetReason = resetInfo.reason;
|
||||
crashCount = s_rebootCount;
|
||||
if (resetInfo.reason == REASON_SOFT_WDT_RST || resetInfo.reason == REASON_WDT_RST || resetInfo.reason == REASON_EXCEPTION_RST) {
|
||||
EEPROM.begin(sizeof(crashCount));
|
||||
EEPROM.get(sizeof(HardwareConfig) + 32, crashCount);
|
||||
EEPROM.end();
|
||||
if (resetInfo.reason == REASON_WDT_RST || resetInfo.reason == REASON_EXCEPTION_RST) {
|
||||
if (crashCount++ >= 3) {
|
||||
// Boot into safe mode if the watchdog reset us three times in a row.
|
||||
isSafeMode = true;
|
||||
}
|
||||
} else {
|
||||
crashCount = 0;
|
||||
EEPROM.begin(sizeof(crashCount));
|
||||
EEPROM.put(sizeof(HardwareConfig) + 32, crashCount);
|
||||
EEPROM.end();
|
||||
}
|
||||
s_rebootCount = crashCount;
|
||||
|
||||
if (resetInfo.reason > 0 && s_forceSafeMode == SAFE_MODE_MAGIC) {
|
||||
isSafeMode = true;
|
||||
s_forceSafeMode = 0;
|
||||
} else if (crashCount != 0) {
|
||||
crashCount = 0;
|
||||
EEPROM.begin(sizeof(crashCount));
|
||||
EEPROM.put(sizeof(HardwareConfig) + 32, crashCount);
|
||||
EEPROM.end();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ struct BootOptions {
|
||||
BootOptions();
|
||||
|
||||
void waitForRelease();
|
||||
void forceSafeMode();
|
||||
|
||||
bool isSetup = false;
|
||||
bool isSerial = false;
|
||||
|
@ -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;
|
||||
|
||||
@ -154,11 +154,6 @@ ConfigService::loadMap(const String& mapName)
|
||||
deserializeJson(jsonConfig, configFile);
|
||||
configFile.close();
|
||||
JsonArray strideList = jsonConfig["strides"];
|
||||
if (jsonConfig.containsKey("rotation")) {
|
||||
m_jsonMap.rotation = jsonConfig["rotation"];
|
||||
} else {
|
||||
m_jsonMap.rotation = 0;
|
||||
}
|
||||
m_jsonMap.load(strideList);
|
||||
return true;
|
||||
} else {
|
||||
@ -218,9 +213,8 @@ ConfigService::loadProfile(const char* profileName)
|
||||
m_jsonMap.loadDefault();
|
||||
}
|
||||
Log.notice("config: Loaded!");
|
||||
strcpy(m_config.data.loadedProfile, profileName);
|
||||
} else {
|
||||
Log.warning("config: Could not find profile json %s!", fname.c_str());
|
||||
Log.warning("config: Could not load profile %s!", profileName);
|
||||
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_TASK(ConfigService);
|
||||
|
56
src/Config.h
56
src/Config.h
@ -2,7 +2,6 @@
|
||||
#include <Figments.h>
|
||||
#include "JsonCoordinateMapping.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <LittleFS.h>
|
||||
|
||||
class Configuration {
|
||||
public:
|
||||
@ -40,7 +39,7 @@ struct HardwareConfig {
|
||||
void save();
|
||||
bool isValid() const;
|
||||
|
||||
static constexpr uint16_t MAX_LED_NUM = 512;
|
||||
static constexpr uint16_t MAX_LED_NUM = 255;
|
||||
|
||||
private:
|
||||
uint8_t getCRC() const;
|
||||
@ -60,59 +59,6 @@ struct ConfigService: public Task {
|
||||
const char* loadedProfile() const;
|
||||
void overrideProfile(const char* profileName);
|
||||
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:
|
||||
HardwareConfig m_config;
|
||||
|
119
src/Platform.cpp
119
src/Platform.cpp
@ -16,21 +16,21 @@
|
||||
#include <ctime>
|
||||
|
||||
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
|
||||
|
||||
#ifdef PLATFORM_PHOTON
|
||||
STARTUP(BootOptions::initPins());
|
||||
#else
|
||||
#include "inputs/Serial.h"
|
||||
#ifdef CONFIG_MQTT
|
||||
#include "platform/arduino/MQTTTelemetry.h"
|
||||
#endif
|
||||
void printNewline(Print* logOutput, int logLevel)
|
||||
{
|
||||
(void)logLevel; // unused
|
||||
logOutput->print("\n");
|
||||
logOutput->print("\r\n");
|
||||
}
|
||||
int printEspLog(const char* fmt, va_list args)
|
||||
{
|
||||
@ -62,7 +62,7 @@ Platform::version()
|
||||
{
|
||||
#ifdef PLATFORM_PHOTON
|
||||
return System.version().c_str();
|
||||
#elif defined(BOARD_ESP32) || defined(BOARD_ESP8266)
|
||||
#elif defined(BOARD_ESP32)
|
||||
return ESP.getSdkVersion();
|
||||
#else
|
||||
return "Unknown!";
|
||||
@ -72,7 +72,10 @@ Platform::version()
|
||||
int
|
||||
Platform::freeRam()
|
||||
{
|
||||
#if defined(BOARD_ESP8266) || defined(BOARD_ESP32)
|
||||
#ifdef BOARD_ESP8266
|
||||
return ESP.getFreeHeap();
|
||||
#endif
|
||||
#ifdef BOARD_ESP32
|
||||
return ESP.getFreeHeap();
|
||||
#endif
|
||||
}
|
||||
@ -81,6 +84,7 @@ void
|
||||
Platform::preSetup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
delay(5000);
|
||||
#ifdef PLATFORM_PHOTON
|
||||
System.enableFeature(FEATURE_RETAINED_MEMORY);
|
||||
if (bootopts.isFlash) {
|
||||
@ -98,7 +102,7 @@ Platform::preSetup()
|
||||
Log.begin(LOG_LEVEL_TRACE, Static<MQTTTelemetry>::instance()->logPrinter());
|
||||
Static<MQTTTelemetry>::instance()->setSequencer(Static<Sequencer>::instance());
|
||||
#else
|
||||
Log.begin(LOG_LEVEL_TRACE, Static<SerialInput>::instance()->logPrinter());
|
||||
Log.begin(LOG_LEVEL_TRACE, &Serial);
|
||||
#endif
|
||||
Log.setSuffix(printNewline);
|
||||
#endif
|
||||
@ -110,9 +114,6 @@ Platform::preSetup()
|
||||
#endif
|
||||
#ifdef BOARD_ESP8266
|
||||
ESP.wdtEnable(0);
|
||||
if (!ESP.checkFlashCRC()) {
|
||||
Log.fatal("Firmware failed CRC check!!!");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -141,30 +142,6 @@ Platform::bootSplash()
|
||||
Log.notice(u8" 4: Flash - %d", bootopts.isFlash);
|
||||
#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:");
|
||||
auto it = beginTasks();
|
||||
while (it != endTasks()) {
|
||||
@ -234,8 +211,8 @@ Platform::deviceID()
|
||||
}
|
||||
|
||||
void
|
||||
Platform::addLEDs(CRGB* leds, uint16_t ledCount) {
|
||||
FastLED.addLeds<WS2812, RENDERBUG_LED_PIN, RENDERBUG_LED_PACKING>(leds, ledCount);
|
||||
Platform::addLEDs(CRGB* leds, unsigned int ledCount) {
|
||||
FastLED.addLeds<WS2812B, RENDERBUG_LED_PIN, RENDERBUG_LED_PACKING>(leds, ledCount);
|
||||
}
|
||||
|
||||
const String
|
||||
@ -255,78 +232,6 @@ Platform::restart() {
|
||||
#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
|
||||
Platform::bootopts;
|
||||
|
||||
|
@ -11,7 +11,7 @@ class Platform : public Task {
|
||||
static BootOptions bootopts;
|
||||
static void setTimezone(int tz) { s_timezone = tz; }
|
||||
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* version();
|
||||
@ -105,6 +105,4 @@ class Platform : public Task {
|
||||
}
|
||||
|
||||
static void restart();
|
||||
|
||||
const std::vector<Command>& commands() const override;
|
||||
};
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include "./Platform.h"
|
||||
#include "./Static.h"
|
||||
#include "./Config.h"
|
||||
#include "./inputs/Serial.h"
|
||||
|
||||
TaskFunc safeModeNag([]{
|
||||
static uint8_t frame = 0;
|
||||
@ -42,7 +41,6 @@ SafeMode::safeModeApp{{
|
||||
// System logging
|
||||
Static<LogService>::instance(),
|
||||
&safeModeNag,
|
||||
Static<SerialInput>::instance(),
|
||||
#ifdef CONFIG_WIFI
|
||||
// ESP Wifi
|
||||
Static<WiFiTask>::instance(),
|
||||
|
@ -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_TASK(Sequencer);
|
||||
|
@ -23,7 +23,6 @@ public:
|
||||
|
||||
const char* currentSceneName();
|
||||
const std::vector<Scene> scenes() const;
|
||||
const std::vector<Command>& commands() const override;
|
||||
|
||||
private:
|
||||
int m_idx;
|
||||
|
@ -2,54 +2,6 @@
|
||||
#include "../Static.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
|
||||
Power::handleConfigChange(const Configuration& config)
|
||||
{
|
||||
|
@ -10,17 +10,15 @@ public:
|
||||
switch (evt.intent) {
|
||||
case InputEvent::PowerToggle:
|
||||
m_powerState = m_powerState.value() <= 128 ? 255 : 0;
|
||||
m_forced = false;
|
||||
Log.notice("Power toggled to %t", m_powerState);
|
||||
//Log.info("POWER TOGGLE %d", m_powerState.value());
|
||||
break;
|
||||
case InputEvent::SetPower:
|
||||
m_powerState = evt.asInt() == 0 ? 0 : 255;
|
||||
m_forced = false;
|
||||
Log.notice("Power state is now %t", m_powerState);
|
||||
Log.notice("Power is now %d", m_powerState);
|
||||
break;
|
||||
case InputEvent::SetBrightness:
|
||||
m_brightness = evt.asInt();
|
||||
m_forced = false;
|
||||
m_brightness = 255;
|
||||
break;
|
||||
case InputEvent::Beat:
|
||||
m_beatDecay.set(0, 255);
|
||||
@ -42,7 +40,7 @@ public:
|
||||
}
|
||||
|
||||
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 clippedBrightness = std::min(decayedBrightness, (uint8_t)255);
|
||||
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:
|
||||
AnimatedNumber m_powerState = 255;
|
||||
@ -64,5 +59,4 @@ private:
|
||||
uint16_t m_milliamps = 500;
|
||||
bool m_valid = true;
|
||||
bool m_useBPM = false;
|
||||
bool m_forced = false;
|
||||
};
|
||||
|
@ -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);
|
@ -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;
|
||||
};
|
@ -1,32 +1,53 @@
|
||||
#include "./TestAnimation.h"
|
||||
#include "../Static.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
|
||||
TestAnimation::loop()
|
||||
{
|
||||
m_x += 1;
|
||||
m_y += 1;
|
||||
m_x += 4;
|
||||
if (m_x % 12 == 0) {
|
||||
m_y += 28;
|
||||
}
|
||||
m_hue.update();
|
||||
m_saturation.update();
|
||||
m_brightness.update();
|
||||
}
|
||||
|
||||
void
|
||||
TestAnimation::render(Display* dpy) const
|
||||
{
|
||||
for(unsigned int i = 0; i < dpy->pixelCount(); i++) {
|
||||
dpy->pixelAt(i) = CRGB{255, 255, 255};
|
||||
for(uint8_t col = 0; col < 3; col++) {
|
||||
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);
|
||||
|
@ -2,11 +2,15 @@
|
||||
|
||||
class TestAnimation: public Figment {
|
||||
public:
|
||||
TestAnimation() : Figment("Test") {}
|
||||
const char* name() const;
|
||||
void handleEvent(const InputEvent& evt) override;
|
||||
void loop() override;
|
||||
void render(Display* dpy) const override;
|
||||
|
||||
private:
|
||||
AnimatedNumber m_hue;
|
||||
AnimatedNumber m_saturation;
|
||||
AnimatedNumber m_brightness;
|
||||
uint8_t m_x;
|
||||
uint8_t m_y;
|
||||
};
|
||||
|
@ -8,7 +8,7 @@ UpdateStatus::handleEvent(const InputEvent& evt)
|
||||
if (evt.intent == InputEvent::FirmwareUpdate) {
|
||||
static int updateCount = 0;
|
||||
updateCount++;
|
||||
Log.info("Update count %d", updateCount);
|
||||
//Log.info("Update count %d", updateCount);
|
||||
m_updateReady = true;
|
||||
}
|
||||
}
|
||||
@ -22,12 +22,11 @@ UpdateStatus::loop()
|
||||
void
|
||||
UpdateStatus::render(Display* dpy) const
|
||||
{
|
||||
int pos = m_pos % dpy->pixelCount();
|
||||
if (m_updateReady) {
|
||||
for(int i = 0; i < 12; i+=3) {
|
||||
dpy->pixelAt(pos + i) = CRGB(255, 0, 0);
|
||||
dpy->pixelAt(pos + i + 1) = CRGB(0, 255, 0);
|
||||
dpy->pixelAt(pos + i + 2) = CRGB(0, 0, 255);
|
||||
dpy->pixelAt(m_pos + i) = CRGB(255, 0, 0);
|
||||
dpy->pixelAt(m_pos + i + 1) = CRGB(0, 255, 0);
|
||||
dpy->pixelAt(m_pos + i + 2) = CRGB(0, 0, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,5 +10,5 @@ public:
|
||||
|
||||
private:
|
||||
bool m_updateReady = false;
|
||||
int m_pos = 0;
|
||||
uint8_t m_pos = 0;
|
||||
};
|
||||
|
@ -1,22 +1,5 @@
|
||||
#include "./BPM.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_TASK(BPM);
|
||||
|
@ -18,8 +18,6 @@ public:
|
||||
ConfigTaskMixin::handleEvent(evt);
|
||||
}
|
||||
|
||||
const std::vector<Command>& commands() const override;
|
||||
|
||||
void loop() {
|
||||
InputSource::loop();
|
||||
ConfigTaskMixin::loop();
|
||||
@ -31,11 +29,6 @@ public:
|
||||
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 {
|
||||
if (m_msPerBeat > 0) {
|
||||
uint16_t now = millis();
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include <Figments.h>
|
||||
#include "../Platform.h"
|
||||
|
||||
struct ScheduleEntry {
|
||||
uint8_t hour;
|
||||
@ -84,9 +83,7 @@ class CircadianRhythm : public InputSource {
|
||||
minute = 0;
|
||||
}
|
||||
Log.notice("Current time: %d:%d", hour, minute);
|
||||
auto brightness = brightnessForTime(hour, minute);
|
||||
Log.notice("Adjusting brightness to %d", brightness);
|
||||
return InputEvent{InputEvent::SetBrightness, brightness};
|
||||
return InputEvent{InputEvent::SetBrightness, brightnessForTime(hour, minute)};
|
||||
}
|
||||
return InputEvent{};
|
||||
}
|
||||
|
@ -1,171 +1,33 @@
|
||||
#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
|
||||
SerialInput::parseNormal(char nextChar)
|
||||
{
|
||||
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()
|
||||
Serial::read()
|
||||
{
|
||||
while (Serial.available() > 0) {
|
||||
char nextChar = Serial.read();
|
||||
InputEvent ret = InputEvent::None;
|
||||
switch (m_state) {
|
||||
case ParseState::Normal:
|
||||
ret = parseNormal(nextChar);break;
|
||||
case ParseState::EscapeSequence:
|
||||
ret = parseEscape(nextChar);break;
|
||||
case ParseState::CSI:
|
||||
ret = parseCSI(nextChar);break;
|
||||
}
|
||||
if (ret != InputEvent::None) {
|
||||
return ret;
|
||||
if (nextChar == '\n') {
|
||||
doCommand();
|
||||
m_buf = "";
|
||||
} else {
|
||||
m_buf += nextChar;
|
||||
}
|
||||
}
|
||||
return InputEvent::None;
|
||||
}
|
||||
|
||||
void
|
||||
doHelp(Args& args, Print& out)
|
||||
{
|
||||
out.println("Available commands:");
|
||||
Serial::doCommand() {
|
||||
if (command == "tasks") {
|
||||
Serial.println("Tasks:");
|
||||
auto sched = MainLoop::instance()->scheduler;
|
||||
for(auto task : sched.tasks) {
|
||||
for(auto &command : task->commands()) {
|
||||
out.print(command.name);
|
||||
out.print(" ");
|
||||
}
|
||||
}
|
||||
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;
|
||||
bool isFigment = task->isFigment();
|
||||
if (isFigment) {
|
||||
Serial.println("F " + task->name);
|
||||
} else {
|
||||
Serial.println("T " + task->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_logPrinter.println("Unknown command");
|
||||
doHelp(args, m_logPrinter);
|
||||
}
|
||||
|
||||
STATIC_ALLOC(SerialInput);
|
||||
|
@ -3,60 +3,14 @@
|
||||
|
||||
class SerialInput: public InputSource {
|
||||
public:
|
||||
SerialInput();
|
||||
InputEvent read() override;
|
||||
|
||||
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;
|
||||
void onStart() override {
|
||||
//Serial.begin();
|
||||
}
|
||||
|
||||
//static SerialInput::Command *s_root;
|
||||
LogPrinter* printer() {
|
||||
return &m_logPrinter;
|
||||
}
|
||||
|
||||
const std::vector<Command> &commands() const override;
|
||||
InputEvent read();
|
||||
|
||||
private:
|
||||
enum ParseState {
|
||||
Normal,
|
||||
EscapeSequence,
|
||||
CSI
|
||||
};
|
||||
String m_buf;
|
||||
ParseState m_state;
|
||||
char m_escapeSeq[3];
|
||||
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);
|
||||
};
|
||||
}
|
||||
|
21
src/main.cpp
21
src/main.cpp
@ -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"📡 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)");
|
||||
Platform::setTimezone(+2);
|
||||
|
||||
@ -95,17 +105,14 @@ void setup() {
|
||||
Platform::setup();
|
||||
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);
|
||||
|
||||
// Tune in,
|
||||
if (Platform::bootopts.isSafeMode) {
|
||||
Log.warning(u8"⚠️ Starting Figment in safe mode!!!");
|
||||
Log.error(u8"⚠️ Starting Figment in safe mode!!!");
|
||||
runner = &SafeMode::safeModeApp;
|
||||
for(auto task : runner->scheduler.tasks) {
|
||||
task->state = Task::Running;
|
||||
}
|
||||
FastLED.showColor(CRGB(255, 0, 0));
|
||||
FastLED.showColor(CRGB(5, 0, 0));
|
||||
FastLED.show();
|
||||
} else {
|
||||
Log.notice(u8"🌌 Starting Figment...");
|
||||
@ -124,7 +131,7 @@ void setup() {
|
||||
Serial.flush();
|
||||
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.");
|
||||
Serial.flush();
|
||||
}
|
||||
|
@ -118,8 +118,6 @@ MQTTTelemetry::handleEventOnline(const InputEvent& evt)
|
||||
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")) {
|
||||
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_json.clear();
|
||||
|
Loading…
Reference in New Issue
Block a user