Created
May 15, 2017 18:57
-
-
Save larsenglund/b2714e6d9962ade2ed2a2b440a6e353d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const char* websocketPageHead = R"( | |
<!DOCTYPE html><html><head><title>RakuTemp</title> | |
<script src='Chart.bundle.min.js'></script> | |
<style> | |
html * { | |
font-family: Arial; | |
} | |
</style> | |
<script> | |
var waitingForData = false; | |
var numData = 0; | |
var dataIdx = 0; | |
var numChunks = 0; | |
var chunkIdx = 0; | |
var numDataAdded = 0; | |
var MAX_SAMPLES = 2048; | |
var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']); | |
connection.binaryType = 'arraybuffer'; | |
var s1 = new Uint16Array(MAX_SAMPLES); | |
connection.onopen = function () { | |
connection.send('Connect ' + new Date()); | |
}; | |
connection.onerror = function (error) { | |
console.log('WebSocket Error ', error); | |
}; | |
connection.onmessage = function (e) { | |
console.log('onmsg type: ', typeof(e.data)); | |
console.log('onmsg data: ', e.data); | |
if (typeof(e.data) == 'object') { | |
var data = e.data; | |
var dv = new DataView(data); | |
/*var idx = dataIdx - numData; | |
if (idx < 0) idx += MAX_SAMPLES; | |
for (var n=0; n<numData; n++) { | |
var tmp = dv.getUint16(idx*2, true); | |
console.log("idx " + idx + ": " + tmp); | |
if (++idx >= MAX_SAMPLES) idx = 0; | |
}*/ | |
for (var n=0; n<MAX_SAMPLES/2; n++) { | |
s1[chunkIdx++] = dv.getUint16(n*2, true); | |
} | |
if (chunkIdx == MAX_SAMPLES) { | |
console.log('All data received'); | |
var idx = dataIdx - numData; | |
if (idx < 0) idx += MAX_SAMPLES; | |
for (var n=0; n<numData; n++) { | |
var tmp = s1[idx]; | |
console.log("idx " + idx + ": " + tmp); | |
if (++idx >= MAX_SAMPLES) idx = 0; | |
} | |
waitingForData = false; | |
} | |
} | |
if (typeof(e.data) == 'string') { | |
var tmp = e.data.split(' '); | |
if (tmp[0] == "D") { | |
waitingForData = true; | |
cfg.data.labels = []; | |
cfg.data.datasets[0].data = []; | |
dataIdx = parseInt(tmp[1]); | |
numData = parseInt(tmp[2]); | |
} | |
/*else if (tmp.length == 1) { | |
var _temp = parseInt(tmp[0]); | |
cfg.data.labels.push(numDataAdded); | |
cfg.data.datasets[0].data.push(_temp); | |
numDataAdded++; | |
if (numDataAdded == numData) { | |
console.log('All data received!'); | |
myChart.update(); | |
waitingForData = false; | |
} | |
}*/ | |
else if (tmp.length == 2) { | |
//console.log('Server: ', e.data); | |
var _temp = parseInt(tmp[0]); | |
var _millis = parseInt(tmp[1]); | |
if (waitingForData) { | |
} | |
else { | |
console.log(_temp); | |
document.getElementById('a').innerHTML = _temp; | |
//document.getElementById('b').value = _millis; | |
if(cfg.data.datasets[0].data.length == MAX_SAMPLES){ | |
//myChart.removeData(); | |
cfg.data.datasets[0].data.shift(); | |
cfg.data.labels.shift(); | |
} | |
//myChart.addData([temp], 'RakuTemp'); | |
//cfg.data.labels.push(_millis); | |
cfg.data.labels.push(numDataAdded); | |
cfg.data.datasets[0].data.push(_temp); | |
numDataAdded++; | |
myChart.update(); | |
} | |
} | |
} | |
}; | |
</script>)"; | |
const char* websocketPageBody = R"( | |
<center style='font-size: 200%;'> | |
RakuTemp <b><span id='a'></span></b><br/> | |
<canvas id='myChart' width='400' height='400'></canvas> | |
</center><br/> | |
<script> | |
var ctx = document.getElementById('myChart'); | |
var cfg = { | |
type: 'line', | |
data: { | |
labels: [], | |
datasets: [{ | |
radius: 0, | |
fillColor : 'rgba(252,233,79,0.5)', | |
strokeColor : 'rgba(82,75,25,1)', | |
label: 'RakuTemp', | |
data: [] | |
}] | |
}, | |
options: { | |
legend: { | |
display: false | |
}, | |
scales: | |
{ | |
xAxes: [{ | |
display: false | |
}] | |
} | |
} | |
}; | |
var myChart = new Chart(ctx, cfg); | |
</script>)"; | |
/** Handle root or redirect to captive portal */ | |
void handleRoot() { | |
if (captivePortal()) { // If caprive portal redirect instead of displaying the page. | |
return; | |
} | |
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); | |
server.sendHeader("Pragma", "no-cache"); | |
server.sendHeader("Expires", "-1"); | |
server.setContentLength(CONTENT_LENGTH_UNKNOWN); | |
server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. | |
server.sendContent( | |
"<html><head>" | |
); | |
server.sendContent(websocketPageHead); | |
server.sendContent( | |
"</head><body>" | |
); | |
server.sendContent(websocketPageBody); | |
if (server.client().localIP() == apIP) { | |
server.sendContent(String("<p>You are connected through the soft AP: ") + softAP_ssid + "</p>"); | |
} else { | |
server.sendContent(String("<p>You are connected through the wifi network: ") + ssid + "</p>"); | |
} | |
server.sendContent( | |
"<p>You may want to <a href='/wifi'>config the wifi connection</a> or <a href='/update'>update the firmware</a></p>" | |
"</body></html>" | |
); | |
server.client().stop(); // Stop is needed because we sent no content length | |
} | |
/** Redirect to captive portal if we got a request for another domain. Return true in that case so the page handler do not try to handle the request again. */ | |
boolean captivePortal() { | |
if (!isIp(server.hostHeader()) && server.hostHeader() != (String(myHostname)+".local")) { | |
Serial.print("Request redirected to captive portal"); | |
server.sendHeader("Location", String("http://") + toStringIp(server.client().localIP()), true); | |
server.send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. | |
server.client().stop(); // Stop is needed because we sent no content length | |
return true; | |
} | |
return false; | |
} | |
/** Wifi config page handler */ | |
void handleWifi() { | |
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); | |
server.sendHeader("Pragma", "no-cache"); | |
server.sendHeader("Expires", "-1"); | |
server.setContentLength(CONTENT_LENGTH_UNKNOWN); | |
server.send(200, "text/html", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. | |
server.sendContent( | |
"<html><head></head><body>" | |
"<h1>Wifi config</h1>" | |
); | |
if (server.client().localIP() == apIP) { | |
server.sendContent(String("<p>You are connected through the soft AP: ") + softAP_ssid + "</p>"); | |
} else { | |
server.sendContent(String("<p>You are connected through the wifi network: ") + ssid + "</p>"); | |
} | |
server.sendContent( | |
"\r\n<br />" | |
"<table><tr><th align='left'>SoftAP config</th></tr>" | |
); | |
server.sendContent(String() + "<tr><td>SSID " + String(softAP_ssid) + "</td></tr>"); | |
server.sendContent(String() + "<tr><td>IP " + toStringIp(WiFi.softAPIP()) + "</td></tr>"); | |
server.sendContent( | |
"</table>" | |
"\r\n<br />" | |
"<table><tr><th align='left'>WLAN config</th></tr>" | |
); | |
server.sendContent(String() + "<tr><td>SSID " + String(ssid) + "</td></tr>"); | |
server.sendContent(String() + "<tr><td>IP " + toStringIp(WiFi.localIP()) + "</td></tr>"); | |
server.sendContent( | |
"</table>" | |
"\r\n<br />" | |
"<table><tr><th align='left'>WLAN list (refresh if any missing)</th></tr>" | |
); | |
Serial.println("scan start"); | |
int n = WiFi.scanNetworks(); | |
Serial.println("scan done"); | |
if (n > 0) { | |
for (int i = 0; i < n; i++) { | |
server.sendContent(String() + "\r\n<tr><td>SSID " + WiFi.SSID(i) + String((WiFi.encryptionType(i) == ENC_TYPE_NONE)?" ":" *") + " (" + WiFi.RSSI(i) + ")</td></tr>"); | |
} | |
} else { | |
server.sendContent(String() + "<tr><td>No WLAN found</td></tr>"); | |
} | |
server.sendContent( | |
"</table>" | |
"\r\n<br /><form method='POST' action='wifisave'><h4>Connect to network:</h4>" | |
"<input type='text' placeholder='network' name='n'/>" | |
"<br /><input type='password' placeholder='password' name='p'/>" | |
"<br /><input type='submit' value='Connect/Disconnect'/></form>" | |
"<p>You may want to <a href='/'>return to the home page</a>.</p>" | |
"</body></html>" | |
); | |
server.client().stop(); // Stop is needed because we sent no content length | |
} | |
/** Handle the WLAN save form and redirect to WLAN config page again */ | |
void handleWifiSave() { | |
Serial.println("wifi save"); | |
server.arg("n").toCharArray(ssid, sizeof(ssid) - 1); | |
server.arg("p").toCharArray(password, sizeof(password) - 1); | |
server.sendHeader("Location", "wifi", true); | |
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); | |
server.sendHeader("Pragma", "no-cache"); | |
server.sendHeader("Expires", "-1"); | |
server.send ( 302, "text/plain", ""); // Empty content inhibits Content-length header so we have to close the socket ourselves. | |
server.client().stop(); // Stop is needed because we sent no content length | |
saveCredentials(); | |
connect = strlen(ssid) > 0; // Request WLAN connect with new credentials if there is a SSID | |
} | |
void handleNotFound() { | |
if (captivePortal()) { // If caprive portal redirect instead of displaying the error page. | |
return; | |
} | |
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.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); | |
server.sendHeader("Pragma", "no-cache"); | |
server.sendHeader("Expires", "-1"); | |
server.send ( 404, "text/plain", message ); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <ESP8266WiFi.h> | |
#include <WiFiClient.h> | |
#include <ESP8266WebServer.h> | |
#include <DNSServer.h> | |
#include <ESP8266mDNS.h> | |
#include <EEPROM.h> | |
#include <FS.h> | |
#include <ESP8266HTTPUpdateServer.h> | |
#include <WebSocketsServer.h> // library: https://github.com/Links2004/arduinoWebSockets | |
#include <SPI.h> | |
#include "Adafruit_MAX31855.h" | |
// Example creating a thermocouple instance with software SPI on any three | |
// digital IO pins. | |
#define MAXDO 14 | |
#define MAXCS 12 | |
#define MAXCLK 13 | |
// initialize the Thermocouple | |
Adafruit_MAX31855 thermocouple(MAXCLK, MAXCS, MAXDO); | |
int sample_time_1 = 2000; | |
int sample_time_2 = 60000; | |
unsigned int sampleTimestamp1; | |
unsigned int sampleTimestamp2; | |
int sample_idx_1 = 0; | |
int num_samples_1 = 0; | |
int sample_idx_2 = 0; | |
int num_samples_2 = 0; | |
#define MAX_SAMPLES 2048 | |
uint16_t samples2s[MAX_SAMPLES]; | |
uint16_t samples60s[MAX_SAMPLES]; | |
/* | |
* This example serves a "hello world" on a WLAN and a SoftAP at the same time. | |
* The SoftAP allow you to configure WLAN parameters at run time. They are not setup in the sketch but saved on EEPROM. | |
* | |
* Connect your computer or cell phone to wifi network ESP_ap with password 12345678. A popup may appear and it allow you to go to WLAN config. If it does not then navigate to http://192.168.4.1/wifi and config it there. | |
* Then wait for the module to connect to your wifi and take note of the WLAN IP it got. Then you can disconnect from ESP_ap and return to your regular WLAN. | |
* | |
* Now the ESP8266 is in your network. You can reach it through http://192.168.x.x/ (the IP you took note of) or maybe at http://esp8266.local too. | |
* | |
* This is a captive portal because through the softAP it will redirect any http request to http://192.168.4.1/ | |
*/ | |
/* Set these to your desired softAP credentials. They are not configurable at runtime */ | |
const char *softAP_ssid = "RakuTemp"; | |
//const char *softAP_password = "12345678"; | |
/* hostname for mDNS. Should work at least on windows. Try http://esp8266.local */ | |
const char *myHostname = "raku"; | |
/* Don't set this wifi credentials. They are configurated at runtime and stored on EEPROM */ | |
char ssid[32] = ""; | |
char password[32] = ""; | |
// DNS server | |
const byte DNS_PORT = 53; | |
DNSServer dnsServer; | |
// Web server | |
ESP8266WebServer server(80); | |
ESP8266HTTPUpdateServer httpUpdater; | |
WebSocketsServer webSocket = WebSocketsServer(81); | |
/* Soft AP network parameters */ | |
IPAddress apIP(192, 168, 4, 1); | |
IPAddress netMsk(255, 255, 255, 0); | |
/** Should I connect to WLAN asap? */ | |
boolean connect; | |
/** Last time I tried to connect to WLAN */ | |
long lastConnectTry = 0; | |
/** Current WLAN status */ | |
int status = WL_IDLE_STATUS; | |
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { | |
switch (type) { | |
case WStype_DISCONNECTED: | |
Serial.printf("[%u] Disconnected!\n", num); | |
break; | |
case WStype_CONNECTED: { | |
IPAddress ip = webSocket.remoteIP(num); | |
Serial.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); | |
webSocket.sendTXT(num, String("D") + " " + sample_idx_1 + " " + num_samples_1 + " " + MAX_SAMPLES); | |
/*int idx = sample_idx_1 - num_samples_1; | |
if (idx < 0) idx = MAX_SAMPLES + idx; | |
for (int n=0; n<num_samples_1; n++) { | |
webSocket.sendTXT(num, String("") + samples2s[idx]); | |
idx++; | |
if (idx >= MAX_SAMPLES) { | |
idx = 0; | |
} | |
}*/ | |
Serial.println("Sending binary"); | |
webSocket.sendBIN(num, (uint8_t *) &samples2s[0], MAX_SAMPLES*2/2); | |
webSocket.sendBIN(num, (uint8_t *) &samples2s[MAX_SAMPLES/2], MAX_SAMPLES*2/2); | |
Serial.println("Binary sent"); | |
} | |
break; | |
case WStype_TEXT: | |
Serial.printf("[%u] get Text: %s\n", num, payload); | |
/*if (payload[0] == '#') { | |
// decode dimmer data | |
uint32_t ab = (uint32_t)strtol((const char *)&payload[1], NULL, 16); | |
setBar(barD1, ((ab >> 8) & 0xFF)); | |
setBar(barD2, ((ab >> 0) & 0xFF)); | |
}*/ | |
break; | |
} | |
} | |
void setup() { | |
delay(1000); | |
Serial.begin(9600); | |
Serial.println(); | |
Serial.print("Configuring access point..."); | |
/* You can remove the password parameter if you want the AP to be open. */ | |
WiFi.softAPConfig(apIP, apIP, netMsk); | |
//WiFi.softAP(softAP_ssid, softAP_password); | |
WiFi.softAP(softAP_ssid); | |
delay(500); // Without delay I've seen the IP address blank | |
Serial.print("AP IP address: "); | |
Serial.println(WiFi.softAPIP()); | |
/* Setup the DNS server redirecting all the domains to the apIP */ | |
dnsServer.setErrorReplyCode(DNSReplyCode::NoError); | |
dnsServer.start(DNS_PORT, "*", apIP); | |
if (!SPIFFS.begin()) { | |
Serial.println("SPIFFS mount failed"); | |
} | |
else { | |
Serial.println("SPIFFS mount succesfull"); | |
} | |
/* Setup web pages: root, wifi config pages, SO captive portal detectors and not found. */ | |
server.on("/", handleRoot); | |
server.on("/wifi", handleWifi); | |
server.on("/wifisave", handleWifiSave); | |
server.on("/generate_204", handleRoot); //Android captive portal. Maybe not needed. Might be handled by notFound handler. | |
server.on("/fwlink", handleRoot); //Microsoft captive portal. Maybe not needed. Might be handled by notFound handler. | |
server.onNotFound ( handleNotFound ); | |
server.serveStatic("/", SPIFFS, "/", "max-age=86400"); | |
httpUpdater.setup(&server); | |
server.begin(); // Web server start | |
webSocket.begin(); | |
webSocket.onEvent(webSocketEvent); | |
Serial.print("WEBSOCKETS_MAX_DATA_SIZE: "); | |
Serial.println(WEBSOCKETS_MAX_DATA_SIZE); | |
Serial.println("HTTP server started"); | |
loadCredentials(); // Load WLAN credentials from network | |
connect = strlen(ssid) > 0; // Request WLAN connect if there is a SSID | |
sampleTimestamp1 = millis() + sample_time_1; | |
pinMode(LED_BUILTIN, OUTPUT); | |
for (int n=0; n<5; n++) { | |
digitalWrite(LED_BUILTIN, LOW); | |
delay(100); | |
digitalWrite(LED_BUILTIN, HIGH); | |
delay(100); | |
} | |
} | |
void connectWifi() { | |
Serial.println("Connecting as wifi client..."); | |
WiFi.disconnect(); | |
WiFi.begin ( ssid, password ); | |
int connRes = WiFi.waitForConnectResult(); | |
Serial.print ( "connRes: " ); | |
Serial.println ( connRes ); | |
} | |
void loop() { | |
if (connect) { | |
Serial.println ( "Connect requested" ); | |
connect = false; | |
connectWifi(); | |
lastConnectTry = millis(); | |
} | |
{ | |
int s = WiFi.status(); | |
if (s == 0 && millis() > (lastConnectTry + 10000) ) { | |
/* If WLAN disconnected and idle try to connect */ | |
/* Don't set retry time too low as retry interfere the softAP operation */ | |
connect = true; | |
} | |
if (status != s) { // WLAN status change | |
Serial.print ( "Status: " ); | |
Serial.println ( s ); | |
status = s; | |
if (s == WL_CONNECTED) { | |
/* Just connected to WLAN */ | |
Serial.println ( "" ); | |
Serial.print ( "Connected to " ); | |
Serial.println ( ssid ); | |
Serial.print ( "IP address: " ); | |
Serial.println ( WiFi.localIP() ); | |
// Setup MDNS responder | |
if (!MDNS.begin(myHostname)) { | |
Serial.println("Error setting up MDNS responder!"); | |
} else { | |
Serial.println("mDNS responder started"); | |
// Add service to MDNS-SD | |
MDNS.addService("http", "tcp", 80); | |
} | |
} else if (s == WL_NO_SSID_AVAIL) { | |
WiFi.disconnect(); | |
} | |
} | |
} | |
// Do work: | |
if (millis() > sampleTimestamp1) { | |
// basic readout test, just print the current temp | |
Serial.print("Internal Temp = "); | |
Serial.println(thermocouple.readInternal()); | |
double c = thermocouple.readCelsius(); | |
unsigned int c_millis = millis(); | |
samples2s[sample_idx_1] = (uint16_t)c; | |
num_samples_1++; | |
if (num_samples_1 > MAX_SAMPLES) num_samples_1 = MAX_SAMPLES; | |
sample_idx_1++; | |
if (sample_idx_1 >= MAX_SAMPLES) sample_idx_1 = 0; | |
if (isnan(c)) { | |
Serial.println("Something wrong with thermocouple!"); | |
} else { | |
Serial.print("C = "); | |
Serial.println(c); | |
webSocket.broadcastTXT(String("") + c + " " + c_millis); | |
} | |
sampleTimestamp1 = millis() + sample_time_1; | |
} | |
//DNS | |
dnsServer.processNextRequest(); | |
//HTTP | |
server.handleClient(); | |
//Websockets | |
webSocket.loop(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment