From 2d7d728e184f24961e4a0871f4d5bed3e2fe652a Mon Sep 17 00:00:00 2001 From: Jakob Stendahl Date: Mon, 23 Oct 2017 14:16:27 +0200 Subject: Moved everything into the new REPO --- IOT-clapSensor.ino | 266 ++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 14 ++- index.h | 119 ++++++++++++++++++++++ memoryAccess.cpp | 77 +++++++++++++++ memoryAccess.h | 25 +++++ settings.h | 146 +++++++++++++++++++++++++++ style.h | 3 + webPage/index.html | 127 ++++++++++++++++++++++++ webPage/settings.html | 167 +++++++++++++++++++++++++++++++ webPage/style.css | 148 ++++++++++++++++++++++++++++ 10 files changed, 1091 insertions(+), 1 deletion(-) create mode 100644 IOT-clapSensor.ino create mode 100644 index.h create mode 100644 memoryAccess.cpp create mode 100644 memoryAccess.h create mode 100644 settings.h create mode 100644 style.h create mode 100644 webPage/index.html create mode 100644 webPage/settings.html create mode 100644 webPage/style.css diff --git a/IOT-clapSensor.ino b/IOT-clapSensor.ino new file mode 100644 index 0000000..a6f236e --- /dev/null +++ b/IOT-clapSensor.ino @@ -0,0 +1,266 @@ +#include +#include +#include +#include +#include +#include "memoryAccess.h" + +#include "style.h" // custom stylesheet, get it from stylesheet +#include "index.h" // Index page html, get it from MAIN_page +#include "settings.h" // Settings page html, get it from SETTINGS_page + +// User variables +const String MemoryAccessVersion = "cp-a1"; +const String deviceType = "clapSensor"; // This is what kind of device it is, crucial for the homeServer when determing which API to use +const int wifiConnectionTimeout = 5; // n*5 sec +char ssid[30]; +char password[30]; +char APssid[30]; +String deviceName; +String deviceLocation; +String deviceId; + +memoryAccess MemoryAccess; + +const int lampPin = 14; // Pin the appliance is connected to +const int sensorPin = 5; // Pin the sensor is connected to + +bool lampOn; // Is the appliance turned on? +bool sensorActive; // Should we care about sensor input? +bool buttonState; // Used to make button not flash indefinetly + +ESP8266WebServer server ( 80 ); // webServer on port 80 + +void setup ( void ) { + // Activate serial + Serial.begin ( 115200 ); + MemoryAccess.init(); + MemoryAccess.dump(); + + + if (MemoryAccess.readAscii("version")) { + deviceName = MemoryAccess.readAscii("deviceName"); + deviceLocation = MemoryAccess.readAscii("deviceLocation"); + deviceId = MemoryAccess.readAscii("deviceId"); + MemoryAccess.readAscii("SSID").toCharArray(ssid, 30); + MemoryAccess.readAscii("Password").toCharArray(password, 30); + (deviceType + "-" + deviceId).toCharArray(APssid, 30); + } else { + // SET TO DEFAULT VALUES + MemoryAccess.writeAscii("version", MemoryAccessVersion); + MemoryAccess.writeAscii("deviceName", ""); + MemoryAccess.writeAscii("deviceLocation", ""); + MemoryAccess.writeAscii("deviceGuid", uniqId()); + MemoryAccess.writeAscii("SSID", ""); + MemoryAccess.writeAscii("Password", ""); + MemoryAccess.commit(); + Serial.println("Have set default values, making watchdog boot us!"); + while (1); + } + + Serial.print("ssid: "); + Serial.println(ssid); + Serial.print("Password: "); + Serial.println(password); + Serial.print("deviceName: "); + Serial.println(deviceName); + Serial.print("deviceLocation: "); + Serial.println(deviceLocation); + Serial.print("deviceId: "); + Serial.println(deviceId); + + + // Setup pins and set default values + pinMode ( lampPin, OUTPUT ); + pinMode ( sensorPin, INPUT ); + lampOn = false; // Vil at denne skal bli husket, altsÃ¥ være samme etter boot + sensorActive = true; + digitalWrite(lampPin, lampOn); + digitalWrite(sensorPin, sensorActive); + + // Try to connect to WiFi + WiFi.begin ( ssid, password ); + + // Wait for connection + int connTime = 0; + while ( WiFi.status() != WL_CONNECTED ) { + if (connTime >= wifiConnectionTimeout) { break; } + delay ( 1000 ); + Serial.print ( "." ); + connTime++; + } + + Serial.println ( "" ); + + if (connTime >= wifiConnectionTimeout) { + WiFi.softAP(APssid); // add password here as second parameter, currently just a open hotspot + IPAddress myIP = WiFi.softAPIP(); + Serial.print("APssid: "); + Serial.println(APssid); + Serial.print("AP IP address: "); + Serial.println(myIP); + } else { + Serial.print ( "Connected to " ); + Serial.println ( ssid ); + Serial.print ( "IP address: " ); + Serial.println ( WiFi.localIP() ); + } + + if ( MDNS.begin ( "esp8266" ) ) { + Serial.println ( "MDNS responder started" ); + } + + // Gui + server.on ( "/", handleRoot ); + server.on ( "/settings", handleSettings ); + // Json + server.on ( "/j/", handleJson ); + server.on ( "/j", handleJson ); + // Others + server.on ( "/style.css", handleStylesheet); + server.on ( "/inline", []() { + server.send ( 200, "text/plain", "this works as well" ); + } ); + server.onNotFound ( handleNotFound ); + server.begin(); + Serial.println ( "HTTP server started" ); +} + +void loop ( void ) { + server.handleClient(); + + if (sensorActive) { + bool currButtonState = digitalRead(sensorPin); + if (currButtonState == true && buttonState == false) { + setLamp("TOGGLE"); + + } + buttonState = currButtonState; + } + +} + +String uniqId() { + return "AAAAAAAAAA"; +} + +void setLamp(String action) { + if (action == "ON") { + lampOn = true; + } else if (action == "OFF") { + lampOn = false; + } else if (action == "TOGGLE") { + lampOn = !lampOn; + } + digitalWrite(lampPin, lampOn); +} + +void setSensor(String action) { + if (action == "ON") { + sensorActive = true; + } else if (action == "OFF") { + sensorActive = false; + } +} + +void handleRoot() { + String htmlPage = MAIN_page; + htmlPage.replace("{{NAME}}", deviceName); + htmlPage.replace("{{Location}}", deviceLocation); + server.send ( 200, "text/html", htmlPage); +} + +void handleJson() { + // escape dobbel tøddler med en \ slik \" for Ã¥ ha dobbel tøddler i en string + + for (int i = 0; i < server.args(); i++) { + String argKey = server.argName(i); + String argVal = server.arg(i); + + if (argKey == "lamp") { + if (argVal == "1" || argVal == "true") { setLamp("ON"); } + else { setLamp("OFF"); } + } else if (argKey == "sens") { + if (argVal == "1" || argVal == "true") { setSensor("ON"); } + else { setSensor("OFF"); } + } + } + + String jsonAnswer = "{"; + jsonAnswer += "\"deviceName\":\""; + jsonAnswer += deviceName; + jsonAnswer += "\",\"deviceId\":\""; + jsonAnswer += deviceId; + jsonAnswer += "\",\"deviceType\":\""; + jsonAnswer += deviceType; + jsonAnswer += "\",\"deviceLocation\":\""; + jsonAnswer += deviceLocation; + jsonAnswer += "\",\"lampOn\":\""; + jsonAnswer += (String)lampOn; + jsonAnswer += "\",\"sensorOn\":\""; + jsonAnswer += (String)sensorActive; + jsonAnswer += "\"}"; + server.send ( 200, "text/html", jsonAnswer); +} + +void handleSettings() { + + String type, _deviceName, _deviceLocation, _ssid, _password; + for (int i = 0; i < server.args(); i++) { + String argKey = server.argName(i); + String argVal = server.arg(i); + + if (argKey == "txtDeviceName") { + _deviceName = argVal; + } else if (argKey == "txtDeviceLocation") { + _deviceLocation = argVal; + } else if (argKey == "txtSSID") { + _ssid = argVal; + } else if (argKey == "txtPassword") { + _password = argVal; + } + } + + if (type == "settings") { + if (_deviceName != "") { + MemoryAccess.writeAscii("deviceName", _deviceName); + } else if (_deviceLocation != "") { + MemoryAccess.writeAscii("deviceLocation", _deviceLocation); + } else if (_ssid != "") { + MemoryAccess.writeAscii("SSID", _ssid); + } else if (_password != "") { + MemoryAccess.writeAscii("Password", _password); + } + MemoryAccess.commit(); + while (1); + } + + String htmlResponse = SETTINGS_page; + htmlResponse.replace("{{NAME}}", deviceName); + htmlResponse.replace("{{SUCCESSMSG}}", ""); + htmlResponse.replace("{{DEVICENAME}}", deviceName); + htmlResponse.replace("{{DEVICELOCATION}}", deviceLocation); + server.send( 200, "text/html", htmlResponse ); +} + +void handleStylesheet() { + String style = stylesheet; + server.send ( 200, "text/css", style); +} + +void handleNotFound() { + String message = "File Not Found\n\n"; + message += "URI: "; + message += server.uri(); + message += "\nMethod: "; + message += ( server.method() == HTTP_GET ) ? "GET" : "POST"; + message += "\nArguments: "; + message += server.args(); + message += "\n"; + + for ( uint8_t i = 0; i < server.args(); i++ ) { + message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n"; + } + + server.send ( 404, "text/plain", message ); +} diff --git a/README.md b/README.md index 80d5e5d..2fe0d41 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ -# IOT-ClapSensor \ No newline at end of file +# IOT-ClapSensor + +### Features +- Enable/Disable lamp through web app +- Enable/Disable possibility to use physical button to toggle lamp through web app +- Live update of stat in web app +- It will act as an AP if it can´t connect to the saved network +- It will save user preferences in the EEPROM + +### TODO +- Gjør OTA mulig +- Sett opp kryptering (Tror bare det kommer til å bli passord for å endre instillingene) +- Få klappesensoren til å virke som ønsket diff --git a/index.h b/index.h new file mode 100644 index 0000000..caa1df7 --- /dev/null +++ b/index.h @@ -0,0 +1,119 @@ +const char MAIN_page[] PROGMEM = R"=====( + + + + + + + + {{NAME}} + + + + + + + + + + +
+ +
+
+ +
+

