Reintroduced PCA9685 and Stairs classes

Compiles, but lacks the tick implementation
This commit is contained in:
Mark van Renswoude 2018-01-04 22:53:16 +01:00
parent 0aca40c71e
commit 42647f0db7
19 changed files with 491 additions and 621 deletions

View File

@ -55,3 +55,7 @@ To rebuild all the assets and compile or upload the source in one go, two tasks
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
### 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.

View File

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

View File

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

View File

@ -11,7 +11,7 @@
class PCA9685
{
private:
uint8_t address;
uint8_t mAddress;
protected:
uint8_t read(uint8_t registerAddress);

View File

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

View File

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

View File

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

View File

@ -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()
{
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);
}
*/
}
void handleNotFound(AsyncWebServerRequest *request)
{
_d("HTTP :: not found: "); _dln(request->url());
request->send(404);
}

View File

@ -1,29 +0,0 @@
#ifndef __Mode
#define __Mode
#include <stdint.h>
#include <Stream.h>
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

View File

@ -1,31 +0,0 @@
#ifndef __Protocol
#define __Protocol
#include <stdint.h>
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

View File

@ -8,21 +8,22 @@
#include <ArduinoJson.h>
#include <IPAddress.h>
#include <ESP8266WiFi.h>
#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);
DynamicJsonBuffer jsonBuffer(2*JSON_ARRAY_SIZE(17) + JSON_OBJECT_SIZE(3) + 130);
JsonObject& root = jsonBuffer.parseObject((char*)data);
if (!root.success())
{
request->send(400);
return;
}
uint16_t transitionTime = root["transitionTime"];
JsonArray& values = root["values"];
void handleGetConnection(AsyncWebServerRequest *request)
{
_dln("API :: get connection");
JsonArray& startTime = root["startTime"];
size_t startTimeCount = startTime.size();
AsyncResponseStream *response = request->beginResponseStream("application/json");
connectionSettings->toJson(*response);
request->send(response);
}
size_t valueCount = values.size();
if (valueCount > stepsSettings->count())
valueCount = stepsSettings->count();
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;
for (uint8_t step = 0; step < valueCount; step++)
stairs->set(step, values[step], transitionTime, step < startTimeCount ? 1 : 0);
request->send(200);
}
else
request->send(400);
}
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);
}

98
src/server/settings.cpp Normal file
View File

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

13
src/server/settings.h Normal file
View File

@ -0,0 +1,13 @@
/*
* Stairs
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#ifndef __server_settings
#define __server_settings
#include <ESPAsyncWebServer.h>
void registerSettingsRoutes(AsyncWebServer* server);
#endif

10
src/server/shared.cpp Normal file
View File

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

15
src/server/shared.h Normal file
View File

@ -0,0 +1,15 @@
/*
* Stairs
* Copyright 2017 (c) Mark van Renswoude
*
* https://git.x2software.net/pub/Stairs
*/
#ifndef __server_shared
#define __server_shared
#include <ESPAsyncWebServer.h>
void devNullRequest(AsyncWebServerRequest *request);
void devNullFileUpload(AsyncWebServerRequest *request, const String& filename, size_t index, uint8_t *data, size_t len, bool final);
#endif

65
src/settings/steps.cpp Normal file
View File

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

38
src/settings/steps.h Normal file
View File

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

View File

@ -2,153 +2,159 @@
#include <Math.h>
#include <FS.h>
#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
{
this->ranges[i].start = IStairs::Off;
this->ranges[i].end = IStairs::On;
}
uint16_t currentValue;
uint16_t startValue;
uint16_t targetValue;
uint16_t startTime;
uint16_t endTime;
};
*/
this->pwmDriver = pwmDriver;
_dln("Loading range configuration");
SPIFFS.begin();
this->readRange();
}
uint8_t Stairs::getCount()
void Stairs::tick()
{
return 0;//StepCount;
}
if (!mTick) return;
mTick = false;
uint32_t elapsedTime = mTransitionStart != 0 ? currentTime - mTransitionStart : 0;
void Stairs::set(uint8_t step, uint16_t brightness)
for (uint8_t step = 0; step < stepsSettings->count(); step++)
{
pwmDriver->setPWM(step, this->getPWMValue(step, brightness));
}
void Stairs::setAll(uint16_t brightness)
if (mStep[step].currentValue != mStep[step].targetValue)
{
//pwmDriver->setAll(this->getPWMValue(brightness));
// TODO more maths!
/*
for (uint8_t step = 0; step < StepCount; step++)
pwmDriver->setPWM(step, this->getPWMValue(step, brightness));
uint32_t diff = this->easeState == Up ? this->parameters.brightness - this->easeStartBrightness : this->easeStartBrightness - this->parameters.brightness;
uint32_t delta = (diff * elapsedTime) / this->parameters.easeTime;
this->currentBrightness = this->easeState == Up ? this->easeStartBrightness + delta : this->easeStartBrightness - delta;
if (elapsedTime >= this->parameters.easeTime)
{
this->currentBrightness = this->parameters.brightness;
this->easeState = None;
}
t /= d/2;
if (t < 1) return c/2*t*t + b;
t--;
return -c/2 * (t*(t-2) - 1) + b;
};
*/
}
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)
{
_dln("Full on/off, returning input");
return brightness;
}
if (step < 0 || step >= getCount())
{
_dln("Step out of bounds, returning input");
return brightness;
// TODO
// mPWMDriver->setPWM(step, this->getPWMValue(step, brightness));
}
Range* range = &this->ranges[step];
_d("Start: "); _dln(range->start);
_d("End: "); _dln(range->end);
if (this->useScaling)
uint8_t Stairs::get(uint8_t step, bool target)
{
_dln("Using scaling");
float factor = ((range->end - range->start) + 1) * factorBase;
brightness = pow(2, (brightness / factor)) - 1 + range->start;
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)
{
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;
}
_d("Output: "); _dln(brightness);
return brightness;
}
void Stairs::getRange(Stream* stream)
void Stairs::setAll(uint8_t brightness, uint16_t transitionTime, uint16_t startTime)
{
stream->write(this->useScaling ? 1 : 0);
stream->write((uint8_t*)&this->ranges, sizeof(this->ranges));
for (uint8_t step = 0; step < stepsSettings->count(); step++)
set(step, brightness, transitionTime, startTime);
}
void Stairs::setRange(uint8_t* data)
uint16_t Stairs::getPWMValue(uint8_t step, uint8_t brightness)
{
this->useScaling = *data;
data++;
memcpy(this->ranges, data, sizeof(this->ranges));
this->writeRange();
}
void Stairs::readRange()
//_d("Stairs :: Getting PWM value for step "); _d(step); _d(", brightness "); _dln(brightness);
if (brightness == 0 || brightness == 255)
{
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);
//_dln("Stairs :: Full on/off, returning input");
return brightness == 0 ? 0 : 4095;
}
void Stairs::writeRange()
uint16_t pwmValue;
if (stepsSettings->useCurve())
{
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();
//_dln("Stairs :: Using curve");
pwmValue = PWMCurve[brightness] + stepsSettings->curveShift(step);
}
else
{
//_dln("Stairs :: Not using curve");
pwmValue = brightness * LinearFactor;
}
//_d("Stairs :: Output: "); _dln(pwmValue);
return pwmValue;
}

View File

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