Merge branch 'ver4dev' into ver4dev

This commit is contained in:
Mit4el
2025-03-10 22:40:53 +03:00
committed by GitHub
201 changed files with 21245 additions and 1364 deletions

55
.github/workflows/build_iotm.yml vendored Normal file
View File

@@ -0,0 +1,55 @@
env:
BOARDS: '["esp8266_4mb", "esp8266_16mb", "esp32_4mb3f", "esp32c3m_4mb", "esp32s2_4mb", "esp32s3_16mb"]'
name: Build Firmware
on:
workflow_dispatch:
jobs:
generate-matrix:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.set_matrix.outputs.json }}
steps:
- name: Prepare matrix JSON Object
id: set_matrix
uses: nickofthyme/object-remap@v2.0.0
with:
__case: kebab
board: ${{ env.BOARDS }}
build:
needs: [ generate-matrix ]
runs-on: ubuntu-latest
strategy:
matrix: ${{ fromJSON(needs.generate-matrix.outputs.matrix) }}
steps:
- uses: actions/checkout@v2
with:
ref: 'ver4dev'
- name: Run PrepareProject.py -b ${{ matrix.board }}
run: python3 ./PrepareProject.py -b ${{ matrix.board }}
- name: Set up Python
uses: actions/setup-python@v4
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install platformio
- name: Run PlatformIO
if: always()
run: platformio run
- name: Build FS
if: always()
run: platformio run -t buildfs --disable-auto-clean
- name: Rearrange Artifacts
run: |
mkdir -p artifacts/${{ matrix.board }}
find .pio/build/${{ matrix.board }} -name "*.bin" -exec mv {} artifacts/${{ matrix.board }} \;
working-directory: ${{ github.workspace }}
- name: Attach artifact
uses: actions/upload-artifact@v4
with:
name: firmware-${{ matrix.board }}-${{ github.run_number }}
path: artifacts/${{ matrix.board }}

View File

@@ -41,7 +41,22 @@ def printHelp():
PrepareProject.py
-p --profile <file.json_in_root_folder>
-u --update
-h --help''')
-h --help
-b --board <board_name>''')
with open('myProfile.json', "r", encoding='utf-8') as read_file:
profJson = json.load(read_file)
print ('')
print ('Choose a board from the list:')
# print(profJson['projectProp']['platformio']['comments_default_envs'])
print (' ', end='')
cnt = 0
for envs in profJson['projectProp']['platformio']['envs']:
if cnt == 5:
cnt = 0
print('')
print(' ', end='')
print(envs['name'] + ', ', end='')
cnt = cnt + 1
def updateModulesInProfile(profJson):
@@ -68,10 +83,11 @@ def updateModulesInProfile(profJson):
update = False # признак необходимости обновить список модулей
profile = 'myProfile.json' # имя профиля. Будет заменено из консоли, если указано при старте
selectDevice = '' # имя платы для которой хотим собрать, если её указали к командной строке -b <board>
argv = sys.argv[1:]
try:
opts, args = getopt.getopt(argv, 'hp:u', ['help', 'profile=', 'update'])
opts, args = getopt.getopt(argv, 'hp:ub:', ['help', 'profile=', 'update', 'board='])
except getopt.GetoptError:
print('Ошибка обработки параметров!')
printHelp()
@@ -82,10 +98,14 @@ for opt, arg in opts:
printHelp()
sys.exit()
elif opt in ("-p", "--profile"):
print('Загрузка профиля из файла:' + arg)
profile = arg
elif opt in ("-u", "--update"):
print('Создание новой конфигурации по исходным файлам!')
update = True
elif opt in ("-b", "--board"):
print('Создание профиля для платы:' + arg)
selectDevice = arg
if Path(profile).is_file():
# подтягиваем уже существующий профиль
@@ -121,9 +141,17 @@ else:
with open(profile, "w", encoding='utf-8') as write_file:
json.dump(profJson, write_file, ensure_ascii=False, indent=4, sort_keys=False)
deviceName = ''
if selectDevice == '':
# определяем какое устройство используется в профиле
deviceName = profJson['projectProp']['platformio']['default_envs']
else:
for envs in profJson['projectProp']['platformio']['envs']:
if envs['name'] == selectDevice:
deviceName = selectDevice
if deviceName == '':
deviceName = profJson['projectProp']['platformio']['default_envs']
print(f"\x1b[1;31;31m Board ", selectDevice, " not found in ",profile,"!!! Use ",deviceName," \x1b[0m")
# заполняем папку /data файлами прошивки в зависимости от устройства
if deviceName == 'esp8266_1mb_ota' or deviceName == 'esp8285_1mb_ota' or deviceName == 'esp8266_2mb_ota':
@@ -134,7 +162,8 @@ else:
deviceType = 'esp32*'
if not 'esp32' in deviceName:
deviceType = 'esp82*'
if 'bk72' in deviceName:
deviceType = 'bk72*'
# генерируем файлы проекта на основе подготовленного профиля
# заполняем конфигурационный файл прошивки параметрами из профиля
with open("data_svelte/settings.json", "r", encoding='utf-8') as read_file:

Binary file not shown.

Binary file not shown.

View File

@@ -14,6 +14,7 @@
"mqttUser": "rise",
"mqttPass": "3hostel3",
"serverip": "http://iotmanager.org",
"serverlocal": "http://192.168.1.2:5500",
"log": 0,
"mqttin": 0,
"i2c": 0,
@@ -21,6 +22,7 @@
"pinSDA": 0,
"i2cFreq": 100000,
"wg": "group1",
"debugTraceMsgTlgrm": 1,
"udps": 1,
"settings_": ""
}

View File

@@ -325,5 +325,12 @@
{
"name": "nil",
"label": "Без виджета"
},
{
"name": "anydataBar",
"label": "давление Bar",
"widget": "anydata",
"after": "Kg/cm²",
"icon": "speedometer"
}
]

View File

@@ -32,6 +32,10 @@
#define FIRMWARE_NAME "esp32_4mb"
#endif
#ifdef esp32_4mb3f
#define FIRMWARE_NAME "esp32_4mb3f"
#endif
#ifdef esp32cam_4mb
#define FIRMWARE_NAME "esp32cam_4mb"
#endif
@@ -52,6 +56,18 @@
#define FIRMWARE_NAME "esp32s3_16mb"
#endif
#ifdef bk7231n
#define FIRMWARE_NAME "bk7231n"
#endif
#ifdef esp32c6_4mb
#define FIRMWARE_NAME "esp32c6_4mb"
#endif
#ifdef esp32c6_8mb
#define FIRMWARE_NAME "esp32c6_8mb"
#endif
// Размер буфера json
#define JSON_BUFFER_SIZE 4096 // держим 2 кб не меняем
@@ -72,8 +88,9 @@ WEB_SOCKETS_FRAME_SIZE создан для того что бы не загру
#define STANDARD_WEB_SERVER
#define STANDARD_WEB_SOCKETS
//#ifndef LIBRETINY
#define UDP_ENABLED
//#endif
// #define REST_FILE_OPERATIONS
#define MQTT_RECONNECT_INTERVAL 20000
@@ -91,22 +108,26 @@ WEB_SOCKETS_FRAME_SIZE создан для того что бы не загру
enum TimerTask_t {
WIFI_SCAN,
WIFI_MQTT_CONNECTION_CHECK,
#ifndef ESP8266
WIFI_CONN,
#endif
TIME,
TIME_SYNC,
UPTIME,
UDP, // UDPP
// TIME_SYNC, // не используется
// UPTIME, // не используется
UDPt, // UDPP
TIMES, // периодические секундные проверки
PTASK,
ST,
PiWS,
END
};
// задачи которые надо протащить через loop
enum NotAsyncActions {
do_ZERO,
do_MQTTPARAMSCHANGED,
do_LAST,
};
// задачи которые надо протащить через loop // не используется
// enum NotAsyncActions {
// do_ZERO,
// do_MQTTPARAMSCHANGED,
// do_LAST,
// };
// состояния обновления
enum UpdateStates { UPDATE_COMPLETED, UPDATE_FAILED, PATH_ERROR };
@@ -114,7 +135,7 @@ enum UpdateStates { UPDATE_COMPLETED, UPDATE_FAILED, PATH_ERROR };
enum distination {
TO_MQTT,
TO_WS,
TO_MQTT_WS,
TO_MQTT_WS
};
#define WS_BROADCAST -1
// #define WS_BROADCAST -1 // не используется

34
include/DebugTrace.h Normal file
View File

@@ -0,0 +1,34 @@
#pragma once
// В папке toolchchain с которым собирались
// (Для esp32 например %%USERPROFILE%/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin)
// из командной строки Windows (cmd) запустить файл c параметрами:
// xtensa-esp32-elf-addr2line.exe -pfiaC -e Путь_к_файлу/firmware.elf Стэк_адресов_из_сообщения
// %%USERPROFILE%/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin xtensa-esp32-elf-addr2line.exe -pfiaC -e .pio/build/esp32_4mb3f/firmware.elf Стэк_адресов
#include "Global.h"
#if defined(ESP32) && !defined(esp32c3m_4mb) && !defined(esp32c6_4mb) && !defined(esp32c6_8mb)
#define RESTART_DEBUG_INFO
#endif
#if defined(RESTART_DEBUG_INFO)
#define CONFIG_RESTART_DEBUG_STACK_DEPTH 15
typedef struct {
size_t heap_total;
size_t heap_free;
size_t heap_free_min;
time_t heap_min_time;
uint32_t backtrace[CONFIG_RESTART_DEBUG_STACK_DEPTH];
} re_restart_debug_t;
__NOINIT_ATTR static int8_t bootloop_panic_count;
void IRAM_ATTR debugUpdate();
#endif // RESTART_DEBUG_INFO
extern "C" void __real_esp_panic_handler(void*);
void printDebugTrace();
void sendDebugTraceAndFreeMemory(bool);
void startWatchDog();
//extern "C" bool verifyRollbackLater();
void verifyFirmware();

View File

@@ -1,13 +1,15 @@
#pragma once
#include "Global.h"
#ifdef ESP8266
#if defined (ESP8266) || defined(LIBRETINY)
// эта библиотека встроена в ядро
#include "ESPAsyncUDP.h"
#else
#elif defined(ESP32)
#include "AsyncUDP.h"
#endif
#ifndef LIBRETINY
extern AsyncUDP asyncUdp;
#endif
extern const String getThisDevice();
extern void addThisDeviceToList();

View File

@@ -6,4 +6,4 @@ extern std::list<IoTItem*> IoTItems; // вектор ссылок базово
extern void configure(String path);
void clearConfigure();
extern IoTItem* myIoTItem;
// extern IoTItem* myIoTItem; // экономим память, используется в одном месте

View File

@@ -15,6 +15,18 @@ extern FS* filesystem;
#endif
#endif
#if defined(LIBRETINY)
#include <FS.h>
#include "LittleFS.h"
#define FileFS LittleFS
#define FS_NAME "LittleFS_LT"
#define FILE_READ "r"
#define FILE_WRITE "w"
#define FILE_APPEND "a"
#endif
#ifdef ESP8266
#if USE_LITTLEFS
#include "LittleFS.h"
@@ -39,6 +51,7 @@ extern void globalVarsSync();
// extern String getParamsJson();
extern void syncSettingsFlashJson();
void resetSettingsFlashByPanic();
extern void syncValuesFlashJson();
extern const String getChipId();

View File

@@ -9,6 +9,15 @@
#include <PubSubClient.h>
#include <list>
#ifdef LIBRETINY
#include <vector>
#include <typedef.h>
#ifdef STANDARD_WEB_SERVER
#include <WebServer.h>
#endif
#include <HTTPClient.h>
#endif
#ifdef ESP32
#include "WiFi.h"
#include <HTTPClient.h>
@@ -22,6 +31,7 @@
#ifdef ASYNC_WEB_SERVER
#include <ESPAsyncWebServer.h>
#include "AsyncWebServer.h"
#endif
#ifdef STANDARD_WEB_SERVER
@@ -50,7 +60,7 @@
#include "utils/StringUtils.h"
#include "PeriodicTasks.h"
#include "classes/IoTGpio.h"
#include "classes/IoTDiscovery.h"
/*********************************************************************************************************************
*****************************************глобальные объекты классов***************************************************
**********************************************************************************************************************/
@@ -60,6 +70,9 @@ extern IoTItem* rtcItem;
extern IoTItem* tlgrmItem;
extern IoTBench* benchLoadItem;
extern IoTBench* benchTaskItem;
extern IoTDiscovery* HADiscovery;
extern IoTDiscovery* HOMEdDiscovery;
extern TickerScheduler ts;
extern WiFiClient espClient;
@@ -77,6 +90,9 @@ extern ESP8266HTTPUpdateServer httpUpdater;
#ifdef ESP32
extern WebServer HTTP;
#endif
#ifdef LIBRETINY
extern WebServer HTTP;
#endif
#endif
#ifdef STANDARD_WEB_SOCKETS
@@ -108,6 +124,7 @@ extern int mqttPort;
extern String mqttPrefix;
extern String mqttUser;
extern String mqttPass;
extern String nameId;
extern unsigned long mqttUptime;
extern unsigned long flashWriteNumber;
@@ -146,6 +163,8 @@ extern Time_t _time_local;
extern Time_t _time_utc;
extern bool _time_isTrust;
#define WEBSOCKETS_CLIENT_MAX 5
extern int8_t ws_clients[WEBSOCKETS_CLIENT_MAX];
// extern unsigned long loopPeriod;
// extern DynamicJsonDocument settingsFlashJsonDoc;

View File

@@ -29,8 +29,8 @@ bool publishChartFileToMqtt(String path, String id, int maxCount);
void publishWidgets();
void mqttCallback(char* topic, uint8_t* payload, size_t length);
void handleMqttStatus(bool send);
void handleMqttStatus(bool send, int state);
//void handleMqttStatus(bool send);
void handleMqttStatus(bool send, int state = -1);
const String getStateStr(int e);

View File

@@ -9,6 +9,8 @@ extern bool handleFileRead(String path);
extern void handleFileUpload();
extern void handleFileDelete();
extern void handleFileCreate();
extern void handleLocalOTA();
extern void handleLocalOTA_Handler();
extern void handleFileList();
//void printDirectory(File dir, String& out);
extern void handleStatus();

View File

@@ -3,7 +3,7 @@
// #include "Upgrade.h"
#ifdef ESP8266
// #include "ESP8266.h"
#else
#elif ESP32
#include <HTTPUpdate.h>
#endif
@@ -22,7 +22,7 @@ extern bool upgradeFS(String path);
extern bool upgradeBuild(String path);
extern void restartEsp();
extern const String getBinPath(String file);
extern const String getBinPath();
extern void putUserDataToRam();
extern void saveUserDataToFlash();
extern void saveUpdeteStatus(String key, int val);

View File

@@ -19,6 +19,6 @@ void periodicWsSend();
void sendFileToWsByFrames(const String& filename, const String& header, const String& json, int client_id, size_t frameSize);
void sendStringToWs(const String& header, String& payload, int client_id);
void disconnectWSClient(uint8_t client_id);
void sendDeviceList(uint8_t num);
int getNumWSClients();

View File

@@ -0,0 +1,36 @@
#pragma once
#include <Arduino.h>
#include "Global.h"
#include "classes/IoTItem.h"
class IoTDiscovery : public IoTItem
{
public:
IoTDiscovery(const String &parameters);
~IoTDiscovery();
// inline bool isDiscoveryHomed() { return HOMEd; }
// inline bool isDiscoveryHA() { return HA; }
String HOMEdTopic = "";
String HATopic = "";
//String ChipId = "";
virtual void mqttSubscribeDiscovery();
virtual void publishStatusHOMEd(const String &topic, const String &data);
protected:
boolean publishRetain(const String &topic, const String &data);
virtual void getlayoutHA();
virtual void deleteFromHOMEd();
virtual void getlayoutHOMEd();
//bool HOMEd = false;
//bool HA = false;
//String HOMEdTopic;
};

View File

@@ -4,6 +4,7 @@
//#include "classes/IoTBench.h"
class IoTBench;
class IoTDiscovery;
struct IoTValue {
float valD = 0;
@@ -60,6 +61,8 @@ class IoTItem {
//virtual IoTBench* getBenchmark();
virtual IoTBench* getBenchmarkTask();
virtual IoTBench* getBenchmarkLoad();
virtual IoTDiscovery* getHADiscovery();
virtual IoTDiscovery* getHOMEdDiscovery();
virtual unsigned long getRtcUnixTime();
// делаем доступным модулям отправку сообщений в телеграм

View File

@@ -40,7 +40,7 @@ boolean isDigitDotCommaStr(const String& str);
String prettyBytes(size_t size);
String uint64ToString(uint64_t input, uint8_t base = 10);
String uint64ToStringIoTM(uint64_t input, uint8_t base = 10);
void cleanString(String& str);

View File

@@ -4,8 +4,20 @@
#include "MqttClient.h"
boolean isNetworkActive();
uint8_t getNumAPClients();
void routerConnect();
bool startAPMode();
#ifdef ESP8266
void routerConnect();
boolean RouterFind(std::vector<String> jArray);
#else
void handleScanResults();
void WiFiUtilsItit();
void connectToNextNetwork();
void checkConnection();
void ScanAsync();
#endif
uint8_t RSSIquality();
extern void wifiSignalInit();
//extern void wifiSignalInit();
#ifdef LIBRETINY
String httpGetString(HTTPClient &http);
#endif

View File

@@ -0,0 +1,63 @@
---
BasedOnStyle: Google
AccessModifierOffset: '-2'
AlignAfterOpenBracket: DontAlign
AlignConsecutiveAssignments: 'true'
AlignConsecutiveDeclarations: 'false'
AlignEscapedNewlines: Left
AlignTrailingComments: 'true'
AllowAllParametersOfDeclarationOnNextLine: 'false'
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: InlineOnly
AllowShortIfStatementsOnASingleLine: 'true'
AllowShortLoopsOnASingleLine: 'true'
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: 'true'
AlwaysBreakTemplateDeclarations: 'false'
BinPackParameters: 'true'
BreakAfterJavaFieldAnnotations: 'false'
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Attach
BreakBeforeInheritanceComma: 'false'
BreakBeforeTernaryOperators: 'false'
BreakConstructorInitializers: BeforeColon
BreakStringLiterals: 'false'
ColumnLimit: '0'
CompactNamespaces: 'true'
ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'
ConstructorInitializerIndentWidth: '4'
ContinuationIndentWidth: '4'
Cpp11BracedListStyle: 'false'
DerivePointerAlignment: 'false'
FixNamespaceComments: 'true'
IndentCaseLabels: 'true'
IndentWidth: '4'
IndentWrappedFunctionNames: 'false'
JavaScriptQuotes: Single
JavaScriptWrapImports: 'false'
KeepEmptyLinesAtTheStartOfBlocks: 'false'
MaxEmptyLinesToKeep: '1'
NamespaceIndentation: All
ObjCBlockIndentWidth: '4'
ObjCSpaceAfterProperty: 'false'
ObjCSpaceBeforeProtocolList: 'false'
PointerAlignment: Middle
SortIncludes: 'false'
SortUsingDeclarations: 'true'
SpaceAfterCStyleCast: 'false'
SpaceAfterTemplateKeyword: 'false'
SpaceBeforeAssignmentOperators: 'true'
SpaceBeforeParens: Never
SpaceInEmptyParentheses: 'false'
SpacesBeforeTrailingComments: '4'
SpacesInAngles: 'false'
SpacesInCStyleCastParentheses: 'false'
SpacesInContainerLiterals: 'false'
SpacesInParentheses: 'false'
SpacesInSquareBrackets: 'false'
TabWidth: '4'
UseTab: Never
...

View File

@@ -0,0 +1,186 @@
name: CI
on:
schedule:
- cron: '0 0 * * 5'
push:
branches: [ master ]
pull_request:
branches: [ master ]
release:
types: [ published, created, edited ]
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
check_version_files:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: check version
run: |
$GITHUB_WORKSPACE/travis/version.py --check
prepare_example_json:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: generate examples
id: set-matrix
run: |
source $GITHUB_WORKSPACE/travis/common.sh
cd $GITHUB_WORKSPACE
echo -en "::set-output name=matrix::"
echo -en "["
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp8266 esp8266 1.8.19 esp8266com:esp8266:generic:xtal=80,dbg=Serial1
echo -en ","
get_sketches_json_matrix arduino $GITHUB_WORKSPACE/examples/esp8266 esp8266 1.8.19 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.19 espressif:esp32:esp32:FlashFreq=80
echo -en "]"
outputs:
matrix: ${{ steps.set-matrix.outputs.matrix }}
prepare_ide:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
IDE_VERSION: [1.8.19]
env:
IDE_VERSION: ${{ matrix.IDE_VERSION }}
steps:
- uses: actions/checkout@v2
- name: Get Date
id: get-date
run: |
echo "::set-output name=date::$(/bin/date -u "+%Y%m%d")"
shell: bash
- uses: actions/cache@v2
id: cache_all
with:
path: |
/home/runner/arduino_ide
/home/runner/Arduino
key: ${{ runner.os }}-${{ steps.get-date.outputs.date }}-${{ matrix.IDE_VERSION }}
- name: download IDE
if: steps.cache_all.outputs.cache-hit != 'true'
run: |
wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz -q
tar xf arduino-$IDE_VERSION-linux64.tar.xz
mv arduino-$IDE_VERSION $HOME/arduino_ide
- name: download ArduinoJson
if: steps.cache_all.outputs.cache-hit != 'true'
run: |
mkdir -p $HOME/Arduino/libraries
wget https://github.com/bblanchon/ArduinoJson/archive/6.x.zip -q
unzip 6.x.zip
mv ArduinoJson-6.x $HOME/Arduino/libraries/ArduinoJson
- name: download esp8266
if: steps.cache_all.outputs.cache-hit != 'true'
run: |
source $GITHUB_WORKSPACE/travis/common.sh
get_core esp8266
- name: download esp32
if: steps.cache_all.outputs.cache-hit != 'true'
run: |
source $GITHUB_WORKSPACE/travis/common.sh
get_core esp32
build:
needs: [prepare_ide, prepare_example_json]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.prepare_example_json.outputs.matrix) }}
env:
CPU: ${{ matrix.cpu }}
BOARD: ${{ matrix.board }}
IDE_VERSION: ${{ matrix.ideversion }}
SKETCH: ${{ matrix.sketch }}
# Steps represent a sequence of tasks that will be executed as part of the job
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: |
echo "::set-output name=date::$(/bin/date -u "+%Y%m%d")"
shell: bash
- uses: actions/cache@v2
id: cache_all
with:
path: |
/home/runner/arduino_ide
/home/runner/Arduino
key: ${{ runner.os }}-${{ steps.get-date.outputs.date }}-${{ matrix.ideversion }}
- name: install python serial
if: matrix.cpu == 'esp32'
run: |
sudo pip3 install pyserial
sudo pip install pyserial
# sudo apt install python-is-python3
- name: start DISPLAY
run: |
/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
export DISPLAY=:1.0
sleep 3
- name: test IDE
run: |
export PATH="$HOME/arduino_ide:$PATH"
which arduino
- name: copy code
run: |
mkdir -p $HOME/Arduino/libraries/
cp -r $GITHUB_WORKSPACE $HOME/Arduino/libraries/arduinoWebSockets
- name: config IDE
run: |
export DISPLAY=:1.0
export PATH="$HOME/arduino_ide:$PATH"
arduino --board $BOARD --save-prefs
arduino --get-pref sketchbook.path
arduino --pref update.check=false
- 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
cd $GITHUB_WORKSPACE
build_sketch arduino $SKETCH
done:
needs: [prepare_ide, prepare_example_json, build, check_version_files]
runs-on: ubuntu-latest
steps:
- name: Done
run: |
echo DONE

37
lib/LT_WebSockets/.gitignore vendored Normal file
View File

@@ -0,0 +1,37 @@
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
# Executables
*.exe
*.out
*.app
/tests/webSocketServer/node_modules
# IDE
.vscode
.cproject
.project
.settings
*.swp

1
lib/LT_WebSockets/.piopm Normal file
View File

@@ -0,0 +1 @@
{"type": "library", "name": "WebSockets", "version": "2.3.7", "spec": {"owner": "links2004", "id": 549, "name": "WebSockets", "requirements": null, "uri": null}}

View File

@@ -0,0 +1,45 @@
sudo: false
dist:
- xenial
addons:
apt:
packages:
- xvfb
language: bash
os:
- linux
env:
matrix:
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80" IDE_VERSION=1.6.13
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,dbg=Serial1" IDE_VERSION=1.6.13
- CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,eesz=1M,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.13
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.5
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.9
- CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.13
script:
- /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
- export DISPLAY=:1.0
- sleep 3
- wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz
- tar xf arduino-$IDE_VERSION-linux64.tar.xz
- mv arduino-$IDE_VERSION $HOME/arduino_ide
- export PATH="$HOME/arduino_ide:$PATH"
- which arduino
- mkdir -p $HOME/Arduino/libraries
- wget https://github.com/bblanchon/ArduinoJson/archive/6.x.zip
- unzip 6.x.zip
- mv ArduinoJson-6.x $HOME/Arduino/libraries/ArduinoJson
- cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/arduinoWebSockets
- source $TRAVIS_BUILD_DIR/travis/common.sh
- get_core $CPU
- cd $TRAVIS_BUILD_DIR
- arduino --board $BOARD --save-prefs
- arduino --get-pref sketchbook.path
- arduino --pref update.check=false
- build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets/examples/$CPU $CPU
notifications:
email:
on_success: change
on_failure: change

502
lib/LT_WebSockets/LICENSE Normal file
View File

@@ -0,0 +1,502 @@
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!

102
lib/LT_WebSockets/README.md Normal file
View File

