Implemented motion sensor settings

Moved all settings to JSON storage
This commit is contained in:
Mark van Renswoude 2018-01-14 21:31:37 +01:00
parent f6808c4833
commit 981d6ac1bb
32 changed files with 636 additions and 405 deletions

52
API.md
View File

@ -10,6 +10,8 @@
- [POST /api/steps](#post-apisteps) - [POST /api/steps](#post-apisteps)
- [GET /api/triggers/time](#get-apitriggerstime) - [GET /api/triggers/time](#get-apitriggerstime)
- [POST /api/triggers/time](#post-apitriggerstime) - [POST /api/triggers/time](#post-apitriggerstime)
- [GET /api/triggers/motion](#get-apitriggersmotion)
- [POST /api/triggers/motion](#post-apitriggersmotion)
- [POST /api/firmware](#post-apifirmware) - [POST /api/firmware](#post-apifirmware)
## GET /api/version ## 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. 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 ## POST /api/firmware
Uploads new firmware. The bin file should be posted as a multipart/form-data file attachment. Name is not relevant. Uploads new firmware. The bin file should be posted as a multipart/form-data file attachment. Name is not relevant.

View File

@ -45,7 +45,7 @@
#include <StreamString.h> #include <StreamString.h>
#include <base64.h> #include <base64.h>
#include "ESP8266HTTPClient-h4xx0red.h" #include "./ESP8266HTTPClient-h4xx0red.h"
class TransportTraits class TransportTraits
{ {

View File

@ -4,10 +4,10 @@
const uint8_t VersionMajor = 2; const uint8_t VersionMajor = 2;
const uint8_t VersionMinor = 0; const uint8_t VersionMinor = 0;
const uint8_t VersionPatch = 0; const uint8_t VersionPatch = 0;
const uint8_t VersionMetadata = 17; const uint8_t VersionMetadata = 18;
const char VersionBranch[] = "release/2.0"; const char VersionBranch[] = "release/2.0";
const char VersionSemVer[] = "2.0.0-beta.1"; 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"; const char VersionCommitDate[] = "2018-01-14";
#endif #endif

View File

@ -4,12 +4,12 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "charproperties.h" #include "./charproperties.h"
#include <cstddef> #include <cstddef>
#include <string.h> #include <string.h>
#include "debug.h" #include "./debug.h"
void CharProperties::assignChar(char** field, const char* newValue) void assignChar(char** field, const char* newValue)
{ {
if (*field != nullptr) if (*field != nullptr)
delete *field; delete *field;

View File

@ -9,10 +9,6 @@
#include <stdint.h> #include <stdint.h>
class CharProperties void assignChar(char** field, const char* newValue);
{
protected:
void assignChar(char** field, const char* newValue);
};
#endif #endif

View File

@ -3,7 +3,7 @@
* Copyright 2017 (c) Mark van Renswoude * Copyright 2017 (c) Mark van Renswoude
*/ */
#include <stdint.h> #include <stdint.h>
#include "PCA9685.h" #include "./PCA9685.h"
#include <Wire.h> #include <Wire.h>
#include <Arduino.h> #include <Arduino.h>

View File

@ -14,8 +14,9 @@ static const uint32_t SerialDebugStartupDelay = 2000;
static const char* ConnectionSettingsFile = "/connection.json"; static const char* ConnectionSettingsFile = "/connection.json";
static const char* SystemSettingsFile = "/system.json"; static const char* SystemSettingsFile = "/system.json";
static const char* StepSettingsFile = "/stepsettings.dat"; static const char* StepsSettingsFile = "/steps.json";
static const char* TimeTriggerSettingsFile = "/timetriggersettings.dat"; static const char* TimeTriggerSettingsFile = "/timetriggers.json";
static const char* MotionTriggerSettingsFile = "/motiontriggers.json";
static const char* DefaultAPSSIDPrefix = "Stairs-"; static const char* DefaultAPSSIDPrefix = "Stairs-";

View File

@ -4,7 +4,7 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "debug.h" #include "./debug.h"
void _dinit() void _dinit()

View File

@ -7,7 +7,7 @@
#ifndef __serialdebug #ifndef __serialdebug
#define __serialdebug #define __serialdebug
#include "config.h" #include "./config.h"
#include <Arduino.h> #include <Arduino.h>
void _dinit(); void _dinit();

View File

@ -4,7 +4,7 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "global.h" #include "./global.h"
ConnectionSettings* connectionSettings = new ConnectionSettings(); ConnectionSettings* connectionSettings = new ConnectionSettings();
bool connectionSettingsChanged = false; bool connectionSettingsChanged = false;
@ -18,6 +18,9 @@ bool stepsSettingsChanged = false;
TimeTriggerSettings* timeTriggerSettings = new TimeTriggerSettings(); TimeTriggerSettings* timeTriggerSettings = new TimeTriggerSettings();
bool timeTriggerSettingsChanged = false; bool timeTriggerSettingsChanged = false;
MotionTriggerSettings* motionTriggerSettings = new MotionTriggerSettings();
bool motionTriggerSettingsChanged = false;
Stairs* stairs; Stairs* stairs;

View File

@ -10,11 +10,12 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <IPAddress.h> #include <IPAddress.h>
#include "settings/connection.h" #include "./settings/connection.h"
#include "settings/system.h" #include "./settings/system.h"
#include "settings/steps.h" #include "./settings/steps.h"
#include "settings/triggers/time.h" #include "./settings/triggers/time.h"
#include "stairs.h" #include "./settings/triggers/motion.h"
#include "./stairs.h"
extern ConnectionSettings* connectionSettings; extern ConnectionSettings* connectionSettings;
extern bool connectionSettingsChanged; extern bool connectionSettingsChanged;
@ -28,6 +29,9 @@ extern bool stepsSettingsChanged;
extern TimeTriggerSettings* timeTriggerSettings; extern TimeTriggerSettings* timeTriggerSettings;
extern bool timeTriggerSettingsChanged; extern bool timeTriggerSettingsChanged;
extern MotionTriggerSettings* motionTriggerSettings;
extern bool motionTriggerSettingsChanged;
extern Stairs* stairs; extern Stairs* stairs;

View File

@ -16,29 +16,28 @@ extern "C" {
#include <user_interface.h> #include <user_interface.h>
} }
#include "config.h" #include "./config.h"
#include "debug.h" #include "./debug.h"
#include "global.h" #include "./global.h"
#include "components/PCA9685.h" #include "./components/PCA9685.h"
#include "settings/connection.h" #include "./settings/connection.h"
#include "server/static.h" #include "./server/static.h"
#include "server/settings.h" #include "./server/settings.h"
#include "server/firmware.h" #include "./server/firmware.h"
#include "server/api.h" #include "./server/api.h"
ADC_MODE(ADC_VCC); ADC_MODE(ADC_VCC);
// Forward declarations // Forward declarations
void initWiFi(); void initWiFi();
void initMotionPins();
#ifdef SerialDebug #ifdef SerialDebug
void wifiEvent(WiFiEvent_t event); void wifiEvent(WiFiEvent_t event);
void updateDebugStatus(); void updateDebugStatus();
#endif #endif
void updateLED(); void updateLED();
void updateNTPClient(); void updateNTPClient();
void updateTimeTrigger();
void checkTriggers(); void checkTriggers();
void handleNotFound(AsyncWebServerRequest* request); void handleNotFound(AsyncWebServerRequest* request);
@ -73,10 +72,12 @@ void setup()
systemSettings->read(); systemSettings->read();
stepsSettings->read(); stepsSettings->read();
timeTriggerSettings->read(); timeTriggerSettings->read();
motionTriggerSettings->read();
pinMode(systemSettings->pinAPButton(), INPUT_PULLUP); pinMode(systemSettings->pinAPButton(), INPUT_PULLUP);
pinMode(systemSettings->pinLEDAP(), OUTPUT); pinMode(systemSettings->pinLEDAP(), OUTPUT);
pinMode(systemSettings->pinLEDSTA(), OUTPUT); pinMode(systemSettings->pinLEDSTA(), OUTPUT);
initMotionPins();
_dln("Setup :: initializing PCA9685"); _dln("Setup :: initializing PCA9685");
@ -142,6 +143,12 @@ void loop()
ESP.restart(); ESP.restart();
} }
if (motionTriggerSettingsChanged)
{
initMotionPins();
motionTriggerSettingsChanged = false;
}
currentTime = millis(); currentTime = millis();
@ -191,12 +198,7 @@ void loop()
updateLED(); updateLED();
updateNTPClient(); updateNTPClient();
checkTriggers();
if (timeTriggerSettings->enabled() /*|| motionTriggerEnabled*/)
{
updateTimeTrigger();
checkTriggers();
}
stairs->tick(); 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 #ifdef SerialDebug
void wifiEvent(WiFiEvent_t event) void wifiEvent(WiFiEvent_t event)
{ {
@ -403,10 +419,20 @@ TimeTrigger* activeTimeTrigger = nullptr;
void updateTimeTrigger() void updateTimeTrigger()
{ {
if (ntpClient == nullptr || !timeTriggerSettings->enabled()) 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; return;
if (currentTime - lastTimeTriggerChecked < 10000)
return;
lastTimeTriggerChecked = currentTime; lastTimeTriggerChecked = currentTime;
_dln("Triggers:: updating time trigger"); _dln("Triggers:: updating time trigger");
@ -414,11 +440,12 @@ void updateTimeTrigger()
uint32_t epochTime = ntpClient->getEpochTime(); uint32_t epochTime = ntpClient->getEpochTime();
if (epochTime == 0) if (epochTime == 0)
{ {
activeTimeTrigger = nullptr;
_dln("Triggers:: time not synchronised yet"); _dln("Triggers:: time not synchronised yet");
return; return;
} }
// TODO compensate for UTC // TODO apply timezone offset
tmElements_t time; tmElements_t time;
breakTime(epochTime, 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() void checkTriggers()
{ {
if (!timeTriggerSettings->enabled() && activeTimeTrigger == nullptr &&
!motionTriggerSettings->enabled() && activeMotionStart == 0)
return;
updateTimeTrigger();
updateMotionTrigger();
bool inTimeTrigger = timeTriggerSettings->enabled() && bool inTimeTrigger = timeTriggerSettings->enabled() &&
activeTimeTrigger != nullptr && activeTimeTrigger != nullptr &&
activeTimeTrigger->brightness; activeTimeTrigger->brightness;
bool timeTriggerChanged = activeTimeTrigger != lastTimeTrigger; bool timeTriggerChanged = activeTimeTrigger != lastTimeTrigger;
lastTimeTrigger = activeTimeTrigger; lastTimeTrigger = activeTimeTrigger;
// TODO motion sensor settings bool inMotionTrigger = (activeMotionStart > 0) && (!inTimeTrigger || motionTriggerSettings->enabledDuringTimeTrigger());
bool inMotionTrigger = false; // && (alsoDuringTimeTrigger || !inTimeTrigger) bool motionChanged = (activeMotionStart > 0) != lastMotion;
bool motionChanged = false; lastMotion = (activeMotionStart > 0);
// TODO dummies, replace these with motionSettings-> values later on
uint16_t motionBrightness = 0;
uint16_t motionTransitionTime = 0;
if (!motionChanged && !timeTriggerChanged) if (!motionChanged && !timeTriggerChanged)
@ -462,20 +527,26 @@ void checkTriggers()
{ {
_dln("Triggers :: start motion trigger"); _dln("Triggers :: start motion trigger");
// Start sweep if (activeMotionDirection == Nondirectional || motionTriggerSettings->transitionTime() == 0)
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++)
{ {
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) for (uint8_t step = 0; step < stepsCount; step++)
offset += offsetIncrement; {
else stairs->set(step, activeMotionBrightness, motionTriggerSettings->transitionTime(), offset);
offset -= offsetIncrement;
if (activeMotionDirection == TopDown)
offset += offsetIncrement;
else
offset -= offsetIncrement;
}
} }
} }
else else
@ -485,14 +556,14 @@ void checkTriggers()
_dln("Triggers :: motion stopped, falling back to time trigger"); _dln("Triggers :: motion stopped, falling back to time trigger");
// Fall back to time trigger value // Fall back to time trigger value
stairs->setAll(activeTimeTrigger->brightness, motionTransitionTime, 0); stairs->setAll(activeTimeTrigger->brightness, motionTriggerSettings->transitionTime(), 0);
} }
else else
{ {
_dln("Triggers :: motion stopped, turning off"); _dln("Triggers :: motion stopped, turning off");
// No more motion, no active time trigger, turn off // No more motion, no active time trigger, turn off
stairs->setAll(0, motionTransitionTime, 0); stairs->setAll(0, motionTriggerSettings->transitionTime(), 0);
} }
} }
} }

View File

@ -7,7 +7,7 @@
* This code was ported over from Domoticz' SunRiseSet.cpp * This code was ported over from Domoticz' SunRiseSet.cpp
* https://github.com/domoticz/domoticz/ * https://github.com/domoticz/domoticz/
*/ */
#include "praisethesun.h" #include "./praisethesun.h"
#include <math.h> #include <math.h>

View File

@ -4,11 +4,11 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "api.h" #include "./api.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <IPAddress.h> #include <IPAddress.h>
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include "shared.h" #include "./shared.h"
#include "../assets/version.h" #include "../assets/version.h"
#include "../debug.h" #include "../debug.h"
#include "../global.h" #include "../global.h"
@ -69,30 +69,8 @@ void handleGetTimeTriggers(AsyncWebServerRequest *request)
{ {
_dln("API :: get time triggers"); _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"); AsyncResponseStream *response = request->beginResponseStream("application/json");
root.printTo(*response); timeTriggerSettings->toJson(*response);
request->send(response); request->send(response);
} }
@ -101,44 +79,47 @@ void handlePostTimeTriggers(AsyncWebServerRequest *request, uint8_t *data, size_
{ {
_dln("API :: post time triggers"); _dln("API :: post time triggers");
DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(10) + JSON_OBJECT_SIZE(3) + 10*JSON_OBJECT_SIZE(5) + 510); bool changed;
JsonObject& root = jsonBuffer.parseObject((char*)data); if (timeTriggerSettings->fromJson((char*)data, &changed))
if (!root.success())
{ {
timeTriggerSettings->write();
if (changed)
timeTriggerSettingsChanged = true;
request->send(200);
}
else
request->send(400); 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); 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_GET, handleGetTimeTriggers);
server->on("/api/triggers/time", HTTP_POST, devNullRequest, devNullFileUpload, handlePostTimeTriggers); 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);
} }

View File

@ -4,7 +4,7 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "firmware.h" #include "./firmware.h"
#include "../config.h" #include "../config.h"
#include "../debug.h" #include "../debug.h"
#include "../global.h" #include "../global.h"

View File

@ -4,11 +4,11 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "settings.h" #include "./settings.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <IPAddress.h> #include <IPAddress.h>
#include <ESP8266WiFi.h> #include <ESP8266WiFi.h>
#include "shared.h" #include "./shared.h"
#include "../assets/version.h" #include "../assets/version.h"
#include "../debug.h" #include "../debug.h"
#include "../global.h" #include "../global.h"

View File

@ -4,7 +4,7 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "shared.h" #include "./shared.h"
void devNullRequest(AsyncWebServerRequest *request) {} void devNullRequest(AsyncWebServerRequest *request) {}
void devNullFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {} void devNullFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) {}

View File

@ -4,7 +4,7 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "static.h" #include "./static.h"
#include "../debug.h" #include "../debug.h"
#include "../assets/html.h" #include "../assets/html.h"
#include "../assets/js.h" #include "../assets/js.h"

View File

@ -0,0 +1,70 @@
/*
* Stairs
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#include "./abstractjson.h"
#include <FS.h>
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<char[]> 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);
}

View File

@ -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

View File

@ -4,65 +4,15 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "connection.h" #include "./connection.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <FS.h> #include "./abstractjson.h"
#include "../debug.h" #include "../debug.h"
#include "../global.h"
#include "../config.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<char[]> 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) void ConnectionSettings::toJson(Print &print)
{ {
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(9)); 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) bool ConnectionSettings::fromJson(char* data, bool* changed)
{ {
if (changed != nullptr) if (changed != nullptr)

View File

@ -10,7 +10,9 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include <IPAddress.h> #include <IPAddress.h>
#include "./abstractjson.h"
#include "../charproperties.h" #include "../charproperties.h"
#include "../config.h"
enum ConnectionSettingsFlags enum ConnectionSettingsFlags
@ -21,7 +23,7 @@ enum ConnectionSettingsFlags
}; };
class ConnectionSettings : CharProperties class ConnectionSettings : public AbstractJsonSettings
{ {
private: private:
char* mHostname = nullptr; char* mHostname = nullptr;
@ -32,13 +34,12 @@ class ConnectionSettings : CharProperties
IPAddress mSubnetMask = (uint32_t)0; IPAddress mSubnetMask = (uint32_t)0;
IPAddress mGateway = (uint32_t)0; IPAddress mGateway = (uint32_t)0;
protected:
virtual const char* getFilename() { return ConnectionSettingsFile; };
virtual const char* getDebugPrefix() { return "ConnectionSettings"; };
public: public:
void read();
void write();
void toJson(Print &print); void toJson(Print &print);
bool fromJson(char* data);
bool fromJson(char* data, bool* changed); bool fromJson(char* data, bool* changed);

View File

@ -4,20 +4,11 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "steps.h" #include "./steps.h"
#include <FS.h> #include <ArduinoJson.h>
#include "../debug.h" #include "../debug.h"
struct Header
{
uint8_t version;
uint8_t stepCount;
bool useCurve;
};
StepsSettings::StepsSettings() StepsSettings::StepsSettings()
{ {
for (uint8_t i = 0; i < MaxStepCount; i++) 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"); DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(16) + JSON_OBJECT_SIZE(3));
File f = SPIFFS.open(StepSettingsFile, "r");
if (!f)
return;
if (!f.available()) JsonObject& root = jsonBuffer.createObject();
return; root["count"] = count();
root["useCurve"] = useCurve();
Header header; JsonArray& jsonCurveShift = root.createNestedArray("curveShift");
f.readBytes((char*)&header, sizeof(Header)); for (uint8_t step = 0; step < MaxStepCount; step++)
jsonCurveShift.add(curveShift(step));
if (header.version != 1) root.printTo(print);
return;
mUseCurve = (header.useCurve == 1);
f.readBytes((char*)&mCurveShift, header.stepCount * sizeof(uint16_t));
f.close();
} }
void StepsSettings::write() bool StepsSettings::fromJson(char* data, bool* changed)
{ {
_dln("StepsSettings :: Saving step settings"); if (changed != nullptr)
File f = SPIFFS.open(StepSettingsFile, "w"); *changed = false;
if (!f)
return;
Header header; DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(16) + JSON_OBJECT_SIZE(3) + 80);
header.version = 1; JsonObject& root = jsonBuffer.parseObject(data);
header.useCurve = mUseCurve;
header.stepCount = mCount;
f.write((uint8_t*)&header, sizeof(Header)); if (!root.success())
f.write((uint8_t*)&mCurveShift, header.stepCount * sizeof(uint16_t)); return false;
f.close();
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;
} }

View File

@ -8,22 +8,29 @@
#define __settingssteps #define __settingssteps
#include <stdint.h> #include <stdint.h>
#include "./abstractjson.h"
#include "../config.h"
#define MaxStepCount 16 #define MaxStepCount 16
class StepsSettings class StepsSettings : public AbstractJsonSettings
{ {
private: private:
uint8_t mCount = 16; uint8_t mCount = MaxStepCount;
bool mUseCurve = true; bool mUseCurve = true;
uint16_t mCurveShift[MaxStepCount]; uint16_t mCurveShift[MaxStepCount];
protected:
virtual const char* getFilename() { return StepsSettingsFile; };
virtual const char* getDebugPrefix() { return "StepsSettings"; };
public: public:
StepsSettings(); StepsSettings();
void read(); void toJson(Print &print);
void write(); bool fromJson(char* data, bool* changed);
uint8_t count() { return mCount; } uint8_t count() { return mCount; }
void count(uint8_t value) { mCount = value; } void count(uint8_t value) { mCount = value; }

View File

@ -4,7 +4,7 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "system.h" #include "./system.h"
#include <ArduinoJson.h> #include <ArduinoJson.h>
#include <FS.h> #include <FS.h>
#include "../debug.h" #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<char[]> 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) void SystemSettings::toJson(Print &print)
{ {
DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(6) + JSON_OBJECT_SIZE(5)); 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) bool SystemSettings::fromJson(char* data, bool* changed)
{ {
if (changed != nullptr) if (changed != nullptr)

View File

@ -11,9 +11,10 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h> #include <stdbool.h>
#include "../charproperties.h" #include "../charproperties.h"
#include "./abstractjson.h"
class SystemSettings : CharProperties class SystemSettings : public AbstractJsonSettings
{ {
private: private:
double mLatitude = 0; double mLatitude = 0;
@ -34,13 +35,12 @@ class SystemSettings : CharProperties
// TODO loginUsername // TODO loginUsername
// TODO loginPassword // TODO loginPassword
protected:
virtual const char* getFilename() { return SystemSettingsFile; };
virtual const char* getDebugPrefix() { return "SystemSettings"; };
public: public:
void read();
void write();
void toJson(Print &print); void toJson(Print &print);
bool fromJson(char* data);
bool fromJson(char* data, bool* changed); bool fromJson(char* data, bool* changed);

View File

@ -0,0 +1,81 @@
/*
* Stairs
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#include "./motion.h"
#include <string.h>
#include <ArduinoJson.h>
#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;
}

View File

@ -0,0 +1,67 @@
/*
* Stairs
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#ifndef __settingstriggersmotion
#define __settingstriggersmotion
#include <Arduino.h>
#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

View File

@ -4,9 +4,9 @@
* *
* https://git.x2software.net/pub/Stairs * https://git.x2software.net/pub/Stairs
*/ */
#include "time.h" #include "./time.h"
#include <string.h> #include <string.h>
#include <FS.h> #include <ArduinoJson.h>
#include "../../debug.h" #include "../../debug.h"
#include "../../global.h" #include "../../global.h"
#include "../../praisethesun.h" #include "../../praisethesun.h"
@ -46,63 +46,77 @@ DayOfWeek toDayOfWeek(timeDayOfWeek_t timeDay)
} }
struct Header
void TimeTriggerSettings::toJson(Print &print)
{ {
uint8_t version; DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(10) + JSON_OBJECT_SIZE(3) + 10*JSON_OBJECT_SIZE(5));
uint8_t triggerCount;
uint16_t transitionTime;
bool enabled;
};
JsonObject& root = jsonBuffer.createObject();
root["enabled"] = enabled();
root["transitionTime"] = transitionTime();
void TimeTriggerSettings::read() JsonArray& jsonTriggers = root.createNestedArray("triggers");
{
_dln("TimeTriggerSettings :: Loading time triggers");
File f = SPIFFS.open(TimeTriggerSettingsFile, "r");
if (!f)
return;
if (!f.available()) for (uint8_t i = 0; i < triggerCount(); i++)
return; {
TimeTrigger* triggerItem = trigger(i);
Header header; JsonObject& jsonTrigger = jsonTriggers.createNestedObject();
f.readBytes((char*)&header, sizeof(Header)); 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) root.printTo(print);
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();
} }
void TimeTriggerSettings::write() bool TimeTriggerSettings::fromJson(char* data, bool* changed)
{ {
_dln("TimeTriggerSettings :: Saving time triggers"); if (changed != nullptr)
File f = SPIFFS.open(TimeTriggerSettingsFile, "w"); *changed = false;
if (!f)
return;
Header header; DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(10) + JSON_OBJECT_SIZE(3) + 10*JSON_OBJECT_SIZE(5) + 270);
header.version = 1; JsonObject& root = jsonBuffer.parseObject(data);
header.enabled = mEnabled;
header.transitionTime = mTransitionTime;
header.triggerCount = mTriggerCount;
f.write((uint8_t*)&header, sizeof(Header)); if (!root.success())
f.write((uint8_t*)&mTriggers, header.triggerCount * sizeof(TimeTrigger)); return false;
f.close();
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) TimeTrigger* TimeTriggerSettings::getActiveTrigger(tmElements_t &time)
{ {
if (mTriggerCount == 0) if (mTriggerCount == 0)
@ -176,55 +190,3 @@ TimeTrigger* TimeTriggerSettings::getActiveTrigger(tmElements_t &time)
return nullptr; 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;
}
}
}
*/
}

