Add MitsibishiAC
This commit is contained in:
5
MitsubishiAC/.gitignore
vendored
Normal file
5
MitsubishiAC/.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
.pio
|
||||||
|
.vscode/.browse.c_cpp.db*
|
||||||
|
.vscode/c_cpp_properties.json
|
||||||
|
.vscode/launch.json
|
||||||
|
.vscode/ipch
|
||||||
67
MitsubishiAC/.travis.yml
Normal file
67
MitsubishiAC/.travis.yml
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
# Continuous Integration (CI) is the practice, in software
|
||||||
|
# engineering, of merging all developer working copies with a shared mainline
|
||||||
|
# several times a day < https://docs.platformio.org/page/ci/index.html >
|
||||||
|
#
|
||||||
|
# Documentation:
|
||||||
|
#
|
||||||
|
# * Travis CI Embedded Builds with PlatformIO
|
||||||
|
# < https://docs.travis-ci.com/user/integration/platformio/ >
|
||||||
|
#
|
||||||
|
# * PlatformIO integration with Travis CI
|
||||||
|
# < https://docs.platformio.org/page/ci/travis.html >
|
||||||
|
#
|
||||||
|
# * User Guide for `platformio ci` command
|
||||||
|
# < https://docs.platformio.org/page/userguide/cmd_ci.html >
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# Please choose one of the following templates (proposed below) and uncomment
|
||||||
|
# it (remove "# " before each line) or use own configuration according to the
|
||||||
|
# Travis CI documentation (see above).
|
||||||
|
#
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Template #1: General project. Test it using existing `platformio.ini`.
|
||||||
|
#
|
||||||
|
|
||||||
|
# language: python
|
||||||
|
# python:
|
||||||
|
# - "2.7"
|
||||||
|
#
|
||||||
|
# sudo: false
|
||||||
|
# cache:
|
||||||
|
# directories:
|
||||||
|
# - "~/.platformio"
|
||||||
|
#
|
||||||
|
# install:
|
||||||
|
# - pip install -U platformio
|
||||||
|
# - platformio update
|
||||||
|
#
|
||||||
|
# script:
|
||||||
|
# - platformio run
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Template #2: The project is intended to be used as a library with examples.
|
||||||
|
#
|
||||||
|
|
||||||
|
# language: python
|
||||||
|
# python:
|
||||||
|
# - "2.7"
|
||||||
|
#
|
||||||
|
# sudo: false
|
||||||
|
# cache:
|
||||||
|
# directories:
|
||||||
|
# - "~/.platformio"
|
||||||
|
#
|
||||||
|
# env:
|
||||||
|
# - PLATFORMIO_CI_SRC=path/to/test/file.c
|
||||||
|
# - PLATFORMIO_CI_SRC=examples/file.ino
|
||||||
|
# - PLATFORMIO_CI_SRC=path/to/test/directory
|
||||||
|
#
|
||||||
|
# install:
|
||||||
|
# - pip install -U platformio
|
||||||
|
# - platformio update
|
||||||
|
#
|
||||||
|
# script:
|
||||||
|
# - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N
|
||||||
10
MitsubishiAC/.vscode/extensions.json
vendored
Normal file
10
MitsubishiAC/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||||
|
// for the documentation about the extensions.json format
|
||||||
|
"recommendations": [
|
||||||
|
"platformio.platformio-ide"
|
||||||
|
],
|
||||||
|
"unwantedRecommendations": [
|
||||||
|
"ms-vscode.cpptools-extension-pack"
|
||||||
|
]
|
||||||
|
}
|
||||||
39
MitsubishiAC/include/README
Normal file
39
MitsubishiAC/include/README
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
|
||||||
|
This directory is intended for project header files.
|
||||||
|
|
||||||
|
A header file is a file containing C declarations and macro definitions
|
||||||
|
to be shared between several project source files. You request the use of a
|
||||||
|
header file in your project source file (C, C++, etc) located in `src` folder
|
||||||
|
by including it, with the C preprocessing directive `#include'.
|
||||||
|
|
||||||
|
```src/main.c
|
||||||
|
|
||||||
|
#include "header.h"
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Including a header file produces the same results as copying the header file
|
||||||
|
into each source file that needs it. Such copying would be time-consuming
|
||||||
|
and error-prone. With a header file, the related declarations appear
|
||||||
|
in only one place. If they need to be changed, they can be changed in one
|
||||||
|
place, and programs that include the header file will automatically use the
|
||||||
|
new version when next recompiled. The header file eliminates the labor of
|
||||||
|
finding and changing all the copies as well as the risk that a failure to
|
||||||
|
find one copy will result in inconsistencies within a program.
|
||||||
|
|
||||||
|
In C, the usual convention is to give header files names that end with `.h'.
|
||||||
|
It is most portable to use only letters, digits, dashes, and underscores in
|
||||||
|
header file names, and at most one dot.
|
||||||
|
|
||||||
|
Read more about using header files in official GCC documentation:
|
||||||
|
|
||||||
|
* Include Syntax
|
||||||
|
* Include Operation
|
||||||
|
* Once-Only Headers
|
||||||
|
* Computed Includes
|
||||||
|
|
||||||
|
https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html
|
||||||
46
MitsubishiAC/lib/README
Normal file
46
MitsubishiAC/lib/README
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
|
||||||
|
This directory is intended for project specific (private) libraries.
|
||||||
|
PlatformIO will compile them to static libraries and link into executable file.
|
||||||
|
|
||||||
|
The source code of each library should be placed in a an own separate directory
|
||||||
|
("lib/your_library_name/[here are source files]").
|
||||||
|
|
||||||
|
For example, see a structure of the following two libraries `Foo` and `Bar`:
|
||||||
|
|
||||||
|
|--lib
|
||||||
|
| |
|
||||||
|
| |--Bar
|
||||||
|
| | |--docs
|
||||||
|
| | |--examples
|
||||||
|
| | |--src
|
||||||
|
| | |- Bar.c
|
||||||
|
| | |- Bar.h
|
||||||
|
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|
||||||
|
| |
|
||||||
|
| |--Foo
|
||||||
|
| | |- Foo.c
|
||||||
|
| | |- Foo.h
|
||||||
|
| |
|
||||||
|
| |- README --> THIS FILE
|
||||||
|
|
|
||||||
|
|- platformio.ini
|
||||||
|
|--src
|
||||||
|
|- main.c
|
||||||
|
|
||||||
|
and a contents of `src/main.c`:
|
||||||
|
```
|
||||||
|
#include <Foo.h>
|
||||||
|
#include <Bar.h>
|
||||||
|
|
||||||
|
int main (void)
|
||||||
|
{
|
||||||
|
...
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
PlatformIO Library Dependency Finder will find automatically dependent
|
||||||
|
libraries scanning project source files.
|
||||||
|
|
||||||
|
More information about PlatformIO Library Dependency Finder
|
||||||
|
- https://docs.platformio.org/page/librarymanager/ldf.html
|
||||||
654
MitsubishiAC/old/utility1/WebSockets.cpp
Normal file
654
MitsubishiAC/old/utility1/WebSockets.cpp
Normal file
@@ -0,0 +1,654 @@
|
|||||||
|
/**
|
||||||
|
* @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" {
|
||||||
|
#ifdef CORE_HAS_LIBB64
|
||||||
|
#include <libb64/cencode.h>
|
||||||
|
#else
|
||||||
|
#include "libb64/cencode_inc.h"
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <Hash.h>
|
||||||
|
#elif defined(ESP32)
|
||||||
|
#include <hwcrypto/sha.h>
|
||||||
|
#else
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
#include "libsha1/libsha1.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 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];
|
||||||
|
}
|
||||||
|
|
||||||
|
// create header
|
||||||
|
|
||||||
|
// byte 0
|
||||||
|
*headerPtr = 0x00;
|
||||||
|
if(fin) {
|
||||||
|
*headerPtr |= bit(7); ///< set Fin
|
||||||
|
}
|
||||||
|
*headerPtr |= opcode; ///< set opcode
|
||||||
|
headerPtr++;
|
||||||
|
|
||||||
|
// byte 1
|
||||||
|
*headerPtr = 0x00;
|
||||||
|
if(client->cIsClient) {
|
||||||
|
*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(client->cIsClient) {
|
||||||
|
if(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);
|
||||||
|
*headerPtr = maskKey[x];
|
||||||
|
headerPtr++;
|
||||||
|
}
|
||||||
|
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
*headerPtr = maskKey[0];
|
||||||
|
headerPtr++;
|
||||||
|
*headerPtr = maskKey[1];
|
||||||
|
headerPtr++;
|
||||||
|
*headerPtr = maskKey[2];
|
||||||
|
headerPtr++;
|
||||||
|
*headerPtr = maskKey[3];
|
||||||
|
headerPtr++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#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
|
||||||
|
sendFrame(client, WSop_pong, payload, header->payloadLen);
|
||||||
|
break;
|
||||||
|
case WSop_pong:
|
||||||
|
DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char*)payload : "");
|
||||||
|
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", client->num, reasonCode);
|
||||||
|
if(header->payloadLen > 2) {
|
||||||
|
DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2));
|
||||||
|
} else {
|
||||||
|
DEBUG_WEBSOCKETS("\n");
|
||||||
|
}
|
||||||
|
clientDisconnect(client, 1000);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
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();
|
||||||
|
size_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()) {
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||||
|
delay(0);
|
||||||
|
#endif
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = client->tcp->read((uint8_t*) out, n);
|
||||||
|
if(len) {
|
||||||
|
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 (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||||
|
delay(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
if(cb) {
|
||||||
|
cb(client, true);
|
||||||
|
}
|
||||||
|
#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("write %d failed left %d!\n", len, n);
|
||||||
|
}
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||||
|
delay(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
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));
|
||||||
|
}
|
||||||
312
MitsubishiAC/old/utility1/WebSockets.h
Normal file
312
MitsubishiAC/old/utility1/WebSockets.h
Normal file
@@ -0,0 +1,312 @@
|
|||||||
|
/**
|
||||||
|
* @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
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef NODEBUG_WEBSOCKETS
|
||||||
|
#ifdef DEBUG_ESP_PORT
|
||||||
|
#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ )
|
||||||
|
#else
|
||||||
|
//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ )
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef DEBUG_WEBSOCKETS
|
||||||
|
#define DEBUG_WEBSOCKETS(...)
|
||||||
|
#define NODEBUG_WEBSOCKETS
|
||||||
|
#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
|
||||||
|
|
||||||
|
#elif defined(STM32_DEVICE)
|
||||||
|
|
||||||
|
#define WEBSOCKETS_MAX_DATA_SIZE (15*1024)
|
||||||
|
#define WEBSOCKETS_USE_BIG_MEM
|
||||||
|
#define GET_FREE_HEAP System.freeMemory()
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
//atmega328p has only 2KB ram!
|
||||||
|
#define WEBSOCKETS_MAX_DATA_SIZE (1024)
|
||||||
|
// moves all Header strings to Flash
|
||||||
|
#define WEBSOCKETS_SAVE_RAM
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#define WEBSOCKETS_TCP_TIMEOUT (2000)
|
||||||
|
|
||||||
|
#define NETWORK_ESP8266_ASYNC (0)
|
||||||
|
#define NETWORK_ESP8266 (1)
|
||||||
|
#define NETWORK_W5100 (2)
|
||||||
|
#define NETWORK_ENC28J60 (3)
|
||||||
|
#define NETWORK_ESP32 (4)
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
#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>
|
||||||
|
#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)
|
||||||
|
|
||||||
|
#if !defined(ESP8266) && !defined(ESP31B)
|
||||||
|
#error "network type ESP8266 only possible on the ESP mcu!"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#else
|
||||||
|
#include <ESP31BWiFi.h>
|
||||||
|
#endif
|
||||||
|
#define WEBSOCKETS_NETWORK_CLASS WiFiClient
|
||||||
|
#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
|
||||||
|
#else
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#define WEBSOCKETS_NETWORK_CLASS EthernetClient
|
||||||
|
#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
|
||||||
|
#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 WEBSOCKETS_NETWORK_CLASS WiFiClient
|
||||||
|
#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
|
||||||
|
|
||||||
|
#else
|
||||||
|
#error "no network type selected!"
|
||||||
|
#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_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_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 {
|
||||||
|
uint8_t num; ///< connection number
|
||||||
|
|
||||||
|
WSclientsStatus_t status;
|
||||||
|
|
||||||
|
WEBSOCKETS_NETWORK_CLASS * tcp;
|
||||||
|
|
||||||
|
bool isSocketIO; ///< client for socket.io server
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
bool isSSL; ///< run in ssl mode
|
||||||
|
WiFiClientSecure * ssl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
String cUrl; ///< http url
|
||||||
|
uint16_t cCode; ///< http code
|
||||||
|
|
||||||
|
bool cIsClient = false; ///< will be used for masking
|
||||||
|
bool cIsUpgrade; ///< Connection == Upgrade
|
||||||
|
bool cIsWebsocket; ///< 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; ///< client Sec-WebSocket-Version
|
||||||
|
|
||||||
|
uint8_t cWsRXsize; ///< 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; ///< non-websocket http header validity indicator
|
||||||
|
size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present 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;
|
||||||
|
|
||||||
|
virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0;
|
||||||
|
|
||||||
|
void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#ifndef UNUSED
|
||||||
|
#define UNUSED(var) (void)(var)
|
||||||
|
#endif
|
||||||
|
#endif /* WEBSOCKETS_H_ */
|
||||||
763
MitsubishiAC/old/utility1/WebSocketsClient.cpp
Normal file
763
MitsubishiAC/old/utility1/WebSocketsClient.cpp
Normal file
@@ -0,0 +1,763 @@
|
|||||||
|
/**
|
||||||
|
* @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://");
|
||||||
|
}
|
||||||
|
|
||||||
|
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 (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
_fingerprint = "";
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_client.num = 0;
|
||||||
|
_client.status = WSC_NOT_CONNECTED;
|
||||||
|
_client.tcp = NULL;
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
_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;
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
randomSeed(RANDOM_REG32);
|
||||||
|
#else
|
||||||
|
// todo find better seed
|
||||||
|
randomSeed(millis());
|
||||||
|
#endif
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||||
|
asyncConnect();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_lastConnectionFail = 0;
|
||||||
|
_reconnectInterval = 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) {
|
||||||
|
begin(host.c_str(), port, url.c_str(), protocol.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, const char * protocol) {
|
||||||
|
return begin(host.toString().c_str(), port, url, protocol);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
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 (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
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 = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) {
|
||||||
|
beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||||
|
/**
|
||||||
|
* called in arduino loop
|
||||||
|
*/
|
||||||
|
void WebSocketsClient::loop(void) {
|
||||||
|
if(!clientIsConnected(&_client)) {
|
||||||
|
// do not flood the server
|
||||||
|
if((millis() - _lastConnectionFail) < _reconnectInterval) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
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 WiFiClientSecure();
|
||||||
|
_client.tcp = _client.ssl;
|
||||||
|
} else {
|
||||||
|
DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n");
|
||||||
|
if(_client.tcp) {
|
||||||
|
delete _client.tcp;
|
||||||
|
_client.tcp = NULL;
|
||||||
|
}
|
||||||
|
_client.tcp = new WiFiClient();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
_client.tcp = new WEBSOCKETS_NETWORK_CLASS();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(!_client.tcp) {
|
||||||
|
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_client.tcp->connect(_host.c_str(), _port)) {
|
||||||
|
connectedCb();
|
||||||
|
_lastConnectionFail = 0;
|
||||||
|
} else {
|
||||||
|
connectFailedCb();
|
||||||
|
_lastConnectionFail = millis();
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
handleClientData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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)) {
|
||||||
|
return sendFrame(&_client, WSop_ping, payload, length);
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//#################################################################################
|
||||||
|
//#################################################################################
|
||||||
|
//#################################################################################
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @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_close:
|
||||||
|
case WSop_ping:
|
||||||
|
case WSop_pong:
|
||||||
|
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 (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
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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) {
|
||||||
|
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_CONNECTED:
|
||||||
|
WebSockets::handleWebsocket(&_client);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
WebSockets::clientDisconnect(&_client, 1002);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
delay(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#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) {
|
||||||
|
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));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
|
||||||
|
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(':')) {
|
||||||
|
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(WEBSOCKETS_STRING("HttpOnly")) > -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());
|
||||||
|
|
||||||
|
bool ok = (client->cIsUpgrade && client->cIsWebsocket);
|
||||||
|
|
||||||
|
if(ok) {
|
||||||
|
switch(client->cCode) {
|
||||||
|
case 101: ///< Switching Protocols
|
||||||
|
|
||||||
|
break;
|
||||||
|
case 200:
|
||||||
|
if(client->isSocketIO) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 403: ///< Forbidden
|
||||||
|
// todo handle login
|
||||||
|
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());
|
||||||
|
|
||||||
|
} else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) {
|
||||||
|
sendHeader(client);
|
||||||
|
} 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 (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||||
|
_client.tcp->setNoDelay(true);
|
||||||
|
|
||||||
|
if(_client.isSSL && _fingerprint.length()) {
|
||||||
|
if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) {
|
||||||
|
DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n");
|
||||||
|
WebSockets::clientDisconnect(&_client, 1000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// send Header to Server
|
||||||
|
sendHeader(&_client);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocketsClient::connectFailedCb() {
|
||||||
|
DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Faild\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
|
||||||
136
MitsubishiAC/old/utility1/WebSocketsClient.h
Normal file
136
MitsubishiAC/old/utility1/WebSocketsClient.h
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
/**
|
||||||
|
* @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: private 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");
|
||||||
|
void begin(IPAddress host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
void beginSSL(const char *host, uint16_t port, const char * url = "/", const char * = "", const char * protocol = "arduino");
|
||||||
|
void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String 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 (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
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");
|
||||||
|
#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 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);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
String _host;
|
||||||
|
uint16_t _port;
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
String _fingerprint;
|
||||||
|
#endif
|
||||||
|
WSclient_t _client;
|
||||||
|
|
||||||
|
WebSocketClientEvent _cbEvent;
|
||||||
|
|
||||||
|
unsigned long _lastConnectionFail;
|
||||||
|
unsigned long _reconnectInterval;
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
#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_ */
|
||||||
873
MitsubishiAC/old/utility1/WebSocketsServer.cpp
Normal file
873
MitsubishiAC/old/utility1/WebSocketsServer.cpp
Normal file
@@ -0,0 +1,873 @@
|
|||||||
|
/**
|
||||||
|
* @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"
|
||||||
|
|
||||||
|
WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol) {
|
||||||
|
_port = port;
|
||||||
|
_origin = origin;
|
||||||
|
_protocol = protocol;
|
||||||
|
_runnning = false;
|
||||||
|
|
||||||
|
_server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port);
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||||
|
_server->onClient([](void *s, AsyncClient* c){
|
||||||
|
((WebSocketsServer*)s)->newClient(new AsyncTCPbuffer(c));
|
||||||
|
}, this);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_cbEvent = NULL;
|
||||||
|
|
||||||
|
_httpHeaderValidationFunc = NULL;
|
||||||
|
_mandatoryHttpHeaders = NULL;
|
||||||
|
_mandatoryHttpHeaderCount = 0;
|
||||||
|
|
||||||
|
memset(&_clients[0], 0x00, (sizeof(WSclient_t) * WEBSOCKETS_SERVER_CLIENT_MAX));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WebSocketsServer::~WebSocketsServer() {
|
||||||
|
// disconnect all clients
|
||||||
|
close();
|
||||||
|
|
||||||
|
if (_mandatoryHttpHeaders)
|
||||||
|
delete[] _mandatoryHttpHeaders;
|
||||||
|
|
||||||
|
_mandatoryHttpHeaderCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* called to initialize the Websocket server
|
||||||
|
*/
|
||||||
|
void WebSocketsServer::begin(void) {
|
||||||
|
WSclient_t * client;
|
||||||
|
|
||||||
|
// init client storage
|
||||||
|
for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
|
||||||
|
client = &_clients[i];
|
||||||
|
|
||||||
|
client->num = i;
|
||||||
|
client->status = WSC_NOT_CONNECTED;
|
||||||
|
client->tcp = NULL;
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
client->isSSL = false;
|
||||||
|
client->ssl = NULL;
|
||||||
|
#endif
|
||||||
|
client->cUrl = "";
|
||||||
|
client->cCode = 0;
|
||||||
|
client->cKey = "";
|
||||||
|
client->cProtocol = "";
|
||||||
|
client->cVersion = 0;
|
||||||
|
client->cIsUpgrade = false;
|
||||||
|
client->cIsWebsocket = false;
|
||||||
|
|
||||||
|
client->base64Authorization = "";
|
||||||
|
|
||||||
|
client->cWsRXsize = 0;
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
|
||||||
|
client->cHttpLine = "";
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#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;
|
||||||
|
_server->begin();
|
||||||
|
|
||||||
|
DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebSocketsServer::close(void) {
|
||||||
|
_runnning = false;
|
||||||
|
disconnect();
|
||||||
|
|
||||||
|
#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 WebSocketsServer::loop(void) {
|
||||||
|
if(_runnning) {
|
||||||
|
handleNewClients();
|
||||||
|
handleClientData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set callback function
|
||||||
|
* @param cbEvent WebSocketServerEvent
|
||||||
|
*/
|
||||||
|
void WebSocketsServer::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 WebSocketsServer::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 WebSocketsServer::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 WebSocketsServer::sendTXT(uint8_t num, const uint8_t * payload, size_t length) {
|
||||||
|
return sendTXT(num, (uint8_t *) payload, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) {
|
||||||
|
return sendTXT(num, (uint8_t *) payload, length, headerToPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::sendTXT(uint8_t num, const char * payload, size_t length) {
|
||||||
|
return sendTXT(num, (uint8_t *) payload, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::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 WebSocketsServer::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||||
|
delay(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::broadcastTXT(const uint8_t * payload, size_t length) {
|
||||||
|
return broadcastTXT((uint8_t *) payload, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::broadcastTXT(char * payload, size_t length, bool headerToPayload) {
|
||||||
|
return broadcastTXT((uint8_t *) payload, length, headerToPayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::broadcastTXT(const char * payload, size_t length) {
|
||||||
|
return broadcastTXT((uint8_t *) payload, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::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 WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
|
||||||
|
if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
WSclient_t * client = &_clients[num];
|
||||||
|
if(clientIsConnected(client)) {
|
||||||
|
return sendFrame(client, WSop_binary, payload, length, true, headerToPayload);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::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 WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, 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(!sendFrame(client, WSop_binary, payload, length, true, headerToPayload)) {
|
||||||
|
ret = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||||
|
delay(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::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 WebSocketsServer::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 WebSocketsServer::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 WebSocketsServer::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||||
|
delay(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WebSocketsServer::broadcastPing(String & payload) {
|
||||||
|
return broadcastPing((uint8_t *) payload.c_str(), payload.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* disconnect all clients
|
||||||
|
*/
|
||||||
|
void WebSocketsServer::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 WebSocketsServer::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 WebSocketsServer::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 WebSocketsServer::setAuthorization(const char * auth) {
|
||||||
|
if(auth) {
|
||||||
|
_base64Authorization = auth;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* count the connected clients (optional ping them)
|
||||||
|
* @param ping bool ping the connected clients
|
||||||
|
*/
|
||||||
|
int WebSocketsServer::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 WebSocketsServer::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
|
||||||
|
*/
|
||||||
|
bool WebSocketsServer::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 (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
client->isSSL = false;
|
||||||
|
client->tcp->setNoDelay(true);
|
||||||
|
#endif
|
||||||
|
#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)
|
||||||
|
IPAddress ip = client->tcp->remoteIP();
|
||||||
|
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([](WebSocketsServer * 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(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine)));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param client WSclient_t * ptr to the client struct
|
||||||
|
* @param opcode WSopcode_t
|
||||||
|
* @param payload uint8_t *
|
||||||
|
* @param length size_t
|
||||||
|
*/
|
||||||
|
void WebSocketsServer::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_close:
|
||||||
|
case WSop_ping:
|
||||||
|
case WSop_pong:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
runCbEvent(client->num, type, payload, length);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect an client
|
||||||
|
* @param client WSclient_t * ptr to the client struct
|
||||||
|
*/
|
||||||
|
void WebSocketsServer::clientDisconnect(WSclient_t * client) {
|
||||||
|
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
if(client->tcp) {
|
||||||
|
if(client->tcp->connected()) {
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 WebSocketsServer::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
|
||||||
|
*/
|
||||||
|
void WebSocketsServer::handleNewClients(void) {
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
while(_server->hasClient()) {
|
||||||
|
#endif
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
// store new connection
|
||||||
|
WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available());
|
||||||
|
#else
|
||||||
|
WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available());
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if(!tcpClient) {
|
||||||
|
DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ok = newClient(tcpClient);
|
||||||
|
|
||||||
|
if(!ok) {
|
||||||
|
// no free space to handle client
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
IPAddress ip = tcpClient->remoteIP();
|
||||||
|
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
|
||||||
|
tcpClient->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
delay(0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handel incomming data from Client
|
||||||
|
*/
|
||||||
|
void WebSocketsServer::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:
|
||||||
|
WebSockets::clientDisconnect(client, 1002);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
|
||||||
|
delay(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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 WebSocketsServer::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 WebSocketsServer::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(':')) {
|
||||||
|
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(&WebSocketsServer::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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
212
MitsubishiAC/old/utility1/WebSocketsServer.h
Normal file
212
MitsubishiAC/old/utility1/WebSocketsServer.h
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
/**
|
||||||
|
* @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 WebSocketsServer: protected WebSockets {
|
||||||
|
public:
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
WebSocketsServer(uint16_t port, String origin = "", String protocol = "arduino");
|
||||||
|
virtual ~WebSocketsServer(void);
|
||||||
|
|
||||||
|
void begin(void);
|
||||||
|
void close(void);
|
||||||
|
|
||||||
|
#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(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 headerToPayload = false);
|
||||||
|
bool sendBIN(uint8_t num, const uint8_t * payload, size_t length);
|
||||||
|
|
||||||
|
bool broadcastBIN(uint8_t * payload, size_t length, 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);
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
|
||||||
|
IPAddress remoteIP(uint8_t num);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
protected:
|
||||||
|
uint16_t _port;
|
||||||
|
String _origin;
|
||||||
|
String _protocol;
|
||||||
|
String _base64Authorization; ///< Base64 encoded Auth request
|
||||||
|
String * _mandatoryHttpHeaders;
|
||||||
|
size_t _mandatoryHttpHeaderCount;
|
||||||
|
|
||||||
|
WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
|
||||||
|
|
||||||
|
WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
|
||||||
|
|
||||||
|
WebSocketServerEvent _cbEvent;
|
||||||
|
WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc;
|
||||||
|
|
||||||
|
bool _runnning;
|
||||||
|
|
||||||
|
bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
|
||||||
|
|
||||||
|
void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
|
||||||
|
|
||||||
|
void clientDisconnect(WSclient_t * client);
|
||||||
|
bool clientIsConnected(WSclient_t * client);
|
||||||
|
|
||||||
|
#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
|
||||||
|
void handleNewClients(void);
|
||||||
|
void handleClientData(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void handleHeader(WSclient_t * client, String * headerLine);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* WEBSOCKETSSERVER_H_ */
|
||||||
7
MitsubishiAC/old/utility1/libb64/AUTHORS
Normal file
7
MitsubishiAC/old/utility1/libb64/AUTHORS
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
libb64: Base64 Encoding/Decoding Routines
|
||||||
|
======================================
|
||||||
|
|
||||||
|
Authors:
|
||||||
|
-------
|
||||||
|
|
||||||
|
Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com
|
||||||
29
MitsubishiAC/old/utility1/libb64/LICENSE
Normal file
29
MitsubishiAC/old/utility1/libb64/LICENSE
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
Copyright-Only Dedication (based on United States law)
|
||||||
|
or Public Domain Certification
|
||||||
|
|
||||||
|
The person or persons who have associated work with this document (the
|
||||||
|
"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
|
||||||
|
his knowledge, the work of authorship identified is in the public domain of the
|
||||||
|
country from which the work is published, or (b) hereby dedicates whatever
|
||||||
|
copyright the dedicators holds in the work of authorship identified below (the
|
||||||
|
"Work") to the public domain. A certifier, moreover, dedicates any copyright
|
||||||
|
interest he may have in the associated work, and for these purposes, is
|
||||||
|
described as a "dedicator" below.
|
||||||
|
|
||||||
|
A certifier has taken reasonable steps to verify the copyright status of this
|
||||||
|
work. Certifier recognizes that his good faith efforts may not shield him from
|
||||||
|
liability if in fact the work certified is not in the public domain.
|
||||||
|
|
||||||
|
Dedicator makes this dedication for the benefit of the public at large and to
|
||||||
|
the detriment of the Dedicator's heirs and successors. Dedicator intends this
|
||||||
|
dedication to be an overt act of relinquishment in perpetuity of all present
|
||||||
|
and future rights under copyright law, whether vested or contingent, in the
|
||||||
|
Work. Dedicator understands that such relinquishment of all rights includes
|
||||||
|
the relinquishment of all rights to enforce (by lawsuit or otherwise) those
|
||||||
|
copyrights in the Work.
|
||||||
|
|
||||||
|
Dedicator recognizes that, once placed in the public domain, the Work may be
|
||||||
|
freely reproduced, distributed, transmitted, used, modified, built upon, or
|
||||||
|
otherwise exploited by anyone for any purpose, commercial or non-commercial,
|
||||||
|
and in any way, including by methods that have not yet been invented or
|
||||||
|
conceived.
|
||||||
98
MitsubishiAC/old/utility1/libb64/cdecode.c
Normal file
98
MitsubishiAC/old/utility1/libb64/cdecode.c
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
cdecoder.c - c source to a base64 decoding algorithm implementation
|
||||||
|
|
||||||
|
This is part of the libb64 project, and has been placed in the public domain.
|
||||||
|
For details, see http://sourceforge.net/projects/libb64
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <core_esp8266_features.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ESP32)
|
||||||
|
#define CORE_HAS_LIBB64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CORE_HAS_LIBB64
|
||||||
|
#include "cdecode_inc.h"
|
||||||
|
|
||||||
|
int base64_decode_value(char value_in)
|
||||||
|
{
|
||||||
|
static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
|
||||||
|
static const char decoding_size = sizeof(decoding);
|
||||||
|
value_in -= 43;
|
||||||
|
if (value_in < 0 || value_in > decoding_size) return -1;
|
||||||
|
return decoding[(int)value_in];
|
||||||
|
}
|
||||||
|
|
||||||
|
void base64_init_decodestate(base64_decodestate* state_in)
|
||||||
|
{
|
||||||
|
state_in->step = step_a;
|
||||||
|
state_in->plainchar = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
|
||||||
|
{
|
||||||
|
const char* codechar = code_in;
|
||||||
|
char* plainchar = plaintext_out;
|
||||||
|
char fragment;
|
||||||
|
|
||||||
|
*plainchar = state_in->plainchar;
|
||||||
|
|
||||||
|
switch (state_in->step)
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
case step_a:
|
||||||
|
do {
|
||||||
|
if (codechar == code_in+length_in)
|
||||||
|
{
|
||||||
|
state_in->step = step_a;
|
||||||
|
state_in->plainchar = *plainchar;
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
fragment = (char)base64_decode_value(*codechar++);
|
||||||
|
} while (fragment < 0);
|
||||||
|
*plainchar = (fragment & 0x03f) << 2;
|
||||||
|
case step_b:
|
||||||
|
do {
|
||||||
|
if (codechar == code_in+length_in)
|
||||||
|
{
|
||||||
|
state_in->step = step_b;
|
||||||
|
state_in->plainchar = *plainchar;
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
fragment = (char)base64_decode_value(*codechar++);
|
||||||
|
} while (fragment < 0);
|
||||||
|
*plainchar++ |= (fragment & 0x030) >> 4;
|
||||||
|
*plainchar = (fragment & 0x00f) << 4;
|
||||||
|
case step_c:
|
||||||
|
do {
|
||||||
|
if (codechar == code_in+length_in)
|
||||||
|
{
|
||||||
|
state_in->step = step_c;
|
||||||
|
state_in->plainchar = *plainchar;
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
fragment = (char)base64_decode_value(*codechar++);
|
||||||
|
} while (fragment < 0);
|
||||||
|
*plainchar++ |= (fragment & 0x03c) >> 2;
|
||||||
|
*plainchar = (fragment & 0x003) << 6;
|
||||||
|
case step_d:
|
||||||
|
do {
|
||||||
|
if (codechar == code_in+length_in)
|
||||||
|
{
|
||||||
|
state_in->step = step_d;
|
||||||
|
state_in->plainchar = *plainchar;
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
fragment = (char)base64_decode_value(*codechar++);
|
||||||
|
} while (fragment < 0);
|
||||||
|
*plainchar++ |= (fragment & 0x03f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* control should not reach here */
|
||||||
|
return plainchar - plaintext_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
28
MitsubishiAC/old/utility1/libb64/cdecode_inc.h
Normal file
28
MitsubishiAC/old/utility1/libb64/cdecode_inc.h
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
cdecode.h - c header for a base64 decoding algorithm
|
||||||
|
|
||||||
|
This is part of the libb64 project, and has been placed in the public domain.
|
||||||
|
For details, see http://sourceforge.net/projects/libb64
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BASE64_CDECODE_H
|
||||||
|
#define BASE64_CDECODE_H
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
step_a, step_b, step_c, step_d
|
||||||
|
} base64_decodestep;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
base64_decodestep step;
|
||||||
|
char plainchar;
|
||||||
|
} base64_decodestate;
|
||||||
|
|
||||||
|
void base64_init_decodestate(base64_decodestate* state_in);
|
||||||
|
|
||||||
|
int base64_decode_value(char value_in);
|
||||||
|
|
||||||
|
int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
|
||||||
|
|
||||||
|
#endif /* BASE64_CDECODE_H */
|
||||||
119
MitsubishiAC/old/utility1/libb64/cencode.c
Normal file
119
MitsubishiAC/old/utility1/libb64/cencode.c
Normal file
@@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
cencoder.c - c source to a base64 encoding algorithm implementation
|
||||||
|
|
||||||
|
This is part of the libb64 project, and has been placed in the public domain.
|
||||||
|
For details, see http://sourceforge.net/projects/libb64
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <core_esp8266_features.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(ESP32)
|
||||||
|
#define CORE_HAS_LIBB64
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef CORE_HAS_LIBB64
|
||||||
|
#include "cencode_inc.h"
|
||||||
|
|
||||||
|
const int CHARS_PER_LINE = 72;
|
||||||
|
|
||||||
|
void base64_init_encodestate(base64_encodestate* state_in)
|
||||||
|
{
|
||||||
|
state_in->step = step_A;
|
||||||
|
state_in->result = 0;
|
||||||
|
state_in->stepcount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
char base64_encode_value(char value_in)
|
||||||
|
{
|
||||||
|
static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||||
|
if (value_in > 63) return '=';
|
||||||
|
return encoding[(int)value_in];
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
|
||||||
|
{
|
||||||
|
const char* plainchar = plaintext_in;
|
||||||
|
const char* const plaintextend = plaintext_in + length_in;
|
||||||
|
char* codechar = code_out;
|
||||||
|
char result;
|
||||||
|
char fragment;
|
||||||
|
|
||||||
|
result = state_in->result;
|
||||||
|
|
||||||
|
switch (state_in->step)
|
||||||
|
{
|
||||||
|
while (1)
|
||||||
|
{
|
||||||
|
case step_A:
|
||||||
|
if (plainchar == plaintextend)
|
||||||
|
{
|
||||||
|
state_in->result = result;
|
||||||
|
state_in->step = step_A;
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
fragment = *plainchar++;
|
||||||
|
result = (fragment & 0x0fc) >> 2;
|
||||||
|
*codechar++ = base64_encode_value(result);
|
||||||
|
result = (fragment & 0x003) << 4;
|
||||||
|
case step_B:
|
||||||
|
if (plainchar == plaintextend)
|
||||||
|
{
|
||||||
|
state_in->result = result;
|
||||||
|
state_in->step = step_B;
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
fragment = *plainchar++;
|
||||||
|
result |= (fragment & 0x0f0) >> 4;
|
||||||
|
*codechar++ = base64_encode_value(result);
|
||||||
|
result = (fragment & 0x00f) << 2;
|
||||||
|
case step_C:
|
||||||
|
if (plainchar == plaintextend)
|
||||||
|
{
|
||||||
|
state_in->result = result;
|
||||||
|
state_in->step = step_C;
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
fragment = *plainchar++;
|
||||||
|
result |= (fragment & 0x0c0) >> 6;
|
||||||
|
*codechar++ = base64_encode_value(result);
|
||||||
|
result = (fragment & 0x03f) >> 0;
|
||||||
|
*codechar++ = base64_encode_value(result);
|
||||||
|
|
||||||
|
++(state_in->stepcount);
|
||||||
|
if (state_in->stepcount == CHARS_PER_LINE/4)
|
||||||
|
{
|
||||||
|
*codechar++ = '\n';
|
||||||
|
state_in->stepcount = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* control should not reach here */
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
|
||||||
|
{
|
||||||
|
char* codechar = code_out;
|
||||||
|
|
||||||
|
switch (state_in->step)
|
||||||
|
{
|
||||||
|
case step_B:
|
||||||
|
*codechar++ = base64_encode_value(state_in->result);
|
||||||
|
*codechar++ = '=';
|
||||||
|
*codechar++ = '=';
|
||||||
|
break;
|
||||||
|
case step_C:
|
||||||
|
*codechar++ = base64_encode_value(state_in->result);
|
||||||
|
*codechar++ = '=';
|
||||||
|
break;
|
||||||
|
case step_A:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*codechar++ = 0x00;
|
||||||
|
|
||||||
|
return codechar - code_out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
31
MitsubishiAC/old/utility1/libb64/cencode_inc.h
Normal file
31
MitsubishiAC/old/utility1/libb64/cencode_inc.h
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
cencode.h - c header for a base64 encoding algorithm
|
||||||
|
|
||||||
|
This is part of the libb64 project, and has been placed in the public domain.
|
||||||
|
For details, see http://sourceforge.net/projects/libb64
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef BASE64_CENCODE_H
|
||||||
|
#define BASE64_CENCODE_H
|
||||||
|
|
||||||
|
typedef enum
|
||||||
|
{
|
||||||
|
step_A, step_B, step_C
|
||||||
|
} base64_encodestep;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
base64_encodestep step;
|
||||||
|
char result;
|
||||||
|
int stepcount;
|
||||||
|
} base64_encodestate;
|
||||||
|
|
||||||
|
void base64_init_encodestate(base64_encodestate* state_in);
|
||||||
|
|
||||||
|
char base64_encode_value(char value_in);
|
||||||
|
|
||||||
|
int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
|
||||||
|
|
||||||
|
int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
|
||||||
|
|
||||||
|
#endif /* BASE64_CENCODE_H */
|
||||||
202
MitsubishiAC/old/utility1/libsha1/libsha1.c
Normal file
202
MitsubishiAC/old/utility1/libsha1/libsha1.c
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
/* from valgrind tests */
|
||||||
|
|
||||||
|
/* ================ sha1.c ================ */
|
||||||
|
/*
|
||||||
|
SHA-1 in C
|
||||||
|
By Steve Reid <steve@edmweb.com>
|
||||||
|
100% Public Domain
|
||||||
|
|
||||||
|
Test Vectors (from FIPS PUB 180-1)
|
||||||
|
"abc"
|
||||||
|
A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
|
||||||
|
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
|
||||||
|
84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
|
||||||
|
A million repetitions of "a"
|
||||||
|
34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
|
||||||
|
/* #define SHA1HANDSOFF * Copies data before messing with it. */
|
||||||
|
|
||||||
|
#if !defined(ESP8266) && !defined(ESP32)
|
||||||
|
|
||||||
|
#define SHA1HANDSOFF
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include "libsha1.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
|
||||||
|
|
||||||
|
/* blk0() and blk() perform the initial expand. */
|
||||||
|
/* I got the idea of expanding during the round function from SSLeay */
|
||||||
|
#if BYTE_ORDER == LITTLE_ENDIAN
|
||||||
|
#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
|
||||||
|
|(rol(block->l[i],8)&0x00FF00FF))
|
||||||
|
#elif BYTE_ORDER == BIG_ENDIAN
|
||||||
|
#define blk0(i) block->l[i]
|
||||||
|
#else
|
||||||
|
#error "Endianness not defined!"
|
||||||
|
#endif
|
||||||
|
#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
|
||||||
|
^block->l[(i+2)&15]^block->l[i&15],1))
|
||||||
|
|
||||||
|
/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
|
||||||
|
#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||||
|
#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
|
||||||
|
#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
|
||||||
|
#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
|
||||||
|
#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
|
||||||
|
|
||||||
|
|
||||||
|
/* Hash a single 512-bit block. This is the core of the algorithm. */
|
||||||
|
|
||||||
|
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
|
||||||
|
{
|
||||||
|
uint32_t a, b, c, d, e;
|
||||||
|
typedef union {
|
||||||
|
unsigned char c[64];
|
||||||
|
uint32_t l[16];
|
||||||
|
} CHAR64LONG16;
|
||||||
|
#ifdef SHA1HANDSOFF
|
||||||
|
CHAR64LONG16 block[1]; /* use array to appear as a pointer */
|
||||||
|
memcpy(block, buffer, 64);
|
||||||
|
#else
|
||||||
|
/* The following had better never be used because it causes the
|
||||||
|
* pointer-to-const buffer to be cast into a pointer to non-const.
|
||||||
|
* And the result is written through. I threw a "const" in, hoping
|
||||||
|
* this will cause a diagnostic.
|
||||||
|
*/
|
||||||
|
CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
|
||||||
|
#endif
|
||||||
|
/* Copy context->state[] to working vars */
|
||||||
|
a = state[0];
|
||||||
|
b = state[1];
|
||||||
|
c = state[2];
|
||||||
|
d = state[3];
|
||||||
|
e = state[4];
|
||||||
|
/* 4 rounds of 20 operations each. Loop unrolled. */
|
||||||
|
R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
|
||||||
|
R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
|
||||||
|
R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
|
||||||
|
R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
|
||||||
|
R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
|
||||||
|
R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
|
||||||
|
R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
|
||||||
|
R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
|
||||||
|
R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
|
||||||
|
R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
|
||||||
|
R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
|
||||||
|
R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
|
||||||
|
R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
|
||||||
|
R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
|
||||||
|
R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
|
||||||
|
R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
|
||||||
|
R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
|
||||||
|
R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
|
||||||
|
R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
|
||||||
|
R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
|
||||||
|
/* Add the working vars back into context.state[] */
|
||||||
|
state[0] += a;
|
||||||
|
state[1] += b;
|
||||||
|
state[2] += c;
|
||||||
|
state[3] += d;
|
||||||
|
state[4] += e;
|
||||||
|
/* Wipe variables */
|
||||||
|
a = b = c = d = e = 0;
|
||||||
|
#ifdef SHA1HANDSOFF
|
||||||
|
memset(block, '\0', sizeof(block));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* SHA1Init - Initialize new context */
|
||||||
|
|
||||||
|
void SHA1Init(SHA1_CTX* context)
|
||||||
|
{
|
||||||
|
/* SHA1 initialization constants */
|
||||||
|
context->state[0] = 0x67452301;
|
||||||
|
context->state[1] = 0xEFCDAB89;
|
||||||
|
context->state[2] = 0x98BADCFE;
|
||||||
|
context->state[3] = 0x10325476;
|
||||||
|
context->state[4] = 0xC3D2E1F0;
|
||||||
|
context->count[0] = context->count[1] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Run your data through this. */
|
||||||
|
|
||||||
|
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
|
||||||
|
{
|
||||||
|
uint32_t i, j;
|
||||||
|
|
||||||
|
j = context->count[0];
|
||||||
|
if ((context->count[0] += len << 3) < j)
|
||||||
|
context->count[1]++;
|
||||||
|
context->count[1] += (len>>29);
|
||||||
|
j = (j >> 3) & 63;
|
||||||
|
if ((j + len) > 63) {
|
||||||
|
memcpy(&context->buffer[j], data, (i = 64-j));
|
||||||
|
SHA1Transform(context->state, context->buffer);
|
||||||
|
for ( ; i + 63 < len; i += 64) {
|
||||||
|
SHA1Transform(context->state, &data[i]);
|
||||||
|
}
|
||||||
|
j = 0;
|
||||||
|
}
|
||||||
|
else i = 0;
|
||||||
|
memcpy(&context->buffer[j], &data[i], len - i);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Add padding and return the message digest. */
|
||||||
|
|
||||||
|
void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
unsigned char finalcount[8];
|
||||||
|
unsigned char c;
|
||||||
|
|
||||||
|
#if 0 /* untested "improvement" by DHR */
|
||||||
|
/* Convert context->count to a sequence of bytes
|
||||||
|
* in finalcount. Second element first, but
|
||||||
|
* big-endian order within element.
|
||||||
|
* But we do it all backwards.
|
||||||
|
*/
|
||||||
|
unsigned char *fcp = &finalcount[8];
|
||||||
|
|
||||||
|
for (i = 0; i < 2; i++)
|
||||||
|
{
|
||||||
|
uint32_t t = context->count[i];
|
||||||
|
int j;
|
||||||
|
|
||||||
|
for (j = 0; j < 4; t >>= 8, j++)
|
||||||
|
*--fcp = (unsigned char) t;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
for (i = 0; i < 8; i++) {
|
||||||
|
finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
|
||||||
|
>> ((3-(i & 3)) * 8) ) & 255); /* Endian independent */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
c = 0200;
|
||||||
|
SHA1Update(context, &c, 1);
|
||||||
|
while ((context->count[0] & 504) != 448) {
|
||||||
|
c = 0000;
|
||||||
|
SHA1Update(context, &c, 1);
|
||||||
|
}
|
||||||
|
SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() */
|
||||||
|
for (i = 0; i < 20; i++) {
|
||||||
|
digest[i] = (unsigned char)
|
||||||
|
((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
|
||||||
|
}
|
||||||
|
/* Wipe variables */
|
||||||
|
memset(context, '\0', sizeof(*context));
|
||||||
|
memset(&finalcount, '\0', sizeof(finalcount));
|
||||||
|
}
|
||||||
|
/* ================ end of sha1.c ================ */
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
||||||
21
MitsubishiAC/old/utility1/libsha1/libsha1.h
Normal file
21
MitsubishiAC/old/utility1/libsha1/libsha1.h
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
/* ================ sha1.h ================ */
|
||||||
|
/*
|
||||||
|
SHA-1 in C
|
||||||
|
By Steve Reid <steve@edmweb.com>
|
||||||
|
100% Public Domain
|
||||||
|
*/
|
||||||
|
|
||||||
|
#if !defined(ESP8266) && !defined(ESP32)
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t state[5];
|
||||||
|
uint32_t count[2];
|
||||||
|
unsigned char buffer[64];
|
||||||
|
} SHA1_CTX;
|
||||||
|
|
||||||
|
void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
|
||||||
|
void SHA1Init(SHA1_CTX* context);
|
||||||
|
void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
|
||||||
|
void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
|
||||||
|
|
||||||
|
#endif
|
||||||
41
MitsubishiAC/platformio.ini
Normal file
41
MitsubishiAC/platformio.ini
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
; PlatformIO Project Configuration File
|
||||||
|
;
|
||||||
|
; Build options: build flags, source filter
|
||||||
|
; Upload options: custom upload port, speed and extra flags
|
||||||
|
; Library options: dependencies, extra library storages
|
||||||
|
; Advanced options: extra scripting
|
||||||
|
;
|
||||||
|
; Please visit documentation for the other options and examples
|
||||||
|
; https://docs.platformio.org/page/projectconf.html
|
||||||
|
[platformio]
|
||||||
|
;default_envs = nodemcuv2
|
||||||
|
default_envs = esp07
|
||||||
|
|
||||||
|
[env:nodemcuv2]
|
||||||
|
platform = espressif8266
|
||||||
|
board = nodemcuv2
|
||||||
|
framework = arduino
|
||||||
|
;board_build.f_cpu = 26000000L
|
||||||
|
;board_build.ldscript = eagle.flash.1m.ld
|
||||||
|
upload_protocol = espota
|
||||||
|
upload_port = 192.168.1.95
|
||||||
|
lib_deps =
|
||||||
|
;grubykarol/RemoteDebugFork997 @ ^3.0.7
|
||||||
|
;links2004/WebSockets @ ^2.3.6
|
||||||
|
swicago/HeatPump @ ^1.0.0
|
||||||
|
bblanchon/ArduinoJson @ ^6.18.0
|
||||||
|
|
||||||
|
[env:esp07]
|
||||||
|
platform = espressif8266
|
||||||
|
board = esp07
|
||||||
|
framework = arduino
|
||||||
|
board_build.f_cpu = 26000000L
|
||||||
|
board_build.ldscript = eagle.flash.1m.ld
|
||||||
|
upload_protocol = espota
|
||||||
|
upload_port = 192.168.1.155
|
||||||
|
lib_deps =
|
||||||
|
# RECOMMENDED
|
||||||
|
# Accept new functionality in a backwards compatible manner and patches
|
||||||
|
;grubykarol/RemoteDebugFork997 @ ^3.0.7
|
||||||
|
swicago/HeatPump @ ^1.0.0
|
||||||
|
; bblanchon/ArduinoJson @ ^6.18.0
|
||||||
448
MitsubishiAC/src/main.cpp
Normal file
448
MitsubishiAC/src/main.cpp
Normal file
@@ -0,0 +1,448 @@
|
|||||||
|
#include <Arduino.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
#include <Ticker.h>
|
||||||
|
#include <AsyncMqttClient.h>
|
||||||
|
//#include <ArduinoJson.h>
|
||||||
|
#include <HeatPump.h>
|
||||||
|
|
||||||
|
#define HOST_NAME "MitsubishiAC"
|
||||||
|
|
||||||
|
#define OTA_USE
|
||||||
|
//#define RDEB
|
||||||
|
//#define SDEB
|
||||||
|
|
||||||
|
#ifdef RDEB
|
||||||
|
#include <RemoteDebug.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef OTA_USE
|
||||||
|
#include <ArduinoOTA.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define MAIN_TOPIC "home/bigroom/ac/"
|
||||||
|
#define SET_TOPIC "/set"
|
||||||
|
#define DEBUG_TOPIC "debug"
|
||||||
|
#define TIMERS_TOPIC "/timers"
|
||||||
|
#define STATUS_TOPIC "/status"
|
||||||
|
|
||||||
|
const char* ssid = "wf-home";
|
||||||
|
const char* password = "0ndthnrf";
|
||||||
|
const char* mqtt_server = "192.168.1.111";
|
||||||
|
unsigned long cRun = millis();
|
||||||
|
bool _debugMode;
|
||||||
|
|
||||||
|
HeatPump hp;
|
||||||
|
|
||||||
|
AsyncMqttClient mqttClient;
|
||||||
|
Ticker mqttReconnectTimer;
|
||||||
|
|
||||||
|
WiFiEventHandler wifiConnectHandler;
|
||||||
|
WiFiEventHandler wifiDisconnectHandler;
|
||||||
|
Ticker wifiReconnectTimer;
|
||||||
|
|
||||||
|
#ifdef RDEB
|
||||||
|
RemoteDebug Debug;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void connectToWifi();
|
||||||
|
void connectToMqtt();
|
||||||
|
void onWifiConnect(const WiFiEventStationModeGotIP& event);
|
||||||
|
void onWifiDisconnect(const WiFiEventStationModeDisconnected& event);
|
||||||
|
void onMqttConnect(bool sessionPresent);
|
||||||
|
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason);
|
||||||
|
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total);
|
||||||
|
void hpSettingsChanged();
|
||||||
|
void hpStatusChanged(heatpumpStatus currentStatus);
|
||||||
|
void hpPacketDebug(byte* packet, unsigned int length, char* packetDirection);
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.begin(9600);
|
||||||
|
#endif
|
||||||
|
pinMode(LED_BUILTIN, OUTPUT);
|
||||||
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
|
WiFi.mode(WIFI_STA);
|
||||||
|
WiFi.hostname(HOST_NAME);
|
||||||
|
// ArduinoOTA.onStart([]() {
|
||||||
|
// Serial1.println("Start Update"); // "Начало OTA-апдейта"
|
||||||
|
// });
|
||||||
|
// ArduinoOTA.onEnd([]() {
|
||||||
|
// Serial1.println("\nEnd Update"); // "Завершение OTA-апдейта"
|
||||||
|
// });
|
||||||
|
// ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
|
||||||
|
// //Serial1.printf("Progress: %u%%\r", (progress / (total / 100)));
|
||||||
|
// });
|
||||||
|
// ArduinoOTA.onError([](ota_error_t error) {
|
||||||
|
// Serial1.printf("Error[%u]: ", error);
|
||||||
|
// if (error == OTA_AUTH_ERROR) Serial1.println("Auth Failed");
|
||||||
|
// // "Ошибка при аутентификации"
|
||||||
|
// else if (error == OTA_BEGIN_ERROR) Serial1.println("Begin Failed");
|
||||||
|
// // "Ошибка при начале OTA-апдейта"
|
||||||
|
// else if (error == OTA_CONNECT_ERROR) Serial1.println("Connect Failed");
|
||||||
|
// // "Ошибка при подключении"
|
||||||
|
// else if (error == OTA_RECEIVE_ERROR) Serial1.println("Receive Failed");
|
||||||
|
// // "Ошибка при получении данных"
|
||||||
|
// else if (error == OTA_END_ERROR) Serial1.println("End Failed");
|
||||||
|
// // "Ошибка при завершении OTA-апдейта"
|
||||||
|
// });
|
||||||
|
wifiConnectHandler = WiFi.onStationModeGotIP(onWifiConnect);
|
||||||
|
wifiDisconnectHandler = WiFi.onStationModeDisconnected(onWifiDisconnect);
|
||||||
|
|
||||||
|
mqttClient.onConnect(onMqttConnect);
|
||||||
|
mqttClient.onDisconnect(onMqttDisconnect);
|
||||||
|
mqttClient.onMessage(onMqttMessage);
|
||||||
|
mqttClient.setServer(mqtt_server, 1883);
|
||||||
|
mqttClient.setClientId(HOST_NAME);
|
||||||
|
|
||||||
|
#ifdef OTA_USE
|
||||||
|
ArduinoOTA.begin();
|
||||||
|
#endif
|
||||||
|
#ifdef RDEB
|
||||||
|
Debug.begin(HOST_NAME); // Initialize the WiFi server
|
||||||
|
Debug.setResetCmdEnabled(true); // Enable the reset command
|
||||||
|
|
||||||
|
Debug.showProfiler(true); // Profiler (Good to measure times, to optimize codes)
|
||||||
|
Debug.showColors(true); // Colors
|
||||||
|
#endif
|
||||||
|
hp.setSettingsChangedCallback(hpSettingsChanged);
|
||||||
|
hp.setStatusChangedCallback(hpStatusChanged);
|
||||||
|
hp.setPacketCallback(hpPacketDebug);
|
||||||
|
#ifndef SDEB
|
||||||
|
hp.connect(&Serial);
|
||||||
|
hp.enableAutoUpdate();
|
||||||
|
hp.enableExternalUpdate();
|
||||||
|
#else
|
||||||
|
Serial.println(F("Connect WIFI"));
|
||||||
|
#endif
|
||||||
|
connectToWifi();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(){
|
||||||
|
#ifndef SDEB
|
||||||
|
hp.sync();
|
||||||
|
#endif
|
||||||
|
#ifdef OTA_USE
|
||||||
|
if(WiFi.isConnected())
|
||||||
|
ArduinoOTA.handle();
|
||||||
|
#endif
|
||||||
|
if((cRun + 4999) < millis()){
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.println(millis());
|
||||||
|
#else
|
||||||
|
hpStatusChanged(hp.getStatus());
|
||||||
|
#endif
|
||||||
|
cRun = millis();
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("$ul Loop Topic %s", cRun, MAIN_TOPIC TIMERS_TOPIC);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
#ifdef RDEB
|
||||||
|
Debug.handle();
|
||||||
|
#endif
|
||||||
|
//yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
void connectToWifi() {
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.println(F("Connecting to Wi-Fi..."));
|
||||||
|
#endif
|
||||||
|
WiFi.begin(ssid, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
void connectToMqtt() {
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.println(F("Connecting to MQTT..."));
|
||||||
|
#endif
|
||||||
|
mqttClient.connect();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onWifiConnect(const WiFiEventStationModeGotIP& event) {
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.println(F("Connected to Wi-Fi."));
|
||||||
|
Serial.print(F("IP: "));
|
||||||
|
Serial.println(WiFi.localIP());
|
||||||
|
#endif
|
||||||
|
digitalWrite(LED_BUILTIN, LOW);
|
||||||
|
connectToMqtt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void onWifiDisconnect(const WiFiEventStationModeDisconnected& event) {
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.println(F("Disconnected from Wi-Fi."));
|
||||||
|
#endif
|
||||||
|
mqttReconnectTimer.detach(); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi
|
||||||
|
wifiReconnectTimer.once(2, connectToWifi);
|
||||||
|
digitalWrite(LED_BUILTIN, HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMqttConnect(bool sessionPresent) {
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.println(F("Connected to MQTT..."));
|
||||||
|
#endif
|
||||||
|
// mqttClient.publish(MAIN_TOPIC "power_set", 1, false, "");
|
||||||
|
// mqttClient.publish(MAIN_TOPIC "mode_set", 1, false, "");
|
||||||
|
// mqttClient.publish(MAIN_TOPIC "temp_set", 1, false, "");
|
||||||
|
// mqttClient.publish(MAIN_TOPIC "fan_set", 1, false, "");
|
||||||
|
// mqttClient.publish(MAIN_TOPIC "vane_set", 1, false, "");
|
||||||
|
// mqttClient.publish(MAIN_TOPIC "widevane_set", 1, false, "");
|
||||||
|
//mqttClient.publish(MAIN_TOPIC DEBUG_TOPIC, 1, false, "");
|
||||||
|
mqttClient.subscribe(MAIN_TOPIC "power_set", 1);
|
||||||
|
mqttClient.subscribe(MAIN_TOPIC "mode_set", 1);
|
||||||
|
mqttClient.subscribe(MAIN_TOPIC "temp_set", 1);
|
||||||
|
mqttClient.subscribe(MAIN_TOPIC "fan_set", 1);
|
||||||
|
mqttClient.subscribe(MAIN_TOPIC "vane_set", 1);
|
||||||
|
mqttClient.subscribe(MAIN_TOPIC "widevane_set", 1);
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("MQTT Connect");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) {
|
||||||
|
if (WiFi.isConnected()) {
|
||||||
|
mqttReconnectTimer.once(2, connectToMqtt);
|
||||||
|
}
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("MQTT DisConnect");
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void hpSettingsChanged() {
|
||||||
|
// const size_t bufferSize = JSON_OBJECT_SIZE(6);
|
||||||
|
// DynamicJsonDocument root(bufferSize);
|
||||||
|
|
||||||
|
heatpumpSettings currentSettings = hp.getSettings();
|
||||||
|
|
||||||
|
// root["power"] = currentSettings.power;
|
||||||
|
// root["mode"] = currentSettings.mode;
|
||||||
|
// root["temperature"] = currentSettings.temperature;
|
||||||
|
// root["fan"] = currentSettings.fan;
|
||||||
|
// root["vane"] = currentSettings.vane;
|
||||||
|
// root["wideVane"] = currentSettings.wideVane;
|
||||||
|
mqttClient.publish(MAIN_TOPIC "power", 1, false, currentSettings.power);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "mode", 1, false, currentSettings.mode);
|
||||||
|
char t[5];
|
||||||
|
dtostrf(currentSettings.temperature, 4, 1, t);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "temp", 1, false, t);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "fan", 1, false, currentSettings.fan);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "vane", 1, false, currentSettings.vane);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "wideVane", 1, false, currentSettings.wideVane);
|
||||||
|
//root["iSee"] = currentSettings.iSee;
|
||||||
|
|
||||||
|
// char buffer[512];
|
||||||
|
// serializeJson(root, buffer);
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("SettingsChanged %s", buffer);
|
||||||
|
#endif
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.print(F("SettChgd: "));
|
||||||
|
// Serial.println(buffer);
|
||||||
|
#endif
|
||||||
|
//bool retain = true;
|
||||||
|
//mqttClient.publish(MAIN_TOPIC, 1, false, buffer);
|
||||||
|
// if (!mqtt_client.publish(heatpump_topic, buffer, retain)) {
|
||||||
|
// mqtt_client.publish(heatpump_debug_topic, "failed to publish to heatpump topic");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void hpStatusChanged(heatpumpStatus currentStatus) {
|
||||||
|
// send room temp and operating info
|
||||||
|
// const size_t bufferSizeInfo = JSON_OBJECT_SIZE(2);
|
||||||
|
// DynamicJsonDocument rootInfo(bufferSizeInfo);
|
||||||
|
|
||||||
|
// rootInfo["roomTemperature"] = currentStatus.roomTemperature;
|
||||||
|
// rootInfo["operating"] = currentStatus.operating;
|
||||||
|
|
||||||
|
// char bufferInfo[512];
|
||||||
|
// serializeJson(rootInfo, bufferInfo);
|
||||||
|
// #ifdef SDEB
|
||||||
|
// Serial.print(F("StatChgd: "));
|
||||||
|
// Serial.println(bufferInfo);
|
||||||
|
// #endif
|
||||||
|
// #ifdef RDEB
|
||||||
|
// debugD("StatusSend %s", bufferInfo);
|
||||||
|
// #endif
|
||||||
|
char t[5];
|
||||||
|
dtostrf(currentStatus.roomTemperature, 4, 1, t);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "temperature", 1, false, t);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "operating", 1, false, currentStatus.operating == false ? "OFF" : "ON");
|
||||||
|
//mqttClient.publish(MAIN_TOPIC STATUS_TOPIC, 1, false, bufferInfo);
|
||||||
|
// if (!mqtt_client.publish(heatpump_status_topic, bufferInfo, true)) {
|
||||||
|
// mqtt_client.publish(heatpump_debug_topic, "failed to publish to room temp and operation status to heatpump/status topic");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// send the timer info
|
||||||
|
// const size_t bufferSizeTimers = JSON_OBJECT_SIZE(5);
|
||||||
|
// DynamicJsonDocument rootTimers(bufferSizeTimers);
|
||||||
|
|
||||||
|
// rootTimers["mode"] = currentStatus.timers.mode;
|
||||||
|
// rootTimers["onMins"] = currentStatus.timers.onMinutesSet;
|
||||||
|
// rootTimers["onRemainMins"] = currentStatus.timers.onMinutesRemaining;
|
||||||
|
// rootTimers["offMins"] = currentStatus.timers.offMinutesSet;
|
||||||
|
// rootTimers["offRemainMins"] = currentStatus.timers.offMinutesRemaining;
|
||||||
|
|
||||||
|
// char bufferTimers[512];
|
||||||
|
// serializeJson(rootTimers, bufferTimers);
|
||||||
|
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("TimersSend %s", bufferTimers);
|
||||||
|
#endif
|
||||||
|
mqttClient.publish(MAIN_TOPIC "timerMode", 1, false, currentStatus.timers.mode);
|
||||||
|
itoa(currentStatus.timers.onMinutesSet, t, 10);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "timerOnMinSet", 1, false, t);
|
||||||
|
itoa(currentStatus.timers.onMinutesRemaining, t, 10);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "timerOnMinRem", 1, false, t);
|
||||||
|
itoa(currentStatus.timers.offMinutesSet, t, 10);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "timerOffMinSet", 1, false, t);
|
||||||
|
itoa(currentStatus.timers.offMinutesRemaining, t, 10);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "timerOffMinRem", 1, false, t);
|
||||||
|
//mqttClient.publish(MAIN_TOPIC TIMERS_TOPIC, 1, false, bufferTimers);
|
||||||
|
// if (!mqtt_client.publish(heatpump_timers_topic, bufferTimers, true)) {
|
||||||
|
// mqtt_client.publish(heatpump_debug_topic, "failed to publish timer info to heatpump/status topic");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
void hpPacketDebug(byte* packet, unsigned int length, char* packetDirection) {
|
||||||
|
if (_debugMode) {
|
||||||
|
String message = packetDirection;
|
||||||
|
message += " ";
|
||||||
|
for (unsigned int idx = 0; idx < length; idx++) {
|
||||||
|
if (packet[idx] < 16) {
|
||||||
|
message += "0"; // pad single hex digits with a 0
|
||||||
|
}
|
||||||
|
message += String(packet[idx], HEX) + " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
// const size_t bufferSize = JSON_OBJECT_SIZE(6);
|
||||||
|
// DynamicJsonDocument root(bufferSize);
|
||||||
|
|
||||||
|
// root[packetDirection] = message;
|
||||||
|
|
||||||
|
// char buffer[512];
|
||||||
|
// serializeJson(root, buffer);
|
||||||
|
|
||||||
|
// #ifdef RDEB
|
||||||
|
// debugD("DebugSend %s", buffer);
|
||||||
|
// #endif
|
||||||
|
mqttClient.publish(MAIN_TOPIC DEBUG_TOPIC, 1, false, message.c_str());
|
||||||
|
// if (!mqtt_client.publish(heatpump_debug_topic, buffer)) {
|
||||||
|
// mqtt_client.publish(heatpump_debug_topic, "failed to publish to heatpump/debug topic");
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void onMqttMessage(char* topic, char* payload, AsyncMqttClientMessageProperties properties, size_t len, size_t index, size_t total) {
|
||||||
|
char message[len + 1];
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
message[i] = (char)payload[i];
|
||||||
|
}
|
||||||
|
message[len] = '\0';
|
||||||
|
String mqtt = "Topic:";
|
||||||
|
mqtt += topic;
|
||||||
|
mqtt += " Payload:";
|
||||||
|
mqtt += String(message);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "debug_mqtt", 1, false, mqtt.c_str());
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.print(F("MQTT msg: "));
|
||||||
|
Serial.println(message);
|
||||||
|
#endif
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("MQTT Message %s", message);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (strcmp(topic, MAIN_TOPIC "power_set") == 0) {
|
||||||
|
mqttClient.publish(MAIN_TOPIC "debug", 1, false, (String("Power set:") + String(message)).c_str());
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.print(F("Setpower: "));
|
||||||
|
Serial.println(message);
|
||||||
|
#else
|
||||||
|
hp.setPowerSetting(atoi(message) == 1 ? "ON" : "OFF");
|
||||||
|
#endif
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("SET Power %s", power);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(topic, MAIN_TOPIC "mode_set") == 0) {
|
||||||
|
mqttClient.publish(MAIN_TOPIC "debug", 1, false, (String("Mode set:") + String(message)).c_str());
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("SET Mode %s", mode);
|
||||||
|
#endif
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.print(F("SetMode: "));
|
||||||
|
Serial.println(message);
|
||||||
|
#else
|
||||||
|
hp.setModeSetting(message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(topic, MAIN_TOPIC "temp_set") == 0) {
|
||||||
|
float temperature = atof(message);
|
||||||
|
mqttClient.publish(MAIN_TOPIC "debug", 1, false, (String("Temp set:") + String(message)).c_str());
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("SET Temp %f", temperature);
|
||||||
|
#endif
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.print(F("SetTemp: "));
|
||||||
|
Serial.println(temperature);
|
||||||
|
#else
|
||||||
|
hp.setTemperature(temperature);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(topic, MAIN_TOPIC "fan_set") == 0) {
|
||||||
|
mqttClient.publish(MAIN_TOPIC "debug", 1, false, (String("Fan set:") + String(message)).c_str());
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("SET Fan %s", fan);
|
||||||
|
#endif
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.print(F("SetFan: "));
|
||||||
|
Serial.println(message);
|
||||||
|
#else
|
||||||
|
hp.setFanSpeed(message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(topic, MAIN_TOPIC "vane_set") == 0) {
|
||||||
|
mqttClient.publish(MAIN_TOPIC "debug", 1, false, (String("Vane set:") + String(message)).c_str());
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("SET Vane %s", vane);
|
||||||
|
#endif
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.print(F("SetVane: "));
|
||||||
|
Serial.println(message);
|
||||||
|
#else
|
||||||
|
hp.setVaneSetting(message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(topic, MAIN_TOPIC "wideVane_set") == 0) {
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.print(F("SetwideVane: "));
|
||||||
|
Serial.println(message);
|
||||||
|
#else
|
||||||
|
hp.setWideVaneSetting(message);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strcmp(topic, MAIN_TOPIC "remoteTemperature_set") == 0) {
|
||||||
|
float temperature = atof(message);
|
||||||
|
#ifdef RDEB
|
||||||
|
debugD("SET RemTemp %f", temperature);
|
||||||
|
#endif
|
||||||
|
#ifdef SDEB
|
||||||
|
Serial.print(F("SetTemp: "));
|
||||||
|
Serial.println(temperature);
|
||||||
|
#else
|
||||||
|
hp.setRemoteTemperature(temperature);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef SDEB
|
||||||
|
// bool result = hp.update();
|
||||||
|
|
||||||
|
// if (!result) {
|
||||||
|
// mqttClient.publish(MAIN_TOPIC DEBUG_TOPIC, 1, false, "heatpump: update() failed");
|
||||||
|
// }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
11
MitsubishiAC/test/README
Normal file
11
MitsubishiAC/test/README
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
This directory is intended for PIO Unit Testing and project tests.
|
||||||
|
|
||||||
|
Unit Testing is a software testing method by which individual units of
|
||||||
|
source code, sets of one or more MCU program modules together with associated
|
||||||
|
control data, usage procedures, and operating procedures, are tested to
|
||||||
|
determine whether they are fit for use. Unit testing finds problems early
|
||||||
|
in the development cycle.
|
||||||
|
|
||||||
|
More information about PIO Unit Testing:
|
||||||
|
- https://docs.platformio.org/page/plus/unit-testing.html
|
||||||
Reference in New Issue
Block a user