@@ -0,0 +1,102 @@
WebSocket Server and Client for Arduino [![Build Status](https://github.com/Links2004/arduinoWebSockets/workflows/CI/badge.svg?branch=master)](https://github.com/Links2004/arduinoWebSockets/actions?query=workflow%3ACI+branch%3Amaster)
===========================================
a WebSocket Server and Client for Arduino based on RFC6455.
##### Supported features of RFC6455 #####
- text frame
- binary frame
- connection close
- ping
- pong
- continuation frame
##### Limitations #####
- max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define
- max output length has no limit (the hardware is the limit)
- Client send big frames with mask 0x00000000 (on AVR all frames)
- continuation frame reassembly need to be handled in the application code
##### Limitations for Async #####
- Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround.
- wss / SSL is not possible.
##### Supported Hardware #####
- ESP8266 [Arduino for ESP8266](https://github.com/esp8266/Arduino/)
- ESP32 [Arduino for ESP32](https://github.com/espressif/arduino-esp32)
- ESP31B
- Particle with STM32 ARM Cortex M3
- ATmega328 with Ethernet Shield (ATmega branch)
- ATmega328 with enc28j60 (ATmega branch)
- ATmega2560 with Ethernet Shield (ATmega branch)
- ATmega2560 with enc28j60 (ATmega branch)
###### Note: ######
version 2.0.0 and up is not compatible with AVR/ATmega, check ATmega branch.
version 2.3.0 has API changes for the ESP8266 BareSSL (may brakes existing code)
Arduino for AVR not supports std namespace of c++.
### wss / SSL ###
supported for:
- wss client on the ESP8266
- wss / SSL is not natively supported in WebSocketsServer however it is possible to achieve secure websockets
by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a
sample Nginx server configuration file to enable this.
### ESP Async TCP ###
This libary can run in Async TCP mode on the ESP.
The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define).
[ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required.
### High Level Client API ###
- `begin` : Initiate connection sequence to the websocket host.
```c++
void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
```
- `onEvent`: Callback to handle for websocket events
```c++
void onEvent(WebSocketClientEvent cbEvent);
```
- `WebSocketClientEvent`: Handler for websocket events
```c++
void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length)
```
Where `WStype_t type` is defined as:
```c++
typedef enum {
WStype_ERROR,
WStype_DISCONNECTED,
WStype_CONNECTED,
WStype_TEXT,
WStype_BIN,
WStype_FRAGMENT_TEXT_START,
WStype_FRAGMENT_BIN_START,
WStype_FRAGMENT,
WStype_FRAGMENT_FIN,
WStype_PING,
WStype_PONG,
} WStype_t;
```
### Issues ###
Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues
[![Join the chat at https://gitter.im/Links2004/arduinoWebSockets](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Links2004/arduinoWebSockets?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
### License and credits ###
The library is licensed under [LGPLv2.1](https://github.com/Links2004/arduinoWebSockets/blob/master/LICENSE)
[libb64](http://libb64.sourceforge.net/) written by Chris Venter. It is distributed under Public Domain see [LICENSE](https://github.com/Links2004/arduinoWebSockets/blob/master/src/libb64/LICENSE).

View File

@@ -0,0 +1,83 @@
# ESP8266 nginx SSL reverse proxy configuration file (tested and working on nginx v1.10.0)
# proxy cache location
proxy_cache_path /opt/etc/nginx/cache levels=1:2 keys_zone=ESP8266_cache:10m max_size=10g inactive=5m use_temp_path=off;
# webserver proxy
server {
# general server parameters
listen 50080;
server_name myDomain.net;
access_log /opt/var/log/nginx/myDomain.net.access.log;
# SSL configuration
ssl on;
ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem;
ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
location / {
# proxy caching configuration
proxy_cache ESP8266_cache;
proxy_cache_revalidate on;
proxy_cache_min_uses 1;
proxy_cache_use_stale off;
proxy_cache_lock on;
# proxy_cache_bypass $http_cache_control;
# include the sessionId cookie value as part of the cache key - keeps the cache per user
# proxy_cache_key $proxy_host$request_uri$cookie_sessionId;
# header pass through configuration
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# ESP8266 custom headers which identify to the device that it's running through an SSL proxy
proxy_set_header X-SSL On;
proxy_set_header X-SSL-WebserverPort 50080;
proxy_set_header X-SSL-WebsocketPort 50081;
# extra debug headers
add_header X-Proxy-Cache $upstream_cache_status;
add_header X-Forwarded-For $proxy_add_x_forwarded_for;
# actual proxying configuration
proxy_ssl_session_reuse on;
# target the IP address of the device with proxy_pass
proxy_pass http://192.168.0.20;
proxy_read_timeout 90;
}
}
# websocket proxy
server {
# general server parameters
listen 50081;
server_name myDomain.net;
access_log /opt/var/log/nginx/myDomain.net.wss.access.log;
# SSL configuration
ssl on;
ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem;
ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem;
ssl_session_cache builtin:1000 shared:SSL:10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
ssl_prefer_server_ciphers on;
location / {
# websocket upgrade tunnel configuration
proxy_pass http://192.168.0.20:81;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_read_timeout 86400;
}
}

View File

@@ -0,0 +1,84 @@
/*
* WebSocketClientAVR.ino
*
* Created on: 10.12.2015
*
*/
#include <Arduino.h>
#include <SPI.h>
#include <Ethernet.h>
#include <WebSocketsClient.h>
// Enter a MAC address for your controller below.
// Newer Ethernet shields have a MAC address printed on a sticker on the shield
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
// Set the static IP address to use if the DHCP fails to assign
IPAddress ip(192, 168, 0, 177);
WebSocketsClient webSocket;
void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
Serial.println("[WSc] Disconnected!\n");
break;
case WStype_CONNECTED:
{
Serial.print("[WSc] Connected to url: ");
Serial.println((char *)payload);
// send message to server when Connected
webSocket.sendTXT("Connected");
}
break;
case WStype_TEXT:
Serial.print("[WSc] get text: ");
Serial.println((char *)payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
Serial.print("[WSc] get binary length: ");
Serial.println(length);
// hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup()
{
// Open serial communications and wait for port to open:
Serial.begin(115200);
while (!Serial) {}
// start the Ethernet connection:
if (Ethernet.begin(mac) == 0) {
Serial.println("Failed to configure Ethernet using DHCP");
// no point in carrying on, so do nothing forevermore:
// try to congifure using IP address instead of DHCP:
Ethernet.begin(mac, ip);
}
webSocket.begin("192.168.0.123", 8011);
webSocket.onEvent(webSocketEvent);
}
void loop()
{
webSocket.loop();
}

View File

@@ -0,0 +1,110 @@
/*
* WebSocketClient.ino
*
* Created on: 24.05.2015
*
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClientSecure.h>
#include <WebSocketsClient.h>
WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial1
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
const uint8_t* src = (const uint8_t*) mem;
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
for(uint32_t i = 0; i < len; i++) {
if(i % cols == 0) {
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
}
USE_SERIAL.printf("%02X ", *src);
src++;
}
USE_SERIAL.printf("\n");
}
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");
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
case WStype_ERROR:
case WStype_FRAGMENT_TEXT_START:
case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT:
case WStype_FRAGMENT_FIN:
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);
}
// server address, port and URL
webSocket.begin("192.168.0.123", 81, "/");
// event handler
webSocket.onEvent(webSocketEvent);
// use HTTP Basic Authorization this is optional remove if not needed
webSocket.setAuthorization("user", "Password");
// try ever 5000 again if connection has failed
webSocket.setReconnectInterval(5000);
}
void loop() {
webSocket.loop();
}

View File

@@ -0,0 +1,106 @@
/*
* WebSocketClientSSL.ino
*
* Created on: 10.12.2015
*
* note SSL is only possible with the ESP8266
*
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClientSecure.h>
#include <WebSocketsClient.h>
WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial1
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
const uint8_t* src = (const uint8_t*) mem;
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
for(uint32_t i = 0; i < len; i++) {
if(i % cols == 0) {
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
}
USE_SERIAL.printf("%02X ", *src);
src++;
}
USE_SERIAL.printf("\n");
}
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");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
case WStype_ERROR:
case WStype_FRAGMENT_TEXT_START:
case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT:
case WStype_FRAGMENT_FIN:
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);
}
webSocket.beginSSL("192.168.0.123", 81);
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,104 @@
/*
* WebSocketServer.ino
*
* Created on: 22.05.2015
*
*/
#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClientSecure.h>
#include <WebSocketsServer.h>
WiFiMulti WiFiMulti;
WebSocketsServer webSocket = WebSocketsServer(81);
#define USE_SERIAL Serial1
void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
const uint8_t* src = (const uint8_t*) mem;
USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
for(uint32_t i = 0; i < len; i++) {
if(i % cols == 0) {
USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
}
USE_SERIAL.printf("%02X ", *src);
src++;
}
USE_SERIAL.printf("\n");
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED:
{
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
// send message to client
// webSocket.sendTXT(num, "message here");
// send data to all connected clients
// webSocket.broadcastTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
hexdump(payload, length);
// send message to client
// webSocket.sendBIN(num, payload, length);
break;
case WStype_ERROR:
case WStype_FRAGMENT_TEXT_START:
case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT:
case WStype_FRAGMENT_FIN:
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");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@@ -0,0 +1,106 @@
/*
* WebSocketClient.ino
*
* Created on: 24.05.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial1
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");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// 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.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);
}
// server address, port and URL
webSocket.begin("192.168.0.123", 81, "/");
// event handler
webSocket.onEvent(webSocketEvent);
// use HTTP Basic Authorization this is optional remove if not needed
webSocket.setAuthorization("user", "Password");
// 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();
}

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

View 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();
}

View 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())

View File

@@ -0,0 +1,2 @@
packaging
websockets

View File

@@ -0,0 +1,88 @@
/*
* WebSocketClientSSL.ino
*
* Created on: 10.12.2015
*
* note SSL is only possible with the ESP8266
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial1
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");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, 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);
}
webSocket.beginSSL("192.168.0.123", 81);
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@@ -0,0 +1,103 @@
/*
* WebSocketClientSSLWithCA.ino
*
* Created on: 27.10.2019
*
* note SSL is only possible with the ESP8266
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsClient.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
#define USE_SERIAL Serial1
// Can be obtained with:
// openssl s_client -showcerts -connect echo.websocket.org:443 </dev/null
const char ENDPOINT_CA_CERT[] PROGMEM = R"EOF(
-----BEGIN CERTIFICATE-----
MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT
DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0NlowSjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT
GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF
q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0
Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWAa6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj
/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG
CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNvbTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k
c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAwVAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC
ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAzMDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu
Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsFAAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo
uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu
X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlGPfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6
KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==
-----END CERTIFICATE-----
)EOF";
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");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
// send message to server
// webSocket.sendTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup() {
USE_SERIAL.begin(115200);
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");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
//When using BearSSL, client certificate and private key can be set:
//webSocket.setSSLClientCertKey(clientCert, clientPrivateKey);
//clientCert and clientPrivateKey can be of types (const char *, const char *) , or of types (BearSSL::X509List, BearSSL::PrivateKey)
webSocket.beginSslWithCA("echo.websocket.org", 443, "/", ENDPOINT_CA_CERT);
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@@ -0,0 +1,128 @@
/*
* WebSocketClientSocketIO.ino
*
* Created on: 06.06.2016
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
SocketIOclient socketIO;
#define USE_SERIAL Serial1
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:
USE_SERIAL.printf("[IOc] get event: %s\n", payload);
break;
case sIOtype_ACK:
USE_SERIAL.printf("[IOc] get ack: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_ERROR:
USE_SERIAL.printf("[IOc] get error: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_BINARY_EVENT:
USE_SERIAL.printf("[IOc] get binary: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_BINARY_ACK:
USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
hexdump(payload, 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);
}
// disable AP
if(WiFi.getMode() & WIFI_AP) {
WiFi.softAPdisconnect(true);
}
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);
}
}

View File

@@ -0,0 +1,165 @@
/*
* WebSocketClientSocketIOack.ino
*
* Created on: 20.07.2019
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>
#include <SocketIOclient.h>
#include <Hash.h>
ESP8266WiFiMulti 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);
hexdump(payload, length);
break;
case sIOtype_ERROR:
USE_SERIAL.printf("[IOc] get error: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_BINARY_EVENT:
USE_SERIAL.printf("[IOc] get binary: %u\n", length);
hexdump(payload, length);
break;
case sIOtype_BINARY_ACK:
USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
hexdump(payload, 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);
}
// disable AP
if(WiFi.getMode() & WIFI_AP) {
WiFi.softAPdisconnect(true);
}
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);
}
}

View File

@@ -0,0 +1,149 @@
/*
WebSocketClientStomp.ino
Example for connecting and maintining a connection with a STOMP websocket connection.
In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html).
Created on: 25.09.2017
Author: Martin Becker <mgbckr>, Contact: becker@informatik.uni-wuerzburg.de
*/
// PRE
#define USE_SERIAL Serial
// LIBRARIES
#include <Arduino.h>
#include <Hash.h>
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
// SETTINGS
const char* wlan_ssid = "yourssid";
const char* wlan_password = "somepassword";
const char* ws_host = "the.host.net";
const int ws_port = 80;
// URL for STOMP endpoint.
// For the default config of Spring's STOMP support, the default URL is "/socketentry/websocket".
const char* stompUrl = "/socketentry/websocket"; // don't forget the leading "/" !!!
// VARIABLES
WebSocketsClient webSocket;
// FUNCTIONS
/**
* STOMP messages need to be NULL-terminated (i.e., \0 or \u0000).
* However, when we send a String or a char[] array without specifying
* a length, the size of the message payload is derived by strlen() internally,
* thus dropping any NULL values appended to the "msg"-String.
*
* To solve this, we first convert the String to a NULL terminated char[] array
* via "c_str" and set the length of the payload to include the NULL value.
*/
void sendMessage(String & msg) {
webSocket.sendTXT(msg.c_str(), msg.length() + 1);
}
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);
String msg = "CONNECT\r\naccept-version:1.1,1.0\r\nheart-beat:10000,10000\r\n\r\n";
sendMessage(msg);
}
break;
case WStype_TEXT:
{
// #####################
// handle STOMP protocol
// #####################
String text = (char*) payload;
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
if (text.startsWith("CONNECTED")) {
// subscribe to some channels
String msg = "SUBSCRIBE\nid:sub-0\ndestination:/user/queue/messages\n\n";
sendMessage(msg);
delay(1000);
// and send a message
msg = "SEND\ndestination:/app/message\n\n{\"user\":\"esp\",\"message\":\"Hello!\"}";
sendMessage(msg);
delay(1000);
} else {
// do something with messages
}
break;
}
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup() {
// setup serial
// USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
// connect to WiFi
USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ...");
WiFi.mode(WIFI_STA);
WiFi.begin(wlan_ssid, wlan_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
USE_SERIAL.print(".");
}
USE_SERIAL.println(" success.");
USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP());
// connect to websocket
webSocket.begin(ws_host, ws_port, stompUrl);
webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
// webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@@ -0,0 +1,150 @@
/*
WebSocketClientStompOverSockJs.ino
Example for connecting and maintining a connection with a SockJS+STOMP websocket connection.
In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html).
Created on: 18.07.2017
Author: Martin Becker <mgbckr>, Contact: becker@informatik.uni-wuerzburg.de
*/
// PRE
#define USE_SERIAL Serial
// LIBRARIES
#include <Arduino.h>
#include <Hash.h>
#include <ESP8266WiFi.h>
#include <WebSocketsClient.h>
// SETTINGS
const char* wlan_ssid = "yourssid";
const char* wlan_password = "somepassword";
const char* ws_host = "the.host.net";
const int ws_port = 80;
// base URL for SockJS (websocket) connection
// The complete URL will look something like this(cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36):
// ws://<ws_host>:<ws_port>/<ws_baseurl>/<3digits>/<randomstring>/websocket
// For the default config of Spring's SockJS/STOMP support, the default base URL is "/socketentry/".
const char* ws_baseurl = "/socketentry/"; // don't forget leading and trailing "/" !!!
// VARIABLES
WebSocketsClient webSocket;
// FUNCTIONS
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);
}
break;
case WStype_TEXT:
{
// #####################
// handle SockJs+STOMP protocol
// #####################
String text = (char*) payload;
USE_SERIAL.printf("[WSc] get text: %s\n", payload);
if (payload[0] == 'h') {
USE_SERIAL.println("Heartbeat!");
} else if (payload[0] == 'o') {
// on open connection
char *msg = "[\"CONNECT\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\"]";
webSocket.sendTXT(msg);
} else if (text.startsWith("a[\"CONNECTED")) {
// subscribe to some channels
char *msg = "[\"SUBSCRIBE\\nid:sub-0\\ndestination:/user/queue/messages\\n\\n\\u0000\"]";
webSocket.sendTXT(msg);
delay(1000);
// and send a message
msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]";
webSocket.sendTXT(msg);
delay(1000);
}
break;
}
case WStype_BIN:
USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
hexdump(payload, length);
// send data to server
// webSocket.sendBIN(payload, length);
break;
}
}
void setup() {
// setup serial
// USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
// USE_SERIAL.setDebugOutput(true);
USE_SERIAL.println();
// connect to WiFi
USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ...");
WiFi.mode(WIFI_STA);
WiFi.begin(wlan_ssid, wlan_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
USE_SERIAL.print(".");
}
USE_SERIAL.println(" success.");
USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP());
// #####################
// create socket url according to SockJS protocol (cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36)
// #####################
String socketUrl = ws_baseurl;
socketUrl += random(0, 999);
socketUrl += "/";
socketUrl += random(0, 999999); // should be a random string, but this works (see )
socketUrl += "/websocket";
// connect to websocket
webSocket.begin(ws_host, ws_port, socketUrl);
webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
// webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@@ -0,0 +1,86 @@
/*
* WebSocketServer.ino
*
* Created on: 22.05.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsServer webSocket = WebSocketsServer(81);
#define USE_SERIAL Serial1
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED:
{
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
// send message to client
// webSocket.sendTXT(num, "message here");
// send data to all connected clients
// webSocket.broadcastTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
hexdump(payload, length);
// send message to client
// webSocket.sendBIN(num, payload, 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");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@@ -0,0 +1,132 @@
/*
* WebSocketServerAllFunctionsDemo.ino
*
* Created on: 10.05.2018
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Hash.h>
#define LED_RED 15
#define LED_GREEN 12
#define LED_BLUE 13
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: {
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
if(payload[0] == '#') {
// we get RGB data
// decode rgb data
uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
analogWrite(LED_RED, ((rgb >> 16) & 0xFF));
analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF));
analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF));
}
break;
}
}
void setup() {
//USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//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);
}
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_RED, 1);
digitalWrite(LED_GREEN, 1);
digitalWrite(LED_BLUE, 1);
WiFiMulti.addAP("SSID", "passpasspass");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
// start webSocket server
webSocket.begin();
webSocket.onEvent(webSocketEvent);
if(MDNS.begin("esp8266")) {
USE_SERIAL.println("MDNS responder started");
}
// handle index
server.on("/", []() {
// send index.html
server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () { connection.send('Connect ' + new Date()); }; connection.onerror = function (error) { console.log('WebSocket Error ', error);};connection.onmessage = function (e) { console.log('Server: ', e.data);};function sendRGB() { var r = parseInt(document.getElementById('r').value).toString(16); var g = parseInt(document.getElementById('g').value).toString(16); var b = parseInt(document.getElementById('b').value).toString(16); if(r.length < 2) { r = '0' + r; } if(g.length < 2) { g = '0' + g; } if(b.length < 2) { b = '0' + b; } var rgb = '#'+r+g+b; console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/></body></html>");
});
server.begin();
// Add service to MDNS
MDNS.addService("http", "tcp", 80);
MDNS.addService("ws", "tcp", 81);
digitalWrite(LED_RED, 0);
digitalWrite(LED_GREEN, 0);
digitalWrite(LED_BLUE, 0);
}
unsigned long last_10sec = 0;
unsigned int counter = 0;
void loop() {
unsigned long t = millis();
webSocket.loop();
server.handleClient();
if((t - last_10sec) > 10 * 1000) {
counter++;
bool ping = (counter % 2);
int i = webSocket.connectedClients(ping);
USE_SERIAL.printf("%d Connected websocket clients ping: %d\n", i, ping);
last_10sec = millis();
}
}

View File

@@ -0,0 +1,94 @@
/*
* WebSocketServer.ino
*
* Created on: 22.05.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsServer webSocket = WebSocketsServer(81);
#define USE_SERIAL Serial
String fragmentBuffer = "";
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: {
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
break;
case WStype_BIN:
USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
hexdump(payload, length);
break;
// Fragmentation / continuation opcode handling
// case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT_TEXT_START:
fragmentBuffer = (char*)payload;
USE_SERIAL.printf("[%u] get start start of Textfragment: %s\n", num, payload);
break;
case WStype_FRAGMENT:
fragmentBuffer += (char*)payload;
USE_SERIAL.printf("[%u] get Textfragment : %s\n", num, payload);
break;
case WStype_FRAGMENT_FIN:
fragmentBuffer += (char*)payload;
USE_SERIAL.printf("[%u] get end of Textfragment: %s\n", num, payload);
USE_SERIAL.printf("[%u] full frame: %s\n", num, fragmentBuffer.c_str());
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");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
webSocket.begin();
webSocket.onEvent(webSocketEvent);
}
void loop() {
webSocket.loop();
}

View File

@@ -0,0 +1,103 @@
/*
* WebSocketServerHooked.ino
*
* Created on: 22.05.2015
* Hooked on: 28.10.2020
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSockets4WebServer.h>
#include <Hash.h>
#include <ESP8266mDNS.h>
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server(80);
WebSockets4WebServer webSocket;
#define USE_SERIAL Serial
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED:
{
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
// send message to client
// webSocket.sendTXT(num, "message here");
// send data to all connected clients
// webSocket.broadcastTXT("message here");
break;
case WStype_BIN:
USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
hexdump(payload, length);
// send message to client
// webSocket.sendBIN(num, payload, 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");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
server.on("/", []() {
server.send(200, "text/plain", "I am a regular webserver on port 80!\r\n");
server.send(200, "text/plain", "I am also a websocket server on '/ws' on the same port 80\r\n");
});
server.addHook(webSocket.hookForWebserver("/ws", webSocketEvent));
server.begin();
Serial.println("HTTP server started on port 80");
Serial.println("WebSocket server started on the same port");
Serial.printf("my network address is either 'arduinoWebsockets.local' (mDNS) or '%s'\n", WiFi.localIP().toString().c_str());
if (!MDNS.begin("arduinoWebsockets")) {
Serial.println("Error setting up MDNS responder!");
}
}
void loop() {
server.handleClient();
webSocket.loop();
MDNS.update();
}

View File

@@ -0,0 +1,20 @@
#!/bin/sh
# linux script to compile&run arduinoWebSockets in a mock environment
if [ -z "$ESP8266ARDUINO" ]; then
echo "please set ESP8266ARDUINO env-var to where esp8266/arduino sits"
exit 1
fi
set -e
where=$(pwd)
cd $ESP8266ARDUINO/tests/host/
make -j FORCE32=0 \
ULIBDIRS=../../libraries/Hash/:~/dev/proj/arduino/libraries/arduinoWebSockets \
${where}/WebSocketServerHooked
valgrind ./bin/WebSocketServerHooked/WebSocketServerHooked -b "$@"

View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python3
# python websocket client to test with
# emulator: server is at ws://127.0.0.1:9080/ws
# esp8266: server is at ws:///ws
# (uncomment the right line below)
#uri = "ws://127.0.0.1:9080/ws"
uri = "ws://arduinoWebsockets.local/ws"
import websocket
try:
import thread
except ImportError:
import _thread as thread
import time
def on_message(ws, message):
print("message");
print(message)
def on_error(ws, error):
print("error")
print(error)
def on_close(ws):
print("### closed ###")
def on_open(ws):
print("opened")
def run(*args):
for i in range(3):
time.sleep(1)
ws.send("Hello %d" % i)
time.sleep(1)
ws.close()
print("thread terminating...")
thread.start_new_thread(run, ())
if __name__ == "__main__":
websocket.enableTrace(True)
ws = websocket.WebSocketApp(uri, on_message = on_message, on_error = on_error, on_close = on_close)
ws.on_open = on_open
ws.run_forever()

View File

@@ -0,0 +1,86 @@
/*
* WebSocketServerHttpHeaderValidation.ino
*
* Created on: 08.06.2016
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <Hash.h>
ESP8266WiFiMulti WiFiMulti;
WebSocketsServer webSocket = WebSocketsServer(81);
#define USE_SERIAL Serial1
const unsigned long int validSessionId = 12345; //some arbitrary value to act as a valid sessionId
/*
* Returns a bool value as an indicator to describe whether a user is allowed to initiate a websocket upgrade
* based on the value of a cookie. This function expects the rawCookieHeaderValue to look like this "sessionId=<someSessionIdNumberValue>|"
*/
bool isCookieValid(String rawCookieHeaderValue) {
if (rawCookieHeaderValue.indexOf("sessionId") != -1) {
String sessionIdStr = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("sessionId=") + 10, rawCookieHeaderValue.indexOf("|"));
unsigned long int sessionId = strtoul(sessionIdStr.c_str(), NULL, 10);
return sessionId == validSessionId;
}
return false;
}
/*
* The WebSocketServerHttpHeaderValFunc delegate passed to webSocket.onValidateHttpHeader
*/
bool validateHttpHeader(String headerName, String headerValue) {
//assume a true response for any headers not handled by this validator
bool valid = true;
if(headerName.equalsIgnoreCase("Cookie")) {
//if the header passed is the Cookie header, validate it according to the rules in 'isCookieValid' function
valid = isCookieValid(headerValue);
}
return valid;
}
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");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
//connecting clients must supply a valid session cookie at websocket upgrade handshake negotiation time
const char * headerkeys[] = { "Cookie" };
size_t headerKeyCount = sizeof(headerkeys) / sizeof(char*);
webSocket.onValidateHttpHeader(validateHttpHeader, headerkeys, headerKeyCount);
webSocket.begin();
}
void loop() {
webSocket.loop();
}

View File

@@ -0,0 +1,121 @@
/*
* WebSocketServer_LEDcontrol.ino
*
* Created on: 26.11.2015
*
*/
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#include <ESP8266mDNS.h>
#include <Hash.h>
#define LED_RED 15
#define LED_GREEN 12
#define LED_BLUE 13
#define USE_SERIAL Serial
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server(80);
WebSocketsServer webSocket = WebSocketsServer(81);
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
USE_SERIAL.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: {
IPAddress ip = webSocket.remoteIP(num);
USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
// send message to client
webSocket.sendTXT(num, "Connected");
}
break;
case WStype_TEXT:
USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
if(payload[0] == '#') {
// we get RGB data
// decode rgb data
uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
analogWrite(LED_RED, ((rgb >> 16) & 0xFF));
analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF));
analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF));
}
break;
}
}
void setup() {
//USE_SERIAL.begin(921600);
USE_SERIAL.begin(115200);
//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);
}
pinMode(LED_RED, OUTPUT);
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_BLUE, OUTPUT);
digitalWrite(LED_RED, 1);
digitalWrite(LED_GREEN, 1);
digitalWrite(LED_BLUE, 1);
WiFiMulti.addAP("SSID", "passpasspass");
while(WiFiMulti.run() != WL_CONNECTED) {
delay(100);
}
// start webSocket server
webSocket.begin();
webSocket.onEvent(webSocketEvent);
if(MDNS.begin("esp8266")) {
USE_SERIAL.println("MDNS responder started");
}
// handle index
server.on("/", []() {
// send index.html
server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () { connection.send('Connect ' + new Date()); }; connection.onerror = function (error) { console.log('WebSocket Error ', error);};connection.onmessage = function (e) { console.log('Server: ', e.data);};function sendRGB() { var r = parseInt(document.getElementById('r').value).toString(16); var g = parseInt(document.getElementById('g').value).toString(16); var b = parseInt(document.getElementById('b').value).toString(16); if(r.length < 2) { r = '0' + r; } if(g.length < 2) { g = '0' + g; } if(b.length < 2) { b = '0' + b; } var rgb = '#'+r+g+b; console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/></body></html>");
});
server.begin();
// Add service to MDNS
MDNS.addService("http", "tcp", 80);
MDNS.addService("ws", "tcp", 81);
digitalWrite(LED_RED, 0);
digitalWrite(LED_GREEN, 0);
digitalWrite(LED_BLUE, 0);
}
void loop() {
webSocket.loop();
server.handleClient();
}

View File

@@ -0,0 +1,46 @@
/* To compile using make CLI, create a folder under \firmware\user\applications and copy application.cpp there.
* Then, copy src files under particleWebSocket folder.
*/
#include "application.h"
#include "particleWebSocket/WebSocketsClient.h"
WebSocketsClient webSocket;
void webSocketEvent(WStype_t type, uint8_t* payload, size_t length)
{
switch (type)
{
case WStype_DISCONNECTED:
Serial.printlnf("[WSc] Disconnected!");
break;
case WStype_CONNECTED:
Serial.printlnf("[WSc] Connected to URL: %s", payload);
webSocket.sendTXT("Connected\r\n");
break;
case WStype_TEXT:
Serial.printlnf("[WSc] get text: %s", payload);
break;
case WStype_BIN:
Serial.printlnf("[WSc] get binary length: %u", length);
break;
}
}
void setup()
{
Serial.begin(9600);
WiFi.setCredentials("[SSID]", "[PASSWORD]", WPA2, WLAN_CIPHER_AES_TKIP);
WiFi.connect();
webSocket.begin("192.168.1.153", 85, "/ClientService/?variable=Test1212");
webSocket.onEvent(webSocketEvent);
}
void loop()
{
webSocket.sendTXT("Hello world!");
delay(500);
webSocket.loop();
}

View File

@@ -0,0 +1,25 @@
{
"authors": [
{
"maintainer": true,
"name": "Markus Sattler",
"url": "https://github.com/Links2004"
}
],
"description": "WebSocket Server and Client for Arduino based on RFC6455",
"export": {
"exclude": [
"tests"
]
},
"frameworks": "arduino",
"keywords": "wifi, http, web, server, client, websocket",
"license": "LGPL-2.1",
"name": "LT_WebSockets",
"platforms": "atmelavr, espressif8266, espressif32",
"repository": {
"type": "git",
"url": "https://github.com/Links2004/arduinoWebSockets.git"
},
"version": "2.3.7"
}

View File

@@ -0,0 +1,9 @@
name=LT_WebSockets
version=2.3.7
author=Markus Sattler
maintainer=Markus Sattler
sentence=WebSockets for Arduino (Server + Client)
paragraph=use 2.x.x for ESP and 1.3 for AVR
category=Communication
url=https://github.com/Links2004/arduinoWebSockets
architectures=*

View File

@@ -0,0 +1,260 @@
/*
* SocketIOclient.cpp
*
* Created on: May 12, 2018
* Author: links
*/
#include "WebSockets.h"
#include "WebSocketsClient.h"
#include "SocketIOclient.h"
SocketIOclient::SocketIOclient() {
}
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_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) {
WebSocketsClient::setSSLClientCertKey(clientCert, clientPrivateKey);
}
void SocketIOclient::setSSLClientCertKey(BearSSL::X509List * clientCert, BearSSL::PrivateKey * clientPrivateKey) {
WebSocketsClient::setSSLClientCertKey(clientCert, clientPrivateKey);
}
#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
*/
void SocketIOclient::onEvent(SocketIOclientEvent cbEvent) {
_cbEvent = cbEvent;
}
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
* @param type socketIOmessageType_t
* @param payload uint8_t *
* @param length size_t
* @param headerToPayload bool (see sendFrame for more details)
* @return true if ok
*/
bool SocketIOclient::send(socketIOmessageType_t type, uint8_t * payload, size_t length, bool headerToPayload) {
bool ret = false;
if(length == 0) {
length = strlen((const char *)payload);
}
if(clientIsConnected(&_client) && _client.status == WSC_CONNECTED) {
if(!headerToPayload) {
// webSocket Header
ret = WebSocketsClient::sendFrameHeader(&_client, WSop_text, length + 2, true);
// Engine.IO / Socket.IO Header
if(ret) {
uint8_t buf[3] = { eIOtype_MESSAGE, type, 0x00 };
ret = WebSocketsClient::write(&_client, buf, 2);
}
if(ret && payload && length > 0) {
ret = WebSocketsClient::write(&_client, payload, length);
}
return ret;
} else {
// TODO implement
}
}
return false;
}
bool SocketIOclient::send(socketIOmessageType_t type, const uint8_t * payload, size_t length) {
return send(type, (uint8_t *)payload, length);
}
bool SocketIOclient::send(socketIOmessageType_t type, char * payload, size_t length, bool headerToPayload) {
return send(type, (uint8_t *)payload, length, headerToPayload);
}
bool SocketIOclient::send(socketIOmessageType_t type, const char * payload, size_t length) {
return send(type, (uint8_t *)payload, length);
}
bool SocketIOclient::send(socketIOmessageType_t type, String & payload) {
return send(type, (uint8_t *)payload.c_str(), payload.length());
}
/**
* send text data to client
* @param num uint8_t client id
* @param payload uint8_t *
* @param length size_t
* @param headerToPayload bool (see sendFrame for more details)
* @return true if ok
*/
bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPayload) {
return send(sIOtype_EVENT, payload, length, headerToPayload);
}
bool SocketIOclient::sendEVENT(const uint8_t * payload, size_t length) {
return sendEVENT((uint8_t *)payload, length);
}
bool SocketIOclient::sendEVENT(char * payload, size_t length, bool headerToPayload) {
return sendEVENT((uint8_t *)payload, length, headerToPayload);
}
bool SocketIOclient::sendEVENT(const char * payload, size_t length) {
return sendEVENT((uint8_t *)payload, length);
}
bool SocketIOclient::sendEVENT(String & payload) {
return sendEVENT((uint8_t *)payload.c_str(), payload.length());
}
void SocketIOclient::loop(void) {
WebSocketsClient::loop();
unsigned long t = millis();
if(!_disableHeartbeat && (t - _lastHeartbeat) > EIO_HEARTBEAT_INTERVAL) {
_lastHeartbeat = t;
DEBUG_WEBSOCKETS("[wsIOc] send ping\n");
WebSocketsClient::sendTXT(eIOtype_PING);
}
}
void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
runIOCbEvent(sIOtype_DISCONNECT, NULL, 0);
DEBUG_WEBSOCKETS("[wsIOc] Disconnected!\n");
break;
case WStype_CONNECTED: {
DEBUG_WEBSOCKETS("[wsIOc] Connected to url: %s\n", payload);
// send message to server when Connected
// Engine.io upgrade confirmation message (required)
WebSocketsClient::sendTXT("2probe");
WebSocketsClient::sendTXT(eIOtype_UPGRADE);
runIOCbEvent(sIOtype_CONNECT, payload, length);
} break;
case WStype_TEXT: {
if(length < 1) {
break;
}
engineIOmessageType_t eType = (engineIOmessageType_t)payload[0];
switch(eType) {
case eIOtype_PING:
payload[0] = eIOtype_PONG;
DEBUG_WEBSOCKETS("[wsIOc] get ping send pong (%s)\n", payload);
WebSocketsClient::sendTXT(payload, length, false);
break;
case eIOtype_PONG:
DEBUG_WEBSOCKETS("[wsIOc] get pong\n");
break;
case eIOtype_MESSAGE: {
if(length < 2) {
break;
}
socketIOmessageType_t ioType = (socketIOmessageType_t)payload[1];
uint8_t * data = &payload[2];
size_t lData = length - 2;
switch(ioType) {
case sIOtype_EVENT:
DEBUG_WEBSOCKETS("[wsIOc] get event (%d): %s\n", lData, data);
break;
case sIOtype_CONNECT:
DEBUG_WEBSOCKETS("[wsIOc] connected (%d): %s\n", lData, data);
return;
case sIOtype_DISCONNECT:
case sIOtype_ACK:
case sIOtype_ERROR:
case sIOtype_BINARY_EVENT:
case sIOtype_BINARY_ACK:
default:
DEBUG_WEBSOCKETS("[wsIOc] Socket.IO Message Type %c (%02X) is not implemented\n", ioType, ioType);
DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
break;
}
runIOCbEvent(ioType, data, lData);
} break;
case eIOtype_OPEN:
case eIOtype_CLOSE:
case eIOtype_UPGRADE:
case eIOtype_NOOP:
default:
DEBUG_WEBSOCKETS("[wsIOc] Engine.IO Message Type %c (%02X) is not implemented\n", eType, eType);
DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
break;
}
} break;
case WStype_ERROR:
case WStype_BIN:
case WStype_FRAGMENT_TEXT_START:
case WStype_FRAGMENT_BIN_START:
case WStype_FRAGMENT:
case WStype_FRAGMENT_FIN:
case WStype_PING:
case WStype_PONG:
break;
}
}

View File

@@ -0,0 +1,105 @@
/**
* SocketIOclient.h
*
* Created on: May 12, 2018
* Author: links
*/
#ifndef SOCKETIOCLIENT_H_
#define SOCKETIOCLIENT_H_
#include "WebSockets.h"
#include "WebSocketsClient.h"
#define EIO_HEARTBEAT_INTERVAL 20000
#define EIO_MAX_HEADER_SIZE (WEBSOCKETS_MAX_HEADER_SIZE + 1)
#define SIO_MAX_HEADER_SIZE (EIO_MAX_HEADER_SIZE + 1)
typedef enum {
eIOtype_OPEN = '0', ///< Sent from the server when a new transport is opened (recheck)
eIOtype_CLOSE = '1', ///< Request the close of this transport but does not shutdown the connection itself.
eIOtype_PING = '2', ///< Sent by the client. Server should answer with a pong packet containing the same data
eIOtype_PONG = '3', ///< Sent by the server to respond to ping packets.
eIOtype_MESSAGE = '4', ///< actual message, client and server should call their callbacks with the data
eIOtype_UPGRADE = '5', ///< Before engine.io switches a transport, it tests, if server and client can communicate over this transport. If this test succeed, the client sends an upgrade packets which requests the server to flush its cache on the old transport and switch to the new transport.
eIOtype_NOOP = '6', ///< A noop packet. Used primarily to force a poll cycle when an incoming websocket connection is received.
} engineIOmessageType_t;
typedef enum {
sIOtype_CONNECT = '0',
sIOtype_DISCONNECT = '1',
sIOtype_EVENT = '2',
sIOtype_ACK = '3',
sIOtype_ERROR = '4',
sIOtype_BINARY_EVENT = '5',
sIOtype_BINARY_ACK = '6',
} socketIOmessageType_t;
class SocketIOclient : protected WebSocketsClient {
public:
#ifdef __AVR__
typedef void (*SocketIOclientEvent)(socketIOmessageType_t type, uint8_t * payload, size_t length);
#else
typedef std::function<void(socketIOmessageType_t type, uint8_t * payload, size_t length)> SocketIOclientEvent;
#endif
SocketIOclient(void);
virtual ~SocketIOclient(void);
void begin(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
void begin(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
#ifdef HAS_SSL
void beginSSL(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
void beginSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
#ifndef SSL_AXTLS
void beginSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * CA_cert = NULL, const char * protocol = "arduino");
void beginSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino");
void setSSLClientCertKey(const char * clientCert = NULL, const char * clientPrivateKey = NULL);
void setSSLClientCertKey(BearSSL::X509List * clientCert = NULL, BearSSL::PrivateKey * clientPrivateKey = NULL);
#endif
#endif
bool isConnected(void);
void onEvent(SocketIOclientEvent cbEvent);
bool sendEVENT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
bool sendEVENT(const uint8_t * payload, size_t length = 0);
bool sendEVENT(char * payload, size_t length = 0, bool headerToPayload = false);
bool sendEVENT(const char * payload, size_t length = 0);
bool sendEVENT(String & payload);
bool send(socketIOmessageType_t type, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
bool send(socketIOmessageType_t type, const uint8_t * payload, size_t length = 0);
bool send(socketIOmessageType_t type, char * payload, size_t length = 0, bool headerToPayload = false);
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) {
if(_cbEvent) {
_cbEvent(type, payload, length);
}
}
void initClient(void);
// Handeling events from websocket layer
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
handleCbEvent(type, payload, length);
}
void handleCbEvent(WStype_t type, uint8_t * payload, size_t length);
};
#endif /* SOCKETIOCLIENT_H_ */

View File

@@ -0,0 +1,776 @@
/**
* @file WebSockets.cpp
* @date 20.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "WebSockets.h"
#ifdef ESP8266
#include <core_esp8266_features.h>
#endif
extern "C" {
#if defined CORE_HAS_LIBB64 || defined LIBRETINY
#include <libb64/cencode.h>
#else
#include "libb64/cencode_inc.h"
#endif
}
#ifdef ESP8266
#include <Hash.h>
#elif defined(ESP32)
#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
#else
#if defined LIBRETINY
extern "C" {
#include <typedef.h>
#include <crypto/sha1_i.h>
}
#else
extern "C" {
#include "libsha1/libsha1.h"
}
#endif
#endif
/**
*
* @param client WSclient_t * ptr to the client struct
* @param code uint16_t see RFC
* @param reason ptr to the disconnect reason message
* @param reasonLen length of the disconnect reason message
*/
void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code);
if(client->status == WSC_CONNECTED && code) {
if(reason) {
sendFrame(client, WSop_close, (uint8_t *)reason, reasonLen);
} else {
uint8_t buffer[2];
buffer[0] = ((code >> 8) & 0xFF);
buffer[1] = (code & 0xFF);
sendFrame(client, WSop_close, &buffer[0], 2);
}
}
clientDisconnect(client);
}
/**
*
* @param buf uint8_t * ptr to the buffer for writing
* @param opcode WSopcode_t
* @param length size_t length of the payload
* @param mask bool add dummy mask to the frame (needed for web browser)
* @param maskkey uint8_t[4] key used for payload
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
*/
uint8_t WebSockets::createHeader(uint8_t * headerPtr, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin) {
uint8_t headerSize;
// calculate header Size
if(length < 126) {
headerSize = 2;
} else if(length < 0xFFFF) {
headerSize = 4;
} else {
headerSize = 10;
}
if(mask) {
headerSize += 4;
}
// create header
// byte 0
*headerPtr = 0x00;
if(fin) {
*headerPtr |= bit(7); ///< set Fin
}
*headerPtr |= opcode; ///< set opcode
headerPtr++;
// byte 1
*headerPtr = 0x00;
if(mask) {
*headerPtr |= bit(7); ///< set mask
}
if(length < 126) {
*headerPtr |= length;
headerPtr++;
} else if(length < 0xFFFF) {
*headerPtr |= 126;
headerPtr++;
*headerPtr = ((length >> 8) & 0xFF);
headerPtr++;
*headerPtr = (length & 0xFF);
headerPtr++;
} else {
// Normally we never get here (to less memory)
*headerPtr |= 127;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = 0x00;
headerPtr++;
*headerPtr = ((length >> 24) & 0xFF);
headerPtr++;
*headerPtr = ((length >> 16) & 0xFF);
headerPtr++;
*headerPtr = ((length >> 8) & 0xFF);
headerPtr++;
*headerPtr = (length & 0xFF);
headerPtr++;
}
if(mask) {
*headerPtr = maskKey[0];
headerPtr++;
*headerPtr = maskKey[1];
headerPtr++;
*headerPtr = maskKey[2];
headerPtr++;
*headerPtr = maskKey[3];
headerPtr++;
}
return headerSize;
}
/**
*
* @param client WSclient_t * ptr to the client struct
* @param opcode WSopcode_t
* @param length size_t length of the payload
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
* @return true if ok
*/
bool WebSockets::sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length, bool fin) {
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
uint8_t headerSize = createHeader(&buffer[0], opcode, length, client->cIsClient, maskKey, fin);
if(write(client, &buffer[0], headerSize) != headerSize) {
return false;
}
return true;
}
/**
*
* @param client WSclient_t * ptr to the client struct
* @param opcode WSopcode_t
* @param payload uint8_t * ptr to the payload
* @param length size_t length of the payload
* @param fin bool can be used to send data in more then one frame (set fin on the last frame)
* @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
* @return true if ok
*/
bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin, bool headerToPayload) {
if(client->tcp && !client->tcp->connected()) {
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num);
return false;
}
if(client->status != WSC_CONNECTED) {
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num);
return false;
}
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num);
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, client->cIsClient, length, headerToPayload);
if(opcode == WSop_text) {
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0)));
}
uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
uint8_t headerSize;
uint8_t * headerPtr;
uint8_t * payloadPtr = payload;
bool useInternBuffer = false;
bool ret = true;
// calculate header Size
if(length < 126) {
headerSize = 2;
} else if(length < 0xFFFF) {
headerSize = 4;
} else {
headerSize = 10;
}
if(client->cIsClient) {
headerSize += 4;
}
#ifdef WEBSOCKETS_USE_BIG_MEM
// only for ESP since AVR has less HEAP
// try to send data in one TCP package (only if some free Heap is there)
if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) {
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
uint8_t * dataPtr = (uint8_t *)malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
if(dataPtr) {
memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
headerToPayload = true;
useInternBuffer = true;
payloadPtr = dataPtr;
}
}
#endif
// set Header Pointer
if(headerToPayload) {
// calculate offset in payload
headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
} else {
headerPtr = &buffer[0];
}
if(client->cIsClient && useInternBuffer) {
// if we use a Intern Buffer we can modify the data
// by this fact its possible the do the masking
for(uint8_t x = 0; x < sizeof(maskKey); x++) {
maskKey[x] = random(0xFF);
}
}
createHeader(headerPtr, opcode, length, client->cIsClient, maskKey, fin);
if(client->cIsClient && useInternBuffer) {
uint8_t * dataMaskPtr;
if(headerToPayload) {
dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
} else {
dataMaskPtr = payloadPtr;
}
for(size_t x = 0; x < length; x++) {
dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
}
}
#ifndef NODEBUG_WEBSOCKETS
unsigned long start = micros();
#endif
if(headerToPayload) {
// header has be added to payload
// payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings
// offset in payload is calculatetd 14 - headerSize
if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
ret = false;
}
} else {
// send header
if(write(client, &buffer[0], headerSize) != headerSize) {
ret = false;
}
if(payloadPtr && length > 0) {
// send payload
if(write(client, &payloadPtr[0], length) != length) {
ret = false;
}
}
}
DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start));
#ifdef WEBSOCKETS_USE_BIG_MEM
if(useInternBuffer && payloadPtr) {
free(payloadPtr);
}
#endif
return ret;
}
/**
* callen when HTTP header is done
* @param client WSclient_t * ptr to the client struct
*/
void WebSockets::headerDone(WSclient_t * client) {
client->status = WSC_CONNECTED;
client->cWsRXsize = 0;
DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num);
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->cHttpLine = "";
handleWebsocket(client);
#endif
}
/**
* handle the WebSocket stream
* @param client WSclient_t * ptr to the client struct
*/
void WebSockets::handleWebsocket(WSclient_t * client) {
if(client->cWsRXsize == 0) {
handleWebsocketCb(client);
}
}
/**
* wait for
* @param client
* @param size
*/
bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
if(!client->tcp || !client->tcp->connected()) {
return false;
}
if(size > WEBSOCKETS_MAX_HEADER_SIZE) {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size);
return false;
}
if(client->cWsRXsize >= size) {
return true;
}
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize);
readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok);
if(ok) {
client->cWsRXsize = size;
server->handleWebsocketCb(client);
} else {
DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num);
client->cWsRXsize = 0;
// timeout or error
server->clientDisconnect(client, 1002);
}
},
this, size, std::placeholders::_1, std::placeholders::_2));
return false;
}
void WebSockets::handleWebsocketCb(WSclient_t * client) {
if(!client->tcp || !client->tcp->connected()) {
return;
}
uint8_t * buffer = client->cWsHeader;
WSMessageHeader_t * header = &client->cWsHeaderDecode;
uint8_t * payload = NULL;
uint8_t headerLen = 2;
if(!handleWebsocketWaitFor(client, headerLen)) {
return;
}
// split first 2 bytes in the data
header->fin = ((*buffer >> 7) & 0x01);
header->rsv1 = ((*buffer >> 6) & 0x01);
header->rsv2 = ((*buffer >> 5) & 0x01);
header->rsv3 = ((*buffer >> 4) & 0x01);
header->opCode = (WSopcode_t)(*buffer & 0x0F);
buffer++;
header->mask = ((*buffer >> 7) & 0x01);
header->payloadLen = (WSopcode_t)(*buffer & 0x7F);
buffer++;
if(header->payloadLen == 126) {
headerLen += 2;
if(!handleWebsocketWaitFor(client, headerLen)) {
return;
}
header->payloadLen = buffer[0] << 8 | buffer[1];
buffer += 2;
} else if(header->payloadLen == 127) {
headerLen += 8;
// read 64bit integer as length
if(!handleWebsocketWaitFor(client, headerLen)) {
return;
}
if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
// really too big!
header->payloadLen = 0xFFFFFFFF;
} else {
header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
}
buffer += 8;
}
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num);
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode);
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen);
if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen);
clientDisconnect(client, 1009);
return;
}
if(header->mask) {
headerLen += 4;
if(!handleWebsocketWaitFor(client, headerLen)) {
return;
}
header->maskKey = buffer;
buffer += 4;
}
if(header->payloadLen > 0) {
// if text data we need one more
payload = (uint8_t *)malloc(header->payloadLen + 1);
if(!payload) {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen);
clientDisconnect(client, 1011);
return;
}
readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload));
} else {
handleWebsocketPayloadCb(client, true, NULL);
}
}
void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) {
WSMessageHeader_t * header = &client->cWsHeaderDecode;
if(ok) {
if(header->payloadLen > 0) {
payload[header->payloadLen] = 0x00;
if(header->mask) {
// decode XOR
for(size_t i = 0; i < header->payloadLen; i++) {
payload[i] = (payload[i] ^ header->maskKey[i % 4]);
}
}
}
switch(header->opCode) {
case WSop_text:
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload);
// no break here!
case WSop_binary:
case WSop_continuation:
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
break;
case WSop_ping:
// send pong back
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ping received (%s)\n", client->num, payload ? (const char *)payload : "");
sendFrame(client, WSop_pong, payload, header->payloadLen);
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
break;
case WSop_pong:
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char *)payload : "");
client->pongReceived = true;
messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
break;
case WSop_close: {
#ifndef NODEBUG_WEBSOCKETS
uint16_t reasonCode = 1000;
if(header->payloadLen >= 2) {
reasonCode = payload[0] << 8 | payload[1];
}
#endif
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d\n", client->num, reasonCode);
if(header->payloadLen > 2) {
DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2));
} else {
DEBUG_WEBSOCKETS("\n");
}
clientDisconnect(client, 1000);
} break;
default:
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] got unknown opcode: %d\n", client->num, header->opCode);
clientDisconnect(client, 1002);
break;
}
if(payload) {
free(payload);
}
// reset input
client->cWsRXsize = 0;
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
// register callback for next message
handleWebsocketWaitFor(client, 2);
#endif
} else {
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] missing data!\n", client->num);
free(payload);
clientDisconnect(client, 1002);
}
}
/**
* generate the key for Sec-WebSocket-Accept
* @param clientKey String
* @return String Accept Key
*/
String WebSockets::acceptKey(String & clientKey) {
uint8_t sha1HashBin[20] = { 0 };
#ifdef ESP8266
sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]);
#elif defined(ESP32)
String data = clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
esp_sha(SHA1, (unsigned char *)data.c_str(), data.length(), &sha1HashBin[0]);
#else
clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
SHA1_CTX ctx;
SHA1Init(&ctx);
SHA1Update(&ctx, (const unsigned char *)clientKey.c_str(), clientKey.length());
SHA1Final(&sha1HashBin[0], &ctx);
#endif
String key = base64_encode(sha1HashBin, 20);
key.trim();
return key;
}
/**
* base64_encode
* @param data uint8_t *
* @param length size_t
* @return base64 encoded String
*/
String WebSockets::base64_encode(uint8_t * data, size_t length) {
size_t size = ((length * 1.6f) + 1);
char * buffer = (char *)malloc(size);
if(buffer) {
base64_encodestate _state;
base64_init_encodestate(&_state);
int len = base64_encode_block((const char *)&data[0], length, &buffer[0], &_state);
len = base64_encode_blockend((buffer + len), &_state);
String base64 = String(buffer);
free(buffer);
return base64;
}
return String("-FAIL-");
}
/**
* read x byte from tcp or get timeout
* @param client WSclient_t *
* @param out uint8_t * data buffer
* @param n size_t byte count
* @return true if ok
*/
bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) {
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
if(!client->tcp || !client->tcp->connected()) {
return false;
}
client->tcp->readBytes(out, n, std::bind([](WSclient_t * client, bool ok, WSreadWaitCb cb) {
if(cb) {
cb(client, ok);
}
},
client, std::placeholders::_1, cb));
#else
unsigned long t = millis();
ssize_t len;
DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t);
while(n > 0) {
if(client->tcp == NULL) {
DEBUG_WEBSOCKETS("[readCb] tcp is null!\n");
if(cb) {
cb(client, false);
}
return false;
}
if(!client->tcp->connected()) {
DEBUG_WEBSOCKETS("[readCb] not connected!\n");
if(cb) {
cb(client, false);
}
return false;
}
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %lu\n", (millis() - t));
if(cb) {
cb(client, false);
}
return false;
}
if(!client->tcp->available()) {
WEBSOCKETS_YIELD_MORE();
continue;
}
len = client->tcp->read((uint8_t *)out, n);
if(len > 0) {
t = millis();
out += len;
n -= len;
// DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
} else {
// DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
}
if(n > 0) {
WEBSOCKETS_YIELD();
}
}
if(cb) {
cb(client, true);
}
WEBSOCKETS_YIELD();
#endif
return true;
}
/**
* write x byte to tcp or get timeout
* @param client WSclient_t *
* @param out uint8_t * data buffer
* @param n size_t byte count
* @return bytes send
*/
size_t WebSockets::write(WSclient_t * client, uint8_t * out, size_t n) {
if(out == NULL)
return 0;
if(client == NULL)
return 0;
unsigned long t = millis();
size_t len = 0;
size_t total = 0;
DEBUG_WEBSOCKETS("[write] n: %zu t: %lu\n", n, t);
while(n > 0) {
if(client->tcp == NULL) {
DEBUG_WEBSOCKETS("[write] tcp is null!\n");
break;
}
if(!client->tcp->connected()) {
DEBUG_WEBSOCKETS("[write] not connected!\n");
break;
}
if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
DEBUG_WEBSOCKETS("[write] write TIMEOUT! %lu\n", (millis() - t));
break;
}
len = client->tcp->write((const uint8_t *)out, n);
if(len) {
t = millis();
out += len;
n -= len;
total += len;
// DEBUG_WEBSOCKETS("write %d left %d!\n", len, n);
} else {
DEBUG_WEBSOCKETS("WS write %d failed left %d!\n", len, n);
}
if(n > 0) {
WEBSOCKETS_YIELD();
}
}
WEBSOCKETS_YIELD();
return total;
}
size_t WebSockets::write(WSclient_t * client, const char * out) {
if(client == NULL)
return 0;
if(out == NULL)
return 0;
return write(client, (uint8_t *)out, strlen(out));
}
/**
* enable ping/pong heartbeat process
* @param client WSclient_t *
* @param pingInterval uint32_t how often ping will be sent
* @param pongTimeout uint32_t millis after which pong should timout if not received
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
*/
void WebSockets::enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
if(client == NULL)
return;
client->pingInterval = pingInterval;
client->pongTimeout = pongTimeout;
client->disconnectTimeoutCount = disconnectTimeoutCount;
client->pongReceived = false;
}
/**
* handle ping/pong heartbeat timeout process
* @param client WSclient_t *
*/
void WebSockets::handleHBTimeout(WSclient_t * client) {
if(client->pingInterval) { // if heartbeat is enabled
uint32_t pi = millis() - client->lastPing;
if(client->pongReceived) {
client->pongTimeoutCount = 0;
} else {
if(pi > client->pongTimeout) { // pong not received in time
client->pongTimeoutCount++;
client->lastPing = millis() - client->pingInterval - 500; // force ping on the next run
DEBUG_WEBSOCKETS("[HBtimeout] pong TIMEOUT! lp=%d millis=%d pi=%d count=%d\n", client->lastPing, millis(), pi, client->pongTimeoutCount);
if(client->disconnectTimeoutCount && client->pongTimeoutCount >= client->disconnectTimeoutCount) {
DEBUG_WEBSOCKETS("[HBtimeout] count=%d, DISCONNECTING\n", client->pongTimeoutCount);
clientDisconnect(client);
}
}
}
}
}
#ifdef LIBRETINY
void randomSeed(unsigned long seed)
{
if (seed != 0) {
srand(seed);
}
}
#endif

