diff --git a/API.md b/API.md index d377e13..f25e41c 100644 --- a/API.md +++ b/API.md @@ -10,6 +10,8 @@ - [POST /api/steps](#post-apisteps) - [GET /api/triggers/time](#get-apitriggerstime) - [POST /api/triggers/time](#post-apitriggerstime) +- [GET /api/triggers/motion](#get-apitriggersmotion) +- [POST /api/triggers/motion](#post-apitriggersmotion) - [POST /api/firmware](#post-apifirmware) ## GET /api/version @@ -203,6 +205,56 @@ enabled: whether or not this trigger is enabled Changes the time trigger settings. Request body format is the same as is returned in the GET request. + +## GET /api/triggers/motion + +Returns the current settings for the motion triggers. + +delay: How long to keep the lights on after the last sensor stops detecting motion. + +pin: GPIO pin to which the motion sensor is connected. High is assumed to be active. + +direction: +Enumeration determining from which side the sweep animation starts if transitionTime is set. + +| value | description | +| ----- | --- | +| 1 | Non-directional. All steps change brightness at the same time. | +| 2 | Top-down. Starts a sweeping fade from the top step. | +| 3 | Bottom-up. Starts a sweeping fade from the bottom step. | + +brightness: value from 0 to 255 + +enabled: whether or not this trigger is enabled + +*Example response:* +```json +{ + "enabled": true, + "enabledDuringTimeTrigger": true, + "transitionTime": 1000, + "delay": 30000, + "triggers": [ + { + "pin": 14, + "brightness": 64, + "direction": 2, + "enabled": true + }, + { + "pin": 15, + "brightness": 64, + "direction": 3, + "enabled": true + } + ] +} +``` + +## POST /api/triggers/motion + +Changes the motion trigger settings. Request body format is the same as is returned in the GET request. + ## POST /api/firmware Uploads new firmware. The bin file should be posted as a multipart/form-data file attachment. Name is not relevant. \ No newline at end of file diff --git a/src/ESP8266HTTPClient-h4xx0red.cpp b/src/ESP8266HTTPClient-h4xx0red.cpp index cc0bcc6..222513f 100644 --- a/src/ESP8266HTTPClient-h4xx0red.cpp +++ b/src/ESP8266HTTPClient-h4xx0red.cpp @@ -45,7 +45,7 @@ #include #include -#include "ESP8266HTTPClient-h4xx0red.h" +#include "./ESP8266HTTPClient-h4xx0red.h" class TransportTraits { diff --git a/src/assets/version.h b/src/assets/version.h index 8bd50d0..2a39259 100644 --- a/src/assets/version.h +++ b/src/assets/version.h @@ -4,10 +4,10 @@ const uint8_t VersionMajor = 2; const uint8_t VersionMinor = 0; const uint8_t VersionPatch = 0; -const uint8_t VersionMetadata = 17; +const uint8_t VersionMetadata = 18; const char VersionBranch[] = "release/2.0"; const char VersionSemVer[] = "2.0.0-beta.1"; -const char VersionFullSemVer[] = "2.0.0-beta.1+17"; +const char VersionFullSemVer[] = "2.0.0-beta.1+18"; const char VersionCommitDate[] = "2018-01-14"; #endif diff --git a/src/charproperties.cpp b/src/charproperties.cpp index 9d3a3b1..de13473 100644 --- a/src/charproperties.cpp +++ b/src/charproperties.cpp @@ -4,12 +4,12 @@ * * https://git.x2software.net/pub/Stairs */ -#include "charproperties.h" +#include "./charproperties.h" #include #include -#include "debug.h" +#include "./debug.h" -void CharProperties::assignChar(char** field, const char* newValue) +void assignChar(char** field, const char* newValue) { if (*field != nullptr) delete *field; diff --git a/src/charproperties.h b/src/charproperties.h index 3ef9df4..2b5e941 100644 --- a/src/charproperties.h +++ b/src/charproperties.h @@ -9,10 +9,6 @@ #include -class CharProperties -{ - protected: - void assignChar(char** field, const char* newValue); -}; +void assignChar(char** field, const char* newValue); #endif \ No newline at end of file diff --git a/src/components/PCA9685.cpp b/src/components/PCA9685.cpp index fcc2f26..4a7f6eb 100644 --- a/src/components/PCA9685.cpp +++ b/src/components/PCA9685.cpp @@ -3,7 +3,7 @@ * Copyright 2017 (c) Mark van Renswoude */ #include -#include "PCA9685.h" +#include "./PCA9685.h" #include #include diff --git a/src/config.h b/src/config.h index 05983bc..c132ae7 100644 --- a/src/config.h +++ b/src/config.h @@ -14,8 +14,9 @@ static const uint32_t SerialDebugStartupDelay = 2000; static const char* ConnectionSettingsFile = "/connection.json"; static const char* SystemSettingsFile = "/system.json"; -static const char* StepSettingsFile = "/stepsettings.dat"; -static const char* TimeTriggerSettingsFile = "/timetriggersettings.dat"; +static const char* StepsSettingsFile = "/steps.json"; +static const char* TimeTriggerSettingsFile = "/timetriggers.json"; +static const char* MotionTriggerSettingsFile = "/motiontriggers.json"; static const char* DefaultAPSSIDPrefix = "Stairs-"; diff --git a/src/debug.cpp b/src/debug.cpp index 00065a7..1501daa 100644 --- a/src/debug.cpp +++ b/src/debug.cpp @@ -4,7 +4,7 @@ * * https://git.x2software.net/pub/Stairs */ -#include "debug.h" +#include "./debug.h" void _dinit() diff --git a/src/debug.h b/src/debug.h index f722331..78ec055 100644 --- a/src/debug.h +++ b/src/debug.h @@ -7,7 +7,7 @@ #ifndef __serialdebug #define __serialdebug -#include "config.h" +#include "./config.h" #include void _dinit(); diff --git a/src/global.cpp b/src/global.cpp index a6d1bb7..07b3335 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -4,7 +4,7 @@ * * https://git.x2software.net/pub/Stairs */ -#include "global.h" +#include "./global.h" ConnectionSettings* connectionSettings = new ConnectionSettings(); bool connectionSettingsChanged = false; @@ -18,6 +18,9 @@ bool stepsSettingsChanged = false; TimeTriggerSettings* timeTriggerSettings = new TimeTriggerSettings(); bool timeTriggerSettingsChanged = false; +MotionTriggerSettings* motionTriggerSettings = new MotionTriggerSettings(); +bool motionTriggerSettingsChanged = false; + Stairs* stairs; diff --git a/src/global.h b/src/global.h index b8fb847..fc751bf 100644 --- a/src/global.h +++ b/src/global.h @@ -10,11 +10,12 @@ #include #include #include -#include "settings/connection.h" -#include "settings/system.h" -#include "settings/steps.h" -#include "settings/triggers/time.h" -#include "stairs.h" +#include "./settings/connection.h" +#include "./settings/system.h" +#include "./settings/steps.h" +#include "./settings/triggers/time.h" +#include "./settings/triggers/motion.h" +#include "./stairs.h" extern ConnectionSettings* connectionSettings; extern bool connectionSettingsChanged; @@ -28,6 +29,9 @@ extern bool stepsSettingsChanged; extern TimeTriggerSettings* timeTriggerSettings; extern bool timeTriggerSettingsChanged; +extern MotionTriggerSettings* motionTriggerSettings; +extern bool motionTriggerSettingsChanged; + extern Stairs* stairs; diff --git a/src/main.cpp b/src/main.cpp index 1ef57ed..f7878a0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,29 +16,28 @@ extern "C" { #include } -#include "config.h" -#include "debug.h" -#include "global.h" -#include "components/PCA9685.h" -#include "settings/connection.h" -#include "server/static.h" -#include "server/settings.h" -#include "server/firmware.h" -#include "server/api.h" +#include "./config.h" +#include "./debug.h" +#include "./global.h" +#include "./components/PCA9685.h" +#include "./settings/connection.h" +#include "./server/static.h" +#include "./server/settings.h" +#include "./server/firmware.h" +#include "./server/api.h" ADC_MODE(ADC_VCC); // Forward declarations void initWiFi(); +void initMotionPins(); #ifdef SerialDebug void wifiEvent(WiFiEvent_t event); void updateDebugStatus(); #endif void updateLED(); void updateNTPClient(); - -void updateTimeTrigger(); void checkTriggers(); void handleNotFound(AsyncWebServerRequest* request); @@ -73,10 +72,12 @@ void setup() systemSettings->read(); stepsSettings->read(); timeTriggerSettings->read(); + motionTriggerSettings->read(); pinMode(systemSettings->pinAPButton(), INPUT_PULLUP); pinMode(systemSettings->pinLEDAP(), OUTPUT); pinMode(systemSettings->pinLEDSTA(), OUTPUT); + initMotionPins(); _dln("Setup :: initializing PCA9685"); @@ -142,6 +143,12 @@ void loop() ESP.restart(); } + if (motionTriggerSettingsChanged) + { + initMotionPins(); + motionTriggerSettingsChanged = false; + } + currentTime = millis(); @@ -191,12 +198,7 @@ void loop() updateLED(); updateNTPClient(); - - if (timeTriggerSettings->enabled() /*|| motionTriggerEnabled*/) - { - updateTimeTrigger(); - checkTriggers(); - } + checkTriggers(); stairs->tick(); } @@ -253,6 +255,20 @@ void initWiFi() } +void initMotionPins() +{ + if (!motionTriggerSettings->enabled()) + return; + + for (uint8_t i = 0; i < motionTriggerSettings->triggerCount(); i++) + { + MotionTrigger* trigger = motionTriggerSettings->trigger(i); + if (trigger->enabled) + pinMode(trigger->pin, INPUT); + } +} + + #ifdef SerialDebug void wifiEvent(WiFiEvent_t event) { @@ -403,10 +419,20 @@ TimeTrigger* activeTimeTrigger = nullptr; void updateTimeTrigger() { if (ntpClient == nullptr || !timeTriggerSettings->enabled()) + { + activeTimeTrigger = nullptr; + return; + } + + if (timeTriggerSettingsChanged) + { + // Time trigger settings changed, activeTimeTrigger pointer is considered + // invalid, force recheck + timeTriggerSettingsChanged = false; + } + else if (currentTime - lastTimeTriggerChecked < 10000) return; - if (currentTime - lastTimeTriggerChecked < 10000) - return; lastTimeTriggerChecked = currentTime; _dln("Triggers:: updating time trigger"); @@ -414,11 +440,12 @@ void updateTimeTrigger() uint32_t epochTime = ntpClient->getEpochTime(); if (epochTime == 0) { + activeTimeTrigger = nullptr; _dln("Triggers:: time not synchronised yet"); return; } - // TODO compensate for UTC + // TODO apply timezone offset tmElements_t time; breakTime(epochTime, time); @@ -435,21 +462,59 @@ void updateTimeTrigger() } +uint32_t activeMotionStart = 0; +uint16_t activeMotionBrightness = 0; +MotionDirection activeMotionDirection = Nondirectional; +bool lastMotion = false; + + +void updateMotionTrigger() +{ + if (!motionTriggerSettings->enabled() || !motionTriggerSettings->triggerCount()) + { + activeMotionStart = 0; + return; + } + + for (uint8_t i = 0; i < motionTriggerSettings->triggerCount(); i++) + { + MotionTrigger* trigger = motionTriggerSettings->trigger(i); + + if (trigger->enabled && digitalRead(trigger->pin) == HIGH) + { + if (activeMotionStart == 0) + { + activeMotionDirection = trigger->direction; + activeMotionBrightness = trigger->brightness; + } + + activeMotionStart = currentTime; + } + } + + if (currentTime - activeMotionStart >= motionTriggerSettings->delay()) + activeMotionStart = 0; +} + + void checkTriggers() { + if (!timeTriggerSettings->enabled() && activeTimeTrigger == nullptr && + !motionTriggerSettings->enabled() && activeMotionStart == 0) + return; + + updateTimeTrigger(); + updateMotionTrigger(); + bool inTimeTrigger = timeTriggerSettings->enabled() && activeTimeTrigger != nullptr && activeTimeTrigger->brightness; bool timeTriggerChanged = activeTimeTrigger != lastTimeTrigger; lastTimeTrigger = activeTimeTrigger; - // TODO motion sensor settings - bool inMotionTrigger = false; // && (alsoDuringTimeTrigger || !inTimeTrigger) - bool motionChanged = false; - - // TODO dummies, replace these with motionSettings-> values later on - uint16_t motionBrightness = 0; - uint16_t motionTransitionTime = 0; + bool inMotionTrigger = (activeMotionStart > 0) && (!inTimeTrigger || motionTriggerSettings->enabledDuringTimeTrigger()); + bool motionChanged = (activeMotionStart > 0) != lastMotion; + lastMotion = (activeMotionStart > 0); if (!motionChanged && !timeTriggerChanged) @@ -462,20 +527,26 @@ void checkTriggers() { _dln("Triggers :: start motion trigger"); - // Start sweep - bool sweepDown = true; - uint8_t stepsCount = stepsSettings->count(); - uint16_t offset = sweepDown ? 0 : motionTransitionTime; - uint16_t offsetIncrement = stepsCount > 0 ? motionTransitionTime / stepsCount : 0; - - for (uint8_t step = 0; step < stepsCount; step++) + if (activeMotionDirection == Nondirectional || motionTriggerSettings->transitionTime() == 0) { - stairs->set(step, motionBrightness, motionTransitionTime, offset); + stairs->setAll(activeMotionBrightness, motionTriggerSettings->transitionTime(), 0); + } + else + { + // Start sweep + uint8_t stepsCount = stepsSettings->count(); + uint16_t offsetIncrement = stepsCount > 0 ? (motionTriggerSettings->transitionTime() / stepsCount) * 1.5 : 0; + uint16_t offset = activeMotionDirection == TopDown ? 0 : (stepsCount - 1) * offsetIncrement; - if (sweepDown) - offset += offsetIncrement; - else - offset -= offsetIncrement; + for (uint8_t step = 0; step < stepsCount; step++) + { + stairs->set(step, activeMotionBrightness, motionTriggerSettings->transitionTime(), offset); + + if (activeMotionDirection == TopDown) + offset += offsetIncrement; + else + offset -= offsetIncrement; + } } } else @@ -485,14 +556,14 @@ void checkTriggers() _dln("Triggers :: motion stopped, falling back to time trigger"); // Fall back to time trigger value - stairs->setAll(activeTimeTrigger->brightness, motionTransitionTime, 0); + stairs->setAll(activeTimeTrigger->brightness, motionTriggerSettings->transitionTime(), 0); } else { _dln("Triggers :: motion stopped, turning off"); // No more motion, no active time trigger, turn off - stairs->setAll(0, motionTransitionTime, 0); + stairs->setAll(0, motionTriggerSettings->transitionTime(), 0); } } } diff --git a/src/praisethesun.cpp b/src/praisethesun.cpp index 80c2294..8c32def 100644 --- a/src/praisethesun.cpp +++ b/src/praisethesun.cpp @@ -7,7 +7,7 @@ * This code was ported over from Domoticz' SunRiseSet.cpp * https://github.com/domoticz/domoticz/ */ -#include "praisethesun.h" +#include "./praisethesun.h" #include diff --git a/src/server/api.cpp b/src/server/api.cpp index 1881059..86b3e5b 100644 --- a/src/server/api.cpp +++ b/src/server/api.cpp @@ -4,11 +4,11 @@ * * https://git.x2software.net/pub/Stairs */ -#include "api.h" +#include "./api.h" #include #include #include -#include "shared.h" +#include "./shared.h" #include "../assets/version.h" #include "../debug.h" #include "../global.h" @@ -69,30 +69,8 @@ void handleGetTimeTriggers(AsyncWebServerRequest *request) { _dln("API :: get time triggers"); - uint8_t count = timeTriggerSettings->triggerCount(); - - DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(count) + JSON_OBJECT_SIZE(3) + count*JSON_OBJECT_SIZE(5)); - JsonObject& root = jsonBuffer.createObject(); - root["enabled"] = timeTriggerSettings->enabled(); - root["transitionTime"] = timeTriggerSettings->transitionTime(); - - JsonArray& jsonTriggers = root.createNestedArray("triggers"); - - for (uint8_t i = 0; i < count; i++) - { - TimeTrigger* trigger = timeTriggerSettings->trigger(i); - - JsonObject& jsonTrigger = jsonTriggers.createNestedObject(); - jsonTrigger["time"] = trigger->time; - jsonTrigger["daysOfWeek"] = trigger->daysOfWeek; - jsonTrigger["brightness"] = trigger->brightness; - jsonTrigger["triggerType"] = (uint8_t)trigger->triggerType; - jsonTrigger["enabled"] = trigger->enabled; - } - AsyncResponseStream *response = request->beginResponseStream("application/json"); - root.printTo(*response); - + timeTriggerSettings->toJson(*response); request->send(response); } @@ -101,44 +79,47 @@ void handlePostTimeTriggers(AsyncWebServerRequest *request, uint8_t *data, size_ { _dln("API :: post time triggers"); - DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(10) + JSON_OBJECT_SIZE(3) + 10*JSON_OBJECT_SIZE(5) + 510); - JsonObject& root = jsonBuffer.parseObject((char*)data); - if (!root.success()) + bool changed; + if (timeTriggerSettings->fromJson((char*)data, &changed)) { + timeTriggerSettings->write(); + + if (changed) + timeTriggerSettingsChanged = true; + + request->send(200); + } + else request->send(400); - return; - } +} - JsonArray& jsonTriggers = root["triggers"]; - if (jsonTriggers.size() > 255) + +void handleGetMotionTriggers(AsyncWebServerRequest *request) +{ + _dln("API :: get motion triggers"); + + AsyncResponseStream *response = request->beginResponseStream("application/json"); + motionTriggerSettings->toJson(*response); + request->send(response); +} + + +void handlePostMotionTriggers(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) +{ + _dln("API :: post motion triggers"); + + bool changed; + if (motionTriggerSettings->fromJson((char*)data, &changed)) { + motionTriggerSettings->write(); + + if (changed) + motionTriggerSettingsChanged = true; + + request->send(200); + } + else request->send(400); - return; - } - - timeTriggerSettings->enabled(root["enabled"]); - timeTriggerSettings->transitionTime(root["transitionTime"]); - timeTriggerSettings->beginSetTriggers(jsonTriggers.size()); - - TimeTrigger trigger; - for (uint8_t i = 0; i < jsonTriggers.size(); i++) - { - JsonObject& jsonTrigger = jsonTriggers[i]; - - trigger.time = jsonTrigger["time"]; - trigger.daysOfWeek = jsonTrigger["daysOfWeek"]; - trigger.brightness = jsonTrigger["brightness"]; - trigger.triggerType = (TimeTriggerType)(uint8_t)jsonTrigger["triggerType"]; - trigger.enabled = jsonTrigger["enabled"]; - - timeTriggerSettings->setTrigger(i, &trigger); - } - - timeTriggerSettings->endSetTriggers(); - timeTriggerSettings->write(); - timeTriggerSettingsChanged = true; - - request->send(200); } @@ -149,4 +130,7 @@ void registerAPIRoutes(AsyncWebServer* server) server->on("/api/triggers/time", HTTP_GET, handleGetTimeTriggers); server->on("/api/triggers/time", HTTP_POST, devNullRequest, devNullFileUpload, handlePostTimeTriggers); + + server->on("/api/triggers/motion", HTTP_GET, handleGetMotionTriggers); + server->on("/api/triggers/motion", HTTP_POST, devNullRequest, devNullFileUpload, handlePostMotionTriggers); } \ No newline at end of file diff --git a/src/server/firmware.cpp b/src/server/firmware.cpp index 5d1389d..5774629 100644 --- a/src/server/firmware.cpp +++ b/src/server/firmware.cpp @@ -4,7 +4,7 @@ * * https://git.x2software.net/pub/Stairs */ -#include "firmware.h" +#include "./firmware.h" #include "../config.h" #include "../debug.h" #include "../global.h" diff --git a/src/server/settings.cpp b/src/server/settings.cpp index a642c41..6b11f84 100644 --- a/src/server/settings.cpp +++ b/src/server/settings.cpp @@ -4,11 +4,11 @@ * * https://git.x2software.net/pub/Stairs */ -#include "settings.h" +#include "./settings.h" #include #include #include -#include "shared.h" +#include "./shared.h" #include "../assets/version.h" #include "../debug.h" #include "../global.h" diff --git a/src/server/shared.cpp b/src/server/shared.cpp index 6fbfd50..eb87a21 100644 --- a/src/server/shared.cpp +++ b/src/server/shared.cpp @@ -4,7 +4,7 @@ * * https://git.x2software.net/pub/Stairs */ -#include "shared.h" +#include "./shared.h" void devNullRequest(AsyncWebServerRequest *request) {} void devNullFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {} diff --git a/src/server/static.cpp b/src/server/static.cpp index 070971d..cf14d69 100644 --- a/src/server/static.cpp +++ b/src/server/static.cpp @@ -4,7 +4,7 @@ * * https://git.x2software.net/pub/Stairs */ -#include "static.h" +#include "./static.h" #include "../debug.h" #include "../assets/html.h" #include "../assets/js.h" diff --git a/src/settings/abstractjson.cpp b/src/settings/abstractjson.cpp new file mode 100644 index 0000000..79eb409 --- /dev/null +++ b/src/settings/abstractjson.cpp @@ -0,0 +1,70 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#include "./abstractjson.h" +#include + + +void AbstractJsonSettings::read() +{ + _d(getDebugPrefix()); _dln(" :: opening file"); + File settingsFile = SPIFFS.open(getFilename(), "r"); + if (!settingsFile) + { + _d(getDebugPrefix()); _dln(" :: failed to open file"); + return; + } + + size_t size = settingsFile.size(); + if (size > 1024) + { + _d(getDebugPrefix()); _dln(" :: file size is too large"); + return; + } + + if (size == 0) + { + _d(getDebugPrefix()); _dln(" :: zero size file"); + return; + } + + std::unique_ptr buf(new char[size]); + settingsFile.readBytes(buf.get(), size); + + _dln(buf.get()); + + if (fromJson(buf.get())) + { + _d(getDebugPrefix()); + _dln(" :: read from file"); + } + else + { + _d(getDebugPrefix()); + _dln(" :: failed to parse file"); + } +} + + +void AbstractJsonSettings::write() +{ + _d(getDebugPrefix()); _dln(" :: opening file for writing"); + File settingsFile = SPIFFS.open(getFilename(), "w"); + if (!settingsFile) + { + _d(getDebugPrefix()); _dln(" :: failed to open file for writing"); + return; + } + + toJson(settingsFile); + _d(getDebugPrefix()); _dln(" :: written to file"); +} + + +bool AbstractJsonSettings::fromJson(char* data) +{ + return fromJson(data, nullptr); +} diff --git a/src/settings/abstractjson.h b/src/settings/abstractjson.h new file mode 100644 index 0000000..326ef21 --- /dev/null +++ b/src/settings/abstractjson.h @@ -0,0 +1,28 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#ifndef __settingsjson +#define __settingsjson + +#include "../debug.h" + +class AbstractJsonSettings +{ + protected: + virtual const char* getFilename() = 0; + virtual const char* getDebugPrefix() = 0; + + public: + void read(); + void write(); + + virtual void toJson(Print &print) = 0; + virtual bool fromJson(char* data, bool* changed) = 0; + + bool fromJson(char* data); +}; + +#endif \ No newline at end of file diff --git a/src/settings/connection.cpp b/src/settings/connection.cpp index 0589192..4053ef3 100644 --- a/src/settings/connection.cpp +++ b/src/settings/connection.cpp @@ -4,65 +4,15 @@ * * https://git.x2software.net/pub/Stairs */ -#include "connection.h" +#include "./connection.h" #include -#include +#include "./abstractjson.h" #include "../debug.h" -#include "../global.h" #include "../config.h" +#include "../global.h" -void ConnectionSettings::read() -{ - _dln("ConnectionSettings :: opening file"); - File settingsFile = SPIFFS.open(ConnectionSettingsFile, "r"); - if (!settingsFile) - { - _dln("ConnectionSettings :: failed to open file"); - return; - } - - size_t size = settingsFile.size(); - if (size > 1024) - { - _dln("ConnectionSettings :: file size is too large"); - return; - } - - if (size == 0) - { - _dln("ConnectionSettings :: zero size file"); - return; - } - - std::unique_ptr buf(new char[size]); - settingsFile.readBytes(buf.get(), size); - - _dln(buf.get()); - - if (fromJson(buf.get())) - _dln("ConnectionSettings :: read from file"); - else - _dln("ConnectionSettings :: failed to parse file"); -} - - -void ConnectionSettings::write() -{ - _dln("ConnectionSettings :: opening file for writing"); - File settingsFile = SPIFFS.open(ConnectionSettingsFile, "w"); - if (!settingsFile) - { - _dln("ConnectionSettings:: failed to open file for writing"); - return; - } - - toJson(settingsFile); - _dln("ConnectionSettings:: written to file"); -} - - void ConnectionSettings::toJson(Print &print) { DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(9)); @@ -82,12 +32,6 @@ void ConnectionSettings::toJson(Print &print) } -bool ConnectionSettings::fromJson(char* data) -{ - return fromJson(data, nullptr); -} - - bool ConnectionSettings::fromJson(char* data, bool* changed) { if (changed != nullptr) diff --git a/src/settings/connection.h b/src/settings/connection.h index 06ee43b..5391523 100644 --- a/src/settings/connection.h +++ b/src/settings/connection.h @@ -10,7 +10,9 @@ #include #include #include +#include "./abstractjson.h" #include "../charproperties.h" +#include "../config.h" enum ConnectionSettingsFlags @@ -21,7 +23,7 @@ enum ConnectionSettingsFlags }; -class ConnectionSettings : CharProperties +class ConnectionSettings : public AbstractJsonSettings { private: char* mHostname = nullptr; @@ -32,13 +34,12 @@ class ConnectionSettings : CharProperties IPAddress mSubnetMask = (uint32_t)0; IPAddress mGateway = (uint32_t)0; + protected: + virtual const char* getFilename() { return ConnectionSettingsFile; }; + virtual const char* getDebugPrefix() { return "ConnectionSettings"; }; + public: - void read(); - void write(); - - void toJson(Print &print); - bool fromJson(char* data); bool fromJson(char* data, bool* changed); diff --git a/src/settings/steps.cpp b/src/settings/steps.cpp index 18e0efd..55fcd76 100644 --- a/src/settings/steps.cpp +++ b/src/settings/steps.cpp @@ -4,20 +4,11 @@ * * https://git.x2software.net/pub/Stairs */ -#include "steps.h" -#include +#include "./steps.h" +#include #include "../debug.h" -struct Header -{ - uint8_t version; - uint8_t stepCount; - bool useCurve; -}; - - - StepsSettings::StepsSettings() { for (uint8_t i = 0; i < MaxStepCount; i++) @@ -25,41 +16,63 @@ StepsSettings::StepsSettings() } -void StepsSettings::read() +void StepsSettings::toJson(Print &print) { - _dln("StepsSettings :: Loading step settings"); - File f = SPIFFS.open(StepSettingsFile, "r"); - if (!f) - return; + DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(16) + JSON_OBJECT_SIZE(3)); - if (!f.available()) - return; + JsonObject& root = jsonBuffer.createObject(); + root["count"] = count(); + root["useCurve"] = useCurve(); - Header header; - f.readBytes((char*)&header, sizeof(Header)); + JsonArray& jsonCurveShift = root.createNestedArray("curveShift"); + for (uint8_t step = 0; step < MaxStepCount; step++) + jsonCurveShift.add(curveShift(step)); - if (header.version != 1) - return; - - mUseCurve = (header.useCurve == 1); - f.readBytes((char*)&mCurveShift, header.stepCount * sizeof(uint16_t)); - f.close(); + root.printTo(print); } -void StepsSettings::write() +bool StepsSettings::fromJson(char* data, bool* changed) { - _dln("StepsSettings :: Saving step settings"); - File f = SPIFFS.open(StepSettingsFile, "w"); - if (!f) - return; + if (changed != nullptr) + *changed = false; - Header header; - header.version = 1; - header.useCurve = mUseCurve; - header.stepCount = mCount; + DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(16) + JSON_OBJECT_SIZE(3) + 80); + JsonObject& root = jsonBuffer.parseObject(data); - f.write((uint8_t*)&header, sizeof(Header)); - f.write((uint8_t*)&mCurveShift, header.stepCount * sizeof(uint16_t)); - f.close(); + if (!root.success()) + return false; + + uint8_t jsonCount = root["count"]; + bool jsonUseCurve = root["useCurve"]; + + if (jsonCount != count() || + jsonUseCurve != useCurve()) + { + count(jsonCount); + useCurve(jsonUseCurve); + + if (changed != nullptr) + *changed = true; + } + + + JsonArray& jsonCurveShift = root["curveShift"]; + uint8_t stepCount = jsonCurveShift.size(); + if (stepCount >= MaxStepCount) + stepCount = MaxStepCount - 1; + + for (uint8_t step = 0; step < stepCount; step++) + { + uint16_t value = jsonCurveShift[step]; + + if (value != curveShift(step)) + { + curveShift(step, value); + if (changed != nullptr) + *changed = true; + } + } + + return true; } \ No newline at end of file diff --git a/src/settings/steps.h b/src/settings/steps.h index 0b1687c..7b83a51 100644 --- a/src/settings/steps.h +++ b/src/settings/steps.h @@ -8,22 +8,29 @@ #define __settingssteps #include +#include "./abstractjson.h" +#include "../config.h" #define MaxStepCount 16 -class StepsSettings +class StepsSettings : public AbstractJsonSettings { private: - uint8_t mCount = 16; + uint8_t mCount = MaxStepCount; bool mUseCurve = true; uint16_t mCurveShift[MaxStepCount]; + protected: + virtual const char* getFilename() { return StepsSettingsFile; }; + virtual const char* getDebugPrefix() { return "StepsSettings"; }; + public: StepsSettings(); - void read(); - void write(); + void toJson(Print &print); + bool fromJson(char* data, bool* changed); + uint8_t count() { return mCount; } void count(uint8_t value) { mCount = value; } diff --git a/src/settings/system.cpp b/src/settings/system.cpp index 4647c2d..18a05f9 100644 --- a/src/settings/system.cpp +++ b/src/settings/system.cpp @@ -4,7 +4,7 @@ * * https://git.x2software.net/pub/Stairs */ -#include "system.h" +#include "./system.h" #include #include #include "../debug.h" @@ -13,56 +13,6 @@ -void SystemSettings::read() -{ - _dln("SystemSettings :: opening file"); - File settingsFile = SPIFFS.open(SystemSettingsFile, "r"); - if (!settingsFile) - { - _dln("SystemSettings :: failed to open file"); - return; - } - - size_t size = settingsFile.size(); - if (size > 1024) - { - _dln("SystemSettings :: file size is too large"); - return; - } - - if (size == 0) - { - _dln("SystemSettings :: zero size file"); - return; - } - - std::unique_ptr buf(new char[size]); - settingsFile.readBytes(buf.get(), size); - - _dln(buf.get()); - - if (fromJson(buf.get())) - _dln("SystemSettings :: read from file"); - else - _dln("SystemSettings :: failed to parse file"); -} - - -void SystemSettings::write() -{ - _dln("SystemSettings :: opening file for writing"); - File settingsFile = SPIFFS.open(SystemSettingsFile, "w"); - if (!settingsFile) - { - _dln("SystemSettings:: failed to open file for writing"); - return; - } - - toJson(settingsFile); - _dln("SystemSettings:: written to file"); -} - - void SystemSettings::toJson(Print &print) { DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(5)); @@ -87,11 +37,6 @@ void SystemSettings::toJson(Print &print) } -bool SystemSettings::fromJson(char* data) -{ - return fromJson(data, nullptr); -} - bool SystemSettings::fromJson(char* data, bool* changed) { if (changed != nullptr) diff --git a/src/settings/system.h b/src/settings/system.h index c212d0a..b7c9652 100644 --- a/src/settings/system.h +++ b/src/settings/system.h @@ -11,9 +11,10 @@ #include #include #include "../charproperties.h" +#include "./abstractjson.h" -class SystemSettings : CharProperties +class SystemSettings : public AbstractJsonSettings { private: double mLatitude = 0; @@ -34,13 +35,12 @@ class SystemSettings : CharProperties // TODO loginUsername // TODO loginPassword + protected: + virtual const char* getFilename() { return SystemSettingsFile; }; + virtual const char* getDebugPrefix() { return "SystemSettings"; }; + public: - void read(); - void write(); - - void toJson(Print &print); - bool fromJson(char* data); bool fromJson(char* data, bool* changed); diff --git a/src/settings/triggers/motion.cpp b/src/settings/triggers/motion.cpp new file mode 100644 index 0000000..0fa9abe --- /dev/null +++ b/src/settings/triggers/motion.cpp @@ -0,0 +1,81 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#include "./motion.h" +#include +#include +#include "../../debug.h" +#include "../../global.h" + + +void MotionTriggerSettings::toJson(Print &print) +{ + DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(2) + 2*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5)); + + JsonObject& root = jsonBuffer.createObject(); + root["enabled"] = enabled(); + root["enabledDuringTimeTrigger"] = enabledDuringTimeTrigger(); + root["transitionTime"] = transitionTime(); + root["delay"] = delay(); + + JsonArray& jsonTriggers = root.createNestedArray("triggers"); + + for (uint8_t i = 0; i < triggerCount(); i++) + { + MotionTrigger* triggerItem = trigger(i); + + JsonObject& jsonTrigger = jsonTriggers.createNestedObject(); + jsonTrigger["pin"] = triggerItem->pin; + jsonTrigger["brightness"] = triggerItem->brightness; + jsonTrigger["direction"] = (uint8_t)triggerItem->direction; + jsonTrigger["enabled"] = triggerItem->enabled; + } + + root.printTo(print); +} + + +bool MotionTriggerSettings::fromJson(char* data, bool* changed) +{ + if (changed != nullptr) + *changed = false; + + DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(2) + 2*JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + 200); + JsonObject& root = jsonBuffer.parseObject(data); + + if (!root.success()) + return false; + + + enabled(root["enabled"]); + enabledDuringTimeTrigger(root["enabledDuringTimeTrigger"]); + transitionTime(root["transitionTime"]); + delay(root["delay"]); + + JsonArray& jsonTriggers = root["triggers"]; + if (mTriggers != nullptr) + delete [] mTriggers; + + mTriggerCount = jsonTriggers.size(); + mTriggers = new MotionTrigger[mTriggerCount]; + + + for (uint8_t i = 0; i < mTriggerCount; i++) + { + JsonObject& jsonTrigger = jsonTriggers[i]; + MotionTrigger* trigger = &mTriggers[i]; + + trigger->pin = jsonTrigger["pin"]; + trigger->brightness = jsonTrigger["brightness"]; + trigger->direction = (MotionDirection)(uint8_t)jsonTrigger["direction"]; + trigger->enabled = jsonTrigger["enabled"]; + } + + if (changed != nullptr) + *changed = true; + + return true; +} diff --git a/src/settings/triggers/motion.h b/src/settings/triggers/motion.h new file mode 100644 index 0000000..cda7176 --- /dev/null +++ b/src/settings/triggers/motion.h @@ -0,0 +1,67 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#ifndef __settingstriggersmotion +#define __settingstriggersmotion + +#include +#include "../../config.h" +#include "../abstractjson.h" + + +enum MotionDirection +{ + Nondirectional = 1, + TopDown = 2, + BottomUp = 3 +}; + + +struct MotionTrigger +{ + uint8_t pin; + uint8_t brightness; + MotionDirection direction; + bool enabled; +}; + + +class MotionTriggerSettings : public AbstractJsonSettings +{ + private: + bool mEnabled = false; + bool mEnabledDuringTimeTrigger = false; + uint16_t mTransitionTime = 0; + uint32_t mDelay = 0; + uint8_t mTriggerCount = 0; + MotionTrigger* mTriggers = nullptr; + + protected: + virtual const char* getFilename() { return MotionTriggerSettingsFile; }; + virtual const char* getDebugPrefix() { return "MotionTriggerSettings"; }; + + public: + void toJson(Print &print); + bool fromJson(char* data, bool* changed); + + + bool enabled() { return mEnabled; } + void enabled(bool value) { mEnabled = value; } + + bool enabledDuringTimeTrigger() { return mEnabledDuringTimeTrigger; } + void enabledDuringTimeTrigger(bool value) { mEnabledDuringTimeTrigger = value; } + + uint16_t transitionTime() { return mTransitionTime; } + void transitionTime(uint16_t value) { mTransitionTime = value; } + + uint32_t delay() { return mDelay; } + void delay(uint32_t value) { mDelay = value; } + + uint8_t triggerCount() { return mTriggerCount; } + MotionTrigger* trigger(uint8_t index) { return &mTriggers[index]; } +}; + +#endif \ No newline at end of file diff --git a/src/settings/triggers/time.cpp b/src/settings/triggers/time.cpp index dc21980..9e90ef6 100644 --- a/src/settings/triggers/time.cpp +++ b/src/settings/triggers/time.cpp @@ -4,9 +4,9 @@ * * https://git.x2software.net/pub/Stairs */ -#include "time.h" +#include "./time.h" #include -#include +#include #include "../../debug.h" #include "../../global.h" #include "../../praisethesun.h" @@ -46,63 +46,77 @@ DayOfWeek toDayOfWeek(timeDayOfWeek_t timeDay) } -struct Header + + +void TimeTriggerSettings::toJson(Print &print) { - uint8_t version; - uint8_t triggerCount; - uint16_t transitionTime; - bool enabled; -}; + DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(10) + JSON_OBJECT_SIZE(3) + 10*JSON_OBJECT_SIZE(5)); + JsonObject& root = jsonBuffer.createObject(); + root["enabled"] = enabled(); + root["transitionTime"] = transitionTime(); -void TimeTriggerSettings::read() -{ - _dln("TimeTriggerSettings :: Loading time triggers"); - File f = SPIFFS.open(TimeTriggerSettingsFile, "r"); - if (!f) - return; + JsonArray& jsonTriggers = root.createNestedArray("triggers"); - if (!f.available()) - return; + for (uint8_t i = 0; i < triggerCount(); i++) + { + TimeTrigger* triggerItem = trigger(i); - Header header; - f.readBytes((char*)&header, sizeof(Header)); + JsonObject& jsonTrigger = jsonTriggers.createNestedObject(); + jsonTrigger["time"] = triggerItem->time; + jsonTrigger["daysOfWeek"] = triggerItem->daysOfWeek; + jsonTrigger["brightness"] = triggerItem->brightness; + jsonTrigger["triggerType"] = (uint8_t)triggerItem->triggerType; + jsonTrigger["enabled"] = triggerItem->enabled; + } - if (header.version != 1) - return; - - mEnabled = header.enabled; - mTransitionTime = header.transitionTime; - beginSetTriggers(header.triggerCount); - - if (header.triggerCount > 0) - f.readBytes((char*)&mTriggers, header.triggerCount * sizeof(TimeTrigger)); - - endSetTriggers(); - - f.close(); + root.printTo(print); } -void TimeTriggerSettings::write() +bool TimeTriggerSettings::fromJson(char* data, bool* changed) { - _dln("TimeTriggerSettings :: Saving time triggers"); - File f = SPIFFS.open(TimeTriggerSettingsFile, "w"); - if (!f) - return; + if (changed != nullptr) + *changed = false; - Header header; - header.version = 1; - header.enabled = mEnabled; - header.transitionTime = mTransitionTime; - header.triggerCount = mTriggerCount; + DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(10) + JSON_OBJECT_SIZE(3) + 10*JSON_OBJECT_SIZE(5) + 270); + JsonObject& root = jsonBuffer.parseObject(data); - f.write((uint8_t*)&header, sizeof(Header)); - f.write((uint8_t*)&mTriggers, header.triggerCount * sizeof(TimeTrigger)); - f.close(); + if (!root.success()) + return false; + + + enabled(root["enabled"]); + transitionTime(root["transitionTime"]); + + JsonArray& jsonTriggers = root["triggers"]; + if (mTriggers != nullptr) + delete [] mTriggers; + + mTriggerCount = jsonTriggers.size(); + mTriggers = new TimeTrigger[mTriggerCount]; + + + for (uint8_t i = 0; i < mTriggerCount; i++) + { + JsonObject& jsonTrigger = jsonTriggers[i]; + TimeTrigger* trigger = &mTriggers[i]; + + trigger->time = jsonTrigger["time"]; + trigger->daysOfWeek = jsonTrigger["daysOfWeek"]; + trigger->brightness = jsonTrigger["brightness"]; + trigger->triggerType = (TimeTriggerType)(uint8_t)jsonTrigger["triggerType"]; + trigger->enabled = jsonTrigger["enabled"]; + } + + if (changed != nullptr) + *changed = true; + + return true; } + TimeTrigger* TimeTriggerSettings::getActiveTrigger(tmElements_t &time) { if (mTriggerCount == 0) @@ -176,55 +190,3 @@ TimeTrigger* TimeTriggerSettings::getActiveTrigger(tmElements_t &time) return nullptr; } - - -void TimeTriggerSettings::beginSetTriggers(uint8_t count) -{ - if (mTriggers != nullptr) - delete [] mTriggers; - - mTriggers = new TimeTrigger[count]; - mTriggerCount = count; -} - - -void TimeTriggerSettings::setTrigger(uint8_t index, TimeTrigger* value) -{ - memcpy(&mTriggers[index], value, sizeof(TimeTrigger)); -} - - -void TimeTriggerSettings::endSetTriggers() -{ - /* No need to sort, sunrise / sunset triggers already mess things up anyways - // Sort triggers by time of day - // Based on the Comb sort implementation by Rob Tillaart - // http://forum.arduino.cc/index.php?topic=280486.0 - uint8_t i, j; - uint8_t gap; - bool swapped = true; - TimeTrigger temp; - - gap = mTriggerCount; - while (gap > 1 || swapped) - { - if (gap > 1) - { - gap = gap * 10/13; - if (gap == 9 || gap == 10) gap = 11; - } - - swapped = false; - for (i = 0, j = gap; j < mTriggerCount; i++, j++) - { - if (mTriggers[i].time > mTriggers[j].time) - { - temp = mTriggers[i]; - mTriggers[i] = mTriggers[j]; - mTriggers[j] = temp; - swapped = true; - } - } - } - */ -} \ No newline at end of file diff --git a/src/settings/triggers/time.h b/src/settings/triggers/time.h index 766bb78..2747fdf 100644 --- a/src/settings/triggers/time.h +++ b/src/settings/triggers/time.h @@ -9,6 +9,8 @@ #include #include +#include "../../config.h" +#include "../abstractjson.h" enum DayOfWeek { @@ -44,7 +46,7 @@ struct TimeTrigger }; -class TimeTriggerSettings +class TimeTriggerSettings : public AbstractJsonSettings { private: bool mEnabled = false; @@ -52,9 +54,13 @@ class TimeTriggerSettings uint8_t mTriggerCount = 0; TimeTrigger* mTriggers = nullptr; + protected: + virtual const char* getFilename() { return ConnectionSettingsFile; }; + virtual const char* getDebugPrefix() { return "ConnectionSettings"; }; + public: - void read(); - void write(); + void toJson(Print &print); + bool fromJson(char* data, bool* changed); TimeTrigger* getActiveTrigger(tmElements_t &time); @@ -68,10 +74,6 @@ class TimeTriggerSettings uint8_t triggerCount() { return mTriggerCount; } TimeTrigger* trigger(uint8_t index) { return &mTriggers[index]; } - - void beginSetTriggers(uint8_t count); - void setTrigger(uint8_t index, TimeTrigger* value); - void endSetTriggers(); }; #endif \ No newline at end of file diff --git a/src/stairs.cpp b/src/stairs.cpp index b8dcdf8..8ff5b3f 100644 --- a/src/stairs.cpp +++ b/src/stairs.cpp @@ -1,8 +1,8 @@ -#include "stairs.h" +#include "./stairs.h" #include #include -#include "debug.h" -#include "global.h" +#include "./debug.h" +#include "./global.h" diff --git a/src/stairs.h b/src/stairs.h index b687b9d..324ec3f 100644 --- a/src/stairs.h +++ b/src/stairs.h @@ -1,9 +1,9 @@ #ifndef __Stairs #define __Stairs -#include "components/PCA9685.h" -#include "config.h" -#include "settings/steps.h" +#include "./components/PCA9685.h" +#include "./config.h" +#include "./settings/steps.h" struct Step {