Major update/rewrite/recombine to get SSDP, and GrowController UI and application shell.
416
Firmware/CustomHandlers.cpp
Normal file
@@ -0,0 +1,416 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
#include "CustomHandlers.h"
|
||||||
|
#include "WiFiStateHandler.h"
|
||||||
|
|
||||||
|
// General Purpose helper stuff
|
||||||
|
|
||||||
|
// Handlers
|
||||||
|
|
||||||
|
void HandleNotFound() {
|
||||||
|
bool found = false;
|
||||||
|
const DirEntry *de = Directory;
|
||||||
|
Serial.printf("No Custom Handler for '%s'\n", server.uri().c_str()+1);
|
||||||
|
while (de->Filename) {
|
||||||
|
Serial.printf("test for '%s' with '%s'\n", de->Filename, server.uri().c_str()+1);
|
||||||
|
#if 0
|
||||||
|
if (0 == strcmp(server.uri().c_str()+1, de->Filename)) {
|
||||||
|
Serial.printf("send '%s' (%i B) as '%s'\n", de->Filename, de->Filesize, de->Filetype);
|
||||||
|
server.send_P(200, de->Filetype, (const char *)de->Filedata, de->Filesize);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
de++;
|
||||||
|
}
|
||||||
|
if (!found) {
|
||||||
|
Serial.printf("!found '%s' - redirect to home\n", server.uri().c_str());
|
||||||
|
server.sendHeader("Location", "/", true);
|
||||||
|
server.send(302, "text/plain", "Not supported. Heading for home.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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/javascript", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void HandleRootPage() {
|
||||||
|
if (server.hasArg("SW")) {
|
||||||
|
String newState = server.arg("SW");
|
||||||
|
if (newState == "1") {
|
||||||
|
// @todo SetCircuit(CMD_On);
|
||||||
|
Serial.printf("SW 1\n");
|
||||||
|
} else if (newState == "0") {
|
||||||
|
// @todo SetCircuit(CMD_Off);
|
||||||
|
Serial.printf("SW 0\n");
|
||||||
|
} else if (newState == "2") {
|
||||||
|
// @todo SetCircuit(CMD_Toggle);
|
||||||
|
Serial.printf("SW 2\n");
|
||||||
|
} else {
|
||||||
|
Serial.printf("SW %s ???\n", newState.c_str());
|
||||||
|
; // no action
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// @todo String modeText = (isStationMode) ? "Station mode<br/>" : "Access Point mode<br/>";
|
||||||
|
// @todo String status = GetCircuitStatus() ? "On<br/>" : "Off<br/>";
|
||||||
|
String myIP = String(WiFi.localIP()[0]);
|
||||||
|
myIP += "." + String(WiFi.localIP()[1]);
|
||||||
|
myIP += "." + String(WiFi.localIP()[2]);
|
||||||
|
myIP += "." + String(WiFi.localIP()[3]);
|
||||||
|
server.send_P(200, "text/html", (const char *)index_htm);
|
||||||
|
}
|
||||||
|
|
||||||
|
static String EncType_str(uint8_t encType) {
|
||||||
|
switch (encType) {
|
||||||
|
case ENC_TYPE_NONE:
|
||||||
|
return "open";
|
||||||
|
break;
|
||||||
|
case ENC_TYPE_WEP:
|
||||||
|
return "wep";
|
||||||
|
break;
|
||||||
|
case ENC_TYPE_TKIP:
|
||||||
|
return "wpa psk";
|
||||||
|
break;
|
||||||
|
case ENC_TYPE_CCMP:
|
||||||
|
return "wpa2 psk";
|
||||||
|
break;
|
||||||
|
case ENC_TYPE_AUTO:
|
||||||
|
return "wpa wpa2 psk";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return "unknown";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleAPScan() {
|
||||||
|
// WiFi.scanNetworks will return the number of networks found
|
||||||
|
Serial.println("Scan ...");
|
||||||
|
int n = WiFi.scanNetworks(false, true); // blocking call and show hidden.
|
||||||
|
Serial.println("scan done.");
|
||||||
|
if (n == 0) {
|
||||||
|
Serial.println("no networks found");
|
||||||
|
} else {
|
||||||
|
Serial.print(n);
|
||||||
|
Serial.println(" networks found");
|
||||||
|
server.sendContent_P("HTTP/1.1 200 OK\r\n");
|
||||||
|
server.sendContent_P("Content-Type: text/html\r\n");
|
||||||
|
server.sendContent_P("\r\n");
|
||||||
|
server.sendContent_P(
|
||||||
|
"<!DOCTYPE html>\n"
|
||||||
|
"<html>\n"
|
||||||
|
"<head><title>Grow Controller - Scan</title></head>\n"
|
||||||
|
"<body>\n"
|
||||||
|
"<h1>Grow Controller - Scan</h1>"
|
||||||
|
);
|
||||||
|
|
||||||
|
server.sendContent_P(
|
||||||
|
" <form action=\"/config\">\n"
|
||||||
|
" <blockquote>\n"
|
||||||
|
" <table width='800' border='0'>\n"
|
||||||
|
" <tr bgcolor='#E0E0E0'><td colspan='2'>Net</td><td>RSSI</td><td>SSID</td><td>BSSID</td><td>Encryption</td><td>Channel</td><td>Hidden</td></tr>\n"
|
||||||
|
);
|
||||||
|
|
||||||
|
for (int i = 0; i < n; ++i) {
|
||||||
|
// Select, Network #, RSSI, SSID, BSSID, Channel, Hidden
|
||||||
|
String bgcolor = (i & 1) ? "#E0E0E0" : "#FFFFFF";
|
||||||
|
String record =
|
||||||
|
" <tr bgcolor='" + bgcolor + "'><td><input type='radio' name='ssid' value='" + WiFi.SSID(i) + "'></td>\n"
|
||||||
|
+ " <td>" + (i + 1) + "</td>\n"
|
||||||
|
+ " <td>" + WiFi.RSSI(i) + "</td>\n"
|
||||||
|
+ " <td>" + WiFi.SSID(i) + "<br/>"
|
||||||
|
+ " <img src='/green1x1.png' width='" + String(5 * (100 + WiFi.RSSI(i))) + "' height='3'></td>\n"
|
||||||
|
+ " <td>" + WiFi.BSSIDstr(i) + "</td>\n";
|
||||||
|
|
||||||
|
record += " <td>" + EncType_str(WiFi.encryptionType(i)) + "</td>\n";
|
||||||
|
record += " <td>" + String(WiFi.channel(i));
|
||||||
|
record += "</td>\n";
|
||||||
|
record += WiFi.isHidden(i)
|
||||||
|
? " <td>Yes</td>\n"
|
||||||
|
: " <td>No</td>\n";
|
||||||
|
record += " </tr>\n";
|
||||||
|
server.sendContent(record);
|
||||||
|
//
|
||||||
|
Serial.print(i + 1);
|
||||||
|
Serial.print(": ");
|
||||||
|
Serial.print(WiFi.SSID(i));
|
||||||
|
Serial.print(" (");
|
||||||
|
Serial.print(WiFi.RSSI(i));
|
||||||
|
Serial.print(")");
|
||||||
|
Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? " " : "*");
|
||||||
|
}
|
||||||
|
server.sendContent_P(
|
||||||
|
" <tr><td> </td>\n"
|
||||||
|
" <td colspan='7'>\n"
|
||||||
|
" <input type=\"submit\" value=\"Select\" />\n"
|
||||||
|
" <input type=\"reset\"/>\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" </table>\n"
|
||||||
|
" </blockquote>\n"
|
||||||
|
" </form>\n"
|
||||||
|
"NAV: "
|
||||||
|
" <a href='/'>Home</a> | "
|
||||||
|
" <a href='/config'>Config</a> | "
|
||||||
|
" <a href='/scan'>Scan</a> | "
|
||||||
|
" <a href='/rssi'>RSSI</a> | "
|
||||||
|
" <a href='/curr'>Current</a> | "
|
||||||
|
" <a href='/config?ssid=reset&pass=reset' onclick=\"return confirm('Are you sure?')\">Factory Reset</a> | "
|
||||||
|
" <a href='/about'>About</a>\n"
|
||||||
|
" <br/>\n"
|
||||||
|
"</body>\n"
|
||||||
|
"</html>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HandleAPConfigPage() {
|
||||||
|
String name = wifiConfig.getName();
|
||||||
|
String ssid = wifiConfig.getSSID();
|
||||||
|
String pass = wifiConfig.getPassword();
|
||||||
|
String url = wifiConfig.getURL();
|
||||||
|
//String ntp = wifiConfig.getNTPServerName();
|
||||||
|
char _onref[6], _offref[6], _autoOff[6];
|
||||||
|
|
||||||
|
snprintf(_onref, sizeof(_onref), "%d", wifiConfig.getOnRef());
|
||||||
|
snprintf(_offref, sizeof(_offref), "%d", wifiConfig.getOffRef());
|
||||||
|
snprintf(_autoOff, sizeof(_autoOff), "%d", wifiConfig.getAutoOff());
|
||||||
|
String onref = String(_onref);
|
||||||
|
String offref = String(_offref);
|
||||||
|
String autooff = String(_autoOff);
|
||||||
|
|
||||||
|
if (server.hasArg("ssid") && server.hasArg("pass")) {
|
||||||
|
String _ssid = server.hasArg("_ssid") ? server.arg("_ssid") : "";
|
||||||
|
String _pass = server.hasArg("_pass") ? server.arg("_pass") : "";
|
||||||
|
name = server.arg("name");
|
||||||
|
ssid = server.arg("ssid");
|
||||||
|
pass = server.arg("pass");
|
||||||
|
url = server.hasArg("url") ? server.arg("url") : "";
|
||||||
|
onref = server.hasArg("onref") ? server.arg("onref") : "";
|
||||||
|
offref = server.hasArg("offref") ? server.arg("offref") : "";
|
||||||
|
autooff = server.hasArg("autooff") ? server.arg("autooff") : "";
|
||||||
|
//ntp = server.hasArg("ntp") ? server.arg("ntp") : "";
|
||||||
|
if (ssid == "reset" && pass == "reset") {
|
||||||
|
FactoryReset(true);
|
||||||
|
}
|
||||||
|
wifiConfig.setName(name);
|
||||||
|
wifiConfig.setSSID(ssid);
|
||||||
|
wifiConfig.setPassword(pass);
|
||||||
|
wifiConfig.setURL(url);
|
||||||
|
wifiConfig.setOnRef(onref.toInt());
|
||||||
|
wifiConfig.setOffRef(offref.toInt());
|
||||||
|
wifiConfig.setAutoOff(autooff.toInt());
|
||||||
|
//wifiConfig.setNTPServerName(ntp);
|
||||||
|
wifiConfig.save();
|
||||||
|
Serial.println("Settings saved.");
|
||||||
|
if (ssid != _ssid || pass != _pass) {
|
||||||
|
// They changed stuff that requires a restart.
|
||||||
|
server.send(200, "text/html", "Configuration Saved! Restarting in 3 sec...");
|
||||||
|
Serial.printf("Config saved. Restarting in 3 sec . . .");
|
||||||
|
delay(3000);
|
||||||
|
ESP.restart();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String modeText = (WiFiStateGet() == WFC_JoinedAP) ? "Station mode<br/>" : "Access Point mode<br/>";
|
||||||
|
if (server.hasArg("ssid") && !server.hasArg("pass")) {
|
||||||
|
ssid = server.arg("ssid");
|
||||||
|
pass = "";
|
||||||
|
Serial.println("ssid: " + ssid + ", pass: " + pass);
|
||||||
|
}
|
||||||
|
if (server.hasArg("url"))
|
||||||
|
url = server.arg("url");
|
||||||
|
//if (server.hasArg("ntp"))
|
||||||
|
// ntp = server.arg("ntp");
|
||||||
|
server.send(200, "text/html",
|
||||||
|
"<!DOCTYPE html>\n"
|
||||||
|
"<html>\n"
|
||||||
|
"<head><title>Grow Controller - Config</title></head>\n"
|
||||||
|
"<body>\n"
|
||||||
|
"<h1>Grow Controller - Configuration</h1>"
|
||||||
|
" <form action=\"\">\n"
|
||||||
|
" <blockquote>\n"
|
||||||
|
" <table border='0'>\n"
|
||||||
|
" <tr>\n"
|
||||||
|
" <td>Node Name</td>\n"
|
||||||
|
" <td><input type=\"text\" size=\"32\" name=\"name\" value=\"" + name + "\" /></td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr>\n"
|
||||||
|
" <td>SSID</td>\n"
|
||||||
|
" <td><input type=\"text\" size=\"32\" name=\"ssid\" value=\"" + ssid + "\" /> **\n"
|
||||||
|
" <input type=\"hidden\" name=\"_ssid\" value=\"" + ssid + "\" /></td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr nowrap='nowrap'>\n"
|
||||||
|
" <td>Password</td>\n"
|
||||||
|
" <td nowrap='nowrap'><input type=\"password\" size=\"64\" name=\"pass\" value=\"" + pass + "\" /> **\n"
|
||||||
|
" <input type=\"hidden\" name=\"_pass\" value=\"" + pass + "\" /></td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr>\n"
|
||||||
|
" <td>Update URL</td>\n"
|
||||||
|
" <td><input type=\"text\" size=\"64\" name=\"url\" value=\"" + url + "\" /></td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
// " <tr>\n"
|
||||||
|
// " <td>NTP Server</td>\n"
|
||||||
|
// " <td><input type=\"text\" size=\"64\" name=\"ntp\" value=\"" + ntp + "\" /></td>\n"
|
||||||
|
// " </tr>\n"
|
||||||
|
" <tr>\n"
|
||||||
|
" <td>Auto-Off</td>\n"
|
||||||
|
" <td><input type=\"text\" size=\"10\" name=\"autooff\" value=\"" + autooff + "\" /> (time in seconds when non-zero)</td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr>\n"
|
||||||
|
" <td>On Reference</td>\n"
|
||||||
|
" <td><input type=\"text\" size=\"10\" name=\"onref\" value=\"" + onref + "\" /> (sense above this indicates power is on)</td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr>\n"
|
||||||
|
" <td>Off Reference</td>\n"
|
||||||
|
" <td><input type=\"text\" size=\"10\" name=\"offref\" value=\"" + offref + "\" /> (sense below this indicates power is off)</td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr>\n"
|
||||||
|
" <td> </td>\n"
|
||||||
|
" <td>\n"
|
||||||
|
" <input type=\"submit\" value=\"Save\" />\n"
|
||||||
|
" <input type=\"reset\"/> ** Changes to these will trigger a module restart.\n"
|
||||||
|
" </td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr>\n"
|
||||||
|
" <td colspan='2'><hr/></td>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" <tr>\n"
|
||||||
|
" <td> </td>\n"
|
||||||
|
" <td><a href='/swupdatecheck'>Check for SW update (module will restart if updated)</a></td/>\n"
|
||||||
|
" </tr>\n"
|
||||||
|
" </table>\n"
|
||||||
|
" </blockquote>\n"
|
||||||
|
" </form>\n"
|
||||||
|
"NAV: "
|
||||||
|
" <a href='/'>Home</a> | "
|
||||||
|
" <a href='/config'>Config</a> | "
|
||||||
|
" <a href='/scan'>Scan</a> | "
|
||||||
|
" <a href='/rssi'>RSSI</a> | "
|
||||||
|
" <a href='/curr'>Current</a> | "
|
||||||
|
" <a href='/config?ssid=reset&pass=reset' onclick=\"return confirm('Are you sure?')\">Factory Reset</a> | "
|
||||||
|
" <a href='/about'>About</a>\n"
|
||||||
|
" <br/>\n"
|
||||||
|
"</body>\n"
|
||||||
|
"</html>\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ###################################################################
|
||||||
|
//
|
||||||
|
// Old handlers, for 'static' information that needs to be send to
|
||||||
|
// build a web page.
|
||||||
|
//
|
||||||
|
// This is to be obsoleted by the new method at the top of this file.
|
||||||
|
//
|
||||||
|
// ###################################################################
|
||||||
|
|
||||||
|
|
||||||
|
void HandlePlantModel() {
|
||||||
|
Serial.printf("PlantModel\n");
|
||||||
|
server.send_P(200, "image/png", plantmodel_png, sizeof(plantmodel_png));
|
||||||
|
}
|
||||||
|
void HandlePlantModel_css() {
|
||||||
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
||||||
|
server.send_P(200, "text/css", plantmodel_css);
|
||||||
|
}
|
||||||
|
void HandleButton_css() {
|
||||||
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
||||||
|
server.send_P(200, "text/css", button_css);
|
||||||
|
}
|
||||||
|
void HandleIndex_js() {
|
||||||
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
||||||
|
server.send_P(200, "text/javascript", 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/javascript", about_js);
|
||||||
|
}
|
||||||
|
void HandleFavIcon() {
|
||||||
|
Serial.printf("favicon.ico\n");
|
||||||
|
server.send_P(200, "image/x-icon", favicon_ico, sizeof(favicon_ico));
|
||||||
|
}
|
||||||
|
void HandleIcon() {
|
||||||
|
Serial.printf("icon.png\n");
|
||||||
|
server.send_P(200, "image/png", icon_png, sizeof(icon_png));
|
||||||
|
}
|
||||||
|
void HandleOpenDoor() {
|
||||||
|
Serial.printf("open.png\n");
|
||||||
|
server.send_P(200, "image/png", open_png, sizeof(open_png));
|
||||||
|
}
|
||||||
|
void HandleGreen1x1() {
|
||||||
|
Serial.printf("green1x1.png\n");
|
||||||
|
server.send_P(200, "image/png", green1x1_png, sizeof(green1x1_png));
|
||||||
|
}
|
||||||
|
void HandleHeaterOn() {
|
||||||
|
Serial.printf("soilheater_on.png\n");
|
||||||
|
server.send_P(200, "image/png", soilheater_on_png, sizeof(soilheater_on_png));
|
||||||
|
}
|
||||||
|
void HandleHeaterOff() {
|
||||||
|
Serial.printf("soilheater_off.png\n");
|
||||||
|
server.send_P(200, "image/png", soilheater_off_png, sizeof(soilheater_off_png));
|
||||||
|
}
|
||||||
|
void HandleThermometer() {
|
||||||
|
Serial.printf("thermometer.png\n");
|
||||||
|
server.send_P(200, "image/png", thermometer_png, sizeof(thermometer_png));
|
||||||
|
}
|
||||||
|
void HandleSoilMoisture() {
|
||||||
|
Serial.printf("soilmoisture.png\n");
|
||||||
|
server.send_P(200, "image/png", soilmoisture_png, sizeof(soilmoisture_png));
|
||||||
|
}
|
||||||
|
void HandlePointer() {
|
||||||
|
Serial.printf("pointer.png\n");
|
||||||
|
server.send_P(200, "image/png", pointer_png, sizeof(pointer_png));
|
||||||
|
}
|
||||||
|
void HandleWater() {
|
||||||
|
Serial.printf("water.png\n");
|
||||||
|
server.send_P(200, "image/png", water_png, sizeof(water_png));
|
||||||
|
}
|
||||||
|
void HandleRSSIPage() {
|
||||||
|
server.send_P(200, "text/html", rssi_htm);
|
||||||
|
}
|
||||||
|
void HandleRSSI_js() {
|
||||||
|
server.send_P(200, "text/javascript", rssi_js);
|
||||||
|
}
|
||||||
|
void HandleCurrPage() {
|
||||||
|
server.send_P(200, "text/html", curr_htm);
|
||||||
|
}
|
||||||
|
void HandleCurr_js() {
|
||||||
|
server.send_P(200, "text/javascript", curr_js);
|
||||||
|
}
|
||||||
|
void HandleNav_js() {
|
||||||
|
server.send_P(200, "text/javascript", nav_js);
|
||||||
|
}
|
||||||
|
void HandleSWUpdateCheck() {
|
||||||
|
updateCheck = true;
|
||||||
|
HandleAPConfigPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
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
|
||||||
|
}
|
||||||
|
#endif
|
||||||
59
Firmware/CustomHandlers.h
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
// Custom Web Page Handlers
|
||||||
|
//
|
||||||
|
// For accepting and processing arguments, or for custom page
|
||||||
|
// generation.
|
||||||
|
//
|
||||||
|
#ifndef CUSTOMHANDERS_H
|
||||||
|
#define CUSTOMHANDERS_H
|
||||||
|
|
||||||
|
#include "ProjGlobals.h"
|
||||||
|
#include "Web_Resources.h"
|
||||||
|
|
||||||
|
/// If a URI does not have a custom handler hook, it will scan the file system for the target resource.
|
||||||
|
/// Only if it is then not found will it try to redirect the user to the main page.
|
||||||
|
///
|
||||||
|
void HandleNotFound();
|
||||||
|
|
||||||
|
///
|
||||||
|
/// For all of the following, there is actively generated content on the page,
|
||||||
|
/// so it must have a custom handler.
|
||||||
|
///
|
||||||
|
void HandleRootPage();
|
||||||
|
void HandleMyIP_js();
|
||||||
|
void HandleAPConfigPage();
|
||||||
|
|
||||||
|
void HandlePlantModel();
|
||||||
|
|
||||||
|
|
||||||
|
void HandleAPScan();
|
||||||
|
void HandlePlantModel();
|
||||||
|
void HandlePlantModel_css();
|
||||||
|
void HandleButton_css();
|
||||||
|
void HandleIndex_js();
|
||||||
|
void HandleAbout_htm();
|
||||||
|
void HandleAbout_js();
|
||||||
|
void HandleCircuitOn();
|
||||||
|
void HandleCircuitOff();
|
||||||
|
void HandleCircuitToggle();
|
||||||
|
void HandleSWUpdateCheck();
|
||||||
|
void HandleFavIcon();
|
||||||
|
void HandleIcon();
|
||||||
|
void HandleOpenDoor();
|
||||||
|
void HandleGreen1x1();
|
||||||
|
void HandleFavIcon();
|
||||||
|
void HandleHeaterOn();
|
||||||
|
void HandleWater();
|
||||||
|
void HandleRSSIPage();
|
||||||
|
void HandleRSSI_js();
|
||||||
|
void HandleCurrPage();
|
||||||
|
void HandleCurr_js();
|
||||||
|
void HandleNav_js();
|
||||||
|
void HandleHeaterOn();
|
||||||
|
void HandleHeaterOff();
|
||||||
|
void HandleThermometer();
|
||||||
|
void HandleSoilMoisture();
|
||||||
|
void HandlePointer();
|
||||||
|
|
||||||
|
#endif // CUSTOMHANDERS_H
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
#include "./DNSServer.h"
|
|
||||||
#include <lwip/def.h>
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#define DEBUG
|
|
||||||
#define DEBUG_OUTPUT Serial
|
|
||||||
|
|
||||||
DNSServer::DNSServer() {
|
|
||||||
_ttl = htonl(60);
|
|
||||||
_errorReplyCode = DNSReplyCode::NonExistentDomain;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DNSServer::start(const uint16_t &port, const String &domainName,
|
|
||||||
const IPAddress &resolvedIP) {
|
|
||||||
_port = port;
|
|
||||||
_domainName = domainName;
|
|
||||||
_resolvedIP[0] = resolvedIP[0];
|
|
||||||
_resolvedIP[1] = resolvedIP[1];
|
|
||||||
_resolvedIP[2] = resolvedIP[2];
|
|
||||||
_resolvedIP[3] = resolvedIP[3];
|
|
||||||
downcaseAndRemoveWwwPrefix(_domainName);
|
|
||||||
return _udp.begin(_port) == 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSServer::setErrorReplyCode(const DNSReplyCode &replyCode) {
|
|
||||||
_errorReplyCode = replyCode;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSServer::setTTL(const uint32_t &ttl) {
|
|
||||||
_ttl = htonl(ttl);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSServer::stop() {
|
|
||||||
_udp.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSServer::downcaseAndRemoveWwwPrefix(String &domainName) {
|
|
||||||
domainName.toLowerCase();
|
|
||||||
domainName.replace("www.", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSServer::processNextRequest() {
|
|
||||||
_currentPacketSize = _udp.parsePacket();
|
|
||||||
if (_currentPacketSize) {
|
|
||||||
_buffer = (unsigned char*) malloc(_currentPacketSize * sizeof(char));
|
|
||||||
_udp.read(_buffer, _currentPacketSize);
|
|
||||||
_dnsHeader = (DNSHeader*) _buffer;
|
|
||||||
|
|
||||||
if (_dnsHeader->QR == DNS_QR_QUERY &&
|
|
||||||
_dnsHeader->OPCode == DNS_OPCODE_QUERY &&
|
|
||||||
requestIncludesOnlyOneQuestion() &&
|
|
||||||
(_domainName == "*" || getDomainNameWithoutWwwPrefix() == _domainName)
|
|
||||||
) {
|
|
||||||
replyWithIP();
|
|
||||||
} else if (_dnsHeader->QR == DNS_QR_QUERY) {
|
|
||||||
replyWithCustomCode();
|
|
||||||
}
|
|
||||||
|
|
||||||
free(_buffer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DNSServer::requestIncludesOnlyOneQuestion() {
|
|
||||||
return ntohs(_dnsHeader->QDCount) == 1 &&
|
|
||||||
_dnsHeader->ANCount == 0 &&
|
|
||||||
_dnsHeader->NSCount == 0 &&
|
|
||||||
_dnsHeader->ARCount == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
String DNSServer::getDomainNameWithoutWwwPrefix() {
|
|
||||||
String parsedDomainName = "";
|
|
||||||
unsigned char *start = _buffer + 12;
|
|
||||||
if (*start == 0) {
|
|
||||||
return parsedDomainName;
|
|
||||||
}
|
|
||||||
int pos = 0;
|
|
||||||
while (true) {
|
|
||||||
unsigned char labelLength = *(start + pos);
|
|
||||||
for (int i = 0; i < labelLength; i++) {
|
|
||||||
pos++;
|
|
||||||
parsedDomainName += (char)*(start + pos);
|
|
||||||
}
|
|
||||||
pos++;
|
|
||||||
if (*(start + pos) == 0) {
|
|
||||||
downcaseAndRemoveWwwPrefix(parsedDomainName);
|
|
||||||
return parsedDomainName;
|
|
||||||
} else {
|
|
||||||
parsedDomainName += ".";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSServer::replyWithIP() {
|
|
||||||
_dnsHeader->QR = DNS_QR_RESPONSE;
|
|
||||||
_dnsHeader->ANCount = _dnsHeader->QDCount;
|
|
||||||
_dnsHeader->QDCount = _dnsHeader->QDCount;
|
|
||||||
//_dnsHeader->RA = 1;
|
|
||||||
|
|
||||||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
|
||||||
_udp.write(_buffer, _currentPacketSize);
|
|
||||||
|
|
||||||
_udp.write((uint8_t) 192); // answer name is a pointer
|
|
||||||
_udp.write((uint8_t) 12); // pointer to offset at 0x00c
|
|
||||||
|
|
||||||
_udp.write((uint8_t) 0); // 0x0001 answer is type A query (host address)
|
|
||||||
_udp.write((uint8_t) 1);
|
|
||||||
|
|
||||||
_udp.write((uint8_t) 0); //0x0001 answer is class IN (internet address)
|
|
||||||
_udp.write((uint8_t) 1);
|
|
||||||
|
|
||||||
_udp.write((unsigned char*) &_ttl, 4);
|
|
||||||
|
|
||||||
// Length of RData is 4 bytes (because, in this case, RData is IPv4)
|
|
||||||
_udp.write((uint8_t) 0);
|
|
||||||
_udp.write((uint8_t) 4);
|
|
||||||
_udp.write(_resolvedIP, sizeof(_resolvedIP));
|
|
||||||
_udp.endPacket();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
DEBUG_OUTPUT.print("DNS responds: ");
|
|
||||||
DEBUG_OUTPUT.print(_resolvedIP[0]);
|
|
||||||
DEBUG_OUTPUT.print(".");
|
|
||||||
DEBUG_OUTPUT.print(_resolvedIP[1]);
|
|
||||||
DEBUG_OUTPUT.print(".");
|
|
||||||
DEBUG_OUTPUT.print(_resolvedIP[2]);
|
|
||||||
DEBUG_OUTPUT.print(".");
|
|
||||||
DEBUG_OUTPUT.print(_resolvedIP[3]);
|
|
||||||
DEBUG_OUTPUT.print(" for ");
|
|
||||||
DEBUG_OUTPUT.println(getDomainNameWithoutWwwPrefix());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
void DNSServer::replyWithCustomCode() {
|
|
||||||
_dnsHeader->QR = DNS_QR_RESPONSE;
|
|
||||||
_dnsHeader->RCode = (unsigned char) _errorReplyCode;
|
|
||||||
_dnsHeader->QDCount = 0;
|
|
||||||
|
|
||||||
_udp.beginPacket(_udp.remoteIP(), _udp.remotePort());
|
|
||||||
_udp.write(_buffer, sizeof(DNSHeader));
|
|
||||||
_udp.endPacket();
|
|
||||||
}
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
#ifndef DNSServer_h
|
|
||||||
#define DNSServer_h
|
|
||||||
#include <WiFiUdp.h>
|
|
||||||
|
|
||||||
#define DNS_QR_QUERY 0
|
|
||||||
#define DNS_QR_RESPONSE 1
|
|
||||||
#define DNS_OPCODE_QUERY 0
|
|
||||||
|
|
||||||
enum class DNSReplyCode {
|
|
||||||
NoError = 0,
|
|
||||||
FormError = 1,
|
|
||||||
ServerFailure = 2,
|
|
||||||
NonExistentDomain = 3,
|
|
||||||
NotImplemented = 4,
|
|
||||||
Refused = 5,
|
|
||||||
YXDomain = 6,
|
|
||||||
YXRRSet = 7,
|
|
||||||
NXRRSet = 8
|
|
||||||
};
|
|
||||||
|
|
||||||
struct DNSHeader {
|
|
||||||
uint16_t ID; // identification number
|
|
||||||
unsigned char RD : 1; // recursion desired
|
|
||||||
unsigned char TC : 1; // truncated message
|
|
||||||
unsigned char AA : 1; // authoritive answer
|
|
||||||
unsigned char OPCode : 4; // message_type
|
|
||||||
unsigned char QR : 1; // query/response flag
|
|
||||||
unsigned char RCode : 4; // response code
|
|
||||||
unsigned char Z : 3; // its z! reserved
|
|
||||||
unsigned char RA : 1; // recursion available
|
|
||||||
uint16_t QDCount; // number of question entries
|
|
||||||
uint16_t ANCount; // number of answer entries
|
|
||||||
uint16_t NSCount; // number of authority entries
|
|
||||||
uint16_t ARCount; // number of resource entries
|
|
||||||
};
|
|
||||||
|
|
||||||
class DNSServer {
|
|
||||||
public:
|
|
||||||
DNSServer();
|
|
||||||
void processNextRequest();
|
|
||||||
void setErrorReplyCode(const DNSReplyCode &replyCode);
|
|
||||||
void setTTL(const uint32_t &ttl);
|
|
||||||
|
|
||||||
// Returns true if successful, false if there are no sockets available
|
|
||||||
bool start(const uint16_t &port,
|
|
||||||
const String &domainName,
|
|
||||||
const IPAddress &resolvedIP);
|
|
||||||
// stops the DNS server
|
|
||||||
void stop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
WiFiUDP _udp;
|
|
||||||
uint16_t _port;
|
|
||||||
String _domainName;
|
|
||||||
unsigned char _resolvedIP[4];
|
|
||||||
int _currentPacketSize;
|
|
||||||
unsigned char* _buffer;
|
|
||||||
DNSHeader* _dnsHeader;
|
|
||||||
uint32_t _ttl;
|
|
||||||
DNSReplyCode _errorReplyCode;
|
|
||||||
|
|
||||||
void downcaseAndRemoveWwwPrefix(String &domainName);
|
|
||||||
String getDomainNameWithoutWwwPrefix();
|
|
||||||
bool requestIncludesOnlyOneQuestion();
|
|
||||||
void replyWithIP();
|
|
||||||
void replyWithCustomCode();
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
@@ -1,847 +1,144 @@
|
|||||||
///
|
|
||||||
///
|
|
||||||
/// 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 <ESP8266WiFi.h>
|
||||||
#include <WiFiClient.h>
|
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
#include <ESP8266httpUpdate.h>
|
#include <ESP8266SSDP.h>
|
||||||
//#include <ESP8266mDNS.h>
|
|
||||||
|
#include "ProjGlobals.h" // Some global settings
|
||||||
|
#include "WiFiConfiguration.h" //
|
||||||
|
#include "WiFiStateHandler.h" // Startup as AP or client and transition
|
||||||
|
#include "CustomHandlers.h" // Handlers for the web pages
|
||||||
|
#include "PlantModel.h" // The Plant Model controller/simulator/status
|
||||||
|
|
||||||
|
const String AP_NAME = "GrowController"; ///< Network name when in Access Point mode
|
||||||
|
const String AP_VERS = "v1.00.11"; ///< Compared to the server version for SW Updates
|
||||||
|
|
||||||
|
ESP8266WebServer server(80);
|
||||||
|
ConfigManager wifiConfig; ///< Track various configuration items
|
||||||
|
|
||||||
|
//ESP8266HTTPUpdate Updater;
|
||||||
|
bool updateCheck = false; ///< Set when a SW update check is pending
|
||||||
|
|
||||||
|
|
||||||
// Application includes
|
// Register the urls with custom handlers
|
||||||
#include "./DNSServer.h" // Patched lib
|
// Register the SSDP interface
|
||||||
#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
|
void StartWebServer();
|
||||||
// 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
|
|
||||||
|
|
||||||
|
|
||||||
// _ +------------------+
|
void setup() {
|
||||||
// Turn On -------------| \____ | +-----------+ |
|
Serial.begin(115200);
|
||||||
// +--|>o--|_/ | _ +-->| D !Q |--+
|
Serial.printf("\n");
|
||||||
// CurrStatus --| and +-------->) \ | |
|
Serial.printf("\n******************************************************************\n");
|
||||||
// | _ +-------->) +--->| ck Q |--------> RelayState
|
Serial.printf(" %s Web Server - Build " __DATE__ " " __TIME__ "\n", AP_NAME.c_str());
|
||||||
// +-------| \____| +---->)_/ +-----------+
|
Serial.printf(" Version %s\n", AP_VERS.c_str());
|
||||||
// Turn Off-------------|_/ | or
|
Serial.printf(" Copyright (c) 2021 by Smartware Computing, all rights reserved.\n");
|
||||||
// and |
|
Serial.printf("******************************************************************\n");
|
||||||
// SW_TOGGLE ----|>o---------------+
|
wifiConfig.load();
|
||||||
//
|
|
||||||
#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);
|
void loop() {
|
||||||
Serial.printf("\n******************************************************************\n");
|
//
|
||||||
Serial.printf(" GrowController Web Server - Build " __DATE__ " " __TIME__ "\n");
|
// Process WiFi State Changes
|
||||||
Serial.printf(" Version %s\n", MyVer.c_str());
|
//
|
||||||
Serial.printf(" Copyright (c) 2018 by Smartware Computing, all rights reserved.\n");
|
static WiFiConnectState lastState = WFC_Idle;
|
||||||
Serial.printf("******************************************************************\n");
|
WiFiConnectState currState = WiFiStateHandler();
|
||||||
|
if (lastState != currState) {
|
||||||
|
lastState = currState;
|
||||||
|
switch (currState) { // On change to this state
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
case WFC_HostingAP:
|
||||||
|
case WFC_JoinedAP:
|
||||||
|
StartWebServer();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switch (currState) {
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
case WFC_HostingAP:
|
||||||
|
case WFC_JoinedAP:
|
||||||
|
server.handleClient();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
GetMac();
|
//
|
||||||
AP_SSID += "-";
|
//
|
||||||
AP_SSID += clientMac;
|
//
|
||||||
|
simulation();
|
||||||
wifiConfig.load();
|
// @todo delay(1);
|
||||||
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() {
|
void StartWebServer() {
|
||||||
// path /firmware
|
// path /firmware
|
||||||
//httpUpdater.setup(&server, update_path, update_username, update_password);
|
Serial.println("HTTP server starting ...");
|
||||||
server.on("/", HandleRootPage);
|
//httpUpdater.setup(&server, update_path, update_username, update_password);
|
||||||
server.on("/myip.js", HandleMyIP_js);
|
server.on("/", HandleRootPage);
|
||||||
server.on("/button.css", HandleButton_css);
|
server.on("/config", HandleAPConfigPage);
|
||||||
server.on("/index.js", HandleIndex_js);
|
server.on("/myip.js", HandleMyIP_js);
|
||||||
server.on("/about", HandleAbout_htm);
|
server.on("/nav.js", HandleNav_js);
|
||||||
server.on("/about.js", HandleAbout_js);
|
server.on("/plantmodel.png", HandlePlantModel);
|
||||||
server.on("/favicon.ico", HandleFavIcon);
|
server.on("/plantmodel.css", HandlePlantModel_css);
|
||||||
server.on("/PlantModel.png", HandlePlantModel);
|
server.on("/button.css", HandleButton_css);
|
||||||
server.on("/Heater.png", HandleHeater);
|
server.on("/index.js", HandleIndex_js);
|
||||||
server.on("/Water.png", HandleWater);
|
server.on("/about.htm", HandleAbout_htm);
|
||||||
server.on("/config", HandleAPConfigPage);
|
server.on("/about.js", HandleAbout_js);
|
||||||
server.on("/swupdatecheck", HandleSWUpdateCheck);
|
server.on("/favicon.ico", HandleFavIcon);
|
||||||
server.on("/scan", HandleAPScan);
|
server.on("/soilheater_on.png", HandleHeaterOn);
|
||||||
server.on("/green1x1.png", HandleGreen1x1);
|
server.on("/soilheater_off.png", HandleHeaterOff);
|
||||||
server.on("/rssi", HandleRSSIPage);
|
server.on("/thermometer.png", HandleThermometer);
|
||||||
server.on("/rssi.js", HandleRSSI_JS);
|
server.on("/soilmoisture.png", HandleSoilMoisture);
|
||||||
server.on("/curr", HandleCurrPage);
|
server.on("/pointer.png", HandlePointer);
|
||||||
server.on("/curr.js", HandleCurr_JS);
|
server.on("/water.png", HandleWater);
|
||||||
server.on("/on", HandleCircuitOn);
|
server.on("/green1x1.png", HandleGreen1x1);
|
||||||
server.on("/off", HandleCircuitOff);
|
server.on("/rssi.htm", HandleRSSIPage);
|
||||||
server.on("/toggle", HandleCircuitToggle);
|
server.on("/rssi.js", HandleRSSI_js);
|
||||||
server.on("/state", HandleGetState);
|
server.on("/curr.htm", HandleCurrPage);
|
||||||
server.on("/icon.png", HandleIcon);
|
server.on("/curr.js", HandleCurr_js);
|
||||||
server.on("/Open.png", HandleOpenDoor);
|
server.on("/icon.png", HandleIcon);
|
||||||
server.onNotFound(HandleNotFound);
|
server.on("/open.png", HandleOpenDoor);
|
||||||
server.begin();
|
server.on("/swupdatecheck", HandleSWUpdateCheck);
|
||||||
Serial.println("HTTP server started");
|
server.on("/scan", HandleAPScan);
|
||||||
|
//server.on("/on", HandleCircuitOn);
|
||||||
|
//server.on("/off", HandleCircuitOff);
|
||||||
|
//server.on("/toggle", HandleCircuitToggle);
|
||||||
|
server.on("/state", HandleGetState);
|
||||||
|
server.on("/description.xml", HTTP_GET, []() {
|
||||||
|
Serial.printf("description.xml handler.\n");
|
||||||
|
SSDP.schema(server.client());
|
||||||
|
});
|
||||||
|
server.onNotFound(HandleNotFound);
|
||||||
|
server.begin();
|
||||||
|
Serial.println("HTTP server started.");
|
||||||
|
|
||||||
|
//
|
||||||
|
// SSDP Startup
|
||||||
|
//
|
||||||
|
Serial.printf("SSDP server starting ...\n");
|
||||||
|
SSDP.setSchemaURL("description.xml");
|
||||||
|
SSDP.setHTTPPort(80);
|
||||||
|
SSDP.setName(wifiConfig.getName().c_str()); // "Philips hue clone");
|
||||||
|
SSDP.setSerialNumber("001788102201");
|
||||||
|
SSDP.setURL("/"); // Root Page
|
||||||
|
SSDP.setModelName(AP_NAME.c_str()); // "Philips hue bridge 2012");
|
||||||
|
SSDP.setModelNumber("929000226503");
|
||||||
|
SSDP.setModelURL("https://www.smart-family.net/GrowController");
|
||||||
|
SSDP.setManufacturer("Smartware Computing");
|
||||||
|
SSDP.setManufacturerURL("https://www.smart-family.net");
|
||||||
|
SSDP.setDeviceType("upnp:rootdevice");
|
||||||
|
SSDP.begin();
|
||||||
|
Serial.println("SSDP server started.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void HandleNotFound() {
|
void FactoryReset(bool forceRestart) {
|
||||||
Serial.printf("not found: %s\n", server.uri().c_str());
|
Serial.println("Factory Reset Configuration...\n");
|
||||||
const DirEntry *de = Directory;
|
wifiConfig.factoryReset();
|
||||||
boolean found = false;
|
wifiConfig.save();
|
||||||
while (*de->Filename) {
|
if (forceRestart) {
|
||||||
if (0 == strcmp(de->Filename, server.uri().c_str())) {
|
Serial.println("Restart in 3 sec ...");
|
||||||
Serial.printf("send %s\n", de->Filename);
|
delay(3000);
|
||||||
server.send_P(200, de->Filetype, de->Filedata, de->Filesize);
|
ESP.restart();
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
|
|
||||||
#ifndef GROWCONTROLLER_H
|
|
||||||
#define GROWCONTROLLER_H
|
|
||||||
|
|
||||||
bool GetCircuitStatus();
|
|
||||||
|
|
||||||
/// Command for the circuit
|
|
||||||
typedef enum {
|
|
||||||
CMD_Off, ///< Command it off
|
|
||||||
CMD_On, ///< Command it on
|
|
||||||
CMD_Toggle ///< Command a toggle
|
|
||||||
} CircuitCmd_T;
|
|
||||||
|
|
||||||
|
|
||||||
/// Set the state
|
|
||||||
/// @param[in] newState
|
|
||||||
/// - 0 = Off
|
|
||||||
/// - 1 = On
|
|
||||||
/// - 2 = Toggle
|
|
||||||
///
|
|
||||||
void SetCircuit(CircuitCmd_T newState);
|
|
||||||
|
|
||||||
#endif // GROWCONTROLLER_H
|
|
||||||
35
Firmware/GrowController.ses
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<NotepadPlus>
|
||||||
|
<Session activeView="0">
|
||||||
|
<mainView activeIndex="16">
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1152" startPos="66" endPos="135" selMode="0" offset="0" wrapCount="1" lang="HTML" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\about.htm" backupFilePath="" originalFileLastModifTimestamp="-1755984831" originalFileLastModifTimestampHigh="30918723" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="7864439" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1152" startPos="135" endPos="135" selMode="0" offset="0" wrapCount="1" lang="HTML" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\curr.htm" backupFilePath="" originalFileLastModifTimestamp="-1755304900" originalFileLastModifTimestampHigh="30918723" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="8699361" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="15" xOffset="0" scrollWidth="1161" startPos="1008" endPos="1008" selMode="0" offset="0" wrapCount="1" lang="HTML" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\index.htm" backupFilePath="" originalFileLastModifTimestamp="582500597" originalFileLastModifTimestampHigh="30918741" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="0" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="576" startPos="399" endPos="412" selMode="0" offset="0" wrapCount="1" lang="JavaScript" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\about.js" backupFilePath="" originalFileLastModifTimestamp="304003083" originalFileLastModifTimestampHigh="30772255" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="0" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="369" startPos="54" endPos="54" selMode="0" offset="0" wrapCount="1" lang="CSS" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\button.css" backupFilePath="" originalFileLastModifTimestamp="-809889817" originalFileLastModifTimestampHigh="30918529" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="-285211648" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="272" startPos="92" endPos="92" selMode="0" offset="0" wrapCount="1" lang="CSS" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\plantmodel.css" backupFilePath="" originalFileLastModifTimestamp="1181250598" originalFileLastModifTimestampHigh="30918741" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="0" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1" startPos="0" endPos="0" selMode="0" offset="0" wrapCount="0" lang="JavaScript" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\curr.js" backupFilePath="" originalFileLastModifTimestamp="-1210165259" originalFileLastModifTimestampHigh="30918511" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="7733353" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="756" startPos="727" endPos="734" selMode="0" offset="0" wrapCount="1" lang="JavaScript" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\index.js" backupFilePath="" originalFileLastModifTimestamp="1069068571" originalFileLastModifTimestampHigh="30918535" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="1139900416" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1" startPos="0" endPos="0" selMode="0" offset="0" wrapCount="0" lang="JavaScript" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\rssi.js" backupFilePath="" originalFileLastModifTimestamp="304113078" originalFileLastModifTimestampHigh="30772255" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="1139900416" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1451" startPos="231" endPos="287" selMode="0" offset="0" wrapCount="1" lang="HTML" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\rssi.htm" backupFilePath="" originalFileLastModifTimestamp="-1754114876" originalFileLastModifTimestampHigh="30918723" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="131072" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1071" startPos="100" endPos="100" selMode="0" offset="0" wrapCount="1" lang="HTML" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\navigation.htm" backupFilePath="" originalFileLastModifTimestamp="-1753474825" originalFileLastModifTimestampHigh="30918723" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="0" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1089" startPos="212" endPos="212" selMode="0" offset="0" wrapCount="1" lang="JavaScript" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\nav.js" backupFilePath="" originalFileLastModifTimestamp="2129232066" originalFileLastModifTimestampHigh="30918721" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="0" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="594" startPos="152" endPos="152" selMode="0" offset="0" wrapCount="1" lang="JavaScript" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\myip.js" backupFilePath="" originalFileLastModifTimestamp="1696718592" originalFileLastModifTimestampHigh="30918535" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="0" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="257" xOffset="0" scrollWidth="994" startPos="11252" endPos="11252" selMode="0" offset="0" wrapCount="1" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\CustomHandlers.cpp" backupFilePath="" originalFileLastModifTimestamp="-1709816653" originalFileLastModifTimestampHigh="30918741" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="7733353" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="721" startPos="661" endPos="661" selMode="0" offset="0" wrapCount="1" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\CustomHandlers.h" backupFilePath="" originalFileLastModifTimestamp="-931836632" originalFileLastModifTimestampHigh="30918741" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="131072" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="71" xOffset="0" scrollWidth="749" startPos="3433" endPos="3443" selMode="0" offset="0" wrapCount="1" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\gcfw.ino" backupFilePath="" originalFileLastModifTimestamp="-347279647" originalFileLastModifTimestampHigh="30918751" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="7733353" mapWrapIndentMode="-1" mapIsWrap="no">
|
||||||
|
<Mark line="110" />
|
||||||
|
</File>
|
||||||
|
<File firstVisibleLine="10" xOffset="0" scrollWidth="567" startPos="487" endPos="487" selMode="0" offset="0" wrapCount="1" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\ProjGlobals.h" backupFilePath="" originalFileLastModifTimestamp="320190381" originalFileLastModifTimestampHigh="30918752" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="7733353" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1" startPos="0" endPos="0" selMode="0" offset="0" wrapCount="0" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Web_Resources.h" backupFilePath="" originalFileLastModifTimestamp="-1355064912" originalFileLastModifTimestampHigh="30918723" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="131072" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1" startPos="0" endPos="0" selMode="0" offset="0" wrapCount="0" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\WiFiConfiguration.cpp" backupFilePath="" originalFileLastModifTimestamp="755248262" originalFileLastModifTimestampHigh="30917526" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="-285211648" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1" startPos="0" endPos="0" selMode="0" offset="0" wrapCount="0" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\WiFiConfiguration.h" backupFilePath="" originalFileLastModifTimestamp="755308276" originalFileLastModifTimestampHigh="30917526" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="7274563" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="1" startPos="0" endPos="0" selMode="0" offset="0" wrapCount="0" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\WiFiStateHandler.cpp" backupFilePath="" originalFileLastModifTimestamp="-1855225827" originalFileLastModifTimestampHigh="30917528" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="5439531" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="0" xOffset="0" scrollWidth="490" startPos="70" endPos="70" selMode="0" offset="0" wrapCount="1" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\WiFiStateHandler.h" backupFilePath="" originalFileLastModifTimestamp="-1855157359" originalFileLastModifTimestampHigh="30917528" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="6553711" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
</mainView>
|
||||||
|
<subView activeIndex="1">
|
||||||
|
<File firstVisibleLine="4" xOffset="0" scrollWidth="648" startPos="278" endPos="278" selMode="0" offset="0" wrapCount="1" lang="C++" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\ProjGlobals.h" backupFilePath="" originalFileLastModifTimestamp="320190381" originalFileLastModifTimestampHigh="30918752" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="7733353" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
<File firstVisibleLine="36" xOffset="0" scrollWidth="272" startPos="98" endPos="107" selMode="0" offset="0" wrapCount="1" lang="CSS" encoding="-1" userReadOnly="no" filename="C:\Projects\GrowController\gcfw\Resources\plantmodel.css" backupFilePath="" originalFileLastModifTimestamp="1181250598" originalFileLastModifTimestampHigh="30918741" mapFirstVisibleDisplayLine="-1" mapFirstVisibleDocLine="-1" mapLastVisibleDocLine="-1" mapNbLine="-1" mapHigherPos="-1" mapWidth="-1" mapHeight="-1" mapKByteInDoc="0" mapWrapIndentMode="-1" mapIsWrap="no" />
|
||||||
|
</subView>
|
||||||
|
</Session>
|
||||||
|
</NotepadPlus>
|
||||||
246
Firmware/PlantModel.cpp
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
// PlantModel
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ProjGlobals.h"
|
||||||
|
#include "PlantModel.h"
|
||||||
|
#include "WiFiStateHandler.h"
|
||||||
|
#include "Utility.h"
|
||||||
|
|
||||||
|
|
||||||
|
PlantData_T plant; /// This keeps track of everything relevant to this system
|
||||||
|
|
||||||
|
|
||||||
|
String OnOffText(bool isOn) {
|
||||||
|
if (isOn)
|
||||||
|
return String("On");
|
||||||
|
else
|
||||||
|
return String("Off");
|
||||||
|
}
|
||||||
|
|
||||||
|
String DrumStateText(DrumState_E state) {
|
||||||
|
switch (state) {
|
||||||
|
case DrumIsOpen:
|
||||||
|
return String("Open");
|
||||||
|
case DrumIsClosed:
|
||||||
|
return String("Closed");
|
||||||
|
case DrumIsMoving:
|
||||||
|
return String("Moving");
|
||||||
|
default:
|
||||||
|
return String("Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String DrumMotorText(DrumMotor_E state) {
|
||||||
|
switch (state) {
|
||||||
|
case DrumOff:
|
||||||
|
return String("Stopped");
|
||||||
|
case DrumCW:
|
||||||
|
return String("CW Rotation");
|
||||||
|
case DrumCCW:
|
||||||
|
return String("CCW Rotation");
|
||||||
|
default:
|
||||||
|
return String("Error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String DrumOpenText(bool isOpen) {
|
||||||
|
if (isOpen)
|
||||||
|
return String("Open");
|
||||||
|
else
|
||||||
|
return String("not open");
|
||||||
|
}
|
||||||
|
String DrumClosedText(bool isClosed) {
|
||||||
|
if (isClosed)
|
||||||
|
return String("Closed");
|
||||||
|
else
|
||||||
|
return String("not closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
String WaterInTankText(bool waterInTank) {
|
||||||
|
if (waterInTank)
|
||||||
|
return String("OK");
|
||||||
|
else
|
||||||
|
return String("Empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
// {
|
||||||
|
// "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, minute, sec;
|
||||||
|
char _upTime[15]; // 0000.00:00:00\0 + 1 spare
|
||||||
|
|
||||||
|
sec = millis() / 1000;
|
||||||
|
minute = sec / 60;
|
||||||
|
hr = minute / 60;
|
||||||
|
day = hr / 24;
|
||||||
|
if (day)
|
||||||
|
snprintf(_upTime, sizeof(_upTime), "%dd %d:%02d:%02d", day, hr % 24, minute % 60, sec % 60);
|
||||||
|
else if (hr)
|
||||||
|
snprintf(_upTime, sizeof(_upTime), "%d:%02d:%02d", hr, minute % 60, sec % 60);
|
||||||
|
else
|
||||||
|
snprintf(_upTime, sizeof(_upTime), "%02d:%02d", minute % 60, sec % 60);
|
||||||
|
|
||||||
|
String modeText = (WiFiStateGet() == WFC_JoinedAP) ? "Station" : "Access Point";
|
||||||
|
String message = "{\n";
|
||||||
|
message += "\t\"id\": \"" + GetMacString() + "\",\n";
|
||||||
|
message += "\t\"name\": \"" + wifiConfig.getName() + "\",\n";
|
||||||
|
message += "\t\"version\": \"" + AP_VERS + "\",\n";
|
||||||
|
//message += "\t\"state\": " + String(CurrStatus) + ",\n";
|
||||||
|
//message += "\t\"raw\": " + String(iRaw) + ",\n";
|
||||||
|
//message += "\t\"sense\": " + String(iRawSum / AVG_RATIO) + ",\n";
|
||||||
|
message += "\t\"ip\": \""
|
||||||
|
+ String(WiFi.localIP()[0])
|
||||||
|
+ "." + String(WiFi.localIP()[1])
|
||||||
|
+ "." + String(WiFi.localIP()[2])
|
||||||
|
+ "." + String(WiFi.localIP()[3])
|
||||||
|
+ "\",\n";
|
||||||
|
message += "\t\"rssi\": \"" + String(WiFi.RSSI()) + "\",\n";
|
||||||
|
message += "\t\"uptime\": \"" + String(_upTime) + "\",\n";
|
||||||
|
message += "\t\"wifimode\": \"" + modeText + "\",\n";
|
||||||
|
|
||||||
|
snprintf(_upTime, sizeof(_upTime), "%02u:%02u", plant.SecondsToday/3660, (plant.SecondsToday/60)%60);
|
||||||
|
message += "\t\"TimeOfDay\": \"" + String(_upTime) + "\",\n";
|
||||||
|
message += "\t\"Fahrenheit\": \"" + String(plant.Fahrenheit) + "\",\n";
|
||||||
|
|
||||||
|
message += "\t\"ChamberTemp_C\": \"" + String(plant.ChamberTemp_C) + "\",\n";
|
||||||
|
message += "\t\"ChamberHumi\": \"" + String(plant.ChamberHumi) + " % \",\n";
|
||||||
|
message += "\t\"SoilHeater\": \"" + OnOffText(plant.SoilHeater) + "\",\n";
|
||||||
|
message += "\t\"SoilTemp_C\": \"" + String(plant.SoilTemp_C) + "\",\n";
|
||||||
|
message += "\t\"AmbientTemp_C\": \"" + String(plant.AmbientTemp_C) + "\",\n";
|
||||||
|
message += "\t\"DrumDoorIcon\": \"" + DrumStateText(plant.DrumDoorIcon) + "\",\n";
|
||||||
|
message += "\t\"DrumOpenSensor\": \"" + DrumOpenText(plant.DrumOpenSensor) + "\",\n";
|
||||||
|
message += "\t\"DrumClosedSensor\": \"" + DrumClosedText(plant.DrumClosedSensor) + "\",\n";
|
||||||
|
message += "\t\"DrumMotorStatus\": \"" + DrumMotorText(plant.DrumMotorStatus) + "\",\n";
|
||||||
|
message += "\t\"DrumMotor_V\": \"" + String(plant.DrumMotor_V) + "\",\n";
|
||||||
|
message += "\t\"DrumMotor_I\": \"" + String(plant.DrumMotor_I) + "\",\n";
|
||||||
|
|
||||||
|
snprintf(_upTime, sizeof(_upTime), "%02u:%02u", plant.DrumMotorOn_sec/60, plant.DrumMotorOn_sec%60);
|
||||||
|
message += "\t\"DrumMotorOn_sec\": \"" + String(_upTime) + "\",\n";
|
||||||
|
message += "\t\"WaterInTank\": \"" + String(plant.WaterInTank) + "\",\n";
|
||||||
|
message += "\t\"WaterMotor_V\": \"" + String(plant.WaterPumpStatus) + "\",\n";
|
||||||
|
message += "\t\"SoilMoisture_X\": \"" + String(plant.SoilMoisture_X) + "\",\n";
|
||||||
|
|
||||||
|
snprintf(_upTime, sizeof(_upTime), "%02u:%02u", plant.WaterCyclePeriod_sec/60, plant.WaterCyclePeriod_sec%60);
|
||||||
|
message += "\t\"WaterCyclePeriod_sec\": \"" + String(_upTime) + "\",\n";
|
||||||
|
|
||||||
|
snprintf(_upTime, sizeof(_upTime), "%02u:%02u", plant.WaterCycleOn_sec/60, plant.WaterCycleOn_sec%60);
|
||||||
|
message += "\t\"WaterCycleOn_sec\": \"" + String(_upTime) + "\",\n";
|
||||||
|
|
||||||
|
snprintf(_upTime, sizeof(_upTime), "%02u:%02u", plant.WaterCycle_sec/60, plant.WaterCycle_sec%60);
|
||||||
|
message += "\t\"waterCycle\": \"" + String(_upTime) + "\"\n";
|
||||||
|
//message += "\t\"toggle\": " + String(!digitalRead(SW_TOGGLE)) + ",\n";
|
||||||
|
//message += "\t\"reset\": " + String(!digitalRead(SW_RESET));
|
||||||
|
message += "}\n";
|
||||||
|
server.sendHeader("Access-Control-Allow-Origin", String("*"), true);
|
||||||
|
server.send(200, "application/json", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void simulation() {
|
||||||
|
if (++plant.SecondsToday >= (24*3600)) { // Seconds are advancing really fast for the simulation
|
||||||
|
plant.SecondsToday -= (24 * 3600);
|
||||||
|
}
|
||||||
|
static int lastMinutesToday = -1;
|
||||||
|
int hoursToday = plant.SecondsToday / 3600;
|
||||||
|
int minutesToday = plant.SecondsToday / 60;
|
||||||
|
// Simulate Ambient temp rising and falling
|
||||||
|
if (lastMinutesToday != minutesToday) {
|
||||||
|
lastMinutesToday = minutesToday;
|
||||||
|
Serial.printf("%02u:%02u, Ambient %3.1f\n", hoursToday, minutesToday % 60, plant.AmbientTemp_C);
|
||||||
|
|
||||||
|
if (hoursToday < 15) {
|
||||||
|
// warming part of the day
|
||||||
|
if (plant.AmbientTemp_C < 45.0) {
|
||||||
|
plant.AmbientTemp_C += (float)(rand() % 10)/1000.0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// cooling part of the day
|
||||||
|
if (plant.AmbientTemp_C > 10.0) {
|
||||||
|
plant.AmbientTemp_C -= (float)(rand() % 10)/1000.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (plant.SoilTemp_C > 40.0) {
|
||||||
|
plant.SoilHeater = false;
|
||||||
|
} else if (plant.SoilTemp_C < 20.0) {
|
||||||
|
plant.SoilHeater = true;
|
||||||
|
}
|
||||||
|
if (plant.SoilHeater) {
|
||||||
|
if (plant.SoilTemp_C < 50.0) {
|
||||||
|
plant.SoilTemp_C += (float)(rand() % 10)/1000.0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (plant.SoilTemp_C < plant.ChamberTemp_C) {
|
||||||
|
plant.SoilTemp_C += (float)(rand() % 10)/1000.0;
|
||||||
|
} else {
|
||||||
|
plant.SoilTemp_C -= (float)(rand() % 10)/1000.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (plant.DrumMotorStatus == DrumCW) {
|
||||||
|
plant.DrumMotorOn_sec++;
|
||||||
|
} else if (plant.DrumMotorStatus == DrumCCW) {
|
||||||
|
plant.DrumMotorOn_sec++;
|
||||||
|
} else {
|
||||||
|
plant.DrumMotorOn_sec = 0;
|
||||||
|
}
|
||||||
|
if (plant.ChamberTemp_C > 50.0) {
|
||||||
|
if (!plant.DrumOpenSensor) {
|
||||||
|
plant.DrumMotorStatus = DrumCW;
|
||||||
|
if (plant.DrumMotorOn_sec > 50) {
|
||||||
|
plant.DrumOpenSensor = true;
|
||||||
|
plant.DrumClosedSensor = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (plant.ChamberTemp_C < 40.0) {
|
||||||
|
if (!plant.DrumClosedSensor) {
|
||||||
|
plant.DrumMotorStatus = DrumCW;
|
||||||
|
if (plant.DrumMotorOn_sec > 50) {
|
||||||
|
plant.DrumOpenSensor = false;
|
||||||
|
plant.DrumClosedSensor = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (plant.DrumDoorIcon == DrumIsOpen) {
|
||||||
|
if (plant.ChamberTemp_C > plant.AmbientTemp_C) {
|
||||||
|
plant.ChamberTemp_C -= (float)(rand() % 10)/1000.0;
|
||||||
|
} else if (plant.ChamberTemp_C < plant.AmbientTemp_C) {
|
||||||
|
plant.ChamberTemp_C += (float)(rand() % 10)/1000.0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (plant.ChamberTemp_C > plant.AmbientTemp_C) {
|
||||||
|
plant.ChamberTemp_C -= (float)(rand() % 5)/1000.0;
|
||||||
|
} else if (plant.ChamberTemp_C < plant.AmbientTemp_C) {
|
||||||
|
plant.ChamberTemp_C += (float)(rand() % 20)/1000.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
plant.ChamberHumi = (float)(rand() % 73) + 25;
|
||||||
|
if (++plant.WaterCycle_sec >= plant.WaterCyclePeriod_sec) {
|
||||||
|
plant.WaterCycle_sec -= plant.WaterCyclePeriod_sec;
|
||||||
|
}
|
||||||
|
if (plant.WaterCycle_sec >= plant.WaterCycleOn_sec) {
|
||||||
|
plant.WaterPumpStatus = false;
|
||||||
|
}
|
||||||
|
if (plant.SoilMoisture_X < 60.0 && plant.WaterCycle_sec < plant.WaterCycleOn_sec) {
|
||||||
|
plant.WaterPumpStatus = true;
|
||||||
|
}
|
||||||
|
if (plant.WaterPumpStatus) {
|
||||||
|
plant.SoilMoisture_X += (float)(rand() % 40)/100.0;
|
||||||
|
} else {
|
||||||
|
plant.SoilMoisture_X -= (float)(rand() % 5)/100.0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
Firmware/PlantModel.h
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ProjGlobals.h"
|
||||||
|
|
||||||
|
void HandleGetState();
|
||||||
|
void simulation();
|
||||||
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// Some settings
|
// Project Global Settings
|
||||||
//
|
//
|
||||||
|
|
||||||
#ifndef PROJGLOBALS_H
|
#ifndef PROJGLOBALS_H
|
||||||
@@ -7,10 +7,60 @@
|
|||||||
|
|
||||||
#include <ESP8266WebServer.h>
|
#include <ESP8266WebServer.h>
|
||||||
|
|
||||||
|
// Manage the WiFi Configuration, AP, SSID, etc.
|
||||||
#include "WiFiConfiguration.h"
|
#include "WiFiConfiguration.h"
|
||||||
|
|
||||||
extern bool isStationMode;
|
typedef enum {
|
||||||
|
DrumIsOpen,
|
||||||
|
DrumIsClosed,
|
||||||
|
DrumIsMoving,
|
||||||
|
DrumIsStuck,
|
||||||
|
} DrumState_E;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
DrumOff,
|
||||||
|
DrumCW,
|
||||||
|
DrumCCW
|
||||||
|
} DrumMotor_E;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t SecondsToday; // elapsed seconds from midnight (for simulation)
|
||||||
|
bool Fahrenheit; // Show temp in Fahrenheit.
|
||||||
|
|
||||||
|
float ChamberTemp_C; // Chamber Temp
|
||||||
|
float ChamberHumi; // Chamber Humidity - if installed
|
||||||
|
|
||||||
|
bool SoilHeater;
|
||||||
|
float SoilTemp_C; // Soil Temp - if installed
|
||||||
|
float SoilMoisture_X; // Soil Moisture - unitless number, if installed
|
||||||
|
|
||||||
|
float AmbientTemp_C; //
|
||||||
|
|
||||||
|
DrumState_E DrumDoorIcon; // Current State: open/close/moving
|
||||||
|
bool DrumOpenSensor;
|
||||||
|
bool DrumClosedSensor;
|
||||||
|
DrumMotor_E DrumMotorStatus; // Off, CW, CCW
|
||||||
|
uint32_t DrumMotorOn_sec;
|
||||||
|
float DrumMotor_V;
|
||||||
|
float DrumMotor_I;
|
||||||
|
|
||||||
|
bool WaterInTank; // controls image of the WaterLevelIcon
|
||||||
|
bool WaterPumpStatus; // WaterLevelIcon pump on
|
||||||
|
float WaterMotor_V;
|
||||||
|
uint32_t WaterCyclePeriod_sec; // Total Time (allows WaterLevelIcon to soak in)
|
||||||
|
uint32_t WaterCycleOn_sec; // WaterLevelIcon Pump on time
|
||||||
|
uint32_t WaterCycle_sec; // Running timer
|
||||||
|
} PlantData_T;
|
||||||
|
|
||||||
|
|
||||||
|
extern void FactoryReset(bool forceRestart);
|
||||||
|
extern const String AP_NAME;
|
||||||
|
extern const String AP_VERS;
|
||||||
|
// extern bool isStationMode;
|
||||||
extern ESP8266WebServer server;
|
extern ESP8266WebServer server;
|
||||||
extern ConfigManager wifiConfig;
|
extern ConfigManager wifiConfig;
|
||||||
|
|
||||||
#endif // PROJGLOBALS_H
|
extern PlantData_T plant; /// This keeps track of everything relevant to this system
|
||||||
|
extern bool updateCheck;
|
||||||
|
|
||||||
|
#endif // PROJGLOBALS_H
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
|
||||||
|
|
||||||
input.BigButton {
|
input.BigButton {
|
||||||
width: 150px;
|
width: 150px;
|
||||||
padding: 20px;
|
height: 100px;
|
||||||
|
padding: 10px;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
text-align: center;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-size: 120%;
|
font-size: 120%;
|
||||||
background: #3366cc;
|
background: #3366cc;
|
||||||
@@ -19,3 +23,26 @@ input.BigButton:hover {
|
|||||||
background: #000;
|
background: #000;
|
||||||
border: 5px solid #fff;
|
border: 5px solid #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.SmallButton {
|
||||||
|
width: 150px;
|
||||||
|
height: 50px;
|
||||||
|
padding: 10px;
|
||||||
|
white-space: normal;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 100%;
|
||||||
|
background: #3366cc;
|
||||||
|
color: #fff;
|
||||||
|
border: 5px solid #112233;
|
||||||
|
border-radius: 10px;
|
||||||
|
-moz-box-shadow: 6px 6px 5px #999;
|
||||||
|
-webkit-box-shadow: 6px 6px 5px #999;
|
||||||
|
box-shadow: 6px 6px 5px #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.SmallButton:hover {
|
||||||
|
color: #ffff00;
|
||||||
|
background: #000;
|
||||||
|
border: 5px solid #fff;
|
||||||
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 443 B |
|
Before Width: | Height: | Size: 655 B After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 22 KiB |
@@ -2,25 +2,25 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Grow Controller</title>
|
<title>Grow Controller</title>
|
||||||
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width">
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
|
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, width=device-width">
|
||||||
<script type='text/javascript' src='myip.js'></script>
|
<script type='text/javascript' src='myip.js'></script>
|
||||||
<script type='text/javascript' src='about.js'></script>
|
<script type='text/javascript' src='about.js'></script>
|
||||||
|
<script type='text/javascript' src='nav.js'></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body onload="NavInit();">
|
||||||
<h1>Grow Controller</h1>
|
<h1>Grow Controller</h1>
|
||||||
by Smartware Computing
|
by Smartware Computing
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<table cellpadding="5">
|
<table cellpadding="5">
|
||||||
<tr valign="top">
|
<tr valign="top">
|
||||||
<td><img src="/icon.jpg" /></td>
|
<td><img src="icon.png" title="logo" /></td>
|
||||||
<td>
|
<td>
|
||||||
This Grow Controller is an IOT device based on an ESP8266-12 module. The software for it has been
|
This Grow Controller is an IOT device based on an ESP8266-12 module. The software for it has been
|
||||||
derived from various sources and where copyrights have been identified, they are listed below.<br />
|
derived from various sources and where copyrights have been identified, they are listed below.<br />
|
||||||
<ul>
|
<ul>
|
||||||
<li>The composite work is Copyright © 2018-2021 by Smartware Computing, all rights reserved.</li>
|
<li>The composite work is Copyright © 2018-2021 by Smartware Computing, all rights reserved.</li>
|
||||||
<li>Library for WEMO emulation, Copyright © 2016 by Xose Pérez</li>
|
|
||||||
<li>Libraries; Copyright © 2001-2013 Free Software Foundation, Inc.</li>
|
<li>Libraries; Copyright © 2001-2013 Free Software Foundation, Inc.</li>
|
||||||
<li>Libraries; Copyright (c) 1997 Silicon Graphics Computer Systems, Inc.</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<dl>
|
<dl>
|
||||||
<dt>Toggle Button</dt>
|
<dt>Toggle Button</dt>
|
||||||
@@ -36,20 +36,6 @@
|
|||||||
Uptime:
|
Uptime:
|
||||||
|
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
<span id='nav'></span>
|
||||||
<table>
|
|
||||||
<tr valign="top">
|
|
||||||
<td>NAV:</td>
|
|
||||||
<td>
|
|
||||||
<a href='/'>Home</a> |
|
|
||||||
<a href='/config'>Config</a> |
|
|
||||||
<a href='/scan'>Scan</a> |
|
|
||||||
<a href='/rssi'>RSSI</a> |
|
|
||||||
<a href='/curr'>Current</a> |
|
|
||||||
<a href='/config?ssid=reset&pass=reset' onclick=\"return confirm('Are you sure?')\">Factory Reset</a> |
|
|
||||||
<a href='/about'>About</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -2,11 +2,13 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Grow Controller</title>
|
<title>Grow Controller</title>
|
||||||
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width">
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
|
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, width=device-width">
|
||||||
<script type='text/javascript' src='myip.js'></script>
|
<script type='text/javascript' src='myip.js'></script>
|
||||||
<script type='text/javascript' src='curr.js'></script>
|
<script type='text/javascript' src='curr.js'></script>
|
||||||
|
<script type='text/javascript' src='nav.js'></script>
|
||||||
</head>
|
</head>
|
||||||
<body onload='CurrInit();'>
|
<body onload='CurrInit(); NavInit();'>
|
||||||
<h1>Grow Controller - Current Sense</h1>
|
<h1>Grow Controller - Current Sense</h1>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<table width='800' border='0'>
|
<table width='800' border='0'>
|
||||||
@@ -25,19 +27,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<table>
|
<span id='nav'></span>
|
||||||
<tr valign="top">
|
|
||||||
<td>NAV:</td>
|
|
||||||
<td>
|
|
||||||
<a href='/'>Home</a> |
|
|
||||||
<a href='/config'>Config</a> |
|
|
||||||
<a href='/scan'>Scan</a> |
|
|
||||||
<a href='/rssi'>RSSI</a> |
|
|
||||||
<a href='/curr'>Current</a> |
|
|
||||||
<a href='/config?ssid=reset&pass=reset' onclick=\"return confirm('Are you sure?')\">Factory Reset</a> |
|
|
||||||
<a href='/about'>About</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ var axes = {};
|
|||||||
var totalSamples = 400; // Width of the graph
|
var totalSamples = 400; // Width of the graph
|
||||||
for (var i = 0; i < totalSamples; i++)
|
for (var i = 0; i < totalSamples; i++)
|
||||||
rawData[i] = avgData[i] = 0;
|
rawData[i] = avgData[i] = 0;
|
||||||
setInterval(RefreshStatus, 250);
|
setInterval(RefreshStatus, 500);
|
||||||
|
|
||||||
// -------------------------
|
// -------------------------
|
||||||
|
|
||||||
|
|||||||
@@ -2,133 +2,16 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<title>Grow Controller</title>
|
<title>Grow Controller</title>
|
||||||
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width">
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
<style>
|
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, width=device-width">
|
||||||
input.SmallButton {
|
<link rel="stylesheet" type="text/css" href="button.css" media="all" />
|
||||||
width: 150px;
|
<link rel="stylesheet" type="text/css" href="plantmodel.css" media="all" />
|
||||||
height: 50px;
|
<script type='text/javascript' src='nav.js'></script>
|
||||||
padding: 10px;
|
|
||||||
white-space: normal;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 100%;
|
|
||||||
background: #3366cc;
|
|
||||||
color: #fff;
|
|
||||||
border: 5px solid #112233;
|
|
||||||
border-radius: 10px;
|
|
||||||
-moz-box-shadow: 6px 6px 5px #999;
|
|
||||||
-webkit-box-shadow: 6px 6px 5px #999;
|
|
||||||
box-shadow: 6px 6px 5px #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.SmallButton:hover {
|
|
||||||
color: #ffff00;
|
|
||||||
background: #000;
|
|
||||||
border: 5px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.BigButton {
|
|
||||||
width: 150px;
|
|
||||||
height: 100px;
|
|
||||||
padding: 10px;
|
|
||||||
white-space: normal;
|
|
||||||
cursor: pointer;
|
|
||||||
font-weight: bold;
|
|
||||||
font-size: 120%;
|
|
||||||
background: #3366cc;
|
|
||||||
color: #fff;
|
|
||||||
border: 5px solid #112233;
|
|
||||||
border-radius: 10px;
|
|
||||||
-moz-box-shadow: 6px 6px 5px #999;
|
|
||||||
-webkit-box-shadow: 6px 6px 5px #999;
|
|
||||||
box-shadow: 6px 6px 5px #999;
|
|
||||||
}
|
|
||||||
|
|
||||||
input.BigButton:hover {
|
|
||||||
color: #ffff00;
|
|
||||||
background: #000;
|
|
||||||
border: 5px solid #fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.box {
|
|
||||||
position: relative;
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
div.airtemp {
|
|
||||||
position: absolute;
|
|
||||||
left: 170px;
|
|
||||||
top: 50px;
|
|
||||||
//border: 2px solid lightgray;
|
|
||||||
}
|
|
||||||
div.soilmoisture {
|
|
||||||
position: absolute;
|
|
||||||
left: 140px;
|
|
||||||
top: 75px;
|
|
||||||
//border: 2px solid lightgray;
|
|
||||||
}
|
|
||||||
div.soiltemp {
|
|
||||||
position: absolute;
|
|
||||||
left: 150px;
|
|
||||||
top: 120px;
|
|
||||||
color: white;
|
|
||||||
//border: 2px solid lightgray;
|
|
||||||
}
|
|
||||||
div.heater {
|
|
||||||
position: absolute;
|
|
||||||
left: 105px;
|
|
||||||
top: 156px;
|
|
||||||
color: white;
|
|
||||||
//border: 2px solid lightgray;
|
|
||||||
}
|
|
||||||
div.ambtemp {
|
|
||||||
position: absolute;
|
|
||||||
left: 205px;
|
|
||||||
top: 205px;
|
|
||||||
//border: 2px solid lightgray;
|
|
||||||
}
|
|
||||||
div.drumMotorV {
|
|
||||||
position: absolute;
|
|
||||||
left: 190px;
|
|
||||||
top: 290px;
|
|
||||||
//border: 2px solid lightgray;
|
|
||||||
}
|
|
||||||
div.drumMotorI {
|
|
||||||
position: absolute;
|
|
||||||
left: 180px;
|
|
||||||
top: 320px;
|
|
||||||
//border: 2px solid lightgray;
|
|
||||||
}
|
|
||||||
div.waterMotorV {
|
|
||||||
position: absolute;
|
|
||||||
left: 210px;
|
|
||||||
top: 390px;
|
|
||||||
//border: 2px solid lightgray;
|
|
||||||
}
|
|
||||||
div.waterLevel {
|
|
||||||
position: absolute;
|
|
||||||
left: 85px;
|
|
||||||
top: 395px;
|
|
||||||
//border: 2px solid lightgray;
|
|
||||||
}
|
|
||||||
div.heaterCoil {
|
|
||||||
position: absolute;
|
|
||||||
left: 78px;
|
|
||||||
top: 177px;
|
|
||||||
}
|
|
||||||
div.water {
|
|
||||||
position: absolute;
|
|
||||||
left: 52px;
|
|
||||||
top: 420px;
|
|
||||||
z-index: -55;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<script type='text/javascript' src='myip.js'></script>
|
<script type='text/javascript' src='myip.js'></script>
|
||||||
|
<script type='text/javascript' src='about.js'></script>
|
||||||
<script type='text/javascript' src='index.js'></script>
|
<script type='text/javascript' src='index.js'></script>
|
||||||
</head>
|
</head>
|
||||||
<body onload="RefreshStatus();">
|
<body onload="NavInit();">
|
||||||
<h1><span id='name'>Grow Controller</span></h1>
|
<h1><span id='name'>Grow Controller</span></h1>
|
||||||
<form>
|
<form>
|
||||||
<table border='0'>
|
<table border='0'>
|
||||||
@@ -137,19 +20,44 @@
|
|||||||
<div class="box">
|
<div class="box">
|
||||||
<div style="position: relative; z-index: -10;">
|
<div style="position: relative; z-index: -10;">
|
||||||
<!-- 243 x 488 -->
|
<!-- 243 x 488 -->
|
||||||
<img src='PlantModel.png' />
|
<img src='plantmodel.png' title='Picture of the System' />
|
||||||
</div>
|
</div>
|
||||||
<div class="airtemp">92°F</div>
|
<div class='TimeOfDay' id='TimeOfDay'>--:--</div>
|
||||||
<div class="soilmoisture">8%</div>
|
|
||||||
<div class="soiltemp">74°F</div>
|
<div class='AmbientTemp_C' id='AmbientTemp_C' >--.- °F</div>
|
||||||
<div class="heater">ON</div>
|
<div class='AmbientTempIcon' id='AmbientTempIcon' ><img src='thermometer.png' title='Ambient Temperature' /></div>
|
||||||
<div class="ambtemp">65°F</div>
|
|
||||||
<div class="drumMotorV">12.3V</div>
|
<div class='ChamberTemp_C' id='ChamberTemp_C' >--.- °F</div>
|
||||||
<div class="drumMotorI">1.2A</div>
|
<div class='ChamberHumi' id='ChamberHumi' >-- %</div>
|
||||||
<div class="waterMotorV">0.0V</div>
|
<div class='ChamberTempIcon' id='ChamberTempIcon' ><img src='thermometer.png' title='Chamber Temperature' /></div>
|
||||||
<div class="waterLevel">OK</div>
|
<div class='SoilHeater' id='SoilHeater' >Heat</div>
|
||||||
<div class="heaterCoil"><img src="Heater.png" /></div>
|
<div class='SoilHeaterOnIcon' id='SoilHeaterOnIcon' ><img src='soilheater_on.png' title='Soil Heater' /></div>
|
||||||
<div class="water"><img src="Water.png" /></div>
|
<div class='SoilHeaterOffIcon' id='SoilHeaterOffIcon' ><img src='soilheater_off.png' title='Soil Heater' /></div>
|
||||||
|
|
||||||
|
<div class='SoilThermoIcon' id='SoilThermoIcon' ><img src='thermometer.png' title='Soil Temperature' /></div>
|
||||||
|
<div class='SoilHumiIcon' id='SoilHumiIcon' ><img src='soilmoisture.png' title='Soil Temperature' /></div>
|
||||||
|
<div class='SoilTemp_C' id='SoilTemp_C' >--.- °F</div>
|
||||||
|
<div class='SoilMoisture_X' id='SoilMoisture_X' >-- %</div>
|
||||||
|
|
||||||
|
<div class='DrumDoorIcon' id='DrumDoorIcon' ><img src='open.png' title='Open' /></div>
|
||||||
|
<div class='DrumOpenSensor' id='DrumOpenSensor' >O</div>
|
||||||
|
<div class='DrumClosedSensor' id='DrumClosedSensor' >C</div>
|
||||||
|
<div class='DrumMotorLabel' id='DrumMotorLabel' >Motor</div>
|
||||||
|
<div class='DrumMotorStatus' id='DrumMotorStatus' >Off</div>
|
||||||
|
<div class='DrumMotor_V' id='DrumMotor_V' >12.5 V</div>
|
||||||
|
<div class='DrumMotor_I' id='DrumMotor_I' >1.2 A</div>
|
||||||
|
<div class='DrumMotorOn_sec' id='DrumMotorOn_sec' >10 s</div>
|
||||||
|
|
||||||
|
<div class='WaterInTank' id='WaterInTank'>Low Water</div>
|
||||||
|
<div class='WaterPumpLabel' id='WaterPumpLabel' >Pump</div>
|
||||||
|
<div class='WaterPumpStatus' id='WaterPumpStatus' >On</div>
|
||||||
|
<div class='WaterLevelIcon' id='WaterLevelIcon'><img src='water.png' title='Water in Tank' /></div>
|
||||||
|
<div class='WaterMotor_V' id='WaterMotor_V' >10.7 V</div>
|
||||||
|
<div class='WaterCyclePeriod_sec' id='WaterCyclePeriod_sec' >90 s</div>
|
||||||
|
<div class='WaterCycleOn_sec' id='WaterCycleOn_sec' >8 s</div>
|
||||||
|
<div class='WaterCycle_sec' id='WaterCycle_sec' >10 s</div>
|
||||||
|
<div class='WaterCycleOnBox' id='WaterCycleOnBox' ></div>
|
||||||
|
<div class='WaterCycleStatusIcon' id='WaterCycleStatusIcon' ><img src='pointer.png' title='Pump On Time' /></div>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td width='10%' align='center'>
|
<td width='10%' align='center'>
|
||||||
@@ -174,33 +82,19 @@
|
|||||||
</table>
|
</table>
|
||||||
</form>
|
</form>
|
||||||
<p></p>
|
<p></p>
|
||||||
<table border='0'>
|
<span id='state'></span>
|
||||||
<tr valign="top">
|
<span id='raw'></span>
|
||||||
<td>NAV:</td>
|
<span id='sense'></span>
|
||||||
<td>
|
<span id='toggle'></span>
|
||||||
<a href='/'>Home</a> |
|
<span id='reset'></span>
|
||||||
<a href='/config'>Config</a> |
|
<span id='countdown'></span>
|
||||||
<a href='/scan'>Scan</a> |
|
<span id='uptime'></span>
|
||||||
<a href='/rssi'>RSSI</a> |
|
<span id='wifimode'></span>
|
||||||
<a href='/curr'>Current</a> |
|
<span id='nav'></span>
|
||||||
<a href='/config?ssid=reset&pass=reset' onclick=\"return confirm('Are you sure?')\">Factory Reset</a> |
|
|
||||||
<a href='/about'>About</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<!-- tr valign="top">
|
|
||||||
<td>M2M:</td>
|
|
||||||
<td>
|
|
||||||
<a href='/on'>http://<span class='ip'></span>/on</a> |
|
|
||||||
<a href='/off'>http://<span class='ip'></span>/off</a> |
|
|
||||||
<a href='/toggle'>http://<span class='ip'></span>/toggle</a> |
|
|
||||||
<a href='/state'>http://<span class='ip'></span>/state</a>
|
|
||||||
</td>
|
|
||||||
</tr!-->
|
|
||||||
</table>
|
|
||||||
<hr>
|
<hr>
|
||||||
<table border='0'>
|
<table border='0'>
|
||||||
<tr>
|
<tr>
|
||||||
<td>GrowController by Origin Technologies</td>
|
<td>GrowController by Smartware Computing</td>
|
||||||
<td align="right"><a href="/swupdatecheck">Firmware</a>: <span id='version'>v0.00.00</span></td>
|
<td align="right"><a href="/swupdatecheck">Firmware</a>: <span id='version'>v0.00.00</span></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
//
|
//
|
||||||
// rssi script
|
// Main Page period status update
|
||||||
//
|
//
|
||||||
// var mySite = 'http://192.168.1.23' from myip.js
|
// var mySite = 'http://192.168.1.23'; // from myip.js
|
||||||
var url = mySite + '/state';
|
var url = mySite + '/state';
|
||||||
setInterval(RefreshStatus, 250);
|
setInterval(RefreshStatus, 2500);
|
||||||
|
|
||||||
getIt = function (aUrl, callback) {
|
getIt = function (aUrl, callback) {
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
@@ -17,6 +17,14 @@ getIt = function (aUrl, callback) {
|
|||||||
xhr.send();
|
xhr.send();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function ConvertTempToString(tempC, showF) {
|
||||||
|
var tempF = tempC * 9 / 5 + 32;
|
||||||
|
if (showF)
|
||||||
|
return tempF.toFixed(1) + "°F";
|
||||||
|
else
|
||||||
|
return tempC.toFixed(1) + "°C";
|
||||||
|
}
|
||||||
|
|
||||||
function RefreshStatus() {
|
function RefreshStatus() {
|
||||||
getIt(url,
|
getIt(url,
|
||||||
function(data) {
|
function(data) {
|
||||||
@@ -27,18 +35,72 @@ function RefreshStatus() {
|
|||||||
state = "Off";
|
state = "Off";
|
||||||
document.getElementById('version').innerHTML = obj.version;
|
document.getElementById('version').innerHTML = obj.version;
|
||||||
document.getElementById('name').innerHTML = obj.name;
|
document.getElementById('name').innerHTML = obj.name;
|
||||||
document.getElementById('state').innerHTML = 'Output: ' + state;
|
//document.getElementById('state').innerHTML = 'Output: ' + state;
|
||||||
document.getElementById('raw').innerHTML = 'raw: ' + obj.raw;
|
//document.getElementById('raw').innerHTML = 'raw: ' + obj.raw;
|
||||||
document.getElementById('sense').innerHTML = 'Sense: ' + obj.sense;
|
//document.getElementById('sense').innerHTML = 'Sense: ' + obj.sense;
|
||||||
document.getElementById('toggle').innerHTML = 'Toggle: ' + obj.toggle;
|
//document.getElementById('toggle').innerHTML = 'Toggle: ' + obj.toggle;
|
||||||
document.getElementById('reset').innerHTML = 'Reset: ' + obj.reset;
|
//document.getElementById('reset').innerHTML = 'Reset: ' + obj.reset;
|
||||||
document.getElementById('countdown').innerHTML = obj.countdown;
|
//document.getElementById('countdown').innerHTML = 'Countdown: ' + obj.countdown;
|
||||||
document.getElementById('uptime').innerHTML = 'Uptime: ' + obj.uptime;
|
document.getElementById('uptime').innerHTML = 'Uptime: ' + obj.uptime;
|
||||||
document.getElementById('wifimode').innerHTML = 'Mode: ' + obj.wifimode;
|
document.getElementById('wifimode').innerHTML = 'WiFi Mode: ' + obj.wifimode;
|
||||||
|
// IP Address
|
||||||
var elms = document.querySelectorAll('.' + 'ip'), i;
|
var elms = document.querySelectorAll('.' + 'ip'), i;
|
||||||
for (i = 0; i < elms.length; ++i) {
|
for (i = 0; i < elms.length; ++i) {
|
||||||
elms[i].textContent = obj.ip;
|
elms[i].textContent = obj.ip;
|
||||||
}
|
}
|
||||||
|
// Plant status
|
||||||
|
document.getElementById('TimeOfDay').innerHTML = obj.TimeOfDay;
|
||||||
|
var userF = obj.Fahrenheit;
|
||||||
|
|
||||||
|
if (typeof obj.AmbientTemp_C != "undefined") {
|
||||||
|
document.getElementById('AmbientTemp_C').innerHTML = ConvertTempToString(obj.AmbientTemp_C, userF);
|
||||||
|
} else {
|
||||||
|
document.getElementById('AmbientTemp_C').innerHTML = "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj.ChamberTemp_C != "undefined") {
|
||||||
|
document.getElementById('ChamberTemp_C').innerHTML = ConvertTempToString(obj.ChamberTemp_C, userF);
|
||||||
|
document.getElementById('ChamberHumi').innerHTML = parseInt(obj.ChamberHumi) + "%";
|
||||||
|
} else {
|
||||||
|
document.getElementById('ChamberTemp_C').innerHTML = "--";
|
||||||
|
document.getElementById('ChamberHumi').innerHTML = "--"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj.SoilHeater != "undefined") {
|
||||||
|
document.getElementById('SoilHeater').innerHTML = obj.SoilHeater;
|
||||||
|
} else {
|
||||||
|
document.getElementById('SoilHeater').innerHTML = "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof obj.SoilTemp_C != "undefined") {
|
||||||
|
document.getElementById('SoilTemp_C').innerHTML = ConvertTempToString(obj.SoilTemp_C, userF);
|
||||||
|
} else {
|
||||||
|
document.getElementById('SoilTemp_C').innerHTML = "--";
|
||||||
|
}
|
||||||
|
if (typeof obj.SoilTemp_C != "undefined") {
|
||||||
|
document.getElementById('SoilMoisture_X').innerHTML = obj.SoilMoisture_X;
|
||||||
|
} else {
|
||||||
|
document.getElementById('SoilMoisture_X').innerHTML = "--";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (obj.DrumDoorIcon == 1) {
|
||||||
|
document.getElementById('DrumDoorIcon').style.display === "none";
|
||||||
|
} else {
|
||||||
|
document.getElementById('DrumDoorIcon').style.display === "block";
|
||||||
|
}
|
||||||
|
document.getElementById('DrumOpenSensor').innerHTML = obj.DrumOpenSensor;
|
||||||
|
document.getElementById('DrumClosedSensor').innerHTML = obj.DrumClosedSensor;
|
||||||
|
document.getElementById('DrumMotorStatus').innerHTML = obj.DrumMotorStatus;
|
||||||
|
document.getElementById('DrumMotorOn_sec').innerHTML = obj.DrumMotorOn_sec + "s";
|
||||||
|
document.getElementById('DrumMotor_V').innerHTML = parseFloat(obj.DrumMotor_V).toFixed(1) + "V";
|
||||||
|
document.getElementById('DrumMotor_I').innerHTML = parseFloat(obj.DrumMotor_I).toFixed(1) + "A";
|
||||||
|
|
||||||
|
document.getElementById('WaterInTank').innerHTML = obj.WaterInTank;
|
||||||
|
document.getElementById('WaterPumpStatus').innerHTML = obj.WaterPumpStatus;
|
||||||
|
document.getElementById('WaterMotor_V').innerHTML = parseFloat(obj.WaterMotor_V).toFixed(1) + "V";
|
||||||
|
document.getElementById('WaterCyclePeriod_sec').innerHTML = obj.WaterCyclePeriod_sec + "s";
|
||||||
|
document.getElementById('WaterCycleOn_sec').innerHTML = obj.WaterCycleOn_sec + "s";
|
||||||
|
document.getElementById('WaterCycle_sec').innerHTML = obj.WaterCycle_sec + "s";
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
5
Firmware/Resources/myip.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
// Normally provided by the application intercepting this request,
|
||||||
|
// but for debug it can be handy to fix it.
|
||||||
|
//
|
||||||
|
var mySite = 'http://192.168.1.10'; // from myip.js
|
||||||
20
Firmware/Resources/nav.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
function NavInit() {
|
||||||
|
nav = document.getElementById('nav');
|
||||||
|
var txt = "<table>";
|
||||||
|
txt += "<tr valign='top'>";
|
||||||
|
txt += " <td>NAV:</td>";
|
||||||
|
txt += " <td>";
|
||||||
|
txt += " <a href='index.htm'>Home</a> | ";
|
||||||
|
txt += " <a href='config'>Config</a> | ";
|
||||||
|
txt += " <a href='scan'>Scan</a> | ";
|
||||||
|
txt += " <a href='rssi.htm'>RSSI</a> | ";
|
||||||
|
txt += " <a href='curr.htm'>Current</a> | ";
|
||||||
|
txt += " <a href='config?ssid=reset&pass=reset' onclick=\\\"return confirm('Are you sure?')\\\">Factory Reset</a> | ";
|
||||||
|
txt += " <a href='about.htm'>About</a>";
|
||||||
|
txt += " </td>";
|
||||||
|
txt += "</tr>";
|
||||||
|
txt += "</table>";
|
||||||
|
nav.innerHTML = txt;
|
||||||
|
}
|
||||||
22
Firmware/Resources/navigation.htm
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<table>
|
||||||
|
<tr valign="top">
|
||||||
|
<td>NAV:</td>
|
||||||
|
<td>
|
||||||
|
<a href='/'>Home</a> |
|
||||||
|
<a href='config'>Config</a> |
|
||||||
|
<a href='scan'>Scan</a> |
|
||||||
|
<a href='rssi'>RSSI</a> |
|
||||||
|
<a href='curr'>Current</a> |
|
||||||
|
<a href='config?ssid=reset&pass=reset' onclick=\"return confirm('Are you sure?')\">Factory Reset</a> |
|
||||||
|
<a href='about'>About</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
249
Firmware/Resources/plantmodel.css
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
<style>
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.box {
|
||||||
|
position: relative;
|
||||||
|
display: inline-block;
|
||||||
|
border-color: black;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
div.TimeOfDay {
|
||||||
|
position: absolute;
|
||||||
|
right: 0px;
|
||||||
|
top: 2px;
|
||||||
|
width: 40px;
|
||||||
|
text-align: right;
|
||||||
|
border-color: green;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.AmbientTemp_C {
|
||||||
|
position: absolute;
|
||||||
|
left: 15px;
|
||||||
|
top: 2px;
|
||||||
|
width: 40px;
|
||||||
|
text-align: right;
|
||||||
|
border-color: green;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
div.AmbientTempIcon {
|
||||||
|
position: absolute;
|
||||||
|
left: 0px;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.ChamberTempIcon {
|
||||||
|
position: absolute;
|
||||||
|
left: 142px;
|
||||||
|
top: 40px;
|
||||||
|
}
|
||||||
|
div.ChamberTemp_C {
|
||||||
|
position: absolute;
|
||||||
|
left: 160px;
|
||||||
|
top: 40px;
|
||||||
|
}
|
||||||
|
div.ChamberHumi {
|
||||||
|
position: absolute;
|
||||||
|
left: 160px;
|
||||||
|
top: 55px;
|
||||||
|
}
|
||||||
|
div.SoilMoisture_X {
|
||||||
|
position: absolute;
|
||||||
|
left: 130px;
|
||||||
|
top: 135px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div.SoilHumiIcon {
|
||||||
|
position: absolute;
|
||||||
|
left: 140px;
|
||||||
|
top: 95px;
|
||||||
|
}
|
||||||
|
div.SoilThermoIcon {
|
||||||
|
position: absolute;
|
||||||
|
left: 170px;
|
||||||
|
top: 100px;
|
||||||
|
}
|
||||||
|
div.SoilTemp_C {
|
||||||
|
position: absolute;
|
||||||
|
left: 165px;
|
||||||
|
top: 135px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div.SoilHeater {
|
||||||
|
position: absolute;
|
||||||
|
left: 105px;
|
||||||
|
top: 156px;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div.SoilHeaterOnIcon {
|
||||||
|
position: absolute;
|
||||||
|
left: 76px;
|
||||||
|
top: 167px;
|
||||||
|
}
|
||||||
|
div.SoilHeaterOffIcon {
|
||||||
|
position: absolute;
|
||||||
|
left: 89px;
|
||||||
|
top: 181px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.DrumDoorIcon {
|
||||||
|
position: absolute;
|
||||||
|
transform: rotate(180deg);
|
||||||
|
left: 14px;
|
||||||
|
top: 0px;
|
||||||
|
}
|
||||||
|
div.DrumOpenSensor {
|
||||||
|
position: absolute;
|
||||||
|
left: 20px;
|
||||||
|
top: 96px;
|
||||||
|
}
|
||||||
|
div.DrumClosedSensor {
|
||||||
|
position: absolute;
|
||||||
|
right: 10px;
|
||||||
|
top: 96px;
|
||||||
|
}
|
||||||
|
div.DrumMotorLabel {
|
||||||
|
position: absolute;
|
||||||
|
left: 95px;
|
||||||
|
top: 270px;
|
||||||
|
width: 50px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div.DrumMotorStatus {
|
||||||
|
position: absolute;
|
||||||
|
left: 95px;
|
||||||
|
top: 290px;
|
||||||
|
width: 50px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div.DrumMotor_V {
|
||||||
|
position: absolute;
|
||||||
|
left: 180px;
|
||||||
|
top: 225px;
|
||||||
|
width: 50px;
|
||||||
|
text-align: right;
|
||||||
|
border-color: green;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
//color: Red;
|
||||||
|
}
|
||||||
|
div.DrumMotor_I {
|
||||||
|
position: absolute;
|
||||||
|
left: 180px;
|
||||||
|
top: 245px;
|
||||||
|
width: 50px;
|
||||||
|
text-align: right;
|
||||||
|
border-color: green;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
div.DrumMotorOn_sec {
|
||||||
|
position: absolute;
|
||||||
|
left: 180px;
|
||||||
|
top: 315px;
|
||||||
|
width: 50px;
|
||||||
|
text-align: right;
|
||||||
|
border-color: green;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.WaterInTank {
|
||||||
|
position: absolute;
|
||||||
|
left: 55px;
|
||||||
|
top: 365px;
|
||||||
|
width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
div.WaterPumpLabel {
|
||||||
|
position: absolute;
|
||||||
|
left: 124px;
|
||||||
|
top: 390px;
|
||||||
|
width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div.WaterPumpStatus {
|
||||||
|
position: absolute;
|
||||||
|
left: 124px;
|
||||||
|
top: 410px;
|
||||||
|
width: 80px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
div.WaterLevelIcon {
|
||||||
|
position: absolute;
|
||||||
|
left: 42px;
|
||||||
|
top: 393px;
|
||||||
|
z-index: -55;
|
||||||
|
}
|
||||||
|
div.WaterMotor_V {
|
||||||
|
position: absolute;
|
||||||
|
left: 180px;
|
||||||
|
top: 350px;
|
||||||
|
width: 50px;
|
||||||
|
text-align: right;
|
||||||
|
border-color: green;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
div.WaterCyclePeriod_sec {
|
||||||
|
position: absolute;
|
||||||
|
left: 182px;
|
||||||
|
top: 467px;
|
||||||
|
width: 50px;
|
||||||
|
text-align: right;
|
||||||
|
border-color: green;
|
||||||
|
//border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
div.WaterCycleOn_sec {
|
||||||
|
position: absolute;
|
||||||
|
left: 23px;
|
||||||
|
top: 467px;
|
||||||
|
width: 50px;
|
||||||
|
text-align: right;
|
||||||
|
border-color: green;
|
||||||
|
//border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
div.WaterCycle_sec {
|
||||||
|
position: absolute;
|
||||||
|
left: 180px;
|
||||||
|
top: 440px;
|
||||||
|
width: 50px;
|
||||||
|
text-align: right;
|
||||||
|
border-color: green;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
div.WaterCycleOnBox {
|
||||||
|
position: absolute;
|
||||||
|
left: 22px;
|
||||||
|
top: 464px;
|
||||||
|
width: 50px;
|
||||||
|
height: 20px;
|
||||||
|
background-color: #BDD7EE;
|
||||||
|
z-index: -35;
|
||||||
|
//border-color: red;
|
||||||
|
border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
div.WaterCycleStatusIcon {
|
||||||
|
position: absolute;
|
||||||
|
left: 80px;
|
||||||
|
top: 469px;
|
||||||
|
width: 25px;
|
||||||
|
text-align: center;
|
||||||
|
z-index: -25;
|
||||||
|
border-color: green;
|
||||||
|
//border-style: solid;
|
||||||
|
border-width: 1px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
BIN
Firmware/Resources/pointer.png
Normal file
|
After Width: | Height: | Size: 418 B |
@@ -2,11 +2,13 @@
|
|||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<title>Grow Controller RSSI</title>
|
<title>Grow Controller RSSI</title>
|
||||||
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, maximum-scale=1.0, width=device-width">
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
|
||||||
|
<meta name="viewport" content="user-scalable=no, initial-scale=1.0, width=device-width">
|
||||||
|
<script type='text/javascript' src='nav.js'></script>
|
||||||
<script type='text/javascript' src='myip.js'></script>
|
<script type='text/javascript' src='myip.js'></script>
|
||||||
<script type='text/javascript' src='rssi.js'></script>
|
<script type='text/javascript' src='rssi.js'></script>
|
||||||
</head>
|
</head>
|
||||||
<body onload='RSSIInit();'>
|
<body onload='RSSIInit(); NavInit();'>
|
||||||
<h1>Grow Controller - RSSI</h1>
|
<h1>Grow Controller - RSSI</h1>
|
||||||
<blockquote>
|
<blockquote>
|
||||||
<table width='800' border='0'>
|
<table width='800' border='0'>
|
||||||
@@ -25,19 +27,6 @@
|
|||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
<table>
|
<span id='nav'></span>
|
||||||
<tr valign="top">
|
|
||||||
<td>NAV:</td>
|
|
||||||
<td>
|
|
||||||
<a href='/'>Home</a> |
|
|
||||||
<a href='/config'>Config</a> |
|
|
||||||
<a href='/scan'>Scan</a> |
|
|
||||||
<a href='/rssi'>RSSI</a> |
|
|
||||||
<a href='/curr'>Current</a> |
|
|
||||||
<a href='/config?ssid=reset&pass=reset' onclick=\"return confirm('Are you sure?')\">Factory Reset</a> |
|
|
||||||
<a href='/about'>About</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
BIN
Firmware/Resources/soilheater_off.png
Normal file
|
After Width: | Height: | Size: 488 B |
BIN
Firmware/Resources/soilheater_on.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |
BIN
Firmware/Resources/soilmoisture.png
Normal file
|
After Width: | Height: | Size: 249 B |
BIN
Firmware/Resources/thermometer.png
Normal file
|
After Width: | Height: | Size: 777 B |
@@ -1,25 +0,0 @@
|
|||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
|
||||||
# Visual Studio 15
|
|
||||||
VisualStudioVersion = 15.0.27130.2020
|
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
|
||||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SmartSwitch", "SmartSwitch.vcxproj", "{C5F80730-F44F-4478-BDAE-6634EFC2CA88}"
|
|
||||||
EndProject
|
|
||||||
Global
|
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
|
||||||
Debug|x86 = Debug|x86
|
|
||||||
Release|x86 = Release|x86
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
|
||||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.ActiveCfg = Debug|Win32
|
|
||||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Debug|x86.Build.0 = Debug|Win32
|
|
||||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.ActiveCfg = Release|Win32
|
|
||||||
{C5F80730-F44F-4478-BDAE-6634EFC2CA88}.Release|x86.Build.0 = Release|Win32
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
|
||||||
HideSolutionNode = FALSE
|
|
||||||
EndGlobalSection
|
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
|
||||||
SolutionGuid = {2245D551-F0E5-4A91-B978-826DFEF0D6BF}
|
|
||||||
EndGlobalSection
|
|
||||||
EndGlobal
|
|
||||||
29
Firmware/Utility.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
// Utility functions
|
||||||
|
//
|
||||||
|
//
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
|
||||||
|
#include "Utility.h"
|
||||||
|
|
||||||
|
static String macToStr(const uint8_t * mac);
|
||||||
|
|
||||||
|
String GetMacString() {
|
||||||
|
unsigned char mac[6];
|
||||||
|
WiFi.macAddress(mac);
|
||||||
|
String clientMac(macToStr(mac));
|
||||||
|
return clientMac;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
11
Firmware/Utility.h
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
// Utility.h
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
|
||||||
|
// Returns the MAC Address as a string object
|
||||||
|
String GetMacString();
|
||||||
|
|
||||||
262
Firmware/WeMo.h
@@ -1,262 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
FAUXMO ESP 2.4.0
|
|
||||||
|
|
||||||
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
const char UDP_TEMPLATE[] PROGMEM =
|
|
||||||
"HTTP/1.1 200 OK\r\n"
|
|
||||||
"CACHE-CONTROL: max-age=86400\r\n"
|
|
||||||
"DATE: Mon, 22 Jun 2015 17:24:01 GMT\r\n"
|
|
||||||
"EXT:\r\n"
|
|
||||||
"LOCATION: http://%s:%d/setup.xml\r\n"
|
|
||||||
"OPT: \"http://schemas.upnp.org/upnp/1/0/\"; ns=01\r\n"
|
|
||||||
"01-NLS: %s\r\n"
|
|
||||||
"SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"
|
|
||||||
"ST: %s\r\n"
|
|
||||||
"USN: uuid:Socket-1_0-%s::"
|
|
||||||
"%s\r\n" // %s = urn:Belkin:device:** (Echo Dot 2Gen, Echo 1Gen) or %s = upnp:rootdevice (Echo 2Gen, Echo Plus)
|
|
||||||
"X-User-Agent: redsonic\r\n"
|
|
||||||
"\r\n";
|
|
||||||
|
|
||||||
const char SETUP_TEMPLATE[] PROGMEM =
|
|
||||||
"<?xml version=\"1.0\"?>\n"
|
|
||||||
"<root xmlns=\"urn:Belkin:device-1-0\">\n"
|
|
||||||
" <specVersion>\n"
|
|
||||||
" <major>1</major>\n"
|
|
||||||
" <minor>0</minor>\n"
|
|
||||||
" </specVersion>\n"
|
|
||||||
" <device>\n"
|
|
||||||
" <deviceType>urn:Belkin:device:controllee:1</deviceType>\n"
|
|
||||||
" <friendlyName>%s</friendlyName>\n"
|
|
||||||
" <manufacturer>Belkin International Inc.</manufacturer>\n" ///< must be "Belkin..." for Alexa discovery
|
|
||||||
" <manufacturerURL>http://www.smart-family.net</manufacturerURL>\n" ///<
|
|
||||||
" <modelDescription>Smartware Grow Controller</modelDescription>\n" ///<
|
|
||||||
" <modelName>Grow Controller</modelName>\n" ///<
|
|
||||||
" <modelNumber>1.0.0</modelNumber>\n"
|
|
||||||
#if 0
|
|
||||||
" <modelURL>http://www.smart-family.net</modelURL>\n" ///< This causes it to fail to send completely
|
|
||||||
#endif
|
|
||||||
" <UDN>uuid:Socket-1_0-%s</UDN>\n"
|
|
||||||
" <serialNumber>%s</serialNumber>\n"
|
|
||||||
" <iconList>\n"
|
|
||||||
" <mimetype>jpg</mimetype>\n"
|
|
||||||
" <width>100</width>\n"
|
|
||||||
" <height>100</height>\n"
|
|
||||||
" <depth>100</depth>\n"
|
|
||||||
" <url>icon.jpg</url>\n"
|
|
||||||
" </iconList>\n"
|
|
||||||
" <presentationURL>http://%s:%d</presentationURL>\n"
|
|
||||||
#if 0
|
|
||||||
" <serviceList>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:WiFiSetup:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:WiFiSetup1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/WiFiSetup1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/WiFiSetup1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/setupservice.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:timesync:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:timesync1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/timesync1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/timesync1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/timesyncservice.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:basicevent:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:basicevent1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/basicevent1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/basicevent1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/eventservice.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:firmwareupdate:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:firmwareupdate1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/firmwareupdate1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/firmwareupdate1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/firmwareupdate.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:rules:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:rules1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/rules1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/rules1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/rulesservice.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:metainfo:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:metainfo1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/metainfo1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/metainfo1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/metainfoservice.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:remoteaccess:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:remoteaccess1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/remoteaccess1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/remoteaccess1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/remoteaccess.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:deviceinfo:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:deviceinfo1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/deviceinfo1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/deviceinfo1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/deviceinfoservice.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:smartsetup:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:smartsetup1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/smartsetup1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/smartsetup1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/smartsetup.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" <service>\n"
|
|
||||||
" <serviceType>urn:Belkin:service:manufacture:1</serviceType>\n"
|
|
||||||
" <serviceId>urn:Belkin:serviceId:manufacture1</serviceId>\n"
|
|
||||||
" <controlURL>/upnp/control/manufacture1</controlURL>\n"
|
|
||||||
" <eventSubURL>/upnp/event/manufacture1</eventSubURL>\n"
|
|
||||||
" <SCPDURL>/manufacture.xml</SCPDURL>\n"
|
|
||||||
" </service>\n"
|
|
||||||
" </serviceList>\n"
|
|
||||||
#endif
|
|
||||||
" </device>\n"
|
|
||||||
"</root>\n";
|
|
||||||
|
|
||||||
const char EVENTSERVICE_TEMPLATE[] PROGMEM =
|
|
||||||
"<scpd xmlns=\"urn:Belkin:service-1-0\">"
|
|
||||||
"<actionList>"
|
|
||||||
"<action>"
|
|
||||||
"<name>SetBinaryState</name>"
|
|
||||||
"<argumentList>"
|
|
||||||
"<argument>"
|
|
||||||
"<retval />"
|
|
||||||
"<name>BinaryState</name>"
|
|
||||||
"<relatedStateVariable>BinaryState</relatedStateVariable>"
|
|
||||||
"<direction>in</direction>"
|
|
||||||
"</argument>"
|
|
||||||
"</argumentList>"
|
|
||||||
"</action>"
|
|
||||||
"<action>"
|
|
||||||
"<name>GetBinaryState</name>"
|
|
||||||
"<argumentList>"
|
|
||||||
"<argument>"
|
|
||||||
"<retval/>"
|
|
||||||
"<name>BinaryState</name>"
|
|
||||||
"<relatedStateVariable>BinaryState</relatedStateVariable>"
|
|
||||||
"<direction>out</direction>"
|
|
||||||
"</argument>"
|
|
||||||
"</argumentList>"
|
|
||||||
"</action>"
|
|
||||||
"</actionList>"
|
|
||||||
"<serviceStateTable>"
|
|
||||||
"<stateVariable sendEvents=\"yes\">"
|
|
||||||
"<name>BinaryState</name>"
|
|
||||||
"<dataType>Boolean</dataType>"
|
|
||||||
"<defaultValue>0</defaultValue>"
|
|
||||||
"</stateVariable>"
|
|
||||||
"<stateVariable sendEvents=\"yes\">"
|
|
||||||
"<name>level</name>"
|
|
||||||
"<dataType>string</dataType>"
|
|
||||||
"<defaultValue>0</defaultValue>"
|
|
||||||
"</stateVariable>"
|
|
||||||
"</serviceStateTable>"
|
|
||||||
"</scpd>\r\n"
|
|
||||||
"\r\n";
|
|
||||||
|
|
||||||
const char METAINFO_TEMPLATE[] PROGMEM =
|
|
||||||
"<scpd xmlns=\"urn:Belkin:service-1-0\">"
|
|
||||||
"<specVersion><major>1</major><minor>0</minor></specVersion>"
|
|
||||||
"<actionList>"
|
|
||||||
"<action>"
|
|
||||||
"<name>GetMetaInfo</name>"
|
|
||||||
"<argumentList>"
|
|
||||||
"<retval/>"
|
|
||||||
"<name>GetMetaInfo</name>"
|
|
||||||
"<relatedStateVariable>MetaInfo</relatedStateVariable>"
|
|
||||||
"<direction>in</direction>"
|
|
||||||
"</argumentList>"
|
|
||||||
"</action>"
|
|
||||||
"</actionList>"
|
|
||||||
"<serviceStateTable>"
|
|
||||||
"<stateVariable sendEvents=\"yes\">"
|
|
||||||
"<name>MetaInfo</name>"
|
|
||||||
"<dataType>string</dataType>"
|
|
||||||
"<defaultValue>0</defaultValue>"
|
|
||||||
"</stateVariable>"
|
|
||||||
"</serviceStateTable>"
|
|
||||||
"</scpd>\r\n"
|
|
||||||
"\r\n";
|
|
||||||
|
|
||||||
const char SETSTATE_TEMPLATE[] PROGMEM =
|
|
||||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
|
||||||
"<s:Body>"
|
|
||||||
"<u:SetBinaryState xmlns:u=\"urn:Belkin:service:basicevent:1\">"
|
|
||||||
"<BinaryState>%d</BinaryState>"
|
|
||||||
"</u:SetBinaryState>"
|
|
||||||
"</s:Body>"
|
|
||||||
"</s:Envelope>\r\n";
|
|
||||||
|
|
||||||
const char GETSTATE_TEMPLATE[] PROGMEM =
|
|
||||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
|
||||||
"<s:Body>"
|
|
||||||
"<u:GetBinaryStateResponse xmlns:u=\"urn:Belkin:service:basicevent:1\">"
|
|
||||||
"<BinaryState>%d</BinaryState>"
|
|
||||||
"</u:GetBinaryStateResponse>"
|
|
||||||
"</s:Body>"
|
|
||||||
"</s:Envelope>\r\n";
|
|
||||||
|
|
||||||
const char HEADERS[] PROGMEM =
|
|
||||||
"HTTP/1.1 200 OK\r\n"
|
|
||||||
"CONTENT-LENGTH: %d\r\n"
|
|
||||||
"CONTENT-TYPE: text/xml\r\n"
|
|
||||||
"DATE: Sun, 01 Jan 2017 00:00:00 GMT\r\n"
|
|
||||||
"LAST-MODIFIED: Sat, 01 Jan 2017 00:00:00 GMT\r\n"
|
|
||||||
"SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"
|
|
||||||
"X-USER-AGENT: redsonic\r\n"
|
|
||||||
"CONNECTION: close\r\n\r\n";
|
|
||||||
|
|
||||||
const char HEADERJPG[] PROGMEM =
|
|
||||||
"HTTP/1.1 200 OK\r\n"
|
|
||||||
"CONTENT-LENGTH: %d\r\n"
|
|
||||||
"CONTENT-TYPE: image/jpg\r\n"
|
|
||||||
"DATE: Sun, 01 Jan 2017 00:00:00 GMT\r\n"
|
|
||||||
"LAST-MODIFIED: Sat, 01 Jan 2017 00:00:00 GMT\r\n"
|
|
||||||
"SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"
|
|
||||||
"X-USER-AGENT: redsonic\r\n"
|
|
||||||
"CONNECTION: close\r\n\r\n";
|
|
||||||
|
|
||||||
const char HEADERPNG[] PROGMEM =
|
|
||||||
"HTTP/1.1 200 OK\r\n"
|
|
||||||
"CONTENT-LENGTH: %d\r\n"
|
|
||||||
"CONTENT-TYPE: image/png\r\n"
|
|
||||||
"DATE: Sun, 01 Jan 2017 00:00:00 GMT\r\n"
|
|
||||||
"LAST-MODIFIED: Sat, 01 Jan 2017 00:00:00 GMT\r\n"
|
|
||||||
"SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"
|
|
||||||
"X-USER-AGENT: redsonic\r\n"
|
|
||||||
"CONNECTION: close\r\n\r\n";
|
|
||||||
@@ -1,253 +0,0 @@
|
|||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
|
|
||||||
#include "ProjGlobals.h"
|
|
||||||
#include "Web_APConfig.h"
|
|
||||||
|
|
||||||
static String EncType_str(uint8_t encType) {
|
|
||||||
switch (encType) {
|
|
||||||
case ENC_TYPE_NONE:
|
|
||||||
return "open";
|
|
||||||
break;
|
|
||||||
case ENC_TYPE_WEP:
|
|
||||||
return "wep";
|
|
||||||
break;
|
|
||||||
case ENC_TYPE_TKIP:
|
|
||||||
return "wpa psk";
|
|
||||||
break;
|
|
||||||
case ENC_TYPE_CCMP:
|
|
||||||
return "wpa2 psk";
|
|
||||||
break;
|
|
||||||
case ENC_TYPE_AUTO:
|
|
||||||
return "wpa wpa2 psk";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return "unknown";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleAPScan() {
|
|
||||||
// WiFi.scanNetworks will return the number of networks found
|
|
||||||
Serial.println("Scan ...");
|
|
||||||
int n = WiFi.scanNetworks(false, true); // blocking call and show hidden.
|
|
||||||
Serial.println("scan done.");
|
|
||||||
if (n == 0) {
|
|
||||||
Serial.println("no networks found");
|
|
||||||
} else {
|
|
||||||
Serial.print(n);
|
|
||||||
Serial.println(" networks found");
|
|
||||||
server.sendContent_P("HTTP/1.1 200 OK\r\n");
|
|
||||||
server.sendContent_P("Content-Type: text/html\r\n");
|
|
||||||
server.sendContent_P("\r\n");
|
|
||||||
server.sendContent_P(
|
|
||||||
"<!DOCTYPE html>\n"
|
|
||||||
"<html>\n"
|
|
||||||
"<head><title>Grow Controller - Scan</title></head>\n"
|
|
||||||
"<body>\n"
|
|
||||||
"<h1>Grow Controller - Scan</h1>"
|
|
||||||
);
|
|
||||||
|
|
||||||
server.sendContent_P(
|
|
||||||
" <form action=\"/config\">\n"
|
|
||||||
" <blockquote>\n"
|
|
||||||
" <table width='800' border='0'>\n"
|
|
||||||
" <tr bgcolor='#E0E0E0'><td colspan='2'>Net</td><td>RSSI</td><td>SSID</td><td>BSSID</td><td>Encryption</td><td>Channel</td><td>Hidden</td></tr>\n"
|
|
||||||
);
|
|
||||||
|
|
||||||
for (int i = 0; i < n; ++i) {
|
|
||||||
// Select, Network #, RSSI, SSID, BSSID, Channel, Hidden
|
|
||||||
String bgcolor = (i & 1) ? "#E0E0E0" : "#FFFFFF";
|
|
||||||
String record =
|
|
||||||
" <tr bgcolor='" + bgcolor + "'><td><input type='radio' name='ssid' value='" + WiFi.SSID(i) + "'></td>\n"
|
|
||||||
+ " <td>" + (i + 1) + "</td>\n"
|
|
||||||
+ " <td>" + WiFi.RSSI(i) + "</td>\n"
|
|
||||||
+ " <td>" + WiFi.SSID(i) + "<br/>"
|
|
||||||
+ " <img src='/green1x1.png' width='" + String(5 * (100 + WiFi.RSSI(i))) + "' height='3'></td>\n"
|
|
||||||
+ " <td>" + WiFi.BSSIDstr(i) + "</td>\n";
|
|
||||||
|
|
||||||
record += " <td>" + EncType_str(WiFi.encryptionType(i)) + "</td>\n";
|
|
||||||
record += " <td>" + String(WiFi.channel(i));
|
|
||||||
record += "</td>\n";
|
|
||||||
record += WiFi.isHidden(i)
|
|
||||||
? " <td>Yes</td>\n"
|
|
||||||
: " <td>No</td>\n";
|
|
||||||
record += " </tr>\n";
|
|
||||||
server.sendContent(record);
|
|
||||||
//
|
|
||||||
Serial.print(i + 1);
|
|
||||||
Serial.print(": ");
|
|
||||||
Serial.print(WiFi.SSID(i));
|
|
||||||
Serial.print(" (");
|
|
||||||
Serial.print(WiFi.RSSI(i));
|
|
||||||
Serial.print(")");
|
|
||||||
Serial.println((WiFi.encryptionType(i) == ENC_TYPE_NONE) ? " " : "*");
|
|
||||||
}
|
|
||||||
server.sendContent_P(
|
|
||||||
" <tr><td> </td>\n"
|
|
||||||
" <td colspan='7'>\n"
|
|
||||||
" <input type=\"submit\" value=\"Select\" />\n"
|
|
||||||
" <input type=\"reset\"/>\n"
|
|
||||||
" </td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" </table>\n"
|
|
||||||
" </blockquote>\n"
|
|
||||||
" </form>\n"
|
|
||||||
"NAV: "
|
|
||||||
" <a href='/'>Home</a> | "
|
|
||||||
" <a href='/config'>Config</a> | "
|
|
||||||
" <a href='/scan'>Scan</a> | "
|
|
||||||
" <a href='/rssi'>RSSI</a> | "
|
|
||||||
" <a href='/curr'>Current</a> | "
|
|
||||||
" <a href='/config?ssid=reset&pass=reset' onclick=\"return confirm('Are you sure?')\">Factory Reset</a> | "
|
|
||||||
" <a href='/about'>About</a>\n"
|
|
||||||
" <br/>\n"
|
|
||||||
"</body>\n"
|
|
||||||
"</html>\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void FactoryReset(bool forceRestart) {
|
|
||||||
Serial.println("Factory Reset Configuration...\n");
|
|
||||||
wifiConfig.factoryReset();
|
|
||||||
wifiConfig.save();
|
|
||||||
if (forceRestart) {
|
|
||||||
Serial.println("Restart in 3 sec ...");
|
|
||||||
delay(3000);
|
|
||||||
ESP.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleAPConfigPage() {
|
|
||||||
String name = wifiConfig.getName();
|
|
||||||
String ssid = wifiConfig.getSSID();
|
|
||||||
String pass = wifiConfig.getPassword();
|
|
||||||
String url = wifiConfig.getURL();
|
|
||||||
//String ntp = wifiConfig.getNTPServerName();
|
|
||||||
char _onref[6], _offref[6], _autoOff[6];
|
|
||||||
|
|
||||||
snprintf(_onref, sizeof(_onref), "%d", wifiConfig.getOnRef());
|
|
||||||
snprintf(_offref, sizeof(_offref), "%d", wifiConfig.getOffRef());
|
|
||||||
snprintf(_autoOff, sizeof(_autoOff), "%d", wifiConfig.getAutoOff());
|
|
||||||
String onref = String(_onref);
|
|
||||||
String offref = String(_offref);
|
|
||||||
String autooff = String(_autoOff);
|
|
||||||
|
|
||||||
if (server.hasArg("ssid") && server.hasArg("pass")) {
|
|
||||||
String _ssid = server.hasArg("_ssid") ? server.arg("_ssid") : "";
|
|
||||||
String _pass = server.hasArg("_pass") ? server.arg("_pass") : "";
|
|
||||||
name = server.arg("name");
|
|
||||||
ssid = server.arg("ssid");
|
|
||||||
pass = server.arg("pass");
|
|
||||||
url = server.hasArg("url") ? server.arg("url") : "";
|
|
||||||
onref = server.hasArg("onref") ? server.arg("onref") : "";
|
|
||||||
offref = server.hasArg("offref") ? server.arg("offref") : "";
|
|
||||||
autooff = server.hasArg("autooff") ? server.arg("autooff") : "";
|
|
||||||
//ntp = server.hasArg("ntp") ? server.arg("ntp") : "";
|
|
||||||
if (ssid == "reset" && pass == "reset") {
|
|
||||||
FactoryReset(true);
|
|
||||||
}
|
|
||||||
wifiConfig.setName(name);
|
|
||||||
wifiConfig.setSSID(ssid);
|
|
||||||
wifiConfig.setPassword(pass);
|
|
||||||
wifiConfig.setURL(url);
|
|
||||||
wifiConfig.setOnRef(onref.toInt());
|
|
||||||
wifiConfig.setOffRef(offref.toInt());
|
|
||||||
wifiConfig.setAutoOff(autooff.toInt());
|
|
||||||
//wifiConfig.setNTPServerName(ntp);
|
|
||||||
wifiConfig.save();
|
|
||||||
Serial.println("Settings saved.");
|
|
||||||
if (ssid != _ssid || pass != _pass) {
|
|
||||||
// They changed stuff that requires a restart.
|
|
||||||
server.send(200, "text/html", "Configuration Saved! Restarting in 3 sec...");
|
|
||||||
Serial.printf("Config saved. Restarting in 3 sec . . .");
|
|
||||||
delay(3000);
|
|
||||||
ESP.restart();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String modeText = (isStationMode) ? "Station mode<br/>" : "Access Point mode<br/>";
|
|
||||||
if (server.hasArg("ssid") && !server.hasArg("pass")) {
|
|
||||||
ssid = server.arg("ssid");
|
|
||||||
pass = "";
|
|
||||||
Serial.println("ssid: " + ssid + ", pass: " + pass);
|
|
||||||
}
|
|
||||||
if (server.hasArg("url"))
|
|
||||||
url = server.arg("url");
|
|
||||||
//if (server.hasArg("ntp"))
|
|
||||||
// ntp = server.arg("ntp");
|
|
||||||
server.send(200, "text/html",
|
|
||||||
"<!DOCTYPE html>\n"
|
|
||||||
"<html>\n"
|
|
||||||
"<head><title>Grow Controller - Config</title></head>\n"
|
|
||||||
"<body>\n"
|
|
||||||
"<h1>Grow Controller - Configuration</h1>"
|
|
||||||
" <form action=\"\">\n"
|
|
||||||
" <blockquote>\n"
|
|
||||||
" <table border='0'>\n"
|
|
||||||
" <tr>\n"
|
|
||||||
" <td>Node Name</td>\n"
|
|
||||||
" <td><input type=\"text\" size=\"32\" name=\"name\" value=\"" + name + "\" /></td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" <tr>\n"
|
|
||||||
" <td>SSID</td>\n"
|
|
||||||
" <td><input type=\"text\" size=\"32\" name=\"ssid\" value=\"" + ssid + "\" /> **\n"
|
|
||||||
" <input type=\"hidden\" name=\"_ssid\" value=\"" + ssid + "\" /></td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" <tr nowrap='nowrap'>\n"
|
|
||||||
" <td>Password</td>\n"
|
|
||||||
" <td nowrap='nowrap'><input type=\"password\" size=\"64\" name=\"pass\" value=\"" + pass + "\" /> **\n"
|
|
||||||
" <input type=\"hidden\" name=\"_pass\" value=\"" + pass + "\" /></td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" <tr>\n"
|
|
||||||
" <td>Update URL</td>\n"
|
|
||||||
" <td><input type=\"text\" size=\"64\" name=\"url\" value=\"" + url + "\" /></td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
// " <tr>\n"
|
|
||||||
// " <td>NTP Server</td>\n"
|
|
||||||
// " <td><input type=\"text\" size=\"64\" name=\"ntp\" value=\"" + ntp + "\" /></td>\n"
|
|
||||||
// " </tr>\n"
|
|
||||||
" <tr>\n"
|
|
||||||
" <td>Auto-Off</td>\n"
|
|
||||||
" <td><input type=\"text\" size=\"10\" name=\"autooff\" value=\"" + autooff + "\" /> (time in seconds when non-zero)</td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" <tr>\n"
|
|
||||||
" <td>On Reference</td>\n"
|
|
||||||
" <td><input type=\"text\" size=\"10\" name=\"onref\" value=\"" + onref + "\" /> (sense above this indicates power is on)</td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" <tr>\n"
|
|
||||||
" <td>Off Reference</td>\n"
|
|
||||||
" <td><input type=\"text\" size=\"10\" name=\"offref\" value=\"" + offref + "\" /> (sense below this indicates power is off)</td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" <tr>\n"
|
|
||||||
" <td> </td>\n"
|
|
||||||
" <td>\n"
|
|
||||||
" <input type=\"submit\" value=\"Save\" />\n"
|
|
||||||
" <input type=\"reset\"/> ** Changes to these will trigger a module restart.\n"
|
|
||||||
" </td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" <tr>\n"
|
|
||||||
" <td colspan='2'><hr/></td>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" <tr>\n"
|
|
||||||
" <td> </td>\n"
|
|
||||||
" <td><a href='/swupdatecheck'>Check for SW update (module will restart if updated)</a></td/>\n"
|
|
||||||
" </tr>\n"
|
|
||||||
" </table>\n"
|
|
||||||
" </blockquote>\n"
|
|
||||||
" </form>\n"
|
|
||||||
"NAV: "
|
|
||||||
" <a href='/'>Home</a> | "
|
|
||||||
" <a href='/config'>Config</a> | "
|
|
||||||
" <a href='/scan'>Scan</a> | "
|
|
||||||
" <a href='/rssi'>RSSI</a> | "
|
|
||||||
" <a href='/curr'>Current</a> | "
|
|
||||||
" <a href='/config?ssid=reset&pass=reset' onclick=\"return confirm('Are you sure?')\">Factory Reset</a> | "
|
|
||||||
" <a href='/about'>About</a>\n"
|
|
||||||
" <br/>\n"
|
|
||||||
"</body>\n"
|
|
||||||
"</html>\n"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
|
|
||||||
#ifndef WEB_APCONFIG_H
|
|
||||||
#define WEB_APCONFIG_H
|
|
||||||
|
|
||||||
|
|
||||||
void FactoryReset(bool forceRestart = false);
|
|
||||||
|
|
||||||
void HandleAPScan();
|
|
||||||
|
|
||||||
void HandleAPConfigPage();
|
|
||||||
|
|
||||||
#endif // WEB_APCONFIG_H
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
|
|
||||||
#include "ProjGlobals.h"
|
|
||||||
#include "Web_FavIcon.h"
|
|
||||||
#include "Web_Resources.h"
|
|
||||||
|
|
||||||
void HandleGreen1x1() {
|
|
||||||
Serial.printf("Green1x1.png\n");
|
|
||||||
server.send_P(200, "image/png", Green1x1_png, sizeof(Green1x1_png));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleFavIcon() {
|
|
||||||
Serial.printf("favicon.ico\n");
|
|
||||||
server.send_P(200, "image/x-icon", favicon_ico, sizeof(favicon_ico));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleIcon() {
|
|
||||||
Serial.printf("icon.png\n");
|
|
||||||
server.send_P(200, "image/png", icon_png, sizeof(icon_png));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleOpenDoor() {
|
|
||||||
Serial.printf("Open.png\n");
|
|
||||||
server.send_P(200, "image/png", Open_png, sizeof(Open_png));
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandlePlantModel() {
|
|
||||||
Serial.printf("PlantModel\n");
|
|
||||||
server.send_P(200, "image/png", PlantModel_png, sizeof(PlantModel_png));
|
|
||||||
}
|
|
||||||
void HandleHeater() {
|
|
||||||
Serial.printf("Heater\n");
|
|
||||||
server.send_P(200, "image/png", Heater_png, sizeof(Heater_png));
|
|
||||||
}
|
|
||||||
void HandleWater() {
|
|
||||||
Serial.printf("Water\n");
|
|
||||||
server.send_P(200, "image/png", Water_png, sizeof(Water_png));
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
|
|
||||||
#ifndef WEB_FAVICON_H
|
|
||||||
#define WEB_FAVICON_H
|
|
||||||
|
|
||||||
void HandleGreen1x1();
|
|
||||||
void HandleFavIcon();
|
|
||||||
void HandlePlantModel();
|
|
||||||
void HandleHeater();
|
|
||||||
void HandleWater();
|
|
||||||
void HandleIcon();
|
|
||||||
void HandleOpenDoor();
|
|
||||||
|
|
||||||
#endif // WEB_FAVICON_H
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
|
|
||||||
#include "ProjGlobals.h"
|
|
||||||
#include "Web_RSSIGraph.h"
|
|
||||||
#include "Web_Resources.h"
|
|
||||||
|
|
||||||
void HandleRSSIPage() {
|
|
||||||
server.send_P(200, "text/html", rssi_htm);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleRSSI_JS() {
|
|
||||||
server.send_P(200, "text/html", rssi_js);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleCurrPage() {
|
|
||||||
server.send_P(200, "text/html", curr_htm);
|
|
||||||
}
|
|
||||||
|
|
||||||
void HandleCurr_JS() {
|
|
||||||
server.send_P(200, "text/html", curr_js);
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
|
|
||||||
#ifndef WEB_RSSIGRAPH_H
|
|
||||||
#define WEB_RSSIGRAPH_H
|
|
||||||
|
|
||||||
void HandleRSSIPage();
|
|
||||||
void HandleRSSI_JS();
|
|
||||||
void HandleCurrPage();
|
|
||||||
void HandleCurr_JS();
|
|
||||||
|
|
||||||
#endif // WEB_RSSIGRAPH_H
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
|
|
||||||
#include "ProjGlobals.h"
|
|
||||||
#include "Web_RootPage.h"
|
|
||||||
#include "GrowController.h"
|
|
||||||
#include "Web_Resources.h"
|
|
||||||
|
|
||||||
void HandleRootPage() {
|
|
||||||
if (server.hasArg("SW")) {
|
|
||||||
String newState = server.arg("SW");
|
|
||||||
if (newState == "1") {
|
|
||||||
SetCircuit(CMD_On);
|
|
||||||
Serial.printf("SW 1\n");
|
|
||||||
} else if (newState == "0") {
|
|
||||||
SetCircuit(CMD_Off);
|
|
||||||
Serial.printf("SW 0\n");
|
|
||||||
} else if (newState == "2") {
|
|
||||||
SetCircuit(CMD_Toggle);
|
|
||||||
Serial.printf("SW 2\n");
|
|
||||||
} else {
|
|
||||||
Serial.printf("SW %s ???\n", newState.c_str());
|
|
||||||
; // no action
|
|
||||||
}
|
|
||||||
}
|
|
||||||
String modeText = (isStationMode) ? "Station mode<br/>" : "Access Point mode<br/>";
|
|
||||||
String status = GetCircuitStatus() ? "On<br/>" : "Off<br/>";
|
|
||||||
String myIP = String(WiFi.localIP()[0]);
|
|
||||||
myIP += "." + String(WiFi.localIP()[1]);
|
|
||||||
myIP += "." + String(WiFi.localIP()[2]);
|
|
||||||
myIP += "." + String(WiFi.localIP()[3]);
|
|
||||||
server.send_P(200, "text/html", index_htm);
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
|
|
||||||
#ifndef WEB_ROOTPAGE_H
|
|
||||||
#define WEB_ROOTPAGE_H
|
|
||||||
|
|
||||||
void HandleRootPage();
|
|
||||||
|
|
||||||
#endif // WEB_ROOTPAGE_H
|
|
||||||
@@ -26,30 +26,29 @@
|
|||||||
///
|
///
|
||||||
class ConfigManager {
|
class ConfigManager {
|
||||||
public:
|
public:
|
||||||
ConfigManager();
|
ConfigManager();
|
||||||
void load();
|
void load();
|
||||||
void save();
|
void save();
|
||||||
void factoryReset();
|
void factoryReset();
|
||||||
String getName();
|
String getName();
|
||||||
void setName(String _name);
|
void setName(String _name);
|
||||||
String getSSID();
|
String getSSID();
|
||||||
void setSSID(String _ssid);
|
void setSSID(String _ssid);
|
||||||
String getPassword();
|
String getPassword();
|
||||||
void setPassword(String _password);
|
void setPassword(String _password);
|
||||||
String getURL();
|
String getURL();
|
||||||
void setURL(String _url);
|
void setURL(String _url);
|
||||||
uint16_t getOnRef();
|
uint16_t getOnRef();
|
||||||
void setOnRef(uint16 onR);
|
void setOnRef(uint16 onR);
|
||||||
uint16_t getOffRef();
|
uint16_t getOffRef();
|
||||||
void setOffRef(uint16 offR);
|
void setOffRef(uint16 offR);
|
||||||
uint16_t getAutoOff();
|
uint16_t getAutoOff();
|
||||||
void setAutoOff(uint16 autoOff);
|
void setAutoOff(uint16 autoOff);
|
||||||
#if 0
|
#if 0
|
||||||
String getNTPServerName();
|
String getNTPServerName();
|
||||||
void setNTPServerName(String _name);
|
void setNTPServerName(String _name);
|
||||||
#endif
|
#endif
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#endif // !WIFICONFIGURATION_H
|
#endif // !WIFICONFIGURATION_H
|
||||||
|
|||||||
103
Firmware/WiFiStateHandler.cpp
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "ProjGlobals.h"
|
||||||
|
#include "WiFiStateHandler.h"
|
||||||
|
|
||||||
|
WiFiConnectState wifiConState; ///< Track the current WiFi state
|
||||||
|
|
||||||
|
|
||||||
|
void WiFiStateSet(WiFiConnectState newState) {
|
||||||
|
wifiConState = newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFiConnectState WiFiStateGet() {
|
||||||
|
return wifiConState;
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFiConnectState WiFiStateHandler() {
|
||||||
|
static unsigned long timeStateChange = 0;
|
||||||
|
// @todo static unsigned long timeLEDControl;
|
||||||
|
// @todo unsigned long timeLEDCycle;
|
||||||
|
String ssid = wifiConfig.getSSID();
|
||||||
|
String pass = wifiConfig.getPassword();
|
||||||
|
IPAddress myIP;
|
||||||
|
|
||||||
|
// @todo
|
||||||
|
#if 0
|
||||||
|
timeLEDCycle = millis() - timeLEDControl;
|
||||||
|
if (timeLEDCycle >= LED_PERIOD__MS) {
|
||||||
|
timeLEDControl = millis();
|
||||||
|
timeLEDCycle = 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (wifiConState) {
|
||||||
|
case WFC_ConnectToAP:
|
||||||
|
// @todo isStationMode = true;
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
wifi_set_sleep_type(NONE_SLEEP_T); // rumor is that this fixes multicast receive
|
||||||
|
//ssid = wifiConfig.getSSID();
|
||||||
|
//pass = wifiConfig.getPassword();
|
||||||
|
WiFi.begin(ssid.c_str(), pass.c_str());
|
||||||
|
timeStateChange = millis();
|
||||||
|
// @todo timeLEDControl = timeStateChange;
|
||||||
|
wifiConState = WFC_JoiningAP;
|
||||||
|
break;
|
||||||
|
case WFC_JoiningAP:
|
||||||
|
// @todo if (timeLEDCycle <= LED_JOINING_MS) {
|
||||||
|
// @todo SetLED(LED_WIFI, 1);
|
||||||
|
// @todo } else {
|
||||||
|
// @todo SetLED(LED_WIFI, 0);
|
||||||
|
// @todo }
|
||||||
|
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:
|
||||||
|
// @todo if (timeLEDCycle <= LED_APMODE__MS) {
|
||||||
|
// @todo SetLED(LED_WIFI, 1);
|
||||||
|
// @todo } else {
|
||||||
|
// @todo SetLED(LED_WIFI, 0);
|
||||||
|
// @todo }
|
||||||
|
// @todo isStationMode = false;
|
||||||
|
Serial.printf("Starting Access Point %s\n", AP_NAME.c_str());
|
||||||
|
WiFi.softAP(AP_NAME.c_str());
|
||||||
|
myIP = WiFi.softAPIP();
|
||||||
|
Serial.printf("IP Address: %s\n", myIP.toString().c_str());
|
||||||
|
// @todo dnsServer.start(DNS_PORT, "*", apIP);
|
||||||
|
timeStateChange = millis();
|
||||||
|
//StartWebServer();
|
||||||
|
wifiConState = WFC_HostingAP;
|
||||||
|
break;
|
||||||
|
case WFC_JoinedAP:
|
||||||
|
// @todo if (timeLEDCycle <= LED_JOINED__MS) {
|
||||||
|
// @todo SetLED(LED_WIFI, 1);
|
||||||
|
// @todo } else {
|
||||||
|
// @todo SetLED(LED_WIFI, 0);
|
||||||
|
// @todo }
|
||||||
|
// @todo server.handleClient();
|
||||||
|
break;
|
||||||
|
case WFC_HostingAP:
|
||||||
|
// @todo if (timeLEDCycle <= LED_APMODE__MS) {
|
||||||
|
// @todo SetLED(LED_WIFI, 1);
|
||||||
|
// @todo } else {
|
||||||
|
// @todo SetLED(LED_WIFI, 0);
|
||||||
|
// @todo }
|
||||||
|
// @todo server.handleClient();
|
||||||
|
// @todo dnsServer.processNextRequest();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case WFC_Idle:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return wifiConState;
|
||||||
|
}
|
||||||
58
Firmware/WiFiStateHandler.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
//
|
||||||
|
// WiFi Connection - handles the state - AP or Client
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef WIFISTATEHANDLER_H
|
||||||
|
#define WIFISTATEHANDLER_H
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
/// WiFi Connection States of connection
|
||||||
|
typedef enum {
|
||||||
|
WFC_ConnectToAP, ///< Connect to an Access Point
|
||||||
|
WFC_JoiningAP, ///< Joining the Access Point
|
||||||
|
WFC_JoinedAP, ///< Joined to the Access Point
|
||||||
|
WFC_CreateAP, ///< Create an Access Point
|
||||||
|
WFC_HostingAP, ///< Hosting an Access Point
|
||||||
|
WFC_Idle, ///< Idle take no action, or invalid
|
||||||
|
} WiFiConnectState;
|
||||||
|
|
||||||
|
/// Get the current state
|
||||||
|
///
|
||||||
|
/// @returns the current state
|
||||||
|
///
|
||||||
|
WiFiConnectState WiFiStateGet();
|
||||||
|
|
||||||
|
|
||||||
|
/// Called from the main loop to manage the WiFi connection state
|
||||||
|
///
|
||||||
|
/// @returns current connection state
|
||||||
|
///
|
||||||
|
/// @code
|
||||||
|
/// void loop() {
|
||||||
|
/// ...
|
||||||
|
/// WiFiStateHandler();
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// @endcode
|
||||||
|
WiFiConnectState WiFiStateHandler(); ///< WiFi interface manager
|
||||||
|
|
||||||
|
/// Used to force a specific state
|
||||||
|
///
|
||||||
|
/// @param[in] newState is the state to set the WiFi connection to
|
||||||
|
///
|
||||||
|
/// @code
|
||||||
|
/// void setup() {
|
||||||
|
/// ...
|
||||||
|
/// if (ssid == "" || pass == "")
|
||||||
|
/// WiFiStateSet(WFC_CreateAP);
|
||||||
|
/// else
|
||||||
|
/// WiFiStateSet(WFC_ConnectToAP);
|
||||||
|
/// ...
|
||||||
|
/// }
|
||||||
|
/// @endcode
|
||||||
|
///
|
||||||
|
void WiFiStateSet(WiFiConnectState newState);
|
||||||
|
|
||||||
|
#endif // WIFISTATEHANDLER_H
|
||||||
@@ -8,31 +8,31 @@
|
|||||||
|
|
||||||
// Change this when it is required to force a reinitialization
|
// Change this when it is required to force a reinitialization
|
||||||
//
|
//
|
||||||
#define EXPECTED_FMT 0x03
|
#define EXPECTED_FMT 0x03
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint8_t format[2]; // Format of this data structure, and ~format
|
uint8_t format[2]; // Format of this data structure, and ~format
|
||||||
struct info {
|
struct info {
|
||||||
char name[CFG_NAMESIZE]; // Name of this node
|
char name[CFG_NAMESIZE]; // Network Discoverable Name of this node
|
||||||
char ssid[CFG_SSIDSIZE]; // SSID of the saved network [max length defined by standard]
|
char ssid[CFG_SSIDSIZE]; // SSID of the saved network [max length defined by standard]
|
||||||
char pass[CFG_PASSSIZE]; // PASScode of the saved network
|
char pass[CFG_PASSSIZE]; // PASScode of the saved network
|
||||||
char updateURL[CFG_UURLSIZE]; // URL of the trusted server with code updates
|
char updateURL[CFG_UURLSIZE]; // URL of the trusted server with code updates
|
||||||
uint16_t onRef; // A/D reference above which the output is considered 'On'
|
uint16_t onRef; // A/D reference above which the output is considered 'On'
|
||||||
uint16_t offRef; // A/D reference below which the output is considered 'Off'
|
uint16_t offRef; // A/D reference below which the output is considered 'Off'
|
||||||
uint16_t autoOff; // When non-zero, this is the # minutes until auto-turn off
|
uint16_t autoOff; // When non-zero, this is the # minutes until auto-turn off
|
||||||
//char NTPIP[64]; // Name or IP of the NTP Server
|
//char NTPIP[64]; // Name or IP of the NTP Server
|
||||||
char Padding[10];
|
char Padding[10];
|
||||||
} info;
|
} info;
|
||||||
} Config_t;
|
} Config_t;
|
||||||
#define OFS_FORMAT (0)
|
#define OFS_FORMAT (0)
|
||||||
#define OFS_NAME (OFS_FORMAT + 2)
|
#define OFS_NAME (OFS_FORMAT + 2)
|
||||||
#define OFS_SSID (OFS_NAME + CFG_NAMESIZE)
|
#define OFS_SSID (OFS_NAME + CFG_NAMESIZE)
|
||||||
#define OFS_PASS (OFS_SSID + CFG_SSIDSIZE)
|
#define OFS_PASS (OFS_SSID + CFG_SSIDSIZE)
|
||||||
#define OFS_URL (OFS_PASS + CFG_PASSSIZE)
|
#define OFS_URL (OFS_PASS + CFG_PASSSIZE)
|
||||||
#define OFS_ONREF (OFS_URL + CFG_UURLSIZE)
|
#define OFS_ONREF (OFS_URL + CFG_UURLSIZE)
|
||||||
#define OFS_OFFREF (OFS_ONREF + sizeof(uint16_t))
|
#define OFS_OFFREF (OFS_ONREF + sizeof(uint16_t))
|
||||||
#define OFS_AUTOOFF (OFS_OFFREF + sizeof(uint16_t))
|
#define OFS_AUTOOFF (OFS_OFFREF + sizeof(uint16_t))
|
||||||
//#define OFS_NTPIP (OFS_OFFREF + 2)
|
//#define OFS_NTPIP (OFS_OFFREF + 2)
|
||||||
|
|
||||||
Config_t Configuration;
|
Config_t Configuration;
|
||||||
|
|
||||||
@@ -41,117 +41,116 @@ ConfigManager::ConfigManager() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::factoryReset() {
|
void ConfigManager::factoryReset() {
|
||||||
Configuration.format[0] = EXPECTED_FMT;
|
Configuration.format[0] = EXPECTED_FMT;
|
||||||
Configuration.format[1] = ~EXPECTED_FMT;
|
Configuration.format[1] = ~EXPECTED_FMT;
|
||||||
strcpy(Configuration.info.name, "3-Way Switch");
|
strcpy(Configuration.info.name, "3-Way Switch");
|
||||||
Configuration.info.ssid[0] = '\0';
|
Configuration.info.ssid[0] = '\0';
|
||||||
Configuration.info.pass[0] = '\0';
|
Configuration.info.pass[0] = '\0';
|
||||||
//Configuration.info.updateURL[0] = '\0';
|
//Configuration.info.updateURL[0] = '\0';
|
||||||
strcpy(Configuration.info.updateURL, "https://192.168.1.201/mbed/ESP.php");
|
strcpy(Configuration.info.updateURL, "https://192.168.1.201/mbed/ESP.php");
|
||||||
Configuration.info.onRef = 150;
|
Configuration.info.onRef = 150;
|
||||||
Configuration.info.offRef = 100;
|
Configuration.info.offRef = 100;
|
||||||
Configuration.info.autoOff = 0;
|
Configuration.info.autoOff = 0;
|
||||||
//Configuration.info.NTPIP[0] = '\0';
|
//Configuration.info.NTPIP[0] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::load() {
|
void ConfigManager::load() {
|
||||||
EEPROM.begin(512);
|
EEPROM.begin(512);
|
||||||
|
uint8_t * ptr = (uint8_t *) &Configuration;
|
||||||
uint8_t * ptr = (uint8_t *) &Configuration;
|
uint8_t i = 0;
|
||||||
uint8_t i = 0;
|
do {
|
||||||
do {
|
*ptr++ = EEPROM.read(OFS_FORMAT + i++);
|
||||||
*ptr++ = EEPROM.read(OFS_FORMAT + i++);
|
} while (i < sizeof(Configuration));
|
||||||
} while (i < sizeof(Configuration));
|
if ((Configuration.format[0] & 0xFF) != (~Configuration.format[1] & 0xFF)) {
|
||||||
if ((Configuration.format[0] & 0xFF) != (~Configuration.format[1] & 0xFF)) {
|
// Factory default
|
||||||
// Factory default
|
Serial.printf("Bad Configuration - reset to factory defaults\n");
|
||||||
Serial.printf("Bad Configuration - reset to factory defaults\n");
|
factoryReset();
|
||||||
factoryReset();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
Serial.printf("Loading from EEROM\n");
|
||||||
Serial.printf("Loading from EEROM\n");
|
Serial.printf("Node Name: %s\n", Configuration.info.name);
|
||||||
Serial.printf("Node Name: %s\n", Configuration.info.name);
|
Serial.printf("SSID: %s\n", Configuration.info.ssid);
|
||||||
Serial.printf("SSID: %s\n", Configuration.info.ssid);
|
Serial.printf("PASS: %s\n", Configuration.info.pass);
|
||||||
Serial.printf("PASS: %s\n", Configuration.info.pass);
|
Serial.printf("URL : %s\n", Configuration.info.updateURL);
|
||||||
Serial.printf("URL : %s\n", Configuration.info.updateURL);
|
Serial.printf("On Ref: %d\n", Configuration.info.onRef);
|
||||||
Serial.printf("On Ref: %d\n", Configuration.info.onRef);
|
Serial.printf("Off Ref: %d\n", Configuration.info.offRef);
|
||||||
Serial.printf("Off Ref: %d\n", Configuration.info.offRef);
|
Serial.printf("Auto Off: %d\n", Configuration.info.autoOff);
|
||||||
Serial.printf("Auto Off: %d\n", Configuration.info.autoOff);
|
//Serial.printf("NTP Svr: %s\n", Configuration.info.NTPIP);
|
||||||
//Serial.printf("NTP Svr: %s\n", Configuration.info.NTPIP);
|
delay(500);
|
||||||
delay(500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::save(void) {
|
void ConfigManager::save(void) {
|
||||||
EEPROM.begin(512);
|
EEPROM.begin(512);
|
||||||
uint8_t * ptr = (uint8_t *) &Configuration;
|
uint8_t * ptr = (uint8_t *) &Configuration;
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
do {
|
do {
|
||||||
EEPROM.write(OFS_FORMAT + i++, *ptr++);
|
EEPROM.write(OFS_FORMAT + i++, *ptr++);
|
||||||
} while (i < sizeof(Configuration));
|
} while (i < sizeof(Configuration));
|
||||||
EEPROM.commit();
|
EEPROM.commit();
|
||||||
delay(500);
|
delay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
String ConfigManager::getNTPServerName() {
|
String ConfigManager::getNTPServerName() {
|
||||||
return String(Configuration.info.NTPIP);
|
return String(Configuration.info.NTPIP);
|
||||||
}
|
}
|
||||||
void ConfigManager::setNTPServerName(String _name) {
|
void ConfigManager::setNTPServerName(String _name) {
|
||||||
strncpy(Configuration.info.NTPIP, _name.c_str(), sizeof(Configuration.info.NTPIP));
|
strncpy(Configuration.info.NTPIP, _name.c_str(), sizeof(Configuration.info.NTPIP));
|
||||||
Serial.printf("setNTPServerName(%s)\n", Configuration.info.NTPIP);
|
Serial.printf("setNTPServerName(%s)\n", Configuration.info.NTPIP);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
String ConfigManager::getName() {
|
String ConfigManager::getName() {
|
||||||
return String(Configuration.info.name);
|
return String(Configuration.info.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::setName(String _name) {
|
void ConfigManager::setName(String _name) {
|
||||||
strncpy(Configuration.info.name, _name.c_str(), sizeof(Configuration.info.name));
|
strncpy(Configuration.info.name, _name.c_str(), sizeof(Configuration.info.name));
|
||||||
Serial.printf("setName(%s)\n", Configuration.info.name);
|
Serial.printf("setName(%s)\n", Configuration.info.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
String ConfigManager::getSSID() {
|
String ConfigManager::getSSID() {
|
||||||
return String(Configuration.info.ssid);
|
return String(Configuration.info.ssid);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::setSSID(String _ssid) {
|
void ConfigManager::setSSID(String _ssid) {
|
||||||
strncpy(Configuration.info.ssid, _ssid.c_str(), sizeof(Configuration.info.ssid));
|
strncpy(Configuration.info.ssid, _ssid.c_str(), sizeof(Configuration.info.ssid));
|
||||||
Serial.printf("setSSID(%s)\n", Configuration.info.ssid);
|
Serial.printf("setSSID(%s)\n", Configuration.info.ssid);
|
||||||
}
|
}
|
||||||
|
|
||||||
String ConfigManager::getPassword() {
|
String ConfigManager::getPassword() {
|
||||||
return String(Configuration.info.pass);
|
return String(Configuration.info.pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::setPassword(String _password) {
|
void ConfigManager::setPassword(String _password) {
|
||||||
strncpy(Configuration.info.pass, _password.c_str(), sizeof(Configuration.info.pass));
|
strncpy(Configuration.info.pass, _password.c_str(), sizeof(Configuration.info.pass));
|
||||||
Serial.printf("setPass(%s)\n", Configuration.info.pass);
|
Serial.printf("setPass(%s)\n", Configuration.info.pass);
|
||||||
}
|
}
|
||||||
|
|
||||||
String ConfigManager::getURL() {
|
String ConfigManager::getURL() {
|
||||||
return String(Configuration.info.updateURL);
|
return String(Configuration.info.updateURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConfigManager::setURL(String _url) {
|
void ConfigManager::setURL(String _url) {
|
||||||
strncpy(Configuration.info.updateURL, _url.c_str(), sizeof(Configuration.info.updateURL));
|
strncpy(Configuration.info.updateURL, _url.c_str(), sizeof(Configuration.info.updateURL));
|
||||||
Serial.printf("setURL (%s)\n", Configuration.info.updateURL);
|
Serial.printf("setURL (%s)\n", Configuration.info.updateURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t ConfigManager::getOnRef() {
|
uint16_t ConfigManager::getOnRef() {
|
||||||
return Configuration.info.onRef;
|
return Configuration.info.onRef;
|
||||||
}
|
}
|
||||||
void ConfigManager::setOnRef(uint16 onR) {
|
void ConfigManager::setOnRef(uint16 onR) {
|
||||||
Configuration.info.onRef = onR;
|
Configuration.info.onRef = onR;
|
||||||
}
|
}
|
||||||
uint16_t ConfigManager::getOffRef() {
|
uint16_t ConfigManager::getOffRef() {
|
||||||
return Configuration.info.offRef;
|
return Configuration.info.offRef;
|
||||||
}
|
}
|
||||||
void ConfigManager::setOffRef(uint16 offR) {
|
void ConfigManager::setOffRef(uint16 offR) {
|
||||||
Configuration.info.offRef = offR;
|
Configuration.info.offRef = offR;
|
||||||
}
|
}
|
||||||
uint16_t ConfigManager::getAutoOff() {
|
uint16_t ConfigManager::getAutoOff() {
|
||||||
return Configuration.info.autoOff;
|
return Configuration.info.autoOff;
|
||||||
}
|
}
|
||||||
void ConfigManager::setAutoOff(uint16 autoOff) {
|
void ConfigManager::setAutoOff(uint16 autoOff) {
|
||||||
Configuration.info.autoOff = autoOff;
|
Configuration.info.autoOff = autoOff;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,484 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
FAUXMO ESP 2.4.2
|
|
||||||
|
|
||||||
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#if defined(ESP32)
|
|
||||||
#include <WiFi.h>
|
|
||||||
#elif defined(ESP8266)
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#else
|
|
||||||
#error Platform not supported
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "fauxmoESP.h"
|
|
||||||
#include "Web_Resources.h"
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// UDP
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void fauxmoESP::_sendUDPResponse(unsigned int device_id) {
|
|
||||||
|
|
||||||
fauxmoesp_device_t device = _devices[device_id];
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] UDP response for device #%d (%s)\n", _current, device.name);
|
|
||||||
|
|
||||||
char buffer[16];
|
|
||||||
IPAddress ip = WiFi.localIP();
|
|
||||||
snprintf_P(buffer, sizeof(buffer), PSTR("%d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
|
|
||||||
|
|
||||||
char response[strlen(UDP_TEMPLATE) + 128];
|
|
||||||
snprintf_P(response, sizeof(response), UDP_TEMPLATE,
|
|
||||||
buffer,
|
|
||||||
_base_port + _current,
|
|
||||||
device.uuid,
|
|
||||||
_udpPattern == 1 ? UDP_DEVICE_PATTERN_1 : _udpPattern == 2 ? UDP_DEVICE_PATTERN_2 : _udpPattern == 3 ? UDP_DEVICE_PATTERN_3 : _udpPattern == 4 ? UDP_DEVICE_PATTERN_4 : _udpPattern == 5 ? UDP_DEVICE_PATTERN_5 : UDP_ROOT_DEVICE,
|
|
||||||
device.uuid,
|
|
||||||
_udpPattern == 1 ? UDP_DEVICE_PATTERN_1 : _udpPattern == 2 ? UDP_ROOT_DEVICE : _udpPattern == 3 ? UDP_ROOT_DEVICE : _udpPattern == 4 ? UDP_ROOT_DEVICE : _udpPattern == 5 ? UDP_ROOT_DEVICE : UDP_ROOT_DEVICE
|
|
||||||
);
|
|
||||||
|
|
||||||
_udp.beginPacket(_remoteIP, _remotePort);
|
|
||||||
#if defined(ESP32)
|
|
||||||
_udp.printf(response);
|
|
||||||
#else
|
|
||||||
_udp.write(response);
|
|
||||||
#endif
|
|
||||||
_udp.endPacket();
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] response:\n%s\n", response);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::_nextUDPResponse() {
|
|
||||||
|
|
||||||
while (_roundsLeft) {
|
|
||||||
if (_devices[_current].hit == false) break;
|
|
||||||
if (++_current == _devices.size()) {
|
|
||||||
--_roundsLeft;
|
|
||||||
_current = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_roundsLeft > 0) {
|
|
||||||
_sendUDPResponse(_current);
|
|
||||||
if (++_current == _devices.size()) {
|
|
||||||
--_roundsLeft;
|
|
||||||
_current = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::_onUDPData(IPAddress remoteIP, unsigned int remotePort, void *data, size_t len) {
|
|
||||||
|
|
||||||
if (!_enabled) {
|
|
||||||
DEBUG_MSG_FAUXMO("onUDPData\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
char * p = (char *) data;
|
|
||||||
p[len] = 0;
|
|
||||||
|
|
||||||
if (strstr(p, UDP_SEARCH_PATTERN) == (char *) data) {
|
|
||||||
Serial.printf("onUDP: %s\n", p);
|
|
||||||
_udpPattern = 0;
|
|
||||||
if (strstr(p, UDP_DEVICE_PATTERN_1) != NULL) _udpPattern = 1;
|
|
||||||
if (strstr(p, UDP_DEVICE_PATTERN_2) != NULL) _udpPattern = 2;
|
|
||||||
if (strstr(p, UDP_DEVICE_PATTERN_3) != NULL) _udpPattern = 3;
|
|
||||||
if (strstr(p, UDP_DEVICE_PATTERN_4) != NULL) _udpPattern = 4; // ssdp:all
|
|
||||||
if (strstr(p, UDP_DEVICE_PATTERN_5) != NULL) _udpPattern = 5; // ssdpsearch:all
|
|
||||||
if (strstr(p, UDP_ROOT_DEVICE) != NULL) _udpPattern = 6; // upnp:rootdevice
|
|
||||||
if (_udpPattern) {
|
|
||||||
|
|
||||||
#ifdef DEBUG_FAUXMO
|
|
||||||
char buffer[16];
|
|
||||||
snprintf_P(buffer, sizeof(buffer), PSTR("%d.%d.%d.%d"), remoteIP[0], remoteIP[1], remoteIP[2], remoteIP[3]);
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Search request from %s\n", buffer);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Set hits to false
|
|
||||||
for (unsigned int i = 0; i < _devices.size(); i++) {
|
|
||||||
_devices[i].hit = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send responses
|
|
||||||
_remoteIP = remoteIP;
|
|
||||||
_remotePort = remotePort;
|
|
||||||
_current = random(0, _devices.size());
|
|
||||||
_roundsLeft = UDP_RESPONSES_TRIES;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// TCP
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
void fauxmoESP::_handleSetup(AsyncClient *client, unsigned int device_id, void *data, size_t len) {
|
|
||||||
(void) data;
|
|
||||||
(void) len;
|
|
||||||
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Device #%d /setup.xml\n", device_id);
|
|
||||||
_devices[device_id].hit = true;
|
|
||||||
fauxmoesp_device_t device = _devices[device_id];
|
|
||||||
|
|
||||||
IPAddress myIP;
|
|
||||||
myIP = WiFi.localIP();
|
|
||||||
char response[80 + strlen_P(SETUP_TEMPLATE) + strlen(device.name) + strlen(device.uuid) + strlen(device.serial) + 25];
|
|
||||||
snprintf_P(response, sizeof(response), SETUP_TEMPLATE,
|
|
||||||
device.name, device.uuid, device.serial,
|
|
||||||
myIP.toString().c_str(), 80);
|
|
||||||
|
|
||||||
char headers[strlen_P(HEADERS) + 10];
|
|
||||||
snprintf_P(headers, sizeof(headers), HEADERS, strlen(response));
|
|
||||||
|
|
||||||
client->write(headers, strlen(headers));
|
|
||||||
client->write(response, strlen(response));
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] end /setup.xml: %d, %d\n", strlen(headers), strlen(response));
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::_handleEventService(AsyncClient *client, unsigned int device_id, void *data, size_t len) {
|
|
||||||
(void) device_id;
|
|
||||||
(void) data;
|
|
||||||
(void) len;
|
|
||||||
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Device #%d /eventservice.xml\n", device_id);
|
|
||||||
|
|
||||||
char response[strlen_P(EVENTSERVICE_TEMPLATE)];
|
|
||||||
snprintf_P(response, sizeof(response), EVENTSERVICE_TEMPLATE);
|
|
||||||
|
|
||||||
char headers[strlen_P(HEADERS) + 10];
|
|
||||||
snprintf_P(headers, sizeof(headers), HEADERS, strlen(response));
|
|
||||||
|
|
||||||
client->write(headers, strlen(headers));
|
|
||||||
client->write(response, strlen(response));
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] end /eventservice.xml\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::_handleMetaInfoService(AsyncClient *client, unsigned int device_id, void *data, size_t len) {
|
|
||||||
(void) device_id;
|
|
||||||
(void) data;
|
|
||||||
(void) len;
|
|
||||||
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Device #%d /metainfoservice.xml\n", device_id);
|
|
||||||
|
|
||||||
char response[strlen_P(METAINFO_TEMPLATE)];
|
|
||||||
snprintf_P(response, sizeof(response), METAINFO_TEMPLATE);
|
|
||||||
|
|
||||||
char headers[strlen_P(HEADERS) + 10];
|
|
||||||
snprintf_P(headers, sizeof(headers), HEADERS, strlen(response));
|
|
||||||
|
|
||||||
client->write(headers, strlen(headers));
|
|
||||||
client->write(response, strlen(response));
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] end /metainfoservice.xml\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::_handleControl(AsyncClient *client, unsigned int device_id, void *data, size_t len) {
|
|
||||||
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Device #%d /upnp/control/basicevent1\n", device_id);
|
|
||||||
|
|
||||||
char content[len+1];
|
|
||||||
memcpy(content, data, len);
|
|
||||||
fauxmoesp_device_t device = _devices[device_id];
|
|
||||||
|
|
||||||
// The default template is the one for GetBinaryState queries
|
|
||||||
const char * response_template = GETSTATE_TEMPLATE;
|
|
||||||
|
|
||||||
if (strstr(content, "SetBinaryState") != NULL) {
|
|
||||||
|
|
||||||
if (strstr(content, "<BinaryState>0</BinaryState>") != NULL) {
|
|
||||||
if (_setCallback) _setCallback(device_id, device.name, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (strstr(content, "<BinaryState>1</BinaryState>") != NULL) {
|
|
||||||
if (_setCallback) _setCallback(device_id, device.name, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Use a specific response template for SetBinaryState action
|
|
||||||
response_template = SETSTATE_TEMPLATE;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update current state
|
|
||||||
if (_getCallback) device.state = _getCallback(device_id, device.name);
|
|
||||||
|
|
||||||
// Send response
|
|
||||||
char response[strlen_P(response_template) + 10];
|
|
||||||
snprintf_P(response, sizeof(response), response_template, device.state ? 1 : 0);
|
|
||||||
|
|
||||||
char headers[strlen_P(HEADERS) + 10];
|
|
||||||
snprintf_P(headers, sizeof(headers), HEADERS, strlen(response));
|
|
||||||
|
|
||||||
client->write(headers, strlen(headers));
|
|
||||||
client->write(response, strlen(response));
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] end /upnp/control/basicevent1\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::_handleIcon(AsyncClient *client, unsigned int device_id, void *data, size_t len) {
|
|
||||||
(void) device_id;
|
|
||||||
(void) data;
|
|
||||||
(void) len;
|
|
||||||
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Icon /icon.png\n");
|
|
||||||
|
|
||||||
char *buf = (char *) malloc(strlen(HEADERJPG) + 5);
|
|
||||||
if (buf) {
|
|
||||||
snprintf_P(buf, strlen_P(HEADERJPG) + 5, HEADERJPG, sizeof(icon_png));
|
|
||||||
client->write(buf, strlen(buf));
|
|
||||||
client->write(icon_png, sizeof(icon_png));
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] sent icon.png [%d bytes]\n", sizeof(icon_png));
|
|
||||||
delay(100);
|
|
||||||
free(buf);
|
|
||||||
}
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] end /icon.png\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void fauxmoESP::_onTCPData(AsyncClient *client, unsigned int device_id, void *data, size_t len) {
|
|
||||||
|
|
||||||
if (!_enabled) {
|
|
||||||
DEBUG_MSG_FAUXMO("onTCPData ! _enabled\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_FAUXMO
|
|
||||||
char * p = (char *) data;
|
|
||||||
p[len] = 0;
|
|
||||||
Serial.printf("[FAUXMO] onTCPData(dev:%d,\n%s\n", device_id, p);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
{
|
|
||||||
char match[] = {"GET /setup.xml HTTP/1.1"};
|
|
||||||
if (memcmp(data, match, strlen(match)-1) == 0) {
|
|
||||||
_handleSetup(client, device_id, data, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
char match[] = {"GET /eventservice.xml HTTP/1.1"};
|
|
||||||
if (memcmp(data, match, strlen(match)-1) == 0) {
|
|
||||||
_handleEventService(client, device_id, data, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
char match[] = {"GET /metainfoservice.xml HTTP/1.1"};
|
|
||||||
if (memcmp(data, match, strlen(match)-1) == 0) {
|
|
||||||
_handleMetaInfoService(client, device_id, data, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
char match[] = {"POST /upnp/control/basicevent1 HTTP/1.1"};
|
|
||||||
if (memcmp(data, match, strlen(match)-1) == 0) {
|
|
||||||
_handleControl(client, device_id, data, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
char match[] = {"GET /icon.png HTTP/1.1"};
|
|
||||||
if (memcmp(data, match, strlen(match)-1) == 0) {
|
|
||||||
_handleIcon(client, device_id, data, len);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::_onTCPClient(AsyncClient *client, unsigned int device_id) {
|
|
||||||
|
|
||||||
for (unsigned char i = 0; i < TCP_MAX_CLIENTS; i++) {
|
|
||||||
|
|
||||||
if (!_tcpClients[i] || !_tcpClients[i]->connected()) {
|
|
||||||
|
|
||||||
_tcpClients[i] = client;
|
|
||||||
|
|
||||||
client->onAck([i](void *s, AsyncClient *c, size_t len, uint32_t time) {
|
|
||||||
(void) s;
|
|
||||||
(void) c;
|
|
||||||
(void) len;
|
|
||||||
(void) time;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
client->onData([this, i, device_id](void *s, AsyncClient *c, void *data, size_t len) {
|
|
||||||
(void) s;
|
|
||||||
_onTCPData(c, device_id, data, len);
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
client->onDisconnect([this, i](void *s, AsyncClient *c) {
|
|
||||||
(void) s;
|
|
||||||
_tcpClients[i]->free();
|
|
||||||
_tcpClients[i] = NULL;
|
|
||||||
delete c;
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Client #%d disconnected\n", i);
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
client->onError([i](void *s, AsyncClient *c, int8_t error) {
|
|
||||||
(void) s;
|
|
||||||
(void) c;
|
|
||||||
(void) error;
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Error %s (%d) on client #%d\n", c->errorToString(error), error, i);
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
client->onTimeout([i](void *s, AsyncClient *c, uint32_t time) {
|
|
||||||
(void) s;
|
|
||||||
(void) time;
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Timeout on client #%d at %i\n", i, time);
|
|
||||||
c->close();
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Client #%d connected\n", i);
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Rejecting - Too many connections\n");
|
|
||||||
client->onDisconnect([](void *s, AsyncClient *c) {
|
|
||||||
(void) s;
|
|
||||||
c->free();
|
|
||||||
delete c;
|
|
||||||
});
|
|
||||||
client->close(true);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
// Public API
|
|
||||||
// -----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
unsigned char fauxmoESP::addDevice(const char * device_name) {
|
|
||||||
|
|
||||||
fauxmoesp_device_t new_device;
|
|
||||||
unsigned int device_id = _devices.size();
|
|
||||||
|
|
||||||
// Copy name
|
|
||||||
new_device.name = strdup(device_name);
|
|
||||||
|
|
||||||
// Chip ID
|
|
||||||
#if defined(ESP32)
|
|
||||||
unsigned long chip_id = (uint32_t) ESP.getEfuseMac();
|
|
||||||
#else
|
|
||||||
unsigned long chip_id = ESP.getChipId();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Create UUID
|
|
||||||
char uuid[15];
|
|
||||||
snprintf_P(uuid, sizeof(uuid), PSTR("444776%06X%02X\0"), chip_id, device_id); // "DEV" + CHIPID + DEV_ID
|
|
||||||
new_device.uuid = strdup(uuid);
|
|
||||||
|
|
||||||
// Create Serialnumber
|
|
||||||
char serial[15];
|
|
||||||
sprintf(serial, "221793K0%06X", (uint32)chip_id); // "221703K0" + CHIPID
|
|
||||||
new_device.serial = strdup(serial);
|
|
||||||
|
|
||||||
// TCP Server
|
|
||||||
new_device.server = new AsyncServer(_base_port + device_id);
|
|
||||||
new_device.server->onClient([this, device_id](void *s, AsyncClient* c) {
|
|
||||||
(void) s;
|
|
||||||
_onTCPClient(c, device_id);
|
|
||||||
}, 0);
|
|
||||||
new_device.server->begin();
|
|
||||||
|
|
||||||
// Attach
|
|
||||||
_devices.push_back(new_device);
|
|
||||||
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Device '%s' added as #%d\n", device_name, device_id);
|
|
||||||
|
|
||||||
return device_id;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fauxmoESP::renameDevice(unsigned char id, const char * device_name) {
|
|
||||||
if (id <= _devices.size()) {
|
|
||||||
free(_devices[id].name);
|
|
||||||
_devices[id].name = strdup(device_name);
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] Device #%d renamed to '%s'\n", id, device_name);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
char * fauxmoESP::getDeviceName(unsigned char id, char * device_name, size_t len) {
|
|
||||||
if (id <= _devices.size()) {
|
|
||||||
strncpy(device_name, _devices[id].name, len);
|
|
||||||
}
|
|
||||||
return device_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::setState(unsigned char id, bool state) {
|
|
||||||
if (id <= _devices.size()) {
|
|
||||||
_devices[id].state = state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::handle() {
|
|
||||||
|
|
||||||
int len = _udp.parsePacket();
|
|
||||||
if (len > 0) {
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] handle %d bytes\n", len);
|
|
||||||
IPAddress remoteIP = _udp.remoteIP();
|
|
||||||
unsigned int remotePort = _udp.remotePort();
|
|
||||||
uint8_t data[len];
|
|
||||||
_udp.read(data, len);
|
|
||||||
_onUDPData(remoteIP, remotePort, data, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_roundsLeft > 0) {
|
|
||||||
if (millis() - _lastTick > UDP_RESPONSES_INTERVAL) {
|
|
||||||
_lastTick = millis();
|
|
||||||
_nextUDPResponse();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void fauxmoESP::enable(bool enable) {
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] %s\n", enable ? "Enabled" : "Disabled");
|
|
||||||
_enabled = enable;
|
|
||||||
#ifdef ESP32
|
|
||||||
_udp.beginMulticast(UDP_MULTICAST_IP, UDP_MULTICAST_PORT);
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
fauxmoESP::fauxmoESP(unsigned int port) {
|
|
||||||
_base_port = port;
|
|
||||||
|
|
||||||
#ifdef ESP8266
|
|
||||||
// Start UDP server on STA connection
|
|
||||||
_handler = WiFi.onStationModeGotIP([this](WiFiEventStationModeGotIP ipInfo) {
|
|
||||||
(void) ipInfo;
|
|
||||||
_udp.beginMulticast(WiFi.localIP(), UDP_MULTICAST_IP, UDP_MULTICAST_PORT);
|
|
||||||
DEBUG_MSG_FAUXMO("[FAUXMO] UDP server started\n");
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
|
|
||||||
FAUXMO ESP 2.4.2
|
|
||||||
|
|
||||||
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com>
|
|
||||||
|
|
||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#define DEFAULT_TCP_BASE_PORT 49152
|
|
||||||
#define UDP_MULTICAST_IP IPAddress(239,255,255,250)
|
|
||||||
#define UDP_MULTICAST_PORT 1900
|
|
||||||
#define TCP_MAX_CLIENTS 10
|
|
||||||
|
|
||||||
#define UDP_SEARCH_PATTERN "M-SEARCH"
|
|
||||||
#define UDP_DEVICE_PATTERN_1 "urn:Belkin:device:**"
|
|
||||||
#define UDP_DEVICE_PATTERN_2 "urn:Belkin:device:controllee:1"
|
|
||||||
#define UDP_DEVICE_PATTERN_3 "urn:Belkin:service:basicevent:1"
|
|
||||||
#define UDP_DEVICE_PATTERN_4 "ssdp:all"
|
|
||||||
#define UDP_DEVICE_PATTERN_5 "ssdpsearch:all"
|
|
||||||
#define UDP_ROOT_DEVICE "upnp:rootdevice"
|
|
||||||
|
|
||||||
#define UDP_RESPONSES_INTERVAL 250
|
|
||||||
#define UDP_RESPONSES_TRIES 5
|
|
||||||
|
|
||||||
// Here, or in the compiler configuration
|
|
||||||
#define DEBUG_FAUXMO Serial
|
|
||||||
#ifdef DEBUG_FAUXMO
|
|
||||||
#define DEBUG_MSG_FAUXMO(...) DEBUG_FAUXMO.printf( __VA_ARGS__ )
|
|
||||||
#else
|
|
||||||
#define DEBUG_MSG_FAUXMO(...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
#if defined(ESP32)
|
|
||||||
#include <WiFi.h>
|
|
||||||
#include <AsyncTCP.h>
|
|
||||||
#elif defined(ESP8266)
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
#include <ESPAsyncTCP.h>
|
|
||||||
#else
|
|
||||||
#error Platform not supported
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <WiFiUdp.h>
|
|
||||||
#include <functional>
|
|
||||||
#include <vector>
|
|
||||||
#include "WeMo.h"
|
|
||||||
|
|
||||||
typedef std::function<void(unsigned char, const char *, bool)> TSetStateCallback;
|
|
||||||
typedef std::function<bool(unsigned char, const char *)> TGetStateCallback;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
char * name;
|
|
||||||
char * uuid;
|
|
||||||
char * serial;
|
|
||||||
bool hit;
|
|
||||||
bool state;
|
|
||||||
AsyncServer * server;
|
|
||||||
} fauxmoesp_device_t;
|
|
||||||
|
|
||||||
class fauxmoESP {
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
fauxmoESP(unsigned int port = DEFAULT_TCP_BASE_PORT);
|
|
||||||
|
|
||||||
unsigned char addDevice(const char * device_name);
|
|
||||||
bool renameDevice(unsigned char id, const char * device_name);
|
|
||||||
char * getDeviceName(unsigned char id, char * buffer, size_t len);
|
|
||||||
void onSetState(TSetStateCallback fn) { _setCallback = fn; }
|
|
||||||
void onGetState(TGetStateCallback fn) { _getCallback = fn; }
|
|
||||||
void enable(bool enable);
|
|
||||||
void handle();
|
|
||||||
|
|
||||||
// backwards compatibility DEPRECATED
|
|
||||||
void onMessage(TSetStateCallback fn) { onSetState(fn); }
|
|
||||||
void setState(unsigned char id, bool state);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
bool _enabled = true;
|
|
||||||
unsigned int _base_port = DEFAULT_TCP_BASE_PORT;
|
|
||||||
std::vector<fauxmoesp_device_t> _devices;
|
|
||||||
#ifdef ESP8266
|
|
||||||
WiFiEventHandler _handler;
|
|
||||||
#endif
|
|
||||||
WiFiUDP _udp;
|
|
||||||
AsyncClient * _tcpClients[TCP_MAX_CLIENTS];
|
|
||||||
TSetStateCallback _setCallback = NULL;
|
|
||||||
TGetStateCallback _getCallback = NULL;
|
|
||||||
|
|
||||||
unsigned int _roundsLeft = 0;
|
|
||||||
unsigned int _current = 0;
|
|
||||||
unsigned long _lastTick;
|
|
||||||
IPAddress _remoteIP;
|
|
||||||
unsigned int _remotePort;
|
|
||||||
unsigned int _udpPattern;
|
|
||||||
|
|
||||||
void _sendUDPResponse(unsigned int device_id);
|
|
||||||
void _nextUDPResponse();
|
|
||||||
|
|
||||||
void _handleSetup(AsyncClient *client, unsigned int device_id, void *data, size_t len);
|
|
||||||
void _handleMetaInfoService(AsyncClient *client, unsigned int device_id, void *data, size_t len);
|
|
||||||
void _handleEventService(AsyncClient *client, unsigned int device_id, void *data, size_t len);
|
|
||||||
void _handleControl(AsyncClient *client, unsigned int device_id, void *data, size_t len);
|
|
||||||
void _handleIcon(AsyncClient *client, unsigned int device_id, void *data, size_t len);
|
|
||||||
|
|
||||||
void _onUDPData(const IPAddress remoteIP, unsigned int remotePort, void *data, size_t len);
|
|
||||||
void _onTCPData(AsyncClient *client, unsigned int device_id, void *data, size_t len);
|
|
||||||
void _onTCPClient(AsyncClient *client, unsigned int device_id);
|
|
||||||
|
|
||||||
};
|
|
||||||
@@ -9,6 +9,8 @@ my $maxCols = 16;
|
|||||||
my $maxLines = 0;
|
my $maxLines = 0;
|
||||||
my $ESP = 0;
|
my $ESP = 0;
|
||||||
my $DIR = 0;
|
my $DIR = 0;
|
||||||
|
my $ROOT = 0;
|
||||||
|
my $UINT8 = 0;
|
||||||
my $file = "";
|
my $file = "";
|
||||||
my $ctrlC = 0; # User induced termination
|
my $ctrlC = 0; # User induced termination
|
||||||
my $lineT = 0; # Linecount induced termination
|
my $lineT = 0; # Linecount induced termination
|
||||||
@@ -34,7 +36,7 @@ my %FileType = (
|
|||||||
"ico" => "image/x-icon",
|
"ico" => "image/x-icon",
|
||||||
"png" => "image/png",
|
"png" => "image/png",
|
||||||
"jpg" => "image/jpeg",
|
"jpg" => "image/jpeg",
|
||||||
"js" => "application/javascript",
|
"js" => "text/javascript",
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@@ -45,21 +47,26 @@ if (!@ARGV)
|
|||||||
$prg [file|<wildcard> [...]] [-n=##] [-l=##] [-ESP] [-DIR] [-o=outfile]
|
$prg [file|<wildcard> [...]] [-n=##] [-l=##] [-ESP] [-DIR] [-o=outfile]
|
||||||
version $VERSION
|
version $VERSION
|
||||||
|
|
||||||
Dumps out the contents of the [file] as a c array.
|
Dumps out the contents of the [file(s)] as a c array.
|
||||||
|
|
||||||
If the file is binary, it emits a hex char-array using
|
If the file is binary, it emits a hex char-array using
|
||||||
the -n and -l settings.
|
the -n and -l settings.
|
||||||
If the file is text, it emits as an ascii char-array
|
If the file is text, it emits as an ascii char-array
|
||||||
where each source line is one ascii quoted line.
|
where each source line is one ascii quoted line.
|
||||||
|
|
||||||
-ESP adds the PROGMEM attribte to the byte array data.
|
-ESP adds the PROGMEM attribute to the byte array data.
|
||||||
|
|
||||||
-DIR adds a 'directory' at the end of the output, providing
|
-DIR adds a 'directory' at the end of the output, providing
|
||||||
a reference to each input file. Most useful for a set
|
a reference to each input file. Most useful for a set
|
||||||
of files.
|
of files.
|
||||||
|
|
||||||
|
-ROOT prefixes each entry in the 'directory' with '/',
|
||||||
|
so "index.htm" becomes
|
||||||
|
|
||||||
-o=outfile Writes the output to outfile.
|
-o=outfile Writes the output to outfile.
|
||||||
|
|
||||||
|
-u overrides the default 'char' array to be 'uint8_t'
|
||||||
|
NOTE that the filename and type info remains 'char'.
|
||||||
-n=## sets the byte count for each line, defaults 32.
|
-n=## sets the byte count for each line, defaults 32.
|
||||||
-l=## sets a termination after ## lines have been printed.
|
-l=## sets a termination after ## lines have been printed.
|
||||||
|
|
||||||
@@ -83,8 +90,12 @@ foreach (@ARGV)
|
|||||||
{ $outfile = $1; }
|
{ $outfile = $1; }
|
||||||
elsif (/-ESP/)
|
elsif (/-ESP/)
|
||||||
{ $ESP = 1; }
|
{ $ESP = 1; }
|
||||||
|
elsif (/-ROOT/)
|
||||||
|
{ $ROOT = 1; }
|
||||||
elsif (/-DIR/)
|
elsif (/-DIR/)
|
||||||
{ $DIR = 1; }
|
{ $DIR = 1; }
|
||||||
|
elsif (/-u/)
|
||||||
|
{ $UINT8 = 1; }
|
||||||
elsif (-e $_ )
|
elsif (-e $_ )
|
||||||
{ push @files, $_; }
|
{ push @files, $_; }
|
||||||
elsif (/[\*\?]/)
|
elsif (/[\*\?]/)
|
||||||
@@ -114,19 +125,32 @@ if ($outfile ne "") {
|
|||||||
printf("No outfile, emitting to console.\n");
|
printf("No outfile, emitting to console.\n");
|
||||||
}
|
}
|
||||||
printf("//\n");
|
printf("//\n");
|
||||||
printf("// Command : %s %s\n", $prg, join(" ", @ARGV));
|
printf("// Command: %s %s\n", $prg, join(" ", @ARGV));
|
||||||
printf("// version: %2.1f\n", $VERSION);
|
printf("// version: %2.1f\n", $VERSION);
|
||||||
printf("// From cwd : %s\n", cwd);
|
printf("// From cwd: %s\n", cwd);
|
||||||
printf("// Generated: %s\n", scalar localtime());
|
printf("// Generated: %s\n", scalar localtime());
|
||||||
printf("// Run script in command shell, not PowerShell\n");
|
printf("// NOTE: Run script in command shell, not PowerShell\n");
|
||||||
|
printf("//\n");
|
||||||
|
|
||||||
|
if ($ESP) {
|
||||||
|
print <<EOM;
|
||||||
|
// PROGMEM is not recognized by Win32 tools...
|
||||||
|
#ifdef WIN32
|
||||||
|
#define PROGMEM
|
||||||
|
#endif
|
||||||
|
EOM
|
||||||
|
}
|
||||||
|
|
||||||
my @summary;
|
my @summary;
|
||||||
foreach my $file (@files) {
|
foreach my $file (@files) {
|
||||||
push @summary, Process($file);
|
push @summary, Process($file);
|
||||||
last if ($ctrlC);
|
last if ($ctrlC);
|
||||||
}
|
}
|
||||||
|
|
||||||
print "\n// Prototypes:\n// " . join("\n// ", @summary) . "\n";
|
print "\n// Prototypes:\n// " . join("\n// ", @summary) . "\n";
|
||||||
|
|
||||||
if ($DIR) {
|
if ($DIR) {
|
||||||
|
my $type = "char "; #($UINT8) ? "uint8_t" : "char ";
|
||||||
print <<EOM;
|
print <<EOM;
|
||||||
|
|
||||||
// Directory of Files
|
// Directory of Files
|
||||||
@@ -135,10 +159,10 @@ if ($DIR) {
|
|||||||
// an empty "" Filename is reached.
|
// an empty "" Filename is reached.
|
||||||
//
|
//
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const char * Filename;
|
const char * Filename;
|
||||||
const char * Filedata;
|
const $type * Filedata;
|
||||||
const char * Filetype;
|
const char * Filetype;
|
||||||
uint16_t Filesize;
|
uint16_t Filesize;
|
||||||
} DirEntry;
|
} DirEntry;
|
||||||
|
|
||||||
EOM
|
EOM
|
||||||
@@ -147,14 +171,14 @@ EOM
|
|||||||
foreach my $file (sort keys %DirInfo) {
|
foreach my $file (sort keys %DirInfo) {
|
||||||
#printf(STDERR "file %s, title %s, size %d\n", $file, $DirInfo{$file}{'reference'}, $DirInfo{$file}{'size'});
|
#printf(STDERR "file %s, title %s, size %d\n", $file, $DirInfo{$file}{'reference'}, $DirInfo{$file}{'size'});
|
||||||
my $title = $DirInfo{$file}{'reference'};
|
my $title = $DirInfo{$file}{'reference'};
|
||||||
my $fn = sprintf("\"%s\"", $file);
|
my $fn = sprintf("\"%s%s\"", $ROOT ? "/" : "", $file);
|
||||||
my $ext = "";
|
my $ext = "";
|
||||||
$ext = $1 if ($file =~ /.*\.(.*)/);
|
$ext = $1 if ($file =~ /.*\.(.*)/);
|
||||||
my $ftype = sprintf("\"%s\"", $FileType{$ext});
|
my $ftype = sprintf("\"%s\"", $FileType{$ext});
|
||||||
#printf(STDERR "ext: '%s' => '%s'\n", $ext, $FileType{lc($ext)});
|
#printf(STDERR "ext: '%s' => '%s'\n", $ext, $FileType{lc($ext)});
|
||||||
printf("\t{ %-20s, %-20s, %24s, %6d },\n", $fn, $title, $ftype, $DirInfo{$file}{'size'});
|
printf("\t{ %-20s, %-20s, %24s, %6d },\n", $fn, $title, $ftype, $DirInfo{$file}{'size'});
|
||||||
}
|
}
|
||||||
printf("\t{ %-20s, %-20s, %24s, %6d }\n", "\"\"", "NULL", "NULL", 0);
|
printf("\t{ %-20s, %-20s, %24s, %6d }\n", "NULL", "NULL", "NULL", 0);
|
||||||
printf("};\n\n");
|
printf("};\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -185,7 +209,7 @@ sub Process {
|
|||||||
printf("\n");
|
printf("\n");
|
||||||
printf("// File: %s\n", $file);
|
printf("// File: %s\n", $file);
|
||||||
printf("//\n");
|
printf("//\n");
|
||||||
$prototype = sprintf("const char %s[]%s", $title, ($ESP) ? " PROGMEM" : "");
|
$prototype = sprintf("const %s %s[]%s", ($UINT8) ? "uint8_t" : "char", $title, ($ESP) ? " PROGMEM" : "");
|
||||||
printf("%s = {\n", $prototype);
|
printf("%s = {\n", $prototype);
|
||||||
|
|
||||||
my $size = 0;
|
my $size = 0;
|
||||||
@@ -198,7 +222,7 @@ sub Process {
|
|||||||
$line =~ s/\r//g;
|
$line =~ s/\r//g;
|
||||||
$line =~ s/"/\\"/g;
|
$line =~ s/"/\\"/g;
|
||||||
printf("\t\"%s\\n\"\n", $line);
|
printf("\t\"%s\\n\"\n", $line);
|
||||||
$size += length($line);
|
$size += length($line) + 1; # +1 for the \n line termination.
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
printf("\t");
|
printf("\t");
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
REM
|
REM
|
||||||
REM "Compile" the web resources folder into code modules.
|
REM "Compile" the web resources folder into code modules.
|
||||||
REM
|
REM
|
||||||
REM Command : MakeByteArray -ESP *.* -o=..\Web_Resources.h
|
REM Command : MakeByteArray -ESP -ROOT -DIR *.* -o=..\Web_Resources.h
|
||||||
REM From cwd : C:/Projects/SmartSwitch/Firmware/Resources
|
REM From cwd : C:/Projects/SmartSwitch/Firmware/Resources
|
||||||
REM Run script in command shell, not PowerShell
|
REM Run script in command shell, not PowerShell
|
||||||
REM
|
REM
|
||||||
@@ -46,7 +46,7 @@ dir /b
|
|||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo.Compiling the web resources into code modules...
|
echo.Compiling the web resources into code modules...
|
||||||
MakeByteArray -ESP -DIR *.* -o=%TARG%
|
MakeByteArray -ESP -ROOT -DIR *.* -o=%TARG%
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo.Done. %TARG% has been updated and returning to prior folder.
|
echo.Done. %TARG% has been updated and returning to prior folder.
|
||||||
|
|||||||