View File

@@ -0,0 +1,379 @@
/**
* @file WebSockets.h
* @date 20.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef WEBSOCKETS_H_
#define WEBSOCKETS_H_
#ifdef STM32_DEVICE
#include <application.h>
#define bit(b) (1UL << (b)) // Taken directly from Arduino.h
#else
#include <Arduino.h>
#include <IPAddress.h>
#endif
#ifdef ARDUINO_ARCH_AVR
#error Version 2.x.x currently does not support Arduino with AVR since there is no support for std namespace of c++.
#error Use Version 1.x.x. (ATmega branch)
#else
#include <functional>
#endif
#include "WebSocketsVersion.h"
#ifndef NODEBUG_WEBSOCKETS
#ifdef DEBUG_ESP_PORT
#define DEBUG_WEBSOCKETS(...) \
{ \
DEBUG_ESP_PORT.printf(__VA_ARGS__); \
DEBUG_ESP_PORT.flush(); \
}
#else
//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ )
#endif
#endif
#ifndef DEBUG_WEBSOCKETS
#define DEBUG_WEBSOCKETS(...)
#ifndef NODEBUG_WEBSOCKETS
#define NODEBUG_WEBSOCKETS
#endif
#endif
#if defined(ESP8266) || defined(ESP32)
#define WEBSOCKETS_MAX_DATA_SIZE (15 * 1024)
#define WEBSOCKETS_USE_BIG_MEM
#define GET_FREE_HEAP ESP.getFreeHeap()
// moves all Header strings to Flash (~300 Byte)
//#define WEBSOCKETS_SAVE_RAM
#if defined(ESP8266)
#define WEBSOCKETS_YIELD() delay(0)
#define WEBSOCKETS_YIELD_MORE() delay(1)
#elif defined(ESP32)
#define WEBSOCKETS_YIELD() yield()
#define WEBSOCKETS_YIELD_MORE() delay(1)
#endif
#elif defined(STM32_DEVICE)
#define WEBSOCKETS_MAX_DATA_SIZE (15 * 1024)
#define WEBSOCKETS_USE_BIG_MEM
#define GET_FREE_HEAP System.freeMemory()
#define WEBSOCKETS_YIELD()
#define WEBSOCKETS_YIELD_MORE()
#else
// atmega328p has only 2KB ram!
#define WEBSOCKETS_MAX_DATA_SIZE (1024)
// moves all Header strings to Flash
#define WEBSOCKETS_SAVE_RAM
#define WEBSOCKETS_YIELD()
#define WEBSOCKETS_YIELD_MORE()
#endif
#define WEBSOCKETS_TCP_TIMEOUT (5000)
#define NETWORK_ESP8266_ASYNC (0)
#define NETWORK_ESP8266 (1)
#define NETWORK_W5100 (2)
#define NETWORK_ENC28J60 (3)
#define NETWORK_ESP32 (4)
#define NETWORK_ESP32_ETH (5)
// max size of the WS Message Header
#define WEBSOCKETS_MAX_HEADER_SIZE (14)
#if !defined(WEBSOCKETS_NETWORK_TYPE)
// select Network type based
#if defined(ESP8266) || defined(ESP31B)
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
#elif defined(ESP32)
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32_ETH
#elif defined(LIBRETINY)
#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
#else
#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
#endif
#endif
// Includes and defined based on Network Type
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
// Note:
// No SSL/WSS support for client in Async mode
// TLS lib need a sync interface!
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#elif defined(ESP32)
#include <WiFi.h>
#include <WiFiClientSecure.h>
#define SSL_AXTLS
#elif defined(ESP31B)
#include <ESP31BWiFi.h>
#else
#error "network type ESP8266 ASYNC only possible on the ESP mcu!"
#endif
#include <ESPAsyncTCP.h>
#include <ESPAsyncTCPbuffer.h>
#define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer
#define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) && !defined(LIBRETINY)
#if !defined(ESP8266) && !defined(ESP31B)
#error "network type ESP8266 only possible on the ESP mcu!"
#endif
#if defined(ESP8266)
#include <ESP8266WiFi.h>
#if defined(wificlientbearssl_h) && !defined(USING_AXTLS) && !defined(wificlientsecure_h)
#define SSL_BARESSL
#else
#define SSL_AXTLS
#endif
#else
#include <ESP31BWiFi.h>
#endif
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
#ifdef STM32_DEVICE
#define WEBSOCKETS_NETWORK_CLASS TCPClient
#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer
#endif
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
#include <UIPEthernet.h>
#define WEBSOCKETS_NETWORK_CLASS UIPClient
#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#include <WiFi.h>
#include <WiFiClientSecure.h>
#define SSL_AXTLS
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32_ETH)
#include <ETH.h>
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
#elif defined(LIBRETINY)
//#include <Ethernet.h>
//#include <SPI.h>
//#define WEBSOCKETS_NETWORK_CLASS EthernetClient
//#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
//#if defined(LIBRETINY)
typedef struct SHA1Context SHA1_CTX;
#include <WiFi.h>
//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
//#include <WiFiClientSecure.h>
//#define SSL_AXTLS
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
//#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
//#endif
#else
#error "no network type selected!"
#endif
#ifdef WEBSOCKETS_NETWORK_SSL_CLASS
#define HAS_SSL
#endif
// moves all Header strings to Flash (~300 Byte)
#ifdef WEBSOCKETS_SAVE_RAM
#define WEBSOCKETS_STRING(var) F(var)
#else
#define WEBSOCKETS_STRING(var) var
#endif
typedef enum {
WSC_NOT_CONNECTED,
WSC_HEADER,
WSC_BODY,
WSC_CONNECTED
} WSclientsStatus_t;
typedef enum {
WStype_ERROR,
WStype_DISCONNECTED,
WStype_CONNECTED,
WStype_TEXT,
WStype_BIN,
WStype_FRAGMENT_TEXT_START,
WStype_FRAGMENT_BIN_START,
WStype_FRAGMENT,
WStype_FRAGMENT_FIN,
WStype_PING,
WStype_PONG,
} WStype_t;
typedef enum {
WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
WSop_text = 0x01, ///< %x1 denotes a text frame
WSop_binary = 0x02, ///< %x2 denotes a binary frame
///< %x3-7 are reserved for further non-control frames
WSop_close = 0x08, ///< %x8 denotes a connection close
WSop_ping = 0x09, ///< %x9 denotes a ping
WSop_pong = 0x0A ///< %xA denotes a pong
///< %xB-F are reserved for further control frames
} WSopcode_t;
typedef struct {
bool fin;
bool rsv1;
bool rsv2;
bool rsv3;
WSopcode_t opCode;
bool mask;
size_t payloadLen;
uint8_t * maskKey;
} WSMessageHeader_t;
typedef struct {
void init(uint8_t num,
uint32_t pingInterval,
uint32_t pongTimeout,
uint8_t disconnectTimeoutCount) {
this->num = num;
this->pingInterval = pingInterval;
this->pongTimeout = pongTimeout;
this->disconnectTimeoutCount = disconnectTimeoutCount;
}
uint8_t num = 0; ///< connection number
WSclientsStatus_t status = WSC_NOT_CONNECTED;
WEBSOCKETS_NETWORK_CLASS * tcp = nullptr;
bool isSocketIO = false; ///< client for socket.io server
#if defined(HAS_SSL)
bool isSSL = false; ///< run in ssl mode
WEBSOCKETS_NETWORK_SSL_CLASS * ssl;
#endif
String cUrl; ///< http url
uint16_t cCode = 0; ///< http code
bool cIsClient = false; ///< will be used for masking
bool cIsUpgrade = false; ///< Connection == Upgrade
bool cIsWebsocket = false; ///< Upgrade == websocket
String cSessionId; ///< client Set-Cookie (session id)
String cKey; ///< client Sec-WebSocket-Key
String cAccept; ///< client Sec-WebSocket-Accept
String cProtocol; ///< client Sec-WebSocket-Protocol
String cExtensions; ///< client Sec-WebSocket-Extensions
uint16_t cVersion = 0; ///< client Sec-WebSocket-Version
uint8_t cWsRXsize = 0; ///< State of the RX
uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer
WSMessageHeader_t cWsHeaderDecode;
String base64Authorization; ///< Base64 encoded Auth request
String plainAuthorization; ///< Base64 encoded Auth request
String extraHeaders;
bool cHttpHeadersValid = false; ///< non-websocket http header validity indicator
size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count
bool pongReceived = false;
uint32_t pingInterval = 0; // how often ping will be sent, 0 means "heartbeat is not active"
uint32_t lastPing = 0; // millis when last pong has been received
uint32_t pongTimeout = 0; // interval in millis after which pong is considered to timeout
uint8_t disconnectTimeoutCount = 0; // after how many subsequent pong timeouts discconnect will happen, 0 means "do not disconnect"
uint8_t pongTimeoutCount = 0; // current pong timeout count
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
String cHttpLine; ///< HTTP header lines
#endif
} WSclient_t;
class WebSockets {
protected:
#ifdef __AVR__
typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok);
#else
typedef std::function<void(WSclient_t * client, bool ok)> WSreadWaitCb;
#endif
virtual void clientDisconnect(WSclient_t * client) = 0;
virtual bool clientIsConnected(WSclient_t * client) = 0;
void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0;
uint8_t createHeader(uint8_t * buf, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin);
bool sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length = 0, bool fin = true);
bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool fin = true, bool headerToPayload = false);
void headerDone(WSclient_t * client);
void handleWebsocket(WSclient_t * client);
bool handleWebsocketWaitFor(WSclient_t * client, size_t size);
void handleWebsocketCb(WSclient_t * client);
void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload);
String acceptKey(String & clientKey);
String base64_encode(uint8_t * data, size_t length);
bool readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb);
virtual size_t write(WSclient_t * client, uint8_t * out, size_t n);
size_t write(WSclient_t * client, const char * out);
void enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
void handleHBTimeout(WSclient_t * client);
};
#ifndef UNUSED
#define UNUSED(var) (void)(var)
#endif
#endif /* WEBSOCKETS_H_ */

View File

@@ -0,0 +1,80 @@
/**
* @file WebSocketsServer.cpp
* @date 28.10.2020
* @author Markus Sattler & esp8266/arduino community
*
* Copyright (c) 2020 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef __WEBSOCKETS4WEBSERVER_H
#define __WEBSOCKETS4WEBSERVER_H
#include <WebSocketsServer.h>
#include <ESP8266WebServer.h>
#if WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
class WebSockets4WebServer : public WebSocketsServerCore {
public:
WebSockets4WebServer(const String & origin = "", const String & protocol = "arduino")
: WebSocketsServerCore(origin, protocol) {
begin();
}
ESP8266WebServer::HookFunction hookForWebserver(const String & wsRootDir, WebSocketServerEvent event) {
onEvent(event);
return [&, wsRootDir](const String & method, const String & url, WiFiClient * tcpClient, ESP8266WebServer::ContentTypeFunction contentType) {
(void)contentType;
if(!(method == "GET" && url.indexOf(wsRootDir) == 0)) {
return ESP8266WebServer::CLIENT_REQUEST_CAN_CONTINUE;
}
// allocate a WiFiClient copy (like in WebSocketsServer::handleNewClients())
WEBSOCKETS_NETWORK_CLASS * newTcpClient = new WEBSOCKETS_NETWORK_CLASS(*tcpClient);
// Then initialize a new WSclient_t (like in WebSocketsServer::handleNewClient())
WSclient_t * client = handleNewClient(newTcpClient);
if(client) {
// give "GET <url>"
String headerLine;
headerLine.reserve(url.length() + 5);
headerLine = "GET ";
headerLine += url;
handleHeader(client, &headerLine);
}
// tell webserver to not close but forget about this client
return ESP8266WebServer::CLIENT_IS_GIVEN;
};
}
};
#else // WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
#ifndef WEBSERVER_HAS_HOOK
#error Your current Framework / Arduino core version does not support Webserver Hook Functions
#else
#error Your Hardware Platform does not support Webserver Hook Functions
#endif
#endif // WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266 && WEBSERVER_HAS_HOOK
#endif // __WEBSOCKETS4WEBSERVER_H

View File

@@ -0,0 +1,980 @@
/**
* @file WebSocketsClient.cpp
* @date 20.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "WebSockets.h"
#include "WebSocketsClient.h"
WebSocketsClient::WebSocketsClient() {
_cbEvent = NULL;
_client.num = 0;
_client.cIsClient = true;
_client.extraHeaders = WEBSOCKETS_STRING("Origin: file://");
_reconnectInterval = 500;
_port = 0;
_host = "";
}
WebSocketsClient::~WebSocketsClient() {
disconnect();
}
/**
* calles to init the Websockets server
*/
void WebSocketsClient::begin(const char * host, uint16_t port, const char * url, const char * protocol) {
_host = host;
_port = port;
#if defined(HAS_SSL)
_fingerprint = SSL_FINGERPRINT_NULL;
_CA_cert = NULL;
#endif
_client.num = 0;
_client.status = WSC_NOT_CONNECTED;
_client.tcp = NULL;
#if defined(HAS_SSL)
_client.isSSL = false;
_client.ssl = NULL;
#endif
_client.cUrl = url;
_client.cCode = 0;
_client.cIsUpgrade = false;
_client.cIsWebsocket = true;
_client.cKey = "";
_client.cAccept = "";
_client.cProtocol = protocol;
_client.cExtensions = "";
_client.cVersion = 0;
_client.base64Authorization = "";
_client.plainAuthorization = "";
_client.isSocketIO = false;
_client.lastPing = 0;
_client.pongReceived = false;
_client.pongTimeoutCount = 0;
#ifdef ESP8266
randomSeed(RANDOM_REG32);
#else
// todo find better seed
randomSeed(millis());
#endif
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
asyncConnect();
#endif
_lastConnectionFail = 0;
_lastHeaderSent = 0;
DEBUG_WEBSOCKETS("[WS-Client] Websocket Version: " WEBSOCKETS_VERSION "\n");
}
void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) {
begin(host.c_str(), port, url.c_str(), protocol.c_str());
}
#ifndef LIBRETINY
void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, const char * protocol) {
return begin(host.toString().c_str(), port, url, protocol);
}
#endif
#if defined(HAS_SSL)
#if defined(SSL_AXTLS)
void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * url, const char * fingerprint, const char * protocol) {
begin(host, port, url, protocol);
_client.isSSL = true;
_fingerprint = fingerprint;
_CA_cert = NULL;
}
void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) {
beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str(), protocol.c_str());
}
void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
begin(host, port, url, protocol);
_client.isSSL = true;
_fingerprint = SSL_FINGERPRINT_NULL;
_CA_cert = CA_cert;
}
#else
void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * url, const uint8_t * fingerprint, const char * protocol) {
begin(host, port, url, protocol);
_client.isSSL = true;
_fingerprint = fingerprint;
_CA_cert = NULL;
}
void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, BearSSL::X509List * CA_cert, const char * protocol) {
begin(host, port, url, protocol);
_client.isSSL = true;
_fingerprint = SSL_FINGERPRINT_NULL;
_CA_cert = CA_cert;
}
void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
beginSslWithCA(host, port, url, new BearSSL::X509List(CA_cert), protocol);
}
void WebSocketsClient::setSSLClientCertKey(BearSSL::X509List * clientCert, BearSSL::PrivateKey * clientPrivateKey) {
_client_cert = clientCert;
_client_key = clientPrivateKey;
}
void WebSocketsClient::setSSLClientCertKey(const char * clientCert, const char * clientPrivateKey) {
setSSLClientCertKey(new BearSSL::X509List(clientCert), new BearSSL::PrivateKey(clientPrivateKey));
}
#endif // SSL_AXTLS
#endif // HAS_SSL
void WebSocketsClient::beginSocketIO(const char * host, uint16_t port, const char * url, const char * protocol) {
begin(host, port, url, protocol);
_client.isSocketIO = true;
}
void WebSocketsClient::beginSocketIO(String host, uint16_t port, String url, String protocol) {
beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str());
}
#if defined(HAS_SSL)
void WebSocketsClient::beginSocketIOSSL(const char * host, uint16_t port, const char * url, const char * protocol) {
begin(host, port, url, protocol);
_client.isSocketIO = true;
_client.isSSL = true;
_fingerprint = SSL_FINGERPRINT_NULL;
}
void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) {
beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str());
}
#if defined(SSL_BARESSL)
void WebSocketsClient::beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url, BearSSL::X509List * CA_cert, const char * protocol) {
begin(host, port, url, protocol);
_client.isSocketIO = true;
_client.isSSL = true;
_fingerprint = SSL_FINGERPRINT_NULL;
_CA_cert = CA_cert;
}
#endif
void WebSocketsClient::beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
begin(host, port, url, protocol);
_client.isSocketIO = true;
_client.isSSL = true;
_fingerprint = SSL_FINGERPRINT_NULL;
#if defined(SSL_BARESSL)
_CA_cert = new BearSSL::X509List(CA_cert);
#else
_CA_cert = CA_cert;
#endif
}
#endif
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
/**
* called in arduino loop
*/
void WebSocketsClient::loop(void) {
if(_port == 0) {
return;
}
WEBSOCKETS_YIELD();
if(!clientIsConnected(&_client)) {
// do not flood the server
if((millis() - _lastConnectionFail) < _reconnectInterval) {
return;
}
#if defined(HAS_SSL)
if(_client.isSSL) {
DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n");
if(_client.ssl) {
delete _client.ssl;
_client.ssl = NULL;
_client.tcp = NULL;
}
_client.ssl = new WEBSOCKETS_NETWORK_SSL_CLASS();
_client.tcp = _client.ssl;
if(_CA_cert) {
DEBUG_WEBSOCKETS("[WS-Client] setting CA certificate");
#if defined(ESP32)
_client.ssl->setCACert(_CA_cert);
#elif defined(ESP8266) && defined(SSL_AXTLS)
_client.ssl->setCACert((const uint8_t *)_CA_cert, strlen(_CA_cert) + 1);
#elif defined(ESP8266) && defined(SSL_BARESSL)
_client.ssl->setTrustAnchors(_CA_cert);
#else
#error setCACert not implemented
#endif
#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();
}
if(_client_cert && _client_key) {
_client.ssl->setClientRSACert(_client_cert, _client_key);
DEBUG_WEBSOCKETS("[WS-Client] setting client certificate and key");
#endif
}
} else {
DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n");
if(_client.tcp) {
delete _client.tcp;
_client.tcp = NULL;
}
_client.tcp = new WEBSOCKETS_NETWORK_CLASS();
}
#else
_client.tcp = new WEBSOCKETS_NETWORK_CLASS();
#endif
if(!_client.tcp) {
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
return;
}
WEBSOCKETS_YIELD();
#if defined(ESP32)
if(_client.tcp->connect(_host.c_str(), _port, WEBSOCKETS_TCP_TIMEOUT)) {
#else
if(_client.tcp->connect(_host.c_str(), _port)) {
#endif
connectedCb();
_lastConnectionFail = 0;
} else {
connectFailedCb();
_lastConnectionFail = millis();
}
} else {
handleClientData();
WEBSOCKETS_YIELD();
if(_client.status == WSC_CONNECTED) {
handleHBPing();
handleHBTimeout(&_client);
}
}
}
#endif
/**
* set callback function
* @param cbEvent WebSocketServerEvent
*/
void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) {
_cbEvent = cbEvent;
}
/**
* send text data to client
* @param num uint8_t client id
* @param payload uint8_t *
* @param length size_t
* @param headerToPayload bool (see sendFrame for more details)
* @return true if ok
*/
bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) {
if(length == 0) {
length = strlen((const char *)payload);
}
if(clientIsConnected(&_client)) {
return sendFrame(&_client, WSop_text, payload, length, true, headerToPayload);
}
return false;
}
bool WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) {
return sendTXT((uint8_t *)payload, length);
}
bool WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) {
return sendTXT((uint8_t *)payload, length, headerToPayload);
}
bool WebSocketsClient::sendTXT(const char * payload, size_t length) {
return sendTXT((uint8_t *)payload, length);
}
bool WebSocketsClient::sendTXT(String & payload) {
return sendTXT((uint8_t *)payload.c_str(), payload.length());
}
bool WebSocketsClient::sendTXT(char payload) {
uint8_t buf[WEBSOCKETS_MAX_HEADER_SIZE + 2] = { 0x00 };
buf[WEBSOCKETS_MAX_HEADER_SIZE] = payload;
return sendTXT(buf, 1, true);
}
/**
* send binary data to client
* @param num uint8_t client id
* @param payload uint8_t *
* @param length size_t
* @param headerToPayload bool (see sendFrame for more details)
* @return true if ok
*/
bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) {
if(clientIsConnected(&_client)) {
return sendFrame(&_client, WSop_binary, payload, length, true, headerToPayload);
}
return false;
}
bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
return sendBIN((uint8_t *)payload, length);
}
/**
* sends a WS ping to Server
* @param payload uint8_t *
* @param length size_t
* @return true if ping is send out
*/
bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) {
if(clientIsConnected(&_client)) {
bool sent = sendFrame(&_client, WSop_ping, payload, length);
if(sent)
_client.lastPing = millis();
return sent;
}
return false;
}
bool WebSocketsClient::sendPing(String & payload) {
return sendPing((uint8_t *)payload.c_str(), payload.length());
}
/**
* disconnect one client
* @param num uint8_t client id
*/
void WebSocketsClient::disconnect(void) {
if(clientIsConnected(&_client)) {
WebSockets::clientDisconnect(&_client, 1000);
}
}
/**
* set the Authorizatio for the http request
* @param user const char *
* @param password const char *
*/
void WebSocketsClient::setAuthorization(const char * user, const char * password) {
if(user && password) {
String auth = user;
auth += ":";
auth += password;
_client.base64Authorization = base64_encode((uint8_t *)auth.c_str(), auth.length());
}
}
/**
* set the Authorizatio for the http request
* @param auth const char * base64
*/
void WebSocketsClient::setAuthorization(const char * auth) {
if(auth) {
//_client.base64Authorization = auth;
_client.plainAuthorization = auth;
}
}
/**
* set extra headers for the http request;
* separate headers by "\r\n"
* @param extraHeaders const char * extraHeaders
*/
void WebSocketsClient::setExtraHeaders(const char * extraHeaders) {
_client.extraHeaders = extraHeaders;
}
/**
* set the reconnect Interval
* how long to wait after a connection initiate failed
* @param time in ms
*/
void WebSocketsClient::setReconnectInterval(unsigned long time) {
_reconnectInterval = time;
}
bool WebSocketsClient::isConnected(void) {
return (_client.status == WSC_CONNECTED);
}
//#################################################################################
//#################################################################################
//#################################################################################
/**
*
* @param client WSclient_t * ptr to the client struct
* @param opcode WSopcode_t
* @param payload uint8_t *
* @param length size_t
*/
void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
WStype_t type = WStype_ERROR;
UNUSED(client);
switch(opcode) {
case WSop_text:
type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
break;
case WSop_binary:
type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
break;
case WSop_continuation:
type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
break;
case WSop_ping:
type = WStype_PING;
break;
case WSop_pong:
type = WStype_PONG;
break;
case WSop_close:
default:
break;
}
runCbEvent(type, payload, length);
}
/**
* Disconnect an client
* @param client WSclient_t * ptr to the client struct
*/
void WebSocketsClient::clientDisconnect(WSclient_t * client) {
bool event = false;
#if defined(HAS_SSL)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
if(client->isSSL && client->ssl) {
if(client->ssl->connected()) {
client->ssl->flush();
client->ssl->stop();
}
event = true;
delete client->ssl;
client->ssl = NULL;
client->tcp = NULL;
}
#endif
#endif //ssl
if(client->tcp) {
if(client->tcp->connected()) {
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
client->tcp->flush();
#endif
client->tcp->stop();
}
event = true;
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->status = WSC_NOT_CONNECTED;
#else
delete client->tcp;
#endif
client->tcp = NULL;
}
client->cCode = 0;
client->cKey = "";
client->cAccept = "";
client->cVersion = 0;
client->cIsUpgrade = false;
client->cIsWebsocket = false;
client->cSessionId = "";
client->status = WSC_NOT_CONNECTED;
_lastConnectionFail = millis();
DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n");
if(event) {
runCbEvent(WStype_DISCONNECTED, NULL, 0);
}
}
/**
* get client state
* @param client WSclient_t * ptr to the client struct
* @return true = conneted
*/
bool WebSocketsClient::clientIsConnected(WSclient_t * client) {
if(!client->tcp) {
return false;
}
if(client->tcp->connected()) {
if(client->status != WSC_NOT_CONNECTED) {
return true;
}
} else {
// client lost
if(client->status != WSC_NOT_CONNECTED) {
DEBUG_WEBSOCKETS("[WS-Client] connection lost.\n");
// do cleanup
clientDisconnect(client);
}
}
if(client->tcp) {
// do cleanup
clientDisconnect(client);
}
return false;
}
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
/**
* Handel incomming data from Client
*/
void WebSocketsClient::handleClientData(void) {
if((_client.status == WSC_HEADER || _client.status == WSC_BODY) && _lastHeaderSent + WEBSOCKETS_TCP_TIMEOUT < millis()) {
DEBUG_WEBSOCKETS("[WS-Client][handleClientData] header response timeout.. disconnecting!\n");
clientDisconnect(&_client);
WEBSOCKETS_YIELD();
return;
}
int len = _client.tcp->available();
if(len > 0) {
switch(_client.status) {
case WSC_HEADER: {
String headerLine = _client.tcp->readStringUntil('\n');
handleHeader(&_client, &headerLine);
} break;
case WSC_BODY: {
char buf[256] = { 0 };
_client.tcp->readBytes(&buf[0], std::min((size_t)len, sizeof(buf)));
String bodyLine = buf;
handleHeader(&_client, &bodyLine);
} break;
case WSC_CONNECTED:
WebSockets::handleWebsocket(&_client);
break;
default:
WebSockets::clientDisconnect(&_client, 1002);
break;
}
}
WEBSOCKETS_YIELD();
}
#endif
/**
* send the WebSocket header to Server
* @param client WSclient_t * ptr to the client struct
*/
void WebSocketsClient::sendHeader(WSclient_t * client) {
static const char * NEW_LINE = "\r\n";
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n");
uint8_t randomKey[16] = { 0 };
for(uint8_t i = 0; i < sizeof(randomKey); i++) {
randomKey[i] = random(0xFF);
}
client->cKey = base64_encode(&randomKey[0], 16);
#ifndef NODEBUG_WEBSOCKETS
unsigned long start = micros();
#endif
String handshake;
bool ws_header = true;
String url = client->cUrl;
if(client->isSocketIO) {
if(client->cSessionId.length() == 0) {
url += WEBSOCKETS_STRING("&transport=polling");
ws_header = false;
} else {
url += WEBSOCKETS_STRING("&transport=websocket&sid=");
url += client->cSessionId;
}
}
handshake = WEBSOCKETS_STRING("GET ");
handshake += url + WEBSOCKETS_STRING(
" HTTP/1.1\r\n"
"Host: ");
handshake += _host + ":" + _port + NEW_LINE;
if(ws_header) {
handshake += WEBSOCKETS_STRING(
"Connection: Upgrade\r\n"
"Upgrade: websocket\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Key: ");
handshake += client->cKey + NEW_LINE;
if(client->cProtocol.length() > 0) {
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
handshake += client->cProtocol + NEW_LINE;
}
if(client->cExtensions.length() > 0) {
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: ");
handshake += client->cExtensions + NEW_LINE;
}
} else {
handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n");
}
// add extra headers; by default this includes "Origin: file://"
if(client->extraHeaders.length() > 0) {
handshake += client->extraHeaders + NEW_LINE;
}
handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n");
if(client->base64Authorization.length() > 0) {
handshake += WEBSOCKETS_STRING("Authorization: Basic ");
handshake += client->base64Authorization + NEW_LINE;
}
if(client->plainAuthorization.length() > 0) {
handshake += WEBSOCKETS_STRING("Authorization: ");
handshake += client->plainAuthorization + NEW_LINE;
}
handshake += NEW_LINE;
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t *)handshake.c_str());
write(client, (uint8_t *)handshake.c_str(), handshake.length());
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
#endif
DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%luus).\n", (micros() - start));
_lastHeaderSent = millis();
}
/**
* handle the WebSocket header reading
* @param client WSclient_t * ptr to the client struct
*/
void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
headerLine->trim(); // remove \r
// this code handels the http body for Socket.IO V3 requests
if(headerLine->length() > 0 && client->isSocketIO && client->status == WSC_BODY && client->cSessionId.length() == 0) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] socket.io json: %s\n", headerLine->c_str());
String sid_begin = WEBSOCKETS_STRING("\"sid\":\"");
if(headerLine->indexOf(sid_begin) > -1) {
int start = headerLine->indexOf(sid_begin) + sid_begin.length();
int end = headerLine->indexOf('"', start);
client->cSessionId = headerLine->substring(start, end);
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str());
// Trigger websocket connection code path
*headerLine = "";
}
}
// headle HTTP header
if(headerLine->length() > 0) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str());
if(headerLine->startsWith(WEBSOCKETS_STRING("HTTP/1."))) {
// "HTTP/1.1 101 Switching Protocols"
client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt();
} else if(headerLine->indexOf(':') >= 0) {
String headerName = headerLine->substring(0, headerLine->indexOf(':'));
String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
// remove space in the beginning (RFC2616)
if(headerValue[0] == ' ') {
headerValue.remove(0, 1);
}
if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) {
client->cIsUpgrade = true;
}
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
client->cIsWebsocket = true;
}
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) {
client->cAccept = headerValue;
client->cAccept.trim(); // see rfc6455
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
client->cProtocol = headerValue;
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
client->cExtensions = headerValue;
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
client->cVersion = headerValue.toInt();
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) {
if(headerValue.indexOf(';') > -1) {
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";"));
} else {
client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1);
}
}
} else {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
}
(*headerLine) = "";
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
#endif
} else {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n");
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n");
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cURL: %s\n", client->cUrl.c_str());
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cKey: %s\n", client->cKey.c_str());
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Server header:\n");
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cCode: %d\n", client->cCode);
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsUpgrade: %d\n", client->cIsUpgrade);
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cIsWebsocket: %d\n", client->cIsWebsocket);
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cAccept: %s\n", client->cAccept.c_str());
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cProtocol: %s\n", client->cProtocol.c_str());
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cExtensions: %s\n", client->cExtensions.c_str());
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cVersion: %d\n", client->cVersion);
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] - cSessionId: %s\n", client->cSessionId.c_str());
if(client->isSocketIO && client->cSessionId.length() == 0 && clientIsConnected(client)) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] still missing cSessionId try socket.io V3\n");
client->status = WSC_BODY;
return;
} else {
client->status = WSC_HEADER;
}
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
if(ok) {
switch(client->cCode) {
case 101: ///< Switching Protocols
break;
case 200:
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);
clientDisconnect(client);
_lastConnectionFail = millis();
break;
}
}
if(ok) {
if(client->cAccept.length() == 0) {
ok = false;
} else {
// generate Sec-WebSocket-Accept key for check
String sKey = acceptKey(client->cKey);
if(sKey != client->cAccept) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Sec-WebSocket-Accept is wrong\n");
ok = false;
}
}
}
if(ok) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n");
headerDone(client);
runCbEvent(WStype_CONNECTED, (uint8_t *)client->cUrl.c_str(), client->cUrl.length());
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
} else if(client->isSocketIO) {
if(client->cSessionId.length() > 0) {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] found cSessionId\n");
if(clientIsConnected(client) && _client.tcp->available()) {
// read not needed data
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] still data in buffer (%d), clean up.\n", _client.tcp->available());
while(_client.tcp->available() > 0) {
_client.tcp->read();
}
}
sendHeader(client);
}
#endif
} else {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n");
_lastConnectionFail = millis();
if(clientIsConnected(client)) {
write(client, "This is a webSocket client!");
}
clientDisconnect(client);
}
}
}
void WebSocketsClient::connectedCb() {
DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port);
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
_client.tcp->onDisconnect(std::bind([](WebSocketsClient * c, AsyncTCPbuffer * obj, WSclient_t * client) -> bool {
DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
client->status = WSC_NOT_CONNECTED;
client->tcp = NULL;
// reconnect
c->asyncConnect();
return true;
},
this, std::placeholders::_1, &_client));
#endif
_client.status = WSC_HEADER;
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
// set Timeout for readBytesUntil and readStringUntil
_client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
#endif
#if !defined(LIBRETINY)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
_client.tcp->setNoDelay(true);
#endif
#endif
#if defined(HAS_SSL)
#if defined(SSL_AXTLS) || defined(ESP32)
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 && SSL_FINGERPRINT_IS_SET) {
#endif
} else if(_client.isSSL && !_CA_cert) {
#if defined(SSL_BARESSL)
_client.ssl->setInsecure();
#endif
}
#endif
// send Header to Server
sendHeader(&_client);
}
void WebSocketsClient::connectFailedCb() {
DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Failed\n", _host.c_str(), _port);
}
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
void WebSocketsClient::asyncConnect() {
DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n");
AsyncClient * tcpclient = new AsyncClient();
if(!tcpclient) {
DEBUG_WEBSOCKETS("[WS-Client] creating AsyncClient class failed!\n");
return;
}
tcpclient->onDisconnect([](void * obj, AsyncClient * c) {
c->free();
delete c;
});
tcpclient->onConnect(std::bind([](WebSocketsClient * ws, AsyncClient * tcp) {
ws->_client.tcp = new AsyncTCPbuffer(tcp);
if(!ws->_client.tcp) {
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n");
ws->connectFailedCb();
return;
}
ws->connectedCb();
},
this, std::placeholders::_2));
tcpclient->onError(std::bind([](WebSocketsClient * ws, AsyncClient * tcp) {
ws->connectFailedCb();
// reconnect
ws->asyncConnect();
},
this, std::placeholders::_2));
if(!tcpclient->connect(_host.c_str(), _port)) {
connectFailedCb();
delete tcpclient;
}
}
#endif
/**
* send heartbeat ping to server in set intervals
*/
void WebSocketsClient::handleHBPing() {
if(_client.pingInterval == 0)
return;
uint32_t pi = millis() - _client.lastPing;
if(pi > _client.pingInterval) {
DEBUG_WEBSOCKETS("[WS-Client] sending HB ping\n");
if(sendPing()) {
_client.lastPing = millis();
_client.pongReceived = false;
} else {
DEBUG_WEBSOCKETS("[WS-Client] sending HB ping failed\n");
WebSockets::clientDisconnect(&_client, 1000);
}
}
}
/**
* enable ping/pong heartbeat process
* @param pingInterval uint32_t how often ping will be sent
* @param pongTimeout uint32_t millis after which pong should timout if not received
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
*/
void WebSocketsClient::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
WebSockets::enableHeartbeat(&_client, pingInterval, pongTimeout, disconnectTimeoutCount);
}
/**
* disable ping/pong heartbeat process
*/
void WebSocketsClient::disableHeartbeat() {
_client.pingInterval = 0;
}

