#include #include #include #include #include #include #include "SPIFFS.h" #include #include #include #include // Internet Variables... WebServer webServer(80); IPAddress apIP(192, 168, 1, 1); const byte DNS_PORT = 53; DNSServer dnsServer; WiFiMulti wifiMulti; char ssid[32] = "Flat4"; char password[64] = "MyN4meIsLuk4"; // Websocket Variables... const char* websockets_connection_string = "wss://sayersdev.azurewebsites.net/ws?id=ESP32"; using namespace websockets; WebsocketsClient client; // Sensor Variables... Ticker readSensor; float readSensorPace = 10; //seconds bool sendSensor = false; BME280I2C bme; bool pauseData = false; // NeoPixel Variables... #define PIXELPIN 21 #define NUMPIXELS 8 Adafruit_NeoPixel pixels(NUMPIXELS, PIXELPIN, NEO_GRB + NEO_KHZ800); // Button Variables... #define BUTTON A3 int buttonState; int lastButtonState = HIGH; // Button Bounce Fix... unsigned long bounceTime = 0; long bounceDelay = 50; // Setup Variables... unsigned long resetTimer = 0; int resetState = 0; // Anim Variables... unsigned long animTimer = millis(); int animPixel = 0; // Global Variables... int deviceMode = 0; // HTML Pages - will be replaced with files... String responseHTML = "" "" "" "WEZMONDO" "" "" "" "" "
\"WEZMONDO\"/
"; String setupHTML = "" "" "" "WEZMONDO" "" "" "" "" "
\"WEZMONDO\"/
"; String doneHTML = "" "" "" "WEZMONDO" "" "" "" "
\"WEZMONDO\"/
"; // Function to Connect to Wifi...... void onMessageCallback(WebsocketsMessage message) { Serial.print("Got Message: "); Serial.println(message.data()); if (message.data() == "stop") { pauseData = true; client.send("Data Stopped"); } if (message.data() == "start") { pauseData = false; client.send("Data Started"); } } void onEventsCallback(WebsocketsEvent event, String data) { if(event == WebsocketsEvent::ConnectionOpened) { Serial.println("Connnection Opened"); } else if(event == WebsocketsEvent::ConnectionClosed) { Serial.println("Connnection Closed"); } else if(event == WebsocketsEvent::GotPing) { Serial.println("Got a Ping!"); client.send("Hello Server"); client.pong(); } else if(event == WebsocketsEvent::GotPong) { Serial.println("Got a Pong!"); } } void setupWifi() { pixels.clear(); delay(50); pixels.show(); for (int i = 0; i < NUMPIXELS; i++) { pixels.setPixelColor(i, pixels.Color(100,0,0)); } delay(50); pixels.show(); Serial.println(String(ssid) + String(password)); wifiMulti.addAP(ssid, password); wifiMulti.run(); Serial.print("WIFI status = "); Serial.println(WiFi.getMode()); WiFi.disconnect(true); Serial.print("WIFI status = "); Serial.println(WiFi.getMode()); if((wifiMulti.run() == WL_CONNECTED)) { Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); webServer.serveStatic("/", SPIFFS, "/readings.html"); webServer.serveStatic("/setup", SPIFFS, "/wezmondo.html"); webServer.serveStatic("/readings", SPIFFS, "/readings.html"); webServer.serveStatic("/wezmondo.css", SPIFFS, "/wezmondo.css"); webServer.serveStatic("/jquery.min.js", SPIFFS, "/jquery.min.js"); webServer.serveStatic("/logo_w.svg", SPIFFS, "/logo_w.svg"); webServer.serveStatic("/icon_w.svg", SPIFFS, "/icon_w.svg"); webServer.onNotFound([]() { webServer.send(200, "text/html", responseHTML); }); webServer.on("/json",[]() { webServer.send(200, "text/json", getBME280Data()); }); webServer.begin(); Serial.println("Web Server On"); // run callback when messages are received client.onMessage(onMessageCallback); // run callback when events are occuring client.onEvent(onEventsCallback); // Connect to server client.connect(websockets_connection_string); // Send a message client.send("Hello Server"); // Send a ping client.ping(); pauseData = false; Wire.begin(23,22); while(!bme.begin()) { Serial.println("Could not find BME280 sensor!"); delay(1000); } readSensor.attach(readSensorPace, sendData); } } // Function To Set-Up Captive Portal ............................................. void setupPortal() { WiFi.mode(WIFI_AP); WiFi.softAP("WEZMONDO", "password"); delay(2000); WiFi.softAPConfig(apIP, apIP, IPAddress(255, 255, 255, 0)); Serial.print("WIFI status = "); Serial.println(WiFi.getMode()); Serial.println("Captive Portal"); dnsServer.start(DNS_PORT, "*", apIP); webServer.serveStatic("/", SPIFFS, "/wezmondo.html"); webServer.serveStatic("/setup", SPIFFS, "/wezmondo.html"); webServer.serveStatic("/wezmondo.css", SPIFFS, "/wezmondo.css"); webServer.serveStatic("/jquery.min.js", SPIFFS, "/jquery.min.js"); webServer.serveStatic("/logo_w.svg", SPIFFS, "/logo_w.svg"); webServer.serveStatic("/icon_w.svg", SPIFFS, "/icon_w.svg");; webServer.on("/update", []() { for (int i = 0; i < 96; ++i) { EEPROM.write(i, 0); } String x = urlDecode(webServer.arg("ssid")); Serial.print("SSID: "); Serial.println(x); String y = urlDecode(webServer.arg("pswd")); Serial.print("Password: "); Serial.println(y); Serial.println("Writing SSID to EEPROM..."); for (int i = 0; i < x.length(); ++i) { EEPROM.write(i, x[i]); } Serial.println("Writing Password to EEPROM..."); for (int i = 0; i < y.length(); ++i) { EEPROM.write(32 + i, y[i]); } EEPROM.commit(); Serial.println("Write EEPROM done!"); webServer.send(200, "text/html", doneHTML); ESP.restart(); }); webServer.onNotFound([]() { webServer.send(200, "text/html", setupHTML); }); // webServer.on("/settings",[]() { // webServer.send(200, "text/html", setupHTML); // }); webServer.begin(); } // Function to toggle device modes............................................ void changeMode() { Serial.println("Turning Off Wifi"); WiFi.mode(WIFI_OFF); Serial.print("WIFI status = "); Serial.println(WiFi.getMode()); if (deviceMode != 1) { pixels.clear(); for (int i = 0; i < NUMPIXELS; i++) { pixels.setPixelColor(i, pixels.Color(0,100,0)); } delay(50); pixels.show(); deviceMode = 1; Serial.println("Mode 1"); } else { // Sets up Wifi Connection... setupWifi(); pixels.clear(); delay(50); pixels.show(); for (int i = 0; i < NUMPIXELS; i++) { pixels.setPixelColor(i, pixels.Color(0,0,100)); } delay(50); pixels.show(); deviceMode = 2; Serial.println("Mode 2"); } } // Function to put device in set-up mode............................................ void setupMode() { if (deviceMode != 3) // enter set-up mode... { pixels.clear(); for (int i = 0; i < NUMPIXELS; i++) { pixels.setPixelColor(i, pixels.Color(100,0,0)); } delay(50); pixels.show(); deviceMode = 3; Serial.println("Set-Up Mode"); // set up captive portal... setupPortal(); } else // exit set-up mode... { pixels.clear(); delay(50); pixels.show(); for (int i = 0; i < NUMPIXELS; i++) { pixels.setPixelColor(i, pixels.Color(0,100,0)); } delay(50); pixels.show(); deviceMode = 0; } } // Function to Handle Pixel Patterns for Animations.................................. int pixelNo(int x) { if (x < 0) { x = (NUMPIXELS) + x; } if (x >= (NUMPIXELS)) { x = (NUMPIXELS) - x; } return x; } // Animation Function................................................................. void anim(){ if (deviceMode == 3) { if (millis() - animTimer > 200) { animTimer += 200; pixels.clear(); // Needed Due to ESP32 Timing Issues delay(1); // Needed Due to ESP32 Timing Issues pixels.show(); for (int i = 0; i < NUMPIXELS; i++) { pixels.setPixelColor(i, pixels.Color(2,0,0)); } pixels.setPixelColor(pixelNo(animPixel), pixels.Color(100,0,0)); pixels.setPixelColor(pixelNo(animPixel - 1), pixels.Color(20,0,0)); delay(1); // Needed Due to ESP32 Timing Issues pixels.show(); animPixel += 1; if (animPixel >= NUMPIXELS) { animPixel = 0; } } } } void sendData() { sendSensor = true; } String getBME280Data() { float temp(NAN), hum(NAN), pres(NAN); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); BME280::PresUnit presUnit(BME280::PresUnit_Pa); bme.read(pres, temp, hum, tempUnit, presUnit); return "{ \"pres\":" + String(pres) + ", \"temp\":" + String(temp) + ", \"hum\":" + String(hum) + " } "; } void printBME280Data() { float temp(NAN), hum(NAN), pres(NAN); BME280::TempUnit tempUnit(BME280::TempUnit_Celsius); BME280::PresUnit presUnit(BME280::PresUnit_Pa); bme.read(pres, temp, hum, tempUnit, presUnit); client.send(String(temp)+","+String(hum)+","+String(pres)); } boolean readConfig() { String x = String(ssid); String y = String(password); Serial.println("CLEAR EEPROM..."); // for (int i = 0; i < 96; ++i) { // EEPROM.write(i, 0); // } // // EEPROM.commit(); //Serial.println("Writing " + x + " to EEPROM..."); // // for (int i = 0; i < x.length(); ++i) { // EEPROM.write(i, x[i]); // } // //Serial.println("Writing " + y + " to EEPROM..."); // // for (int i = 0; i < y.length(); ++i) { // EEPROM.write(32 + i, y[i]); // } Serial.println("Reading EEPROM..."); if (EEPROM.read(0) != 0) { x = ""; y = ""; for (int i = 0; i < 32; ++i) { x += char(EEPROM.read(i)); } Serial.print("SSID: "); Serial.println(x); for (int i = 32; i < 96; ++i) { y += char(EEPROM.read(i)); } Serial.print("Password: "); Serial.println(y); x.toCharArray(ssid, 32); y.toCharArray(password, 64); return true; } else { Serial.println("Config not found."); } return false; } String urlDecode(String input) { String s = input; s.replace("%20", " "); s.replace("+", " "); s.replace("%21", "!"); s.replace("%22", "\""); s.replace("%23", "#"); s.replace("%24", "$"); s.replace("%25", "%"); s.replace("%26", "&"); s.replace("%27", "\'"); s.replace("%28", "("); s.replace("%29", ")"); s.replace("%30", "*"); s.replace("%31", "+"); s.replace("%2C", ","); s.replace("%2E", "."); s.replace("%2F", "/"); s.replace("%2C", ","); s.replace("%3A", ":"); s.replace("%3A", ";"); s.replace("%3C", "<"); s.replace("%3D", "="); s.replace("%3E", ">"); s.replace("%3F", "?"); s.replace("%40", "@"); s.replace("%5B", "["); s.replace("%5C", "\\"); s.replace("%5D", "]"); s.replace("%5E", "^"); s.replace("%5F", "-"); s.replace("%60", "`"); return s; } //--------------------------------------------------------------------------------------------- void setup() { // Start Serial Interface... Serial.begin(115200); while(!Serial) Serial.println("Serial Started..."); // Clear Pixels... pixels.begin(); pixels.clear(); pixels.show(); pixels.show(); // Needed Due to ESP32 Timing Issues Serial.println("Pixels Cleared..."); delay(2000); // Set Pixels to Red... for (int i = 0; i < NUMPIXELS; i++) { pixels.setPixelColor(i, pixels.Color(100,0,0)); } pixels.show(); Serial.println("Pixels Red..."); // Set Pin Mode for BUTTON... pinMode(BUTTON, INPUT); // Set up stuff here... delay(4000); // Used to simulate a delay... EEPROM.begin(512); readConfig(); if(!SPIFFS.begin(true)){ Serial.println("Error Starting SPIFFS"); return; } else { Serial.println("SPIFFS Started..."); } changeMode(); } //--------------------------------------------------------------------------------------------- void loop() { int reading = digitalRead(BUTTON); if (reading != lastButtonState) // If button state has changed previously... { bounceTime = millis(); } if ((millis() - bounceTime) > bounceDelay) // If it's been [bounceDelay] since last button state change... { if (reading != buttonState) // If button state has changed again... { buttonState = reading; if (buttonState == LOW && deviceMode != 3) { Serial.println("Button Up"); changeMode(); } } } if (reading == HIGH) { if (resetState == 0) { resetTimer = millis(); } resetState = 1; if ((millis() - resetTimer) > 5000) // If the button has been pressed for 5 seconds... { resetState = 0; resetTimer = 0; setupMode(); } } else { resetState = 0; resetTimer = 0; } lastButtonState = reading; // If Captive Portal use DNS Server and Web Server... if (deviceMode == 3) { dnsServer.processNextRequest(); webServer.handleClient(); } // If Mode2 use Web Server... if (deviceMode == 2) { client.poll(); if (sendSensor && pauseData == false) { printBME280Data(); int voltagePin = (analogRead(35) * 2); float voltage = ((float(voltagePin) / 4095) * 3.2); client.send("voltage," + String(voltage)); sendSensor = false; } webServer.handleClient(); } anim(); } //---------------------------------------------------------------------------------------------