forked from Links2004/arduinoWebSockets
Compare commits
30 Commits
Author | SHA1 | Date | |
---|---|---|---|
1fa9a1321c | |||
8fd9c5eff1 | |||
57976f7b51 | |||
5881a99a30 | |||
52b3aa8ea4 | |||
28c0cf3094 | |||
ab9af162b2 | |||
8d76469e90 | |||
61ea44942e | |||
28ed615145 | |||
108090e8cf | |||
46b2ae1c97 | |||
27f86a10ad | |||
860ac9da69 | |||
c897f60567 | |||
b242882ac5 | |||
f8da05aa87 | |||
72aae52655 | |||
a14b6b73b4 | |||
ed685e551f | |||
7c3b1b7408 | |||
738e43fda4 | |||
f55bf8d4ed | |||
a484da47ed | |||
4355199120 | |||
0a4fcd44c2 | |||
3a2b757155 | |||
900d81e534 | |||
0ecef8c552 | |||
410489f7c5 |
22
.github/workflows/main.yml
vendored
22
.github/workflows/main.yml
vendored
@ -36,16 +36,13 @@ jobs:
|
||||
echo -en "::set-output name=matrix::"
|
||||
echo -en "["
|
||||
|
||||
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp8266 esp8266 1.6.13 esp8266com:esp8266:generic:xtal=80
|
||||
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp8266 esp8266 1.8.19 esp8266com:esp8266:generic:xtal=80,vt=flash,exception=disabled,stacksmash=disabled,ssl=all,mmu=3232,non32xfer=fast,ResetMethod=nodemcu,CrystalFreq=26,FlashFreq=80,FlashMode=qio,eesz=4M2M,led=2,sdk=nonosdk_190703,ip=lm2f,dbg=Serial1,lvl=SSL,wipe=none,baud=115200
|
||||
echo -en ","
|
||||
|
||||
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp8266 esp8266 1.6.13 esp8266com:esp8266:generic:xtal=80,dbg=Serial1
|
||||
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp8266 esp8266 1.8.19 esp8266com:esp8266:generic:xtal=80,vt=flash,exception=disabled,stacksmash=disabled,ssl=all,mmu=3232,non32xfer=fast,ResetMethod=nodemcu,CrystalFreq=26,FlashFreq=80,FlashMode=qio,eesz=4M2M,led=2,sdk=nonosdk_190703,ip=lm2f,dbg=Disabled,lvl=None____,wipe=none,baud=115200
|
||||
echo -en ","
|
||||
|
||||
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp8266 esp8266 1.8.13 esp8266com:esp8266:generic:xtal=80,eesz=1M,FlashMode=qio,FlashFreq=80
|
||||
echo -en ","
|
||||
|
||||
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp32 esp32 1.8.13 espressif:esp32:esp32:FlashFreq=80
|
||||
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp32 esp32 1.8.19 espressif:esp32:esp32:FlashFreq=80
|
||||
|
||||
echo -en "]"
|
||||
outputs:
|
||||
@ -56,7 +53,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
IDE_VERSION: [1.8.13, 1.6.13]
|
||||
IDE_VERSION: [1.8.19]
|
||||
env:
|
||||
IDE_VERSION: ${{ matrix.IDE_VERSION }}
|
||||
|
||||
@ -99,7 +96,7 @@ jobs:
|
||||
get_core esp8266
|
||||
|
||||
- name: download esp32
|
||||
if: steps.cache_all.outputs.cache-hit != 'true' && matrix.IDE_VERSION != '1.6.13'
|
||||
if: steps.cache_all.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
source $GITHUB_WORKSPACE/travis/common.sh
|
||||
get_core esp32
|
||||
@ -121,6 +118,10 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: install libgtk2.0-0
|
||||
run: |
|
||||
sudo apt-get install -y libgtk2.0-0
|
||||
|
||||
- name: Get Date
|
||||
id: get-date
|
||||
run: |
|
||||
@ -160,15 +161,18 @@ jobs:
|
||||
|
||||
- name: config IDE
|
||||
run: |
|
||||
set +x
|
||||
export DISPLAY=:1.0
|
||||
export PATH="$HOME/arduino_ide:$PATH"
|
||||
arduino --board $BOARD --save-prefs
|
||||
arduino --pref update.check=false --pref build.verbose=false --pref cache.enable=true --pref compiler.cache_core=true --pref compiler.warning_level=default --save-prefs
|
||||
arduino --get-pref sketchbook.path
|
||||
arduino --pref update.check=false
|
||||
arduino --get-pref
|
||||
|
||||
- name: build example
|
||||
timeout-minutes: 20
|
||||
run: |
|
||||
set -ex
|
||||
export DISPLAY=:1.0
|
||||
export PATH="$HOME/arduino_ide:$PATH"
|
||||
source $GITHUB_WORKSPACE/travis/common.sh
|
||||
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* WebSocketClientSocketIOack.ino
|
||||
*
|
||||
* Created on: 20.07.2019
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <WiFiMulti.h>
|
||||
#include <WiFiClientSecure.h>
|
||||
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#include <WebSocketsClient.h>
|
||||
#include <SocketIOclient.h>
|
||||
|
||||
WiFiMulti WiFiMulti;
|
||||
SocketIOclient socketIO;
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
|
||||
void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
|
||||
switch(type) {
|
||||
case sIOtype_DISCONNECT:
|
||||
USE_SERIAL.printf("[IOc] Disconnected!\n");
|
||||
break;
|
||||
case sIOtype_CONNECT:
|
||||
USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload);
|
||||
|
||||
// join default namespace (no auto join in Socket.IO V3)
|
||||
socketIO.send(sIOtype_CONNECT, "/");
|
||||
break;
|
||||
case sIOtype_EVENT:
|
||||
{
|
||||
char * sptr = NULL;
|
||||
int id = strtol((char *)payload, &sptr, 10);
|
||||
USE_SERIAL.printf("[IOc] get event: %s id: %d\n", payload, id);
|
||||
if(id) {
|
||||
payload = (uint8_t *)sptr;
|
||||
}
|
||||
DynamicJsonDocument doc(1024);
|
||||
DeserializationError error = deserializeJson(doc, payload, length);
|
||||
if(error) {
|
||||
USE_SERIAL.print(F("deserializeJson() failed: "));
|
||||
USE_SERIAL.println(error.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
String eventName = doc[0];
|
||||
USE_SERIAL.printf("[IOc] event name: %s\n", eventName.c_str());
|
||||
|
||||
// Message Includes a ID for a ACK (callback)
|
||||
if(id) {
|
||||
// creat JSON message for Socket.IO (ack)
|
||||
DynamicJsonDocument docOut(1024);
|
||||
JsonArray array = docOut.to<JsonArray>();
|
||||
|
||||
// add payload (parameters) for the ack (callback function)
|
||||
JsonObject param1 = array.createNestedObject();
|
||||
param1["now"] = millis();
|
||||
|
||||
// JSON to String (serializion)
|
||||
String output;
|
||||
output += id;
|
||||
serializeJson(docOut, output);
|
||||
|
||||
// Send event
|
||||
socketIO.send(sIOtype_ACK, output);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case sIOtype_ACK:
|
||||
USE_SERIAL.printf("[IOc] get ack: %u\n", length);
|
||||
break;
|
||||
case sIOtype_ERROR:
|
||||
USE_SERIAL.printf("[IOc] get error: %u\n", length);
|
||||
break;
|
||||
case sIOtype_BINARY_EVENT:
|
||||
USE_SERIAL.printf("[IOc] get binary: %u\n", length);
|
||||
break;
|
||||
case sIOtype_BINARY_ACK:
|
||||
USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void setup() {
|
||||
//USE_SERIAL.begin(921600);
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
//Serial.setDebugOutput(true);
|
||||
USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
USE_SERIAL.println();
|
||||
|
||||
for(uint8_t t = 4; t > 0; t--) {
|
||||
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
|
||||
USE_SERIAL.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFiMulti.addAP("SSID", "passpasspass");
|
||||
|
||||
//WiFi.disconnect();
|
||||
while(WiFiMulti.run() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
String ip = WiFi.localIP().toString();
|
||||
USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
|
||||
|
||||
// server address, port and URL
|
||||
socketIO.begin("10.11.100.100", 8880, "/socket.io/?EIO=4");
|
||||
|
||||
// event handler
|
||||
socketIO.onEvent(socketIOEvent);
|
||||
}
|
||||
|
||||
unsigned long messageTimestamp = 0;
|
||||
void loop() {
|
||||
socketIO.loop();
|
||||
|
||||
uint64_t now = millis();
|
||||
|
||||
if(now - messageTimestamp > 2000) {
|
||||
messageTimestamp = now;
|
||||
|
||||
// creat JSON message for Socket.IO (event)
|
||||
DynamicJsonDocument doc(1024);
|
||||
JsonArray array = doc.to<JsonArray>();
|
||||
|
||||
// add evnet name
|
||||
// Hint: socket.on('event_name', ....
|
||||
array.add("event_name");
|
||||
|
||||
// add payload (parameters) for the event
|
||||
JsonObject param1 = array.createNestedObject();
|
||||
param1["now"] = (uint32_t) now;
|
||||
|
||||
// JSON to String (serializion)
|
||||
String output;
|
||||
serializeJson(doc, output);
|
||||
|
||||
// Send event
|
||||
socketIO.sendEVENT(output);
|
||||
|
||||
// Print JSON for debugging
|
||||
USE_SERIAL.println(output);
|
||||
}
|
||||
}
|
27
examples/esp8266/WebSocketClientOTA/README.md
Normal file
27
examples/esp8266/WebSocketClientOTA/README.md
Normal file
@ -0,0 +1,27 @@
|
||||
## Minimal example of WebsocketClientOTA and Python server
|
||||
|
||||
Take this as small example, how achieve OTA update on ESP8266 and ESP32.
|
||||
|
||||
Python server was wrote from train so take it only as bare example.
|
||||
It's working, but it's not mean to run in production.
|
||||
|
||||
|
||||
### Usage:
|
||||
|
||||
Start server:
|
||||
```bash
|
||||
cd python_ota_server
|
||||
python3 -m venv .venv
|
||||
source .venv/bin/activate
|
||||
pip3 install -r requirements.txt
|
||||
python3 main.py
|
||||
```
|
||||
|
||||
Flash ESP with example sketch and start it.
|
||||
|
||||
Change version inside example sketch to higher and compile it and save it to bin file.
|
||||
|
||||
Rename it to `mydevice-1.0.1-esp8266.bin` and place it inside new folder firmware (server create it).
|
||||
|
||||
When the ESP connect to server, it check if version flashed is equal to fw in firmware folder. If higher FW version is present,
|
||||
start the flash process.
|
263
examples/esp8266/WebSocketClientOTA/WebSocketClientOTA.ino
Normal file
263
examples/esp8266/WebSocketClientOTA/WebSocketClientOTA.ino
Normal file
@ -0,0 +1,263 @@
|
||||
/*
|
||||
* WebSocketClientOTA.ino
|
||||
*
|
||||
* Created on: 25.10.2021
|
||||
*
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
#ifdef ESP8266
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ESP8266mDNS.h>
|
||||
#include <Updater.h>
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
#include "WiFi.h"
|
||||
#include "ESPmDNS.h"
|
||||
#include <Update.h>
|
||||
#endif
|
||||
|
||||
#include <WiFiUdp.h>
|
||||
#include <ESP8266WiFiMulti.h>
|
||||
|
||||
#include <WebSocketsClient.h>
|
||||
|
||||
#include <Hash.h>
|
||||
|
||||
ESP8266WiFiMulti WiFiMulti;
|
||||
WebSocketsClient webSocket;
|
||||
|
||||
#define USE_SERIAL Serial
|
||||
|
||||
// Variables:
|
||||
// Settable:
|
||||
const char *version = "1.0.0";
|
||||
const char *name = "mydevice";
|
||||
|
||||
// Others:
|
||||
#ifdef ESP8266
|
||||
const char *chip = "esp8266";
|
||||
#endif
|
||||
#ifdef ESP32
|
||||
const char *chip = "esp32";
|
||||
#endif
|
||||
|
||||
uint32_t maxSketchSpace = 0;
|
||||
int SketchSize = 0;
|
||||
bool ws_conn = false;
|
||||
|
||||
String IpAddress2String(const IPAddress& ipAddress)
|
||||
{
|
||||
return String(ipAddress[0]) + String(".") +
|
||||
String(ipAddress[1]) + String(".") +
|
||||
String(ipAddress[2]) + String(".") +
|
||||
String(ipAddress[3]);
|
||||
}
|
||||
|
||||
void greetings_(){
|
||||
StaticJsonDocument<200> doc;
|
||||
doc["type"] = "greetings";
|
||||
doc["mac"] = WiFi.macAddress();
|
||||
doc["ip"] = IpAddress2String(WiFi.localIP());
|
||||
doc["version"] = version;
|
||||
doc["name"] = name;
|
||||
doc["chip"] = chip;
|
||||
|
||||
char data[200];
|
||||
serializeJson(doc, data);
|
||||
webSocket.sendTXT(data);
|
||||
}
|
||||
|
||||
void register_(){
|
||||
StaticJsonDocument<200> doc;
|
||||
doc["type"] = "register";
|
||||
doc["mac"] = WiFi.macAddress();
|
||||
|
||||
char data[200];
|
||||
serializeJson(doc, data);
|
||||
webSocket.sendTXT(data);
|
||||
ws_conn = true;
|
||||
}
|
||||
|
||||
typedef void (*CALLBACK_FUNCTION)(JsonDocument &msg);
|
||||
|
||||
typedef struct {
|
||||
char type[50];
|
||||
CALLBACK_FUNCTION func;
|
||||
} RESPONSES_STRUCT;
|
||||
|
||||
void OTA(JsonDocument &msg){
|
||||
USE_SERIAL.print(F("[WSc] OTA mode: "));
|
||||
const char* go = "go";
|
||||
const char* ok = "ok";
|
||||
if(strncmp( msg["value"], go, strlen(go)) == 0 ) {
|
||||
USE_SERIAL.print(F("go\n"));
|
||||
SketchSize = int(msg["size"]);
|
||||
maxSketchSpace = (ESP.getFreeSketchSpace() - 0x1000) & 0xFFFFF000;
|
||||
USE_SERIAL.printf("[WSc] Max sketch size: %u\n", maxSketchSpace);
|
||||
USE_SERIAL.printf("[WSc] Sketch size: %d\n", SketchSize);
|
||||
USE_SERIAL.setDebugOutput(true);
|
||||
if (!Update.begin(maxSketchSpace)) { //start with max available size
|
||||
Update.printError(Serial);
|
||||
ESP.restart();
|
||||
}
|
||||
} else if (strncmp( msg["value"], ok, strlen(ok)) == 0) {
|
||||
USE_SERIAL.print(F("OK\n"));
|
||||
register_();
|
||||
} else {
|
||||
USE_SERIAL.print(F("unknown value : "));
|
||||
USE_SERIAL.print(msg["value"].as<char>());
|
||||
USE_SERIAL.print(F("\n"));
|
||||
}
|
||||
}
|
||||
|
||||
void STATE(JsonDocument &msg){
|
||||
// Do something with message
|
||||
}
|
||||
|
||||
RESPONSES_STRUCT responses[] = {
|
||||
{"ota", OTA},
|
||||
{"state", STATE},
|
||||
};
|
||||
|
||||
void text(uint8_t * payload, size_t length){
|
||||
// Convert mesage to something usable
|
||||
char msgch[length];
|
||||
for (unsigned int i = 0; i < length; i++)
|
||||
{
|
||||
USE_SERIAL.print((char)payload[i]);
|
||||
msgch[i] = ((char)payload[i]);
|
||||
}
|
||||
msgch[length] = '\0';
|
||||
|
||||
// Parse Json
|
||||
StaticJsonDocument<200> doc_in;
|
||||
DeserializationError error = deserializeJson(doc_in, msgch);
|
||||
|
||||
if (error) {
|
||||
USE_SERIAL.print(F("deserializeJson() failed: "));
|
||||
USE_SERIAL.println(error.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle each TYPE of message
|
||||
int b = 0;
|
||||
|
||||
for( b=0 ; strlen(responses[b].type) ; b++ )
|
||||
{
|
||||
if( strncmp(doc_in["type"], responses[b].type, strlen(responses[b].type)) == 0 ) {
|
||||
responses[b].func(doc_in);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
|
||||
switch(type) {
|
||||
case WStype_DISCONNECTED:
|
||||
USE_SERIAL.printf("[WSc] Disconnected!\n");
|
||||
break;
|
||||
case WStype_CONNECTED: {
|
||||
USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
|
||||
|
||||
// send message to server when Connected
|
||||
// webSocket.sendTXT("Connected");
|
||||
greetings_();
|
||||
}
|
||||
break;
|
||||
case WStype_TEXT:
|
||||
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
|
||||
|
||||
// send message to server
|
||||
// webSocket.sendTXT("message here");
|
||||
text(payload, length);
|
||||
break;
|
||||
case WStype_BIN:
|
||||
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
|
||||
// hexdump(payload, length);
|
||||
if (Update.write(payload, length) != length) {
|
||||
Update.printError(Serial);
|
||||
ESP.restart();
|
||||
}
|
||||
yield();
|
||||
SketchSize -= length;
|
||||
USE_SERIAL.printf("[WSc] Sketch size left: %u\n", SketchSize);
|
||||
if (SketchSize < 1){
|
||||
if (Update.end(true)) { //true to set the size to the current progress
|
||||
USE_SERIAL.printf("Update Success: \nRebooting...\n");
|
||||
delay(5);
|
||||
yield();
|
||||
ESP.restart();
|
||||
} else {
|
||||
Update.printError(USE_SERIAL);
|
||||
ESP.restart();
|
||||
}
|
||||
USE_SERIAL.setDebugOutput(false);
|
||||
}
|
||||
|
||||
// send data to server
|
||||
// webSocket.sendBIN(payload, length);
|
||||
break;
|
||||
case WStype_PING:
|
||||
// pong will be send automatically
|
||||
USE_SERIAL.printf("[WSc] get ping\n");
|
||||
break;
|
||||
case WStype_PONG:
|
||||
// answer to a ping we send
|
||||
USE_SERIAL.printf("[WSc] get pong\n");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void setup() {
|
||||
// USE_SERIAL.begin(921600);
|
||||
USE_SERIAL.begin(115200);
|
||||
|
||||
//Serial.setDebugOutput(true);
|
||||
USE_SERIAL.setDebugOutput(true);
|
||||
|
||||
USE_SERIAL.print(F("\nMAC: "));
|
||||
USE_SERIAL.println(WiFi.macAddress());
|
||||
USE_SERIAL.print(F("\nDevice: "));
|
||||
USE_SERIAL.println(name);
|
||||
USE_SERIAL.printf("\nVersion: %s\n", version);
|
||||
|
||||
for(uint8_t t = 4; t > 0; t--) {
|
||||
USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
|
||||
USE_SERIAL.flush();
|
||||
delay(1000);
|
||||
}
|
||||
|
||||
WiFiMulti.addAP("SSID", "PASS");
|
||||
|
||||
//WiFi.disconnect();
|
||||
while(WiFiMulti.run() != WL_CONNECTED) {
|
||||
delay(100);
|
||||
}
|
||||
|
||||
// server address, port and URL
|
||||
webSocket.begin("10.0.1.5", 8081, "/");
|
||||
|
||||
// event handler
|
||||
webSocket.onEvent(webSocketEvent);
|
||||
|
||||
// use HTTP Basic Authorization this is optional remove if not needed
|
||||
// webSocket.setAuthorization("USER", "PASS");
|
||||
|
||||
// try ever 5000 again if connection has failed
|
||||
webSocket.setReconnectInterval(5000);
|
||||
|
||||
// start heartbeat (optional)
|
||||
// ping server every 15000 ms
|
||||
// expect pong from server within 3000 ms
|
||||
// consider connection disconnected if pong is not received 2 times
|
||||
webSocket.enableHeartbeat(15000, 3000, 2);
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
webSocket.loop();
|
||||
}
|
235
examples/esp8266/WebSocketClientOTA/python_ota_server/main.py
Normal file
235
examples/esp8266/WebSocketClientOTA/python_ota_server/main.py
Normal file
@ -0,0 +1,235 @@
|
||||
"""Minimal example of Python websocket server
|
||||
handling OTA updates for ESP32 amd ESP8266
|
||||
|
||||
Check and upload of firmware works.
|
||||
Register and state function are jus for example.
|
||||
"""
|
||||
# pylint: disable=W0703,E1101
|
||||
import asyncio
|
||||
import copy
|
||||
import json
|
||||
import logging
|
||||
import subprocess
|
||||
import threading
|
||||
import time
|
||||
from os import listdir
|
||||
from os.path import join as join_pth
|
||||
from pathlib import Path
|
||||
|
||||
import websockets
|
||||
from packaging import version
|
||||
|
||||
# Logger settings
|
||||
logging.basicConfig(filename="ws_server.log")
|
||||
Logger = logging.getLogger('WS-OTA')
|
||||
Logger.addHandler(logging.StreamHandler())
|
||||
Logger.setLevel(logging.INFO)
|
||||
|
||||
# Path to directory with FW
|
||||
fw_path = join_pth(Path().absolute(), "firmware")
|
||||
|
||||
|
||||
def create_path(path: str) -> None:
|
||||
"""Check if path exist or create it"""
|
||||
Path(path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def shell(command):
|
||||
"""Handle execution of shell commands"""
|
||||
with subprocess.Popen(command, shell=True,
|
||||
stdout=subprocess.PIPE,
|
||||
universal_newlines=True
|
||||
) as process:
|
||||
for stdout_line in iter(process.stdout.readline, ""):
|
||||
Logger.debug(stdout_line)
|
||||
process.stdout.close()
|
||||
return_code = process.wait()
|
||||
Logger.debug("Shell returned: %s", return_code)
|
||||
|
||||
return process.returncode
|
||||
return None
|
||||
|
||||
|
||||
async def binary_send(websocket, fw_file):
|
||||
"""Read firmware file, divide it to chunks and send them"""
|
||||
with open(fw_file, "rb") as binaryfile:
|
||||
|
||||
while True:
|
||||
chunk = binaryfile.read(2048)
|
||||
if not chunk:
|
||||
break
|
||||
try:
|
||||
await websocket.send(chunk)
|
||||
except Exception as exception:
|
||||
Logger.exception(exception)
|
||||
return False
|
||||
time.sleep(0.2)
|
||||
|
||||
|
||||
def version_checker(name, vdev, vapp):
|
||||
"""Parse and compare FW version"""
|
||||
|
||||
if version.parse(vdev) < version.parse(vapp):
|
||||
Logger.info("Client(%s) version %s is smaller than %s: Go for update", name, vdev, vapp)
|
||||
return True
|
||||
Logger.info("Client(%s) version %s is greater or equal to %s: Not updating", name, vdev, vapp)
|
||||
return False
|
||||
|
||||
|
||||
class WsOtaHandler (threading.Thread):
|
||||
"""Thread handling ota update
|
||||
|
||||
Runing ota directly from message would kill WS
|
||||
as message bus would timeout.
|
||||
"""
|
||||
def __init__(self, name, message, websocket):
|
||||
threading.Thread.__init__(self, daemon=True)
|
||||
self.name = name
|
||||
self.msg = message
|
||||
self.websocket = websocket
|
||||
|
||||
def run(self, ):
|
||||
try:
|
||||
asyncio.run(self.start_)
|
||||
except Exception as exception:
|
||||
Logger.exception(exception)
|
||||
finally:
|
||||
pass
|
||||
|
||||
async def start_(self):
|
||||
"""Start _ota se asyncio future"""
|
||||
msg_task = asyncio.ensure_future(
|
||||
self._ota())
|
||||
|
||||
done, pending = await asyncio.wait(
|
||||
[msg_task],
|
||||
return_when=asyncio.FIRST_COMPLETED,
|
||||
)
|
||||
Logger.info("WS Ota Handler done: %s", done)
|
||||
for task in pending:
|
||||
task.cancel()
|
||||
|
||||
async def _ota(self):
|
||||
"""Check for new fw and update or pass"""
|
||||
device_name = self.msg['name']
|
||||
device_chip = self.msg['chip']
|
||||
device_version = self.msg['version']
|
||||
fw_version = ''
|
||||
fw_name = ''
|
||||
fw_device = ''
|
||||
|
||||
for filename in listdir(fw_path):
|
||||
fw_info = filename.split("-")
|
||||
fw_device = fw_info[0]
|
||||
if fw_device == device_name:
|
||||
fw_version = fw_info[1]
|
||||
fw_name = filename
|
||||
break
|
||||
|
||||
if not fw_version:
|
||||
Logger.info("Client(%s): No fw found!", device_name)
|
||||
msg = '{"type": "ota", "value":"ok"}'
|
||||
await self.websocket.send(msg)
|
||||
return
|
||||
|
||||
if not version_checker(device_name, device_version, fw_version):
|
||||
return
|
||||
|
||||
fw_file = join_pth(fw_path, fw_name)
|
||||
if device_chip == 'esp8266' and not fw_file.endswith('.gz'):
|
||||
# We can compress fw to make it smaller for upload
|
||||
fw_cpress = fw_file
|
||||
fw_file = fw_cpress + ".gz"
|
||||
cpress = f"gzip -9 {fw_cpress}"
|
||||
cstate = shell(cpress)
|
||||
if cstate:
|
||||
Logger.error("Cannot compress firmware: %s", fw_name)
|
||||
return
|
||||
|
||||
# Get size of fw
|
||||
size = Path(fw_file).stat().st_size
|
||||
|
||||
# Request ota mode
|
||||
msg = '{"type": "ota", "value":"go", "size":' + str(size) + '}'
|
||||
await self.websocket.send(msg)
|
||||
|
||||
# send file by chunks trough websocket
|
||||
await binary_send(self.websocket, fw_file)
|
||||
|
||||
|
||||
async def _register(websocket, message):
|
||||
mac = message.get('mac')
|
||||
name = message.get('name')
|
||||
Logger.info("Client(%s) mac: %s", name, mac)
|
||||
# Some code
|
||||
|
||||
response = {'response_type': 'registry', 'state': 'ok'}
|
||||
await websocket.send(json.dumps(response))
|
||||
|
||||
|
||||
async def _state(websocket, message):
|
||||
mac = message.get('mac')
|
||||
name = message.get('name')
|
||||
Logger.info("Client(%s) mac: %s", name, mac)
|
||||
# Some code
|
||||
|
||||
response = {'response_type': 'state', 'state': 'ok'}
|
||||
await websocket.send(json.dumps(response))
|
||||
|
||||
|
||||
async def _unhandleld(websocket, msg):
|
||||
Logger.info("Unhandled message from device: %s", str(msg))
|
||||
response = {'response_type': 'response', 'state': 'nok'}
|
||||
await websocket.send(json.dumps(response))
|
||||
|
||||
|
||||
async def _greetings(websocket, message):
|
||||
WsOtaHandler('thread_ota', copy.deepcopy(message), websocket).start()
|
||||
|
||||
|
||||
async def message_received(websocket, message) -> None:
|
||||
"""Handle incoming messages
|
||||
|
||||
Check if message contain json and run waned function
|
||||
"""
|
||||
switcher = {"greetings": _greetings,
|
||||
"register": _register,
|
||||
"state": _state
|
||||
}
|
||||
|
||||
if message[0:1] == "{":
|
||||
try:
|
||||
msg_json = json.loads(message)
|
||||
except Exception as exception:
|
||||
Logger.error(exception)
|
||||
return
|
||||
|
||||
type_ = msg_json.get('type')
|
||||
name = msg_json.get('name')
|
||||
func = switcher.get(type_, _unhandleld)
|
||||
Logger.debug("Client(%s)said: %s", name, type_)
|
||||
|
||||
try:
|
||||
await func(websocket, msg_json)
|
||||
except Exception as exception:
|
||||
Logger.error(exception)
|
||||
|
||||
|
||||
# pylint: disable=W0613
|
||||
async def ws_server(websocket, path) -> None:
|
||||
"""Run in cycle and wait for new messages"""
|
||||
async for message in websocket:
|
||||
await message_received(websocket, message)
|
||||
|
||||
|
||||
async def main():
|
||||
"""Server starter
|
||||
|
||||
Normal user can bind only port nubers greater than 1024
|
||||
"""
|
||||
async with websockets.serve(ws_server, "10.0.1.5", 8081):
|
||||
await asyncio.Future() # run forever
|
||||
|
||||
|
||||
create_path(fw_path)
|
||||
asyncio.run(main())
|
@ -0,0 +1,2 @@
|
||||
packaging
|
||||
websockets
|
@ -88,7 +88,7 @@ void setup() {
|
||||
USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
|
||||
|
||||
// server address, port and URL
|
||||
socketIO.begin("10.11.100.100", 8880);
|
||||
socketIO.begin("10.11.100.100", 8880, "/socket.io/?EIO=4");
|
||||
|
||||
// event handler
|
||||
socketIO.onEvent(socketIOEvent);
|
||||
|
@ -125,7 +125,7 @@ void setup() {
|
||||
USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
|
||||
|
||||
// server address, port and URL
|
||||
socketIO.begin("10.11.100.100", 8880);
|
||||
socketIO.begin("10.11.100.100", 8880, "/socket.io/?EIO=4");
|
||||
|
||||
// event handler
|
||||
socketIO.onEvent(socketIOEvent);
|
||||
|
@ -21,5 +21,5 @@
|
||||
"type": "git",
|
||||
"url": "https://github.com/Links2004/arduinoWebSockets.git"
|
||||
},
|
||||
"version": "2.3.2"
|
||||
"version": "2.3.7"
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
name=WebSockets
|
||||
version=2.3.2
|
||||
version=2.3.7
|
||||
author=Markus Sattler
|
||||
maintainer=Markus Sattler
|
||||
sentence=WebSockets for Arduino (Server + Client)
|
||||
|
@ -18,31 +18,37 @@ SocketIOclient::~SocketIOclient() {
|
||||
void SocketIOclient::begin(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
WebSocketsClient::beginSocketIO(host, port, url, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
|
||||
void SocketIOclient::begin(String host, uint16_t port, String url, String protocol) {
|
||||
WebSocketsClient::beginSocketIO(host, port, url, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
#if defined(HAS_SSL)
|
||||
void SocketIOclient::beginSSL(const char * host, uint16_t port, const char * url, const char * protocol) {
|
||||
WebSocketsClient::beginSocketIOSSL(host, port, url, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
|
||||
void SocketIOclient::beginSSL(String host, uint16_t port, String url, String protocol) {
|
||||
WebSocketsClient::beginSocketIOSSL(host, port, url, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
#if !defined(SSL_AXTLS)
|
||||
#if defined(SSL_BARESSL)
|
||||
void SocketIOclient::beginSSLWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
|
||||
WebSocketsClient::beginSocketIOSSLWithCA(host, port, url, CA_cert, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
|
||||
void SocketIOclient::beginSSLWithCA(const char * host, uint16_t port, const char * url, BearSSL::X509List * CA_cert, const char * protocol) {
|
||||
WebSocketsClient::beginSocketIOSSLWithCA(host, port, url, CA_cert, protocol);
|
||||
WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
|
||||
initClient();
|
||||
}
|
||||
|
||||
void SocketIOclient::setSSLClientCertKey(const char * clientCert, const char * clientPrivateKey) {
|
||||
@ -55,6 +61,18 @@ void SocketIOclient::setSSLClientCertKey(BearSSL::X509List * clientCert, BearSSL
|
||||
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void SocketIOclient::configureEIOping(bool disableHeartbeat) {
|
||||
_disableHeartbeat = disableHeartbeat;
|
||||
}
|
||||
|
||||
void SocketIOclient::initClient(void) {
|
||||
if(_client.cUrl.indexOf("EIO=4") != -1) {
|
||||
DEBUG_WEBSOCKETS("[wsIOc] found EIO=4 disable EIO ping on client\n");
|
||||
configureEIOping(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* set callback function
|
||||
* @param cbEvent SocketIOclientEvent
|
||||
@ -67,6 +85,14 @@ bool SocketIOclient::isConnected(void) {
|
||||
return WebSocketsClient::isConnected();
|
||||
}
|
||||
|
||||
void SocketIOclient::setExtraHeaders(const char * extraHeaders) {
|
||||
return WebSocketsClient::setExtraHeaders(extraHeaders);
|
||||
}
|
||||
|
||||
void SocketIOclient::setReconnectInterval(unsigned long time) {
|
||||
return WebSocketsClient::setReconnectInterval(time);
|
||||
}
|
||||
|
||||
/**
|
||||
* send text data to client
|
||||
* @param num uint8_t client id
|
||||
@ -148,8 +174,8 @@ bool SocketIOclient::sendEVENT(String & payload) {
|
||||
void SocketIOclient::loop(void) {
|
||||
WebSocketsClient::loop();
|
||||
unsigned long t = millis();
|
||||
if((t - _lastConnectionFail) > EIO_HEARTBEAT_INTERVAL) {
|
||||
_lastConnectionFail = t;
|
||||
if(!_disableHeartbeat && (t - _lastHeartbeat) > EIO_HEARTBEAT_INTERVAL) {
|
||||
_lastHeartbeat = t;
|
||||
DEBUG_WEBSOCKETS("[wsIOc] send ping\n");
|
||||
WebSocketsClient::sendTXT(eIOtype_PING);
|
||||
}
|
||||
|
@ -9,6 +9,7 @@
|
||||
#define SOCKETIOCLIENT_H_
|
||||
|
||||
#include "WebSockets.h"
|
||||
#include "WebSocketsClient.h"
|
||||
|
||||
#define EIO_HEARTBEAT_INTERVAL 20000
|
||||
|
||||
@ -75,9 +76,15 @@ class SocketIOclient : protected WebSocketsClient {
|
||||
bool send(socketIOmessageType_t type, const char * payload, size_t length = 0);
|
||||
bool send(socketIOmessageType_t type, String & payload);
|
||||
|
||||
void setExtraHeaders(const char * extraHeaders = NULL);
|
||||
void setReconnectInterval(unsigned long time);
|
||||
|
||||
void loop(void);
|
||||
|
||||
void configureEIOping(bool disableHeartbeat = false);
|
||||
|
||||
protected:
|
||||
bool _disableHeartbeat = false;
|
||||
uint64_t _lastHeartbeat = 0;
|
||||
SocketIOclientEvent _cbEvent;
|
||||
virtual void runIOCbEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
|
||||
@ -86,6 +93,8 @@ class SocketIOclient : protected WebSocketsClient {
|
||||
}
|
||||
}
|
||||
|
||||
void initClient(void);
|
||||
|
||||
// Handeling events from websocket layer
|
||||
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
|
||||
handleCbEvent(type, payload, length);
|
||||
|
@ -42,7 +42,11 @@ extern "C" {
|
||||
#include <esp_system.h>
|
||||
|
||||
#if ESP_IDF_VERSION_MAJOR >= 4
|
||||
#if(ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(1, 0, 6))
|
||||
#include "sha/sha_parallel_engine.h"
|
||||
#else
|
||||
#include <esp32/sha.h>
|
||||
#endif
|
||||
#else
|
||||
#include <hwcrypto/sha.h>
|
||||
#endif
|
||||
|
@ -229,8 +229,11 @@ void WebSocketsClient::loop(void) {
|
||||
#else
|
||||
#error setCACert not implemented
|
||||
#endif
|
||||
#if defined(SSL_BARESSL)
|
||||
} else if(_fingerprint) {
|
||||
#if defined(ESP32)
|
||||
} else if(!SSL_FINGERPRINT_IS_SET) {
|
||||
_client.ssl->setInsecure();
|
||||
#elif defined(SSL_BARESSL)
|
||||
} else if(SSL_FINGERPRINT_IS_SET) {
|
||||
_client.ssl->setFingerprint(_fingerprint);
|
||||
} else {
|
||||
_client.ssl->setInsecure();
|
||||
@ -781,8 +784,10 @@ void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
|
||||
if(client->isSocketIO) {
|
||||
break;
|
||||
}
|
||||
// falls through
|
||||
case 403: ///< Forbidden
|
||||
// todo handle login
|
||||
// falls through
|
||||
default: ///< Server dont unterstand requrst
|
||||
ok = false;
|
||||
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode);
|
||||
@ -865,14 +870,14 @@ void WebSocketsClient::connectedCb() {
|
||||
|
||||
#if defined(HAS_SSL)
|
||||
#if defined(SSL_AXTLS) || defined(ESP32)
|
||||
if(_client.isSSL && _fingerprint.length()) {
|
||||
if(_client.isSSL && SSL_FINGERPRINT_IS_SET) {
|
||||
if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n");
|
||||
WebSockets::clientDisconnect(&_client, 1000);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
if(_client.isSSL && _fingerprint) {
|
||||
if(_client.isSSL && SSL_FINGERPRINT_IS_SET) {
|
||||
#endif
|
||||
} else if(_client.isSSL && !_CA_cert) {
|
||||
#if defined(SSL_BARESSL)
|
||||
@ -945,6 +950,9 @@ void WebSocketsClient::handleHBPing() {
|
||||
if(sendPing()) {
|
||||
_client.lastPing = millis();
|
||||
_client.pongReceived = false;
|
||||
} else {
|
||||
DEBUG_WEBSOCKETS("[WS-Client] sending HB ping failed\n");
|
||||
WebSockets::clientDisconnect(&_client, 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -112,12 +112,14 @@ class WebSocketsClient : protected WebSockets {
|
||||
#ifdef SSL_AXTLS
|
||||
String _fingerprint;
|
||||
const char * _CA_cert;
|
||||
#define SSL_FINGERPRINT_IS_SET (_fingerprint.length())
|
||||
#define SSL_FINGERPRINT_NULL ""
|
||||
#else
|
||||
const uint8_t * _fingerprint;
|
||||
BearSSL::X509List * _CA_cert;
|
||||
BearSSL::X509List * _client_cert;
|
||||
BearSSL::PrivateKey * _client_key;
|
||||
#define SSL_FINGERPRINT_IS_SET (_fingerprint != NULL)
|
||||
#define SSL_FINGERPRINT_NULL NULL
|
||||
#endif
|
||||
|
||||
|
@ -516,6 +516,9 @@ void WebSocketsServerCore::messageReceived(WSclient_t * client, WSopcode_t opcod
|
||||
* @param client WSclient_t * ptr to the client struct contaning the native client "->tcp"
|
||||
*/
|
||||
void WebSocketsServerCore::dropNativeClient(WSclient_t * client) {
|
||||
if(!client) {
|
||||
return;
|
||||
}
|
||||
if(client->tcp) {
|
||||
if(client->tcp->connected()) {
|
||||
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) && (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP32)
|
||||
@ -619,6 +622,10 @@ WSclient_t * WebSocketsServerCore::handleNewClient(WEBSOCKETS_NETWORK_CLASS * tc
|
||||
#else
|
||||
DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n");
|
||||
#endif
|
||||
// no client! => create dummy!
|
||||
WSclient_t dummy = WSclient_t();
|
||||
client = &dummy;
|
||||
client->tcp = tcpClient;
|
||||
dropNativeClient(client);
|
||||
}
|
||||
|
||||
@ -914,7 +921,7 @@ void WebSocketsServer::begin(void) {
|
||||
}
|
||||
|
||||
void WebSocketsServer::close(void) {
|
||||
WebSocketsServer::close();
|
||||
WebSocketsServerCore::close();
|
||||
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||
_server->close();
|
||||
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||
|
@ -98,6 +98,8 @@ class WebSocketsServerCore : protected WebSockets {
|
||||
void loop(void); // handle client data only
|
||||
#endif
|
||||
|
||||
WSclient_t * newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
|
||||
|
||||
protected:
|
||||
String _origin;
|
||||
String _protocol;
|
||||
@ -116,8 +118,6 @@ class WebSocketsServerCore : protected WebSockets {
|
||||
uint32_t _pongTimeout;
|
||||
uint8_t _disconnectTimeoutCount;
|
||||
|
||||
WSclient_t * newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
|
||||
|
||||
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
|
||||
|
||||
void clientDisconnect(WSclient_t * client);
|
||||
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
* @file WebSocketsVersion.h
|
||||
* @date 07.01.2021
|
||||
* @date 05.04.2022
|
||||
* @author Markus Sattler
|
||||
*
|
||||
* Copyright (c) 2015 Markus Sattler. All rights reserved.
|
||||
@ -25,12 +25,12 @@
|
||||
#ifndef WEBSOCKETSVERSION_H_
|
||||
#define WEBSOCKETSVERSION_H_
|
||||
|
||||
#define WEBSOCKETS_VERSION "2.3.2"
|
||||
#define WEBSOCKETS_VERSION "2.3.7"
|
||||
|
||||
#define WEBSOCKETS_VERSION_MAJOR 2
|
||||
#define WEBSOCKETS_VERSION_MINOR 3
|
||||
#define WEBSOCKETS_VERSION_PATCH 2
|
||||
#define WEBSOCKETS_VERSION_PATCH 7
|
||||
|
||||
#define WEBSOCKETS_VERSION_INT 2003002
|
||||
#define WEBSOCKETS_VERSION_INT 2003007
|
||||
|
||||
#endif /* WEBSOCKETSVERSION_H_ */
|
||||
|
@ -1,5 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -x
|
||||
|
||||
function build_sketches()
|
||||
{
|
||||
local arduino=$1
|
||||
@ -31,7 +33,7 @@ function build_sketch()
|
||||
{
|
||||
local arduino=$1
|
||||
local sketch=$2
|
||||
$arduino --verify $sketch;
|
||||
$arduino --verify --verbose $sketch;
|
||||
local result=$?
|
||||
if [ $result -ne 0 ]; then
|
||||
echo "Build failed ($sketch) build verbose..."
|
||||
@ -97,6 +99,7 @@ function get_core()
|
||||
cd esp8266com
|
||||
git clone --depth 1 https://github.com/esp8266/Arduino.git esp8266
|
||||
cd esp8266/
|
||||
git submodule update --init
|
||||
rm -rf .git
|
||||
cd tools
|
||||
python get.py
|
||||
|
Reference in New Issue
Block a user