2017-03-23 18:45:29 +00:00
|
|
|
/*
|
|
|
|
* Stairs lighting
|
|
|
|
* Copyright 2017 (c) Mark van Renswoude
|
2018-01-01 18:56:07 +00:00
|
|
|
*
|
|
|
|
* https://git.x2software.net/pub/Stairs
|
2017-03-23 18:45:29 +00:00
|
|
|
*/
|
2018-01-01 18:56:07 +00:00
|
|
|
#include <Arduino.h>
|
2017-03-23 18:45:29 +00:00
|
|
|
#include <ESP8266WiFi.h>
|
2018-01-01 18:56:07 +00:00
|
|
|
#include <ESPAsyncTCP.h>
|
|
|
|
#include <ESPAsyncWebServer.h>
|
2018-01-05 20:15:04 +00:00
|
|
|
#include <NTPClient.h>
|
|
|
|
#include <WiFiUdp.h>
|
|
|
|
#include <TimeLib.h>
|
2018-01-01 18:56:07 +00:00
|
|
|
|
|
|
|
extern "C" {
|
|
|
|
#include <user_interface.h>
|
|
|
|
}
|
2017-03-23 18:45:29 +00:00
|
|
|
|
|
|
|
#include "config.h"
|
2018-01-01 18:56:07 +00:00
|
|
|
#include "debug.h"
|
|
|
|
#include "global.h"
|
2018-01-04 21:53:16 +00:00
|
|
|
#include "components/PCA9685.h"
|
|
|
|
#include "settings/connection.h"
|
2018-01-01 18:56:07 +00:00
|
|
|
#include "server/static.h"
|
2018-01-04 21:53:16 +00:00
|
|
|
#include "server/settings.h"
|
2018-01-03 20:44:34 +00:00
|
|
|
#include "server/firmware.h"
|
2018-01-04 21:53:16 +00:00
|
|
|
#include "server/api.h"
|
2018-01-07 22:12:42 +00:00
|
|
|
#include "server/geocode.h"
|
2018-01-01 18:56:07 +00:00
|
|
|
|
|
|
|
|
|
|
|
ADC_MODE(ADC_VCC);
|
|
|
|
|
|
|
|
// Forward declarations
|
|
|
|
void initWiFi();
|
|
|
|
#ifdef SerialDebug
|
|
|
|
void wifiEvent(WiFiEvent_t event);
|
2018-01-05 20:15:04 +00:00
|
|
|
void updateDebugStatus();
|
2018-01-01 18:56:07 +00:00
|
|
|
#endif
|
|
|
|
void updateLED();
|
2018-01-05 20:15:04 +00:00
|
|
|
void updateNTPClient();
|
2018-01-01 18:56:07 +00:00
|
|
|
|
|
|
|
void startServer();
|
|
|
|
void stopServer();
|
|
|
|
void handleNotFound(AsyncWebServerRequest* request);
|
|
|
|
|
|
|
|
|
|
|
|
AsyncWebServer server(80);
|
2018-01-04 21:53:16 +00:00
|
|
|
PCA9685* pwmDriver;
|
|
|
|
|
2018-01-05 20:15:04 +00:00
|
|
|
WiFiUDP ntpUDP;
|
2018-01-07 22:12:42 +00:00
|
|
|
NTPClient* ntpClient = nullptr;
|
2018-01-05 20:15:04 +00:00
|
|
|
|
2018-01-01 18:56:07 +00:00
|
|
|
bool accessPoint = false;
|
|
|
|
bool stationMode = false;
|
|
|
|
bool forceAccessPoint = false;
|
|
|
|
|
|
|
|
uint32_t stationModeStart = 0;
|
2018-01-07 22:12:42 +00:00
|
|
|
uint32_t blinkOnTime = 0;
|
|
|
|
|
|
|
|
enum LEDState
|
|
|
|
{
|
|
|
|
Off,
|
|
|
|
BlinkLow,
|
|
|
|
BlinkHigh,
|
|
|
|
On
|
|
|
|
};
|
|
|
|
|
|
|
|
bool ledAP = false;
|
|
|
|
LEDState ledWiFi = Off;
|
2018-01-01 18:56:07 +00:00
|
|
|
|
2018-01-03 20:44:34 +00:00
|
|
|
#ifdef SerialDebug
|
2018-01-05 20:15:04 +00:00
|
|
|
uint32_t debugStatusTime = 0;
|
2018-01-03 20:44:34 +00:00
|
|
|
#endif
|
|
|
|
|
2018-01-01 18:56:07 +00:00
|
|
|
|
|
|
|
void setup()
|
|
|
|
{
|
|
|
|
_dinit();
|
2018-01-03 20:44:34 +00:00
|
|
|
|
2018-01-01 18:56:07 +00:00
|
|
|
currentTime = millis();
|
2018-01-07 22:12:42 +00:00
|
|
|
blinkOnTime = currentTime;
|
2018-01-01 18:56:07 +00:00
|
|
|
|
|
|
|
if (!SPIFFS.begin())
|
|
|
|
_dln("Setup :: failed to mount file system");
|
|
|
|
|
|
|
|
connectionSettings->read();
|
2018-01-04 21:53:16 +00:00
|
|
|
stepsSettings->read();
|
|
|
|
|
|
|
|
|
2018-01-07 22:12:42 +00:00
|
|
|
pinMode(PinAPButton, INPUT_PULLUP);
|
|
|
|
pinMode(PinLEDAP, OUTPUT);
|
|
|
|
pinMode(PinLEDSTA, OUTPUT);
|
|
|
|
|
|
|
|
|
2018-01-04 21:53:16 +00:00
|
|
|
_dln("Setup :: initializing PCA9685");
|
|
|
|
pwmDriver = new PCA9685();
|
|
|
|
pwmDriver->setAddress(PWMDriverAddress, PinSDA, PinSCL);
|
|
|
|
pwmDriver->setPWMFrequency(PWMDriverPWMFrequency);
|
2018-01-05 19:02:30 +00:00
|
|
|
pwmDriver->setAll(0);
|
2018-01-04 21:53:16 +00:00
|
|
|
|
|
|
|
_dln("Setup :: initializing Stairs");
|
|
|
|
stairs = new Stairs();
|
|
|
|
stairs->init(pwmDriver);
|
|
|
|
|
|
|
|
|
|
|
|
_dln("Setup :: starting initialization sequence");
|
|
|
|
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);
|
2018-01-01 18:56:07 +00:00
|
|
|
|
|
|
|
_dln("Setup :: initializing WiFi");
|
|
|
|
WiFi.persistent(false);
|
|
|
|
WiFi.mode(WIFI_OFF);
|
|
|
|
|
|
|
|
#ifdef SerialDebug
|
2018-01-05 15:43:14 +00:00
|
|
|
// onEvent is already deprecated, but since I'm only using it
|
|
|
|
// for debug purposes we'll see how long it lasts...
|
|
|
|
#pragma GCC diagnostic push
|
|
|
|
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
|
|
|
WiFi.onEvent(wifiEvent);
|
|
|
|
_d("WiFi :: MAC address: ");
|
|
|
|
_dln(WiFi.macAddress());
|
|
|
|
#pragma GCC diagnostic pop
|
2018-01-01 18:56:07 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
initWiFi();
|
|
|
|
|
|
|
|
_dln("Setup :: registering routes");
|
|
|
|
registerStaticRoutes(&server);
|
2018-01-04 21:53:16 +00:00
|
|
|
registerSettingsRoutes(&server);
|
2018-01-03 20:44:34 +00:00
|
|
|
registerFirmwareRoutes(&server);
|
2018-01-04 21:53:16 +00:00
|
|
|
registerAPIRoutes(&server);
|
2018-01-07 22:12:42 +00:00
|
|
|
registerGeocodeRoutes(&server);
|
2018-01-01 18:56:07 +00:00
|
|
|
|
|
|
|
_dln("Setup :: starting HTTP server");
|
|
|
|
server.onNotFound(handleNotFound);
|
|
|
|
server.begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void loop()
|
|
|
|
{
|
2018-01-03 20:44:34 +00:00
|
|
|
if (shouldReboot)
|
|
|
|
{
|
2018-01-04 21:53:16 +00:00
|
|
|
_dln("Loop :: reboot requested, so long and thanks for all the fish!");
|
2018-01-03 20:44:34 +00:00
|
|
|
delay(100);
|
|
|
|
ESP.restart();
|
|
|
|
}
|
|
|
|
|
2018-01-01 18:56:07 +00:00
|
|
|
currentTime = millis();
|
|
|
|
|
2018-01-03 20:44:34 +00:00
|
|
|
|
|
|
|
#ifdef SerialDebug
|
2018-01-05 20:15:04 +00:00
|
|
|
updateDebugStatus();
|
2018-01-03 20:44:34 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
2018-01-01 18:56:07 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-07 22:12:42 +00:00
|
|
|
// TODO check AP button
|
2018-01-01 18:56:07 +00:00
|
|
|
updateLED();
|
2018-01-05 20:15:04 +00:00
|
|
|
updateNTPClient();
|
2018-01-07 22:12:42 +00:00
|
|
|
|
|
|
|
// TODO check for triggers every 10 seconds or so
|
|
|
|
|
2018-01-04 21:53:16 +00:00
|
|
|
stairs->tick();
|
2018-01-01 18:56:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void initWiFi()
|
|
|
|
{
|
|
|
|
WiFi.disconnect();
|
|
|
|
WiFi.softAPdisconnect();
|
|
|
|
|
|
|
|
accessPoint = connectionSettings->flag(AccessPoint) || forceAccessPoint;
|
2018-01-07 22:12:42 +00:00
|
|
|
stationMode = connectionSettings->flag(StationMode) && connectionSettings->ssid() != nullptr;
|
2018-01-01 18:56:07 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2018-01-05 20:15:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
void updateDebugStatus()
|
|
|
|
{
|
|
|
|
if (currentTime - debugStatusTime < 5000) return;
|
|
|
|
debugStatusTime = currentTime;
|
|
|
|
|
|
|
|
|
|
|
|
_d("Status :: available heap: ");
|
|
|
|
_dln(ESP.getFreeHeap());
|
|
|
|
|
2018-01-07 22:12:42 +00:00
|
|
|
if (ntpClient != nullptr)
|
2018-01-05 20:15:04 +00:00
|
|
|
{
|
|
|
|
_d("Status :: time: ");
|
|
|
|
uint32_t time = ntpClient->getEpochTime();
|
|
|
|
|
|
|
|
_d(day(time)); _d("-"); _d(month(time)); _d("-"); _d(year(time)); _d(" ");
|
|
|
|
_d(hour(time)); _d(":"); _d(minute(time)); _d(":"); _dln(second(time));
|
|
|
|
}
|
|
|
|
}
|
2018-01-01 18:56:07 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
void updateLED()
|
|
|
|
{
|
2018-01-07 22:12:42 +00:00
|
|
|
uint8_t value = (currentTime - blinkOnTime >= 1000) ? LOW : HIGH;
|
2017-03-23 18:45:29 +00:00
|
|
|
|
2018-01-07 22:12:42 +00:00
|
|
|
WiFiMode_t mode = WiFi.getMode();
|
|
|
|
if (mode == WIFI_AP_STA || mode == WIFI_AP)
|
2017-03-23 18:45:29 +00:00
|
|
|
{
|
2018-01-07 22:12:42 +00:00
|
|
|
if (!ledAP)
|
|
|
|
{
|
|
|
|
digitalWrite(PinLEDAP, HIGH);
|
|
|
|
ledAP = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (ledAP)
|
|
|
|
{
|
|
|
|
digitalWrite(PinLEDAP, LOW);
|
|
|
|
ledAP = false;
|
|
|
|
}
|
|
|
|
}
|
2017-03-23 18:45:29 +00:00
|
|
|
|
2018-01-07 22:12:42 +00:00
|
|
|
if (mode == WIFI_AP_STA || mode == WIFI_STA)
|
|
|
|
{
|
|
|
|
wl_status_t status = WiFi.status();
|
|
|
|
|
|
|
|
if (status == WL_CONNECTED)
|
|
|
|
{
|
|
|
|
if (ledWiFi != On)
|
|
|
|
{
|
|
|
|
digitalWrite(PinLEDSTA, HIGH);
|
|
|
|
ledWiFi = On;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
LEDState expectedState = value == HIGH ? BlinkHigh : BlinkLow;
|
|
|
|
if (ledWiFi != expectedState)
|
|
|
|
{
|
|
|
|
digitalWrite(PinLEDSTA, value);
|
|
|
|
ledWiFi = expectedState;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (ledWiFi != Off)
|
|
|
|
{
|
|
|
|
digitalWrite(PinLEDSTA, LOW);
|
|
|
|
ledWiFi = Off;
|
|
|
|
}
|
2017-03-23 18:45:29 +00:00
|
|
|
}
|
2018-01-07 22:12:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
if (currentTime - blinkOnTime >= 2000)
|
|
|
|
blinkOnTime = currentTime;
|
2017-03-23 18:45:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-05 20:15:04 +00:00
|
|
|
void updateNTPClient()
|
|
|
|
{
|
2018-01-07 22:12:42 +00:00
|
|
|
if (ntpClient == nullptr && WiFi.status() == WL_CONNECTED)
|
2018-01-05 20:15:04 +00:00
|
|
|
{
|
|
|
|
_dln("NTP :: initializing NTP client");
|
|
|
|
|
|
|
|
// TODO make NTP address and refresh interval configurable
|
|
|
|
ntpClient = new NTPClient(ntpUDP, "nl.pool.ntp.org", 0, 5 * 60 * 1000);
|
|
|
|
ntpClient->begin();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-07 22:12:42 +00:00
|
|
|
if (ntpClient != nullptr)
|
2018-01-05 20:15:04 +00:00
|
|
|
ntpClient->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-01-04 21:53:16 +00:00
|
|
|
void handleNotFound(AsyncWebServerRequest *request)
|
2017-03-23 18:45:29 +00:00
|
|
|
{
|
2018-01-04 21:53:16 +00:00
|
|
|
_d("HTTP :: not found: "); _dln(request->url());
|
|
|
|
request->send(404);
|
|
|
|
}
|