+ {{NAME}} + + + +

+
+
{{Location}}
+ +
+
+ + +
+
+ +
+
+ +
+ + + + + + + +)====="; diff --git a/memoryAccess.cpp b/memoryAccess.cpp new file mode 100644 index 0000000..2887b74 --- /dev/null +++ b/memoryAccess.cpp @@ -0,0 +1,77 @@ +/* This class is made spesifically for the IOT-clapSensor + ADRESSES + version and others {0, 9} + deviceGuid {10, 19} + deviceName {20, 69} + deviceLocation {70, 119} + SSID {120, 269} + Password {270, 219} +*/ +#include "Arduino.h" +#include "memoryAccess.h" +#include + +void memoryAccess::init() { + EEPROM.begin(512); +} + +struct memoryAdress memoryAccess::getAdress(String varName) { + memoryAdress tmp; + if ((String) "version" == varName) { tmp = {0, 4}; } + else if ((String) "deviceId" == varName) { tmp = {10, 19}; } + else if ((String) "deviceName" == varName) { tmp = {20, 69}; } + else if ((String) "deviceLocation" == varName) { tmp = {70, 119}; } + else if ((String) "SSID" == varName) { tmp = {120, 269}; } + else if ((String) "Password" == varName) { tmp = {270, 219}; } + else { Serial.println("UNKNOWN MEMNAME"); } + return tmp; +} + +bool memoryAccess::writeAscii(String varName, String data) { + /* Write ascii string to EEPROM */ + struct memoryAdress memAdress = this->getAdress(varName); + int LString = data.length() - 1; + int LMemory = memAdress.endPos - memAdress.startPos; + + if (LString > LMemory) { return false; } + + int o = 0; + for (int i = memAdress.startPos; i <= memAdress.endPos; i++) { + byte Cchar = data.charAt(o); + if (EEPROM.read(i) != Char) { + EEPROM.write(i, Cchar); + } + o++; + } + return true; +} + +String memoryAccess::readAscii(String varName) { + /* Read ascii string from EEPROM */ + struct memoryAdress memAdress = this->getAdress(varName); + String buffer = ""; + + for (int i = memAdress.startPos; i <= memAdress.endPos; i++) { + char tmpByte = EEPROM.read(i); + if (tmpByte != '\0') { + buffer += tmpByte; + } + } + + return buffer; +} + +void memoryAccess::commit() { + EEPROM.commit(); +} + +void memoryAccess::dump() { + Serial.println(""); + Serial.println("EEPROM DUMP:"); + for (int i = 0; i < 512; i++) { + char cChar = EEPROM.read(i); + String cStr = String(cChar); + Serial.print("[" + String(i) + "]: "); + Serial.println(String(cStr)); + } +} diff --git a/memoryAccess.h b/memoryAccess.h new file mode 100644 index 0000000..048ae86 --- /dev/null +++ b/memoryAccess.h @@ -0,0 +1,25 @@ +/* + memoryAccess.h - Library for easy writing to the IOT-ClapSensor EEPROM +*/ +#ifndef memoryAccess_h +#define memoryAccess_h + +#include "Arduino.h" + +class memoryAccess { + public: + void init(); + bool writeAscii(String varName, String data); + String readAscii(String varName); + void commit(); + void dump(); + private: + struct memoryAdress getAdress(String varName); +}; + +struct memoryAdress { + int startPos; + int endPos; +}; + +#endif diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..af1b7f6 --- /dev/null +++ b/settings.h @@ -0,0 +1,146 @@ +const char SETTINGS_page[] PROGMEM = R"=====( + + + + + + + + {{NAME}} + + + + + + + + + + +
+ + + +
+
+ +
+

Password

+
+ +
+
+ +
To change any settings, you must enter the correct password"
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+

Settings

+
+ +
+
+ +
Device settings:
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
Wifi settings:
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ + + +
Note: The device will reboot and turn of any connected appliances when you save the settings. +
+
+ +
+
+ +
+
+ +
+
+ +
+

Version & OTA

+
+ +
+
+
Device type: clapSensor
+
Current version: 1.0.0
+
+
+ +
+ +
+
+ +
+
+ +
Note: The device will reboot and turn of any connected appliances when you flash a new version. +
+
+ +
+
+ +
+
+
+ + + + +)====="; diff --git a/style.h b/style.h new file mode 100644 index 0000000..b29f3b5 --- /dev/null +++ b/style.h @@ -0,0 +1,3 @@ +const char stylesheet[] PROGMEM = R"=====( + body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;background-color:#222}.switch{position:relative;display:inline-block;width:60px;height:34px}.switch input{display:none}.slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.4s;transition:.4s}.slider:before{position:absolute;content:"";height:26px;width:26px;left:4px;bottom:4px;background-color:#fff;-webkit-transition:.4s;transition:.4s}input:checked+.slider{background-color:#2196F3}input:focus+.slider{box-shadow:0 0 1px #2196F3}input:checked+.slider:before{-webkit-transform:translateX(26px);-ms-transform:translateX(26px);transform:translateX(26px)}.slider.round{border-radius:34px}.slider.round:before{border-radius:50%}.toggle-button{position:relative;display:inline-block;color:#fff}.toggle-button label{display:inline-block;text-transform:uppercase;cursor:pointer;text-align:left}.toggle-button input{display:none}.toggle-button__icon{cursor:pointer;pointer-events:none}.toggle-button__icon:after,.toggle-button__icon:before{content:"";position:absolute;top:45%;left:35%;transition:.2s ease-out}.toggle-button--tuli label{line-height:20px;text-indent:30px}.toggle-button--tuli input[type=checkbox]:checked~.toggle-button__icon{background:#fff}.toggle-button--tuli input[type=checkbox]:checked~.toggle-button__icon:after,.toggle-button--tuli input[type=checkbox]:checked~.toggle-button__icon:before{opacity:1}.toggle-button--tuli .toggle-button__icon{position:absolute;top:0;left:0;width:20px;height:20px;transition:all .2s;border:2px solid #fff;border-radius:1px;box-shadow:0 1px 0 rgba(0,0,0,.1);text-shadow:0 1px 0 rgba(0,0,0,.1)}.toggle-button--tuli .toggle-button__icon:after,.toggle-button--tuli .toggle-button__icon:before{left:2px;width:12px;height:2px;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.1);top:35%;background:#61B136;opacity:0;transform-origin:left center}.toggle-button--tuli .toggle-button__icon:before{transform:translate(0,0) rotate(45deg) scale(.6,1)}.toggle-button--tuli .toggle-button__icon:after{transform:translate(4px,6px) rotate(-45deg)}.toggle-button--tuli:hover input[type=checkbox]:not(:checked)~.toggle-button__icon{box-shadow:0 1px 3px rgba(0,0,0,.2);text-shadow:0 1px 3px rgba(0,0,0,.2)} +)====="; diff --git a/webPage/index.html b/webPage/index.html new file mode 100644 index 0000000..c80ee61 --- /dev/null +++ b/webPage/index.html @@ -0,0 +1,127 @@ + + + + + + + + {{NAME}} + + + + + + + + + + +
+ +
+
+ +
+

+ {{NAME}} + + + +

+
+
{{Location}}
+ +
+
+ + +
+
+ +
+
+ +
+ + + + + + diff --git a/webPage/settings.html b/webPage/settings.html new file mode 100644 index 0000000..cdda75a --- /dev/null +++ b/webPage/settings.html @@ -0,0 +1,167 @@ + + + + + + + + {{NAME}} + + + + + + + + + + +
+ + + + + +
+
+ +
+

Password

+
+ +
+
+ +
To change any settings, you must enter the correct password"
+
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+

Settings

+
+ +
+
+ + + +
Device settings:
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
Wifi settings:
+
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ + +
Note: The device will reboot and turn of any connected appliances when you save the settings. +
Note: Empty or unchanged fields will not be updated. +
+
+ +
+
+ +
+
+ +
+
+ +
+

Version & OTA

+
+ +
+
+
Device type: clapSensor
+
Current version: 1.0.0
+
+
+ +
+ +
+
+ +
+
+ +
Note: The device will reboot and turn of any connected appliances when you flash a new version. +
+
+ +
+
+ +
+
+
+ + + + + + diff --git a/webPage/style.css b/webPage/style.css new file mode 100644 index 0000000..63663d5 --- /dev/null +++ b/webPage/style.css @@ -0,0 +1,148 @@ +body { + margin: 0; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; + font-size: 1rem; + font-weight: normal; + line-height: 1.5; + background-color: #222; +} + +/* SWITCH */ + .switch { + position: relative; + display: inline-block; + width: 60px; + height: 34px; +} + +.switch input {display:none;} + +.slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #ccc; + -webkit-transition: .4s; + transition: .4s; +} + +.slider:before { + position: absolute; + content: ""; + height: 26px; + width: 26px; + left: 4px; + bottom: 4px; + background-color: white; + -webkit-transition: .4s; + transition: .4s; +} + +input:checked + .slider { + background-color: #2196F3; +} + +input:focus + .slider { + box-shadow: 0 0 1px #2196F3; +} + +input:checked + .slider:before { + -webkit-transform: translateX(26px); + -ms-transform: translateX(26px); + transform: translateX(26px); +} + +.slider.round { + border-radius: 34px; +} + +.slider.round:before { + border-radius: 50%; +} +/* ./SWITCH */ + +.toggle-button { + position: relative; + display: inline-block; + color: #fff; +} + + .toggle-button label { + display: inline-block; + text-transform: uppercase; + cursor: pointer; + text-align: left; +} + +.toggle-button input { + display: none; +} + + .toggle-button__icon { + cursor: pointer; + pointer-events: none; +} + +.toggle-button__icon:before, .toggle-button__icon:after { + content: ""; + position: absolute; + top: 45%; + left: 35%; + transition: 0.2s ease-out; +} + +.toggle-button--tuli label { + line-height: 20px; + text-indent: 30px; +} + +.toggle-button--tuli input[type=checkbox]:checked ~ .toggle-button__icon { + background: #fff; +} + +.toggle-button--tuli input[type=checkbox]:checked ~ .toggle-button__icon:before, .toggle-button--tuli input[type=checkbox]:checked ~ .toggle-button__icon:after { + opacity: 1; +} + +.toggle-button--tuli .toggle-button__icon { + position: absolute; + top: 0; + left: 0; + width: 20px; + height: 20px; + transition: all 0.2s; + border: 2px solid #fff; + border-radius: 1px; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); + text-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); +} + +.toggle-button--tuli .toggle-button__icon:before, .toggle-button--tuli .toggle-button__icon:after { + top: 5px; + left: 2px; + width: 12px; + height: 2px; + border-radius: 3px; + background: #fff; + box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1); + top: 35%; + background: #61B136; + opacity: 0; + transform-origin: left center; +} + +.toggle-button--tuli .toggle-button__icon:before { + transform: translate(0, 0) rotate(45deg) scale(0.6, 1); +} + +.toggle-button--tuli .toggle-button__icon:after { + transform: translate(4px, 6px) rotate(-45deg); +} + +.toggle-button--tuli:hover input[type=checkbox]:not(:checked) ~ .toggle-button__icon { + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.2); +} -- cgit v1.2.3