View File

@@ -0,0 +1,171 @@
/**
* @file WebSocketsClient.h
* @date 20.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef WEBSOCKETSCLIENT_H_
#define WEBSOCKETSCLIENT_H_
#include "WebSockets.h"
class WebSocketsClient : protected WebSockets {
public:
#ifdef __AVR__
typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
#else
typedef std::function<void(WStype_t type, uint8_t * payload, size_t length)> WebSocketClientEvent;
#endif
WebSocketsClient(void);
virtual ~WebSocketsClient(void);
void begin(const char * host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
#ifndef LIBRETINY
void begin(IPAddress host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
#endif
#if defined(HAS_SSL)
#ifdef SSL_AXTLS
void beginSSL(const char * host, uint16_t port, const char * url = "/", const char * fingerprint = "", const char * protocol = "arduino");
void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino");
#else
void beginSSL(const char * host, uint16_t port, const char * url = "/", const uint8_t * fingerprint = NULL, const char * protocol = "arduino");
void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino");
void setSSLClientCertKey(BearSSL::X509List * clientCert = NULL, BearSSL::PrivateKey * clientPrivateKey = NULL);
void setSSLClientCertKey(const char * clientCert = NULL, const char * clientPrivateKey = NULL);
#endif
void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", const char * CA_cert = NULL, const char * protocol = "arduino");
#endif
void beginSocketIO(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
void beginSocketIO(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
#if defined(HAS_SSL)
void beginSocketIOSSL(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
void beginSocketIOSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
void beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * CA_cert = NULL, const char * protocol = "arduino");
#if defined(SSL_BARESSL)
void beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", BearSSL::X509List * CA_cert = NULL, const char * protocol = "arduino");
#endif
#endif
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void loop(void);
#else
// Async interface not need a loop call
void loop(void) __attribute__((deprecated)) {}
#endif
void onEvent(WebSocketClientEvent cbEvent);
bool sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
bool sendTXT(const uint8_t * payload, size_t length = 0);
bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false);
bool sendTXT(const char * payload, size_t length = 0);
bool sendTXT(String & payload);
bool sendTXT(char payload);
bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
bool sendBIN(const uint8_t * payload, size_t length);
bool sendPing(uint8_t * payload = NULL, size_t length = 0);
bool sendPing(String & payload);
void disconnect(void);
void setAuthorization(const char * user, const char * password);
void setAuthorization(const char * auth);
void setExtraHeaders(const char * extraHeaders = NULL);
void setReconnectInterval(unsigned long time);
void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
void disableHeartbeat();
bool isConnected(void);
protected:
String _host;
uint16_t _port;
#if defined(HAS_SSL)
#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
#endif
WSclient_t _client;
WebSocketClientEvent _cbEvent;
unsigned long _lastConnectionFail;
unsigned long _reconnectInterval;
unsigned long _lastHeaderSent;
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
void clientDisconnect(WSclient_t * client);
bool clientIsConnected(WSclient_t * client);
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void handleClientData(void);
#endif
void sendHeader(WSclient_t * client);
void handleHeader(WSclient_t * client, String * headerLine);
void connectedCb();
void connectFailedCb();
void handleHBPing(); // send ping in specified intervals
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
void asyncConnect();
#endif
/**
* called for sending a Event to the app
* @param type WStype_t
* @param payload uint8_t *
* @param length size_t
*/
virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
if(_cbEvent) {
_cbEvent(type, payload, length);
}
}
};
#endif /* WEBSOCKETSCLIENT_H_ */

View File

@@ -0,0 +1,966 @@
/**
* @file WebSocketsServer.cpp
* @date 20.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "WebSockets.h"
#include "WebSocketsServer.h"
#ifdef LIBRETINY
#include "Common.h"
#endif
WebSocketsServerCore::WebSocketsServerCore(const String & origin, const String & protocol) {
_origin = origin;
_protocol = protocol;
_runnning = false;
_pingInterval = 0;
_pongTimeout = 0;
_disconnectTimeoutCount = 0;
_cbEvent = NULL;
_httpHeaderValidationFunc = NULL;
_mandatoryHttpHeaders = NULL;
_mandatoryHttpHeaderCount = 0;
}
WebSocketsServer::WebSocketsServer(uint16_t port, const String & origin, const String & protocol)
: WebSocketsServerCore(origin, protocol) {
_port = port;
_server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port);
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
_server->onClient([](void * s, AsyncClient * c) {
((WebSocketsServerCore *)s)->newClient(new AsyncTCPbuffer(c));
},
this);
#endif
}
WebSocketsServerCore::~WebSocketsServerCore() {
// disconnect all clients
close();
if(_mandatoryHttpHeaders)
delete[] _mandatoryHttpHeaders;
_mandatoryHttpHeaderCount = 0;
}
WebSocketsServer::~WebSocketsServer() {
}
/**
* called to initialize the Websocket server
*/
void WebSocketsServerCore::begin(void) {
// adjust clients storage:
// _clients[i]'s constructor are already called,
// all its members are initialized to their default value,
// except the ones explicitly detailed in WSclient_t() constructor.
// Then we need to initialize some members to non-trivial values:
for(int i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
_clients[i].init(i, _pingInterval, _pongTimeout, _disconnectTimeoutCount);
}
#ifdef ESP8266
randomSeed(RANDOM_REG32);
#elif defined(ESP32)
#define DR_REG_RNG_BASE 0x3ff75144
randomSeed(READ_PERI_REG(DR_REG_RNG_BASE));
#else
// TODO find better seed
randomSeed(millis());
#endif
_runnning = true;
DEBUG_WEBSOCKETS("[WS-Server] Websocket Version: " WEBSOCKETS_VERSION "\n");
}
void WebSocketsServerCore::close(void) {
_runnning = false;
disconnect();
// restore _clients[] to their initial state
// before next call to ::begin()
for(int i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
_clients[i] = WSclient_t();
}
}
/**
* set callback function
* @param cbEvent WebSocketServerEvent
*/
void WebSocketsServerCore::onEvent(WebSocketServerEvent cbEvent) {
_cbEvent = cbEvent;
}
/*
* Sets the custom http header validator function
* @param httpHeaderValidationFunc WebSocketServerHttpHeaderValFunc ///< pointer to the custom http header validation function
* @param mandatoryHttpHeaders[] const char* ///< the array of named http headers considered to be mandatory / must be present in order for websocket upgrade to succeed
* @param mandatoryHttpHeaderCount size_t ///< the number of items in the mandatoryHttpHeaders array
*/
void WebSocketsServerCore::onValidateHttpHeader(
WebSocketServerHttpHeaderValFunc validationFunc,
const char * mandatoryHttpHeaders[],
size_t mandatoryHttpHeaderCount) {
_httpHeaderValidationFunc = validationFunc;
if(_mandatoryHttpHeaders)
delete[] _mandatoryHttpHeaders;
_mandatoryHttpHeaderCount = mandatoryHttpHeaderCount;
_mandatoryHttpHeaders = new String[_mandatoryHttpHeaderCount];
for(size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
_mandatoryHttpHeaders[i] = mandatoryHttpHeaders[i];
}
}
/*
* send text data to client
* @param num uint8_t client id
* @param payload uint8_t *
* @param length size_t
* @param headerToPayload bool (see sendFrame for more details)
* @return true if ok
*/
bool WebSocketsServerCore::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false;
}
if(length == 0) {
length = strlen((const char *)payload);
}
WSclient_t * client = &_clients[num];
if(clientIsConnected(client)) {
return sendFrame(client, WSop_text, payload, length, true, headerToPayload);
}
return false;
}
bool WebSocketsServerCore::sendTXT(uint8_t num, const uint8_t * payload, size_t length) {
return sendTXT(num, (uint8_t *)payload, length);
}
bool WebSocketsServerCore::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) {
return sendTXT(num, (uint8_t *)payload, length, headerToPayload);
}
bool WebSocketsServerCore::sendTXT(uint8_t num, const char * payload, size_t length) {
return sendTXT(num, (uint8_t *)payload, length);
}
bool WebSocketsServerCore::sendTXT(uint8_t num, String & payload) {
return sendTXT(num, (uint8_t *)payload.c_str(), payload.length());
}
/**
* send text data to client all
* @param payload uint8_t *
* @param length size_t
* @param headerToPayload bool (see sendFrame for more details)
* @return true if ok
*/
bool WebSocketsServerCore::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) {
WSclient_t * client;
bool ret = true;
if(length == 0) {
length = strlen((const char *)payload);
}
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
if(clientIsConnected(client)) {
if(!sendFrame(client, WSop_text, payload, length, true, headerToPayload)) {
ret = false;
}
}
WEBSOCKETS_YIELD();
}
return ret;
}
bool WebSocketsServerCore::broadcastTXT(const uint8_t * payload, size_t length) {
return broadcastTXT((uint8_t *)payload, length);
}
bool WebSocketsServerCore::broadcastTXT(char * payload, size_t length, bool headerToPayload) {
return broadcastTXT((uint8_t *)payload, length, headerToPayload);
}
bool WebSocketsServerCore::broadcastTXT(const char * payload, size_t length) {
return broadcastTXT((uint8_t *)payload, length);
}
bool WebSocketsServerCore::broadcastTXT(String & payload) {
return broadcastTXT((uint8_t *)payload.c_str(), payload.length());
}
/**
* send binary data to client
* @param num uint8_t client id
* @param payload uint8_t *
* @param length size_t
* @param headerToPayload bool (see sendFrame for more details)
* @return true if ok
*/
bool WebSocketsServerCore::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool fin, bool continuation, bool headerToPayload) {
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false;
}
WSclient_t * client = &_clients[num];
if(clientIsConnected(client)) {
if(continuation) {
return sendFrame(client, WSop_continuation, payload, length, fin, headerToPayload);
} else {
return sendFrame(client, WSop_binary, payload, length, fin, headerToPayload);
}
}
return false;
}
bool WebSocketsServerCore::sendBIN(uint8_t num, const uint8_t * payload, size_t length) {
return sendBIN(num, (uint8_t *)payload, length);
}
/**
* send binary data to client all
* @param payload uint8_t *
* @param length size_t
* @param headerToPayload bool (see sendFrame for more details)
* @return true if ok
*/
bool WebSocketsServerCore::broadcastBIN(uint8_t * payload, size_t length, bool fin, bool continuation, bool headerToPayload) {
WSclient_t * client;
bool ret = true;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
if(clientIsConnected(client)) {
if(continuation) {
ret = sendFrame(client, WSop_continuation, payload, length, fin, headerToPayload);
} else {
ret = sendFrame(client, WSop_binary, payload, length, fin, headerToPayload);
}
}
WEBSOCKETS_YIELD();
}
return ret;
}
bool WebSocketsServerCore::broadcastBIN(const uint8_t * payload, size_t length) {
return broadcastBIN((uint8_t *)payload, length);
}
/**
* sends a WS ping to Client
* @param num uint8_t client id
* @param payload uint8_t *
* @param length size_t
* @return true if ping is send out
*/
bool WebSocketsServerCore::sendPing(uint8_t num, uint8_t * payload, size_t length) {
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false;
}
WSclient_t * client = &_clients[num];
if(clientIsConnected(client)) {
return sendFrame(client, WSop_ping, payload, length);
}
return false;
}
bool WebSocketsServerCore::sendPing(uint8_t num, String & payload) {
return sendPing(num, (uint8_t *)payload.c_str(), payload.length());
}
/**
* sends a WS ping to all Client
* @param payload uint8_t *
* @param length size_t
* @return true if ping is send out
*/
bool WebSocketsServerCore::broadcastPing(uint8_t * payload, size_t length) {
WSclient_t * client;
bool ret = true;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
if(clientIsConnected(client)) {
if(!sendFrame(client, WSop_ping, payload, length)) {
ret = false;
}
}
WEBSOCKETS_YIELD();
}
return ret;
}
bool WebSocketsServerCore::broadcastPing(String & payload) {
return broadcastPing((uint8_t *)payload.c_str(), payload.length());
}
/**
* disconnect all clients
*/
void WebSocketsServerCore::disconnect(void) {
WSclient_t * client;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
if(clientIsConnected(client)) {
WebSockets::clientDisconnect(client, 1000);
}
}
}
/**
* disconnect one client
* @param num uint8_t client id
*/
void WebSocketsServerCore::disconnect(uint8_t num) {
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return;
}
WSclient_t * client = &_clients[num];
if(clientIsConnected(client)) {
WebSockets::clientDisconnect(client, 1000);
}
}
/*
* set the Authorization for the http request
* @param user const char *
* @param password const char *
*/
void WebSocketsServerCore::setAuthorization(const char * user, const char * password) {
if(user && password) {
String auth = user;
auth += ":";
auth += password;
_base64Authorization = base64_encode((uint8_t *)auth.c_str(), auth.length());
}
}
/**
* set the Authorizatio for the http request
* @param auth const char * base64
*/
void WebSocketsServerCore::setAuthorization(const char * auth) {
if(auth) {
_base64Authorization = auth;
}
}
/**
* count the connected clients (optional ping them)
* @param ping bool ping the connected clients
*/
int WebSocketsServerCore::connectedClients(bool ping) {
WSclient_t * client;
int count = 0;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
if(client->status == WSC_CONNECTED) {
if(ping != true || sendPing(i)) {
count++;
}
}
}
return count;
}
/**
* see if one client is connected
* @param num uint8_t client id
*/
bool WebSocketsServerCore::clientIsConnected(uint8_t num) {
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
return false;
}
WSclient_t * client = &_clients[num];
return clientIsConnected(client);
}
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
/**
* get an IP for a client
* @param num uint8_t client id
* @return IPAddress
*/
IPAddress WebSocketsServerCore::remoteIP(uint8_t num) {
if(num < WEBSOCKETS_SERVER_CLIENT_MAX) {
WSclient_t * client = &_clients[num];
if(clientIsConnected(client)) {
return client->tcp->remoteIP();
}
}
return IPAddress();
}
#endif
//#################################################################################
//#################################################################################
//#################################################################################
/**
* handle new client connection
* @param client
*/
WSclient_t * WebSocketsServerCore::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
WSclient_t * client;
// search free list entry for client
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
// state is not connected or tcp connection is lost
if(!clientIsConnected(client)) {
client->tcp = TCPclient;
#if defined(HAS_SSL)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
client->isSSL = false;
client->tcp->setNoDelay(true);
#endif
#endif //ssl
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
// set Timeout for readBytesUntil and readStringUntil
client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
#endif
client->status = WSC_HEADER;
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#ifndef NODEBUG_WEBSOCKETS
IPAddress ip = client->tcp->remoteIP();
#endif
DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]);
#else
DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num);
#endif
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->tcp->onDisconnect(std::bind([](WebSocketsServerCore * server, AsyncTCPbuffer * obj, WSclient_t * client) -> bool {
DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp;
if(*sl == obj) {
client->status = WSC_NOT_CONNECTED;
*sl = NULL;
}
return true;
},
this, std::placeholders::_1, client));
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServerCore::handleHeader, this, client, &(client->cHttpLine)));
#endif
client->pingInterval = _pingInterval;
client->pongTimeout = _pongTimeout;
client->disconnectTimeoutCount = _disconnectTimeoutCount;
client->lastPing = millis();
client->pongReceived = false;
return client;
break;
}
}
return nullptr;
}
/**
*
* @param client WSclient_t * ptr to the client struct
* @param opcode WSopcode_t
* @param payload uint8_t *
* @param length size_t
*/
void WebSocketsServerCore::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
WStype_t type = WStype_ERROR;
switch(opcode) {
case WSop_text:
type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
break;
case WSop_binary:
type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
break;
case WSop_continuation:
type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
break;
case WSop_ping:
type = WStype_PING;
break;
case WSop_pong:
type = WStype_PONG;
break;
case WSop_close:
default:
break;
}
runCbEvent(client->num, type, payload, length);
}
/**
* Discard a native client
* @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)
client->tcp->flush();
#endif
client->tcp->stop();
}
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->status = WSC_NOT_CONNECTED;
#else
delete client->tcp;
#endif
client->tcp = NULL;
}
}
/**
* Disconnect an client
* @param client WSclient_t * ptr to the client struct
*/
void WebSocketsServerCore::clientDisconnect(WSclient_t * client) {
#if defined(HAS_SSL)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
if(client->isSSL && client->ssl) {
if(client->ssl->connected()) {
client->ssl->flush();
client->ssl->stop();
}
delete client->ssl;
client->ssl = NULL;
client->tcp = NULL;
}
#endif
#endif //ssl
dropNativeClient(client);
client->cUrl = "";
client->cKey = "";
client->cProtocol = "";
client->cVersion = 0;
client->cIsUpgrade = false;
client->cIsWebsocket = false;
client->cWsRXsize = 0;
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->cHttpLine = "";
#endif
client->status = WSC_NOT_CONNECTED;
DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num);
runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0);
}
/**
* get client state
* @param client WSclient_t * ptr to the client struct
* @return true = connected
*/
bool WebSocketsServerCore::clientIsConnected(WSclient_t * client) {
if(!client->tcp) {
return false;
}
if(client->tcp->connected()) {
if(client->status != WSC_NOT_CONNECTED) {
return true;
}
} else {
// client lost
if(client->status != WSC_NOT_CONNECTED) {
DEBUG_WEBSOCKETS("[WS-Server][%d] client connection lost.\n", client->num);
// do cleanup
clientDisconnect(client);
}
}
if(client->tcp) {
// do cleanup
DEBUG_WEBSOCKETS("[WS-Server][%d] client list cleanup.\n", client->num);
clientDisconnect(client);
}
return false;
}
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
/**
* Handle incoming Connection Request
*/
WSclient_t * WebSocketsServerCore::handleNewClient(WEBSOCKETS_NETWORK_CLASS * tcpClient) {
WSclient_t * client = newClient(tcpClient);
if(!client) {
// no free space to handle client
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#ifndef NODEBUG_WEBSOCKETS
IPAddress ip = tcpClient->remoteIP();
#endif
DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
#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);
}
WEBSOCKETS_YIELD();
return client;
}
/**
* Handle incoming Connection Request
*/
void WebSocketsServer::handleNewClients(void) {
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
while(_server->hasClient()) {
#endif
// store new connection
WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available());
if(!tcpClient) {
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
return;
}
handleNewClient(tcpClient);
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
}
#endif
}
/**
* Handel incomming data from Client
*/
void WebSocketsServerCore::handleClientData(void) {
WSclient_t * client;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
if(clientIsConnected(client)) {
int len = client->tcp->available();
if(len > 0) {
// DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len);
switch(client->status) {
case WSC_HEADER: {
String headerLine = client->tcp->readStringUntil('\n');
handleHeader(client, &headerLine);
} break;
case WSC_CONNECTED:
WebSockets::handleWebsocket(client);
break;
default:
DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] unknown client status %d\n", client->num, client->status);
WebSockets::clientDisconnect(client, 1002);
break;
}
}
handleHBPing(client);
handleHBTimeout(client);
}
WEBSOCKETS_YIELD();
}
}
#endif
/*
* returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
* @param headerName String ///< the name of the header being checked
*/
bool WebSocketsServerCore::hasMandatoryHeader(String headerName) {
for(size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
if(_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName))
return true;
}
return false;
}
/**
* handles http header reading for WebSocket upgrade
* @param client WSclient_t * ///< pointer to the client struct
* @param headerLine String ///< the header being read / processed
*/
void WebSocketsServerCore::handleHeader(WSclient_t * client, String * headerLine) {
static const char * NEW_LINE = "\r\n";
headerLine->trim(); // remove \r
if(headerLine->length() > 0) {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str());
// websocket requests always start with GET see rfc6455
if(headerLine->startsWith("GET ")) {
// cut URL out
client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4));
// reset non-websocket http header validation state for this client
client->cHttpHeadersValid = true;
client->cMandatoryHeadersCount = 0;
} else if(headerLine->indexOf(':') >= 0) {
String headerName = headerLine->substring(0, headerLine->indexOf(':'));
String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
// remove space in the beginning (RFC2616)
if(headerValue[0] == ' ') {
headerValue.remove(0, 1);
}
if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
headerValue.toLowerCase();
if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) {
client->cIsUpgrade = true;
}
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
client->cIsWebsocket = true;
}
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
client->cVersion = headerValue.toInt();
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) {
client->cKey = headerValue;
client->cKey.trim(); // see rfc6455
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
client->cProtocol = headerValue;
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
client->cExtensions = headerValue;
} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) {
client->base64Authorization = headerValue;
} else {
client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue);
if(_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) {
client->cMandatoryHeadersCount++;
}
}
} else {
DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
}
(*headerLine) = "";
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServerCore::handleHeader, this, client, &(client->cHttpLine)));
#endif
} else {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num);
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str());
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade);
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsWebsocket: %d\n", client->num, client->cIsWebsocket);
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cKey: %s\n", client->num, client->cKey.c_str());
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cProtocol: %s\n", client->num, client->cProtocol.c_str());
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cExtensions: %s\n", client->num, client->cExtensions.c_str());
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cVersion: %d\n", client->num, client->cVersion);
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - base64Authorization: %s\n", client->num, client->base64Authorization.c_str());
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cHttpHeadersValid: %d\n", client->num, client->cHttpHeadersValid);
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cMandatoryHeadersCount: %d\n", client->num, client->cMandatoryHeadersCount);
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
if(ok) {
if(client->cUrl.length() == 0) {
ok = false;
}
if(client->cKey.length() == 0) {
ok = false;
}
if(client->cVersion != 13) {
ok = false;
}
if(!client->cHttpHeadersValid) {
ok = false;
}
if(client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) {
ok = false;
}
}
if(_base64Authorization.length() > 0) {
String auth = WEBSOCKETS_STRING("Basic ");
auth += _base64Authorization;
if(auth != client->base64Authorization) {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num);
handleAuthorizationFailed(client);
return;
}
}
if(ok) {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num);
// generate Sec-WebSocket-Accept key
String sKey = acceptKey(client->cKey);
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - sKey: %s\n", client->num, sKey.c_str());
client->status = WSC_CONNECTED;
String handshake = WEBSOCKETS_STRING(
"HTTP/1.1 101 Switching Protocols\r\n"
"Server: arduino-WebSocketsServer\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Version: 13\r\n"
"Sec-WebSocket-Accept: ");
handshake += sKey + NEW_LINE;
if(_origin.length() > 0) {
handshake += WEBSOCKETS_STRING("Access-Control-Allow-Origin: ");
handshake += _origin + NEW_LINE;
}
if(client->cProtocol.length() > 0) {
handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
handshake += _protocol + NEW_LINE;
}
// header end
handshake += NEW_LINE;
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] handshake %s", client->num, (uint8_t *)handshake.c_str());
write(client, (uint8_t *)handshake.c_str(), handshake.length());
headerDone(client);
// send ping
WebSockets::sendFrame(client, WSop_ping);
runCbEvent(client->num, WStype_CONNECTED, (uint8_t *)client->cUrl.c_str(), client->cUrl.length());
} else {
handleNonWebsocketConnection(client);
}
}
}
/**
* send heartbeat ping to server in set intervals
*/
void WebSocketsServerCore::handleHBPing(WSclient_t * client) {
if(client->pingInterval == 0)
return;
uint32_t pi = millis() - client->lastPing;
if(pi > client->pingInterval) {
DEBUG_WEBSOCKETS("[WS-Server][%d] sending HB ping\n", client->num);
if(sendPing(client->num)) {
client->lastPing = millis();
client->pongReceived = false;
}
}
}
/**
* enable ping/pong heartbeat process
* @param pingInterval uint32_t how often ping will be sent
* @param pongTimeout uint32_t millis after which pong should timout if not received
* @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
*/
void WebSocketsServerCore::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
_pingInterval = pingInterval;
_pongTimeout = pongTimeout;
_disconnectTimeoutCount = disconnectTimeoutCount;
WSclient_t * client;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
WebSockets::enableHeartbeat(client, pingInterval, pongTimeout, disconnectTimeoutCount);
}
}
/**
* disable ping/pong heartbeat process
*/
void WebSocketsServerCore::disableHeartbeat() {
_pingInterval = 0;
WSclient_t * client;
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
client->pingInterval = 0;
}
}
////////////////////
// WebSocketServer
/**
* called to initialize the Websocket server
*/
void WebSocketsServer::begin(void) {
WebSocketsServerCore::begin();
_server->begin();
DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n");
}
void WebSocketsServer::close(void) {
WebSocketsServerCore::close();
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
_server->close();
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
_server->end();
#else
// TODO how to close server?
#endif
}
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
/**
* called in arduino loop
*/
void WebSocketsServerCore::loop(void) {
if(_runnning) {
WEBSOCKETS_YIELD();
handleClientData();
}
}
/**
* called in arduino loop
*/
void WebSocketsServer::loop(void) {
if(_runnning) {
WEBSOCKETS_YIELD();
handleNewClients();
WebSocketsServerCore::loop();
}
}
#endif

View File

@@ -0,0 +1,244 @@
/**
* @file WebSocketsServer.h
* @date 20.05.2015
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef WEBSOCKETSSERVER_H_
#define WEBSOCKETSSERVER_H_
#include "WebSockets.h"
#ifndef WEBSOCKETS_SERVER_CLIENT_MAX
#define WEBSOCKETS_SERVER_CLIENT_MAX (5)
#endif
class WebSocketsServerCore : protected WebSockets {
public:
WebSocketsServerCore(const String & origin = "", const String & protocol = "arduino");
virtual ~WebSocketsServerCore(void);
void begin(void);
void close(void);
#ifdef __AVR__
typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue);
#else
typedef std::function<void(uint8_t num, WStype_t type, uint8_t * payload, size_t length)> WebSocketServerEvent;
typedef std::function<bool(String headerName, String headerValue)> WebSocketServerHttpHeaderValFunc;
#endif
void onEvent(WebSocketServerEvent cbEvent);
void onValidateHttpHeader(
WebSocketServerHttpHeaderValFunc validationFunc,
const char * mandatoryHttpHeaders[],
size_t mandatoryHttpHeaderCount);
bool sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
bool sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0);
bool sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false);
bool sendTXT(uint8_t num, const char * payload, size_t length = 0);
bool sendTXT(uint8_t num, String & payload);
bool broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
bool broadcastTXT(const uint8_t * payload, size_t length = 0);
bool broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false);
bool broadcastTXT(const char * payload, size_t length = 0);
bool broadcastTXT(String & payload);
bool sendBIN(uint8_t num, uint8_t * payload, size_t length, bool fin = true, bool continuation = false, bool headerToPayload = false);
bool sendBIN(uint8_t num, const uint8_t * payload, size_t length);
bool broadcastBIN(uint8_t * payload, size_t length, bool fin = true, bool continuation = false, bool headerToPayload = false);
bool broadcastBIN(const uint8_t * payload, size_t length);
bool sendPing(uint8_t num, uint8_t * payload = NULL, size_t length = 0);
bool sendPing(uint8_t num, String & payload);
bool broadcastPing(uint8_t * payload = NULL, size_t length = 0);
bool broadcastPing(String & payload);
void disconnect(void);
void disconnect(uint8_t num);
void setAuthorization(const char * user, const char * password);
void setAuthorization(const char * auth);
int connectedClients(bool ping = false);
bool clientIsConnected(uint8_t num);
void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
void disableHeartbeat();
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
IPAddress remoteIP(uint8_t num);
#endif
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void loop(void); // handle client data only
#endif
WSclient_t * newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
protected:
String _origin;
String _protocol;
String _base64Authorization; ///< Base64 encoded Auth request
String * _mandatoryHttpHeaders;
size_t _mandatoryHttpHeaderCount;
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
WebSocketServerEvent _cbEvent;
WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc;
bool _runnning;
uint32_t _pingInterval;
uint32_t _pongTimeout;
uint8_t _disconnectTimeoutCount;
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
void clientDisconnect(WSclient_t * client);
bool clientIsConnected(WSclient_t * client);
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void handleClientData(void);
#endif
void handleHeader(WSclient_t * client, String * headerLine);
void handleHBPing(WSclient_t * client); // send ping in specified intervals
/**
* called if a non Websocket connection is coming in.
* Note: can be override
* @param client WSclient_t * ptr to the client struct
*/
virtual void handleNonWebsocketConnection(WSclient_t * client) {
DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num);
client->tcp->write(
"HTTP/1.1 400 Bad Request\r\n"
"Server: arduino-WebSocket-Server\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 32\r\n"
"Connection: close\r\n"
"Sec-WebSocket-Version: 13\r\n"
"\r\n"
"This is a Websocket server only!");
clientDisconnect(client);
}
/**
* called if a non Authorization connection is coming in.
* Note: can be override
* @param client WSclient_t * ptr to the client struct
*/
virtual void handleAuthorizationFailed(WSclient_t * client) {
client->tcp->write(
"HTTP/1.1 401 Unauthorized\r\n"
"Server: arduino-WebSocket-Server\r\n"
"Content-Type: text/plain\r\n"
"Content-Length: 45\r\n"
"Connection: close\r\n"
"Sec-WebSocket-Version: 13\r\n"
"WWW-Authenticate: Basic realm=\"WebSocket Server\""
"\r\n"
"This Websocket server requires Authorization!");
clientDisconnect(client);
}
/**
* called for sending a Event to the app
* @param num uint8_t
* @param type WStype_t
* @param payload uint8_t *
* @param length size_t
*/
virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
if(_cbEvent) {
_cbEvent(num, type, payload, length);
}
}
/*
* Called at client socket connect handshake negotiation time for each http header that is not
* a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*)
* If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the
* socket negotiation is considered invalid and the upgrade to websockets request is denied / rejected
* This mechanism can be used to enable custom authentication schemes e.g. test the value
* of a session cookie to determine if a user is logged on / authenticated
*/
virtual bool execHttpHeaderValidation(String headerName, String headerValue) {
if(_httpHeaderValidationFunc) {
// return the value of the custom http header validation function
return _httpHeaderValidationFunc(headerName, headerValue);
}
// no custom http header validation so just assume all is good
return true;
}
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
WSclient_t * handleNewClient(WEBSOCKETS_NETWORK_CLASS * tcpClient);
#endif
/**
* drop native tcp connection (client->tcp)
*/
void dropNativeClient(WSclient_t * client);
private:
/*
* returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
* @param headerName String ///< the name of the header being checked
*/
bool hasMandatoryHeader(String headerName);
};
class WebSocketsServer : public WebSocketsServerCore {
public:
WebSocketsServer(uint16_t port, const String & origin = "", const String & protocol = "arduino");
virtual ~WebSocketsServer(void);
void begin(void);
void close(void);
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void loop(void); // handle incoming client and client data
#else
// Async interface not need a loop call
void loop(void) __attribute__((deprecated)) {
}
#endif
protected:
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
void handleNewClients(void);
#endif
uint16_t _port;
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
};
#endif /* WEBSOCKETSSERVER_H_ */

View File

@@ -0,0 +1,36 @@
/**
* @file WebSocketsVersion.h
* @date 05.04.2022
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef WEBSOCKETSVERSION_H_
#define WEBSOCKETSVERSION_H_
#define WEBSOCKETS_VERSION "2.3.7"
#define WEBSOCKETS_VERSION_MAJOR 2
#define WEBSOCKETS_VERSION_MINOR 3
#define WEBSOCKETS_VERSION_PATCH 7
#define WEBSOCKETS_VERSION_INT 2003007
#endif /* WEBSOCKETSVERSION_H_ */

View File

