#include "./Config.h" #include "./Static.h" #include "./Sequencer.h" #include #include #include #include #include StaticJsonDocument<256> jsonConfig; constexpr uint16_t HardwareConfig::MAX_LED_NUM; HardwareConfig HardwareConfig::load() { HardwareConfig ret; #ifndef BOARD_TEENSY EEPROM.begin(sizeof(ret)); #endif EEPROM.get(0, ret); #ifndef BOARD_TEENSY EEPROM.end(); #endif Log.notice("config: Loaded SRAM config version %d, CRC %d", ret.version, ret.checksum); return ret; } void HardwareConfig::save() { HardwareConfig dataCopy{*this}; dataCopy.checksum = getCRC(); #ifndef BOARD_TEENSY EEPROM.begin(sizeof(dataCopy)); #endif EEPROM.put(0, dataCopy); #ifndef BOARD_TEENSY EEPROM.commit(); EEPROM.end(); #endif } bool HardwareConfig::isValid() const { return version == 3 && checksum == getCRC(); } uint8_t HardwareConfig::getCRC() const { const unsigned char* message = reinterpret_cast(&data); constexpr uint8_t length = sizeof(data); unsigned char i, j, crc = 0; for(i = 0; i < length; i++) { crc ^= message[i]; for(j = 0; j < 8; j++) { if (crc & 1) { crc ^= CRC7_POLY; } crc >>= 1; } } return crc; } void ConfigService::onStart() { Log.notice("config: Starting configuration service..."); m_config = HardwareConfig::load(); if (m_config.isValid()) { Log.notice("config: Configuration found!"); } else { Log.notice("config: No configuration found. Writing defaults..."); m_config = HardwareConfig{}; m_config.save(); } if (strlen(m_config.data.loadedProfile) == 0) { strcpy(m_config.data.loadedProfile, "default"); } if (m_overrideProfile != nullptr) { loadProfile(m_overrideProfile); } else { loadProfile(m_config.data.loadedProfile); } } void ConfigService::overrideProfile(const char* profileName) { m_overrideProfile = profileName; } 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 scenes; for(JsonPair pair : sceneList) { Log.notice("config: \tFound scene %s", pair.key().c_str()); std::vector patterns; for(const char* taskName : pair.value().as()) { patterns.push_back(taskName); } scenes.push_back(Sequencer::Scene{pair.key().c_str(), patterns}); } Static::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()}); } 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 ConfigService::loop() { } const char* ConfigService::loadedProfile() const { return m_config.data.loadedProfile; } void ConfigService::handleEvent(const InputEvent &evt) { switch(evt.intent) { case InputEvent::LoadConfigurationByName: Log.notice("Reloading configuration %s", evt.asString()); strcpy(m_config.data.loadedProfile, evt.asString()); loadProfile(evt.asString()); case InputEvent::SaveConfigurationRequest: Log.notice("Saving configuration"); m_config.save(); break; default: break; } } STATIC_ALLOC(ConfigService); STATIC_TASK(ConfigService);