config: first version of storing pixel maps and configs in SPIFFS
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed
This commit is contained in:
parent
07e6e98673
commit
f0abdc0567
118
src/Config.cpp
118
src/Config.cpp
@ -1,8 +1,16 @@
|
||||
#include "./Config.h"
|
||||
#include "./Static.h"
|
||||
#include "./Sequencer.h"
|
||||
|
||||
#include <ArduinoLog.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <EEPROM.h>
|
||||
|
||||
#include <LittleFS.h>
|
||||
#include <vector>
|
||||
|
||||
StaticJsonDocument<256> jsonConfig;
|
||||
|
||||
constexpr uint16_t HardwareConfig::MAX_LED_NUM;
|
||||
|
||||
HardwareConfig
|
||||
@ -15,7 +23,7 @@ HardwareConfig::load() {
|
||||
#ifndef BOARD_TEENSY
|
||||
EEPROM.end();
|
||||
#endif
|
||||
Log.notice("Loaded config version %d, CRC %d", ret.version, ret.checksum);
|
||||
Log.notice("config: Loaded SRAM config version %d, CRC %d", ret.version, ret.checksum);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@ -33,18 +41,10 @@ HardwareConfig::save() {
|
||||
#endif
|
||||
}
|
||||
|
||||
LinearCoordinateMapping
|
||||
HardwareConfig::toCoordMap() const
|
||||
{
|
||||
auto pixelCount = min(HardwareConfig::MAX_LED_NUM, std::max((uint16_t)1, data.pixelCount));
|
||||
auto startPixel = min(pixelCount, std::max((uint16_t)1, data.startPixel));
|
||||
return LinearCoordinateMapping{pixelCount, startPixel};
|
||||
}
|
||||
|
||||
bool
|
||||
HardwareConfig::isValid() const
|
||||
{
|
||||
return version == 2 && checksum == getCRC() && data.pixelCount <= MAX_LED_NUM;
|
||||
return version == 3 && checksum == getCRC();
|
||||
}
|
||||
|
||||
uint8_t
|
||||
@ -68,18 +68,82 @@ HardwareConfig::getCRC() const
|
||||
void
|
||||
ConfigService::onStart()
|
||||
{
|
||||
Log.notice("Starting configuration service...");
|
||||
Log.notice("config: Starting configuration service...");
|
||||
m_config = HardwareConfig::load();
|
||||
if (m_config.isValid()) {
|
||||
Log.notice("Configuration found!");
|
||||
Log.notice("config: Configuration found!");
|
||||
} else {
|
||||
Log.notice("No configuration found. Writing defaults...");
|
||||
Log.notice("config: No configuration found. Writing defaults...");
|
||||
m_config = HardwareConfig{};
|
||||
m_config.save();
|
||||
}
|
||||
m_coordMap = m_config.toCoordMap();
|
||||
if (strlen(m_config.data.loadedProfile) == 0) {
|
||||
strcpy(m_config.data.loadedProfile, "default");
|
||||
}
|
||||
loadProfile(m_config.data.loadedProfile);
|
||||
}
|
||||
|
||||
Log.notice("Configured to use %d pixels, starting at %d", m_config.data.pixelCount, m_config.data.startPixel);
|
||||
void
|
||||
ConfigService::loadMap(const String& mapName)
|
||||
{
|
||||
String fname = String("/maps/") + mapName + ".json";
|
||||
if (LittleFS.exists(fname)) {
|
||||
File configFile = LittleFS.open(fname, "r");
|
||||
Log.notice("config: Loading coordinate map %s", mapName.c_str());
|
||||
deserializeJson(jsonConfig, configFile);
|
||||
configFile.close();
|
||||
JsonArray strideList = jsonConfig["strides"];
|
||||
m_jsonMap.load(strideList);
|
||||
} else {
|
||||
Log.warning("config: Couldn't load coordinate map %s!!! Defaulting to linear mapping.", mapName.c_str());
|
||||
m_jsonMap.loadDefault();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ConfigService::loadProfile(const char* profileName)
|
||||
{
|
||||
Log.notice("config: Loading profile %s...", profileName);
|
||||
String fname = String("/profiles/") + profileName + ".json";
|
||||
|
||||
LittleFS.begin();
|
||||
if (LittleFS.exists(fname)) {
|
||||
File configFile = LittleFS.open(fname, "r");
|
||||
jsonConfig.clear();
|
||||
deserializeJson(jsonConfig, configFile);
|
||||
configFile.close();
|
||||
|
||||
JsonObject sceneList = jsonConfig["scenes"];
|
||||
std::vector<Sequencer::Scene> scenes;
|
||||
for(JsonPair pair : sceneList) {
|
||||
Log.notice("config: \tFound scene %s", pair.key().c_str());
|
||||
std::vector<const char*> patterns;
|
||||
for(const char* taskName : pair.value().as<JsonArray>()) {
|
||||
patterns.push_back(taskName);
|
||||
}
|
||||
scenes.push_back(Sequencer::Scene{pair.key().c_str(), patterns});
|
||||
}
|
||||
Static<Sequencer>::instance()->setScenes(std::move(scenes));
|
||||
|
||||
JsonArray taskList = jsonConfig["tasks"];
|
||||
Log.notice("config: Starting %d tasks", taskList.size());
|
||||
for(int i = 0; i < taskList.size();i++) {
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, taskList[i].as<const char*>()});
|
||||
}
|
||||
Log.notice("config: Loaded!");
|
||||
} else {
|
||||
Log.warning("config: Could not load profile %s!", profileName);
|
||||
}
|
||||
|
||||
String configName = jsonConfig["surfaceMap"];
|
||||
jsonConfig.clear();
|
||||
loadMap(configName);
|
||||
LittleFS.end();
|
||||
Log.notice("config: Configured to use %d pixels", m_jsonMap.physicalPixelCount());
|
||||
PhysicalCoordinates topLeft = m_jsonMap.virtualToPhysicalCoords({0, 0});
|
||||
PhysicalCoordinates bottomRight = m_jsonMap.virtualToPhysicalCoords({255, 255});
|
||||
Log.verbose(" (0,0) -> (%d, %d) -> %d", topLeft.x, topLeft.y, m_jsonMap.physicalCoordsToIndex(topLeft));
|
||||
Log.verbose(" (255,255) -> (%d, %d) -> %d", bottomRight.x, bottomRight.y, m_jsonMap.physicalCoordsToIndex(bottomRight));
|
||||
}
|
||||
|
||||
void
|
||||
@ -87,24 +151,22 @@ ConfigService::loop()
|
||||
{
|
||||
}
|
||||
|
||||
const char*
|
||||
ConfigService::loadedProfile() const
|
||||
{
|
||||
return m_config.data.loadedProfile;
|
||||
}
|
||||
|
||||
void
|
||||
ConfigService::handleEvent(const InputEvent &evt)
|
||||
{
|
||||
switch(evt.intent) {
|
||||
case InputEvent::SetDisplayLength:
|
||||
//Log.info("Updating pixel count from %d to %d", m_coordMap.pixelCount, evt.asInt());
|
||||
m_config.data.pixelCount = evt.asInt();
|
||||
m_coordMap = m_config.toCoordMap();
|
||||
//Log.info("Count is now %d", m_coordMap.pixelCount);
|
||||
break;
|
||||
case InputEvent::SetDisplayOffset:
|
||||
//Log.info("Updating pixel offset from %d to %d", m_coordMap.startPixel, evt.asInt());
|
||||
m_config.data.startPixel = evt.asInt();
|
||||
m_coordMap = m_config.toCoordMap();
|
||||
//Log.info("Offset is now %d", m_coordMap.startPixel);
|
||||
break;
|
||||
case InputEvent::LoadConfigurationByName:
|
||||
Log.notice("Reloading configuration %s", evt.asString());
|
||||
strcpy(m_config.data.loadedProfile, evt.asString());
|
||||
loadProfile(evt.asString());
|
||||
case InputEvent::SaveConfigurationRequest:
|
||||
//Log.info("Saving configuration");
|
||||
Log.notice("Saving configuration");
|
||||
m_config.save();
|
||||
break;
|
||||
default:
|
||||
|
80
src/Config.h
80
src/Config.h
@ -1,78 +1,12 @@
|
||||
#pragma once
|
||||
#include <Figments.h>
|
||||
|
||||
struct MaskCoordinateMapping : CoordinateMapping {
|
||||
struct Span {
|
||||
int length = 0;
|
||||
int x = 0;
|
||||
int y = 0;
|
||||
|
||||
Span(int length, int x, int y) : length(length), x(x), y(y) {}
|
||||
};
|
||||
|
||||
Span displayMap[13] = {
|
||||
{6, 0, 6},
|
||||
{6, 1, 6},
|
||||
{7, 2, 6},
|
||||
{9, 3, 4},
|
||||
{14, 4, 4},
|
||||
{17, 5, 0},
|
||||
{12, 6, 2},
|
||||
{18, 7, 0},
|
||||
{14, 8, 4},
|
||||
{9, 9, 5},
|
||||
{7, 10, 4},
|
||||
{6, 11, 5},
|
||||
{6, 12, 5}
|
||||
};
|
||||
|
||||
VirtualCoordinates physicalToVirtualCoords(const PhysicalCoordinates localCoords) const override {
|
||||
int offset = localCoords.x;
|
||||
for(int i = 0; i < 12; i++) {
|
||||
if (offset > displayMap[i].length) {
|
||||
offset -= displayMap[i].length;
|
||||
} else {
|
||||
return VirtualCoordinates{i, offset};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PhysicalCoordinates virtualToPhysicalCoords(const VirtualCoordinates virtualCoords) const override {
|
||||
const uint8_t spanIdx = scale8(12, virtualCoords.x);
|
||||
const uint8_t spanOffset = scale8(17, virtualCoords.y);
|
||||
return PhysicalCoordinates{spanIdx, spanOffset};
|
||||
}
|
||||
|
||||
int physicalCoordsToIndex(const PhysicalCoordinates localCoords) const override {
|
||||
uint8_t idx = 0;
|
||||
bool inverse = false;
|
||||
for(int i = 0; i < localCoords.x; i++) {
|
||||
idx += displayMap[i].length;
|
||||
inverse = !inverse;
|
||||
}
|
||||
if (inverse) {
|
||||
idx += std::max(0, displayMap[localCoords.x].length - 1 - std::max(0, (int)localCoords.y - displayMap[localCoords.x].y));
|
||||
} else {
|
||||
idx += std::min((int)displayMap[localCoords.x].length - 1, std::max(0, (int)localCoords.y - displayMap[localCoords.x].y));
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
unsigned int physicalPixelCount() const override {
|
||||
int total = 0;
|
||||
for(int i = 0; i < 13; i++) {
|
||||
total += displayMap[i].length;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
};
|
||||
#include "JsonCoordinateMapping.h"
|
||||
|
||||
struct HardwareConfig {
|
||||
uint8_t version = 3;
|
||||
uint8_t checksum = 0;
|
||||
struct Data {
|
||||
uint16_t pixelCount = 255;
|
||||
uint16_t startPixel = 0;
|
||||
char loadedProfile[16] = {0};
|
||||
uint8_t lastRed = 255;
|
||||
uint8_t lastGreen = 255;
|
||||
uint8_t lastBlue = 255;
|
||||
@ -84,7 +18,6 @@ struct HardwareConfig {
|
||||
void save();
|
||||
bool isValid() const;
|
||||
|
||||
LinearCoordinateMapping toCoordMap() const;
|
||||
static constexpr uint16_t MAX_LED_NUM = 255;
|
||||
|
||||
private:
|
||||
@ -101,10 +34,13 @@ struct ConfigService: public Task {
|
||||
void onStart();
|
||||
void loop() override;
|
||||
void handleEvent(const InputEvent &evt) override;
|
||||
const CoordinateMapping* coordMap() const { return /*&m_maskMap;*/ &m_coordMap; }
|
||||
const CoordinateMapping* coordMap() const { return &m_jsonMap; }
|
||||
const char* loadedProfile() const;
|
||||
|
||||
private:
|
||||
HardwareConfig m_config;
|
||||
MaskCoordinateMapping m_maskMap;
|
||||
LinearCoordinateMapping m_coordMap;
|
||||
JsonCoordinateMapping m_jsonMap;
|
||||
|
||||
void loadProfile(const char* name);
|
||||
void loadMap(const String& mapName);
|
||||
};
|
||||
|
@ -1,18 +1,29 @@
|
||||
#include "Sequencer.h"
|
||||
#include <MainLoop.h>
|
||||
#include "Static.h"
|
||||
|
||||
Sequencer::Sequencer(std::vector<Sequencer::Scene> &&scenes) :
|
||||
Sequencer::Sequencer() :
|
||||
Task("SceneSequencer"),
|
||||
m_idx(0),
|
||||
m_scenes(std::move(scenes))
|
||||
m_idx(0)
|
||||
{
|
||||
}
|
||||
|
||||
Sequencer::Sequencer(std::vector<Sequencer::Scene> &&scenes, int startIndex) :
|
||||
Task("SceneSequencer"),
|
||||
m_idx(startIndex),
|
||||
m_scenes(std::move(scenes))
|
||||
void
|
||||
Sequencer::Scene::start()
|
||||
{
|
||||
for(const char* pattern : patterns) {
|
||||
Log.verbose("Starting pattern task %s", pattern);
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Sequencer::Scene::stop()
|
||||
{
|
||||
for(const char* pattern : patterns) {
|
||||
Log.verbose("Stopping pattern task %s", pattern);
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StopThing, pattern});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -35,32 +46,35 @@ Sequencer::onStart()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
Sequencer::setScenes(std::vector<Scene> &&scenes)
|
||||
{
|
||||
Log.notice("Updated scenes");
|
||||
m_idx = 0;
|
||||
m_scenes = scenes;
|
||||
}
|
||||
|
||||
void
|
||||
Sequencer::handleEvent(const InputEvent& evt)
|
||||
{
|
||||
if (evt.intent == InputEvent::ReadyToRoll) {
|
||||
Log.notice("Starting pattern %s!", m_scenes[m_idx].name);
|
||||
for(const char* pattern : m_scenes[m_idx].patterns) {
|
||||
Log.verbose("Starting pattern task %s", pattern);
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
||||
}
|
||||
}
|
||||
if (evt.intent == InputEvent::SetPattern && evt.asString() == m_scenes[m_idx].name) {
|
||||
if (evt.intent == InputEvent::ReadyToRoll && !m_scenes.empty()) {
|
||||
Log.notice("Starting pattern %s!", m_scenes[m_idx].name);
|
||||
for(const char* pattern : m_scenes[m_idx].patterns) {
|
||||
Log.verbose("Starting pattern task %s", pattern);
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
||||
}
|
||||
} else if (evt.intent == InputEvent::SetPattern && evt.asString() == m_scenes[m_idx].name) {
|
||||
return;
|
||||
}
|
||||
if (evt.intent == InputEvent::SetPattern || evt.intent == InputEvent::NextPattern || evt.intent == InputEvent::PreviousPattern) {
|
||||
} else if (evt.intent == InputEvent::SetPattern || evt.intent == InputEvent::NextPattern || evt.intent == InputEvent::PreviousPattern) {
|
||||
Log.notice("Switching pattern!");
|
||||
for(const char* pattern : m_scenes[m_idx].patterns) {
|
||||
Log.verbose("Stopping pattern task %s", pattern);
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StopThing, pattern});
|
||||
}
|
||||
|
||||
m_scenes[m_idx].stop();
|
||||
|
||||
if (evt.intent == InputEvent::NextPattern) {
|
||||
m_idx++;
|
||||
} else if (evt.intent == InputEvent::PreviousPattern) {
|
||||
m_idx--;
|
||||
} else {
|
||||
//m_idx = evt.asInt();
|
||||
for(m_idx = 0; m_idx < m_scenes.size(); m_idx++) {
|
||||
if (!strcmp(evt.asString(), m_scenes[m_idx].name)) {
|
||||
break;
|
||||
@ -76,9 +90,9 @@ Sequencer::handleEvent(const InputEvent& evt)
|
||||
m_idx = 0;
|
||||
}
|
||||
|
||||
for(const char* pattern : m_scenes[m_idx].patterns) {
|
||||
Log.verbose("Starting pattern task %s", pattern);
|
||||
MainLoop::instance()->dispatch(InputEvent{InputEvent::StartThing, pattern});
|
||||
}
|
||||
m_scenes[m_idx].start();
|
||||
}
|
||||
}
|
||||
|
||||
STATIC_ALLOC(Sequencer);
|
||||
STATIC_TASK(Sequencer);
|
||||
|
@ -10,14 +10,16 @@ public:
|
||||
public:
|
||||
const char* name;
|
||||
std::vector<const char*> patterns;
|
||||
void start();
|
||||
void stop();
|
||||
};
|
||||
|
||||
Sequencer(std::vector<Scene> &&scenes);
|
||||
Sequencer(std::vector<Scene> &&scenes, int startingIndex);
|
||||
Sequencer();
|
||||
|
||||
void loop() override;
|
||||
void onStart() override;
|
||||
void handleEvent(const InputEvent& evt) override;
|
||||
void setScenes(std::vector<Scene> &&scenes);
|
||||
|
||||
const char* currentSceneName();
|
||||
const std::vector<Scene> scenes() const;
|
||||
|
50
src/main.cpp
50
src/main.cpp
@ -12,17 +12,13 @@
|
||||
#include "Static.h"
|
||||
#include "Config.h"
|
||||
|
||||
#include "Sequencer.h"
|
||||
#include "LogService.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "animations/Power.h"
|
||||
#include "animations/SolidAnimation.h"
|
||||
#include "animations/Chimes.h"
|
||||
#include "animations/Flashlight.h"
|
||||
#include "animations/Drain.h"
|
||||
#include "animations/UpdateStatus.h"
|
||||
#include "animations/InputBlip.h"
|
||||
|
||||
#include "inputs/ColorCycle.h"
|
||||
#include "inputs/Buttons.h"
|
||||
@ -65,32 +61,6 @@ REGISTER_TASK(power);
|
||||
}
|
||||
});*/
|
||||
|
||||
class InputBlip: public Figment {
|
||||
public:
|
||||
InputBlip() : Figment("InputBlip", Task::Stopped) {}
|
||||
|
||||
void handleEvent(const InputEvent& evt) override {
|
||||
if (evt.intent != InputEvent::None) {
|
||||
m_time = qadd8(m_time, 5);
|
||||
}
|
||||
}
|
||||
void loop() override {
|
||||
if (m_time > 0) {
|
||||
m_time--;
|
||||
}
|
||||
}
|
||||
void render(Display* dpy) const override {
|
||||
if (m_time > 0) {
|
||||
dpy->pixelAt(0) = CRGB(0, brighten8_video(ease8InOutApprox(m_time)), 0);
|
||||
}
|
||||
}
|
||||
private:
|
||||
uint8_t m_time = 0;
|
||||
};
|
||||
|
||||
InputBlip inputBlip;
|
||||
REGISTER_TASK(inputBlip);
|
||||
|
||||
InputFunc randomPulse([]() {
|
||||
static unsigned int pulse = 0;
|
||||
EVERY_N_MILLISECONDS(25) {
|
||||
@ -131,22 +101,6 @@ InputMapper keyMap([](const InputEvent& evt) {
|
||||
|
||||
REGISTER_TASK(keyMap);
|
||||
|
||||
|
||||
#ifndef DEFAULT_PATTERN_INDEX
|
||||
#define DEFAULT_PATTERN_INDEX 0
|
||||
#endif
|
||||
|
||||
Sequencer sequencer{{
|
||||
{"Idle", {"Solid", "MPU5060", "Pulse", "IdleColors", "CircadianRhythm"}},
|
||||
{"Acid", {"Chimes", "Pulse", "MPU5060", "IdleColors", "Rainbow"}},
|
||||
{"Solid", {"Solid", "MPU5060", "Pulse", "CircadianRhythm"}},
|
||||
{"Interactive", {"Drain", "MPU5060", "CircadianRhythm"}},
|
||||
{"Flashlight", {"Flashlight"}},
|
||||
{"Gay", {"Solid", "Pulse", "Rainbow"}},
|
||||
}, DEFAULT_PATTERN_INDEX};
|
||||
|
||||
REGISTER_TASK(sequencer);
|
||||
|
||||
class BPM : public InputSource {
|
||||
public:
|
||||
BPM() : InputSource("BPM") {}
|
||||
@ -218,7 +172,7 @@ REGISTER_TASK(renderer);
|
||||
|
||||
Renderer configRenderer{
|
||||
{&dpy},
|
||||
{Static<DrainAnimation>::instance(), /*&configDisplay,*/ &inputBlip, &power}
|
||||
{Static<DrainAnimation>::instance(), /*&configDisplay,*/ Static<InputBlip>::instance(), &power}
|
||||
};
|
||||
|
||||
// Cycle some random colors
|
||||
|
Loading…
Reference in New Issue
Block a user