Implemented Access Point mode button
Split main.cpp in multiple files
This commit is contained in:
parent
981d6ac1bb
commit
85a60ea44e
1864
src/assets/js.h
1864
src/assets/js.h
File diff suppressed because it is too large
Load Diff
@ -4,10 +4,10 @@
|
||||
const uint8_t VersionMajor = 2;
|
||||
const uint8_t VersionMinor = 0;
|
||||
const uint8_t VersionPatch = 0;
|
||||
const uint8_t VersionMetadata = 18;
|
||||
const uint8_t VersionMetadata = 19;
|
||||
const char VersionBranch[] = "release/2.0";
|
||||
const char VersionSemVer[] = "2.0.0-beta.1";
|
||||
const char VersionFullSemVer[] = "2.0.0-beta.1+18";
|
||||
const char VersionCommitDate[] = "2018-01-14";
|
||||
const char VersionFullSemVer[] = "2.0.0-beta.1+19";
|
||||
const char VersionCommitDate[] = "2018-01-16";
|
||||
|
||||
#endif
|
||||
|
@ -25,5 +25,7 @@ static const char* DefaultAPSSIDPrefix = "Stairs-";
|
||||
// to the STA will block the AP)
|
||||
static const uint32_t StationModeTimeout = 30000;
|
||||
|
||||
static const uint16_t APButtonHoldTime = 5000;
|
||||
|
||||
|
||||
#endif
|
@ -28,5 +28,6 @@ bool shouldReboot = false;
|
||||
|
||||
uint32_t currentTime;
|
||||
|
||||
NTPClient* ntpClient = nullptr;
|
||||
|
||||
IPAddress emptyIP(0, 0, 0, 0);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <IPAddress.h>
|
||||
#include <NTPClient.h>
|
||||
#include "./settings/connection.h"
|
||||
#include "./settings/system.h"
|
||||
#include "./settings/steps.h"
|
||||
@ -39,6 +40,8 @@ extern bool shouldReboot;
|
||||
|
||||
extern uint32_t currentTime;
|
||||
|
||||
extern NTPClient* ntpClient;
|
||||
|
||||
extern IPAddress emptyIP;
|
||||
|
||||
#endif
|
455
src/main.cpp
455
src/main.cpp
@ -6,10 +6,9 @@
|
||||
*/
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESPAsyncTCP.h>
|
||||
//#include <ESPAsyncTCP.h>
|
||||
#include <WiFiUDP.h>
|
||||
#include <ESPAsyncWebServer.h>
|
||||
#include <NTPClient.h>
|
||||
#include <WiFiUdp.h>
|
||||
#include <TimeLib.h>
|
||||
|
||||
extern "C" {
|
||||
@ -26,44 +25,28 @@ extern "C" {
|
||||
#include "./server/firmware.h"
|
||||
#include "./server/api.h"
|
||||
|
||||
#include "./main.wifi.h"
|
||||
#include "./main.debug.h"
|
||||
#include "./main.led.h"
|
||||
#include "./main.triggers.h"
|
||||
|
||||
|
||||
ADC_MODE(ADC_VCC);
|
||||
|
||||
// Forward declarations
|
||||
void initWiFi();
|
||||
void initMotionPins();
|
||||
#ifdef SerialDebug
|
||||
void wifiEvent(WiFiEvent_t event);
|
||||
void updateDebugStatus();
|
||||
#endif
|
||||
void updateLED();
|
||||
void updateNTPClient();
|
||||
void checkTriggers();
|
||||
|
||||
// Forward declarations
|
||||
void handleNotFound(AsyncWebServerRequest* request);
|
||||
|
||||
|
||||
AsyncWebServer server(80);
|
||||
PCA9685* pwmDriver;
|
||||
|
||||
WiFiUDP ntpUDP;
|
||||
NTPClient* ntpClient = nullptr;
|
||||
|
||||
bool accessPoint = false;
|
||||
bool stationMode = false;
|
||||
bool forceAccessPoint = false;
|
||||
|
||||
uint32_t stationModeStart = 0;
|
||||
uint32_t blinkOnTime = 0;
|
||||
|
||||
|
||||
|
||||
void setup()
|
||||
{
|
||||
_dinit();
|
||||
|
||||
currentTime = millis();
|
||||
blinkOnTime = currentTime;
|
||||
|
||||
if (!SPIFFS.begin())
|
||||
_dln("Setup :: failed to mount file system");
|
||||
@ -109,17 +92,7 @@ void setup()
|
||||
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...
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
WiFi.onEvent(wifiEvent);
|
||||
_d("WiFi :: MAC address: ");
|
||||
_dln(WiFi.macAddress());
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
initDebug();
|
||||
initWiFi();
|
||||
|
||||
_dln("Setup :: registering routes");
|
||||
@ -149,12 +122,9 @@ void loop()
|
||||
motionTriggerSettingsChanged = false;
|
||||
}
|
||||
|
||||
|
||||
currentTime = millis();
|
||||
|
||||
|
||||
#ifdef SerialDebug
|
||||
updateDebugStatus();
|
||||
#endif
|
||||
|
||||
|
||||
if (connectionSettingsChanged)
|
||||
@ -165,37 +135,7 @@ void loop()
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO check AP button
|
||||
|
||||
updateWiFi();
|
||||
updateLED();
|
||||
updateNTPClient();
|
||||
checkTriggers();
|
||||
@ -204,379 +144,6 @@ void loop()
|
||||
}
|
||||
|
||||
|
||||
void initWiFi()
|
||||
{
|
||||
WiFi.disconnect();
|
||||
WiFi.softAPdisconnect();
|
||||
|
||||
accessPoint = connectionSettings->flag(AccessPoint) || forceAccessPoint;
|
||||
stationMode = connectionSettings->flag(StationMode) && connectionSettings->ssid() != nullptr;
|
||||
|
||||
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 (connectionSettings->hostname() != nullptr)
|
||||
WiFi.hostname(connectionSettings->hostname());
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void initMotionPins()
|
||||
{
|
||||
if (!motionTriggerSettings->enabled())
|
||||
return;
|
||||
|
||||
for (uint8_t i = 0; i < motionTriggerSettings->triggerCount(); i++)
|
||||
{
|
||||
MotionTrigger* trigger = motionTriggerSettings->trigger(i);
|
||||
if (trigger->enabled)
|
||||
pinMode(trigger->pin, INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t debugStatusTime = 0;
|
||||
|
||||
void updateDebugStatus()
|
||||
{
|
||||
if (currentTime - debugStatusTime < 5000) return;
|
||||
debugStatusTime = currentTime;
|
||||
|
||||
|
||||
_d("Status :: available heap: ");
|
||||
_dln(ESP.getFreeHeap());
|
||||
|
||||
if (ntpClient != nullptr)
|
||||
{
|
||||
_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));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
enum LEDState
|
||||
{
|
||||
Off,
|
||||
BlinkLow,
|
||||
BlinkHigh,
|
||||
On
|
||||
};
|
||||
|
||||
bool ledAP = false;
|
||||
LEDState ledWiFi = Off;
|
||||
|
||||
|
||||
void updateLED()
|
||||
{
|
||||
uint8_t value = (currentTime - blinkOnTime >= 1000) ? LOW : HIGH;
|
||||
|
||||
WiFiMode_t mode = WiFi.getMode();
|
||||
if (mode == WIFI_AP_STA || mode == WIFI_AP)
|
||||
{
|
||||
if (!ledAP)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDAP(), HIGH);
|
||||
ledAP = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ledAP)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDAP(), LOW);
|
||||
ledAP = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == WIFI_AP_STA || mode == WIFI_STA)
|
||||
{
|
||||
wl_status_t status = WiFi.status();
|
||||
|
||||
if (status == WL_CONNECTED)
|
||||
{
|
||||
if (ledWiFi != On)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDSTA(), HIGH);
|
||||
ledWiFi = On;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LEDState expectedState = value == HIGH ? BlinkHigh : BlinkLow;
|
||||
if (ledWiFi != expectedState)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDSTA(), value);
|
||||
ledWiFi = expectedState;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ledWiFi != Off)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDSTA(), LOW);
|
||||
ledWiFi = Off;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (currentTime - blinkOnTime >= 2000)
|
||||
blinkOnTime = currentTime;
|
||||
}
|
||||
|
||||
|
||||
void updateNTPClient()
|
||||
{
|
||||
if (ntpClient == nullptr && WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
_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();
|
||||
}
|
||||
|
||||
|
||||
if (ntpClient != nullptr)
|
||||
ntpClient->update();
|
||||
}
|
||||
|
||||
|
||||
uint32_t lastTimeTriggerChecked = 0;
|
||||
TimeTrigger* lastTimeTrigger = nullptr;
|
||||
TimeTrigger* activeTimeTrigger = nullptr;
|
||||
|
||||
|
||||
void updateTimeTrigger()
|
||||
{
|
||||
if (ntpClient == nullptr || !timeTriggerSettings->enabled())
|
||||
{
|
||||
activeTimeTrigger = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeTriggerSettingsChanged)
|
||||
{
|
||||
// Time trigger settings changed, activeTimeTrigger pointer is considered
|
||||
// invalid, force recheck
|
||||
timeTriggerSettingsChanged = false;
|
||||
}
|
||||
else if (currentTime - lastTimeTriggerChecked < 10000)
|
||||
return;
|
||||
|
||||
|
||||
lastTimeTriggerChecked = currentTime;
|
||||
_dln("Triggers:: updating time trigger");
|
||||
|
||||
uint32_t epochTime = ntpClient->getEpochTime();
|
||||
if (epochTime == 0)
|
||||
{
|
||||
activeTimeTrigger = nullptr;
|
||||
_dln("Triggers:: time not synchronised yet");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO apply timezone offset
|
||||
|
||||
tmElements_t time;
|
||||
breakTime(epochTime, time);
|
||||
|
||||
activeTimeTrigger = timeTriggerSettings->getActiveTrigger(time);
|
||||
|
||||
#ifdef SerialDebug
|
||||
_d("Triggers:: active time trigger: ");
|
||||
if (activeTimeTrigger != nullptr)
|
||||
_dln(activeTimeTrigger->time);
|
||||
else
|
||||
_dln("null");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
uint32_t activeMotionStart = 0;
|
||||
uint16_t activeMotionBrightness = 0;
|
||||
MotionDirection activeMotionDirection = Nondirectional;
|
||||
bool lastMotion = false;
|
||||
|
||||
|
||||
void updateMotionTrigger()
|
||||
{
|
||||
if (!motionTriggerSettings->enabled() || !motionTriggerSettings->triggerCount())
|
||||
{
|
||||
activeMotionStart = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < motionTriggerSettings->triggerCount(); i++)
|
||||
{
|
||||
MotionTrigger* trigger = motionTriggerSettings->trigger(i);
|
||||
|
||||
if (trigger->enabled && digitalRead(trigger->pin) == HIGH)
|
||||
{
|
||||
if (activeMotionStart == 0)
|
||||
{
|
||||
activeMotionDirection = trigger->direction;
|
||||
activeMotionBrightness = trigger->brightness;
|
||||
}
|
||||
|
||||
activeMotionStart = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentTime - activeMotionStart >= motionTriggerSettings->delay())
|
||||
activeMotionStart = 0;
|
||||
}
|
||||
|
||||
|
||||
void checkTriggers()
|
||||
{
|
||||
if (!timeTriggerSettings->enabled() && activeTimeTrigger == nullptr &&
|
||||
!motionTriggerSettings->enabled() && activeMotionStart == 0)
|
||||
return;
|
||||
|
||||
updateTimeTrigger();
|
||||
updateMotionTrigger();
|
||||
|
||||
bool inTimeTrigger = timeTriggerSettings->enabled() &&
|
||||
activeTimeTrigger != nullptr &&
|
||||
activeTimeTrigger->brightness;
|
||||
bool timeTriggerChanged = activeTimeTrigger != lastTimeTrigger;
|
||||
lastTimeTrigger = activeTimeTrigger;
|
||||
|
||||
bool inMotionTrigger = (activeMotionStart > 0) && (!inTimeTrigger || motionTriggerSettings->enabledDuringTimeTrigger());
|
||||
bool motionChanged = (activeMotionStart > 0) != lastMotion;
|
||||
lastMotion = (activeMotionStart > 0);
|
||||
|
||||
|
||||
if (!motionChanged && !timeTriggerChanged)
|
||||
return;
|
||||
|
||||
|
||||
if (motionChanged)
|
||||
{
|
||||
if (inMotionTrigger)
|
||||
{
|
||||
_dln("Triggers :: start motion trigger");
|
||||
|
||||
if (activeMotionDirection == Nondirectional || motionTriggerSettings->transitionTime() == 0)
|
||||
{
|
||||
stairs->setAll(activeMotionBrightness, motionTriggerSettings->transitionTime(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start sweep
|
||||
uint8_t stepsCount = stepsSettings->count();
|
||||
uint16_t offsetIncrement = stepsCount > 0 ? (motionTriggerSettings->transitionTime() / stepsCount) * 1.5 : 0;
|
||||
uint16_t offset = activeMotionDirection == TopDown ? 0 : (stepsCount - 1) * offsetIncrement;
|
||||
|
||||
for (uint8_t step = 0; step < stepsCount; step++)
|
||||
{
|
||||
stairs->set(step, activeMotionBrightness, motionTriggerSettings->transitionTime(), offset);
|
||||
|
||||
if (activeMotionDirection == TopDown)
|
||||
offset += offsetIncrement;
|
||||
else
|
||||
offset -= offsetIncrement;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inTimeTrigger)
|
||||
{
|
||||
_dln("Triggers :: motion stopped, falling back to time trigger");
|
||||
|
||||
// Fall back to time trigger value
|
||||
stairs->setAll(activeTimeTrigger->brightness, motionTriggerSettings->transitionTime(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dln("Triggers :: motion stopped, turning off");
|
||||
|
||||
// No more motion, no active time trigger, turn off
|
||||
stairs->setAll(0, motionTriggerSettings->transitionTime(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (timeTriggerChanged && !inMotionTrigger)
|
||||
{
|
||||
_dln("Triggers :: time trigger changed");
|
||||
|
||||
// Set to time trigger value
|
||||
stairs->setAll(activeTimeTrigger->brightness, timeTriggerSettings->transitionTime(), 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void handleNotFound(AsyncWebServerRequest *request)
|
||||
{
|
||||
_d("HTTP :: not found: "); _dln(request->url());
|
||||
|
74
src/main.debug.h
Normal file
74
src/main.debug.h
Normal file
@ -0,0 +1,74 @@
|
||||
#ifdef SerialDebug
|
||||
void wifiEvent(WiFiEvent_t event);
|
||||
|
||||
|
||||
void initDebug()
|
||||
{
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uint32_t debugStatusTime = 0;
|
||||
|
||||
void updateDebugStatus()
|
||||
{
|
||||
if (currentTime - debugStatusTime < 5000) return;
|
||||
debugStatusTime = currentTime;
|
||||
|
||||
|
||||
_d("Status :: available heap: ");
|
||||
_dln(ESP.getFreeHeap());
|
||||
|
||||
if (ntpClient != nullptr)
|
||||
{
|
||||
_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));
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#define initDebug() do { } while (0)
|
||||
#define updateDebugStatus() do { } while (0)
|
||||
|
||||
#endif
|
70
src/main.led.h
Normal file
70
src/main.led.h
Normal file
@ -0,0 +1,70 @@
|
||||
enum LEDState
|
||||
{
|
||||
Off,
|
||||
BlinkLow,
|
||||
BlinkHigh,
|
||||
On
|
||||
};
|
||||
|
||||
bool ledAP = false;
|
||||
LEDState ledWiFi = Off;
|
||||
uint32_t blinkOnTime = 0;
|
||||
|
||||
|
||||
void updateLED()
|
||||
{
|
||||
uint8_t value = (currentTime - blinkOnTime >= 1000) ? LOW : HIGH;
|
||||
|
||||
WiFiMode_t mode = WiFi.getMode();
|
||||
if (mode == WIFI_AP_STA || mode == WIFI_AP)
|
||||
{
|
||||
if (!ledAP)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDAP(), HIGH);
|
||||
ledAP = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ledAP)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDAP(), LOW);
|
||||
ledAP = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mode == WIFI_AP_STA || mode == WIFI_STA)
|
||||
{
|
||||
wl_status_t status = WiFi.status();
|
||||
|
||||
if (status == WL_CONNECTED)
|
||||
{
|
||||
if (ledWiFi != On)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDSTA(), HIGH);
|
||||
ledWiFi = On;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LEDState expectedState = value == HIGH ? BlinkHigh : BlinkLow;
|
||||
if (ledWiFi != expectedState)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDSTA(), value);
|
||||
ledWiFi = expectedState;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ledWiFi != Off)
|
||||
{
|
||||
digitalWrite(systemSettings->pinLEDSTA(), LOW);
|
||||
ledWiFi = Off;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (currentTime - blinkOnTime >= 2000)
|
||||
blinkOnTime = currentTime;
|
||||
}
|
197
src/main.triggers.h
Normal file
197
src/main.triggers.h
Normal file
@ -0,0 +1,197 @@
|
||||
WiFiUDP ntpUDP;
|
||||
|
||||
uint32_t lastTimeTriggerChecked = 0;
|
||||
TimeTrigger* lastTimeTrigger = nullptr;
|
||||
TimeTrigger* activeTimeTrigger = nullptr;
|
||||
|
||||
|
||||
void initMotionPins()
|
||||
{
|
||||
if (!motionTriggerSettings->enabled())
|
||||
return;
|
||||
|
||||
for (uint8_t i = 0; i < motionTriggerSettings->triggerCount(); i++)
|
||||
{
|
||||
MotionTrigger* trigger = motionTriggerSettings->trigger(i);
|
||||
if (trigger->enabled)
|
||||
pinMode(trigger->pin, INPUT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void updateNTPClient()
|
||||
{
|
||||
if (ntpClient == nullptr && WiFi.status() == WL_CONNECTED)
|
||||
{
|
||||
_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();
|
||||
}
|
||||
|
||||
|
||||
if (ntpClient != nullptr)
|
||||
ntpClient->update();
|
||||
}
|
||||
|
||||
|
||||
void updateTimeTrigger()
|
||||
{
|
||||
if (ntpClient == nullptr || !timeTriggerSettings->enabled())
|
||||
{
|
||||
activeTimeTrigger = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeTriggerSettingsChanged)
|
||||
{
|
||||
// Time trigger settings changed, activeTimeTrigger pointer is considered
|
||||
// invalid, force recheck
|
||||
timeTriggerSettingsChanged = false;
|
||||
}
|
||||
else if (currentTime - lastTimeTriggerChecked < 10000)
|
||||
return;
|
||||
|
||||
|
||||
lastTimeTriggerChecked = currentTime;
|
||||
_dln("Triggers:: updating time trigger");
|
||||
|
||||
uint32_t epochTime = ntpClient->getEpochTime();
|
||||
if (epochTime == 0)
|
||||
{
|
||||
activeTimeTrigger = nullptr;
|
||||
_dln("Triggers:: time not synchronised yet");
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO apply timezone offset
|
||||
|
||||
tmElements_t time;
|
||||
breakTime(epochTime, time);
|
||||
|
||||
activeTimeTrigger = timeTriggerSettings->getActiveTrigger(time);
|
||||
|
||||
#ifdef SerialDebug
|
||||
_d("Triggers:: active time trigger: ");
|
||||
if (activeTimeTrigger != nullptr)
|
||||
_dln(activeTimeTrigger->time);
|
||||
else
|
||||
_dln("null");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
uint32_t activeMotionStart = 0;
|
||||
uint16_t activeMotionBrightness = 0;
|
||||
MotionDirection activeMotionDirection = Nondirectional;
|
||||
bool lastMotion = false;
|
||||
|
||||
|
||||
void updateMotionTrigger()
|
||||
{
|
||||
if (!motionTriggerSettings->enabled() || !motionTriggerSettings->triggerCount())
|
||||
{
|
||||
activeMotionStart = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < motionTriggerSettings->triggerCount(); i++)
|
||||
{
|
||||
MotionTrigger* trigger = motionTriggerSettings->trigger(i);
|
||||
|
||||
if (trigger->enabled && digitalRead(trigger->pin) == HIGH)
|
||||
{
|
||||
if (activeMotionStart == 0)
|
||||
{
|
||||
activeMotionDirection = trigger->direction;
|
||||
activeMotionBrightness = trigger->brightness;
|
||||
}
|
||||
|
||||
activeMotionStart = currentTime;
|
||||
}
|
||||
}
|
||||
|
||||
if (currentTime - activeMotionStart >= motionTriggerSettings->delay())
|
||||
activeMotionStart = 0;
|
||||
}
|
||||
|
||||
|
||||
void checkTriggers()
|
||||
{
|
||||
if (!timeTriggerSettings->enabled() && activeTimeTrigger == nullptr &&
|
||||
!motionTriggerSettings->enabled() && activeMotionStart == 0)
|
||||
return;
|
||||
|
||||
updateTimeTrigger();
|
||||
updateMotionTrigger();
|
||||
|
||||
bool inTimeTrigger = timeTriggerSettings->enabled() &&
|
||||
activeTimeTrigger != nullptr &&
|
||||
activeTimeTrigger->brightness;
|
||||
bool timeTriggerChanged = activeTimeTrigger != lastTimeTrigger;
|
||||
lastTimeTrigger = activeTimeTrigger;
|
||||
|
||||
bool inMotionTrigger = (activeMotionStart > 0) && (!inTimeTrigger || motionTriggerSettings->enabledDuringTimeTrigger());
|
||||
bool motionChanged = (activeMotionStart > 0) != lastMotion;
|
||||
lastMotion = (activeMotionStart > 0);
|
||||
|
||||
|
||||
if (!motionChanged && !timeTriggerChanged)
|
||||
return;
|
||||
|
||||
|
||||
if (motionChanged)
|
||||
{
|
||||
if (inMotionTrigger)
|
||||
{
|
||||
_dln("Triggers :: start motion trigger");
|
||||
|
||||
if (activeMotionDirection == Nondirectional || motionTriggerSettings->transitionTime() == 0)
|
||||
{
|
||||
stairs->setAll(activeMotionBrightness, motionTriggerSettings->transitionTime(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Start sweep
|
||||
uint8_t stepsCount = stepsSettings->count();
|
||||
uint16_t offsetIncrement = stepsCount > 0 ? (motionTriggerSettings->transitionTime() / stepsCount) * 1.5 : 0;
|
||||
uint16_t offset = activeMotionDirection == TopDown ? 0 : (stepsCount - 1) * offsetIncrement;
|
||||
|
||||
for (uint8_t step = 0; step < stepsCount; step++)
|
||||
{
|
||||
stairs->set(step, activeMotionBrightness, motionTriggerSettings->transitionTime(), offset);
|
||||
|
||||
if (activeMotionDirection == TopDown)
|
||||
offset += offsetIncrement;
|
||||
else
|
||||
offset -= offsetIncrement;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (inTimeTrigger)
|
||||
{
|
||||
_dln("Triggers :: motion stopped, falling back to time trigger");
|
||||
|
||||
// Fall back to time trigger value
|
||||
stairs->setAll(activeTimeTrigger->brightness, motionTriggerSettings->transitionTime(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dln("Triggers :: motion stopped, turning off");
|
||||
|
||||
// No more motion, no active time trigger, turn off
|
||||
stairs->setAll(0, motionTriggerSettings->transitionTime(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (timeTriggerChanged && !inMotionTrigger)
|
||||
{
|
||||
_dln("Triggers :: time trigger changed");
|
||||
|
||||
// Set to time trigger value
|
||||
stairs->setAll(activeTimeTrigger->brightness, timeTriggerSettings->transitionTime(), 0);
|
||||
}
|
||||
}
|
121
src/main.wifi.h
Normal file
121
src/main.wifi.h
Normal file
@ -0,0 +1,121 @@
|
||||
bool accessPoint = false;
|
||||
bool stationMode = false;
|
||||
|
||||
uint32_t stationModeStart = 0;
|
||||
uint32_t apButtonStart = 0;
|
||||
|
||||
|
||||
void startAccessPoint();
|
||||
void startStationMode();
|
||||
|
||||
|
||||
void initWiFi()
|
||||
{
|
||||
WiFi.disconnect();
|
||||
WiFi.softAPdisconnect();
|
||||
|
||||
accessPoint = connectionSettings->flag(AccessPoint);
|
||||
stationMode = connectionSettings->flag(StationMode) && connectionSettings->ssid() != nullptr;
|
||||
|
||||
WiFi.mode(accessPoint && stationMode ? WIFI_AP_STA :
|
||||
accessPoint ? WIFI_AP :
|
||||
stationMode ? WIFI_STA :
|
||||
WIFI_OFF);
|
||||
|
||||
if (accessPoint)
|
||||
startAccessPoint();
|
||||
|
||||
if (stationMode)
|
||||
startStationMode();
|
||||
}
|
||||
|
||||
|
||||
void updateWiFi()
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!accessPoint)
|
||||
{
|
||||
if (digitalRead(systemSettings->pinAPButton()) == LOW)
|
||||
{
|
||||
if (apButtonStart == 0)
|
||||
apButtonStart = currentTime;
|
||||
else if (currentTime - apButtonStart >= APButtonHoldTime)
|
||||
{
|
||||
connectionSettings->flag(AccessPoint, true);
|
||||
connectionSettings->write();
|
||||
|
||||
startAccessPoint();
|
||||
apButtonStart = 0;
|
||||
}
|
||||
}
|
||||
else if (apButtonStart > 0)
|
||||
apButtonStart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void startAccessPoint()
|
||||
{
|
||||
_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");
|
||||
}
|
||||
|
||||
|
||||
void startStationMode()
|
||||
{
|
||||
_d("WiFi :: starting station mode to: ");
|
||||
_dln(connectionSettings->ssid());
|
||||
|
||||
stationModeStart = currentTime;
|
||||
|
||||
if (connectionSettings->hostname() != nullptr)
|
||||
WiFi.hostname(connectionSettings->hostname());
|
||||
|
||||
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");
|
||||
}
|
91
web/app.js
91
web/app.js
@ -96,20 +96,49 @@ function startApp()
|
||||
self.disableStepsChanged = false;
|
||||
self.savingStepsTimer = false;
|
||||
|
||||
axios.get('/api/version')
|
||||
.then(function(response)
|
||||
{
|
||||
if (typeof response.data == 'object')
|
||||
self.version = response.data;
|
||||
})
|
||||
.catch(function(error)
|
||||
{
|
||||
console.log(error);
|
||||
});
|
||||
|
||||
// TODO retrieve system settings
|
||||
axios.all([
|
||||
axios.get('/api/connection')
|
||||
// Sequential loading of all the settings makes sure
|
||||
// we don't overload the ESP8266 with requests, as that
|
||||
// can cause it to run out of memory easily.
|
||||
self.loadVersion().then(function()
|
||||
{
|
||||
self.loadConnection().then(function()
|
||||
{
|
||||
self.loadSystem().then(function()
|
||||
{
|
||||
self.loadTimeTriggers().then(function()
|
||||
{
|
||||
self.loadSteps().then(function()
|
||||
{
|
||||
self.stopLoadingIndicator();
|
||||
self.loading = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
loadVersion: function()
|
||||
{
|
||||
var self = this;
|
||||
return axios.get('/api/version')
|
||||
.then(function(response)
|
||||
{
|
||||
if (typeof response.data == 'object')
|
||||
self.version = response.data;
|
||||
})
|
||||
.catch(function(error)
|
||||
{
|
||||
console.log(error);
|
||||
});
|
||||
},
|
||||
|
||||
loadConnection: function()
|
||||
{
|
||||
var self = this;
|
||||
return axios.get('/api/connection')
|
||||
.then(function(response)
|
||||
{
|
||||
if (typeof response.data == 'object')
|
||||
@ -118,9 +147,13 @@ function startApp()
|
||||
.catch(function(error)
|
||||
{
|
||||
console.log(error);
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
axios.get('/api/system')
|
||||
loadSystem: function()
|
||||
{
|
||||
var self = this;
|
||||
return axios.get('/api/system')
|
||||
.then(function(response)
|
||||
{
|
||||
if (typeof response.data == 'object')
|
||||
@ -129,9 +162,13 @@ function startApp()
|
||||
.catch(function(error)
|
||||
{
|
||||
console.log(error);
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
axios.get('/api/triggers/time')
|
||||
loadTimeTriggers: function()
|
||||
{
|
||||
var self = this;
|
||||
return axios.get('/api/triggers/time')
|
||||
.then(function(response)
|
||||
{
|
||||
if (typeof response.data == 'object')
|
||||
@ -140,9 +177,14 @@ function startApp()
|
||||
.catch(function(error)
|
||||
{
|
||||
console.log(error);
|
||||
}),
|
||||
});
|
||||
},
|
||||
|
||||
axios.get('/api/steps')
|
||||
|
||||
loadSteps: function()
|
||||
{
|
||||
var self = this;
|
||||
return axios.get('/api/steps')
|
||||
.then(function(response)
|
||||
{
|
||||
if (Array.isArray(response.data))
|
||||
@ -171,15 +213,10 @@ function startApp()
|
||||
self.allStepsValue = Math.floor(total / steps.length);
|
||||
self.allSteps = allSteps;
|
||||
}
|
||||
})
|
||||
])
|
||||
.then(axios.spread(function(acct, perms) {
|
||||
self.stopLoadingIndicator();
|
||||
self.loading = false;
|
||||
}));
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
methods: {
|
||||
applyConnection: function()
|
||||
{
|
||||
var self = this;
|
||||
|
2
web/dist/bundle.js
vendored
2
web/dist/bundle.js
vendored
File diff suppressed because one or more lines are too long
@ -93,7 +93,7 @@ var messages = {
|
||||
pwmFrequency: 'PCA9685 PWM driver frequency',
|
||||
|
||||
mapsAPIKey: 'Google Maps API key',
|
||||
mapsAPIKeyhint: 'Optional. Currently only used for translating a location name to it\'s latitude / longitude, which you will most likely only do once. Will work without an API key, but Google might throttle your request.'
|
||||
mapsAPIKeyhint: 'Recommended if using time triggers. Used for looking up the current timezone. Will work without an API key, but Google might throttle your request. Register for a free API key at http://console.developers.google.com/ and activate it\'s use for the Maps API.'
|
||||
}
|
||||
},
|
||||
|
||||
@ -191,7 +191,7 @@ var messages = {
|
||||
pwmFrequency: 'PCA9685 PWM driver frequency',
|
||||
|
||||
mapsAPIKey: 'Google Maps API key',
|
||||
mapsAPIKeyhint: 'Optioneel. Wordt op dit moment enkel gebruikt om een locatie te vertalen naar een breedte- / lengtegraad, wat je waarschijnlijk maar één keer nodig hebt. Werkt ook zonder API key, met de kans dat Google je verzoek tijdelijk niet toestaat.'
|
||||
mapsAPIKeyhint: 'Aangeraden bij gebruik van de tijd triggers. Wordt gebruikt om de huidige tijdzone te bepalen. Werkt ook zonder API key, maar Google beperkt dan sterk de requests. Registreer een gratis API key op http://console.developers.google.com/ en activeer het voor gebruik met de Maps API.'
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user