├── ESP8266WebSocketsDemo ├── data │ ├── index.html │ ├── style.css │ └── main.js └── ESP8266WebSocketsDemo.ino ├── LICENSE └── README.md /ESP8266WebSocketsDemo/data/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ESP8266 Websockets Demonstration 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ESP8266WebSocketsDemo/data/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #EEE; 3 | text-align: center; 4 | padding: 30px; 5 | } 6 | 7 | /* Define dial size */ 8 | #dial { 9 | width: 300px; 10 | height: 300px; 11 | } 12 | 13 | /* Style the slider */ 14 | .slider { 15 | margin-top: 60px; 16 | width: 100%; 17 | -webkit-appearance: none; 18 | } 19 | .slider:focus { 20 | outline: none; 21 | } 22 | .slider::-webkit-slider-runnable-track { 23 | width: 300px; 24 | height: 5px; 25 | background: #ddd; 26 | border: none; 27 | border-radius: 3px; 28 | } 29 | .slider::-webkit-slider-thumb { 30 | -webkit-appearance: none; 31 | border: none; 32 | height: 30px; 33 | width: 30px; 34 | border-radius: 50%; 35 | background: #ab194f; 36 | margin-top: -13px; 37 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Ben Hegarty 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # About 2 | This project uses an ESP8266 to host a super quick, WebSockets based, user interface. 3 | 4 | [Youtube Video Demo](https://www.youtube.com/watch?v=jFhkxL-Fyv8) 5 | 6 | ## Requirements 7 | - An ESP8266 8 | - The latest version of the Arduino IDE with: 9 | - [The ESP8266 Core Installed](https://github.com/esp8266/Arduino) 10 | - [The ESP8266FS plugin Installed](https://github.com/esp8266/arduino-esp8266fs-plugin) (used to upload the HTML, JS and CSS files to the ESP) 11 | - The "WebSockets" library installed (available from the library manager. From the top menu of the IDE, choose Sketch -> Include Library -> Manage Libraries). 12 | 13 | ## Setup 14 | - Attach a potentiometer to the `A0` pin. 15 | - Attach a servo to the `D2` pin. 16 | - Connect the ESP8266 to your computer 17 | - Open the `ESP8266WebSocketsDemo` sketch and update the network settings to match your network's details (right after the #includes). 18 | - Upload the sketch. 19 | - From the top menu in the IDE, choose Tools -> ESP8266 Sketch Upload to upload the web files from the `data` directory. 20 | - Reboot your ESP8266 and open the serial monitor to see its IP address. 21 | - Navigate to the IP address on your phone and enjoy! 22 | -------------------------------------------------------------------------------- /ESP8266WebSocketsDemo/data/main.js: -------------------------------------------------------------------------------- 1 | // Connect to the socket. Same URL, port 81. 2 | var Socket = new WebSocket('ws://' + window.location.hostname + ':81'); 3 | 4 | // When a new websockets message is recieved, redraw the dial with the updated value 5 | Socket.onmessage = function (evt) { 6 | var thePercentage = parseInt(evt.data); 7 | drawDial(thePercentage, '#ab194f'); 8 | }; 9 | 10 | // Send degrees when the slider is slid 11 | // NOTE: Logic below also prevents sending more than 1 message every 10ms. The ESP hates if you blast it with updates. 12 | var lastSend = 0; 13 | function sendDegrees(degrees) { 14 | var now = (new Date).getTime(); 15 | if (lastSend > now - 20) return; 16 | lastSend = now; 17 | Socket.send(degrees); 18 | } 19 | 20 | // Draw the dial initially at 0 21 | drawDial(0, '#ab194f'); 22 | 23 | // This function draws the dial based on a given percventage and color 24 | function drawDial(percentage, color) { 25 | 26 | // First, we get a reference to the div in the HTML which we will draw the dial in 27 | var dialCanv = document.getElementById('dial'); 28 | var ctx = dialCanv.getContext("2d"); 29 | dialCanv.height = dialCanv.offsetHeight * 2; 30 | dialCanv.width = dialCanv.offsetWidth * 2; 31 | 32 | // Calculate the center of the div 33 | var centerX = dialCanv.width / 2; 34 | var centerY = dialCanv.height / 2; 35 | 36 | // Draw the colored arc showing the value 37 | ctx.beginPath(); 38 | ctx.fillStyle = color; 39 | ctx.moveTo(centerX, centerY.height / 2); 40 | ctx.arc(centerX, centerY, centerY*0.8, Math.PI * 1.5, (Math.PI * 2 * (percentage / 100)) + (Math.PI * 1.5), false); 41 | ctx.lineTo(centerX, centerY); 42 | ctx.fill(); 43 | ctx.closePath(); 44 | 45 | // Draw the white background circle 46 | ctx.beginPath(); 47 | ctx.fillStyle = "white"; 48 | ctx.arc(centerX, centerY, centerY*0.65, 0, Math.PI * 2, false); 49 | ctx.fill(); 50 | ctx.closePath(); 51 | 52 | // Add label 53 | ctx.font = "bold 90px sans-serif"; 54 | ctx.fillStyle = color; 55 | ctx.textBaseline = "center"; 56 | ctx.textAlign = "center"; 57 | ctx.fillText(parseInt(percentage), centerX, centerY * 1.1); 58 | } -------------------------------------------------------------------------------- /ESP8266WebSocketsDemo/ESP8266WebSocketsDemo.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | // Update to contain your network information :) 10 | const char *ssid = "Your Network Name"; 11 | const char *password = "Your Network Password"; 12 | 13 | // A few variables which we'll need throughout the program 14 | int16_t thisRead = 0; // The current pot value 15 | int16_t lastRead = 0; // The last pot value (this is used to prevent sending duplicate values) 16 | uint8_t counter = 0; // Used to limit how often we send pot updates via websockets 17 | 18 | // Initialise websockets, web server and servo 19 | WebSocketsServer webSocket = WebSocketsServer(81); 20 | ESP8266WebServer server(80); 21 | Servo myservo; 22 | 23 | void setup(void){ 24 | // Wait a second before we jump into starting everything 25 | delay(1000); 26 | 27 | Serial.begin(115200); 28 | Serial.println(); 29 | Serial.print("Configuring wifi..."); 30 | 31 | // If you'd like to use the ESP as a wifi access point instead of a client, 32 | // comment out everything between HERE... 33 | 34 | WiFi.begin(ssid, password); 35 | while (WiFi.status() != WL_CONNECTED) { 36 | delay(500); 37 | Serial.print("."); 38 | } 39 | IPAddress myIP = WiFi.localIP(); 40 | 41 | // AND HERE... Then uncomment these 2 lines: 42 | // WiFi.softAP(ssid); 43 | // IPAddress myIP = WiFi.softAPIP(); 44 | 45 | // Print out the IP address 46 | Serial.print("IP address: "); 47 | Serial.println(myIP); 48 | 49 | // Configure our servo 50 | myservo.attach(D2); 51 | 52 | // Begin access to our file system (which stores the HTML) 53 | SPIFFS.begin(); 54 | 55 | // Start the websockets and link the events function (defined below, under the loop) 56 | webSocket.begin(); 57 | webSocket.onEvent(webSocketEvent); 58 | 59 | // Configure web server to host HTML files 60 | server.onNotFound([](){ 61 | if(!handleFileRead(server.uri())) 62 | server.send(404, "text/plain", "FileNotFound"); 63 | }); 64 | server.begin(); 65 | Serial.println("HTTP server started"); 66 | } 67 | 68 | void loop(void) { 69 | // Process any incoming HTTP or WebSockets requests 70 | webSocket.loop(); 71 | server.handleClient(); 72 | 73 | // Every 256 times we run the loop, read the pot and send out the value (if it's changed) 74 | counter++; 75 | if (counter == 255) { 76 | counter = 0; // Reset the counter 77 | 78 | // Read in the pot value and map it so that it is between 0 and 100 79 | thisRead = map(analogRead(A0), 900, 20, 0, 100); 80 | 81 | // If it is out of range, nudge it back into range 82 | if (thisRead > 100) thisRead = 100; 83 | if (thisRead < 0) thisRead = 0; 84 | 85 | // If what we read last time ISN'T what we read last time, send off the new value via WebSockets! 86 | if (thisRead != lastRead) { 87 | String message = String(thisRead); 88 | webSocket.broadcastTXT(message); 89 | } 90 | 91 | // Update the last read variable 92 | lastRead = thisRead; 93 | } 94 | 95 | } 96 | 97 | // A function to handle our incoming sockets messages 98 | void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { 99 | 100 | switch(type) { 101 | 102 | // Runs when a user disconnects 103 | case WStype_DISCONNECTED: { 104 | Serial.printf("User #%u - Disconnected!\n", num); 105 | break; 106 | } 107 | 108 | // Runs when a user connects 109 | case WStype_CONNECTED: { 110 | IPAddress ip = webSocket.remoteIP(num); 111 | Serial.printf("--- Connection. IP: %d.%d.%d.%d Namespace: %s UserID: %u\n", ip[0], ip[1], ip[2], ip[3], payload, num); 112 | 113 | // Send last pot value on connect 114 | String message = String(lastRead); 115 | webSocket.broadcastTXT(message); 116 | break; 117 | } 118 | 119 | // Runs when a user sends us a message 120 | case WStype_TEXT: { 121 | String incoming = ""; 122 | for (int i = 0; i < lenght; i++) { 123 | incoming.concat((char)payload[i]); 124 | } 125 | uint8_t deg = incoming.toInt(); 126 | myservo.write(deg); 127 | break; 128 | } 129 | 130 | } 131 | 132 | } 133 | 134 | // A function we use to get the content type for our HTTP responses 135 | String getContentType(String filename){ 136 | if(server.hasArg("download")) return "application/octet-stream"; 137 | else if(filename.endsWith(".htm")) return "text/html"; 138 | else if(filename.endsWith(".html")) return "text/html"; 139 | else if(filename.endsWith(".css")) return "text/css"; 140 | else if(filename.endsWith(".js")) return "application/javascript"; 141 | else if(filename.endsWith(".png")) return "image/png"; 142 | else if(filename.endsWith(".gif")) return "image/gif"; 143 | else if(filename.endsWith(".jpg")) return "image/jpeg"; 144 | else if(filename.endsWith(".ico")) return "image/x-icon"; 145 | else if(filename.endsWith(".xml")) return "text/xml"; 146 | else if(filename.endsWith(".pdf")) return "application/x-pdf"; 147 | else if(filename.endsWith(".zip")) return "application/x-zip"; 148 | else if(filename.endsWith(".gz")) return "application/x-gzip"; 149 | return "text/plain"; 150 | } 151 | 152 | // Takes a URL (for example /index.html) and looks up the file in our file system, 153 | // Then sends it off via the HTTP server! 154 | bool handleFileRead(String path){ 155 | #ifdef DEBUG 156 | Serial.println("handleFileRead: " + path); 157 | #endif 158 | if(path.endsWith("/")) path += "index.html"; 159 | if(SPIFFS.exists(path)){ 160 | File file = SPIFFS.open(path, "r"); 161 | size_t sent = server.streamFile(file, getContentType(path)); 162 | file.close(); 163 | return true; 164 | } 165 | return false; 166 | } 167 | --------------------------------------------------------------------------------