@ -1 +1 @@ | |||||
0.9.7 | |||||
0.9.8 |
@ -1,214 +0,0 @@ | |||||
/* | |||||
JustWifi | |||||
Wifi Manager for ESP8266 | |||||
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com> | |||||
This program is free software: you can redistribute it and/or modify | |||||
it under the terms of the GNU General Public License as published by | |||||
the Free Software Foundation, either version 3 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU General Public License for more details. | |||||
You should have received a copy of the GNU General Public License | |||||
along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#include "JustWifi.h" | |||||
#include <functional> | |||||
bool JustWifi::connected() { | |||||
return (WiFi.status() == WL_CONNECTED); | |||||
} | |||||
bool JustWifi::autoConnect() { | |||||
unsigned long timeout; | |||||
// Return if already connected | |||||
if (connected()) return true; | |||||
// Try to connect to last successful network | |||||
if (WiFi.SSID().length() > 0) { | |||||
ETS_UART_INTR_DISABLE(); | |||||
wifi_station_disconnect(); | |||||
ETS_UART_INTR_ENABLE(); | |||||
_doCallback(MESSAGE_AUTO_CONNECTING, (char *) WiFi.SSID().c_str()); | |||||
WiFi.mode(WIFI_STA); | |||||
WiFi.begin(); | |||||
// Check connection with timeout | |||||
timeout = millis(); | |||||
while (millis() - timeout < _connect_timeout) { | |||||
_doCallback(MESSAGE_CONNECT_WAITING); | |||||
if (WiFi.status() == WL_CONNECTED) break; | |||||
delay(100); | |||||
} | |||||
if (WiFi.status() == WL_CONNECTED) { | |||||
_mode = MODE_STATION; | |||||
_doCallback(MESSAGE_CONNECTED); | |||||
return true; | |||||
} | |||||
_doCallback(MESSAGE_AUTO_FAILED); | |||||
} else { | |||||
_doCallback(MESSAGE_AUTO_NOSSID); | |||||
} | |||||
_mode = MODE_NONE; | |||||
return false; | |||||
} | |||||
bool JustWifi::connect() { | |||||
unsigned long timeout; | |||||
// Return if already connected | |||||
if (connected()) return true; | |||||
// Loop through configured networks | |||||
for (unsigned char i=0; i<_network_count; i++) { | |||||
// Skip if no SSID defined | |||||
if (_network[i].ssid.length() == 0) continue; | |||||
// TODO: Static IP options here | |||||
// Connect | |||||
_doCallback(MESSAGE_CONNECTING, (char *) _network[i].ssid.c_str()); | |||||
WiFi.begin(_network[i].ssid.c_str(), _network[i].pass.c_str()); | |||||
// Check connection with timeout | |||||
timeout = millis(); | |||||
while (millis() - timeout < _connect_timeout) { | |||||
_doCallback(MESSAGE_CONNECT_WAITING); | |||||
if (WiFi.status() == WL_CONNECTED) break; | |||||
delay(100); | |||||
} | |||||
// Get out of the i loop if connected | |||||
if (WiFi.status() == WL_CONNECTED) { | |||||
break; | |||||
} else { | |||||
_mode = MODE_NONE; | |||||
_doCallback(MESSAGE_CONNECT_FAILED, (char *) _network[i].ssid.c_str()); | |||||
} | |||||
} | |||||
if (WiFi.status() == WL_CONNECTED) { | |||||
//WiFi.setAutoConnect(true); | |||||
//WiFi.setAutoReconnect(true); | |||||
_mode = MODE_STATION; | |||||
_doCallback(MESSAGE_CONNECTED); | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
bool JustWifi::startAP(char * ssid, char * pass) { | |||||
// Return if already connected | |||||
// if (connected()) return true; | |||||
// TODO: Static IP options here | |||||
_doCallback(MESSAGE_ACCESSPOINT_CREATING); | |||||
WiFi.mode(WIFI_AP); | |||||
if (strlen(pass) > 0) { | |||||
if (strlen(pass) < 8 || strlen(pass) > 63) { | |||||
_mode = MODE_NONE; | |||||
_doCallback(MESSAGE_ACCESSPOINT_FAILED); | |||||
return false; | |||||
} | |||||
WiFi.softAP(ssid, pass); | |||||
} else { | |||||
WiFi.softAP(ssid); | |||||
} | |||||
// TODO: Setup the DNS server redirecting all the queries to this IP | |||||
_ssid = String(ssid); | |||||
_mode = MODE_ACCESS_POINT; | |||||
_doCallback(MESSAGE_ACCESSPOINT_CREATED); | |||||
return true; | |||||
} | |||||
bool JustWifi::disconnect() { | |||||
WiFi.disconnect(true); | |||||
_mode = MODE_NONE; | |||||
_doCallback(MESSAGE_DISCONNECTED); | |||||
} | |||||
bool JustWifi::cleanNetworks() { | |||||
_network_count = 0; | |||||
} | |||||
bool JustWifi::addNetwork(char * ssid, char * pass) { | |||||
if (_network_count == MAX_NETWORKS) return false; | |||||
_network[_network_count].ssid = String(ssid); | |||||
_network[_network_count].pass = String(pass); | |||||
_network_count++; | |||||
return true; | |||||
} | |||||
justwifi_mode_t JustWifi::getMode() { | |||||
return _mode; | |||||
} | |||||
String JustWifi::getIP() { | |||||
if (_mode == MODE_STATION) { | |||||
return WiFi.localIP().toString(); | |||||
} else if (_mode == MODE_ACCESS_POINT) { | |||||
return WiFi.softAPIP().toString(); | |||||
} | |||||
return String(""); | |||||
} | |||||
String JustWifi::getNetwork() { | |||||
if (_mode == MODE_STATION) { | |||||
return WiFi.SSID(); | |||||
} | |||||
return _ssid; | |||||
} | |||||
void JustWifi::setConnectTimeout(unsigned long ms) { | |||||
_connect_timeout = ms; | |||||
} | |||||
void JustWifi::onMessage(TMessageFunction fn) { | |||||
_callback = fn; | |||||
} | |||||
void JustWifi::loop() { | |||||
if ((WiFi.status() != WL_CONNECTED) && (_mode == MODE_STATION)) { | |||||
_mode = MODE_NONE; | |||||
_doCallback(MESSAGE_DISCONNECTED); | |||||
} | |||||
if ((WiFi.status() == WL_CONNECTED) && (_mode != MODE_STATION)) { | |||||
_mode = MODE_STATION; | |||||
_doCallback(MESSAGE_CONNECTED); | |||||
} | |||||
} | |||||
void JustWifi::_doCallback(justwifi_messages_t message, char * parameter) { | |||||
if (_callback != NULL) _callback(message, parameter); | |||||
} |
@ -1,99 +0,0 @@ | |||||
/* | |||||
JustWifi | |||||
Wifi Manager for ESP8266 | |||||
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com> | |||||
This program is free software: you can redistribute it and/or modify | |||||
it under the terms of the GNU General Public License as published by | |||||
the Free Software Foundation, either version 3 of the License, or | |||||
(at your option) any later version. | |||||
This program is distributed in the hope that it will be useful, | |||||
but WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||||
GNU General Public License for more details. | |||||
You should have received a copy of the GNU General Public License | |||||
along with this program. If not, see <http://www.gnu.org/licenses/>. | |||||
*/ | |||||
#ifndef JustWifi_h | |||||
#define JustWifi_h | |||||
#include <functional> | |||||
#include <ESP8266WiFi.h> | |||||
extern "C" { | |||||
#include "user_interface.h" | |||||
} | |||||
#define MAX_NETWORKS 3 | |||||
#define WIFI_CONNECT_TIMEOUT 10000 | |||||
struct network_t { | |||||
String ssid; | |||||
String pass; | |||||
}; | |||||
typedef enum { | |||||
MODE_NONE, | |||||
MODE_STATION, | |||||
MODE_ACCESS_POINT | |||||
} justwifi_mode_t; | |||||
typedef enum { | |||||
MESSAGE_AUTO_NOSSID, | |||||
MESSAGE_AUTO_CONNECTING, | |||||
MESSAGE_AUTO_FAILED, | |||||
MESSAGE_CONNECTING, | |||||
MESSAGE_CONNECT_WAITING, | |||||
MESSAGE_CONNECT_FAILED, | |||||
MESSAGE_CONNECTED, | |||||
MESSAGE_ACCESSPOINT_CREATING, | |||||
MESSAGE_ACCESSPOINT_FAILED, | |||||
MESSAGE_ACCESSPOINT_CREATED, | |||||
MESSAGE_DISCONNECTED | |||||
} justwifi_messages_t; | |||||
class JustWifi { | |||||
public: | |||||
typedef std::function<void(justwifi_messages_t, char *)> TMessageFunction; | |||||
bool autoConnect(); | |||||
bool connect(); | |||||
bool startAP(char * ssid, char * pass); | |||||
bool disconnect(); | |||||
bool connected(); | |||||
bool cleanNetworks(); | |||||
bool addNetwork(char * ssid, char * pass); | |||||
void setConnectTimeout(unsigned long ms); | |||||
justwifi_mode_t getMode(); | |||||
String getIP(); | |||||
String getNetwork(); | |||||
void onMessage(TMessageFunction fn); | |||||
void loop(); | |||||
private: | |||||
network_t _network[MAX_NETWORKS]; | |||||
String _ssid; | |||||
justwifi_mode_t _mode = MODE_NONE; | |||||
unsigned char _network_count = 0; | |||||
unsigned long _connect_timeout = WIFI_CONNECT_TIMEOUT; | |||||
TMessageFunction _callback = NULL; | |||||
void _doCallback(justwifi_messages_t message, char * parameter = NULL); | |||||
}; | |||||
#endif |
@ -0,0 +1 @@ | |||||
src |
@ -0,0 +1,28 @@ | |||||
#!/bin/python | |||||
import subprocess | |||||
import socket | |||||
from SCons.Script import DefaultEnvironment | |||||
env = DefaultEnvironment() | |||||
def is_valid_ip(ip): | |||||
try: | |||||
socket.inet_aton(ip) | |||||
return True | |||||
except socket.error: | |||||
return False | |||||
def before_build_spiffs(source, target, env): | |||||
env.Execute("gulp buildfs2") | |||||
def before_upload(source, target, env): | |||||
upload_port = env.get('UPLOAD_PORT', False) | |||||
if upload_port and upload_port[0] == '/': | |||||
cmd = ["mosquitto_sub", "-t", upload_port, "-h", "192.168.1.10", "-N", "-C", "1"] | |||||
ip = subprocess.check_output(cmd) | |||||
if is_valid_ip(ip): | |||||
env['UPLOAD_PORT'] = '"' + ip + '"' | |||||
#env.AddPreAction("uploadfs", before_upload) | |||||
#env.AddPreAction("upload", before_upload) | |||||
env.AddPreAction(".pioenvs/%s/spiffs.bin" % env['PIOENV'], before_build_spiffs) |
@ -1,111 +1,66 @@ | |||||
[env:sonoff-debug] | |||||
platform = espressif | |||||
framework = arduino | |||||
board = esp01_1m | |||||
lib_install = 89,64,19 | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld | |||||
build_flags = -D SONOFF -D DEBUG | |||||
[platformio] | |||||
env_default = node-debug | |||||
[env:sonoff-debug-ota] | |||||
platform = espressif | |||||
[common] | |||||
platform = espressif8266 | |||||
framework = arduino | framework = arduino | ||||
board = esp01_1m | |||||
lib_install = 89,64,19 | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld | |||||
build_flags = -D SONOFF -D DEBUG | |||||
lib_install = 19,44,64,89,549,727 | |||||
extra_script = pio_hooks.py | |||||
[ota] | |||||
upload_speed = 115200 | upload_speed = 115200 | ||||
upload_port = "192.168.4.1" | upload_port = "192.168.4.1" | ||||
upload_flags = --auth=fibonacci --port 8266 | upload_flags = --auth=fibonacci --port 8266 | ||||
[env:sonoff-debug] | |||||
include = common | |||||
board = esp01_1m | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF | |||||
[env:sonoff-debug-ota] | |||||
include = env:sonoff-debug,ota | |||||
[env:slampher-debug] | [env:slampher-debug] | ||||
platform = espressif | |||||
framework = arduino | |||||
include = common | |||||
board = esp01_1m | board = esp01_1m | ||||
lib_install = 89,64,19 | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld | |||||
build_flags = -D SLAMPHER -D DEBUG | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSLAMPHER | |||||
[env:slampher-debug-ota] | [env:slampher-debug-ota] | ||||
platform = espressif | |||||
framework = arduino | |||||
board = esp01_1m | |||||
lib_install = 89,64,19 | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld | |||||
build_flags = -D SLAMPHER -D DEBUG | |||||
upload_speed = 115200 | |||||
upload_port = "192.168.4.1" | |||||
upload_flags = --auth=fibonacci --port 8266 | |||||
include = env:slampher-debug,ota | |||||
[env:s20-debug] | [env:s20-debug] | ||||
platform = espressif | |||||
framework = arduino | |||||
include = common | |||||
board = esp01_1m | board = esp01_1m | ||||
lib_install = 89,64,19 | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld | |||||
build_flags = -D S20 -D DEBUG | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DS20 | |||||
[env:s20-debug-ota] | [env:s20-debug-ota] | ||||
platform = espressif | |||||
framework = arduino | |||||
board = esp01_1m | |||||
lib_install = 89,64,19 | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld | |||||
build_flags = -D S20 -D DEBUG | |||||
upload_speed = 115200 | |||||
upload_port = "192.168.4.1" | |||||
upload_flags = --auth=fibonacci --port 8266 | |||||
include = env:s20-debug,ota | |||||
[env:node-debug] | [env:node-debug] | ||||
platform = espressif | |||||
framework = arduino | |||||
include = common | |||||
board = nodemcuv2 | board = nodemcuv2 | ||||
lib_install = 89,64,19 | |||||
build_flags = -D NODEMCUV2 -D DEBUG | |||||
build_flags = -DNODEMCUV2 -DDEBUG_PORT=Serial | |||||
[env:node-debug-ota] | [env:node-debug-ota] | ||||
platform = espressif | |||||
framework = arduino | |||||
board = nodemcuv2 | |||||
lib_install = 89,64,19 | |||||
build_flags = -D NODEMCUV2 -D DEBUG | |||||
upload_speed = 115200 | |||||
upload_port = "192.168.4.1" | |||||
upload_flags = --auth=fibonacci --port 8266 | |||||
include = env:node-debug,ota | |||||
[env:ac-device] | |||||
topic = /home/cellar/airconditioner/ip | |||||
include = env:s20-debug-ota | |||||
[env:washer-device] | [env:washer-device] | ||||
platform = espressif | |||||
framework = arduino | |||||
board = esp01_1m | |||||
lib_install = 89,64,19 | |||||
topic = /home/cellar/washer/ip | topic = /home/cellar/washer/ip | ||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld | |||||
build_flags = -D SONOFF -D DEBUG -D ENABLE_EMON -D ENABLE_DHT | |||||
upload_speed = 115200 | |||||
upload_port = "192.168.1.114" | |||||
upload_flags = --auth=fibonacci --port 8266 | |||||
include = env:sonoff-debug-ota | |||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld -DDEBUG_PORT=Serial -DSONOFF -DENABLE_EMON=1 -DENABLE_DHT=1 | |||||
[env:studio-lamp-device] | [env:studio-lamp-device] | ||||
platform = espressif | |||||
framework = arduino | |||||
board = esp01_1m | |||||
lib_install = 89,64,19 | |||||
topic = /home/studio/lamp/ip | topic = /home/studio/lamp/ip | ||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld | |||||
build_flags = -D SONOFF -D DEBUG | |||||
upload_speed = 115200 | |||||
upload_port = "192.168.1.114" | |||||
upload_flags = --auth=fibonacci --port 8266 | |||||
include = env:sonoff-debug-ota | |||||
[env:living-lamp-device] | [env:living-lamp-device] | ||||
platform = espressif | |||||
framework = arduino | |||||
board = esp01_1m | |||||
lib_install = 89,64,19 | |||||
topic = /home/living/lamp/ip | topic = /home/living/lamp/ip | ||||
build_flags = -Wl,-Tesp8266.flash.1m256.ld | |||||
build_flags = -D SONOFF -D DEBUG -D RF | |||||
upload_speed = 115200 | |||||
upload_port = "192.168.1.114" | |||||
upload_flags = --auth=fibonacci --port 8266 | |||||
include = env:sonoff-debug-ota |
@ -0,0 +1,5 @@ | |||||
#ifdef DEBUG_PORT | |||||
#define DEBUG_MSG(...) DEBUG_PORT.printf( __VA_ARGS__ ) | |||||
#else | |||||
#define DEBUG_MSG(...) | |||||
#endif |
@ -0,0 +1,44 @@ | |||||
/* | |||||
RENTALITO | |||||
NTP MODULE | |||||
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com> | |||||
*/ | |||||
#include <TimeLib.h> | |||||
#include <NtpClientLib.h> | |||||
#include <WiFiClient.h> | |||||
// ----------------------------------------------------------------------------- | |||||
// NTP | |||||
// ----------------------------------------------------------------------------- | |||||
void ntpConnect(WiFiEventStationModeGotIP ipInfo) { | |||||
NTP.begin(NTP_SERVER, NTP_TIME_OFFSET, NTP_DAY_LIGHT); | |||||
NTP.setInterval(NTP_UPDATE_INTERVAL); | |||||
} | |||||
void ntpSetup() { | |||||
NTP.onNTPSyncEvent([](NTPSyncEvent_t error) { | |||||
if (error) { | |||||
if (error == noResponse) { | |||||
DEBUG_MSG("[NTP] Error: NTP server not reachable\n"); | |||||
} else if (error == invalidAddress) { | |||||
DEBUG_MSG("[NTP] Error: Invalid NTP server address\n"); | |||||
} | |||||
} else { | |||||
DEBUG_MSG("[NTP] Time: %s\n", (char *) NTP.getTimeDateString(NTP.getLastNTPSync()).c_str()); | |||||
} | |||||
}); | |||||
static WiFiEventHandler e; | |||||
e = WiFi.onStationModeGotIP(ntpConnect); | |||||
} | |||||
void ntpLoop() { | |||||
now(); | |||||
} |
@ -1,4 +1,4 @@ | |||||
#define APP_NAME "Espurna" | #define APP_NAME "Espurna" | ||||
#define APP_VERSION "0.9.7" | |||||
#define APP_VERSION "0.9.8" | |||||
#define APP_AUTHOR "xose.perez@gmail.com" | #define APP_AUTHOR "xose.perez@gmail.com" | ||||
#define APP_WEBSITE "http://tinkerman.cat" | #define APP_WEBSITE "http://tinkerman.cat" |
@ -0,0 +1,107 @@ | |||||
/* | |||||
RENTALITO | |||||
WEBSERVER MODULE | |||||
Copyright (C) 2016 by Xose Pérez <xose dot perez at gmail dot com> | |||||
*/ | |||||
#include <WebSocketsServer.h> | |||||
#include <Hash.h> | |||||
#include <ArduinoJson.h> | |||||
WebSocketsServer webSocket = WebSocketsServer(81); | |||||
// ----------------------------------------------------------------------------- | |||||
// WEBSOCKETS | |||||
// ----------------------------------------------------------------------------- | |||||
bool webSocketSend(char * payload) { | |||||
//DEBUG_MSG("[WEBSOCKET] Broadcasting '%s'\n", payload); | |||||
webSocket.broadcastTXT(payload); | |||||
} | |||||
bool webSocketSend(uint8_t num, char * payload) { | |||||
//DEBUG_MSG("[WEBSOCKET] Sending '%s' to #%d\n", payload, num); | |||||
webSocket.sendTXT(num, payload); | |||||
} | |||||
void webSocketStart(uint8_t num) { | |||||
char buffer[64]; | |||||
sprintf(buffer, "%s %s", APP_NAME, APP_VERSION); | |||||
DynamicJsonBuffer jsonBuffer; | |||||
JsonObject& root = jsonBuffer.createObject(); | |||||
root["app"] = buffer; | |||||
root["manufacturer"] = String(MANUFACTURER); | |||||
root["device"] = String(DEVICE); | |||||
root["hostname"] = getSetting("hostname", HOSTNAME); | |||||
root["network"] = getNetwork(); | |||||
root["ip"] = getIP(); | |||||
root["mqttStatus"] = mqttConnected() ? "1" : "0"; | |||||
root["mqttServer"] = getSetting("mqttServer", MQTT_SERVER); | |||||
root["mqttPort"] = getSetting("mqttPort", String(MQTT_PORT)); | |||||
root["mqttUser"] = getSetting("mqttUser"); | |||||
root["mqttPassword"] = getSetting("mqttPassword"); | |||||
root["mqttTopic"] = getSetting("mqttTopic", MQTT_TOPIC); | |||||
root["relayStatus"] = digitalRead(RELAY_PIN) == HIGH; | |||||
#if ENABLE_RF | |||||
root["rfChannel"] = getSetting("rfChannel", String(RF_CHANNEL)); | |||||
root["rfDevice"] = getSetting("rfDevice", String(RF_DEVICE)); | |||||
#endif | |||||
#if ENABLE_EMON | |||||
root["pwMainsVoltage"] = getSetting("pwMainsVoltage", String(EMON_MAINS_VOLTAGE)); | |||||
root["pwCurrentRatio"] = getSetting("pwCurrentRatio", String(EMON_CURRENT_RATIO)); | |||||
#endif | |||||
JsonArray& wifi = root.createNestedArray("wifi"); | |||||
for (byte i=0; i<3; i++) { | |||||
JsonObject& network = wifi.createNestedObject(); | |||||
network["ssid"] = getSetting("ssid" + String(i)); | |||||
network["pass"] = getSetting("pass" + String(i)); | |||||
} | |||||
String output; | |||||
root.printTo(output); | |||||
webSocket.sendTXT(num, (char *) output.c_str()); | |||||
} | |||||
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) { | |||||
switch(type) { | |||||
case WStype_DISCONNECTED: | |||||
DEBUG_MSG("[WEBSOCKET] #%u disconnected\n", num); | |||||
break; | |||||
case WStype_CONNECTED: | |||||
#if DEBUG_PORT | |||||
{ | |||||
IPAddress ip = webSocket.remoteIP(num); | |||||
DEBUG_MSG("[WEBSOCKET] #%u connected, ip: %d.%d.%d.%d, url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload); | |||||
} | |||||
#endif | |||||
webSocketStart(num); | |||||
break; | |||||
case WStype_TEXT: | |||||
DEBUG_MSG("[WEBSOCKET] #%u sent: %s\n", num, payload); | |||||
break; | |||||
case WStype_BIN: | |||||
DEBUG_MSG("[WEBSOCKET] #%u sent binary length: %u\n", num, length); | |||||
break; | |||||
} | |||||
} | |||||
void webSocketSetup() { | |||||
webSocket.begin(); | |||||
webSocket.onEvent(webSocketEvent); | |||||
} | |||||
void webSocketLoop() { | |||||
webSocket.loop(); | |||||
} |