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)
- [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.

View File

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

View File

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

View File

@ -4,12 +4,12 @@
*
* https://git.x2software.net/pub/Stairs
*/
#include "charproperties.h"
#include "./charproperties.h"
#include <cstddef>
#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)
delete *field;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,11 +10,12 @@
#include <stdint.h>
#include <stdbool.h>
#include <IPAddress.h>
#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;

View File

@ -16,29 +16,28 @@ extern "C" {
#include <user_interface.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"
#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();
}
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,22 +527,28 @@ void checkTriggers()
{
_dln("Triggers :: start motion trigger");
if (activeMotionDirection == Nondirectional || motionTriggerSettings->transitionTime() == 0)
{
stairs->setAll(activeMotionBrightness, motionTriggerSettings->transitionTime(), 0);
}
else
{
// Start sweep
bool sweepDown = true;
uint8_t stepsCount = stepsSettings->count();
uint16_t offset = sweepDown ? 0 : motionTransitionTime;
uint16_t offsetIncrement = stepsCount > 0 ? motionTransitionTime / stepsCount : 0;
uint16_t offsetIncrement = stepsCount > 0 ? (motionTriggerSettings->transitionTime() / stepsCount) * 1.5 : 0;
uint16_t offset = activeMotionDirection == TopDown ? 0 : (stepsCount - 1) * offsetIncrement;
for (uint8_t step = 0; step < stepsCount; step++)
{
stairs->set(step, motionBrightness, motionTransitionTime, offset);
stairs->set(step, activeMotionBrightness, motionTriggerSettings->transitionTime(), offset);
if (sweepDown)
if (activeMotionDirection == TopDown)
offset += offsetIncrement;
else
offset -= offsetIncrement;
}
}
}
else
{
if (inTimeTrigger)
@ -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);
}
}
}

View File

@ -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 <math.h>

View File

@ -4,11 +4,11 @@
*
* https://git.x2software.net/pub/Stairs
*/
#include "api.h"
#include "./api.h"
#include <ArduinoJson.h>
#include <IPAddress.h>
#include <ESP8266WiFi.h>
#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))
{
request->send(400);
return;
}
JsonArray& jsonTriggers = root["triggers"];
if (jsonTriggers.size() > 255)
{
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();
if (changed)
timeTriggerSettingsChanged = true;
request->send(200);
}
else
request->send(400);
}
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);
}
@ -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);
}

View File

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

View File

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

View File

@ -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) {}

View File

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

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
*/
#include "connection.h"
#include "./connection.h"
#include <ArduinoJson.h>
#include <FS.h>
#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<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)
{
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)

View File

@ -10,7 +10,9 @@
#include <stdint.h>
#include <stdbool.h>
#include <IPAddress.h>
#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);

View File

@ -4,20 +4,11 @@
*
* https://git.x2software.net/pub/Stairs
*/
#include "steps.h"
#include <FS.h>
#include "./steps.h"
#include <ArduinoJson.h>
#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;
}

View File

@ -8,22 +8,29 @@
#define __settingssteps
#include <stdint.h>
#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; }

View File

@ -4,7 +4,7 @@
*
* https://git.x2software.net/pub/Stairs
*/
#include "system.h"
#include "./system.h"
#include <ArduinoJson.h>
#include <FS.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)
{
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)

View File

@ -11,9 +11,10 @@
#include <stdint.h>
#include <stdbool.h>
#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);

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
*/
#include "time.h"
#include "./time.h"
#include <string.h>
#include <FS.h>
#include <ArduinoJson.h>
#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;
}
}
}
*/
}

View File

@ -9,6 +9,8 @@
#include <Arduino.h>
#include <TimeLib.h>
#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

View File

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

View File

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