aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--IOT-clapSensor.ino266
-rw-r--r--README.md14
-rw-r--r--index.h119
-rw-r--r--memoryAccess.cpp77
-rw-r--r--memoryAccess.h25
-rw-r--r--settings.h146
-rw-r--r--style.h3
-rw-r--r--webPage/index.html127
-rw-r--r--webPage/settings.html167
-rw-r--r--webPage/style.css148
10 files changed, 1091 insertions, 1 deletions
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 <ESP8266WiFi.h>
+#include <WiFiClient.h>
+#include <ESP8266WebServer.h>
+#include <ESP8266mDNS.h>
+#include <EEPROM.h>
+#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"=====(
+ <!DOCTYPE html>
+ <html>
+
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+ <title>{{NAME}}</title>
+
+ <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
+ <link rel="stylesheet" href="./style.css">
+ </head>
+
+
+ <body>
+
+ <nav class="navbar navbar-dark bg-dark">
+ <a class="navbar-brand" href="/">IOT Device</a>
+ <a class="navbar-toggler navbar-toggler-right" href="/settings">
+ &#9881;
+ </a>
+ </nav>
+
+ <div class="container-fluid pt-3">
+
+ <div class="card card-inverse bg-dark text-white">
+ <div class="card-body">
+
+ <div class="card-title">
+ <h3>
+ {{NAME}}
+ <span class="text-align:right;">
+ <label class="switch float-right">
+ <input type="checkbox" id="lampActive">
+ <span class="slider round"></span>
+ </label>
+ </span>
+ </h3>
+ </div>
+ <h6 class="card-subtitle mb-2 text-muted">{{Location}}</h6>
+
+ <hr />
+ <div class="toggle-button toggle-button--tuli">
+ <input id="sensorActive" type="checkbox">
+ <label for="sensorActive">Clap sensor</label>
+ <div class="toggle-button__icon"></div>
+ </div>
+
+ </div>
+ </div>
+
+ </div>
+
+
+ <script type="text/javascript">
+ document.getElementById("lampActive").addEventListener("change", changLampState);
+ document.getElementById("sensorActive").addEventListener("change", changSensorState);
+ setInterval(getStates, 1000);
+
+ function ajax_request(adress, callback_function) {
+ /* A simple ajax request wrapper
+ Doesn´t return anything else than the callback */
+ var xhttp = new XMLHttpRequest();
+ xhttp.onreadystatechange = function() {
+ if (this.readyState == 4 && this.status == 200) {
+ callback_function(this.responseText);
+ }
+ };
+ xhttp.open("POST", adress, true);
+ xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ xhttp.send();
+ }
+
+ function changLampState() {
+ var boxState = document.getElementById("lampActive").checked;
+ if (boxState) {
+ ajax_request("/j/lamp/1", function(e) {
+ console.log(e);
+ });
+ } else {
+ ajax_request("/j/lamp/0", function(e) {
+ console.log(e);
+ });
+ }
+ }
+
+ function changSensorState() {
+ var sensorState = document.getElementById("sensorActive").checked;
+ if (sensorState) {
+ ajax_request("/j/sens/1", function(e) {
+ console.log(e);
+ });
+ } else {
+ ajax_request("/j/sens/0", function(e) {
+ console.log(e);
+ });
+ }
+ }
+
+ function getStates() {
+ ajax_request("/j/", function(e) {
+ result = JSON.parse(e);
+
+ var lampOn = false;
+ var sensorOn = false;
+ if (result['lampOn'] == 1) { lampOn = true; }
+ if (result['sensorOn'] == 1) { sensorOn = true; }
+
+ document.getElementById("lampActive").checked = lampOn;
+ document.getElementById("sensorActive").checked = sensorOn;
+ });
+ }
+ </script>
+ </body>
+
+ </html>
+
+)=====";
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 <EEPROM.h>
+
+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"=====(
+ <!DOCTYPE html>
+ <html>
+
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+ <title>{{NAME}}</title>
+
+ <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
+ <link rel="stylesheet" href="./style.css">
+ </head>
+
+
+ <body>
+
+ <nav class="navbar navbar-dark bg-dark">
+ <a class="navbar-brand" href="/">IOT Device</a>
+ <a class="navbar-toggler navbar-toggler-right" href="/settings">
+ &#9881;
+ </a>
+ </nav>
+
+ <div class="container-fluid pt-3 pb-3">
+
+ <div class="alert alert-danger" role="alert">
+ <b>Warning:</b> The password-protection does not work yet! Make sure to don´t expose the device.
+ </div>
+
+ <div class="card card-inverse bg-dark text-white mt-3">
+ <div class="card-body">
+
+ <div class="card-title">
+ <h3>Password</h3>
+ </div>
+
+ <div class="container">
+ <form>
+
+ <h5 class="small text-muted">To change any settings, you must enter the correct password"</h5>
+ <div class="form-group row">
+
+ <div class="col-sm-12">
+ <input type="password" class="form-control" id="name" placeholder="Device password" readonly>
+ </div>
+ </div>
+
+ </form>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="card card-inverse bg-dark text-white mt-3">
+ <div class="card-body">
+
+ <div class="card-title">
+ <h3>Settings</h3>
+ </div>
+
+ <div class="container">
+ <form>
+
+ <h5 class="small text-muted">Device settings:</h5>
+ <div class="form-group row">
+ <label for="inputEmail3" class="col-sm-2 col-form-label">Device name</label>
+ <div class="col-sm-10">
+ <input type="name" class="form-control" id="name" placeholder="Rooflight">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <label for="inputEmail3" class="col-sm-2 col-form-label">Location</label>
+ <div class="col-sm-10">
+ <input type="name" class="form-control" id="name" placeholder="Bathroom">
+ </div>
+ </div>
+
+ <h5 class="small text-muted">Wifi settings:</h5>
+ <div class="form-group row">
+ <label for="inputEmail3" class="col-sm-2 col-form-label">SSID</label>
+ <div class="col-sm-10">
+ <input type="name" class="form-control" id="name" placeholder="Name of router">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <label for="inputEmail3" class="col-sm-2 col-form-label">Pasword</label>
+ <div class="col-sm-10">
+ <input type="name" class="form-control" id="name" placeholder="Passphrase (If open, write nothing)">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <div class="col-sm-10">
+
+ <button type="submit" class="btn btn-primary">Save</button>
+ <button type="submit" class="btn btn-muted">Cancel</button>
+ <br /><span class="small text-muted"> <b> Note:</b> The device will reboot and turn of any connected appliances when you save the settings.</span>
+ </div>
+ </div>
+
+ </form>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="card card-inverse bg-dark text-white mt-3">
+ <div class="card-body">
+
+ <div class="card-title">
+ <h3>Version & OTA</h3>
+ </div>
+
+ <div class="container">
+ <form>
+ <h5>Device type: <b>clapSensor</b></h5>
+ <h5>Current version: <b>1.0.0</b></h5>
+ <hr />
+ <div class="form-group row">
+ <label for="inputEmail3" class="col-sm-2 col-form-label">Update file:</label>
+ <div class="col-sm-10">
+ <input type="file" class="form-control-file" id="exampleFormControlFile1">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <div class="col-sm-10">
+ <button type="submit" class="btn btn-danger">Flash new version</button>
+ <br /><span class="small text-muted"> <b> Note:</b> The device will reboot and turn of any connected appliances when you flash a new version.</span>
+ </div>
+ </div>
+
+ </form>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+ </body>
+
+ </html>
+)=====";
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 @@
+<!DOCTYPE html>
+<html>
+
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+ <title>{{NAME}}</title>
+
+ <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
+ <link rel="stylesheet" href="./style.css">
+ </head>
+
+
+ <body>
+
+ <nav class="navbar navbar-dark bg-dark">
+ <a class="navbar-brand" href="/">IOT Device</a>
+ <a class="navbar-toggler navbar-toggler-right" href="/settings">
+ &#9881;
+ </a>
+ </nav>
+
+ <div class="container-fluid pt-3">
+
+ <div class="card card-inverse bg-dark text-white">
+ <div class="card-body">
+
+ <div class="card-title">
+ <h3>
+ {{NAME}}
+ <span class="text-align:right;">
+ <label class="switch float-right">
+ <input type="checkbox" id="lampActive">
+ <span class="slider round"></span>
+ </label>
+ </span>
+ </h3>
+ </div>
+ <h6 class="card-subtitle mb-2 text-muted">{{Location}}</h6>
+
+ <hr />
+ <div class="toggle-button toggle-button--tuli">
+ <input id="sensorActive" type="checkbox">
+ <label for="sensorActive">Clap sensor</label>
+ <div class="toggle-button__icon"></div>
+ </div>
+
+ </div>
+ </div>
+
+ </div>
+
+
+ <script type="text/javascript">
+ var pollingActive = true; // Prevent states from being updated while trying to change them.
+ document.getElementById("lampActive").addEventListener("change", changLampState);
+ document.getElementById("sensorActive").addEventListener("change", changSensorState);
+ setInterval(getStates, 1000);
+
+ function ajax_request(adress, callback_function) {
+ /* A simple ajax request wrapper
+ Doesn´t return anything else than the callback */
+ var xhttp = new XMLHttpRequest();
+ xhttp.onreadystatechange = function() {
+ if (this.readyState == 4 && this.status == 200) {
+ callback_function(this.responseText);
+ }
+ };
+ xhttp.open("POST", adress, true);
+ xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+ xhttp.send();
+ }
+
+ function changLampState() {
+ var boxState = document.getElementById("lampActive").checked;
+ if (boxState) {
+ pollingActive = false;
+ ajax_request("/j?lamp=1", function(e) {
+ pollingActive = true;
+ console.log(e);
+ });
+ } else {
+ pollingActive = false;
+ ajax_request("/j?lamp=0", function(e) {
+ pollingActive = true;
+ console.log(e);
+ });
+ }
+ }
+
+ function changSensorState() {
+ var sensorState = document.getElementById("sensorActive").checked;
+ if (sensorState) {
+ pollingActive = false;
+ ajax_request("/j?sens=1", function(e) {
+ pollingActive = true;
+ console.log(e);
+ });
+ } else {
+ pollingActive = false;
+ ajax_request("/j?sens=0", function(e) {
+ pollingActive = true;
+ console.log(e);
+ });
+ }
+ }
+
+ function getStates() {
+ if (pollingActive) {
+ ajax_request("/j/", function(e) {
+ result = JSON.parse(e);
+
+ var lampOn = false;
+ var sensorOn = false;
+ if (result['lampOn'] == 1) { lampOn = true; }
+ if (result['sensorOn'] == 1) { sensorOn = true; }
+
+ document.getElementById("lampActive").checked = lampOn;
+ document.getElementById("sensorActive").checked = sensorOn;
+ });
+ }
+ }
+ </script>
+ </body>
+
+</html>
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 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+
+ <title>{{NAME}}</title>
+
+ <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
+ <link rel="stylesheet" href="./style.css">
+</head>
+
+
+<body>
+
+ <nav class="navbar navbar-dark bg-dark">
+ <a class="navbar-brand" href="/">IOT Device</a>
+ <a class="navbar-toggler navbar-toggler-right" href="/settings">
+ &#9881;
+ </a>
+ </nav>
+
+ <div class="container-fluid pt-3 pb-3">
+
+ <div id="success-box" class="alert alert-success" role="alert">
+ {{SUCCESSMSG}}
+ </div>
+
+ <div class="alert alert-danger" role="alert">
+ <b>Warning:</b> The password-protection does not work yet! Make sure to don´t expose the device.
+ </div>
+
+ <div class="card card-inverse bg-dark text-white mt-3">
+ <div class="card-body">
+
+ <div class="card-title">
+ <h3>Password</h3>
+ </div>
+
+ <div class="container">
+ <form>
+
+ <h5 class="small text-muted">To change any settings, you must enter the correct password"</h5>
+ <div class="form-group row">
+
+ <div class="col-sm-12">
+ <input type="password" class="form-control" id="" placeholder="Device password" readonly>
+ </div>
+ </div>
+
+ </form>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="card card-inverse bg-dark text-white mt-3">
+ <div class="card-body">
+
+ <div class="card-title">
+ <h3>Settings</h3>
+ </div>
+
+ <div class="container">
+ <form method="post">
+
+ <input type="hidden" name="type" value="settings">
+
+ <h5 class="small text-muted">Device settings:</h5>
+ <div class="form-group row">
+ <label for="lblDeviceName" class="col-sm-2 col-form-label">Device name</label>
+ <div class="col-sm-10">
+ <input type="text" class="form-control" name="txtDeviceName" id="txtDeviceName" placeholder="Ex. Rooflight">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <label for="lblDeviceLocation" class="col-sm-2 col-form-label">Location</label>
+ <div class="col-sm-10">
+ <input type="text" class="form-control" name="txtDeviceLocation" id="txtDeviceLocation" placeholder="Ex. Bathroom">
+ </div>
+ </div>
+
+ <h5 class="small text-muted">Wifi settings:</h5>
+ <div class="form-group row">
+ <label for="lblSSID" class="col-sm-2 col-form-label">SSID</label>
+ <div class="col-sm-10">
+ <input type="name" class="form-control" name="txtSSID" id="txtSSID" placeholder="Name of router">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <label for="lblPassword" class="col-sm-2 col-form-label">Pasword</label>
+ <div class="col-sm-10">
+ <input type="name" class="form-control" name="txtPassword" id="txtPassword" placeholder="Passphrase (If open, write nothing)">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <div class="col-sm-10">
+
+ <button type="submit" class="btn btn-primary">Save</button>
+ <br /><span class="small text-muted"> <b> Note:</b> The device will reboot and turn of any connected appliances when you save the settings.</span>
+ <br /><span class="small text-muted"> <b> Note:</b> Empty or unchanged fields will not be updated.</span>
+ </div>
+ </div>
+
+ </form>
+ </div>
+
+ </div>
+ </div>
+
+ <div class="card card-inverse bg-dark text-white mt-3">
+ <div class="card-body">
+
+ <div class="card-title">
+ <h3>Version & OTA</h3>
+ </div>
+
+ <div class="container">
+ <form>
+ <h5>Device type: <b>clapSensor</b></h5>
+ <h5>Current version: <b>1.0.0</b></h5>
+ <hr />
+ <div class="form-group row">
+ <label for="inputEmail3" class="col-sm-2 col-form-label">Update file:</label>
+ <div class="col-sm-10">
+ <input type="file" class="form-control-file" id="exampleFormControlFile1">
+ </div>
+ </div>
+
+ <div class="form-group row">
+ <div class="col-sm-10">
+ <button type="submit" class="btn btn-danger">Flash new version</button>
+ <br /><span class="small text-muted"> <b> Note:</b> The device will reboot and turn of any connected appliances when you flash a new version.</span>
+ </div>
+ </div>
+
+ </form>
+ </div>
+
+ </div>
+ </div>
+ </div>
+
+
+ <script type="text/javascript">
+ var successAlert = document.getElementById('success-box');
+
+ updatePlaceholder("txtDeviceName", "{{DEVICENAME}}");
+ updatePlaceholder("txtDeviceLocation", "{{DEVICELOCATION}}");;
+
+ if (successAlert.innerHtml == "") {
+ successAlert.style.display = "none";
+ }
+
+ function updatePlaceholder(elemId, textToInsert) {
+ var field = document.getElementById(elemId);
+ if (textToInsert != "") { field.placeholder = "Curr; " + textToInsert }
+ }
+
+ </script>
+</body>
+
+</html>
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);
+}