848 lines
27 KiB
C++
848 lines
27 KiB
C++
///
|
|
///
|
|
/// Grow Controller
|
|
///
|
|
///
|
|
/// The beginnings of a WiFi accessible switch.
|
|
///
|
|
#include <dummy.h>
|
|
#include <tcp_axtls.h>
|
|
#include <SyncClient.h>
|
|
#include <ESPAsyncTCPbuffer.h>
|
|
#include <ESPAsyncTCP.h>
|
|
#include <async_config.h>
|
|
#include <AsyncPrinter.h>
|
|
#include <Wire.h>
|
|
#include <ESP8266WiFi.h>
|
|
#include <WiFiClient.h>
|
|
#include <ESP8266WebServer.h>
|
|
#include <ESP8266httpUpdate.h>
|
|
//#include <ESP8266mDNS.h>
|
|
|
|
|
|
// Application includes
|
|
#include "./DNSServer.h" // Patched lib
|
|
#include "WiFiConfiguration.h"
|
|
#include "Web_RootPage.h"
|
|
#include "Web_RSSIGraph.h"
|
|
#include "Web_FavIcon.h"
|
|
#include "Web_APConfig.h"
|
|
#include "ProjGlobals.h"
|
|
#include "Web_Resources.h"
|
|
#include "GrowController.h" // APIs available in this (.ino) file
|
|
#include "fauxmoESP.h"
|
|
|
|
|
|
// If there is no hardware, such as with an ESP-01 module only,
|
|
// set this. Then it won't loop through reset constantly, and
|
|
// other behaviors that will be determined.
|
|
#define HW_SIMULATE
|
|
|
|
// SW Update Deployment instructions:
|
|
//
|
|
// 1) If any resources changed, rebuild Web_Resources.h
|
|
// 2) Update this version number
|
|
// 3) Build the binary
|
|
// 4) Rename the .bin to match the version (e.g. GrowController v1.02.12.bin)
|
|
// 5) Move the product (.bin) to the server location
|
|
// 6) Update the PHP script on the server to match this version (if it does not auto-adapt)
|
|
// 7) Visit the node's web page and initiate an update, or let it check on its own cycle
|
|
//
|
|
const String MyVer = "GrowController v2.00.11"; ///< Compared to the server version for SW Updates
|
|
#define UPDATE_INTERVAL (12 * 60 * 60 * 1000) ///< Check once every 12 hours
|
|
bool swUpdateInProcess = false; ///< Status of an update to keep the relays from running
|
|
|
|
// ESP12-E pinout Version 2
|
|
//
|
|
// +------------------+
|
|
// | __ __ _____ |
|
|
// | | |_| |_| |
|
|
// | |__________ |
|
|
// |__________________|
|
|
// Reset | !Reset Tx | Tx
|
|
// Current | ADC Rx | Rx
|
|
// Prog En | CHPD-EN GPIO5 | FAULT
|
|
// SW_RESET | GPIO16 GPIO4 | SW_TOGGLE
|
|
// OLED SCL | GPIO14 GPIO0 |
|
|
// RLY Set2 | GPIO12 GPIO2 | OLED SDA
|
|
// RLY Set1 | GPIO13 GPIO15 | Pull-down
|
|
// | Vcc Gnd |
|
|
// +------------------+
|
|
|
|
// ESP12-E pinout Version 1
|
|
//
|
|
// +------------------+
|
|
// | __ __ _____ |
|
|
// | | |_| |_| |
|
|
// | |__________ |
|
|
// |__________________|
|
|
// Reset | !Reset Tx | Tx
|
|
// Current | ADC Rx | Rx
|
|
// Prog En | CHPD-EN GPIO5 |
|
|
// SW_RESET | GPIO16 GPIO4 | SW_TOGGLE
|
|
// RLY Set2 | GPIO14 GPIO0 |
|
|
// LED On | GPIO12 GPIO2 |
|
|
// RLY Set1 | GPIO13 GPIO15 | LED WiFi
|
|
// | Vcc Gnd |
|
|
// +------------------+
|
|
|
|
// +-------------+
|
|
// SW_RESET ---+----|(----------->| WiFiConfig |-----> LED_WIFI
|
|
// | | |
|
|
// | | |-----> isStationMode
|
|
// | |-------------|
|
|
// +----|>o---------->| Timer |
|
|
// | |-----> Factory Reset
|
|
// RESET_MS --------------------->| |
|
|
// +-------------+
|
|
//
|
|
#define SW_RESET 16 ///< The local reset switch
|
|
#define RESET_MS 8000 ///< Hold Reset for this long to factory reset
|
|
#define LED_WIFI 15 ///< The Pin controlling the WiFi on LED
|
|
bool isStationMode = false; ///< Tracks if the WiFi is in station mode
|
|
|
|
// WiFI LED Duty Cycle
|
|
//
|
|
// |******* | AP Mode
|
|
// |************************* | Joining AP
|
|
// |************************************************ | Joined
|
|
// | | | | | |
|
|
// 0 100 200 300 400 500ms
|
|
//
|
|
#define LED_APMODE__MS 80
|
|
#define LED_JOINING_MS 250
|
|
#define LED_JOINED__MS 490
|
|
#define LED_PERIOD__MS 500
|
|
|
|
// __ //
|
|
// | \ //
|
|
// onRef ------------------------------|- \ +-----------+ //
|
|
// | +-----| S Q |--+--> CurrStatus //
|
|
// +--------|+ / | | | //
|
|
// +---------+ | |__/ +--| R !Q | +--> LED_ON //
|
|
// ANA_IN --+--->| average |----+-->iRawSum/X | +-----------+ //
|
|
// | +---------+ | __ | //
|
|
// | | | \ | //
|
|
// +--> iRaw +--------|- \ | //
|
|
// | +--+ //
|
|
// offRef ------------------------------|+ / //
|
|
// |__/ //
|
|
//
|
|
#define ANA_IN A0 ///< The Analog input signal
|
|
#define SENSE_SAMPLE_MS 20 ///< The Sample rate for the analog input
|
|
#define AVG_RATIO 64 ///< Average Ratio (new is 1/AVG_RATIO of old)
|
|
#define LED_ON 12 ///< The load-on indicating LED
|
|
int32_t iRaw; ///< The raw sampled value
|
|
int32_t iRawSum; ///< The Averaged Sum of Raw Samples
|
|
bool CurrStatus = false; ///< Track the current sense indication of power on/off
|
|
|
|
|
|
// _ +------------------+
|
|
// Turn On -------------| \____ | +-----------+ |
|
|
// +--|>o--|_/ | _ +-->| D !Q |--+
|
|
// CurrStatus --| and +-------->) \ | |
|
|
// | _ +-------->) +--->| ck Q |--------> RelayState
|
|
// +-------| \____| +---->)_/ +-----------+
|
|
// Turn Off-------------|_/ | or
|
|
// and |
|
|
// SW_TOGGLE ----|>o---------------+
|
|
//
|
|
#define SW_TOGGLE 4 ///< The local toggle switch
|
|
bool RelayState = false; ///< The current tracked relay drive state
|
|
|
|
// +-----------+
|
|
// | _ _ |
|
|
// | _/ _| |_ |--------> Relay Set
|
|
// RelayState ---------------------------------->| _ _ |
|
|
// | \_ _| |_ |--------> Relay Reset
|
|
// | |
|
|
// +-----------+
|
|
//
|
|
#define RELAY_PULSE_MS 15 ///< Duration of a pulse
|
|
#define RLY_SET1 13 ///< The set-pin for the relay
|
|
#define RLY_SET2 14 ///< The reset-pin for the relay
|
|
|
|
// Timing:
|
|
//
|
|
// | | | | |
|
|
// _________________________
|
|
// 3WaySwitch ____________________/ \__________________
|
|
// | | |b->| | |b->|
|
|
// ______________________
|
|
// RelayState __________/ \______________________
|
|
// | |a->| | |a->| |
|
|
// | | | | |
|
|
// +++++++ ++++++++++
|
|
// +++ +++ +++ +++
|
|
// AnalogIn ++++++++++++ ++++++++++ +++++++++++++++
|
|
// | | | | |
|
|
// CurrStatus |Off |On |Off |On |Off
|
|
//
|
|
// a) Locally applied change in load to filtered AnalogIn delay
|
|
// b) Externally applied change in load to filtered AnalogIn delay
|
|
//
|
|
|
|
bool DebugValue; ///< Makes it chatty while running
|
|
|
|
const byte DNS_PORT = 53; ///< Capture DNS requests on port 53
|
|
IPAddress apIP(192,168,4,1); ///< Private network for server
|
|
DNSServer dnsServer; ///< Create the DNS object
|
|
|
|
ESP8266WebServer server(80); ///< For the user experience
|
|
fauxmoESP fauxmo; ///< For the WEMO emulator
|
|
ConfigManager wifiConfig; ///< Track various configuration items
|
|
|
|
ESP8266HTTPUpdate Updater;
|
|
bool updateCheck = false; ///< Set when a SW update check is pending
|
|
|
|
uint16_t autoOffTimer; ///< When the output is turned on, this is set to timeout in seconds
|
|
|
|
// local functions
|
|
void WiFiStateHandler(); ///< WiFi interface manager
|
|
void StartWebServer(); ///< Activate the web server
|
|
void HandleNotFound(); ///< Webpage for not-found URL
|
|
void ProcessAutoOff(bool init = false); ///< Process the auto-off timer
|
|
|
|
String AP_SSID = "GrowController"; ///< When in Access Point, this is the prefix
|
|
void GetMac();
|
|
String macToStr(const uint8_t* mac); ///< Create C++ String with the mac
|
|
String clientMac = ""; ///< Hosts the MAC to easily identify this node
|
|
|
|
|
|
// ===========================================================================
|
|
typedef enum {
|
|
WFC_ConnectToAP,
|
|
WFC_JoiningAP,
|
|
WFC_JoinedAP,
|
|
WFC_CreateAP,
|
|
WFC_HostingAP,
|
|
} WiFiConnectState;
|
|
WiFiConnectState wifiConState; ///< Track the current WiFi state
|
|
|
|
void SetLED(int pin, int state) {
|
|
digitalWrite(pin, state);
|
|
if (pin == LED_WIFI)
|
|
; // Serial.printf("SET LED_WIFI to %d\n", state);
|
|
else if (pin == LED_ON)
|
|
Serial.printf("SET LED_ON to %d\n", state);
|
|
else
|
|
Serial.printf("***** ERROR setting LED pin %d to %d\n", pin, state);
|
|
}
|
|
|
|
void ResetMonitor(bool init = false) {
|
|
static unsigned long timeAtReset;
|
|
static bool monitorActive;
|
|
|
|
if (init) {
|
|
timeAtReset = millis();
|
|
monitorActive = true;
|
|
} else if (monitorActive) {
|
|
int sw_state = digitalRead(SW_RESET);
|
|
#ifdef HW_SIMULATE
|
|
sw_state = 1;
|
|
#endif
|
|
if (sw_state == 1) {
|
|
// not pressed
|
|
monitorActive = false;
|
|
} else if ((millis() - timeAtReset) > RESET_MS) {
|
|
// reset has been held long enough
|
|
FactoryReset(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Average the new sample by summation with 7/8 of the running Sum.
|
|
//
|
|
void ProcessCurrentSense() {
|
|
static unsigned long last = millis();
|
|
unsigned long now = millis();
|
|
|
|
if (now - last > SENSE_SAMPLE_MS) {
|
|
last = now;
|
|
iRaw = analogRead(ANA_IN); // 1023 = 1.0v
|
|
iRawSum = iRawSum - (iRawSum / AVG_RATIO) + iRaw; // Now have running avg.
|
|
if (DebugValue)
|
|
Serial.printf("Sample: %d, rawSum: %d, relay: %d (%d%d), \n",
|
|
iRaw, iRawSum, RelayState, digitalRead(RLY_SET1), digitalRead(RLY_SET2));
|
|
if (iRawSum / AVG_RATIO > wifiConfig.getOnRef() && !CurrStatus) {
|
|
CurrStatus = true;
|
|
SetLED(LED_ON, 1);
|
|
ProcessAutoOff(true);
|
|
autoOffTimer = wifiConfig.getAutoOff(); // if zero, we do nothing...
|
|
Serial.printf("Auto Off Timer set to %d\n", autoOffTimer);
|
|
} else if (iRawSum / AVG_RATIO < wifiConfig.getOffRef() && CurrStatus) {
|
|
CurrStatus = false;
|
|
SetLED(LED_ON, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// _______ ___ __ _______
|
|
// Press \_________________________/ \___/ \___/
|
|
// | |
|
|
// v v
|
|
// _ _
|
|
// Toggle _______/ \____________.............../ \______________....
|
|
//
|
|
// Sample | | | | | ...
|
|
// Block |---- 80 ms ---| |---- 80 ms -----|
|
|
#define Block_ms 800
|
|
#define SAMPL_ms 100
|
|
void ProcessToggleSw() {
|
|
static unsigned long last = millis();
|
|
static bool lastSWState = false;
|
|
static uint8_t blockTics;
|
|
unsigned long now = millis();
|
|
|
|
if (now - last > SAMPL_ms) {
|
|
last = now;
|
|
if (blockTics) {
|
|
blockTics--;
|
|
return;
|
|
}
|
|
bool swState = !digitalRead(SW_TOGGLE); // active low is pressed.
|
|
if (DebugValue)
|
|
Serial.printf("Sample: %d, rawSum: %d, relay: %d (%d%d), sw: %d\n",
|
|
iRaw, iRawSum, RelayState, digitalRead(RLY_SET1), digitalRead(RLY_SET2), swState);
|
|
if (swState != lastSWState && swState == true) {
|
|
Serial.printf("Toggle pushed\n");
|
|
blockTics = Block_ms / SAMPL_ms;
|
|
SetCircuit(CMD_Toggle);
|
|
}
|
|
lastSWState = swState;
|
|
}
|
|
}
|
|
|
|
// Drive Relay controls pulsing either the set or the reset pin
|
|
//
|
|
// If the control signal changes, then it will generate a pulse on the
|
|
// appropriate relay driver pin, ensuring that the alternate pin is
|
|
// first off.
|
|
//
|
|
// This API is also called periodically, which permits it to then
|
|
// time and auto-off any active control pin.
|
|
//
|
|
// @param i can be -1 for timing, 0 to drive one relay pin, 1 to drive the other.
|
|
//
|
|
void DriveRelay(int i = -1) {
|
|
static int currentSignal = -1;
|
|
static bool isActive = false;
|
|
static unsigned long timeSet = 0;
|
|
|
|
if (swUpdateInProcess)
|
|
i = 2;
|
|
|
|
switch (i) {
|
|
case -1:
|
|
// Any active timing to process
|
|
if (isActive && (millis() - timeSet > RELAY_PULSE_MS)) {
|
|
digitalWrite(RLY_SET1, LOW);
|
|
digitalWrite(RLY_SET2, LOW);
|
|
isActive = false;
|
|
}
|
|
break;
|
|
case 0:
|
|
if (i != currentSignal) {
|
|
currentSignal = i;
|
|
digitalWrite(RLY_SET2, LOW); // Turn off the alternate
|
|
digitalWrite(RLY_SET1, HIGH);
|
|
timeSet = millis();
|
|
isActive = true;
|
|
}
|
|
break;
|
|
case 1:
|
|
if (i != currentSignal) {
|
|
currentSignal = i;
|
|
digitalWrite(RLY_SET1, LOW); // Turn off the alternate
|
|
digitalWrite(RLY_SET2, HIGH);
|
|
timeSet = millis();
|
|
isActive = true;
|
|
}
|
|
break;
|
|
case 2:
|
|
default:
|
|
// Error, or the force-off state
|
|
digitalWrite(RLY_SET1, LOW); // Turn off the elements
|
|
digitalWrite(RLY_SET2, LOW);
|
|
isActive = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ProcessNameChange() {
|
|
char xName[CFG_NAMESIZE];
|
|
|
|
fauxmo.getDeviceName(0, xName, CFG_NAMESIZE);
|
|
if (0 != strcmp(wifiConfig.getName().c_str(), xName)) {
|
|
fauxmo.renameDevice(0, wifiConfig.getName().c_str());
|
|
}
|
|
}
|
|
|
|
void ProcessSWUpdate() {
|
|
static unsigned long lastCheck;
|
|
unsigned long nowCheck = millis();
|
|
|
|
if ((nowCheck - lastCheck) > (UPDATE_INTERVAL)) {
|
|
lastCheck = nowCheck;
|
|
updateCheck = true;
|
|
}
|
|
if (updateCheck) {
|
|
WiFiClient wifiClient;
|
|
swUpdateInProcess = true;
|
|
Serial.printf("SW Update start '%s'...\n", wifiConfig.getURL().c_str());
|
|
t_httpUpdate_return swRet = Updater.update(wifiClient, wifiConfig.getURL(), MyVer);
|
|
switch (swRet) {
|
|
default:
|
|
case HTTP_UPDATE_FAILED:
|
|
Serial.printf("SW Update - failed.\n");
|
|
Serial.printf("Error: %s\n", Updater.getLastErrorString().c_str());
|
|
delay(100);
|
|
ESP.restart();
|
|
break;
|
|
case HTTP_UPDATE_NO_UPDATES:
|
|
Serial.printf("SW Update - no updates.\n");
|
|
break;
|
|
case HTTP_UPDATE_OK:
|
|
Serial.printf("SW Update - update ok.\n");
|
|
break;
|
|
}
|
|
swUpdateInProcess = false;
|
|
updateCheck = false;
|
|
Serial.printf("SW Update - process end.\n");
|
|
}
|
|
}
|
|
|
|
void ProcessConsole() {
|
|
if (Serial.available()) {
|
|
int ch = Serial.read();
|
|
Serial.printf("Serial.available() => %d\n", ch);
|
|
switch (ch) {
|
|
case '0':
|
|
iRawSum = 10;
|
|
break;
|
|
case '1':
|
|
iRawSum = 1000;
|
|
break;
|
|
case 'd':
|
|
DebugValue = !DebugValue;
|
|
break;
|
|
case 'u':
|
|
updateCheck = true;
|
|
break;
|
|
case '\x0D': // Ignore <cr>
|
|
break;
|
|
case '?':
|
|
default:
|
|
DebugValue = false;
|
|
Serial.printf("Commands:\n");
|
|
Serial.printf(" 0|1 Off or On (by forcing rawSum\n");
|
|
Serial.printf(" d Debug toggle\n");
|
|
Serial.printf(" u update check\n");
|
|
break;
|
|
}
|
|
}
|
|
static unsigned long last = millis();
|
|
if (millis() - last > 5000) {
|
|
static uint32_t refFreeHeap;
|
|
uint32_t freeHeap = ESP.getFreeHeap();
|
|
last = millis();
|
|
if (freeHeap != refFreeHeap)
|
|
Serial.printf("[MAIN] Free heap: %d bytes\n", ESP.getFreeHeap());
|
|
refFreeHeap = freeHeap;
|
|
}
|
|
}
|
|
|
|
void ProcessAutoOff(bool init) {
|
|
static unsigned long lastCheck;
|
|
unsigned long nowCheck = millis();
|
|
|
|
if (init)
|
|
lastCheck = nowCheck;
|
|
if (autoOffTimer && ((nowCheck - lastCheck) >= 1000)) {
|
|
lastCheck += 1000;
|
|
--autoOffTimer;
|
|
SetLED(LED_ON, autoOffTimer & 1); // During auto-Off, blink slowly...
|
|
if (autoOffTimer == 0) {
|
|
Serial.printf("AutoOff time remaining 0 sec. Off Now.\n");
|
|
SetCircuit(CMD_Off);
|
|
}
|
|
else {
|
|
Serial.printf("AutoOff time remaining %d sec\n", autoOffTimer);
|
|
}
|
|
}
|
|
}
|
|
|
|
void HWIOInit() {
|
|
// User push-buttons
|
|
pinMode(SW_TOGGLE, INPUT);
|
|
pinMode(SW_RESET, INPUT);
|
|
// Turn off relay drivers
|
|
pinMode(RLY_SET1, OUTPUT);
|
|
digitalWrite(RLY_SET1, 0);
|
|
pinMode(RLY_SET2, OUTPUT);
|
|
digitalWrite(RLY_SET2, 0);
|
|
// Turn off user LEDs
|
|
pinMode(LED_ON, OUTPUT);
|
|
digitalWrite(LED_ON, 0);
|
|
pinMode(LED_WIFI, OUTPUT);
|
|
digitalWrite(LED_WIFI, 0);
|
|
}
|
|
|
|
|
|
void setup(void) {
|
|
HWIOInit();
|
|
ResetMonitor(true); /// Initialize the reset switch monitor for long press
|
|
|
|
Serial.begin(115200);
|
|
Serial.printf("\n******************************************************************\n");
|
|
Serial.printf(" GrowController Web Server - Build " __DATE__ " " __TIME__ "\n");
|
|
Serial.printf(" Version %s\n", MyVer.c_str());
|
|
Serial.printf(" Copyright (c) 2018 by Smartware Computing, all rights reserved.\n");
|
|
Serial.printf("******************************************************************\n");
|
|
|
|
GetMac();
|
|
AP_SSID += "-";
|
|
AP_SSID += clientMac;
|
|
|
|
wifiConfig.load();
|
|
String name = wifiConfig.getName();
|
|
String ssid = wifiConfig.getSSID();
|
|
String pass = wifiConfig.getPassword();
|
|
if (ssid == "" || pass == "")
|
|
wifiConState = WFC_CreateAP;
|
|
else
|
|
wifiConState = WFC_ConnectToAP;
|
|
#if 0
|
|
if (MDNS.begin("esp")) { // clientMac.c_str()
|
|
//MDNS.addService("switch", "tcp", 80);
|
|
//MDNS.addServiceTxt("switch", "tcp", "switchkey", "SWITCHVALUE");
|
|
Serial.printf("MDNS responder started.\n");
|
|
}
|
|
#endif
|
|
StartWebServer();
|
|
#if 1
|
|
fauxmo.addDevice(name.c_str());
|
|
fauxmo.enable(true);
|
|
fauxmo.onSetState([](unsigned char device_id, const char * device_name, bool state) {
|
|
Serial.printf("[MAIN] Device #%d (%s) state command: %s\n", device_id, device_name, state ? "ON" : "OFF");
|
|
if ((state && !CurrStatus)
|
|
|| (!state && CurrStatus)) {
|
|
SetCircuit(RelayState ? CMD_Off : CMD_On);
|
|
}
|
|
});
|
|
fauxmo.onGetState([](unsigned char device_id, const char * device_name) {
|
|
(void) device_id;
|
|
(void) device_name;
|
|
return CurrStatus; // GetCircuitStatus(); // whatever the state of the device is
|
|
});
|
|
#endif
|
|
}
|
|
|
|
|
|
void loop(void) {
|
|
ResetMonitor(); // Monitor for long press to factory reset
|
|
WiFiStateHandler(); // start/connect as AP or Station
|
|
ProcessSWUpdate(); // query the server for fresh SW
|
|
fauxmo.handle(); // WEMO interface
|
|
ProcessNameChange(); // Update fauxmo if the name changes
|
|
ProcessCurrentSense(); // Get status by measuring current
|
|
ProcessToggleSw(); // Monitor for user pressing toggle
|
|
DriveRelay(); // Do what the relays need, if anything
|
|
ProcessConsole();
|
|
ProcessAutoOff(); // If auto-off is enabled...
|
|
}
|
|
|
|
|
|
// ###########################################################################
|
|
|
|
void WiFiStateHandler() {
|
|
static unsigned long timeStateChange = 0;
|
|
static unsigned long timeLEDControl;
|
|
unsigned long timeLEDCycle;
|
|
String ssid = wifiConfig.getSSID();
|
|
String pass = wifiConfig.getPassword();
|
|
IPAddress myIP;
|
|
|
|
timeLEDCycle = millis() - timeLEDControl;
|
|
if (timeLEDCycle >= LED_PERIOD__MS) {
|
|
timeLEDControl = millis();
|
|
timeLEDCycle = 0;
|
|
}
|
|
switch (wifiConState) {
|
|
case WFC_ConnectToAP:
|
|
isStationMode = true;
|
|
WiFi.mode(WIFI_STA);
|
|
ssid = wifiConfig.getSSID();
|
|
pass = wifiConfig.getPassword();
|
|
WiFi.begin(ssid.c_str(), pass.c_str());
|
|
timeStateChange = millis();
|
|
timeLEDControl = timeStateChange;
|
|
wifiConState = WFC_JoiningAP;
|
|
break;
|
|
case WFC_JoiningAP:
|
|
if (timeLEDCycle <= LED_JOINING_MS)
|
|
SetLED(LED_WIFI, 1);
|
|
else
|
|
SetLED(LED_WIFI, 0);
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
myIP = WiFi.localIP();
|
|
Serial.printf("IP Address: %s\n", myIP.toString().c_str());
|
|
timeStateChange = millis();
|
|
StartWebServer();
|
|
wifiConState = WFC_JoinedAP;
|
|
} else if (millis() - timeStateChange > 30000) {
|
|
timeStateChange = millis();
|
|
wifiConState = WFC_CreateAP; // failed for 30 sec, now what. retry or CreateAP?
|
|
}
|
|
break;
|
|
case WFC_CreateAP:
|
|
if (timeLEDCycle <= LED_APMODE__MS)
|
|
SetLED(LED_WIFI, 1);
|
|
else
|
|
SetLED(LED_WIFI, 0);
|
|
isStationMode = false;
|
|
Serial.printf("Starting Access Point %s\n", AP_SSID.c_str());
|
|
WiFi.softAP(AP_SSID.c_str());
|
|
myIP = WiFi.softAPIP();
|
|
Serial.printf("IP Address: %s\n", myIP.toString().c_str());
|
|
dnsServer.start(DNS_PORT, "*", apIP);
|
|
timeStateChange = millis();
|
|
StartWebServer();
|
|
wifiConState = WFC_HostingAP;
|
|
break;
|
|
case WFC_JoinedAP:
|
|
if (timeLEDCycle <= LED_JOINED__MS)
|
|
SetLED(LED_WIFI, 1);
|
|
else
|
|
SetLED(LED_WIFI, 0);
|
|
server.handleClient();
|
|
break;
|
|
case WFC_HostingAP:
|
|
if (timeLEDCycle <= LED_APMODE__MS)
|
|
SetLED(LED_WIFI, 1);
|
|
else
|
|
SetLED(LED_WIFI, 0);
|
|
server.handleClient();
|
|
dnsServer.processNextRequest();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
bool GetCircuitStatus() {
|
|
return CurrStatus;
|
|
}
|
|
void SetCircuit(CircuitCmd_T newState) {
|
|
if (swUpdateInProcess)
|
|
return;
|
|
switch (newState) {
|
|
case CMD_Off:
|
|
if (GetCircuitStatus()) {
|
|
RelayState = !RelayState;
|
|
DriveRelay(RelayState);
|
|
}
|
|
break;
|
|
case CMD_On:
|
|
if (!GetCircuitStatus()) {
|
|
RelayState = !RelayState;
|
|
DriveRelay(RelayState);
|
|
}
|
|
break;
|
|
case CMD_Toggle:
|
|
RelayState = !RelayState;
|
|
DriveRelay(RelayState);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void HandleCircuitOn() {
|
|
SetCircuit(CMD_On);
|
|
HandleGetState(); // Reply with current state
|
|
}
|
|
|
|
void HandleCircuitOff() {
|
|
SetCircuit(CMD_Off);
|
|
HandleGetState(); // Reply with current state
|
|
}
|
|
|
|
void HandleCircuitToggle() {
|
|
SetCircuit(CMD_Toggle);
|
|
HandleGetState(); // Reply with current state
|
|
}
|
|
|
|
// {
|
|
// "id": "5c:cf:7f:c0:52:82",
|
|
// "name": "3-Way Switch",
|
|
// "state": 0,
|
|
// "sense": 56,
|
|
// "ip": "192.168.1.23",
|
|
// "rssi": -63,
|
|
// "countdown": "3:45",
|
|
// "uptime": "0:11:45",
|
|
// "wifimode": "station"|"ap",
|
|
// }
|
|
//
|
|
void HandleGetState() {
|
|
int day, hr, min, sec;
|
|
char _upTime[15]; // 0000.00:00:00\0 + 1 spare
|
|
char _timeout[10]; // 00:00:00\0 + 1 spare
|
|
|
|
sec = millis() / 1000;
|
|
min = sec / 60;
|
|
hr = min / 60;
|
|
day = hr / 24;
|
|
if (day)
|
|
snprintf(_upTime, sizeof(_upTime), "%dd %d:%02d:%02d", day, hr % 24, min % 60, sec % 60);
|
|
else if (hr)
|
|
snprintf(_upTime, sizeof(_upTime), "%d:%02d:%02d", hr, min % 60, sec % 60);
|
|
else
|
|
snprintf(_upTime, sizeof(_upTime), "%02d:%02d", min % 60, sec % 60);
|
|
|
|
sec = autoOffTimer;
|
|
min = sec / 60;
|
|
hr = min / 60;
|
|
if (hr)
|
|
snprintf(_timeout, sizeof(_timeout), "%d:%02d:%02d", hr, min % 60, sec % 60);
|
|
else
|
|
snprintf(_timeout, sizeof(_timeout), "%d:%02d", min, sec % 60);
|
|
|
|
GetMac();
|
|
String modeText = (isStationMode) ? "Station" : "Access Point";
|
|
String message = "{ \"id\": \"" + String(clientMac) + "\", ";
|
|
message += "\"name\": \"" + wifiConfig.getName() + "\", ";
|
|
message += "\"version\": \"" + MyVer + "\", ";
|
|
message += "\"state\": " + String(CurrStatus) + ", ";
|
|
message += "\"raw\": " + String(iRaw) + ", ";
|
|
message += "\"sense\": " + String(iRawSum / AVG_RATIO) + ", ";
|
|
message += "\"ip\": \""
|
|
+ String(WiFi.localIP()[0])
|
|
+ "." + String(WiFi.localIP()[1])
|
|
+ "." + String(WiFi.localIP()[2])
|
|
+ "." + String(WiFi.localIP()[3])
|
|
+ "\", ";
|
|
message += "\"rssi\": " + String(WiFi.RSSI()) + ", ";
|
|
message += "\"countdown\": \"" + String(_timeout) + "\", ";
|
|
message += "\"uptime\": \"" + String(_upTime) + "\", ";
|
|
message += "\"wifimode\": \"" + modeText + "\", ";
|
|
message += "\"toggle\": " + String(!digitalRead(SW_TOGGLE)) + ", ";
|
|
message += "\"reset\": " + String(!digitalRead(SW_RESET));
|
|
message += " }";
|
|
if (DebugValue)
|
|
Serial.println(message + "\0");
|
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
|
server.send(200, "text/plain", message);
|
|
}
|
|
|
|
|
|
// Prototypes:
|
|
// const char Button_css[]
|
|
// const char favicon_ico[]
|
|
// const char Green1x1_png[]
|
|
// const char index_htm[]
|
|
// const char index_js[]
|
|
// const char rssi_htm[]
|
|
// const char rssi_js[]
|
|
|
|
void HandleSWUpdateCheck() {
|
|
updateCheck = true;
|
|
HandleAPConfigPage();
|
|
}
|
|
void HandleButton_css() {
|
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
|
server.send_P(200, "text/plain", Button_css);
|
|
}
|
|
void HandleIndex_js() {
|
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
|
server.send_P(200, "text/plain", index_js);
|
|
}
|
|
void HandleAbout_htm() {
|
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
|
server.send_P(200, "text/html", about_htm);
|
|
}
|
|
void HandleAbout_js() {
|
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
|
server.send_P(200, "text/plain", about_js);
|
|
}
|
|
void HandleMyIP_js() {
|
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
|
String message = "var mySite = 'http://";
|
|
message += String(WiFi.localIP()[0]);
|
|
message += "." + String(WiFi.localIP()[1]);
|
|
message += "." + String(WiFi.localIP()[2]);
|
|
message += "." + String(WiFi.localIP()[3]);
|
|
message += "';\n";
|
|
server.send(200, "text/plain", message);
|
|
}
|
|
void StartWebServer() {
|
|
// path /firmware
|
|
//httpUpdater.setup(&server, update_path, update_username, update_password);
|
|
server.on("/", HandleRootPage);
|
|
server.on("/myip.js", HandleMyIP_js);
|
|
server.on("/button.css", HandleButton_css);
|
|
server.on("/index.js", HandleIndex_js);
|
|
server.on("/about", HandleAbout_htm);
|
|
server.on("/about.js", HandleAbout_js);
|
|
server.on("/favicon.ico", HandleFavIcon);
|
|
server.on("/PlantModel.png", HandlePlantModel);
|
|
server.on("/Heater.png", HandleHeater);
|
|
server.on("/Water.png", HandleWater);
|
|
server.on("/config", HandleAPConfigPage);
|
|
server.on("/swupdatecheck", HandleSWUpdateCheck);
|
|
server.on("/scan", HandleAPScan);
|
|
server.on("/green1x1.png", HandleGreen1x1);
|
|
server.on("/rssi", HandleRSSIPage);
|
|
server.on("/rssi.js", HandleRSSI_JS);
|
|
server.on("/curr", HandleCurrPage);
|
|
server.on("/curr.js", HandleCurr_JS);
|
|
server.on("/on", HandleCircuitOn);
|
|
server.on("/off", HandleCircuitOff);
|
|
server.on("/toggle", HandleCircuitToggle);
|
|
server.on("/state", HandleGetState);
|
|
server.on("/icon.png", HandleIcon);
|
|
server.on("/Open.png", HandleOpenDoor);
|
|
server.onNotFound(HandleNotFound);
|
|
server.begin();
|
|
Serial.println("HTTP server started");
|
|
}
|
|
|
|
void HandleNotFound() {
|
|
Serial.printf("not found: %s\n", server.uri().c_str());
|
|
const DirEntry *de = Directory;
|
|
boolean found = false;
|
|
while (*de->Filename) {
|
|
if (0 == strcmp(de->Filename, server.uri().c_str())) {
|
|
Serial.printf("send %s\n", de->Filename);
|
|
server.send_P(200, de->Filetype, de->Filedata, de->Filesize);
|
|
found = true;
|
|
break;
|
|
} else {
|
|
de++;
|
|
}
|
|
}
|
|
if (!found) {
|
|
Serial.printf("!found - redirect to home\n");
|
|
server.sendHeader("Location", String("/"), true);
|
|
server.send(302, "text/plain", "Not supported. Heading for home.");
|
|
}
|
|
}
|
|
|
|
void GetMac() {
|
|
unsigned char mac[6];
|
|
WiFi.macAddress(mac);
|
|
clientMac = macToStr(mac);
|
|
}
|
|
|
|
String macToStr(const uint8_t * mac) {
|
|
String result;
|
|
|
|
for (int i = 0; i < 6; ++i) {
|
|
result += String(mac[i], 16);
|
|
if (i < 5)
|
|
result += ':';
|
|
}
|
|
return result;
|
|
}
|