/* * Stairs lighting * Copyright 2017 (c) Mark van Renswoude */ #include #include #include #include #include #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); }