/* * Stairs lighting * Copyright 2017 (c) Mark van Renswoude * * https://git.x2software.net/pub/Stairs */ #include #include #include #include extern "C" { #include } #include "config.h" #include "debug.h" #include "settings/connection.h" #include "global.h" #include "server/static.h" #include "server/api.h" ADC_MODE(ADC_VCC); // Forward declarations void initWiFi(); #ifdef SerialDebug void wifiEvent(WiFiEvent_t event); #endif void updateLED(); void startServer(); void stopServer(); void handleNotFound(AsyncWebServerRequest* request); AsyncWebServer server(80); bool accessPoint = false; bool stationMode = false; bool forceAccessPoint = false; uint32_t stationModeStart = 0; void setup() { _dinit(); currentTime = millis(); if (!SPIFFS.begin()) _dln("Setup :: failed to mount file system"); connectionSettings->read(); _dln("Setup :: initializing WiFi"); WiFi.persistent(false); WiFi.mode(WIFI_OFF); #ifdef SerialDebug // onEvent is already deprecated, but since I'm only using it // for debug purposes we'll see how long it lasts... WiFi.onEvent(wifiEvent); _d("WiFi :: MAC address: "); _dln(WiFi.macAddress()); #endif initWiFi(); _dln("Setup :: registering routes"); registerStaticRoutes(&server); registerAPIRoutes(&server); _dln("Setup :: starting HTTP server"); server.onNotFound(handleNotFound); server.begin(); } void loop() { currentTime = millis(); if (connectionSettingsChanged) { _dln("Loop :: connection settings changed"); initWiFi(); connectionSettingsChanged = false; } if (stationModeStart > 0) { bool isConnected = WiFi.status() == WL_CONNECTED; if (isConnected) { _d("WiFi :: connected, IP address: "); _dln(WiFi.localIP()); stationModeStart = 0; } else if (stationMode && accessPoint && currentTime - stationModeStart >= StationModeTimeout) { _dln("WiFi :: unable to connect, switching off station mode, status:"); _dln(WiFi.status()); #ifdef SerialDebug WiFi.printDiag(Serial); #endif // Connecting to access point is taking too long and is blocking // the access point mode, stop trying stationMode = false; WiFi.disconnect(); WiFi.mode(WIFI_AP); } } updateLED(); } void initWiFi() { WiFi.disconnect(); WiFi.softAPdisconnect(); accessPoint = connectionSettings->flag(AccessPoint) || forceAccessPoint; stationMode = connectionSettings->flag(StationMode) && connectionSettings->ssid() != NULL; WiFi.mode(accessPoint && stationMode ? WIFI_AP_STA : accessPoint ? WIFI_AP : stationMode ? WIFI_STA : WIFI_OFF); if (accessPoint) { _dln("WiFi :: starting access point"); String ssidString = DefaultAPSSIDPrefix + String(ESP.getChipId(), HEX); if (WiFi.softAP((const char *)ssidString.c_str())) { _d("WiFi :: IP address: "); _dln(WiFi.softAPIP()); } else _d("WiFi :: failed to start soft access point"); } if (stationMode) { _d("WiFi :: starting station mode to: "); _dln(connectionSettings->ssid()); stationModeStart = currentTime; if (WiFi.begin(connectionSettings->ssid(), connectionSettings->password())) { if (connectionSettings->flag(DHCP)) // I've had the same issue as described here with config(0, 0, 0): // https://stackoverflow.com/questions/40069654/how-to-clear-static-ip-configuration-and-start-dhcp wifi_station_dhcpc_start(); else WiFi.config(connectionSettings->ip(), connectionSettings->gateway(), connectionSettings->subnetMask()); } else _d("WiFi :: failed to start station mode"); } } #ifdef SerialDebug void wifiEvent(WiFiEvent_t event) { switch (event) { case WIFI_EVENT_STAMODE_CONNECTED: _dln("WiFi:: station mode: connected"); break; case WIFI_EVENT_STAMODE_DISCONNECTED: _dln("WiFi:: station mode: disconnected"); break; case WIFI_EVENT_STAMODE_AUTHMODE_CHANGE: _dln("WiFi:: station mode: authmode change"); break; case WIFI_EVENT_STAMODE_GOT_IP: _dln("WiFi:: station mode: got IP"); _dln(WiFi.localIP()); break; case WIFI_EVENT_STAMODE_DHCP_TIMEOUT: _dln("WiFi:: station mode: DHCP timeout"); break; case WIFI_EVENT_SOFTAPMODE_STACONNECTED: _dln("WiFi:: soft AP mode: station connected"); break; case WIFI_EVENT_SOFTAPMODE_STADISCONNECTED: _dln("WiFi:: soft AP mode: station disconnected"); break; } } #endif 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; 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); } */