@@ -0,0 +1,134 @@
#!/bin/bash
set -ex
function build_sketches()
{
local arduino=$1
local srcpath=$2
local platform=$3
local sketches=$(find $srcpath -name *.ino)
for sketch in $sketches; do
local sketchdir=$(dirname $sketch)
if [[ -f "$sketchdir/.$platform.skip" ]]; then
echo -e "\n\n ------------ Skipping $sketch ------------ \n\n";
continue
fi
echo -e "\n\n ------------ Building $sketch ------------ \n\n";
$arduino --verify $sketch;
local result=$?
if [ $result -ne 0 ]; then
echo "Build failed ($sketch) build verbose..."
$arduino --verify --verbose --preserve-temp-files $sketch
result=$?
fi
if [ $result -ne 0 ]; then
echo "Build failed ($1) $sketch"
return $result
fi
done
}
function build_sketch()
{
local arduino=$1
local sketch=$2
$arduino --verify $sketch;
local result=$?
if [ $result -ne 0 ]; then
echo "Build failed ($sketch) build verbose..."
$arduino --verify --verbose --preserve-temp-files $sketch
result=$?
fi
if [ $result -ne 0 ]; then
echo "Build failed ($1) $sketch"
return $result
fi
}
function get_sketches_json()
{
local arduino=$1
local srcpath=$2
local platform=$3
local sketches=($(find $srcpath -name *.ino))
echo -en "["
for sketch in "${sketches[@]}" ; do
local sketchdir=$(dirname $sketch)
if [[ -f "$sketchdir/.$platform.skip" ]]; then
continue
fi
echo -en "\"$sketch\""
if [[ $sketch != ${sketches[-1]} ]] ; then
echo -en ","
fi
done
echo -en "]"
}
function get_sketches_json_matrix()
{
local arduino=$1
local srcpath=$2
local platform=$3
local ideversion=$4
local board=$5
local sketches=($(find $srcpath -name *.ino))
for sketch in "${sketches[@]}" ; do
local sketchdir=$(dirname $sketch)
local sketchname=$(basename $sketch)
if [[ -f "$sketchdir/.$platform.skip" ]]; then
continue
fi
echo -en "{\"name\":\"$sketchname\",\"board\":\"$board\",\"ideversion\":\"$ideversion\",\"cpu\":\"$platform\",\"sketch\":\"$sketch\"}"
if [[ $sketch != ${sketches[-1]} ]] ; then
echo -en ","
fi
done
}
function get_core()
{
echo Setup core for $1
cd $HOME/arduino_ide/hardware
if [ "$1" = "esp8266" ] ; then
mkdir esp8266com
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
fi
if [ "$1" = "esp32" ] ; then
mkdir espressif
cd espressif
git clone --depth 1 https://github.com/espressif/arduino-esp32.git esp32
cd esp32/
rm -rf .git
cd tools
python get.py
fi
}
function clone_library() {
local url=$1
echo clone $(basename $url)
mkdir -p $HOME/Arduino/libraries
cd $HOME/Arduino/libraries
git clone --depth 1 $url
rm -rf */.git
rm -rf */.github
rm -rf */examples
}
function hash_library_names() {
cd $HOME/Arduino/libraries
ls | sha1sum -z | cut -c1-5
}

View File

@@ -0,0 +1,132 @@
#!/usr/bin/python3
import json
import configparser
import argparse
import re
import os
import datetime
travis_dir = os.path.dirname(os.path.abspath(__file__))
base_dir = os.path.abspath(travis_dir + "/../")
def write_header_file(version):
hvs = version.split('.')
intversion = int(hvs[0]) * 1000000 + int(hvs[1]) * 1000 + int(hvs[2])
now = datetime.datetime.now()
text = f'''/**
* @file WebSocketsVersion.h
* @date {now.strftime("%d.%m.%Y")}
* @author Markus Sattler
*
* Copyright (c) 2015 Markus Sattler. All rights reserved.
* This file is part of the WebSockets for Arduino.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifndef WEBSOCKETSVERSION_H_
#define WEBSOCKETSVERSION_H_
#define WEBSOCKETS_VERSION "{version}"
#define WEBSOCKETS_VERSION_MAJOR {hvs[0]}
#define WEBSOCKETS_VERSION_MINOR {hvs[1]}
#define WEBSOCKETS_VERSION_PATCH {hvs[2]}
#define WEBSOCKETS_VERSION_INT {intversion}
#endif /* WEBSOCKETSVERSION_H_ */
'''
with open(f'{base_dir}/src/WebSocketsVersion.h', 'w') as f:
f.write(text)
def get_library_properties_version():
library_properties = {}
with open(f'{base_dir}/library.properties', 'r') as f:
library_properties = configparser.ConfigParser()
library_properties.read_string('[root]\n' + f.read())
return library_properties['root']['version']
def get_library_json_version():
library_json = {}
with open(f'{base_dir}/library.json', 'r') as f:
library_json = json.load(f)
return library_json['version']
def get_header_versions():
data = {}
define = re.compile('^#define WEBSOCKETS_VERSION_?(.*) "?([0-9\.]*)"?$')
with open(f'{base_dir}/src/WebSocketsVersion.h', 'r') as f:
for line in f:
m = define.match(line)
if m:
name = m[1]
if name == "":
name = "VERSION"
data[name] = m[2]
return data
parser = argparse.ArgumentParser(description='Checks and update Version files')
parser.add_argument(
'--update', action='store_true', default=False)
parser.add_argument(
'--check', action='store_true', default=True)
args = parser.parse_args()
if args.update:
library_properties_version = get_library_properties_version()
with open(f'{base_dir}/library.json', 'r') as f:
library_json = json.load(f)
library_json['version'] = library_properties_version
with open(f'{base_dir}/library.json', 'w') as f:
json.dump(library_json, f, indent=4, sort_keys=True)
write_header_file(library_properties_version)
library_json_version = get_library_json_version()
library_properties_version = get_library_properties_version()
header_version = get_header_versions()
print("WebSocketsVersion.h", header_version)
print(f"library.json: {library_json_version}")
print(f"library.properties: {library_properties_version}")
if args.check:
if library_json_version != library_properties_version or header_version['VERSION'] != library_properties_version:
raise Exception('versions did not match!')
hvs = header_version['VERSION'].split('.')
if header_version['MAJOR'] != hvs[0]:
raise Exception('header MAJOR version wrong!')
if header_version['MINOR'] != hvs[1]:
raise Exception('header MINOR version wrong!')
if header_version['PATCH'] != hvs[2]:
raise Exception('header PATCH version wrong!')
intversion = int(hvs[0]) * 1000000 + int(hvs[1]) * 1000 + int(hvs[2])
if int(header_version['INT']) != intversion:
raise Exception('header INT version wrong!')

View File

