diff --git a/DEVELOPING.md b/DEVELOPING.md index 693ea78..9cdd25f 100644 --- a/DEVELOPING.md +++ b/DEVELOPING.md @@ -54,4 +54,8 @@ If you make any changes to the SCSS files, make sure to run ```gulp compileSass` To rebuild all the assets and compile or upload the source in one go, two tasks have been added to the gulpfile.js: 1. ```gulp build``` first runs all the tasks run by a regular ```gulp```, then builds the source code using ```platformio run``` -1. ```gulp upload``` is similar, but executes ```platformio run -t upload``` to directly upload the newly compiled source to the ESP8266 \ No newline at end of file +1. ```gulp upload``` is similar, but executes ```platformio run -t upload``` to directly upload the newly compiled source to the ESP8266 + +### version.h + +The version.h file is generated based on the current GitVersion, which means it changes if you build again right after committing, which causes a change that needs to be committed... [did you mean: recursion?](https://www.google.nl/search?q=recursion) The best way I found to deal with this is commit your changes, build, then [amend the commit](https://git-scm.com/book/en/v2/Git-Tools-Rewriting-History) with the updated version.h before pushing the changes. This ensures the version.h is in sync when cloning the repository. \ No newline at end of file diff --git a/src/assets/version.h b/src/assets/version.h index 5833678..baad0ab 100644 --- a/src/assets/version.h +++ b/src/assets/version.h @@ -4,11 +4,11 @@ const uint8_t VersionMajor = 2; const uint8_t VersionMinor = 0; const uint8_t VersionPatch = 0; -const uint8_t VersionMetadata = 3; +const uint8_t VersionMetadata = 4; const char VersionBranch[] = "release/2.0"; -const char VersionSha[] = "61d544f6f347c875e64b1e79985eeabd52ee1c9f"; +const char VersionSha[] = "176fa2a7bae0e9604a70c69da27b68819b479b73"; const char VersionSemVer[] = "2.0.0-beta.1"; -const char VersionFullSemVer[] = "2.0.0-beta.1+3"; -const char VersionCommitDate[] = "2018-01-03"; +const char VersionFullSemVer[] = "2.0.0-beta.1+4"; +const char VersionCommitDate[] = "2018-01-04"; #endif diff --git a/src/components/PCA9685.cpp b/src/components/PCA9685.cpp index 11e4621..fcc2f26 100644 --- a/src/components/PCA9685.cpp +++ b/src/components/PCA9685.cpp @@ -18,7 +18,7 @@ void PCA9685::setAddress(uint8_t address, uint8_t pinSDA, uint8_t pinSCL) void PCA9685::setAddress(uint8_t address) { - this->address = address; + this->mAddress = address; } @@ -26,11 +26,11 @@ uint8_t PCA9685::read(uint8_t registerAddress) { uint8_t result = 0; - Wire.beginTransmission(this->address); + Wire.beginTransmission(this->mAddress); Wire.write(registerAddress); Wire.endTransmission(); - Wire.requestFrom(this->address, (uint8_t)1); + Wire.requestFrom(this->mAddress, (uint8_t)1); if (Wire.available()) result = Wire.read(); @@ -40,7 +40,7 @@ uint8_t PCA9685::read(uint8_t registerAddress) void PCA9685::write(uint8_t registerAddress, uint8_t value) { - Wire.beginTransmission(this->address); + Wire.beginTransmission(this->mAddress); Wire.write(registerAddress); Wire.write(value); Wire.endTransmission(); @@ -97,7 +97,7 @@ void PCA9685::setPWM(uint8_t pin, uint16_t value) void PCA9685::setPWM(uint8_t pin, uint16_t on, uint16_t off) { - Wire.beginTransmission(this->address); + Wire.beginTransmission(this->mAddress); this->write(PCA9685::RegisterLED0OnL + (4 * pin)); this->write(on); this->write(on >> 8); @@ -124,7 +124,7 @@ void PCA9685::setAll(uint16_t value) void PCA9685::setAll(uint16_t on, uint16_t off) { - Wire.beginTransmission(this->address); + Wire.beginTransmission(this->mAddress); this->write(PCA9685::RegisterAllLEDOnL); this->write(on); this->write(on >> 8); diff --git a/src/components/PCA9685.h b/src/components/PCA9685.h index c459def..08dc822 100644 --- a/src/components/PCA9685.h +++ b/src/components/PCA9685.h @@ -11,7 +11,7 @@ class PCA9685 { private: - uint8_t address; + uint8_t mAddress; protected: uint8_t read(uint8_t registerAddress); diff --git a/src/config.h b/src/config.h index c0569d5..55ae8a6 100644 --- a/src/config.h +++ b/src/config.h @@ -14,7 +14,9 @@ static const char* DefaultAPSSIDPrefix = "Stairs-"; static const uint32_t StationModeTimeout = 30000; -/* +static const char* StepSettingsFile = "/stepsettings"; + + // Pins for the I2C bus static const uint8_t PinSDA = 13; static const uint8_t PinSCL = 12; @@ -23,6 +25,6 @@ static const uint8_t PinSCL = 12; // I2C address and PWM frequency of the PCA9685 board static const uint8_t PWMDriverAddress = 0x40; static const uint16_t PWMDriverPWMFrequency = 1600; -*/ + #endif \ No newline at end of file diff --git a/src/global.cpp b/src/global.cpp index e0af11a..f73addf 100644 --- a/src/global.cpp +++ b/src/global.cpp @@ -9,6 +9,12 @@ ConnectionSettings* connectionSettings = new ConnectionSettings(); bool connectionSettingsChanged = false; +StepsSettings* stepsSettings = new StepsSettings(); +bool stepsSettingsChanged = false; + + +Stairs* stairs; + bool shouldReboot = false; uint32_t currentTime; diff --git a/src/global.h b/src/global.h index 7d5657e..962b7d5 100644 --- a/src/global.h +++ b/src/global.h @@ -11,10 +11,17 @@ #include #include #include "settings/connection.h" +#include "settings/steps.h" +#include "stairs.h" extern ConnectionSettings* connectionSettings; extern bool connectionSettingsChanged; +extern StepsSettings* stepsSettings; +extern bool stepsSettingsChanged; + +extern Stairs* stairs; + extern bool shouldReboot; extern uint32_t currentTime; diff --git a/src/main.cpp b/src/main.cpp index c611dca..ed3fe7b 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -15,11 +15,13 @@ extern "C" { #include "config.h" #include "debug.h" -#include "settings/connection.h" #include "global.h" +#include "components/PCA9685.h" +#include "settings/connection.h" #include "server/static.h" -#include "server/api.h" +#include "server/settings.h" #include "server/firmware.h" +#include "server/api.h" ADC_MODE(ADC_VCC); @@ -37,6 +39,8 @@ void handleNotFound(AsyncWebServerRequest* request); AsyncWebServer server(80); +PCA9685* pwmDriver; + bool accessPoint = false; bool stationMode = false; bool forceAccessPoint = false; @@ -58,6 +62,34 @@ void setup() _dln("Setup :: failed to mount file system"); connectionSettings->read(); + stepsSettings->read(); + + + _dln("Setup :: initializing PCA9685"); + pwmDriver = new PCA9685(); + pwmDriver->setAddress(PWMDriverAddress, PinSDA, PinSCL); + pwmDriver->setPWMFrequency(PWMDriverPWMFrequency); + + _dln("Setup :: initializing Stairs"); + stairs = new Stairs(); + stairs->init(pwmDriver); + + + _dln("Setup :: starting initialization sequence"); + stairs->setAll(0); + + stairs->set(0, 255); + delay(300); + + uint8_t stepCount = stepsSettings->count(); + for (int step = 1; step < stepCount; step++) + { + stairs->set(step - 1, 0); + stairs->set(step, 255); + delay(300); + } + + stairs->set(stepCount - 1, 0); _dln("Setup :: initializing WiFi"); WiFi.persistent(false); @@ -75,8 +107,9 @@ void setup() _dln("Setup :: registering routes"); registerStaticRoutes(&server); - registerAPIRoutes(&server); + registerSettingsRoutes(&server); registerFirmwareRoutes(&server); + registerAPIRoutes(&server); _dln("Setup :: starting HTTP server"); server.onNotFound(handleNotFound); @@ -88,7 +121,7 @@ void loop() { if (shouldReboot) { - _dln("Reboot requested, bye bye!"); + _dln("Loop :: reboot requested, so long and thanks for all the fish!"); delay(100); ESP.restart(); } @@ -144,6 +177,7 @@ void loop() } updateLED(); + stairs->tick(); } @@ -229,80 +263,7 @@ void wifiEvent(WiFiEvent_t event) void updateLED() { - //while (WiFi.status() != WL_CONNECTED) - //{ - - //} -} - - -void handleNotFound(AsyncWebServerRequest *request) -{ - _d("HTTP :: not found: "); _dln(request->url()); - request->send(404); -} - - - -/* -uint8_t currentModeIdentifier; -IMode* currentMode; - - -// Forward declarations -void checkRequest(); -void handleRequest(uint8_t* packet); -IMode* createMode(uint8_t identifier); -void setCurrentMode(IMode *mode, uint8_t identifier); -void handleCurrentMode(); - - - - -void setup() -{ - #ifdef SerialDebug - Serial.begin(115200); - delay(5000); - #endif - - - _dln("Initializing PCA9685"); - _d("Version: "); - _dln(FirmwareVersion); - - pwmDriver = new PCA9685(); - pwmDriver->setAddress(PWMDriverAddress, PinSDA, PinSCL); - pwmDriver->setPWMFrequency(PWMDriverPWMFrequency); - - _dln("Initializing Stairs"); - - stairs = new Stairs(); - stairs->init(pwmDriver); - - _dln("Initializing WiFi"); - WiFi.mode(WIFI_STA); - WiFi.hostname(WiFiHostname); - WiFi.begin(WiFiSSID, WiFiPassword); - - - _dln("Starting initialization sequence"); - stairs->setAll(IStairs::Off); - stairs->set(0, IStairs::On); - delay(300); - - for (int step = 1; step < StepCount; step++) - { - stairs->set(step - 1, IStairs::Off); - stairs->set(step, IStairs::On); - delay(300); - } - - stairs->set(StepCount - 1, IStairs::Off); - - - _dln("Waiting for WiFi"); - + /* // Pulsate the bottom step while WiFi is connecting uint16_t brightness = 0; uint16_t speed = 16; @@ -316,273 +277,12 @@ void setup() stairs->set(0, brightness); delay(16); } - - setCurrentMode(new StaticMode(), Mode::Static); - - - _d("IP address: "); - _dln(WiFi.localIP()); - - _dln("Starting UDP server"); - - // Start the UDP server - udpServer.begin(UDPPort); + */ } -uint32_t currentTime; - -// Note: the packet size must at least be able to accomodate the -// command with the largest parameter list, there is no overflow -// checking in the mode classes! -const uint8_t maxPacketSize = 255; - -uint8_t packet[maxPacketSize]; -uint8_t* packetRef; - -void loop() +void handleNotFound(AsyncWebServerRequest *request) { - currentTime = millis(); - - checkRequest(); - handleCurrentMode(); -} - - -void checkRequest() -{ - int packetSize = udpServer.parsePacket(); - if (packetSize) - { - _dln("Handling incoming packet"); - - memset(packet, 0, sizeof(packet)); - int length = udpServer.read(packet, maxPacketSize - 1); - if (length && packet[0]) - { - packetRef = packet; - handleRequest(packetRef); - } - } -} - - -void handlePing(uint8_t* packet) -{ - _dln("Handling Ping"); - - udpServer.write(Command::Ping); - udpServer.write(StepCount); -} - - -void handleGetMode(uint8_t* packet) -{ - _dln("Handling GetMode"); - - udpServer.write(Command::GetMode); - udpServer.write(currentModeIdentifier); - currentMode->write(&udpServer); -} - - -void handleSetMode(uint8_t* packet) -{ - _dln("Handling SetMode"); - uint8_t newIdentifier = *packet; - packet++; - - IMode* newMode = createMode(newIdentifier); - - if (newMode != NULL) - { - newMode->read(packet); - - udpServer.write(Command::SetMode); - udpServer.write(newIdentifier); - newMode->write(&udpServer); - - _dln("Updating current mode"); - setCurrentMode(newMode, newIdentifier); - } - else - { - udpServer.write(Command::Error); - udpServer.write(Command::SetMode); - udpServer.write(newIdentifier); - } -} - - -void handleGetRange(uint8_t* packet) -{ - udpServer.write(Command::GetRange); - stairs->getRange(&udpServer); -} - - -void handleSetRange(uint8_t* packet) -{ - stairs->setRange(packet); - - udpServer.write(Command::SetRange); - stairs->getRange(&udpServer); - - currentMode->init(stairs, currentTime); -} - - -uint32_t lastUpdateCheck = 0; - -void handleUpdateFirmware(uint8_t* packet) -{ - _dln("Handling UpdateFirmware"); - - HTTPUpdateResult result; - - if (currentTime - lastUpdateCheck <= OTAUpdateThrottle) - { - udpServer.write(Command::Error); - udpServer.write(Command::UpdateFirmware); - udpServer.write((uint8_t)2); - return; - } - - lastUpdateCheck = currentTime; - - switch (OTAUpdateEnabled) - { - case 1: - _dln("Checking for update (fixed)"); - result = ESPhttpUpdate.update(OTAUpdateFixedHost, OTAUpdateFixedPort, OTAUpdateFixedPath, FirmwareVersion); - break; - - case 2: - { - _dln("Checking for update (client defined)"); - - uint16_t port; - memcpy(&port, packet, sizeof(port)); - packet += sizeof(port); - - _d("Port: "); - _dln(port); - - char host[255]; - char path[255]; - - strcpy(host, (char*)packet); - packet += strlen(host) + 1; - - strcpy(path, (char*)packet); - - _d("Host: "); - _dln(host); - _d("Path: "); - _dln(path); - - result = ESPhttpUpdate.update(host, port, path, FirmwareVersion); - break; - } - - default: - udpServer.write(Command::Error); - udpServer.write(Command::UpdateFirmware); - udpServer.write((uint8_t)0); - return; - } - - switch (result) - { - case HTTP_UPDATE_NO_UPDATES: - _dln("No updates"); - udpServer.write(Command::UpdateFirmware); - udpServer.write((uint8_t)0); - break; - - case HTTP_UPDATE_OK: - _dln("Update OK"); - udpServer.write(Command::UpdateFirmware); - udpServer.write((uint8_t)1); - break; - - default: - _d("Error while updating: "); - _dln(ESPhttpUpdate.getLastError()); - _dln(ESPhttpUpdate.getLastErrorString().c_str()); - - udpServer.write(Command::Error); - udpServer.write(Command::UpdateFirmware); - udpServer.write((uint8_t)2); - break; - } -} - - -void handleRequest(uint8_t* packet) -{ - _d("Handling request: "); - _dln(*packet); - - - // Every request will result in a reply, either containing the - // requested data or a copy of the input parameters for verification. - // - // Apparantly this also makes the ESP8266 more stable, as reports - // have been made that UDP communication can stall if no replies are sent. - udpServer.beginPacket(udpServer.remoteIP(), udpServer.remotePort()); - udpServer.write(Command::Reply); - - uint8_t command = *packet; - packet++; - - switch (command) - { - case Command::Ping: handlePing(packet); break; - case Command::GetMode: handleGetMode(packet); break; - case Command::SetMode: handleSetMode(packet); break; - case Command::GetRange: handleGetRange(packet); break; - case Command::SetRange: handleSetRange(packet); break; - case Command::UpdateFirmware: handleUpdateFirmware(packet); break; - - default: - udpServer.write(Command::Error); - udpServer.write(command); - break; - } - - udpServer.endPacket(); -} - - -IMode* createMode(uint8_t identifier) -{ - if (identifier == currentModeIdentifier) - return currentMode; - - switch (identifier) - { - case Mode::Static: return new StaticMode(); - case Mode::Custom: return new CustomMode(); - case Mode::Alternate: return new AlternateMode(); - //case Mode::Slide: return new SlideMode(); - //case Mode::ADC: return new ADCInputMode(); - } - - return NULL; -} - - -void setCurrentMode(IMode* mode, uint8_t identifier) -{ - currentModeIdentifier = identifier; - currentMode = mode; - currentMode->init(stairs, currentTime); -} - - -void handleCurrentMode() -{ - currentMode->tick(stairs, currentTime); -} -*/ \ No newline at end of file + _d("HTTP :: not found: "); _dln(request->url()); + request->send(404); +} \ No newline at end of file diff --git a/src/mode.h b/src/mode.h deleted file mode 100644 index fa8f43a..0000000 --- a/src/mode.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef __Mode -#define __Mode - -#include -#include - - -class IStairs -{ - public: - static const uint16_t Off = 0; - static const uint16_t On = 4095; - - virtual uint8_t getCount() = 0; - virtual void set(uint8_t step, uint16_t value) = 0; - virtual void setAll(uint16_t value) = 0; -}; - -class IMode -{ - public: - virtual void read(uint8_t* data) = 0; - virtual void write(Stream* stream) = 0; - - virtual void init(IStairs* stairs, uint32_t currentTime) = 0; - virtual void tick(IStairs* stairs, uint32_t currentTime) = 0; -}; - -#endif \ No newline at end of file diff --git a/src/protocol.h b/src/protocol.h deleted file mode 100644 index 4037b78..0000000 --- a/src/protocol.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef __Protocol -#define __Protocol - -#include - -class Command -{ - public: - static const uint8_t Error = 0x00; - - static const uint8_t Ping = 0x01; - static const uint8_t Reply = 0x02; - static const uint8_t GetMode = 0x03; - static const uint8_t SetMode = 0x04; - static const uint8_t GetRange = 0x05; - static const uint8_t SetRange = 0x06; - static const uint8_t UpdateFirmware = 0xFF; -}; - - -class Mode -{ - public: - static const uint8_t Static = 0x01; - static const uint8_t Custom = 0x02; - static const uint8_t Alternate = 0x03; - static const uint8_t Slide = 0x04; - //static const uint8_t ADC = 0x05; -}; - -#endif \ No newline at end of file diff --git a/src/server/api.cpp b/src/server/api.cpp index a8e743b..49c5097 100644 --- a/src/server/api.cpp +++ b/src/server/api.cpp @@ -8,21 +8,22 @@ #include #include #include +#include "shared.h" #include "../assets/version.h" #include "../debug.h" #include "../global.h" #include "../settings/connection.h" -void handleVersion(AsyncWebServerRequest *request) +void handleGetSteps(AsyncWebServerRequest *request) { - _dln("API :: version"); + _dln("API :: get steps"); - DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(2)); + DynamicJsonBuffer jsonBuffer(JSON_ARRAY_SIZE(17)); - JsonObject& root = jsonBuffer.createObject(); - root["systemID"] = String(ESP.getChipId(), HEX); - root["version"] = String(VersionFullSemVer) + " sha." + String(VersionSha); + JsonArray& root = jsonBuffer.createArray(); + for (uint8_t step = 0; step < stepsSettings->count(); step++) + root.add(stairs->get(step)); AsyncResponseStream *response = request->beginResponseStream("application/json"); root.printTo(*response); @@ -31,71 +32,38 @@ void handleVersion(AsyncWebServerRequest *request) } -void handleConnectionStatus(AsyncWebServerRequest *request) +void handlePostSteps(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) { - _dln("API :: connection status"); + _dln("API :: post steps"); - WiFiMode_t mode = WiFi.getMode(); - - - DynamicJsonBuffer jsonBuffer((2 * JSON_OBJECT_SIZE(2)) + JSON_OBJECT_SIZE(3)); - - JsonObject& root = jsonBuffer.createObject(); - JsonObject& ap = root.createNestedObject("ap"); - ap["enabled"] = (mode == WIFI_AP || mode == WIFI_AP_STA); - ap["ip"] = WiFi.softAPIP().toString(); - - JsonObject& station = root.createNestedObject("station"); - station["enabled"] = (mode == WIFI_STA || mode == WIFI_AP_STA); - station["status"] = (uint8_t)WiFi.status(); - station["ip"] = WiFi.localIP().toString(); - - AsyncResponseStream *response = request->beginResponseStream("application/json"); - root.printTo(*response); - - request->send(response); -} - - -void handleGetConnection(AsyncWebServerRequest *request) -{ - _dln("API :: get connection"); - - AsyncResponseStream *response = request->beginResponseStream("application/json"); - connectionSettings->toJson(*response); - request->send(response); -} - - -void handlePostConnection(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) -{ - _dln("API :: post connection"); - - bool changed; - if (connectionSettings->fromJson((char*)data, &changed)) + DynamicJsonBuffer jsonBuffer(2*JSON_ARRAY_SIZE(17) + JSON_OBJECT_SIZE(3) + 130); + JsonObject& root = jsonBuffer.parseObject((char*)data); + if (!root.success()) { - connectionSettings->write(); - - if (changed) - connectionSettingsChanged = true; - - request->send(200); - } - else request->send(400); + return; + } + + uint16_t transitionTime = root["transitionTime"]; + JsonArray& values = root["values"]; + + JsonArray& startTime = root["startTime"]; + size_t startTimeCount = startTime.size(); + + size_t valueCount = values.size(); + if (valueCount > stepsSettings->count()) + valueCount = stepsSettings->count(); + + + for (uint8_t step = 0; step < valueCount; step++) + stairs->set(step, values[step], transitionTime, step < startTimeCount ? 1 : 0); + + request->send(200); } -void devNullRequest(AsyncWebServerRequest *request) { } -void devNullFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final) { } - - void registerAPIRoutes(AsyncWebServer* server) { - server->on("/api/version", HTTP_GET, handleVersion); - - server->on("/api/connection/status", HTTP_GET, handleConnectionStatus); - - server->on("/api/connection", HTTP_GET, handleGetConnection); - server->on("/api/connection", HTTP_POST, devNullRequest, devNullFileUpload, handlePostConnection); + server->on("/api/steps", HTTP_GET, handleGetSteps); + server->on("/api/steps", HTTP_POST, devNullRequest, devNullFileUpload, handlePostSteps); } \ No newline at end of file diff --git a/src/server/settings.cpp b/src/server/settings.cpp new file mode 100644 index 0000000..4b0089b --- /dev/null +++ b/src/server/settings.cpp @@ -0,0 +1,98 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#include "settings.h" +#include +#include +#include +#include "shared.h" +#include "../assets/version.h" +#include "../debug.h" +#include "../global.h" +#include "../settings/connection.h" + + +void handleVersion(AsyncWebServerRequest *request) +{ + _dln("API :: version"); + + DynamicJsonBuffer jsonBuffer(JSON_OBJECT_SIZE(2)); + + JsonObject& root = jsonBuffer.createObject(); + root["systemID"] = String(ESP.getChipId(), HEX); + root["version"] = String(VersionFullSemVer) + " sha." + String(VersionSha); + + AsyncResponseStream *response = request->beginResponseStream("application/json"); + root.printTo(*response); + + request->send(response); +} + + +void handleConnectionStatus(AsyncWebServerRequest *request) +{ + _dln("API :: connection status"); + + WiFiMode_t mode = WiFi.getMode(); + + + DynamicJsonBuffer jsonBuffer((2 * JSON_OBJECT_SIZE(2)) + JSON_OBJECT_SIZE(3)); + + JsonObject& root = jsonBuffer.createObject(); + JsonObject& ap = root.createNestedObject("ap"); + ap["enabled"] = (mode == WIFI_AP || mode == WIFI_AP_STA); + ap["ip"] = WiFi.softAPIP().toString(); + + JsonObject& station = root.createNestedObject("station"); + station["enabled"] = (mode == WIFI_STA || mode == WIFI_AP_STA); + station["status"] = (uint8_t)WiFi.status(); + station["ip"] = WiFi.localIP().toString(); + + AsyncResponseStream *response = request->beginResponseStream("application/json"); + root.printTo(*response); + + request->send(response); +} + + +void handleGetConnection(AsyncWebServerRequest *request) +{ + _dln("API :: get connection"); + + AsyncResponseStream *response = request->beginResponseStream("application/json"); + connectionSettings->toJson(*response); + request->send(response); +} + + +void handlePostConnection(AsyncWebServerRequest *request, uint8_t *data, size_t len, size_t index, size_t total) +{ + _dln("API :: post connection"); + + bool changed; + if (connectionSettings->fromJson((char*)data, &changed)) + { + connectionSettings->write(); + + if (changed) + connectionSettingsChanged = true; + + request->send(200); + } + else + request->send(400); +} + + +void registerSettingsRoutes(AsyncWebServer* server) +{ + server->on("/api/version", HTTP_GET, handleVersion); + + server->on("/api/connection/status", HTTP_GET, handleConnectionStatus); + + server->on("/api/connection", HTTP_GET, handleGetConnection); + server->on("/api/connection", HTTP_POST, devNullRequest, devNullFileUpload, handlePostConnection); +} \ No newline at end of file diff --git a/src/server/settings.h b/src/server/settings.h new file mode 100644 index 0000000..579a4bd --- /dev/null +++ b/src/server/settings.h @@ -0,0 +1,13 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#ifndef __server_settings +#define __server_settings +#include + +void registerSettingsRoutes(AsyncWebServer* server); + +#endif \ No newline at end of file diff --git a/src/server/shared.cpp b/src/server/shared.cpp new file mode 100644 index 0000000..6fbfd50 --- /dev/null +++ b/src/server/shared.cpp @@ -0,0 +1,10 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#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/shared.h b/src/server/shared.h new file mode 100644 index 0000000..a74b1ec --- /dev/null +++ b/src/server/shared.h @@ -0,0 +1,15 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#ifndef __server_shared +#define __server_shared + +#include + +void devNullRequest(AsyncWebServerRequest *request); +void devNullFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final); + +#endif \ No newline at end of file diff --git a/src/settings/steps.cpp b/src/settings/steps.cpp new file mode 100644 index 0000000..18e0efd --- /dev/null +++ b/src/settings/steps.cpp @@ -0,0 +1,65 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#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++) + mCurveShift[i] = 0; +} + + +void StepsSettings::read() +{ + _dln("StepsSettings :: Loading step settings"); + File f = SPIFFS.open(StepSettingsFile, "r"); + if (!f) + return; + + if (!f.available()) + return; + + Header header; + f.readBytes((char*)&header, sizeof(Header)); + + if (header.version != 1) + return; + + mUseCurve = (header.useCurve == 1); + f.readBytes((char*)&mCurveShift, header.stepCount * sizeof(uint16_t)); + f.close(); +} + + +void StepsSettings::write() +{ + _dln("StepsSettings :: Saving step settings"); + File f = SPIFFS.open(StepSettingsFile, "w"); + if (!f) + return; + + Header header; + header.version = 1; + header.useCurve = mUseCurve; + header.stepCount = mCount; + + f.write((uint8_t*)&header, sizeof(Header)); + f.write((uint8_t*)&mCurveShift, header.stepCount * sizeof(uint16_t)); + f.close(); +} \ No newline at end of file diff --git a/src/settings/steps.h b/src/settings/steps.h new file mode 100644 index 0000000..0b1687c --- /dev/null +++ b/src/settings/steps.h @@ -0,0 +1,38 @@ +/* + * Stairs + * Copyright 2017 (c) Mark van Renswoude + * + * https://git.x2software.net/pub/Stairs +*/ +#ifndef __settingssteps +#define __settingssteps + +#include + +#define MaxStepCount 16 + + +class StepsSettings +{ + private: + uint8_t mCount = 16; + bool mUseCurve = true; + uint16_t mCurveShift[MaxStepCount]; + + public: + StepsSettings(); + + void read(); + void write(); + + uint8_t count() { return mCount; } + void count(uint8_t value) { mCount = value; } + + bool useCurve() { return mUseCurve; } + void useCurve(bool value) { mUseCurve = value; } + + uint16_t curveShift(uint8_t step) { return step < MaxStepCount ? mCurveShift[step] : 0; } + uint16_t curveShift(uint8_t step, uint16_t value) { if (step < MaxStepCount) mCurveShift[step] = value; } +}; + +#endif \ No newline at end of file diff --git a/src/stairs.cpp b/src/stairs.cpp index 559a7ad..7442957 100644 --- a/src/stairs.cpp +++ b/src/stairs.cpp @@ -2,153 +2,159 @@ #include #include #include "debug.h" +#include "global.h" -const static float factorBase = log10(2) / log10(PCA9685::On); - - -struct Header +const uint16_t PWMCurve[] = { - uint8_t version; - uint8_t rangeCount; - bool useScaling; + 0, 10, 11, 12, 12, 13, 13, 14, 15, 16, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 26, 27, 29, 30, 32, 33, 35, 36, 38, 40, 42, 44, + 46, 48, 51, 53, 56, 58, 61, 64, 67, 70, 74, 77, 81, 85, 89, 93, + 97, 102, 107, 112, 117, 123, 129, 135, 141, 148, 155, 162, 169, 177, 186, 194, + 203, 213, 222, 233, 243, 254, 266, 278, 291, 304, 318, 332, 347, 362, 378, 395, + 412, 430, 449, 468, 488, 509, 531, 553, 576, 600, 625, 651, 677, 704, 733, 762, + 792, 823, 854, 887, 920, 955, 990, 1026, 1063, 1101, 1140, 1180, 1220, 1261, 1303, 1346, + 1389, 1433, 1478, 1523, 1569, 1615, 1662, 1709, 1757, 1805, 1853, 1901, 1950, 1999, 2048, 2096, + 2145, 2194, 2242, 2290, 2338, 2386, 2433, 2480, 2526, 2572, 2617, 2662, 2706, 2749, 2792, 2834, + 2875, 2915, 2955, 2994, 3032, 3069, 3105, 3140, 3175, 3208, 3241, 3272, 3303, 3333, 3362, 3391, + 3418, 3444, 3470, 3495, 3519, 3542, 3564, 3586, 3607, 3627, 3646, 3665, 3683, 3700, 3717, 3733, + 3748, 3763, 3777, 3791, 3804, 3817, 3829, 3841, 3852, 3862, 3873, 3882, 3892, 3901, 3909, 3918, + 3926, 3933, 3940, 3947, 3954, 3960, 3966, 3972, 3978, 3983, 3988, 3993, 3998, 4002, 4006, 4010, + 4014, 4018, 4021, 4025, 4028, 4031, 4034, 4037, 4039, 4042, 4044, 4047, 4049, 4051, 4053, 4055, + 4057, 4059, 4060, 4062, 4063, 4065, 4066, 4068, 4069, 4070, 4071, 4072, 4073, 4074, 4075, 4076, + 4077, 4078, 4079, 4079, 4080, 4081, 4082, 4082, 4083, 4083, 4084, 4084, 4085, 4085, 4090, 4095 }; +const float LinearFactor = 4095.0f / 255.0f; + void Stairs::init(PCA9685* pwmDriver) { - this->useScaling = false; + mPWMDriver = pwmDriver; + + memset(&mStep[0], 0, sizeof(mStep)); +} + /* - for (uint8_t i = 0; i < StepCount; i++) +struct Step +{ + uint16_t currentValue; + uint16_t startValue; + uint16_t targetValue; + uint16_t startTime; + uint16_t endTime; +}; +*/ + + + +void Stairs::tick() +{ + if (!mTick) return; + mTick = false; + + uint32_t elapsedTime = mTransitionStart != 0 ? currentTime - mTransitionStart : 0; + + for (uint8_t step = 0; step < stepsSettings->count(); step++) { - this->ranges[i].start = IStairs::Off; - this->ranges[i].end = IStairs::On; - } - */ + if (mStep[step].currentValue != mStep[step].targetValue) + { + // TODO more maths! - this->pwmDriver = pwmDriver; + /* + uint32_t diff = this->easeState == Up ? this->parameters.brightness - this->easeStartBrightness : this->easeStartBrightness - this->parameters.brightness; + uint32_t delta = (diff * elapsedTime) / this->parameters.easeTime; - _dln("Loading range configuration"); - SPIFFS.begin(); - this->readRange(); -} + this->currentBrightness = this->easeState == Up ? this->easeStartBrightness + delta : this->easeStartBrightness - delta; -uint8_t Stairs::getCount() -{ - return 0;//StepCount; -} - - -void Stairs::set(uint8_t step, uint16_t brightness) -{ - pwmDriver->setPWM(step, this->getPWMValue(step, brightness)); -} - - -void Stairs::setAll(uint16_t brightness) -{ - //pwmDriver->setAll(this->getPWMValue(brightness)); - -/* - for (uint8_t step = 0; step < StepCount; step++) - pwmDriver->setPWM(step, this->getPWMValue(step, brightness)); - */ -} - - -uint16_t Stairs::getPWMValue(uint8_t step, uint16_t brightness) -{ - _d("Getting PWM value for step "); _d(step); _d(", brightness "); _dln(brightness); - if (brightness == IStairs::Off || brightness == IStairs::On) + if (elapsedTime >= this->parameters.easeTime) { - _dln("Full on/off, returning input"); - return brightness; + this->currentBrightness = this->parameters.brightness; + this->easeState = None; } - if (step < 0 || step >= getCount()) - { - _dln("Step out of bounds, returning input"); - return brightness; + + + + t /= d/2; + if (t < 1) return c/2*t*t + b; + t--; + return -c/2 * (t*(t-2) - 1) + b; +}; + +*/ + } } - Range* range = &this->ranges[step]; - _d("Start: "); _dln(range->start); - _d("End: "); _dln(range->end); + // TODO + // mPWMDriver->setPWM(step, this->getPWMValue(step, brightness)); - if (this->useScaling) +} + + +uint8_t Stairs::get(uint8_t step, bool target) +{ + if (step >= MaxStepCount) return 0; + return target ? mStep[step].targetValue : mStep[step].currentValue; +} + + +void Stairs::set(uint8_t step, uint8_t brightness, uint16_t transitionTime, uint16_t startTime) +{ + if (step >= MaxStepCount) return; + if (mStep[step].currentValue == brightness) + return; + + // TODO continue transition if one is already going on + + mTick = true; + mStep[step].targetValue = brightness; + + if (transitionTime > 0) { - _dln("Using scaling"); - float factor = ((range->end - range->start) + 1) * factorBase; - brightness = pow(2, (brightness / factor)) - 1 + range->start; + mTransitionStart = currentTime; + mStep[step].startValue = mStep[step].currentValue; + mStep[step].startTime = startTime; + mStep[step].endTime = transitionTime; } else { - _dln("Not using scaling"); - if (brightness < range->start) brightness = range->start; - if (brightness > range->end) brightness = range->end; + mTransitionStart = 0; + } +} + + +void Stairs::setAll(uint8_t brightness, uint16_t transitionTime, uint16_t startTime) +{ + for (uint8_t step = 0; step < stepsSettings->count(); step++) + set(step, brightness, transitionTime, startTime); +} + + +uint16_t Stairs::getPWMValue(uint8_t step, uint8_t brightness) +{ + //_d("Stairs :: Getting PWM value for step "); _d(step); _d(", brightness "); _dln(brightness); + if (brightness == 0 || brightness == 255) + { + //_dln("Stairs :: Full on/off, returning input"); + return brightness == 0 ? 0 : 4095; } - _d("Output: "); _dln(brightness); - return brightness; -} + uint16_t pwmValue; + if (stepsSettings->useCurve()) + { + //_dln("Stairs :: Using curve"); + pwmValue = PWMCurve[brightness] + stepsSettings->curveShift(step); + } + else + { + //_dln("Stairs :: Not using curve"); + pwmValue = brightness * LinearFactor; + } - -void Stairs::getRange(Stream* stream) -{ - stream->write(this->useScaling ? 1 : 0); - stream->write((uint8_t*)&this->ranges, sizeof(this->ranges)); -} - - -void Stairs::setRange(uint8_t* data) -{ - this->useScaling = *data; - data++; - - memcpy(this->ranges, data, sizeof(this->ranges)); - this->writeRange(); -} - - -void Stairs::readRange() -{ - File f = SPIFFS.open("/range", "r"); - if (!f) - return; - - if (!f.available()) - return; - - Header header; - f.readBytes((char*)&header, sizeof(Header)); - - if (header.version != 1) - return; - - this->useScaling = (header.useScaling == 1); - f.readBytes((char*)&this->ranges, header.rangeCount * sizeof(Range)); - f.close(); - - _d("- useScaling: "); - _dln(this->useScaling); -} - - -void Stairs::writeRange() -{ - File f = SPIFFS.open("/range", "w"); - if (!f) - return; - - Header header; - header.version = 1; - header.useScaling = this->useScaling; - header.rangeCount = getCount(); - - f.write((uint8_t*)&header, sizeof(Header)); - f.write((uint8_t*)&this->ranges, sizeof(this->ranges)); - f.close(); + //_d("Stairs :: Output: "); _dln(pwmValue); + return pwmValue; } \ No newline at end of file diff --git a/src/stairs.h b/src/stairs.h index a61c6d0..58bfc12 100644 --- a/src/stairs.h +++ b/src/stairs.h @@ -3,39 +3,37 @@ #include "components/PCA9685.h" #include "config.h" -#include "mode.h" +#include "settings/steps.h" - -struct Range +struct Step { - uint16_t start; - uint16_t end; + uint16_t currentValue; + uint16_t startValue; + uint16_t targetValue; + uint16_t startTime; + uint16_t endTime; }; -class Stairs : public IStairs +class Stairs { private: - PCA9685* pwmDriver; + PCA9685* mPWMDriver; + Step mStep[MaxStepCount]; - bool useScaling; - Range ranges[16]; + uint32_t mTransitionStart; + bool mTick = false; protected: - void readRange(); - void writeRange(); - - uint16_t getPWMValue(uint8_t step, uint16_t brightness); + uint16_t getPWMValue(uint8_t step, uint8_t brightness); public: void init(PCA9685* pwmDriver); + void tick(); - uint8_t getCount(); - void set(uint8_t step, uint16_t brightness); - void setAll(uint16_t brightness); - - void getRange(Stream* stream); - void setRange(uint8_t* data); + uint8_t get(uint8_t step, bool target = true); + void set(uint8_t step, uint8_t brightness, uint16_t transitionTime = 0, uint16_t startTime = 0); + void setAll(uint8_t brightness, uint16_t transitionTime = 0, uint16_t startTime = 0); }; #endif \ No newline at end of file