View File

@ -9,6 +9,8 @@
#include <Arduino.h> #include <Arduino.h>
#include <TimeLib.h> #include <TimeLib.h>
#include "../../config.h"
#include "../abstractjson.h"
enum DayOfWeek enum DayOfWeek
{ {
@ -44,7 +46,7 @@ struct TimeTrigger
}; };
class TimeTriggerSettings class TimeTriggerSettings : public AbstractJsonSettings
{ {
private: private:
bool mEnabled = false; bool mEnabled = false;
@ -52,9 +54,13 @@ class TimeTriggerSettings
uint8_t mTriggerCount = 0; uint8_t mTriggerCount = 0;
TimeTrigger* mTriggers = nullptr; TimeTrigger* mTriggers = nullptr;
protected:
virtual const char* getFilename() { return ConnectionSettingsFile; };
virtual const char* getDebugPrefix() { return "ConnectionSettings"; };
public: public:
void read(); void toJson(Print &print);
void write(); bool fromJson(char* data, bool* changed);
TimeTrigger* getActiveTrigger(tmElements_t &time); TimeTrigger* getActiveTrigger(tmElements_t &time);
@ -68,10 +74,6 @@ class TimeTriggerSettings
uint8_t triggerCount() { return mTriggerCount; } uint8_t triggerCount() { return mTriggerCount; }
TimeTrigger* trigger(uint8_t index) { return &mTriggers[index]; } TimeTrigger* trigger(uint8_t index) { return &mTriggers[index]; }
void beginSetTriggers(uint8_t count);
void setTrigger(uint8_t index, TimeTrigger* value);
void endSetTriggers();
}; };
#endif #endif

View File

@ -1,8 +1,8 @@
#include "stairs.h" #include "./stairs.h"
#include <Math.h> #include <Math.h>
#include <FS.h> #include <FS.h>
#include "debug.h" #include "./debug.h"
#include "global.h" #include "./global.h"

View File

@ -1,9 +1,9 @@
#ifndef __Stairs #ifndef __Stairs
#define __Stairs #define __Stairs
#include "components/PCA9685.h" #include "./components/PCA9685.h"
#include "config.h" #include "./config.h"
#include "settings/steps.h" #include "./settings/steps.h"
struct Step struct Step
{ {