@@ -105,7 +105,7 @@ void TickerScheduler::update()
{
if (this->items[i].is_used)
{
#ifdef ARDUINO_ARCH_AVR
#if defined ARDUINO_ARCH_AVR || defined LIBRETINY
this->items[i].t.Tick();
#endif

View File

@@ -7,7 +7,7 @@
#include <stdint.h>
#ifdef ARDUINO_ARCH_AVR
#if defined ARDUINO_ARCH_AVR || defined LIBRETINY
class Ticker
{
typedef void(*ticker_callback_t)(bool*);
@@ -41,12 +41,17 @@ public:
this->is_attached = true;
}
};
#endif
#else
//#ifdef ARDUINO_ARCH_ESP8266
#include <Ticker.h>
#include <functional>
//#endif
#endif
#if defined LIBRETINY
#include <functional>
#endif
void tickerFlagHandle(volatile bool * flag);

View File

@@ -25,6 +25,14 @@
#include "WebSockets.h"
#include "WebSocketsServer.h"
#ifdef ESP32
#if defined __has_include
#if __has_include("soc/wdev_reg.h")
#include "soc/wdev_reg.h"
#endif // __has_include
#endif // defined __has_include
#endif
WebSocketsServerCore::WebSocketsServerCore(const String & origin, const String & protocol) {
_origin = origin;
_protocol = protocol;
@@ -65,6 +73,7 @@ WebSocketsServerCore::~WebSocketsServerCore() {
}
WebSocketsServer::~WebSocketsServer() {
delete _server;
}
/**
@@ -82,9 +91,13 @@ void WebSocketsServerCore::begin(void) {
#ifdef ESP8266
randomSeed(RANDOM_REG32);
#elif defined(ESP32) && defined(WDEV_RND_REG)
randomSeed(REG_READ(WDEV_RND_REG));
#elif defined(ESP32)
#define DR_REG_RNG_BASE 0x3ff75144
randomSeed(READ_PERI_REG(DR_REG_RNG_BASE));
#elif defined(ARDUINO_ARCH_RP2040)
randomSeed(rp2040.hwrand32());
#else
// TODO find better seed
randomSeed(millis());
@@ -400,7 +413,7 @@ bool WebSocketsServerCore::clientIsConnected(uint8_t num) {
return clientIsConnected(client);
}
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_RP2040)
/**
* get an IP for a client
* @param num uint8_t client id
@@ -432,8 +445,16 @@ WSclient_t * WebSocketsServerCore::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclien
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
client = &_clients[i];
// look for match to existing socket before creating a new one
if(clientIsConnected(client)) {
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
// Check to see if it is the same socket - if so, return it
if(client->tcp->getSocketNumber() == TCPclient->getSocketNumber()) {
return client;
}
#endif
} else {
// state is not connected or tcp connection is lost
if(!clientIsConnected(client)) {
client->tcp = TCPclient;
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
@@ -445,7 +466,7 @@ WSclient_t * WebSocketsServerCore::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclien
client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
#endif
client->status = WSC_HEADER;
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_RP2040)
#ifndef NODEBUG_WEBSOCKETS
IPAddress ip = client->tcp->remoteIP();
#endif
@@ -527,7 +548,7 @@ void WebSocketsServerCore::dropNativeClient(WSclient_t * client) {
}
if(client->tcp) {
if(client->tcp->connected()) {
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) && (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP32)
#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC) && (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP32) && (WEBSOCKETS_NETWORK_TYPE != NETWORK_RP2040)
client->tcp->flush();
#endif
client->tcp->stop();
@@ -546,7 +567,7 @@ void WebSocketsServerCore::dropNativeClient(WSclient_t * client) {
* @param client WSclient_t * ptr to the client struct
*/
void WebSocketsServerCore::clientDisconnect(WSclient_t * client) {
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_RP2040)
if(client->isSSL && client->ssl) {
if(client->ssl->connected()) {
client->ssl->flush();
@@ -620,7 +641,7 @@ WSclient_t * WebSocketsServerCore::handleNewClient(WEBSOCKETS_NETWORK_CLASS * tc
if(!client) {
// no free space to handle client
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_RP2040)
#ifndef NODEBUG_WEBSOCKETS
IPAddress ip = tcpClient->remoteIP();
#endif
@@ -633,6 +654,7 @@ WSclient_t * WebSocketsServerCore::handleNewClient(WEBSOCKETS_NETWORK_CLASS * tc
client = &dummy;
client->tcp = tcpClient;
dropNativeClient(client);
return nullptr;
}
WEBSOCKETS_YIELD();
@@ -644,12 +666,16 @@ WSclient_t * WebSocketsServerCore::handleNewClient(WEBSOCKETS_NETWORK_CLASS * tc
* Handle incoming Connection Request
*/
void WebSocketsServer::handleNewClients(void) {
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_RP2040)
while(_server->hasClient()) {
#endif
// store new connection
WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available());
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available()); //available
#else
WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->accept()); //available
#endif
if(!tcpClient) {
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
return;
@@ -657,7 +683,7 @@ void WebSocketsServer::handleNewClients(void) {
handleNewClient(tcpClient);
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_RP2040)
}
#endif
}
@@ -928,7 +954,7 @@ void WebSocketsServer::begin(void) {
void WebSocketsServer::close(void) {
WebSocketsServerCore::close();
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_RP2040)
_server->close();
#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
_server->end();

View File

@@ -90,7 +90,7 @@ class WebSocketsServerCore : protected WebSockets {
void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
void disableHeartbeat();
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_RP2040)
IPAddress remoteIP(uint8_t num);
#endif

View File

@@ -15,17 +15,19 @@
"mqttUser": "rise",
"mqttPass": "3hostel3",
"serverip": "http://iotmanager.org",
"serverlocal": "http://192.168.1.2:5500",
"log": 0,
"mqttin": 0,
"pinSCL": 0,
"pinSDA": 0,
"i2cFreq": 100000,
"wg": "group1"
"wg": "group1",
"debugTraceMsgTlgrm": 1
},
"projectProp": {
"platformio": {
"default_envs": "esp8266_4mb",
"comments_default_envs": "choose from: esp8266_4mb or esp32_4mb or esp32cam_4mb or esp32s2_4mb or esp32_4mb3f or esp32s3_16mb or esp32c3m_4mb or esp8266_1mb or esp8266_1mb_ota or esp8285_1mb or esp8285_1mb_ota",
"default_envs": "esp32_4mb3f",
"comments_default_envs": "choose from: esp8266_4mb, esp32_4mb, esp32_4mb3f, esp8266_16mb, esp32_16mb, esp32cam_4mb, esp32s2_4mb, esp32s3_16mb, esp32c3m_4mb, esp8266_1mb, esp8266_1mb_ota, esp8266_2mb, esp8266_2mb_ota, esp8285_1mb, esp8285_1mb_ota, esp32c6_4mb, esp32c6_8mb, bk7231n",
"envs": [
{
"name": "esp8266_4mb",
@@ -122,6 +124,22 @@
"firmware": "0x10000",
"partitions": "0x8000",
"littlefs": "0x910000"
},
{
"name": "esp32c6_4mb",
"boot_app0": "0xe000",
"bootloader_qio_80m": "0x1000",
"firmware": "0x10000",
"partitions": "0x8000",
"littlefs": "0x310000"
},
{
"name": "esp32c6_8mb",
"boot_app0": "0xe000",
"bootloader_qio_80m": "0x1000",
"firmware": "0x10000",
"partitions": "0x8000",
"littlefs": "0x670000"
}
]
}
@@ -136,6 +154,14 @@
"path": "src/modules/virtual/Cron",
"active": true
},
{
"path": "src/modules/virtual/DiscoveryHA",
"active": false
},
{
"path": "src/modules/virtual/DiscoveryHomeD",
"active": false
},
{
"path": "src/modules/virtual/GoogleSheet",
"active": false
@@ -148,6 +174,10 @@
"path": "src/modules/virtual/LogingDaily",
"active": true
},
{
"path": "src/modules/virtual/LogingHourly",
"active": true
},
{
"path": "src/modules/virtual/Math",
"active": true
@@ -164,6 +194,10 @@
"path": "src/modules/virtual/Timer",
"active": true
},
{
"path": "src/modules/virtual/UpdateServer",
"active": true
},
{
"path": "src/modules/virtual/Variable",
"active": true
@@ -179,16 +213,12 @@
],
"sensors": [
{
"path": "src/modules/exec/Pcf8591",
"path": "src/modules/sensors/A02Distance",
"active": false
},
{
"path": "src/modules/sensors/A02Distance",
"active": true
},
{
"path": "src/modules/sensors/Acs712",
"active": true
"active": false
},
{
"path": "src/modules/sensors/Ads1115",
@@ -208,7 +238,11 @@
},
{
"path": "src/modules/sensors/BL0937",
"active": true
"active": false
},
{
"path": "src/modules/sensors/BL0942",
"active": false
},
{
"path": "src/modules/sensors/Ble",
@@ -306,12 +340,24 @@
"path": "src/modules/sensors/Mhz19",
"active": false
},
{
"path": "src/modules/sensors/ModbusRTU",
"active": false
},
{
"path": "src/modules/sensors/ModbusRTUasync",
"active": false
},
{
"path": "src/modules/sensors/MQgas",
"active": true
"active": false
},
{
"path": "src/modules/sensors/Ntc",
"active": true
},
{
"path": "src/modules/sensors/Pcf8591",
"active": false
},
{
@@ -320,7 +366,7 @@
},
{
"path": "src/modules/sensors/Pzem004t_v2",
"active": true
"active": false
},
{
"path": "src/modules/sensors/RCswitch",
@@ -332,7 +378,7 @@
},
{
"path": "src/modules/sensors/S8",
"active": true
"active": false
},
{
"path": "src/modules/sensors/Scd40",
@@ -348,15 +394,15 @@
},
{
"path": "src/modules/sensors/Sht20",
"active": true
"active": false
},
{
"path": "src/modules/sensors/Sht30",
"active": true
"active": false
},
{
"path": "src/modules/sensors/Sonar",
"active": true
"active": false
},
{
"path": "src/modules/sensors/UART",
@@ -384,6 +430,10 @@
"path": "src/modules/exec/Buzzer",
"active": true
},
{
"path": "src/modules/exec/EctoControlAdapter",
"active": false
},
{
"path": "src/modules/exec/Enconder",
"active": true
@@ -402,7 +452,11 @@
},
{
"path": "src/modules/exec/IoTServo",
"active": true
"active": false
},
{
"path": "src/modules/exec/IRremote",
"active": false
},
{
"path": "src/modules/exec/Mcp23008",
@@ -410,11 +464,11 @@
},
{
"path": "src/modules/exec/Mcp23017",
"active": true
"active": false
},
{
"path": "src/modules/exec/Mp3",
"active": true
"active": false
},
{
"path": "src/modules/exec/Multitouch",
@@ -426,7 +480,7 @@
},
{
"path": "src/modules/exec/Pcf8574",
"active": true
"active": false
},
{
"path": "src/modules/exec/Pwm32",
@@ -434,7 +488,7 @@
},
{
"path": "src/modules/exec/Pwm8266",
"active": true
"active": false
},
{
"path": "src/modules/exec/SDcard",
@@ -466,7 +520,7 @@
},
{
"path": "src/modules/exec/Thermostat",
"active": false
"active": true
},
{
"path": "src/modules/sensors/Ds2423",
@@ -476,11 +530,15 @@
"screens": [
{
"path": "src/modules/display/DwinI",
"active": true
"active": false
},
{
"path": "src/modules/display/GyverLAMP",
"active": false
},
{
"path": "src/modules/display/Lcd2004",
"active": true
"active": false
},
{
"path": "src/modules/display/Nextion",
@@ -496,11 +554,11 @@
},
{
"path": "src/modules/display/Oled64",
"active": true
"active": false
},
{
"path": "src/modules/display/Smi2_m",
"active": true
"active": false
},
{
"path": "src/modules/display/TM16XX",

View File

@@ -1,5 +1,5 @@
[platformio]
default_envs = esp8266_4mb
default_envs = esp32_4mb3f
data_dir = data_svelte
[common_env_data]
@@ -15,7 +15,9 @@ lib_deps =
${common_env_data.lib_deps_external}
${env:esp8266_1mb_ota_fromitems.lib_deps}
ESPAsyncUDP
build_flags = -Desp8266_1mb_ota="esp8266_1mb_ota"
lib_ignore = LT_WebSockets
build_flags =
-Desp8266_1mb_ota="esp8266_1mb_ota"
framework = arduino
board = nodemcuv2
board_build.ldscript = eagle.flash.1m64.ld
@@ -36,7 +38,9 @@ lib_deps =
${common_env_data.lib_deps_external}
${env:esp8266_1mb_fromitems.lib_deps}
ESPAsyncUDP
build_flags = -Desp8266_1mb="esp8266_1mb"
lib_ignore = LT_WebSockets
build_flags =
-Desp8266_1mb="esp8266_1mb"
framework = arduino
board = nodemcuv2
board_build.ldscript = eagle.flash.1m256.ld
@@ -57,7 +61,9 @@ lib_deps =
${common_env_data.lib_deps_external}
${env:esp8285_1mb_ota_fromitems.lib_deps}
ESPAsyncUDP
build_flags = -Desp8266_1mb_ota="esp8266_1mb_ota"
lib_ignore = LT_WebSockets
build_flags =
-Desp8266_1mb_ota="esp8266_1mb_ota"
framework = arduino
board = esp8285
board_build.ldscript = eagle.flash.1m64.ld
@@ -78,7 +84,9 @@ lib_deps =
${common_env_data.lib_deps_external}
${env:esp8266_2mb_fromitems.lib_deps}
ESPAsyncUDP
build_flags = -Desp8266_2mb="esp8266_2mb"
lib_ignore = LT_WebSockets
build_flags =
-Desp8266_2mb="esp8266_2mb"
framework = arduino
board = d1_wroom_02
board_build.ldscript = eagle.flash.2m1m.ld
@@ -99,7 +107,9 @@ lib_deps =
${common_env_data.lib_deps_external}
${env:esp8266_2mb_ota_fromitems.lib_deps}
ESPAsyncUDP
build_flags = -Desp8266_2mb_ota="esp8266_2mb_ota"
lib_ignore = LT_WebSockets
build_flags =
-Desp8266_2mb_ota="esp8266_2mb_ota"
framework = arduino
board = d1_wroom_02
board_build.ldscript = eagle.flash.2m256.ld
@@ -120,7 +130,9 @@ lib_deps =
${common_env_data.lib_deps_external}
${env:esp8285_1mb_fromitems.lib_deps}
ESPAsyncUDP
build_flags = -Desp8266_1mb="esp8266_1mb"
lib_ignore = LT_WebSockets
build_flags =
-Desp8266_1mb="esp8266_1mb"
framework = arduino
board = esp8285
board_build.ldscript = eagle.flash.1m256.ld
@@ -141,7 +153,9 @@ lib_deps =
${common_env_data.lib_deps_external}
${env:esp8266_4mb_fromitems.lib_deps}
ESPAsyncUDP
build_flags = -Desp8266_4mb="esp8266_4mb"
lib_ignore = LT_WebSockets
build_flags =
-Desp8266_4mb="esp8266_4mb"
framework = arduino
board = nodemcuv2
board_build.ldscript = eagle.flash.4m1m.ld
@@ -163,7 +177,9 @@ lib_deps =
${common_env_data.lib_deps_external}
${env:esp8266_16mb_fromitems.lib_deps}
ESPAsyncUDP
build_flags = -Desp8266_16mb="esp8266_16mb"
lib_ignore = LT_WebSockets
build_flags =
-Desp8266_16mb="esp8266_16mb"
framework = arduino
board = nodemcuv2
platform = espressif8266 @4.0.1
@@ -184,10 +200,13 @@ extra_scripts = pre:tools/patch32_ws.py
lib_deps =
${common_env_data.lib_deps_external}
${env:esp32_4mb_fromitems.lib_deps}
build_flags = -Desp32_4mb="esp32_4mb"
lib_ignore = LT_WebSockets
build_flags =
-Desp32_4mb="esp32_4mb"
-Wl,--wrap=esp_panic_handler
framework = arduino
board = esp32dev
platform = espressif32 @5.1.1
platform = espressif32 @6.6.0
monitor_filters = esp32_exception_decoder
upload_speed = 921600
monitor_speed = 115200
@@ -205,10 +224,13 @@ extra_scripts = pre:tools/patch32_ws.py
lib_deps =
${common_env_data.lib_deps_external}
${env:esp32_4mb3f_fromitems.lib_deps}
build_flags = -Desp32_4mb="esp32_4mb"
lib_ignore = LT_WebSockets
build_flags =
-Desp32_4mb3f="esp32_4mb3f"
-Wl,--wrap=esp_panic_handler
framework = arduino
board = esp32dev
platform = espressif32 @5.1.1
platform = espressif32 @6.6.0
monitor_filters = esp32_exception_decoder
upload_speed = 921600
monitor_speed = 115200
@@ -227,13 +249,15 @@ extra_scripts = pre:tools/patch32_ws.py
lib_deps =
${common_env_data.lib_deps_external}
${env:esp32cam_4mb_fromitems.lib_deps}
lib_ignore = LT_WebSockets
build_flags =
-Desp32cam_4mb="esp32cam_4mb"
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-Wl,--wrap=esp_panic_handler
framework = arduino
board = esp32cam
platform = espressif32 @5.1.1
platform = espressif32 @6.6.0
monitor_filters = esp32_exception_decoder
upload_speed = 921600
monitor_speed = 115200
@@ -251,13 +275,15 @@ extra_scripts = pre:tools/patch32_ws.py
lib_deps =
${common_env_data.lib_deps_external}
${env:esp32s2_4mb_fromitems.lib_deps}
lib_ignore = LT_WebSockets
build_flags =
-Desp32s2_4mb="esp32s2_4mb"
-DARDUINO_USB_CDC_ON_BOOT=1
-DARDUINO_USB_MODE=0
-Wl,--wrap=esp_panic_handler
framework = arduino
board = lolin_s2_mini
platform = espressif32 @6.3.1
platform = espressif32 @6.6.0
monitor_filters = esp32_exception_decoder
upload_speed = 921600
monitor_speed = 115200
@@ -275,11 +301,13 @@ extra_scripts = pre:tools/patch32_ws.py
lib_deps =
${common_env_data.lib_deps_external}
${env:esp32c3m_4mb_fromitems.lib_deps}
lib_ignore = LT_WebSockets
build_flags =
-Desp32c3m_4mb="esp32c3m_4mb"
-Wl,--wrap=esp_panic_handler
framework = arduino
board = lolin_c3_mini
platform = espressif32 @6.3.1
platform = espressif32 @6.6.0
monitor_filters = esp32_exception_decoder
upload_speed = 921600
monitor_speed = 115200
@@ -298,12 +326,14 @@ extra_scripts = pre:tools/patch32_ws.py
lib_deps =
${common_env_data.lib_deps_external}
${env:esp32s3_16mb_fromitems.lib_deps}
lib_ignore = LT_WebSockets
build_flags =
-Desp32s3_16mb="esp32s3_16mb"
-Wl,--wrap=esp_panic_handler
framework = arduino
board = esp32-s3-devkitc-1
board_build.mcu = esp32s3
platform = espressif32 @6.3.1
platform = espressif32 @6.6.0
monitor_filters = esp32_exception_decoder
upload_speed = 921600
monitor_speed = 115200
@@ -323,10 +353,13 @@ extra_scripts = pre:tools/patch32_ws.py
lib_deps =
${common_env_data.lib_deps_external}
${env:esp32_16mb_fromitems.lib_deps}
build_flags = -Desp32_16mb="esp32_16mb"
lib_ignore = LT_WebSockets
build_flags =
-Desp32_16mb="esp32_16mb"
-Wl,--wrap=esp_panic_handler
framework = arduino
board = esp32dev
platform = espressif32 @5.1.1
platform = espressif32 @6.6.0
monitor_filters = esp32_exception_decoder
upload_port = COM11
upload_speed = 921600
@@ -342,6 +375,102 @@ build_src_filter =
+<modules/*.cpp>
${env:esp32_16mb_fromitems.build_src_filter}
[env:esp32c6_4mb]
extra_scripts = pre:tools/patch32c6.py
lib_deps =
${common_env_data.lib_deps_external}
${env:esp32c6_4mb_fromitems.lib_deps}
lib_ignore = LT_WebSockets
build_flags =
-Desp32c6_4mb="esp32c6_4mb"
-DARDUINO_USB_CDC_ON_BOOT=0
-DARDUINO_USB_MODE=0
-Wl,--wrap=esp_panic_handler
framework = arduino
board = esp32-c6-devkitm-1
platform = espressif32 @6.9.0
platform_packages =
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1
framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip
monitor_filters = esp32_exception_decoder
upload_speed = 921600
monitor_speed = 115200
debug_tool = esp-prog
board_build.partitions = tools/partitions_custom.csv
board_build.filesystem = littlefs
build_src_filter =
+<*.cpp>
+<classes/*.cpp>
+<utils/*.cpp>
+<modules/*.cpp>
${env:esp32c6_4mb_fromitems.build_src_filter}
[env:esp32c6_8mb]
extra_scripts = pre:tools/patch32c6.py
lib_deps =
${common_env_data.lib_deps_external}
${env:esp32c6_8mb_fromitems.lib_deps}
lib_ignore = LT_WebSockets
build_flags =
-Desp32c6_8mb="esp32c6_8mb"
-DARDUINO_USB_CDC_ON_BOOT=0
-DARDUINO_USB_MODE=0
-Wl,--wrap=esp_panic_handler
framework = arduino
board = esp32-c6-devkitm-1
platform = espressif32 @6.9.0
platform_packages =
framework-arduinoespressif32 @ https://github.com/espressif/arduino-esp32.git#3.0.1
framework-arduinoespressif32-libs @ https://github.com/espressif/arduino-esp32/releases/download/3.0.1/esp32-arduino-libs-3.0.1.zip
monitor_filters = esp32_exception_decoder
upload_speed = 921600
monitor_speed = 115200
debug_tool = esp-prog
board_build.partitions = tools/partitions_custom_8mb.csv
board_upload.flash_size = 8MB
board_build.filesystem = littlefs
build_src_filter =
+<*.cpp>
+<classes/*.cpp>
+<utils/*.cpp>
+<modules/*.cpp>
${env:esp32c6_8mb_fromitems.build_src_filter}
[env:bk7231n]
extra_scripts =
pre:tools/lt_fsbuild.py
pre:tools/lt_fsflash.py
lib_compat_mode = off
lib_deps =
LT_WebSockets
https://github.com/Mit4el/ESPAsyncUDP#master
${common_env_data.lib_deps_external}
${env:bk7231n_fromitems.lib_deps}
lib_ignore = EspSoftwareSerial, HTTPUpdate, WebSockets
platform = https://github.com/Mit4el/libretiny#master
framework = arduino
board = generic-bk7231n-qfn32-tuya
custom_fw_name = iotm_tiny
custom_fw_version = 0.0.1
upload_speed = 921600
monitor_speed = 115200
build_flags =
-Dbk7231n="bk7231n"
-DLT_LOGLEVEL=LT_LEVEL_DEBUG
-DLT_DEBUG_ALL=1
-DPROJECT_DATA_DIR="data_svelte"
-DLT_USE_TIME=1
-DDEBUG_ESP_PORT=Serial1
-DNODEBUG_WEBSOCKETS=1
-DLT_UART_DEFAULT_PORT=1
-DUPLOAD_PORT=auto
build_src_filter =
+<*.cpp>
+<classes/*.cpp>
+<utils/*.cpp>
+<modules/*.cpp>
${env:bk7231n_fromitems.build_src_filter}
[env:esp8266_1mb_ota_fromitems]
lib_deps =
adafruit/Adafruit BME280 Library
@@ -544,62 +673,38 @@ lib_deps =
adafruit/Adafruit BMP280 Library
beegee-tokyo/DHT sensor library for ESPx
https://github.com/milesburton/Arduino-Temperature-Control-Library
robtillaart/SHT2x@^0.1.1
WEMOS SHT3x@1.0.0
plerup/EspSoftwareSerial
gyverlibs/EncButton @ ^2.0
adafruit/Adafruit MCP23017 Arduino Library@^2.1.0
adafruit/Adafruit BusIO @ ^1.13.2
dfrobot/DFRobotDFPlayerMini @ ^1.0.5
adafruit/Adafruit BusIO @ ^1.13.2
plerup/EspSoftwareSerial
https://github.com/robotclass/RobotClass_LiquidCrystal_I2C
marcoschwartz/LiquidCrystal_I2C@^1.1.4
https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED
https://github.com/adafruit/Adafruit-GFX-Library
build_src_filter =
+<modules/virtual/Cron>
+<modules/virtual/Loging>
+<modules/virtual/LogingDaily>
+<modules/virtual/LogingHourly>
+<modules/virtual/Math>
+<modules/virtual/owmWeather>
+<modules/virtual/Ping>
+<modules/virtual/Timer>
+<modules/virtual/UpdateServer>
+<modules/virtual/Variable>
+<modules/virtual/VButton>
+<modules/sensors/A02Distance>
+<modules/sensors/Acs712>
+<modules/sensors/AhtXX>
+<modules/sensors/AnalogAdc>
+<modules/sensors/BL0937>
+<modules/sensors/Bme280>
+<modules/sensors/Bmp280>
+<modules/sensors/Dht1122>
+<modules/sensors/Ds18b20>
+<modules/sensors/Impulse>
+<modules/sensors/MQgas>
+<modules/sensors/Pzem004t_v2>
+<modules/sensors/Ntc>
+<modules/sensors/RTC>
+<modules/sensors/S8>
+<modules/sensors/Sht20>
+<modules/sensors/Sht30>
+<modules/sensors/Sonar>
+<modules/sensors/UART>
+<modules/exec/AnalogBtn>
+<modules/exec/ButtonIn>
+<modules/exec/ButtonOut>
+<modules/exec/Buzzer>
+<modules/exec/Enconder>
+<modules/exec/IoTServo>
+<modules/exec/Mcp23017>
+<modules/exec/Mp3>
+<modules/exec/Multitouch>
+<modules/exec/Pcf8574>
+<modules/exec/Pwm8266>
+<modules/exec/TelegramLT>
+<modules/display/DwinI>
+<modules/display/Lcd2004>
+<modules/display/Oled64>
+<modules/exec/Thermostat>
[env:esp32_4mb_fromitems]
lib_deps =
@@ -608,31 +713,20 @@ lib_deps =
adafruit/Adafruit BMP280 Library
beegee-tokyo/DHT sensor library for ESPx
https://github.com/milesburton/Arduino-Temperature-Control-Library
robtillaart/SHT2x@^0.1.1
WEMOS SHT3x@1.0.0
plerup/EspSoftwareSerial
gyverlibs/EncButton @ ^2.0
https://github.com/RoboticsBrno/ServoESP32#v1.0.3
adafruit/Adafruit MCP23017 Arduino Library@^2.1.0
adafruit/Adafruit BusIO @ ^1.13.2
dfrobot/DFRobotDFPlayerMini @ ^1.0.5
adafruit/Adafruit BusIO @ ^1.13.2
https://github.com/robotclass/RobotClass_LiquidCrystal_I2C
marcoschwartz/LiquidCrystal_I2C@^1.1.4
https://github.com/stblassitude/Adafruit_SSD1306_Wemos_OLED
https://github.com/adafruit/Adafruit-GFX-Library
https://github.com/maxint-rd/TM16xx
adafruit/Adafruit GFX Library @ ^1.11.5
adafruit/Adafruit BusIO @ ^1.13.2
build_src_filter =
+<modules/virtual/Cron>
+<modules/virtual/Loging>
+<modules/virtual/LogingDaily>
+<modules/virtual/LogingHourly>
+<modules/virtual/Math>
+<modules/virtual/owmWeather>
+<modules/virtual/Ping>
+<modules/virtual/Timer>
+<modules/virtual/UpdateServer>
+<modules/virtual/Variable>
+<modules/virtual/VButton>
+<modules/sensors/Acs712>
+<modules/sensors/AhtXX>
+<modules/sensors/AnalogAdc>
+<modules/sensors/Bme280>
@@ -640,29 +734,18 @@ build_src_filter =
+<modules/sensors/Dht1122>
+<modules/sensors/Ds18b20>
+<modules/sensors/Impulse>
+<modules/sensors/Pzem004t>
+<modules/sensors/Ntc>
+<modules/sensors/RTC>
+<modules/sensors/S8>
+<modules/sensors/Sht20>
+<modules/sensors/Sht30>
+<modules/sensors/Sonar>
+<modules/sensors/UART>
+<modules/exec/AnalogBtn>
+<modules/exec/ButtonIn>
+<modules/exec/ButtonOut>
+<modules/exec/Buzzer>
+<modules/exec/Enconder>
+<modules/exec/IoTServo>
+<modules/exec/Mcp23017>
+<modules/exec/Mp3>
+<modules/exec/Multitouch>
+<modules/exec/MySensors>
+<modules/exec/Pcf8574>
+<modules/exec/Pwm32>
+<modules/exec/TelegramLT>
+<modules/display/Lcd2004>
+<modules/display/Oled64>
+<modules/display/Smi2_m>
+<modules/display/TM16XX>
+<modules/exec/Thermostat>
[env:esp32_4mb3f_fromitems]
lib_deps =
@@ -671,29 +754,20 @@ lib_deps =
adafruit/Adafruit BMP280 Library
beegee-tokyo/DHT sensor library for ESPx
https://github.com/milesburton/Arduino-Temperature-Control-Library
https://github.com/tremaru/iarduino_RTC
robtillaart/SHT2x@^0.1.1
WEMOS SHT3x@1.0.0
plerup/EspSoftwareSerial
gyverlibs/EncButton @ ^2.0
https://github.com/RoboticsBrno/ServoESP32#v1.0.3
adafruit/Adafruit MCP23017 Arduino Library@^2.1.0
adafruit/Adafruit BusIO @ ^1.13.2
dfrobot/DFRobotDFPlayerMini @ ^1.0.5
adafruit/Adafruit BusIO @ ^1.13.2
https://github.com/robotclass/RobotClass_LiquidCrystal_I2C
marcoschwartz/LiquidCrystal_I2C@^1.1.4
https://github.com/maxint-rd/TM16xx
adafruit/Adafruit GFX Library @ ^1.11.5
build_src_filter =
+<modules/virtual/Cron>
+<modules/virtual/Loging>
+<modules/virtual/LogingDaily>
+<modules/virtual/LogingHourly>
+<modules/virtual/Math>
+<modules/virtual/owmWeather>
+<modules/virtual/Ping>
+<modules/virtual/Timer>
+<modules/virtual/UpdateServer>
+<modules/virtual/Variable>
+<modules/virtual/VariableColor>
+<modules/virtual/VButton>
+<modules/sensors/Acs712>
+<modules/sensors/AhtXX>
+<modules/sensors/AnalogAdc>
+<modules/sensors/Bme280>
@@ -701,27 +775,18 @@ build_src_filter =
+<modules/sensors/Dht1122>
+<modules/sensors/Ds18b20>
+<modules/sensors/Impulse>
+<modules/sensors/Pzem004t>
+<modules/sensors/Ntc>
+<modules/sensors/RTC>
+<modules/sensors/S8>
+<modules/sensors/Sht20>
+<modules/sensors/Sht30>
+<modules/sensors/Sonar>
+<modules/sensors/UART>
+<modules/exec/AnalogBtn>
+<modules/exec/ButtonIn>
+<modules/exec/ButtonOut>
+<modules/exec/Buzzer>
+<modules/exec/Enconder>
+<modules/exec/IoTServo>
+<modules/exec/Mcp23017>
+<modules/exec/Mp3>
+<modules/exec/Multitouch>
+<modules/exec/Pcf8574>
+<modules/exec/Pwm32>
+<modules/exec/TelegramLT>
+<modules/display/Lcd2004>
+<modules/display/Smi2_m>
+<modules/display/TM16XX>
+<modules/exec/Thermostat>
[env:esp32cam_4mb_fromitems]
lib_deps =
@@ -754,19 +819,44 @@ build_src_filter =
[env:esp8266_16mb_fromitems]
lib_deps =
https://github.com/dancol90/ESP8266Ping
https://github.com/enjoyneering/AHTxx.git
adafruit/Adafruit BME280 Library
adafruit/Adafruit BMP280 Library
beegee-tokyo/DHT sensor library for ESPx
https://github.com/milesburton/Arduino-Temperature-Control-Library
plerup/EspSoftwareSerial
gyverlibs/EncButton @ ^2.0
build_src_filter =
+<modules/virtual/Cron>
+<modules/virtual/Loging>
+<modules/virtual/LogingDaily>
+<modules/virtual/LogingHourly>
+<modules/virtual/Math>
+<modules/virtual/owmWeather>
+<modules/virtual/Ping>
+<modules/virtual/Timer>
+<modules/virtual/UpdateServer>
+<modules/virtual/Variable>
+<modules/virtual/VariableColor>
+<modules/virtual/VButton>
+<modules/sensors/AhtXX>
+<modules/sensors/AnalogAdc>
+<modules/sensors/Bme280>
+<modules/sensors/Bmp280>
+<modules/sensors/Dht1122>
+<modules/sensors/Ds18b20>
+<modules/sensors/Impulse>
+<modules/sensors/Ntc>
+<modules/sensors/RTC>
+<modules/sensors/UART>
+<modules/exec/AnalogBtn>
+<modules/exec/ButtonIn>
+<modules/exec/ButtonOut>
+<modules/exec/Buzzer>
+<modules/exec/Enconder>
+<modules/exec/Multitouch>
+<modules/exec/TelegramLT>
+<modules/exec/Thermostat>
[env:esp32_16mb_fromitems]
lib_deps =
@@ -804,3 +894,103 @@ build_src_filter =
+<modules/virtual/VariableColor>
+<modules/virtual/VButton>
[env:esp32c6_4mb_fromitems]
lib_deps =
https://github.com/enjoyneering/AHTxx.git
adafruit/Adafruit BME280 Library
adafruit/Adafruit BMP280 Library
beegee-tokyo/DHT sensor library for ESPx
https://github.com/pstolarz/Arduino-Temperature-Control-Library.git#OneWireNg
plerup/EspSoftwareSerial
gyverlibs/EncButton @ ^2.0
build_src_filter =
+<modules/virtual/Cron>
+<modules/virtual/Loging>
+<modules/virtual/LogingDaily>
+<modules/virtual/LogingHourly>
+<modules/virtual/Math>
+<modules/virtual/owmWeather>
+<modules/virtual/Ping>
+<modules/virtual/Timer>
+<modules/virtual/UpdateServer>
+<modules/virtual/Variable>
+<modules/virtual/VButton>
+<modules/sensors/AhtXX>
+<modules/sensors/AnalogAdc>
+<modules/sensors/Bme280>
+<modules/sensors/Bmp280>
+<modules/sensors/Dht1122>
+<modules/sensors/Ds18b20>
+<modules/sensors/Impulse>
+<modules/sensors/Ntc>
+<modules/sensors/RTC>
+<modules/sensors/UART>
+<modules/exec/AnalogBtn>
+<modules/exec/ButtonIn>
+<modules/exec/ButtonOut>
+<modules/exec/Buzzer>
+<modules/exec/Enconder>
+<modules/exec/Multitouch>
+<modules/exec/Pwm32>
+<modules/exec/TelegramLT>
+<modules/exec/Thermostat>
[env:esp32c6_8mb_fromitems]
lib_deps =
https://github.com/enjoyneering/AHTxx.git
adafruit/Adafruit BME280 Library
adafruit/Adafruit BMP280 Library
beegee-tokyo/DHT sensor library for ESPx
https://github.com/pstolarz/Arduino-Temperature-Control-Library.git#OneWireNg
plerup/EspSoftwareSerial
gyverlibs/EncButton @ ^2.0
build_src_filter =
+<modules/virtual/Cron>
+<modules/virtual/Loging>
+<modules/virtual/LogingDaily>
+<modules/virtual/LogingHourly>
+<modules/virtual/Math>
+<modules/virtual/owmWeather>
+<modules/virtual/Ping>
+<modules/virtual/Timer>
+<modules/virtual/UpdateServer>
+<modules/virtual/Variable>
+<modules/virtual/VButton>
+<modules/sensors/AhtXX>
+<modules/sensors/AnalogAdc>
+<modules/sensors/Bme280>
+<modules/sensors/Bmp280>
+<modules/sensors/Dht1122>
+<modules/sensors/Ds18b20>
+<modules/sensors/Impulse>
+<modules/sensors/Ntc>
+<modules/sensors/RTC>
+<modules/sensors/UART>
+<modules/exec/AnalogBtn>
+<modules/exec/ButtonIn>
+<modules/exec/ButtonOut>
+<modules/exec/Buzzer>
+<modules/exec/Enconder>
+<modules/exec/Multitouch>
+<modules/exec/Pwm32>
+<modules/exec/TelegramLT>
+<modules/exec/Thermostat>
[env:bk7231n_fromitems]
lib_deps =
build_src_filter =
+<modules/virtual/Cron>
+<modules/virtual/Loging>
+<modules/virtual/LogingDaily>
+<modules/virtual/LogingHourly>
+<modules/virtual/Math>
+<modules/virtual/owmWeather>
+<modules/virtual/Timer>
+<modules/virtual/Variable>
+<modules/virtual/VButton>
+<modules/sensors/AnalogAdc>
+<modules/exec/AnalogBtn>
+<modules/exec/ButtonIn>
+<modules/exec/ButtonOut>
+<modules/exec/TelegramLT>

352
src/DebugTrace.cpp Normal file
View File

@@ -0,0 +1,352 @@
#include "DebugTrace.h"
#if defined(RESTART_DEBUG_INFO)
// #ifdef RESTART_DEBUG_INFO
__NOINIT_ATTR static re_restart_debug_t _debug_info;
#include "esp_debug_helpers.h"
#include "esp_types.h"
#include "esp_attr.h"
#include "esp_err.h"
#include "soc/soc_memory_layout.h"
#include "soc/cpu.h"
// RU: Размер буфера для конвертации даты и времeни в строку
#define CONFIG_FORMAT_STRFTIME_BUFFER_SIZE 32
#define CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE 20 // YYYY.MM.DD HH:NN:SS + \n
// RU: Форматы даты и времени
#define CONFIG_FORMAT_DTS "%d.%m.%Y %H:%M:%S"
void IRAM_ATTR debugHeapUpdate()
{
_debug_info.heap_total = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
_debug_info.heap_free = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
size_t _new_free_min = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
if ((_debug_info.heap_free_min == 0) || (_new_free_min < _debug_info.heap_free_min))
{
_debug_info.heap_free_min = _new_free_min;
_debug_info.heap_min_time = time(nullptr);
};
}
void IRAM_ATTR debugBacktraceUpdate()
{
esp_backtrace_frame_t stk_frame;
esp_backtrace_get_start(&(stk_frame.pc), &(stk_frame.sp), &(stk_frame.next_pc));
_debug_info.backtrace[0] = esp_cpu_process_stack_pc(stk_frame.pc);
bool corrupted = (esp_stack_ptr_is_sane(stk_frame.sp) &&
esp_ptr_executable((void *)esp_cpu_process_stack_pc(stk_frame.pc)))
? false
: true;
uint8_t i = CONFIG_RESTART_DEBUG_STACK_DEPTH;
while (i-- > 0 && stk_frame.next_pc != 0 && !corrupted)
{
if (!esp_backtrace_get_next_frame(&stk_frame))
{
corrupted = true;
};
_debug_info.backtrace[CONFIG_RESTART_DEBUG_STACK_DEPTH - i] = esp_cpu_process_stack_pc(stk_frame.pc);
};
}
void IRAM_ATTR debugUpdate()
{
debugHeapUpdate();
debugBacktraceUpdate();
}
extern "C" void __wrap_esp_panic_handler(void *info)
{
debugHeapUpdate();
debugBacktraceUpdate();
bootloop_panic_count += 1;
// Call the original panic handler function to finish processing this error (creating a core dump for example...)
__real_esp_panic_handler(info);
}
re_restart_debug_t debugGet()
{
re_restart_debug_t ret;
memset(&ret, 0, sizeof(re_restart_debug_t));
esp_reset_reason_t esp_reason = esp_reset_reason();
if ((esp_reason != ESP_RST_UNKNOWN) && (esp_reason != ESP_RST_POWERON))
{
uint8_t i = CONFIG_RESTART_DEBUG_STACK_DEPTH;
ret = _debug_info;
if (_debug_info.heap_total > heap_caps_get_total_size(MALLOC_CAP_DEFAULT))
{
memset(&ret, 0, sizeof(re_restart_debug_t));
};
};
memset(&_debug_info, 0, sizeof(re_restart_debug_t));
return ret;
}
#define CONFIG_MESSAGE_TG_VERSION_DEF "! Устройство запущено\n\nИмя устройства: %s\nПричина перезапуска: %s\nCPU0: %s\nCPU1: %s"
#define CONFIG_MESSAGE_TG_VERSION_HEAP "! Устройство аварийно перезапущено !\n\nИмя устройства: %s\nПричина перезапуска: %s\nCPU0: %s\nCPU1: %s\nHEAP: %s"
#define CONFIG_MESSAGE_TG_VERSION_TRACE "! Устройство аварийно перезапущено !\n\nИмя устройства: %s\nПричина перезапуска: %s\nCPU0: %s\nCPU1: %s\nHEAP: %s\nTRACE: %s"
#define INFO_MESSAGE_DEBUG "By used -> USERPROFILE/.platformio/packages/toolchain-xtensa-esp32@8.4.0+2021r2-patch5/bin xtensa-esp32-elf-addr2line.exe -pfiaC -e .pio/build/esp32_4mb3f/firmware.elf Стэк_адресов"
char *malloc_stringf(const char *format, ...)
{
char *ret = nullptr;
if (format != nullptr)
{
// get the list of arguments
va_list args1, args2;
va_start(args1, format);
va_copy(args2, args1);
// calculate length of resulting string
int len = vsnprintf(nullptr, 0, format, args1);
va_end(args1);
// allocate memory for string
if (len > 0)
{
#if USE_ESP_MALLOC
ret = (char *)esp_malloc(len + 1);
#else
ret = (char *)malloc(len + 1);
#endif
if (ret != nullptr)
{
memset(ret, 0, len + 1);
vsnprintf(ret, len + 1, format, args2);
}
else
{
// rlog_e(tagHEAP, "Failed to format string: out of memory!");
};
};
va_end(args2);
};
return ret;
}
static char *statesGetDebugHeap(re_restart_debug_t *debug)
{
if ((debug->heap_total > 0) && (debug->heap_total > debug->heap_free))
{
struct tm timeinfo;
localtime_r(&debug->heap_min_time, &timeinfo);
char time_buffer[CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE];
memset(&time_buffer, 0, CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE);
strftime(time_buffer, CONFIG_FORMAT_STRFTIME_DTS_BUFFER_SIZE, CONFIG_FORMAT_DTS, &timeinfo);
double heapTotal = (double)debug->heap_total / 1024;
double heapFree = (double)debug->heap_free / 1024;
double heapFreeMin = (double)debug->heap_free_min / 1024;
return malloc_stringf("Total %.1fkB ; Free %.1fkB (%.1f%%) ; FreeMin %.1fkB (%.1f%%) %s",
heapTotal,
heapFree, 100.0 * (heapFree / heapTotal),
heapFreeMin, 100.0 * (heapFreeMin / heapTotal), time_buffer);
};
return nullptr;
}
static char *statesGetDebugTrace(re_restart_debug_t *debug)
{
char *backtrace = nullptr;
char *item = nullptr;
char *temp = nullptr;
for (uint8_t i = 0; i < CONFIG_RESTART_DEBUG_STACK_DEPTH; i++)
{
if (debug->backtrace[i] != 0)
{
item = malloc_stringf("0x%08x", debug->backtrace[i]);
if (item)
{
if (backtrace)
{
temp = backtrace;
backtrace = malloc_stringf("%s %s", temp, item);
free(item);
free(temp);
}
else
{
backtrace = item;
};
item = nullptr;
};
}
else
{
break;
}
};
return backtrace;
}
void printDebugTrace()
{
// esp_register_shutdown_handler(debugUpdate);
re_restart_debug_t debug = debugGet();
char *debug_heap = statesGetDebugHeap(&debug);
char *debug_trace = nullptr;
if (debug_heap)
{
debug_trace = statesGetDebugTrace(&debug);
if (debug_trace)
{
Serial.printf(CONFIG_MESSAGE_TG_VERSION_TRACE,
jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(),
debug_heap, debug_trace);
// free(debug_trace);
}
else
{
Serial.printf(CONFIG_MESSAGE_TG_VERSION_HEAP,
jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(),
debug_heap);
};
// free(debug_heap);
}
else
{
// Serial.println("DEVICE START");
Serial.printf(CONFIG_MESSAGE_TG_VERSION_DEF,
jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str());
}
Serial.println(INFO_MESSAGE_DEBUG);
}
void sendDebugTraceAndFreeMemory(bool postMsg)
{
// esp_register_shutdown_handler(debugUpdate);
re_restart_debug_t debug = debugGet();
char *debug_heap = statesGetDebugHeap(&debug);
char *debug_trace = nullptr;
if (debug_heap)
{
if (isNetworkActive() && postMsg)
{
debug_trace = statesGetDebugTrace(&debug);
if (debug_trace)
{
if (tlgrmItem)
{
char *msg;
msg = malloc_stringf(CONFIG_MESSAGE_TG_VERSION_TRACE,
jsonReadStr(settingsFlashJson, F("name")).c_str(), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(),
debug_heap, debug_trace);
tlgrmItem->sendTelegramMsg(false, String(msg));
tlgrmItem->sendTelegramMsg(false, String("Подробности /helpDebug в Telegram_v2"));
free(msg);
}
free(debug_trace);
}
else
{
/*
Serial.printf(CONFIG_MESSAGE_TG_VERSION_HEAP,
jsonReadStr(settingsFlashJson, F("name")), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(),
debug_heap);
*/
if (tlgrmItem)
{
char *msg;
msg = malloc_stringf(CONFIG_MESSAGE_TG_VERSION_HEAP,
jsonReadStr(settingsFlashJson, F("name")).c_str(), ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str(),
debug_heap);
tlgrmItem->sendTelegramMsg(false, String(msg));
tlgrmItem->sendTelegramMsg(false, String("Подробности /helpDebug в Telegram_v2"));
free(msg);
}
}
}
free(debug_heap);
}
/* else
{
// Serial.println("DEVICE START");
// Serial.printf(CONFIG_MESSAGE_TG_VERSION_DEF,
// FIRMWARE_VERSION, ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str());
if (tlgrmItem && isNetworkActive())
{
char *msg;
msg = malloc_stringf(CONFIG_MESSAGE_TG_VERSION_DEF,
WiFi.localIP().toString(), FIRMWARE_VERSION, ESP_getResetReason().c_str(), ESP32GetResetReason(0).c_str(), ESP32GetResetReason(1).c_str());
tlgrmItem->sendTelegramMsg(false, String(msg));
free(msg);
}
};*/
}
#else // RESTART_DEBUG_INFO
void printDebugTrace() {}
void sendDebugTraceAndFreeMemory(bool) {}
//void IRAM_ATTR debugUpdate() {}
#if !defined(esp32c6_4mb) && !defined(esp32c6_8mb)
extern "C" void __wrap_esp_panic_handler(void *info)
{
// Call the original panic handler function to finish processing this error (creating a core dump for example...)
__real_esp_panic_handler(info);
}
#endif //esp32c6
#endif // !RESTART_DEBUG_INFO
#if defined(ESP32)
#include "esp_ota_ops.h"
#include <esp_task_wdt.h>
// 3 seconds WDT, reset in 1 seconds
#define WDT_TIMEOUT 180
void startWatchDog()
{
#if !defined(esp32c6_4mb) && !defined(esp32c6_8mb) //TODO esp32-c6 переписать esp_task_wdt_init
esp_task_wdt_init(WDT_TIMEOUT, true); // enable panic so ESP32 restarts
esp_task_wdt_add(NULL); // add current thread to WDT watch
#endif
}
extern "C" bool verifyRollbackLater()
{
ets_printf("[SYSTEM] - verifyRollbackLater OVERRIDDEN FUNCTION!\n");
return true;
}
void verifyFirmware()
{
Serial.printf("[SYSTEM] - Checking firmware...\n");
const esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK)
{
const char *otaState = ota_state == ESP_OTA_IMG_NEW ? "ESP_OTA_IMG_NEW"
: ota_state == ESP_OTA_IMG_PENDING_VERIFY ? "ESP_OTA_IMG_PENDING_VERIFY"
: ota_state == ESP_OTA_IMG_VALID ? "ESP_OTA_IMG_VALID"
: ota_state == ESP_OTA_IMG_INVALID ? "ESP_OTA_IMG_INVALID"
: ota_state == ESP_OTA_IMG_ABORTED ? "ESP_OTA_IMG_ABORTED"
: "ESP_OTA_IMG_UNDEFINED";
Serial.printf("[SYSTEM] - Ota state: %s\n", otaState);
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY)
{
if (esp_ota_mark_app_valid_cancel_rollback() == ESP_OK)
{
Serial.printf("[SYSTEM] - App is valid, rollback cancelled successfully\n");
}
else
{
Serial.printf("[SYSTEM] - Failed to cancel rollback\n");
}
}
}
else
{
Serial.printf("[SYSTEM] - OTA partition has no record in OTA data\n");
}
}
#else //ESP32
void startWatchDog() {}
//extern "C" bool verifyRollbackLater() {}
void verifyFirmware() {}
#endif

View File

@@ -37,7 +37,11 @@ void addThisDeviceToList() {
AsyncUDP asyncUdp;
void udpListningInit() {
#if defined(LIBRETINY)
if (asyncUdp.listenMulticast(IPAddress(239, 255, 255, 255), 4210 , WiFi.localIP() )) {
#else
if (asyncUdp.listenMulticast(IPAddress(239, 255, 255, 255), 4210)) {
#endif
asyncUdp.onPacket([](AsyncUDPPacket packet) {
// если был включен автоматический поиск устройств то начнем запись в оперативную память
if (jsonReadInt(settingsFlashJson, F("udps")) != 0) {
@@ -65,7 +69,11 @@ void udpListningInit() {
String loacalWorkgroup = "";
jsonRead(settingsFlashJson, F("wg"), loacalWorkgroup);
if (remoteWorkgroup == loacalWorkgroup) {
#if defined(LIBRETINY)
SerialPrint("i", F("UDP"), "IP: " + ipToString(packet.remoteIP()) + ":" + String(packet.remotePort()) + " localIP:"+String(packet.localIP()));
#else
SerialPrint("i", F("UDP"), "IP: " + packet.remoteIP().toString() + ":" + String(packet.remotePort()));
#endif
jsonMergeArrays(devListHeapJson, data);
// эксперементальный вариант отправки нового списка сразу по приходу
// sendStringToWs("devlis", devListHeapJson, -1);
@@ -91,7 +99,7 @@ void udpListningInit() {
void udpBroadcastInit() {
// будем отправлять каждые 60 секунд презентацию данного устройства
ts.add(
UDP, 60000, [&](void*) { // UDPP
UDPt, 60000, [&](void*) { // UDPP
if (isNetworkActive()) {
SerialPrint("i", F("UDP"), F("Broadcast device presentation"));
asyncUdp.broadcastTo(getThisDevice().c_str(), 4210);

View File

@@ -1,5 +1,6 @@
#include "ESPConfiguration.h"
#include "classes/IoTGpio.h"
//#include "classes/IoTDiscovery.h"
extern IoTGpio IoTgpio;
@@ -8,6 +9,10 @@ void* getAPI(String subtype, String params);
void configure(String path) {
File file = seekFile(path);
if (!file) {
SerialPrint(F("E"), F("FS"), F("configure file open error"));
return;
}
file.find("[");
while (file.available()) {
String jsonArrayElement = file.readStringUntil('}') + "}";
@@ -18,6 +23,7 @@ void configure(String path) {
jsonArrayElement = "";
}
if (jsonArrayElement != "") {
IoTItem* myIoTItem;
String subtype;
if (!jsonRead(jsonArrayElement, F("subtype"), subtype)) { //если нет такого ключа в представленном json или он не валидный
SerialPrint(F("E"), F("Config"), "json error " + subtype);
@@ -36,6 +42,9 @@ void configure(String path) {
// пробуем спросить драйвер Benchmark
if (driver = myIoTItem->getBenchmarkTask()) benchTaskItem = ((IoTBench*)driver);
if (driver = myIoTItem->getBenchmarkLoad()) benchLoadItem = ((IoTBench*)driver);
// пробуем спросить драйвер для интеграций
if (driver = myIoTItem->getHOMEdDiscovery()) HOMEdDiscovery = ((IoTDiscovery*)driver);
if (driver = myIoTItem->getHADiscovery()) HADiscovery = ((IoTDiscovery*)driver);
// пробуем спросить драйвер Telegram_v2
if (driver = myIoTItem->getTlgrmDriver()) tlgrmItem = (IoTItem*)driver;
IoTItems.push_back(myIoTItem);
@@ -45,6 +54,26 @@ void configure(String path) {
}
file.close();
SerialPrint("i", "Config", "Configured");
/*
#ifdef ESP32
if(HOMEdDiscovery)
HOMEdDiscovery->mqttSubscribeDiscovery();
if(HADiscovery)
HADiscovery->mqttSubscribeDiscovery();
// оттправляем все статусы
if(HOMEdDiscovery || HADiscovery)
{
for (std::list<IoTItem *>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it)
{
if ((*it)->iAmLocal)
{
publishStatusMqtt((*it)->getID(), (*it)->getValue());
(*it)->onMqttWsAppConnectEvent();
}
}
}
#endif
*/
}
void clearConfigure() {
@@ -59,8 +88,13 @@ void clearConfigure() {
if (*it) delete *it;
}
IoTItems.clear();
#ifdef LIBRETINY
valuesFlashJson.remove(0, valuesFlashJson.length());
#else
valuesFlashJson.clear();
#endif
benchTaskItem = nullptr;
benchLoadItem = nullptr;
HOMEdDiscovery = nullptr;
HADiscovery = nullptr;
}

View File

@@ -1,5 +1,8 @@
#include "EspFileSystem.h"
#include "Global.h"
#if defined(esp32c6_4mb) || defined(esp32c6_8mb)
#include "esp_mac.h"
#endif
bool fileSystemInit()
{
@@ -18,14 +21,17 @@ void globalVarsSync()
valuesFlashJson = readFile(F("values.json"), 4096);
valuesFlashJson.replace("\r\n", "");
if (settingsFlashJson == "failed")
return;
mqttPrefix = jsonReadStr(settingsFlashJson, F("mqttPrefix"));
jsonWriteStr_(settingsFlashJson, "id", chipId);
mqttRootDevice = mqttPrefix + "/" + chipId;
#ifdef LIBRETINY
jsonWriteStr_(settingsFlashJson, "ip", ipToString(WiFi.localIP()));
#else
jsonWriteStr_(settingsFlashJson, "ip", WiFi.localIP().toString());
#endif
// это не используется - удалить в последствии
jsonWriteStr_(settingsFlashJson, "root", mqttRootDevice);
}
@@ -35,6 +41,25 @@ void syncSettingsFlashJson()
writeFile(F("settings.json"), settingsFlashJson);
}
void resetSettingsFlashByPanic()
{
FileFS.rename("/config.json", "/config_bak.json");
/*
update.configJson = readFile("config.json", 4096 * 4);
update.layoutJson = readFile("layout.json", 4096 * 4);
update.scenarioTxt = readFile("scenario.txt", 4096 * 4);
writeFile(F("/config_bak.json"), update.configJson);
writeFile(F("/scenario_bak.txt"), update.scenarioTxt);
writeFile(F("/layout_bak.json"), update.layoutJson);
*/
//update.configJson = "[]";
//update.scenarioTxt = "";
//update.layoutJson = "[]";
writeFile(F("/config.json"), "[]");
writeFile(F("/scenario.txt"), "");
writeFile(F("/layout.json"), "[]");
}
void syncValuesFlashJson()
{
writeFile(F("values.json"), valuesFlashJson);
@@ -79,7 +104,7 @@ uint32_t ESP_getChipId(void)
#endif
}
// устарела используем новую функцию ниже
/*// устарела используем новую функцию ниже
#if !defined(esp32s2_4mb) && !defined(esp32c3m_4mb) && !defined(esp32s3_16mb)
//#ifndef esp32s2_4mb
uint32_t ESP_getFlashChipId(void)
@@ -93,6 +118,7 @@ uint32_t ESP_getFlashChipId(void)
#endif
}
#endif
*/
// https://github.com/espressif/arduino-esp32/issues/6945#issuecomment-1199900892
// получение flash ch id из проекта esp easy
@@ -113,7 +139,7 @@ uint32_t getFlashChipIdNew()
}
// esp_flash_read_id(nullptr, &flashChipId);
#elif defined(ESP8266)
#elif defined(ESP8266) || defined(LIBRETINY)
flashChipId = ESP.getFlashChipId();
#endif // ifdef ESP32
}
@@ -127,9 +153,12 @@ const String getMacAddress()
#if defined(ESP8266)
WiFi.macAddress(mac);
sprintf(buf, MACSTR, MAC2STR(mac));
#else
#elif defined(ESP32)
esp_read_mac(mac, ESP_MAC_WIFI_STA);
sprintf(buf, MACSTR, mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
#elif defined(LIBRETINY)
uint32_t macid = lt_cpu_get_mac_id ();
memcpy(buf, &macid, sizeof(macid));
#endif
return String(buf);
}

View File

@@ -4,7 +4,8 @@
*****************************************глобальные объекты классов***************************************************
**********************************************************************************************************************/
TickerScheduler ts(END + 1);
// TickerScheduler ts(END + 1); // зачем на 1 больше?
TickerScheduler ts(END);
WiFiClient espClient;
PubSubClient mqtt(espClient);
@@ -20,6 +21,9 @@ ESP8266WebServer HTTP(80);
#ifdef ESP32
WebServer HTTP(80);
#endif
#ifdef LIBRETINY
WebServer HTTP(80);
#endif
#endif
#ifdef STANDARD_WEB_SOCKETS
@@ -35,6 +39,8 @@ IoTItem* rtcItem = nullptr;
IoTItem* tlgrmItem = nullptr;
IoTBench* benchTaskItem = nullptr;
IoTBench* benchLoadItem = nullptr;
IoTDiscovery* HOMEdDiscovery = nullptr;
IoTDiscovery* HADiscovery = nullptr;
String settingsFlashJson = "{}"; // переменная в которой хранятся все настройки, находится в оперативной памяти и синхронизированна с flash памятью
String valuesFlashJson = "{}"; // переменная в которой хранятся все значения элементов, которые необходимо сохранить на flash. Находится в оперативной памяти и синхронизированна с flash памятью
String errorsHeapJson = "{}"; // переменная в которой хранятся все ошибки, находится в оперативной памяти только
@@ -57,6 +63,7 @@ int mqttPort = 0;
String mqttPrefix = "";
String mqttUser = "";
String mqttPass = "";
String nameId = "";
unsigned long mqttUptime = 0;
unsigned long flashWriteNumber = 0;
@@ -78,7 +85,7 @@ String prevDate = "";
bool firstTimeInit = true;
// unsigned long loopPeriod;
int8_t ws_clients[WEBSOCKETS_CLIENT_MAX];
bool isTimeSynch = false;
Time_t _time_local;
Time_t _time_utc;

View File

@@ -3,7 +3,13 @@
#include "classes/IoTDB.h"
#include "utils/Statistic.h"
#include "classes/IoTBench.h"
#ifndef LIBRETINY
#include <Wire.h>
#endif
#include "DebugTrace.h"
#if defined(ESP32)
#include <esp_task_wdt.h>
#endif
#if defined(esp32s2_4mb) || defined(esp32s3_16mb)
#include <USB.h>
#endif
@@ -46,7 +52,7 @@ void elementsLoop() {
#define COUNTER_ERRORMARKER 4 // количество шагов счетчика
#define STEPPER_ERRORMARKER 100000 // размер шага счетчика интервала доверия выполнения блока кода мкс
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
#if defined(esp32_4mb) || defined(esp32_4mb3f) || defined(esp32_16mb) || defined(esp32cam_4mb)
static int IRAM_ATTR initErrorMarkerId = 0; // ИД маркера
static int IRAM_ATTR errorMarkerId = 0;
@@ -65,14 +71,14 @@ void IRAM_ATTR onTimer() {
#endif
void initErrorMarker(int id) {
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
#if defined(esp32_4mb) || defined(esp32_4mb3f) || defined(esp32_16mb) || defined(esp32cam_4mb)
initErrorMarkerId = id;
errorMarkerCounter = 0;
#endif
}
void stopErrorMarker(int id) {
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
#if defined(esp32_4mb) || defined(esp32_4mb3f) || defined(esp32_16mb) || defined(esp32cam_4mb)
errorMarkerCounter = -1;
if (errorMarkerId)
SerialPrint("I", "WARNING!", "A lazy (freezing loop more than " + (String)(COUNTER_ERRORMARKER * STEPPER_ERRORMARKER / 1000) + " ms) section has been found! With ID=" + (String)errorMarkerId);
@@ -85,7 +91,7 @@ void setup() {
#if defined(esp32s2_4mb) || defined(esp32s3_16mb)
USB.begin();
#endif
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
#if defined(esp32_4mb) || defined(esp32_4mb3f) || defined(esp32_16mb) || defined(esp32cam_4mb)
My_timer = timerBegin(0, 80, true);
timerAttachInterrupt(My_timer, &onTimer, true);
timerAlarmWrite(My_timer, STEPPER_ERRORMARKER, true);
@@ -97,6 +103,14 @@ void setup() {
Serial.begin(115200);
Serial.flush();
//----------- Отладка EXCEPTION (функции с заглушками для отключения) ---------
#if defined(RESTART_DEBUG_INFO)
//Привязка коллбэк функции для вызова при перезагрузке
esp_register_shutdown_handler(debugUpdate);
#endif // RESTART_DEBUG_INFO
// Печать или оправка отладочной информации
printDebugTrace();
startWatchDog();
Serial.println();
Serial.println(F("--------------started----------------"));
@@ -117,6 +131,8 @@ void setup() {
// получение chip id
setChipId();
verifyFirmware();
// синхронизация глобальных переменных с flash
globalVarsSync();
@@ -134,13 +150,38 @@ void setup() {
#ifdef ESP32
Wire.end();
Wire.begin(pinSDA, pinSCL, (uint32_t)i2cFreq);
#else
#elif defined(ESP8266)
Wire.begin(pinSDA, pinSCL);
Wire.setClock(i2cFreq);
#endif
SerialPrint("i", "i2c", F("i2c pins overriding done"));
}
#if defined(RESTART_DEBUG_INFO)
esp_reset_reason_t esp_reason = esp_reset_reason();
if (esp_reason == ESP_RST_UNKNOWN || esp_reason == ESP_RST_POWERON)
bootloop_panic_count = 0;
/* else if (bootloop_panic_count == 3 || bootloop_panic_count == 0 ) bootloop_panic_count = 1;
else if (bootloop_panic_count == 2) bootloop_panic_count = 3;
else if (bootloop_panic_count == 1) bootloop_panic_count = 2;
else bootloop_panic_count = 0; */
Serial.println("bootloop_panic_count " + String(bootloop_panic_count));
if (bootloop_panic_count >3 )
{
//resetSettingsFlashByPanic();
bootloop_panic_count = 0;
}
if (bootloop_panic_count >= 3)
{
resetSettingsFlashByPanic();
bootloop_panic_count = -1;
}
if (bootloop_panic_count == -1)
{
SerialPrint("E", "CORE", F("CONFIG and SCENARIO reset !!!"));
bootloop_panic_count = 0;
ESP.restart();
}
#endif // RESTART_DEBUG_INFO
// настраиваем микроконтроллер
configure("/config.json");
@@ -159,8 +200,11 @@ void setup() {
initErrorMarker(SETUPINET_ERRORMARKER);
// подключаемся к роутеру
#ifdef ESP8266
routerConnect();
#else
WiFiUtilsItit();
#endif
// инициализация асинхронного веб сервера и веб сокетов
#ifdef ASYNC_WEB_SERVER
asyncWebServerInit();
@@ -179,6 +223,10 @@ void setup() {
stopErrorMarker(SETUPINET_ERRORMARKER);
bool postMsgTelegram;
if (!jsonRead(settingsFlashJson, "debugTraceMsgTlgrm", postMsgTelegram, false)) postMsgTelegram = 1;
sendDebugTraceAndFreeMemory(postMsgTelegram);
initErrorMarker(SETUPLAST_ERRORMARKER);
elementsLoop();
@@ -188,11 +236,15 @@ void setup() {
// инициализация задач переодического выполнения
periodicTasksInit();
#if defined(ESP8266)
// Перенесли после получения IP, так как теперь работа WiFi асинхронная
// запуск работы udp
addThisDeviceToList();
#ifdef UDP_ENABLED
udpListningInit();
udpBroadcastInit();
#endif
#endif
// создаем событие завершения конфигурирования для возможности выполнения блока кода при загрузке
createItemFromNet("onStart", "1", 1);
@@ -201,6 +253,13 @@ void setup() {
// настраиваем секундные обслуживания системы
ts.add(
TIMES, 1000, [&](void *) {
// сброс WDT
#if defined(ESP32)
//SerialPrint("i", "Task", "reset wdt");
#if !defined(esp32c6_4mb) && !defined(esp32c6_8mb) //TODO esp32-c6 переписать esp_task_wdt_init
esp_task_wdt_reset();
#endif
#endif
// сохраняем значения IoTItems в файл каждую секунду, если были изменения (установлены маркеры на сохранение)
if (needSaveValues) {
syncValuesFlashJson();
@@ -216,14 +275,50 @@ void setup() {
},
nullptr, true);
// ловим пинги от WS (5сек) и дисконнектим если их нет (20сек)
ts.add(
PiWS, 6000, [&](void*) {
if (isNetworkActive()) {
for (size_t i = 0; i < WEBSOCKETS_CLIENT_MAX; i++)
{
if (ws_clients[i] == 0) {
disconnectWSClient(i);
ws_clients[i]=-1;
}
if (ws_clients[i] > 0) {
ws_clients[i]=0;
}
}
}
},
nullptr, true);
// test
Serial.println("-------test start--------");
Serial.println("--------test end---------");
//Serial.println("-------test start--------");
//Serial.println("--------test end---------");
stopErrorMarker(SETUPLAST_ERRORMARKER);
#if defined(RESTART_DEBUG_INFO)
bootloop_panic_count = 0;
#endif // RESTART_DEBUG_INFO
}
void loop() {
#if !defined(ESP8266)
static bool udpFirstFlag = true;
// Перенесли после получения IP, так как теперь работа WiFi асинхронная
if (isNetworkActive() && udpFirstFlag) {
udpFirstFlag = false;
// запуск работы udp
addThisDeviceToList();
#ifdef UDP_ENABLED
udpListningInit();
udpBroadcastInit();
#endif
}
#endif
#ifdef LOOP_DEBUG
unsigned long st = millis();
#endif

View File

@@ -1,4 +1,5 @@
#include "MqttClient.h"
#include "classes/IoTDiscovery.h"
void mqttInit() {
mqtt.setCallback(mqttCallback);
@@ -59,10 +60,24 @@ boolean mqttConnect() {
if (!mqtt.connected()) {
bool connected = false;
if (mqttUser != "" && mqttPass != "") {
connected = mqtt.connect(chipId.c_str(), mqttUser.c_str(), mqttPass.c_str());
if (HOMEdDiscovery)
{
connected = mqtt.connect(chipId.c_str(), mqttUser.c_str(), mqttPass.c_str(), (HOMEdDiscovery->HOMEdTopic + "/device/custom/" + nameId).c_str(), 1, true, "{\"status\":\"offline\"}");
}
else
{
connected = mqtt.connect(chipId.c_str(), mqttUser.c_str(), mqttPass.c_str(), (mqttRootDevice + "/state").c_str(), 1, true, "{\"status\":\"offline\"}");
}
SerialPrint("i", F("MQTT"), F("Go to connection with login and password"));
} else if (mqttUser == "" && mqttPass == "") {
connected = mqtt.connect(chipId.c_str());
if (HOMEdDiscovery)
{
connected = mqtt.connect(chipId.c_str(), (HOMEdDiscovery->HOMEdTopic + "/device/custom/" + nameId).c_str(), 1, true, "{\"status\":\"offline\"}");
}
else
{
connected = mqtt.connect(chipId.c_str(), (mqttRootDevice + "/state").c_str(), 1, true, "{\"status\":\"offline\"}");
}
SerialPrint("i", F("MQTT"), F("Go to connection without login and password"));
} else {
SerialPrint("E", F("MQTT"), F("✖ Login or password missed"));
@@ -105,6 +120,9 @@ void getMqttData() {
mqttUser = jsonReadStr(settingsFlashJson, F("mqttUser"));
mqttPass = jsonReadStr(settingsFlashJson, F("mqttPass"));
mqttPrefix = jsonReadStr(settingsFlashJson, F("mqttPrefix"));
if (jsonReadInt(settingsFlashJson, F("HOMEd_names"))){
nameId = jsonReadStr(settingsFlashJson, F("name"));}
else{nameId = getChipId();}
mqttRootDevice = mqttPrefix + "/" + chipId;
}
@@ -129,6 +147,22 @@ void mqttSubscribe() {
}
}
}
if(HOMEdDiscovery)
HOMEdDiscovery->mqttSubscribeDiscovery();
if(HADiscovery)
HADiscovery->mqttSubscribeDiscovery();
// оттправляем все статусы
if(HOMEdDiscovery || HADiscovery)
{
for (std::list<IoTItem *>::iterator it = IoTItems.begin(); it != IoTItems.end(); ++it)
{
if ((*it)->iAmLocal)
{
publishStatusMqtt((*it)->getID(), (*it)->getValue());
(*it)->onMqttWsAppConnectEvent();
}
}
}
}
void mqttSubscribeExternal(String topic, bool usePrefix) {
@@ -253,6 +287,10 @@ boolean publishChartMqtt(const String& topic, const String& data) {
}
boolean publishStatusMqtt(const String& topic, const String& data) {
if (HOMEdDiscovery)
{
HOMEdDiscovery->publishStatusHOMEd(topic, data);
}
String path = mqttRootDevice + "/" + topic + "/status";
String json = "{}";
jsonWriteStr(json, "status", data);
@@ -305,14 +343,17 @@ bool publishChartFileToMqtt(String path, String id, int maxCount) {
return true;
}
void handleMqttStatus(bool send) {
String stateStr = getStateStr(mqtt.state());
// Serial.println(stateStr);
jsonWriteStr_(errorsHeapJson, F("mqtt"), stateStr);
if (!send) sendStringToWs("errors", errorsHeapJson, -1);
}
// void handleMqttStatus(bool send) {
// String stateStr = getStateStr(mqtt.state());
// // Serial.println(stateStr);
// jsonWriteStr_(errorsHeapJson, F("mqtt"), stateStr);
// if (!send) sendStringToWs("errors", errorsHeapJson, -1);
// }
void handleMqttStatus(bool send, int state) {
if (state == -1) {
state = mqtt.state();
}
String stateStr = getStateStr(state);
// Serial.println(stateStr);
jsonWriteStr_(errorsHeapJson, F("mqtt"), stateStr);

View File

@@ -1,14 +1,29 @@
#include "NTP.h"
#if defined(LIBRETINY)
#include "lwip/apps/sntp.h"
#endif
#include "Global.h"
#include "utils/SerialPrint.h"
void ntpInit() {
#if defined(LIBRETINY)
if (sntp_enabled()) {
sntp_stop();
}
sntp_setoperatingmode(SNTP_OPMODE_POLL);
sntp_setservername(0, jsonReadStr(settingsFlashJson, F("ntp")).c_str());
sntp_setservername(1, "pool.ntp.org");
sntp_setservername(2, "ru.pool.ntp.org");
sntp_init();
#endif
synchTime();
ts.add(
TIME, 1000, [&](void*) {
unixTime = getSystemTime();
//SerialPrint("I", F("NTP"), "TIME " + String(unixTime));
if (unixTime < MIN_DATETIME) {
isTimeSynch = false;
// SerialPrint("E", "NTP", "Time not synched");
@@ -44,7 +59,16 @@ void ntpInit() {
}
void synchTime() {
#if defined LIBRETINY
// force resync
if (sntp_enabled()) {
sntp_stop();
}
sntp_init();
#else
configTime(0, 0, "pool.ntp.org", "ru.pool.ntp.org", jsonReadStr(settingsFlashJson, F("ntp")).c_str());
#endif
}
//событие смены даты

View File

@@ -59,9 +59,29 @@ String ESP_getResetReason(void) {
return ESP.getResetReason();
}
#endif
#if defined(esp32s2_4mb) || defined(esp32s3_16mb) || defined(esp32c3m_4mb)
#ifdef LIBRETINY
String ESP_getResetReason(void) {
return ESP32GetResetReason(0); // CPU 0
return ESP.getResetReason();
}
#endif
#if defined(esp32s2_4mb) || defined(esp32s3_16mb) || defined(esp32c3m_4mb) || defined(esp32c6_4mb) || defined(esp32c6_8mb)
String ESP_getResetReason(void) {
// return ESP32GetResetReason(0); // CPU 0
esp_reset_reason_t esp_reason = esp_reset_reason();
switch (esp_reason) {
case ESP_RST_UNKNOWN: return "UNKNOWN";
case ESP_RST_POWERON: return "POWER ON";
case ESP_RST_EXT: return "EXTERNAL PIN";
case ESP_RST_SW: return "SOFTWARE RESET";
case ESP_RST_PANIC: return "EXCEPTION / PANIC";
case ESP_RST_INT_WDT: return "INTERRUPT WATCHDOG";
case ESP_RST_TASK_WDT: return "TASK WATCHDOG";
case ESP_RST_WDT: return "WATCHDOGS";
case ESP_RST_DEEPSLEEP: return "EXITING DEEP SLLEP MODE";
case ESP_RST_BROWNOUT: return "BROWNOUT";
case ESP_RST_SDIO: return "SDIO";
default : return "NO MEAN";
};
}
String ESP32GetResetReason(uint32_t cpu_no) {
// tools\sdk\include\esp32\rom\rtc.h
@@ -82,8 +102,8 @@ String ESP32GetResetReason(uint32_t cpu_no) {
return F("Timer Group1 Watchdog reset digital core"); // 8
case RTCWDT_SYS_RESET:
return F("RTC Watchdog Reset digital core"); // 9
case INTRUSION_RESET:
return F("Instrusion tested to reset CPU"); // 10
// case INTRUSION_RESET:
// return F("Instrusion tested to reset CPU"); // 10
case TG0WDT_CPU_RESET:
return F("Time Group reset CPU"); // 11
case RTC_SW_CPU_RESET:
@@ -101,9 +121,24 @@ String ESP32GetResetReason(uint32_t cpu_no) {
}
}
#endif
#if defined(esp32_4mb) || defined(esp32_16mb) || defined(esp32cam_4mb)
#if defined(esp32_4mb) || defined(esp32_4mb3f) || defined(esp32_16mb) || defined(esp32cam_4mb)
String ESP_getResetReason(void) {
return ESP32GetResetReason(0); // CPU 0
// return ESP32GetResetReason(0); // CPU 0
esp_reset_reason_t esp_reason = esp_reset_reason();
switch (esp_reason) {
case ESP_RST_UNKNOWN: return "UNKNOWN";
case ESP_RST_POWERON: return "POWER ON";
case ESP_RST_EXT: return "EXTERNAL PIN";
case ESP_RST_SW: return "SOFTWARE RESET";
case ESP_RST_PANIC: return "EXCEPTION / PANIC";
case ESP_RST_INT_WDT: return "INTERRUPT WATCHDOG";
case ESP_RST_TASK_WDT: return "TASK WATCHDOG";
case ESP_RST_WDT: return "WATCHDOGS";
case ESP_RST_DEEPSLEEP: return "EXITING DEEP SLLEP MODE";
case ESP_RST_BROWNOUT: return "BROWNOUT";
case ESP_RST_SDIO: return "SDIO";
default : return "NO MEAN";
};
}
String ESP32GetResetReason(uint32_t cpu_no) {
// tools\sdk\include\esp32\rom\rtc.h

View File

@@ -89,6 +89,10 @@ void standWebServerInit() {
// - second callback handles file upload at that location
HTTP.on("/edit", HTTP_POST, replyOK, handleFileUpload);
HTTP.on("/localota", HTTP_GET, handleLocalOTA);
HTTP.on("/localota_handler", HTTP_GET, handleLocalOTA_Handler);
// Default handler for all URIs not defined above
// Use it to read files from filesystem
HTTP.onNotFound(handleNotFound);
@@ -156,6 +160,15 @@ void handleStatus() {
HTTP.send(200, "application/json", json);
}
void handleLocalOTA() {
String page = "<form action='/localota' method='POST'><label for='server'>Server Address:</label><input type='text' name='server' value='http://192.168.1.2:5500'><input type='submit' value='Update'></form>";
HTTP.send(200, "text/html", page);}
void handleLocalOTA_Handler() {
String serverValue = HTTP.arg("server");
upgrade_firmware(3,serverValue);
}
#ifdef ESP32
String getContentType(String filename) {
if (HTTP.hasArg("download")) {
@@ -231,7 +244,12 @@ bool handleFileRead(String path) {
return the path of the closest parent still existing
*/
String lastExistingParent(String path) {
while (!path.isEmpty() && !FileFS.exists(path)) {
#ifndef LIBRETINY
while (!path.isEmpty() && !FileFS.exists(path))
#else
while (!path.length()==0 && !FileFS.exists(path))
#endif
{
if (path.lastIndexOf('/') > 0) {
path = path.substring(0, path.lastIndexOf('/'));
} else {
@@ -278,7 +296,7 @@ void handleFileUpload() {
}
}
#ifdef ESP8266
#if defined ESP8266
void deleteRecursive(String path) {
File file = FileFS.open(path, "r");
bool isDir = file.isDirectory();
@@ -299,14 +317,14 @@ void deleteRecursive(String path) {
}
#endif
#ifdef ESP32
#if defined ESP32 || defined LIBRETINY
struct treename {
uint8_t type;
char *name;
};
void deleteRecursive(String path) {
fs::File dir = FileFS.open(path);
fs::File dir = FileFS.open(path.c_str());
if (!dir.isDirectory()) {
Serial.printf("%s is a file\n", path);
@@ -321,7 +339,11 @@ void deleteRecursive(String path) {
while (entry = dir.openNextFile()) {
if (entry.isDirectory()) {
#if defined ESP32
deleteRecursive(entry.path());
#elif defined LIBRETINY
deleteRecursive(entry.fullName());
#endif
} else {
String tmpname = path + "/" + strdup(entry.name()); // buffer file name
entry.close();
@@ -342,10 +364,15 @@ void deleteRecursive(String path) {
*/
void handleFileDelete() {
String path = HTTP.arg(0);
#ifndef LIBRETINY
if (path.isEmpty() || path == "/") {
return replyBadRequest("BAD PATH");
}
#else
if (path.length()==0 || path == "/") {
return replyBadRequest("BAD PATH");
}
#endif
// DBG_OUTPUT_PORT.println(String("handleFileDelete: ") + path);
if (!FileFS.exists(path)) {
return replyNotFound(FPSTR(FILE_NOT_FOUND));
@@ -368,10 +395,15 @@ void handleFileDelete() {
*/
void handleFileCreate() {
String path = HTTP.arg("path");
#ifndef LIBRETINY
if (path.isEmpty()) {
return replyBadRequest(F("PATH ARG MISSING"));
}
#else
if (path.length()==0) {
return replyBadRequest(F("PATH ARG MISSING"));
}
#endif
#ifdef USE_SPIFFS
if (checkForUnsupportedPath(path).length() > 0) {
return replyServerError(F("INVALID FILENAME"));
@@ -386,7 +418,11 @@ void handleFileCreate() {
}
String src = HTTP.arg("src");
#ifndef LIBRETINY
if (src.isEmpty()) {
#else
if (src.length()==0) {
#endif
// No source specified: creation
// DBG_OUTPUT_PORT.println(String("handleFileCreate: ") + path);
if (path.endsWith("/")) {
@@ -404,6 +440,9 @@ void handleFileCreate() {
#endif
#ifdef ESP32
file.write(0);
#endif
#ifdef LIBRETINY
file.write((uint8_t)0);
#endif
file.close();
} else {
@@ -509,7 +548,7 @@ void handleFileList() {
}
#endif
#ifdef ESP32
#if defined ESP32
void handleFileList() {
if (!HTTP.hasArg("dir")) {
HTTP.send(500, "text/plain", "BAD ARGS");
@@ -517,15 +556,29 @@ void handleFileList() {
}
String path = HTTP.arg("dir");
// DBG_OUTPUT_PORT.println("handleFileList: " + path);
if (path != "/" && !FileFS.exists(path)) {
return replyBadRequest("BAD PATH");
}
//path = "/build/";
Serial.println("handleFileList: " + path);
#if defined LIBRETINY
File root = FileFS.open(path.c_str());
//Dir root = FileFS.openDir(path);
Serial.println("handleFileList FIRST OPEN Name: " + String(root.name()));
#else
File root = FileFS.open(path);
#endif
path = String();
String output = "[";
if (root.isDirectory()) {
Serial.println("handleFileList IS DIR: " + String(root.name()));
//root.close();
File file = root.openNextFile();
Serial.println("handleFileList openNextFile: " + String(file.name()));
while (file) {
if (output != "[") {
output += ',';
}
@@ -549,6 +602,67 @@ void handleFileList() {
}
#endif
#if defined LIBRETINY
void handleFileList() {
if (!HTTP.hasArg("dir")) {
HTTP.send(500, "text/plain", "BAD ARGS");
return;
}
String path = HTTP.arg("dir");
if (path != "/" && !FileFS.exists(path)) {
return replyBadRequest("BAD PATH");
}
FileFS.open(path.c_str());
lfs_dir_t dir;
struct lfs_info info;
int err = lfs_dir_open(FileFS.getFS(), &dir, path.c_str());
if (err) {
HTTP.send(500, "text/plain", "FAIL OPEN DIR");
return;
}
String output = "[";
while (true) {
int res = lfs_dir_read(FileFS.getFS(), &dir, &info);
if (res < 0) {
lfs_dir_close(FileFS.getFS(), &dir);
return ;
}
if (!res) {
break;
}
Serial.printf("%s %d", info.name, info.type);
if (output != "[") {
output += ',';
}
output += "{\"type\":\"";
// output += (file.isDirectory()) ? "dir" : "file";
if (info.type == LFS_TYPE_DIR) {
output += "dir";
} else {
output += F("file\",\"size\":\"");
output += info.size;
}
output += "\",\"name\":\"";
output += String(info.name);
output += "\"}";
//file = root.openNextFile();
}
err = lfs_dir_close(FileFS.getFS(), &dir);
if (err) {
HTTP.send(500, "text/plain", "FAIL CLOSE DIR");
return;
}
output += "]";
HTTP.send(200, "text/json", output);
}
#endif
/*
The "Not Found" handler catches all URI not explicitly declared in code
First try to find and return the requested file from the filesystem,
@@ -560,6 +674,9 @@ void handleNotFound() {
#endif
#ifdef ESP32
String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks
#endif
#ifdef LIBRETINY
String uri = WebServer::urlDecode(HTTP.uri()); // required to read paths with blanks
#endif
if (handleFileRead(uri)) {
return;

View File

@@ -3,6 +3,9 @@
updateFirm update;
void upgrade_firmware(int type, String path) {
if (path == ""){
path = getBinPath();
}
putUserDataToRam();
// сбросим файл статуса последнего обновления
writeFile("ota.json", "{}");
@@ -36,6 +39,7 @@ void upgrade_firmware(int type, String path) {
bool upgradeFS(String path) {
bool ret = false;
#ifndef LIBRETINY
WiFiClient wifiClient;
SerialPrint("!!!", F("Update"), "Start upgrade FS... " + path);
@@ -57,21 +61,27 @@ bool upgradeFS(String path) {
// если FS обновилась успешно
if (retFS == HTTP_UPDATE_OK) {
SerialPrint("!!!", F("Update"), F("FS upgrade done!"));
//HTTP.send(200, "text/plain", "FS upgrade done!");
saveUpdeteStatus("fs", UPDATE_COMPLETED);
ret = true;
} else {
saveUpdeteStatus("fs", UPDATE_FAILED);
if (retFS == HTTP_UPDATE_FAILED) {
SerialPrint("E", F("Update"), "HTTP_UPDATE_FAILED");
String page = "<html><body>Ошибка обновления FS!<br>FS Update failed!<br><a href='/'>Home</a></body></html>";
HTTP.send(200, "text/html; charset=UTF-8", page);
} else if (retFS == HTTP_UPDATE_NO_UPDATES) {
SerialPrint("E", F("Update"), "HTTP_UPDATE_NO_UPDATES");
SerialPrint("E", F("Update"), "HTTP_UPDATE_NO_UPDATES! DELETE /localota !!!");
//HTTP.send(200, "text/plain", "NO_UPDATES");
}
}
#endif
return ret;
}
bool upgradeBuild(String path) {
bool ret = false;
#ifndef LIBRETINY
WiFiClient wifiClient;
SerialPrint("!!!", F("Update"), "Start upgrade BUILD... " + path);
@@ -92,16 +102,23 @@ bool upgradeBuild(String path) {
// если BUILD обновился успешно
if (retBuild == HTTP_UPDATE_OK) {
SerialPrint("!!!", F("Update"), F("BUILD upgrade done!"));
String page = "<html><body>Обновление BUILD выполнено!<br>Build upgrade done!<br><a href='/'>Home</a></body></html>";
HTTP.send(200, "text/html; charset=UTF-8", page);
saveUpdeteStatus("build", UPDATE_COMPLETED);
ret = true;
} else {
saveUpdeteStatus("build", UPDATE_FAILED);
if (retBuild == HTTP_UPDATE_FAILED) {
SerialPrint("E", F("Update"), "HTTP_UPDATE_FAILED");
String page = "<html><body>Ошибка обновления прошивки!<br>Firmware update failed!<br><a href='/'>Home</a></body></html>";
HTTP.send(200, "text/html; charset=UTF-8", page);
} else if (retBuild == HTTP_UPDATE_NO_UPDATES) {
SerialPrint("E", F("Update"), "HTTP_UPDATE_NO_UPDATES");
String page = "<html><body>Нет обновлений!<br>No updates!<br><a href='/'>Home</a></body></html>";
HTTP.send(200, "text/html; charset=UTF-8", page);
}
}
#endif
return ret;
}
@@ -112,22 +129,36 @@ void restartEsp() {
}
//теперь путь к обнавленю прошивки мы получаем из веб интерфейса
// const String getBinPath(String file) {
// String path = "error";
// int targetVersion = 0;
// String serverip;
// if (jsonRead(errorsHeapJson, F("chver"), targetVersion)) {
// if (targetVersion >= 400) {
// if (jsonRead(settingsFlashJson, F("serverip"), serverip)) {
// if (serverip != "") {
// path = jsonReadStr(settingsFlashJson, F("serverip")) + "/iotm/" + String(FIRMWARE_NAME) + "/" + String(targetVersion) + "/" + file;
// }
// }
// }
// }
// SerialPrint("i", F("Update"), "path: " + path);
// return path;
// }
const String getBinPath() {
String path = "error";
int targetVersion = 400; //HACKFUCK local OTA version in PrepareServer.py
String serverip;
if (jsonRead(errorsHeapJson, F("chver"), targetVersion)) {
if (targetVersion >= 400)
{
if (jsonRead(settingsFlashJson, F("serverip"), serverip))
{
if (serverip != "")
{
path = jsonReadStr(settingsFlashJson, F("serverip")) + "/iotm/" + String(FIRMWARE_NAME) + "/" + String(targetVersion) + "/" ;
}
}
}
}
else if (targetVersion >= 400)
{
if (jsonRead(settingsFlashJson, F("serverlocal"), serverip)) {
if (serverip != "")
{
path = jsonReadStr(settingsFlashJson, F("serverlocal")) + "/iotm/" + String(FIRMWARE_NAME) + "/" + String(targetVersion) + "/";
}
}
}
SerialPrint("i", F("Update"), "server local: " + path);
return path;
}
// https://t.me/IoTmanager/128814/164752 - убрал ограничение
void putUserDataToRam() {

View File

@@ -7,6 +7,10 @@ void standWebSocketsInit() {
standWebSocket.begin();
standWebSocket.onEvent(webSocketEvent);
SerialPrint("i", "WS", "WS server initialized");
for (size_t i = 0; i < WEBSOCKETS_CLIENT_MAX; i++)
{
ws_clients[i] = -1;
}
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
@@ -17,6 +21,7 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
case WStype_DISCONNECTED: {
Serial.printf("[%u] Disconnected!\n", num);
standWebSocket.disconnect(num);
} break;
case WStype_CONNECTED: {
@@ -54,7 +59,11 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
//----------------------------------------------------------------------//
// Страница веб интерфейса dashboard
//----------------------------------------------------------------------//
if (headerStr == "p|") {
standWebSocket.sendTXT(num, "p|");
//Serial.printf("Ping client: %u\n", num);
ws_clients[num]=1;
}
// публикация всех виджетов
if (headerStr == "/|") {
sendFileToWsByFrames("/layout.json", "layout", "", num, WEB_SOCKETS_FRAME_SIZE);
@@ -166,7 +175,9 @@ void webSocketEvent(uint8_t num, WStype_t type, uint8_t* payload, size_t length)
if (headerStr == "/scan|") {
std::vector<String> jArray;
jsonReadArray(settingsFlashJson, "routerssid", jArray);
#ifdef ESP8266
RouterFind(jArray);
#endif
sendStringToWs("ssidli", ssidListHeapJson, num);
}
@@ -378,6 +389,7 @@ void sendFileToWsByFrames(const String& filename, const String& header, const St
auto path = filepath(filename);
auto file = FileFS.open(path, "r");
//SerialPrint("I", "sendFileToWsByFrames", ("reed file: ")+ path);
if (!file) {
SerialPrint("E", "FS", F("reed file error"));
return;
@@ -425,16 +437,25 @@ void sendFileToWsByFrames(const String& filename, const String& header, const St
continuation = true;
}
// Serial.println(String(i) + ") " + "ws: " + String(client_id) + " fr sz:
// " + String(size) + " fin: " + String(fin) + " cnt: " +
// Serial.println(String(i) + ") " + "ws: " + String(client_id) + " fr sz: "
// + String(size) + " fin: " + String(fin) + " cnt: " +
// String(continuation));
#ifdef ASYNC_WEB_SOCKETS
if (client_id == -1) {
//ws.broadcastBIN(frameBuf, size, fin, continuation);
ws.binaryAll(frameBuf, size);
} else {
//ws.sendBIN(client_id, frameBuf, size, fin, continuation);
ws.binary(client_id,frameBuf, size);
}
#elif defined (STANDARD_WEB_SOCKETS)
if (client_id == -1) {
standWebSocket.broadcastBIN(frameBuf, size, fin, continuation);
} else {
standWebSocket.sendBIN(client_id, frameBuf, size, fin, continuation);
}
#endif
}
i++;
}
@@ -444,7 +465,12 @@ void sendFileToWsByFrames(const String& filename, const String& header, const St
}
void sendStringToWs(const String& header, String& payload, int client_id) {
#ifdef LIBRETINY
if (/* (!getNumAPClients() && !isNetworkActive()) || */ !getNumWSClients()) {
#else
if ( (!getNumAPClients() && !isNetworkActive()) || !getNumWSClients()) {
#endif
// SerialPrint("E", "sendStringToWs", "getNumAPClients: " + String(getNumAPClients()) + "isNetworkActive: " + String(isNetworkActive() + "getNumWSClients: " + String(getNumWSClients())));
// standWebSocket.disconnect(); // это и ниже надо сделать при -
// standWebSocket.close(); // - отключении AP И WiFi(STA), надо менять ядро WiFi. Сейчас не закрывается сессия клиента при пропаже AP И WiFi(STA)
return;
@@ -457,17 +483,32 @@ void sendStringToWs(const String& header, String& payload, int client_id) {
String msg = header + "|0012|" + payload;
size_t totalSize = msg.length();
// SerialPrint("E", "sendStringToWs", msg);
char dataArray[totalSize];
msg.toCharArray(dataArray, totalSize + 1);
#ifdef ASYNC_WEB_SOCKETS
if (client_id == -1) {
ws.binaryAll((uint8_t*)dataArray, totalSize);
} else {
ws.binary(client_id, (uint8_t*)dataArray, totalSize);
}
#elif defined (STANDARD_WEB_SOCKETS)
if (client_id == -1) {
standWebSocket.broadcastBIN((uint8_t*)dataArray, totalSize);
} else {
standWebSocket.sendBIN(client_id, (uint8_t*)dataArray, totalSize);
}
#endif
}
void sendDeviceList(uint8_t num) {
void disconnectWSClient(uint8_t client_id)
{
standWebSocket.disconnect(client_id);
Serial.printf("[WS] Client %u -disconnected\n", client_id);
}
void sendDeviceList(uint8_t num)
{
if (jsonReadInt(settingsFlashJson, F("udps")) != 0) {
// если включен автопоиск то отдаем список из оперативной памяти
SerialPrint("i", "FS", "heap list");
@@ -478,5 +519,8 @@ void sendDeviceList(uint8_t num) {
SerialPrint("i", "FS", "flash list");
}
}
#ifdef ASYNC_WEB_SOCKETS
int getNumWSClients() { return ws.count(); }
#elif defined (STANDARD_WEB_SOCKETS)
int getNumWSClients() { return standWebSocket.connectedClients(false); }
#endif

View File

@@ -0,0 +1,31 @@
#include "Global.h"
#include "classes/IoTDiscovery.h"
IoTDiscovery::IoTDiscovery(const String &parameters) : IoTItem(parameters)
{
/* int _tx, _rx, _speed, _line;
jsonRead(parameters, "rx", _rx);
jsonRead(parameters, "tx", _tx);
jsonRead(parameters, "speed", _speed);
jsonRead(parameters, "line", _line);
*/
//ChipId = getChipId();
}
void IoTDiscovery::publishStatusHOMEd(const String &topic, const String &data) {}
void IoTDiscovery::getlayoutHA() {}
void IoTDiscovery::getlayoutHOMEd() {}
void IoTDiscovery::deleteFromHOMEd() {}
void IoTDiscovery::mqttSubscribeDiscovery(){}
boolean IoTDiscovery::publishRetain(const String &topic, const String &data)
{
if (mqtt.beginPublish(topic.c_str(), data.length(), true))
{
mqtt.print(data);
return mqtt.endPublish();
}
return false;
}
IoTDiscovery::~IoTDiscovery() {}

View File

@@ -9,7 +9,7 @@ IoTGpio::~IoTGpio(){
}
#ifndef LIBRETINY
void IoTGpio::pinMode(int pin, uint8_t mode) {
int pinH = pin/100;
if (_drivers[pinH]) _drivers[pinH]->pinMode(pin - pinH*100, mode);
@@ -21,7 +21,19 @@ void IoTGpio::digitalWrite(int pin, uint8_t val) {
if (_drivers[pinH]) _drivers[pinH]->digitalWrite(pin - pinH*100, val);
else ::digitalWrite(pin, val);
}
#else
void IoTGpio::pinMode(int pin, uint8_t mode) {
int pinH = pin/100;
if (_drivers[pinH]) _drivers[pinH]->pinMode(pin - pinH*100, mode);
else ::pinMode(pin, (PinMode)mode);
}
void IoTGpio::digitalWrite(int pin, uint8_t val) {
int pinH = pin/100;
if (_drivers[pinH]) _drivers[pinH]->digitalWrite(pin - pinH*100, val);
else ::digitalWrite(pin, (PinStatus)val);
}
#endif
int IoTGpio::digitalRead(int pin) {
int pinH = pin/100;
if (_drivers[pinH]) return _drivers[pinH]->digitalRead(pin - pinH*100);
@@ -51,7 +63,11 @@ void IoTGpio::analogWrite(int pin, int val) {
void IoTGpio::digitalInvert(int pin) {
int pinH = pin/100;
if (_drivers[pinH]) _drivers[pinH]->digitalInvert(pin - pinH*100);
else ::digitalWrite(pin, 1 - ::digitalRead(pin));
#ifdef LIBRETINY
else ::digitalWrite(pin, (PinStatus)(1 - ::digitalRead(pin)));
#else
else ::digitalWrite(pin, (1 - ::digitalRead(pin)));
#endif
}

View File

@@ -258,6 +258,14 @@ IoTBench *IoTItem::getBenchmarkLoad()
{
return nullptr;
}
IoTDiscovery *IoTItem::getHOMEdDiscovery()
{
return nullptr;
}
IoTDiscovery *IoTItem::getHADiscovery()
{
return nullptr;
}
unsigned long IoTItem::getRtcUnixTime()
{
return 0;
@@ -281,7 +289,7 @@ unsigned long IoTItem::getRtcUnixTime()
//=========================================================================================================================================
IoTItem* myIoTItem;
// IoTItem* myIoTItem; // экономим память, используется в одном месте
// поиск элемента модуля в существующей конфигурации
IoTItem* findIoTItem(const String& name) {

View File

@@ -342,7 +342,8 @@ enum SysOp {
sysop_getIP,
sysop_mqttPub,
sysop_getUptime,
sysop_mqttIsConnect
sysop_mqttIsConnect,
sysop_wifiIsConnect
};
IoTValue sysExecute(SysOp command, std::vector<IoTValue> &param) {
@@ -414,11 +415,11 @@ IoTValue sysExecute(SysOp command, std::vector<IoTValue> &param) {
case sysop_deepSleep:
if (param.size()) {
Serial.printf("Ушел спать на %d сек...", (int)param[0].valD);
#ifdef ESP32
#if defined(ESP32)
esp_sleep_enable_timer_wakeup(param[0].valD * 1000000);
delay(1000);
esp_deep_sleep_start();
#else
#elif defined(ESP8266)
ESP.deepSleep(param[0].valD * 1000000);
#endif
}
@@ -435,7 +436,7 @@ IoTValue sysExecute(SysOp command, std::vector<IoTValue> &param) {
if (param.size() == 2) {
// Serial.printf("Call from sysExecute %s %s\n", param[0].valS.c_str(), param[1].valS.c_str());
String tmpStr = param[1].valS;
if (param[1].isDecimal) tmpStr = param[1].valD;
if (param[1].isDecimal) tmpStr = String(param[1].valD);
value.valD = mqtt.publish(param[0].valS.c_str(), tmpStr.c_str(), false);
}
break;
@@ -446,6 +447,9 @@ IoTValue sysExecute(SysOp command, std::vector<IoTValue> &param) {
case sysop_mqttIsConnect:
value.valD = mqttIsConnect();
break;
case sysop_wifiIsConnect:
value.valD = isNetworkActive();
break;
}
return value;
@@ -502,6 +506,8 @@ class SysCallExprAST : public ExprAST {
operation = sysop_getUptime;
else if (Callee == F("mqttIsConnect"))
operation = sysop_mqttIsConnect;
else if (Callee == F("wifiIsConnect"))
operation = sysop_wifiIsConnect;
else
operation = sysop_notfound;
}
@@ -658,7 +664,7 @@ int IoTScenario::gettok() {
LastChar = getLastChar();
if (isalpha(LastChar) || LastChar == '_') { // идентификатор: [a-zA-Z][a-zA-Z0-9]*
IdentifierStr = (char)LastChar;
IdentifierStr = String((char)LastChar);
while (isalnum((LastChar = getLastChar())) || LastChar == '_') {
IdentifierStr += (char)LastChar;
}
@@ -701,7 +707,16 @@ int IoTScenario::gettok() {
IdentifierStr = "";
LastChar = getLastChar();
while (LastChar != '"' && LastChar != EOF) {
if (LastChar == '\\') { // обработка экранированных символов в строке
LastChar = getLastChar();
if (LastChar == '"') {
IdentifierStr += '"';
} else if (LastChar == 'n') {
IdentifierStr += '\n';
}
} else {
IdentifierStr += (char)LastChar;
}
LastChar = getLastChar();
}
LastChar = getLastChar();

View File

@@ -5,43 +5,15 @@ void* getAPI_Loging(String subtype, String params);
void* getAPI_LogingDaily(String subtype, String params);
void* getAPI_IoTMath(String subtype, String params);
void* getAPI_owmWeather(String subtype, String params);
void* getAPI_Ping(String subtype, String params);
void* getAPI_Timer(String subtype, String params);
void* getAPI_Variable(String subtype, String params);
void* getAPI_VButton(String subtype, String params);
void* getAPI_A02Distance(String subtype, String params);
void* getAPI_Acs712(String subtype, String params);
void* getAPI_AhtXX(String subtype, String params);
void* getAPI_AnalogAdc(String subtype, String params);
void* getAPI_BL0937(String subtype, String params);
void* getAPI_Bme280(String subtype, String params);
void* getAPI_Bmp280(String subtype, String params);
void* getAPI_Dht1122(String subtype, String params);
void* getAPI_Ds18b20(String subtype, String params);
void* getAPI_Impulse(String subtype, String params);
void* getAPI_MQgas(String subtype, String params);
void* getAPI_Pzem004_v2(String subtype, String params);
void* getAPI_RTC(String subtype, String params);
void* getAPI_S8(String subtype, String params);
void* getAPI_Sht20(String subtype, String params);
void* getAPI_Sht30(String subtype, String params);
void* getAPI_Sonar(String subtype, String params);
void* getAPI_UART(String subtype, String params);
void* getAPI_AnalogBtn(String subtype, String params);
void* getAPI_ButtonIn(String subtype, String params);
void* getAPI_ButtonOut(String subtype, String params);
void* getAPI_Buzzer(String subtype, String params);
void* getAPI_Encoder(String subtype, String params);
void* getAPI_IoTServo(String subtype, String params);
void* getAPI_Mcp23017(String subtype, String params);
void* getAPI_Mp3(String subtype, String params);
void* getAPI_Multitouch(String subtype, String params);
void* getAPI_Pcf8574(String subtype, String params);
void* getAPI_Pwm8266(String subtype, String params);
void* getAPI_TelegramLT(String subtype, String params);
void* getAPI_DwinI(String subtype, String params);
void* getAPI_Lcd2004(String subtype, String params);
void* getAPI_Oled64(String subtype, String params);
void* getAPI(String subtype, String params) {
void* tmpAPI;
@@ -50,42 +22,14 @@ if ((tmpAPI = getAPI_Loging(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_LogingDaily(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_IoTMath(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_owmWeather(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Ping(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Timer(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Variable(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_VButton(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_A02Distance(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Acs712(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_AhtXX(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_AnalogAdc(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_BL0937(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Bme280(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Bmp280(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Dht1122(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Ds18b20(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Impulse(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_MQgas(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Pzem004_v2(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_RTC(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_S8(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Sht20(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Sht30(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Sonar(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_UART(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_AnalogBtn(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_ButtonIn(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_ButtonOut(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Buzzer(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Encoder(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_IoTServo(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Mcp23017(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Mp3(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Multitouch(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Pcf8574(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Pwm8266(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_TelegramLT(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_DwinI(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Lcd2004(subtype, params)) != nullptr) return tmpAPI;
if ((tmpAPI = getAPI_Oled64(subtype, params)) != nullptr) return tmpAPI;
return nullptr;
}

View File

@@ -40,36 +40,12 @@
"btn-uploadUI": "Формирует автоматически графический интерфейс на базе конфигурации и выгружает в экран. Занимает продолжительное время! (в разработке)"
}
},
"defActive": true,
"defActive": false,
"usedLibs": {
"esp32_4mb": [
"esp32*": [
"plerup/EspSoftwareSerial"
],
"esp32_4mb3f": [
"plerup/EspSoftwareSerial"
],
"esp32cam_4mb": [
"plerup/EspSoftwareSerial"
],
"esp8266_4mb": [
"plerup/EspSoftwareSerial"
],
"esp8266_1mb": [
"plerup/EspSoftwareSerial"
],
"esp8266_1mb_ota": [
"plerup/EspSoftwareSerial"
],
"esp8266_2mb": [
"plerup/EspSoftwareSerial"
],
"esp8266_2mb_ota": [
"plerup/EspSoftwareSerial"
],
"esp8285_1mb": [
"plerup/EspSoftwareSerial"
],
"esp8285_1mb_ota": [
"esp82*": [
"plerup/EspSoftwareSerial"
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,9 @@
// матрица
#define WIDTH 16 // ширина матрицы
#define HEIGHT 16 // высота матрицы
#define SEGMENTS 1 // диодов в одном "пикселе" (для создания матрицы из кусков ленты)
#define NUM_LEDS WIDTH *HEIGHT *SEGMENTS
// лента
#define LED_COUNT NUM_LEDS // число светодиодов в кольце/ленте
#define LED_DT 13 // пин, куда подключен DIN ленты

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,770 @@
#include "utility.h"
byte hue;
boolean loadingFlag = true;
// **************** НАСТРОЙКИ ЭФФЕКТОВ ****************
// эффект "синусоиды" - ОТКЛЮЧЕН
#define WAVES_AMOUNT 2 // количество синусоид
// эффект "шарики"
#define BALLS_AMOUNT 3 // количество "шариков"
#define CLEAR_PATH 1 // очищать путь
#define BALL_TRACK 1 // (0 / 1) - вкл/выкл следы шариков
#define DRAW_WALLS 0 // режим с рисованием препятствий для шаров (не работает на ESP и STM32)
#define TRACK_STEP 70 // длина хвоста шарика (чем больше цифра, тем хвост короче)
// эффект "квадратик"
#define BALL_SIZE 3 // размер шара
#define RANDOM_COLOR 1 // случайный цвет при отскоке
// эффект "огонь"
#define SPARKLES 1 // вылетающие угольки вкл выкл
#define HUE_ADD 0 // добавка цвета в огонь (от 0 до 230) - меняет весь цвет пламени
// эффект "кометы"
#define TAIL_STEP 100 // длина хвоста кометы
#define SATURATION 150 // насыщенность кометы (от 0 до 255)
#define STAR_DENSE 60 // количество (шанс появления) комет
// эффект "конфетти"
#define DENSE 3 // плотность конфетти
#define BRIGHT_STEP 70 // шаг уменьшения яркости
// эффект "снег"
#define SNOW_DENSE 10 // плотность снегопада
// эффект "Светляки"
#define LIGHTERS_AM 35 // количество светляков
uint32_t globalColor = 0xffffff; // Цвет рисования при запуске белый
unsigned char matrixValue[8][16];
unsigned char line[WIDTH];
int pcnt = 0;
int effectSpeed = _speed; // скрость изменения эффекта
uint8_t USE_SEGMENTS = 1;
uint8_t BorderWidth = 0;
uint8_t dir_mx, seg_num, seg_size, seg_offset;
uint16_t XY(uint8_t, uint8_t); // __attribute__ ((weak));
// эффекты матрицы
// *********** "дыхание" яркостью ***********
boolean brightnessDirection;
byte breathBrightness; // Яркость эффекта "Дыхание"
byte globalBrightness = _brightness;
void brightnessRoutine()
{
if (brightnessDirection)
{
breathBrightness += 2;
if (breathBrightness > globalBrightness - 1)
{
brightnessDirection = false;
}
}
else
{
breathBrightness -= 2;
if (breathBrightness < 1)
{
brightnessDirection = true;
}
}
FastLED.setBrightness(breathBrightness);
}
// *********** снегопад 2.0 ***********
void snowRoutine()
{
// сдвигаем всё вниз
for (byte x = 0; x < WIDTH; x++)
{
for (byte y = 0; y < HEIGHT - 1; y++)
{
drawPixelXY(x, y, getPixColorXY(x, y + 1));
}
}
for (byte x = 0; x < WIDTH; x++)
{
// заполняем случайно верхнюю строку
// а также не даём двум блокам по вертикали вместе быть
if (getPixColorXY(x, HEIGHT - 2) == 0 && (random(0, SNOW_DENSE) == 0))
drawPixelXY(x, HEIGHT - 1, 0xE0FFFF - 0x101010 * random(0, 4));
else
drawPixelXY(x, HEIGHT - 1, 0x000000);
}
}
// ***************************** БЛУДНЫЙ КУБИК *****************************
int coordB[2];
int8_t vectorB[2];
CRGB ballColor;
void ballRoutine()
{
if (loadingFlag)
{
for (byte i = 0; i < 2; i++)
{
coordB[i] = WIDTH / 2 * 10;
vectorB[i] = random(8, 20);
ballColor = CHSV(random(0, 9) * 28, 255, 255);
}
loadingFlag = false;
}
for (byte i = 0; i < 2; i++)
{
coordB[i] += vectorB[i];
if (coordB[i] < 0)
{
coordB[i] = 0;
vectorB[i] = -vectorB[i];
if (RANDOM_COLOR)
ballColor = CHSV(random(0, 9) * 28, 255, 255);
// vectorB[i] += random(0, 6) - 3;
}
}
if (coordB[0] > (WIDTH - BALL_SIZE) * 10)
{
coordB[0] = (WIDTH - BALL_SIZE) * 10;
vectorB[0] = -vectorB[0];
if (RANDOM_COLOR)
ballColor = CHSV(random(0, 9) * 28, 255, 255);
// vectorB[0] += random(0, 6) - 3;
}
if (coordB[1] > (HEIGHT - BALL_SIZE) * 10)
{
coordB[1] = (HEIGHT - BALL_SIZE) * 10;
vectorB[1] = -vectorB[1];
if (RANDOM_COLOR)
ballColor = CHSV(random(0, 9) * 28, 255, 255);
// vectorB[1] += random(0, 6) - 3;
}
FastLED.clear();
for (byte i = 0; i < BALL_SIZE; i++)
for (byte j = 0; j < BALL_SIZE; j++)
leds[getPixelNumber(coordB[0] / 10 + i, coordB[1] / 10 + j)] = ballColor;
}
// *********** радуга заливка ***********
void rainbowRoutine()
{
hue += 3;
for (byte i = 0; i < WIDTH; i++)
{
CHSV thisColor = CHSV((byte)(hue + i * float(255 / WIDTH)), 255, 255);
for (byte j = 0; j < HEIGHT; j++)
drawPixelXY(i, j, thisColor); // leds[getPixelNumber(i, j)] = thisColor;
}
}
// *********** радуга дигональная ***********
void rainbowDiagonalRoutine()
{
hue += 3;
for (byte x = 0; x < WIDTH; x++)
{
for (byte y = 0; y < HEIGHT; y++)
{
CHSV thisColor = CHSV((byte)(hue + (float)(WIDTH / HEIGHT * x + y) * (float)(255 / 100)), 255, 255);
drawPixelXY(x, y, thisColor); // leds[getPixelNumber(i, j)] = thisColor;
}
}
}
// *********** радуга активных светодиодов (рисунка) ***********
void rainbowColorsRoutine()
{
hue++;
for (byte i = 0; i < WIDTH; i++)
{
CHSV thisColor = CHSV((byte)(hue + i * float(255 / WIDTH)), 255, 255);
for (byte j = 0; j < HEIGHT; j++)
if (getPixColor(getPixelNumber(i, j)) > 0)
drawPixelXY(i, j, thisColor);
}
}
// ****************************** ОГОНЬ ******************************
// ********************** огонь **********************
// these values are substracetd from the generated values to give a shape to the animation
const unsigned char valueMask[8][16] PROGMEM = {
{32, 0, 0, 0, 0, 0, 0, 32, 32, 0, 0, 0, 0, 0, 0, 32},
{64, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0, 64},
{96, 32, 0, 0, 0, 0, 32, 96, 96, 32, 0, 0, 0, 0, 32, 96},
{128, 64, 32, 0, 0, 32, 64, 128, 128, 64, 32, 0, 0, 32, 64, 128},
{160, 96, 64, 32, 32, 64, 96, 160, 160, 96, 64, 32, 32, 64, 96, 160},
{192, 128, 96, 64, 64, 96, 128, 192, 192, 128, 96, 64, 64, 96, 128, 192},
{255, 160, 128, 96, 96, 128, 160, 255, 255, 160, 128, 96, 96, 128, 160, 255},
{255, 192, 160, 128, 128, 160, 192, 255, 255, 192, 160, 128, 128, 160, 192, 255}};
// these are the hues for the fire,
// should be between 0 (red) to about 25 (yellow)
const unsigned char hueMask[8][16] PROGMEM = {
{1, 11, 19, 25, 25, 22, 11, 1, 1, 11, 19, 25, 25, 22, 11, 1},
{1, 8, 13, 19, 25, 19, 8, 1, 1, 8, 13, 19, 25, 19, 8, 1},
{1, 8, 13, 16, 19, 16, 8, 1, 1, 8, 13, 16, 19, 16, 8, 1},
{1, 5, 11, 13, 13, 13, 5, 1, 1, 5, 11, 13, 13, 13, 5, 1},
{1, 5, 11, 11, 11, 11, 5, 1, 1, 5, 11, 11, 11, 11, 5, 1},
{0, 1, 5, 8, 8, 5, 1, 0, 0, 1, 5, 8, 8, 5, 1, 0},
{0, 0, 1, 5, 5, 1, 0, 0, 0, 0, 1, 5, 5, 1, 0, 0},
{0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0}};
// Randomly generate the next line (matrix row)
void generateLine()
{
for (uint8_t x = 0; x < WIDTH; x++)
{
line[x] = random(64, 255);
}
}
// shift all values in the matrix up one row
void shiftUp()
{
for (uint8_t y = HEIGHT - 1; y > 0; y--)
{
for (uint8_t x = 0; x < WIDTH; x++)
{
uint8_t newX = x;
if (x > 15)
newX = x % 16;
if (y > 7)
continue;
matrixValue[y][newX] = matrixValue[y - 1][newX];
}
}
for (uint8_t x = 0; x < WIDTH; x++)
{
uint8_t newX = x;
if (x > 15)
newX = x % 16;
matrixValue[0][newX] = line[newX];
}
}
// draw a frame, interpolating between 2 "key frames"
// @param pcnt percentage of interpolation
void drawFrame(int pcnt)
{
int nextv;
// each row interpolates with the one before it
for (unsigned char y = HEIGHT - 1; y > 0; y--)
{
for (unsigned char x = 0; x < WIDTH; x++)
{
uint8_t newX = x;
if (x > 15)
newX = x % 16;
if (y < 8)
{
nextv =
(((100.0 - pcnt) * matrixValue[y][newX] + pcnt * matrixValue[y - 1][newX]) / 100.0) - pgm_read_byte(&(valueMask[y][newX]));
CRGB color = CHSV(
HUE_ADD + pgm_read_byte(&(hueMask[y][newX])), // H
255, // S
(uint8_t)max(0, nextv) // V
);
leds[getPixelNumber(x, y)] = color;
}
else if (y == 8 && SPARKLES)
{
if (random(0, 20) == 0 && getPixColorXY(x, y - 1) != 0)
drawPixelXY(x, y, getPixColorXY(x, y - 1));
else
drawPixelXY(x, y, 0);
}
else if (SPARKLES)
{
// старая версия для яркости
if (getPixColorXY(x, y - 1) > 0)
drawPixelXY(x, y, getPixColorXY(x, y - 1));
else
drawPixelXY(x, y, 0);
}
}
}
// first row interpolates with the "next" line
for (unsigned char x = 0; x < WIDTH; x++)
{
uint8_t newX = x;
if (x > 15)
newX = x % 16;
CRGB color = CHSV(
HUE_ADD + pgm_read_byte(&(hueMask[0][newX])), // H
255, // S
(uint8_t)(((100.0 - pcnt) * matrixValue[0][newX] + pcnt * line[newX]) / 100.0) // V
);
// leds[getPixelNumber(newX, 0)] = color; // На форуме пишут что это ошибка - вместо newX должно быть x, иначе
leds[getPixelNumber(x, 0)] = color; // на матрицах шире 16 столбцов нижний правый угол неработает
}
}
void fireRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear();
generateLine();
memset(matrixValue, 0, sizeof(matrixValue));
}
if (pcnt >= 100)
{
shiftUp();
generateLine();
pcnt = 0;
}
drawFrame(pcnt);
pcnt += 30;
}
// **************** МАТРИЦА *****************
void matrixRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear();
}
for (byte x = 0; x < WIDTH; x++)
{
// заполняем случайно верхнюю строку
uint32_t thisColor = getPixColorXY(x, HEIGHT - 1);
if (thisColor == 0)
drawPixelXY(x, HEIGHT - 1, 0x00FF00 * (random(0, 10) == 0));
else if (thisColor < 0x002000)
drawPixelXY(x, HEIGHT - 1, 0);
else
drawPixelXY(x, HEIGHT - 1, thisColor - 0x002000);
}
// сдвигаем всё вниз
for (byte x = 0; x < WIDTH; x++)
{
for (byte y = 0; y < HEIGHT - 1; y++)
{
drawPixelXY(x, y, getPixColorXY(x, y + 1));
}
}
}
// ********************* ЗВЕЗДОПАД ******************
void fadePixel(byte i, byte j, byte step)
{ // новый фейдер
int pixelNum = getPixelNumber(i, j);
if (getPixColor(pixelNum) == 0)
return;
if (leds[pixelNum].r >= 30 ||
leds[pixelNum].g >= 30 ||
leds[pixelNum].b >= 30)
{
leds[pixelNum].fadeToBlackBy(step);
}
else
{
leds[pixelNum] = 0;
}
}
// функция плавного угасания цвета для всех пикселей
void fader(byte step)
{
for (byte i = 0; i < WIDTH; i++)
{
for (byte j = 0; j < HEIGHT; j++)
{
fadePixel(i, j, step);
}
}
}
void starfallRoutine()
{
// заполняем головами комет левую и верхнюю линию
for (byte i = HEIGHT / 2; i < HEIGHT; i++)
{
if (getPixColorXY(0, i) == 0 && (random(0, STAR_DENSE) == 0) && getPixColorXY(0, i + 1) == 0 && getPixColorXY(0, i - 1) == 0)
leds[getPixelNumber(0, i)] = CHSV(random(0, 200), SATURATION, 255);
}
for (byte i = 0; i < WIDTH / 2; i++)
{
if (getPixColorXY(i, HEIGHT - 1) == 0 && (random(0, STAR_DENSE) == 0) && getPixColorXY(i + 1, HEIGHT - 1) == 0 && getPixColorXY(i - 1, HEIGHT - 1) == 0)
leds[getPixelNumber(i, HEIGHT - 1)] = CHSV(random(0, 200), SATURATION, 255);
}
// сдвигаем по диагонали
for (byte y = 0; y < HEIGHT - 1; y++)
{
for (byte x = WIDTH - 1; x > 0; x--)
{
drawPixelXY(x, y, getPixColorXY(x - 1, y + 1));
}
}
// уменьшаем яркость левой и верхней линии, формируем "хвосты"
for (byte i = HEIGHT / 2; i < HEIGHT; i++)
{
fadePixel(0, i, TAIL_STEP);
}
for (byte i = 0; i < WIDTH / 2; i++)
{
fadePixel(i, HEIGHT - 1, TAIL_STEP);
}
}
// рандомные гаснущие вспышки
void sparklesRoutine()
{
for (byte i = 0; i < DENSE; i++)
{
byte x = random(0, WIDTH);
byte y = random(0, HEIGHT);
if (getPixColorXY(x, y) == 0)
leds[getPixelNumber(x, y)] = CHSV(random(0, 255), 255, 255);
}
fader(BRIGHT_STEP);
}
// ----------------------------- СВЕТЛЯКИ ------------------------------
int lightersPos[2][LIGHTERS_AM];
int8_t lightersSpeed[2][LIGHTERS_AM];
CHSV lightersColor[LIGHTERS_AM];
byte loopCounter;
int angle[LIGHTERS_AM];
int speedV[LIGHTERS_AM];
int8_t angleSpeed[LIGHTERS_AM];
void lightersRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
randomSeed(millis());
for (byte i = 0; i < LIGHTERS_AM; i++)
{
lightersPos[0][i] = random(0, WIDTH * 10);
lightersPos[1][i] = random(0, HEIGHT * 10);
lightersSpeed[0][i] = random(-10, 10);
lightersSpeed[1][i] = random(-10, 10);
lightersColor[i] = CHSV(random(0, 255), 255, 255);
}
}
FastLED.clear();
if (++loopCounter > 20)
loopCounter = 0;
for (byte i = 0; i < map(LIGHTERS_AM, 0, 255, 5, 150); i++)
{
if (loopCounter == 0)
{ // меняем скорость каждые 255 отрисовок
lightersSpeed[0][i] += random(-3, 4);
lightersSpeed[1][i] += random(-3, 4);
lightersSpeed[0][i] = constrain(lightersSpeed[0][i], -20, 20);
lightersSpeed[1][i] = constrain(lightersSpeed[1][i], -20, 20);
}
lightersPos[0][i] += lightersSpeed[0][i];
lightersPos[1][i] += lightersSpeed[1][i];
if (lightersPos[0][i] < 0)
lightersPos[0][i] = (WIDTH - 1) * 10;
if (lightersPos[0][i] >= WIDTH * 10)
lightersPos[0][i] = 0;
if (lightersPos[1][i] < 0)
{
lightersPos[1][i] = 0;
lightersSpeed[1][i] = -lightersSpeed[1][i];
}
if (lightersPos[1][i] >= (HEIGHT - 1) * 10)
{
lightersPos[1][i] = (HEIGHT - 1) * 10;
lightersSpeed[1][i] = -lightersSpeed[1][i];
}
drawPixelXY(lightersPos[0][i] / 10, lightersPos[1][i] / 10, lightersColor[i]);
}
}
// ------------- ПЕЙНТБОЛ -------------
void lightBallsRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear(); // очистить
dir_mx = WIDTH > HEIGHT ? 0 : 1; // 0 - квадратные сегменты расположены горизонтально, 1 - вертикально
seg_num = dir_mx == 0 ? (WIDTH / HEIGHT) : (HEIGHT / WIDTH); // вычисляем количество сегментов, умещающихся на матрице
seg_size = dir_mx == 0 ? HEIGHT : WIDTH; // Размер квадратного сегмента (высота и ширина равны)
seg_offset = ((dir_mx == 0 ? WIDTH : HEIGHT) - seg_size * seg_num) / (seg_num + 1); // смещение от края матрицы и между сегментами
BorderWidth = 0;
}
// Apply some blurring to whatever's already on the matrix
// Note that we never actually clear the matrix, we just constantly
// blur it repeatedly. Since the blurring is 'lossy', there's
// an automatic trend toward black -- by design.
uint8_t blurAmount = dim8_raw(beatsin8(2, 64, 100));
blur2d(leds, WIDTH, HEIGHT, blurAmount);
// The color of each point shifts over time, each at a different speed.
uint32_t ms = millis();
int16_t idx;
byte cnt = map(effectSpeed, 0, 255, 1, 4);
if (USE_SEGMENTS != 0)
{
// Для неквадратных - вычленяем квадратные сегменты, которые равномерно распределяем по ширине / высоте матрицы
uint8_t i = beatsin8(91, 0, seg_size - BorderWidth - 1);
uint8_t j = beatsin8(109, 0, seg_size - BorderWidth - 1);
uint8_t k = beatsin8(73, 0, seg_size - BorderWidth - 1);
uint8_t m = beatsin8(123, 0, seg_size - BorderWidth - 1);
uint8_t d1 = ms / 29;
uint8_t d2 = ms / 41;
uint8_t d3 = ms / 73;
uint8_t d4 = ms / 97;
for (uint8_t ii = 0; ii < seg_num; ii++)
{
delay(0); // Для предотвращения ESP8266 Watchdog Timer
uint8_t cx = dir_mx == 0 ? (seg_offset * (ii + 1) + seg_size * ii) : 0;
uint8_t cy = dir_mx == 0 ? 0 : (seg_offset * (ii + 1) + seg_size * ii);
uint8_t color_shift = ii * 50;
if (cnt <= 1)
{
idx = XY(i + cx, j + cy);
leds[idx] += CHSV(color_shift + d1, 200U, 255U);
}
if (cnt <= 2)
{
idx = XY(j + cx, k + cy);
leds[idx] += CHSV(color_shift + d2, 200U, 255U);
}
if (cnt <= 3)
{
idx = XY(k + cx, m + cy);
leds[idx] += CHSV(color_shift + d3, 200U, 255U);
}
if (cnt <= 4)
{
idx = XY(m + cx, i + cy);
leds[idx] += CHSV(color_shift + d4, 200U, 255U);
}
// При соединении матрицы из угла вверх или вниз почему-то слева и справа узора остаются полосы, которые
// не гаснут обычным blur - гасим полоски левой и правой стороны дополнительно.
// При соединении из угла влево или вправо или на неквадратных матрицах такого эффекта не наблюдается
for (byte i2 = cy; i2 < cy + seg_size; i2++)
{
fadePixel(cx + BorderWidth, i2, 15);
fadePixel(cx + seg_size - BorderWidth - 1, i2, 15);
}
}
}
else
{
uint8_t i = beatsin8(91, BorderWidth, WIDTH - BorderWidth - 1);
uint8_t j = beatsin8(109, BorderWidth, HEIGHT - BorderWidth - 1);
uint8_t k = beatsin8(73, BorderWidth, WIDTH - BorderWidth - 1);
uint8_t m = beatsin8(123, BorderWidth, HEIGHT - BorderWidth - 1);
if (cnt <= 1)
{
idx = XY(i, j);
leds[idx] += CHSV(ms / 29, 200U, 255U);
}
if (cnt <= 2)
{
idx = XY(k, j);
leds[idx] += CHSV(ms / 41, 200U, 255U);
}
if (cnt <= 3)
{
idx = XY(k, m);
leds[idx] += CHSV(ms / 73, 200U, 255U);
}
if (cnt <= 4)
{
idx = XY(i, m);
leds[idx] += CHSV(ms / 97, 200U, 255U);
}
if (WIDTH == HEIGHT)
{
// При соединении матрицы из угла вверх или вниз почему-то слева и справа узора остаются полосы, которые
// не гаснут обычным blur - гасим полоски левой и правой стороны дополнительно.
// При соединении из угла влево или вправо или на неквадратных матрицах такого эффекта не наблюдается
for (byte i = 0; i < HEIGHT; i++)
{
fadePixel(0, i, 15);
fadePixel(WIDTH - 1, i, 15);
}
}
}
}
// ------------- ВОДОВОРОТ -------------
void swirlRoutine()
{
if (loadingFlag)
{
loadingFlag = false;
FastLED.clear(); // очистить
dir_mx = WIDTH > HEIGHT ? 0 : 1; // 0 - квадратные сегменты расположены горизонтально, 1 - вертикально
seg_num = dir_mx == 0 ? (WIDTH / HEIGHT) : (HEIGHT / WIDTH); // вычисляем количество сегментов, умещающихся на матрице
seg_size = dir_mx == 0 ? HEIGHT : WIDTH; // Размер квадратного сегмента (высота и ширина равны)
seg_offset = ((dir_mx == 0 ? WIDTH : HEIGHT) - seg_size * seg_num) / (seg_num + 1); // смещение от края матрицы и между сегментами
BorderWidth = seg_num == 1 ? 0 : 1;
}
// Apply some blurring to whatever's already on the matrix
// Note that we never actually clear the matrix, we just constantly
// blur it repeatedly. Since the blurring is 'lossy', there's
// an automatic trend toward black -- by design.
uint8_t blurAmount = dim8_raw(beatsin8(2, 64, 100));
blur2d(leds, WIDTH, HEIGHT, blurAmount);
uint32_t ms = millis();
int16_t idx;
if (USE_SEGMENTS != 0)
{
// Use two out-of-sync sine waves
uint8_t i = beatsin8(41, 0, seg_size - BorderWidth - 1);
uint8_t j = beatsin8(27, 0, seg_size - BorderWidth - 1);
// Also calculate some reflections
uint8_t ni = (seg_size - 1) - i;
uint8_t nj = (seg_size - 1) - j;
uint8_t d1 = ms / 11;
uint8_t d2 = ms / 13;
uint8_t d3 = ms / 17;
uint8_t d4 = ms / 29;
uint8_t d5 = ms / 37;
uint8_t d6 = ms / 41;
for (uint8_t ii = 0; ii < seg_num; ii++)
{
delay(0); // Для предотвращения ESP8266 Watchdog Timer
uint8_t cx = dir_mx == 0 ? (seg_offset * (ii + 1) + seg_size * ii) : 0;
uint8_t cy = dir_mx == 0 ? 0 : (seg_offset * (ii + 1) + seg_size * ii);
uint8_t color_shift = ii * 50;
// The color of each point shifts over time, each at a different speed.
idx = XY(i + cx, j + cy);
leds[idx] += CHSV(color_shift + d1, 200, 192);
idx = XY(ni + cx, nj + cy);
leds[idx] += CHSV(color_shift + d2, 200, 192);
idx = XY(i + cx, nj + cy);
leds[idx] += CHSV(color_shift + d3, 200, 192);
idx = XY(ni + cx, j + cy);
leds[idx] += CHSV(color_shift + d4, 200, 192);
idx = XY(j + cx, i + cy);
leds[idx] += CHSV(color_shift + d5, 200, 192);
idx = XY(nj + cx, ni + cy);
leds[idx] += CHSV(color_shift + d6, 200, 192);
// При соединении матрицы из угла вверх или вниз почему-то слева и справа узора остаются полосы, которые
// не гаснут обычным blur - гасим полоски левой и правой стороны дополнительно.
// При соединении из угла влево или вправо или на неквадратных матрицах такого эффекта не наблюдается
for (byte i2 = cy; i2 < cy + seg_size; i2++)
{
fadePixel(cx, i2, 15);
fadePixel(cx + BorderWidth, i2, 15);
fadePixel(cx + seg_size - 1, i2, 15);
fadePixel(cx + seg_size - BorderWidth - 1, i2, 15);
}
}
}
else
{
// Use two out-of-sync sine waves
uint8_t i = beatsin8(41, BorderWidth, WIDTH - BorderWidth - 1);
uint8_t j = beatsin8(27, BorderWidth, HEIGHT - BorderWidth - 1);
// Also calculate some reflections
uint8_t ni = (WIDTH - 1) - i;
uint8_t nj = (HEIGHT - 1) - j;
// The color of each point shifts over time, each at a different speed.
idx = XY(i, j);
leds[idx] += CHSV(ms / 11, 200, 192);
idx = XY(ni, nj);
leds[idx] += CHSV(ms / 13, 200, 192);
idx = XY(i, nj);
leds[idx] += CHSV(ms / 17, 200, 192);
idx = XY(ni, j);
leds[idx] += CHSV(ms / 29, 200, 192);
if (HEIGHT == WIDTH)
{
// для квадратных матриц - 6 точек создают более красивую картину
idx = XY(j, i);
leds[idx] += CHSV(ms / 37, 200, 192);
idx = XY(nj, ni);
leds[idx] += CHSV(ms / 41, 200, 192);
// При соединении матрицы из угла вверх или вниз почему-то слева и справа узора остаются полосы, которые
// не гаснут обычным blur - гасим полоски левой и правой стороны дополнительно.
// При соединении из угла влево или вправо или на неквадратных матрицах такого эффекта не наблюдается
for (byte i = 0; i < HEIGHT; i++)
{
fadePixel(0, i, 15);
fadePixel(WIDTH - 1, i, 15);
}
}
}
}
uint16_t XY(uint8_t x, uint8_t y)
{
return getPixelNumber(x, y);
}
//--------------------крутящаяся радуга матрица------------------
void rainbow_loop_matrix()
{ //-m3-LOOP HSV RAINBOW
idex++;
ihue = ihue + thisstep;
if (idex >= LED_COUNT)
{
idex = 0;
}
if (ihue > 255)
{
ihue = 0;
}
for (byte i = 0; i < WIDTH; i++)
{
CHSV thisColor = CHSV(ihue, thissat, 255);
for (byte j = 0; j < HEIGHT; j++)
drawPixelXY(i, j, thisColor); // leds[getPixelNumber(i, j)] = thisColor;
}
LEDS.show();
if (safeDelay(thisdelay))
return;
}

View File

@@ -0,0 +1,40 @@
{
"menuSection": "screens",
"configItem": [
{
"global": 0,
"name": "GyverLAMP",
"type": "Reading",
"subtype": "GyverLAMP",
"id": "GyverLAMP",
"widget": "range",
"page": "Кнопки",
"descr": "GyverLAMP",
"needSave": 0,
"brightness": "10",
"speed": 30,
"dalay": 10000
}
],
"about": {
"authorName": "",
"authorContact": "",
"authorGit": "",
"exampleURL": "",
"specialThanks": "",
"moduleName": "GyverLAMP",
"moduleVersion": "1",
"moduleDesc": "xxx",
"propInfo": {},
"funcInfo": []
},
"defActive": false,
"usedLibs": {
"esp32*": [
"fastled/FastLED@^3.5.0"
],
"esp82*": [
"fastled/FastLED@^3.5.0"
]
}
}

View File

@@ -0,0 +1,92 @@
#include "config.h"
#define MATRIX_TYPE 0
// #define NUM_LEDS LED_COUNT
// **************** НАСТРОЙКА МАТРИЦЫ ****************
#if (CONNECTION_ANGLE == 0 && STRIP_DIRECTION == 0)
#define _WIDTH WIDTH
#define THIS_X x
#define THIS_Y y
#elif (CONNECTION_ANGLE == 0 && STRIP_DIRECTION == 1)
#define _WIDTH HEIGHT
#define THIS_X y
#define THIS_Y x
#elif (CONNECTION_ANGLE == 1 && STRIP_DIRECTION == 0)
#define _WIDTH WIDTH
#define THIS_X x
#define THIS_Y (HEIGHT - y - 1)
#elif (CONNECTION_ANGLE == 1 && STRIP_DIRECTION == 3)
#define _WIDTH HEIGHT
#define THIS_X (HEIGHT - y - 1)
#define THIS_Y x
#elif (CONNECTION_ANGLE == 2 && STRIP_DIRECTION == 2)
#define _WIDTH WIDTH
#define THIS_X (WIDTH - x - 1)
#define THIS_Y (HEIGHT - y - 1)
#elif (CONNECTION_ANGLE == 2 && STRIP_DIRECTION == 3)
#define _WIDTH HEIGHT
#define THIS_X (HEIGHT - y - 1)
#define THIS_Y (WIDTH - x - 1)
#elif (CONNECTION_ANGLE == 3 && STRIP_DIRECTION == 2)
#define _WIDTH WIDTH
#define THIS_X (WIDTH - x - 1)
#define THIS_Y y
#elif (CONNECTION_ANGLE == 3 && STRIP_DIRECTION == 1)
#define _WIDTH HEIGHT
#define THIS_X y
#define THIS_Y (WIDTH - x - 1)
#else
#define _WIDTH WIDTH
#define THIS_X x
#define THIS_Y y
#pragma message "Wrong matrix parameters! Set to default"
#endif
// получить номер пикселя в ленте по координатам
uint16_t getPixelNumber(int8_t x, int8_t y)
{
if ((THIS_Y % 2 == 0) || MATRIX_TYPE)
{ // если чётная строка
return (THIS_Y * _WIDTH + THIS_X);
}
else
{ // если нечётная строка
return (THIS_Y * _WIDTH + _WIDTH - THIS_X - 1);
}
}
// функция отрисовки точки по координатам X Y
void drawPixelXY(int8_t x, int8_t y, CRGB color)
{
if (x < 0 || x > WIDTH - 1 || y < 0 || y > HEIGHT - 1)
return;
int thisPixel = getPixelNumber(x, y) * SEGMENTS;
for (byte i = 0; i < SEGMENTS; i++)
{
leds[thisPixel + i] = color;
}
//FastLED.show();
}
// функция получения цвета пикселя по его номеру
uint32_t getPixColor(int thisSegm)
{
int thisPixel = thisSegm * SEGMENTS;
if (thisPixel < 0 || thisPixel > NUM_LEDS - 1)
return 0;
return (((uint32_t)leds[thisPixel].r << 16) | ((long)leds[thisPixel].g << 8) | (long)leds[thisPixel].b);
}
// функция получения цвета пикселя в матрице по его координатам
uint32_t getPixColorXY(int8_t x, int8_t y)
{
return getPixColor(getPixelNumber(x, y));
}

View File

@@ -117,7 +117,7 @@
}
]
},
"defActive": true,
"defActive": false,
"usedLibs": {
"esp32*": [
"https://github.com/robotclass/RobotClass_LiquidCrystal_I2C",

File diff suppressed because it is too large Load Diff

View File

@@ -2,6 +2,15 @@
* @file NexUpload.h
* The definition of class NexUpload.
*
* 1 - Removed all the Arduino code and replaced it by ESP-IDF
* 2 - Removed hard-coded UART configuration, see ESPNexUpload constructor
* 3 - Removed statusMessage and the function _printInfoLine
* 4 - Removed call-back functionality
* 5 - Removed one out of two upload functions
* 6 - BugFix in upload function
* @author Machiel Mastenbroek (machiel.mastenbroek@gmail.com)
* @date 2022/08/14
* @version 0.6.0
*
* 1 - BugFix when display baudrate is diffrent from initial ESP baudrate
* 2 - Improved debug information
@@ -52,25 +61,27 @@
#ifndef __ESPNEXUPLOAD_H__
#define __ESPNEXUPLOAD_H__
#include <functional>
//#include <iostream>
#include <string.h> /* printf, scanf, NULL */
//#include <inttypes.h>
#include "esp_log.h"
//#include "freertos/FreeRTOS.h"
//#include "freertos/task.h"
//#include "driver/gpio.h"
#include "driver/uart.h"
//#include "hal/uart_types.h"
#include <Arduino.h>
#include <StreamString.h>
#ifdef ESP8266
#include <SoftwareSerial.h>
#else
#include <HardwareSerial.h>
#include <SoftwareSerial.h>
#endif
#define CONFIG_NEX_UART_RECV_BUFFER_SIZE 256
/**
* @addtogroup CoreAPI
* @{
*/
// callback template definition
typedef std::function<void(void)> THandlerFunction;
/**
*
* Provides the API for nextion to upload the ftf file.
@@ -78,15 +89,12 @@ typedef std::function<void(void)> THandlerFunction;
class ESPNexUpload
{
public: /* methods */
// callback template definition
typedef std::function<void(void)> THandlerFunction;
/**
* Constructor.
*
* @param uint32_t upload_baudrate - set upload baudrate.
*/
ESPNexUpload(uint32_t upload_baudrate, int line, int rx, int tx);
ESPNexUpload(uart_port_t uart_num, uint32_t baud_rate, gpio_num_t tx_io_num, gpio_num_t rx_io_num);
/**
* destructor.
@@ -106,14 +114,7 @@ public: /* methods */
*
* @return true if success, false for failure.
*/
bool prepareUpload(uint32_t file_size);
/**
* set Update Progress Callback. (What to do during update progress)
*
* @return none
*/
void setUpdateProgressCallback(THandlerFunction value);
bool prepareUpload(uint32_t file_size, bool oldProt);
/**
* start update tft file to nextion.
@@ -124,14 +125,7 @@ public: /* methods */
*/
bool upload(const uint8_t *file_buf, size_t buf_size);
/**
* start update tft file to nextion.
*
* @param Stream &myFile
* @return true if success, false for failure.
*/
bool upload(Stream &myFile);
/**
* Send reset command to Nextion over serial
*
@@ -146,10 +140,14 @@ public: /* methods */
*/
void end(void);
public: /* data */
String statusMessage = "";
private: /* methods */
/*
* Semaphore construction to prevent double UART actions
*
*/
void uart_mutex_lock(void) {do {} while (xSemaphoreTake(_upload_uart_lock, portMAX_DELAY) != pdPASS);};
void uart_mutex_unlock(void) {xSemaphoreGive(_upload_uart_lock);};
/*
* get communicate baudrate.
*
@@ -165,7 +163,7 @@ private: /* methods */
*
* @return true if success, false for failure.
*/
bool _searchBaudrate(uint32_t baudrate);
bool _searchBaudrate(int baudrate);
/*
* set download baudrate.
@@ -197,7 +195,7 @@ private: /* methods */
* This test is used by the 'upload to Nextion device' feature of the Nextion Editor V0.58
*
*/
bool _echoTest(String input);
bool _echoTest(std::string input);
/*
* This function get the sleep and dim value from the Nextion display.
@@ -219,14 +217,7 @@ private: /* methods */
* @param input - string to print
*
*/
void _printSerialData(bool esp_request, String input);
/*
* This function print a prefix debug line
*
* @param line: optional debug/ info line
*/
void _printInfoLine(String line = "");
void _printSerialData(bool esp_request, std::string input);
/*
* Send command to Nextion.
@@ -249,7 +240,7 @@ private: /* methods */
* @return the length of string buffer.
*
*/
uint16_t recvRetString(String &string, uint32_t timeout = 500, bool recv_flag = false);
uint16_t recvRetString(std::string &string, uint32_t timeout = 500,bool recv_flag = false);
/*
*
@@ -261,25 +252,72 @@ private: /* methods */
* @return time in us length of string buffer.
*
*/
uint32_t calculateTransmissionTimeMs(String message);
uint32_t calculateTransmissionTimeMs(std::string message);
void nexSerialBegin(uint32_t upload_baudrate, int line, int rx, int tx);
/*
* Setup UART for communication with display
*
* @param uart_num - UART number
* @param baud_rate - baud rate speed
* @param tx_io_num - GPIO TX pin
* @param rx_io_num - GPIO RX pin
*
*/
void setBaudrate(uart_port_t uart_num, uint32_t baud_rate, gpio_num_t tx_io_num, gpio_num_t rx_io_num);
/*
* Check is UART is avaialble
*/
uint32_t uartAvailable();
/*
* Read one RX byte
*
* @return one received UART byte
*/
uint8_t uartRead();
/*
* Write one TX byte
*
* @param c - one byte
*
*/
void uartWrite(uint8_t c);
/*
* Write char string
*
* @param data - char string of data to send
* @param len - length of the string
*
*/
void uartWriteBuf(const char * data, size_t len);
/*
* Clear TX UART buffer
*/
void uartFlushTxOnly();
private: /* data */
bool _oldProtv11;
uint32_t _baudrate; /* nextion serail baudrate */
uint32_t _undownloadByte; /* undownload byte of tft file */
uart_port_t _upload_uart_num; /* upload uart port number */
uint32_t _upload_baudrate; /* upload baudrate */
uint16_t _sent_packets = 0; /* upload baudrate */
uint8_t _rx;
uint8_t _tx;
uint8_t _line;
THandlerFunction _updateProgressCallback;
gpio_num_t _upload_tx_io_num; /* upload gpio TX */
gpio_num_t _upload_rx_io_num; /* upload gpio RX */
xSemaphoreHandle _upload_uart_lock; /* semaphore to prevent double UART actions */
bool _upload_uart_has_peek; /* UART RX peek flag */
uint8_t _upload_uart_peek_byte; /* UART RX peek byte */
//uint16_t _sent_packets = 0; /* _sent_packets till 4096 bytes */
uint32_t _sent_packets_total = 0; /* total number of uploaded display firmware bytes */
bool _uart_diver_installed; /* flag, if true UART is installed */
#ifdef ESP8266
SoftwareSerial* nexSerial;
#else
Stream* nexSerial;
#endif
std::string str_snprintf(const char *fmt, size_t len, ...);
/// Format the byte array \p data of length \p len in pretty-printed, human-readable hex.
std::string format_hex_pretty(const uint8_t *data, size_t length);
static char format_hex_pretty_char(uint8_t v);
};
/**
* @}

Some files were not shown because too many files have changed in this diff Show More