Stairs/src/main.cpp

364 lines
7.3 KiB
C++

/*
* Stairs lighting
* Copyright 2017 (c) Mark van Renswoude
*/
#include <stdint.h>
#include <Stream.h>
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <ESP8266httpUpdate.h>
#include "config.h"
#include "protocol.h"
#include "components\PCA9685.h"
//#include "modes\adc.h"
#include "modes\alternate.h"
#include "modes\custom.h"
#include "modes\slide.h"
#include "modes\static.h"
#include "stairs.h"
#include "version.h"
PCA9685* pwmDriver;
Stairs* stairs;
WiFiUDP udpServer;
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.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;
while (WiFi.status() != WL_CONNECTED)
{
brightness += speed;
if (brightness <= 0 || brightness >= 1024)
speed = -speed;
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);
}