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 |
--------------------------------------------------------------------------------