\ No newline at end of file
diff --git a/3rdparty/PersWiFiManager/examples/spiffs_rest_api_nonblocking/data/wifi.min.htm.gz b/3rdparty/PersWiFiManager/examples/spiffs_rest_api_nonblocking/data/wifi.min.htm.gz
new file mode 100644
index 00000000..c8b0d2fb
Binary files /dev/null and b/3rdparty/PersWiFiManager/examples/spiffs_rest_api_nonblocking/data/wifi.min.htm.gz differ
diff --git a/3rdparty/PersWiFiManager/examples/spiffs_rest_api_nonblocking/spiffs_rest_api_nonblocking.ino b/3rdparty/PersWiFiManager/examples/spiffs_rest_api_nonblocking/spiffs_rest_api_nonblocking.ino
new file mode 100644
index 00000000..bab08e5d
--- /dev/null
+++ b/3rdparty/PersWiFiManager/examples/spiffs_rest_api_nonblocking/spiffs_rest_api_nonblocking.ino
@@ -0,0 +1,111 @@
+/*
+SPIFFS-served REST API example for PersWiFiManager v3.0
+*/
+
+#define DEBUG_SERIAL //uncomment for Serial debugging statements
+
+#ifdef DEBUG_SERIAL
+#define DEBUG_BEGIN Serial.begin(115200)
+#define DEBUG_PRINT(x) Serial.println(x)
+#else
+#define DEBUG_PRINT(x)
+#define DEBUG_BEGIN
+#endif
+
+//includes
+#include
+#include
+#include
+
+#include // http://ryandowning.net/EasySSDP/
+
+//extension of ESP8266WebServer with SPIFFS handlers built in
+#include // http://ryandowning.net/SPIFFSReadServer/
+// upload data folder to chip with Arduino ESP8266 filesystem uploader
+// https://github.com/esp8266/arduino-esp8266fs-plugin
+
+#include
+#include
+
+#define DEVICE_NAME "ESP8266 DEVICE"
+
+
+//server objects
+SPIFFSReadServer server(80);
+DNSServer dnsServer;
+PersWiFiManager persWM(server, dnsServer);
+
+////// Sample program data
+int x;
+String y;
+
+void setup() {
+ DEBUG_BEGIN; //for terminal debugging
+ DEBUG_PRINT();
+
+ //optional code handlers to run everytime wifi is connected...
+ persWM.onConnect([]() {
+ DEBUG_PRINT("wifi connected");
+ DEBUG_PRINT(WiFi.localIP());
+ EasySSDP::begin(server);
+ });
+ //...or AP mode is started
+ persWM.onAp([](){
+ DEBUG_PRINT("AP MODE");
+ DEBUG_PRINT(persWM.getApSsid());
+ });
+
+ //allows serving of files from SPIFFS
+ SPIFFS.begin();
+ //sets network name for AP mode
+ persWM.setApCredentials(DEVICE_NAME);
+ //persWM.setApCredentials(DEVICE_NAME, "password"); optional password
+
+ //make connecting/disconnecting non-blocking
+ persWM.setConnectNonBlock(true);
+
+ //in non-blocking mode, program will continue past this point without waiting
+ persWM.begin();
+
+ //handles commands from webpage, sends live data in JSON format
+ server.on("/api", []() {
+ DEBUG_PRINT("server.on /api");
+ if (server.hasArg("x")) {
+ x = server.arg("x").toInt();
+ DEBUG_PRINT(String("x: ") + x);
+ } //if
+ if (server.hasArg("y")) {
+ y = server.arg("y");
+ DEBUG_PRINT("y: " + y);
+ } //if
+
+ //build json object of program data
+ StaticJsonBuffer<200> jsonBuffer;
+ JsonObject &json = jsonBuffer.createObject();
+ json["x"] = x;
+ json["y"] = y;
+
+ char jsonchar[200];
+ json.printTo(jsonchar); //print to char array, takes more memory but sends in one piece
+ server.send(200, "application/json", jsonchar);
+
+ }); //server.on api
+
+
+ server.begin();
+ DEBUG_PRINT("setup complete.");
+} //void setup
+
+void loop() {
+ //in non-blocking mode, handleWiFi must be called in the main loop
+ persWM.handleWiFi();
+
+ dnsServer.processNextRequest();
+ server.handleClient();
+
+ //DEBUG_PRINT(millis());
+
+ // do stuff with x and y
+
+} //void loop
+
diff --git a/3rdparty/PersWiFiManager/keywords.txt b/3rdparty/PersWiFiManager/keywords.txt
new file mode 100644
index 00000000..01e0d404
--- /dev/null
+++ b/3rdparty/PersWiFiManager/keywords.txt
@@ -0,0 +1,28 @@
+#######################################
+# Syntax Coloring Map For PersWiFiManager Library
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+PersWiFiManager KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+attemptConnection KEYWORD2
+setupWiFiHandlers KEYWORD2
+begin KEYWORD2
+getApSsid KEYWORD2
+setApCredentials KEYWORD2
+handleWiFi KEYWORD2
+setConnectNonBlock KEYWORD2
+startApMode KEYWORD2
+onConnect KEYWORD2
+onAp KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
diff --git a/3rdparty/PersWiFiManager/library.properties b/3rdparty/PersWiFiManager/library.properties
new file mode 100644
index 00000000..03ad2842
--- /dev/null
+++ b/3rdparty/PersWiFiManager/library.properties
@@ -0,0 +1,9 @@
+name=PersWiFiManager
+version=3.0.1
+author=Ryan Downing
+maintainer=Ryan Downing
+sentence=Persistent WiFi Manager
+paragraph=A non-blocking, persistant wifi manager for ESP8266 that allows network changes at any time
+category=Communication
+url=http://ryandowning.net/PersWiFiManager/
+architectures=esp8266
diff --git a/3rdparty/Si5351mcu/.gitignore b/3rdparty/Si5351mcu/.gitignore
new file mode 100644
index 00000000..caa28400
--- /dev/null
+++ b/3rdparty/Si5351mcu/.gitignore
@@ -0,0 +1,7 @@
+.amake
+<<<<<<< HEAD
+.gch
+~*
+
+=======
+>>>>>>> 95b7ed7323809929eba7b3b2a5185d73b4242877
diff --git a/3rdparty/Si5351mcu/ChangeLog.md b/3rdparty/Si5351mcu/ChangeLog.md
new file mode 100644
index 00000000..6864f8a0
--- /dev/null
+++ b/3rdparty/Si5351mcu/ChangeLog.md
@@ -0,0 +1,54 @@
+# Si5351mcu Changelog File #
+
+## v0.6.1 (Augus 1, 2018) ##
+
+* Documentation update and completion to match code features and tricks
+
+## v0.6 (February 10, 2018) ##
+
+* Feature: you can now OVERCLOCK the Si5351 to get up to 250 MHz from it, see the overclock subject on the README.md
+* Documentation improvements and re-arrangement.
+
+## v0.5 (February 7, 2018) ##
+
+* Feature: All integer math now, induced error must be at worst +/- 2 Hz.
+* Feature: Clock status via clkOn[clk] public var.
+* Bug Fix: Output divider low limit safe guard in place (it make some trouble under some circumstances)
+* New super simple example.
+
+## v0.4 (August 2, 2017) ##
+
+* Bug Fix: Triaged a strange level problem with freqs above ~112 Mhz, the signal level start dropping around 112MHz and ~150 MHz suddenly go beyond limits (high) to slowly drop again up to the end. Fact: the lib needs a reset() on EVERY frequency change above VCO/8 (~112MHz). Remember that datasheet specs are 8KHz to 160MHz, but we are pushing it up to ~225 Mhz (max_vco/4)
+* Code refractory on some points to match correct behavior, as previous code has little bugs introduced by bad documentation from Silicon Labs (+1 for the chip; -5 for the docs errors). See Bitx20 mail-lits archives for June-August 2017 for more info and the debate.
+
+## v0.3 (June 14, 2017) ##
+
+* Feature: the lib now handle the include and start of the I2C (Wire) library internally via the init procedures
+* Added a new generic init() procedure to handle the default parameters
+* The init() function is required from now on (MANDATORY)
+* Fixed the way we handled the base xtal and the correction factor
+
+## v0.2rc (April 23, 2017) ##
+
+* Added power level support for each output independently, watch out!: setting the power level will enable the output.
+* Set default power to the lowest one (2mA) from the maximun possible (8mA).
+* Fixed the need for a reset after each correction, it does it now automatically
+* Added a init function to pass a custom xtal
+* Modified the example to show no need for a init unless you use a different xtal
+* Improved the keywords.txt file to improve edition on the Arduino IDE
+* Included a "features" section on the README.md
+
+## v0.1rc (April 20, 2017) ##
+
+* Added enable(), disable() and off() functions.
+* Added support for handling all the three outputs of the Si5351A, (CLK1 & CLK2 are mutually-exclusive)
+* Updated the example with the new functions.
+* Improved library logic by reusing and optimizing functions.
+* Improved the documentation and comments (lib code, README and example)
+* The compiled code is slightly smaller now (~1% on an ATMega328p)
+* Added Changelog and version files.
+* Extensive tests made to validate every function.
+
+## Initial Release, v0.0 (April 9, 2017) ##
+
+* Basic functionality.
diff --git a/3rdparty/Si5351mcu/README.md b/3rdparty/Si5351mcu/README.md
new file mode 100644
index 00000000..c24b6fd1
--- /dev/null
+++ b/3rdparty/Si5351mcu/README.md
@@ -0,0 +1,271 @@
+# Arduino Si5351 Library tuned for size and click noise free. #
+
+This library is tuned for size on the Arduino platform, it will control CLK0, CLK1 and CLK2 outputs for the Si5351A (the version with just 3 clocks out, but you will not be able to use the three at once).
+
+## Inspiration ##
+
+This work is based on the previous work of these great people:
+
+* [Etherkit/NT7S:](https://github.com/etherkit/Si5351Arduino) The mainstream full featured lib, with big code as well (based on Linux kernel code)
+* [QRP Labs demo code from Hans Summers:](http://qrp-labs.com/synth/si5351ademo.html) The smallest and simple ones on the net.
+* [DK7IH demo code:](https://radiotransmitter.wordpress.com/category/si5351a/) The first clickless noise code on the wild.
+* [Jerry Gaffke](https://github.com/afarhan/ubitx.git) integer routines for the Raduino and ubitx
+
+## Set your Goal and make an estrategy ##
+
+There is a few routines in the Internet to manage the Si5351 chip, all of them has a few distinct feature set because they use different strategies (different goals) that make them unique in some way.
+
+My goal is this:
+* Keep it as small as possible (Smallest firmware footprint)
+* Less phase and click noise possible (Playing with every trick possible)
+
+The main purpose is to be used in Radio receiver projects, so this two mentioned goals are the golden rule.
+
+Let's list some of goals archeivements and bonuses:
+
+**Small firmware footprint:**
+
+A basic sketch to set just only one clock out to a given frequency with a change in power and correcting the XTAL ppm error is only ~3.3 kBytes of firmware (~10% of an Arduino Uno)
+
+The same settings with the [Si5351Arduino library (Etherkit)](https://github.com/etherkit/Si5351Arduino) will give you a bigger firmware space of ~10 kBytes or 31% of an Arduino Uno.
+
+[Jerry Gaffke](https://github.com/afarhan/ubitx.git) embedded routines in the ubitx transceiver has the smallest footprint in the Arduino platform I have seen, but has worst phase noise and smallest frequency range.
+
+**Phase noise to the minimum:**
+
+We use every trick on the datasheet, OEM Application Notes or the Internet to minimize phase noise. (Even a few ones learned on the process)
+
+For example the [Etherkit](https://github.com/etherkit/Si5351Arduino) library and [Jerry Gaffke](https://github.com/afarhan/ubitx.git) embedded routines uses some but not all the tricks to minimize phase noise (Etherkit one gives control over all features, Jerry Gaffke has small footprint and in the process he sacrifice phase noise and frequency range)
+
+**Click noise free:**
+
+If you play from the book (Datasheet and Application Notes) you will have a "click-like" noise burst every time you change the output frequency.
+
+That's not a problem if you intend to use only fixed frequencies at the output, but if you plan to sweep or use it on any application that moves the frequency that click-like noise will haunt you. (like in a radio receiver or transceiver)
+
+I have learned a few tricks from many sources in the Internet and after some local testing I have came across a scheme that make this lib virtually click-noise-less; see the "Click noise free" section below for details.
+
+**Fast frequency changes:**
+
+This was a side effect of the last trick to minimize the click noise, see the "Click noise free" section below for details.
+
+Summary: other routines write all registers for every frequency change, I write half of them most of the time, speeding up the process.
+
+**Two of three**
+
+Yes, there is no such thing as free lunch, to get all the above features and the ones that follow I have to make some sacrifices, in this case spare one of the outputs. See "Two of three" section below.
+
+## Features ##
+
+This are so far the implemented features (Any particular wish? use the Issues tab for that):
+
+* Custom XTAL passing on init (Default is 27.000 MHz (See _Si.init()_ )
+* You can pass a correction to the xtal while running (See _Si.correction()_ )
+* You have a fast way to power off all outputs of the Chip at once. (See _Si.off()_ )
+* You can enable/disable any output at any time (See _Si.enable(clk) and Si.disable(clk)_ )
+* By default all outputs are off after the Si.init() procedure. You has to enable them by hand.
+* You can only have 2 of the 3 outputs running at any moment (See "Two of three" section below)
+* Power control on each output independently (See _Si.setPower(clk, level)_ on the lib header)
+* Initial power defaults to the lowest level (2mA) for all outputs.
+* You don't need to include and configure the Wire (I2C) library, this lib do that for you already.
+* Frequency limits are not hard coded on the lib, so you can stress your hardware to it's particular limit (_You can move usually from ~3kHz to ~225 MHz, far away from the 8kHz to 160 MHz limits from the datasheet_)
+* You has a way to verify the status of a particular clock (_Enabled/Disabled by the Si.clkOn[clk] var_)
+* From v0.5 and beyond we saved more than 1 kbyte of your precious firmware space due to the use of all integer math now (Worst induced error is below +/- 1 Hz)
+* Overclock, yes, you can move the limits upward up to ~250MHz (see the "OVERCLOCK" section below)
+* Improved the click noise algorithm to get even more click noise reduction (see Click noise free section below)
+* Fast frequency changes as part of the improved click noise algorithm (see Click noise free section below)
+
+## How to use the lib ##
+
+Get the lib by cloning this git repository or get it by clicking the green "Download button" on the page.
+
+Move it or extract it on your library directory
+
+Include the lib in your code:
+
+
+```
+(... your code here ...)
+
+// now include the library
+#include "si5351mcu.h"
+
+// lib instantiation as "Si"
+Si5351mcu Si;
+
+(... more of your code here ...)
+
+```
+
+Follow this sequence on you setup() procedure:
+
+* Initialize the library with the default or optional Xtal Clock.
+* Apply correction factor (if needed)
+* Set some frequencies to the desired outputs.
+* Enable the desired outputs
+
+Here you have an example code ("Si" is the lib instance):
+
+```
+setup() {
+ (... your code here ...)
+
+ //////////////////////////////////
+ // Si5351 functions /
+ //////////////////////////////////
+
+ // Init the library, in this case with the default 27.000 Mhz Xtal
+ Si.init();
+
+ // commented Init procedure for a not default 25.000 MHz xtal
+ //Si.init(25000000L);
+
+ // Optional, apply a pre-calculated correction factor
+ Si.correction(-150); // Xtal is low by 150 Hz
+
+ // Enable the desired outputs with some frequencies
+ Si.setFreq(0, 25000000); // CLK0 output 25.000 MHz
+ Si.setFreq(1, 145000000); // CLK1 output 145.000 MHz
+
+ // enable the outputs
+ Si.enable(0);
+ Si.enable(1);
+
+ (... more of your code here ...)
+}
+
+```
+
+If you need to apply/vary the correction factor **after** the setup process you will get a click noise on the next setFreq() to apply the changes.
+
+Use it, you can enable, disable, change the power or frequency, see this code fragment with some examples:
+
+```
+loop() {
+ (... your code here ...)
+
+ // disable clk1
+ Si.disable(1);
+
+ // change the power of clk0 to 4mA
+ Si.setPower(0, SIOUT_4mA);
+
+ // apply a correction factor of 300 Hz (correction will be applied on the next Si.setFreq() call)
+ Si.correction(300);
+
+ // change the clk0 output frequency
+ Si.setFreq(0, 7110000);
+
+ // power of all outputs
+ Si.off();
+
+ (... more of your code here ...)
+}
+```
+
+
+## OVERCLOCK ##
+
+Yes, you can overclock the Si5351, the datasheet states that the VCO moves from 600 to 900 MHz and that gives us a usable range from ~3 kHz to 225 MHz.
+
+But what if we can move the VCO frequency to a higher values?
+
+The overclock feature does just that, use a higher top limit for the VCO on the calculations. In my test with two batch of the Si5351A I can get safely up to 1.000 GHz without trouble; in one batch the PLL unlocks around 1.1 GHz and in the other about 1.050 GHz; so I recommend not going beyond 1.000 GHz.
+
+With a maximum VCO of 1.000 GHz and a lower division factor of 4 we have jumped from a 255 MHz to 250 MHz top frequency that can be generated with our cheap chip.
+
+**Some "must include" WARNINGS:**
+
+* The chip was not intended to go that high, so, use it with caution and test it on your hardware moving the overclock limit in steps of 10 MHz starting with 900 MHz and testing with every change until it does not work; then go down by 10 MHz to be in a safe zone.
+* Moving the upper limit has its penalty on the lower level, your bottom frequency will move from the ~3 kHz to ~10 kHz range.
+* The phase noise of the output if worst as you use a higher frequency, at a _**fixed**_ 250 MHz it's noticeable but no so bad for a TTL or CMOS clock application.
+* The phase noise is specially bad if you make a sweep or move action beyond 180 MHz; the phase noise from the unlock state to next lock of the PLL is very noticeable in a spectrum analyzer, even on a cheap RTL-SDR one.
+* I recommend to only use the outputs beyond 150 MHz as fixed value and don't move them if you cares about phase noise.
+
+**How to do it?**
+
+You need to declare a macro with the overclock value **BEFORE** the library include, just like this:
+
+```
+(... your code here ...)
+
+// Using the overclock feature for the Si5351mcu library
+#define SI_OVERCLOCK 1000000000L // 1.0 GHz in Hz
+
+// now include the library
+#include "si5351mcu.h"
+
+// lib instantiation as "Si"
+Si5351mcu Si;
+
+// now you can generate frequencies from ~10 kHz up to 250 MHz.
+
+(... more of your code here ...)
+
+```
+
+## Click noise free ##
+
+Click-like noise came from a few sources as per my testing:
+
+* Turn off then on the CLKx outputs (Register 3. Output Enable Control)
+* Power down then on the Msynths (CLKx_PDN bits for every Msynth)
+* Reset the PLL (Register 177: PLL Reset)
+
+We are concerned about click noise only when changing from one frequency to the other, so if we don't touch the output enable control or the power down msynth registers once activated; then we are set to avoid click from this two first sources.
+
+The last one is tricky, in theory a PLL does not need to be reseted every time you change it's output frequency as it's a **P**hase **L**ocked **L**oop and it's a self correcting algorithm/hardware.
+
+That last idea was put to test on a simple scheme: what if I set a fixed output divider Msynth and move the VCO for it's entire range without resetting it on any point?
+
+If the "PLL reset" is in deed needed I will have some strange behavior at some point, right?
+
+But practice confirmed my idea, I can set a output Msynth of 6 and move the VCO (PLL) for the entire range (600 to 900 MHz) and get a continuous and stable output from 100 to 150 MHz.
+
+Then what is for the "PLL reset" (Register 177) in practice?
+
+Some further test showed that this "reset" function is applied no to the PLL Msynth, but to the output Msynth and not in every case, yes, it has a bad name or a bad explained name.
+
+After some test I find that you need the "PLL reset" (Register 177) trick only on some cases when you change the value of the output divider Msynth.
+
+Implementing that in code was easy, an array to keep track of the actual output divider Msynth and only write it to the chip and reset "the PLL" when it's needed.
+
+Hey! that leads to a I2C time reduction by half (most of the time) as a side effect!
+
+Most of the time when you are making a sweep the output divider Msynth has a constant value and you only moves the VCO (PLL) Then I wrote just 8 bytes to the I2C bus (to control the VCO/PLL) instead of 16 (8 for the VCO/PLL & 8 more for the output divider Msynth) or 17 (16 + reset byte) most of the time, cutting time between writes to half making frequency changes 2x fast as before.
+
+## Two of three ##
+
+Yes, there is a tittle catch here with CLK1 and CLK2: both share PLL_B and as we use our own algorithm to calculate the frequencies and minimize phase noise you can only use one of them at a time.
+
+Note: _In practice you can, but the other will move from the frequency you set, which is an unexpected behavior, so I made them mutually exclusive (CLK1 and CLK2)._
+
+This are the valid combinations for independent clocks output.
+
+* CLK0 and CLK1
+* CLK0 and CLK2
+
+Again: You can't use CLK1 and CLK2 at the same time, as soon as you set one of them the other will shut off. That's why you get two of three and one of them must be always CLK0.
+
+## Author & contributors ##
+
+The only author is Pavel Milanes, CO7WT, a cuban amateur radio operator; reachable at pavelmc@gmail.com, Until now I have no contributors or sponsors.
+
+## Where to download the latest version? ##
+
+Always download the latest version from the [github repository](https://github.com/pavelmc/Si5351mcu/)
+
+See ChangeLog.md and Version files on this repository to know what are the latest changes and versions.
+
+## If you like to give thanks... ##
+
+No payment of whatsoever is required to use this code: this is [Free/Libre Software](https://en.wikipedia.org/wiki/Software_Libre), nevertheless donations are very welcomed.
+
+I live in Cuba island and the Internet/Cell is very expensive here (USD $1.00/hour), you can donate anonymously internet time or cell phone air time to me via [Ding Topups](https://www.ding.com/) to keep me connected and developing for the homebrewers community.
+
+If you like to do so, please go to Ding, select Cuba, select Cubacell (for phone top up) or Nauta (for Internet time)
+
+* For phone topup use this number (My cell, feel free to call me if you like): +53 538-478-19
+* For internet time use this user (Nauta service): co7wt@nauta.com.cu (that's not an email but an user account name)
+
+Thanks!
diff --git a/3rdparty/Si5351mcu/examples/si5351_mcu/si5351_mcu.ino b/3rdparty/Si5351mcu/examples/si5351_mcu/si5351_mcu.ino
new file mode 100644
index 00000000..b38a3016
--- /dev/null
+++ b/3rdparty/Si5351mcu/examples/si5351_mcu/si5351_mcu.ino
@@ -0,0 +1,158 @@
+/*
+ * si5351mcu - Si5351 library for Arduino, MCU tuned for size and click-less
+ *
+ * This is the packed example.
+ *
+ * Copyright (C) 2017 Pavel Milanes
+ *
+ * Many chunk of codes are derived-from/copied from other libs
+ * all GNU GPL licenced:
+ * - Linux Kernel (www.kernel.org)
+ * - Hans Summers libs and demo code (qrp-labs.com)
+ * - Etherkit (NT7S) Si5351 libs on github
+ * - DK7IH example.
+ * - Jerry Gaffke integer routines for the bitx20 group
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+/***************************************************************************
+ * This example is meant to be monitored with an RTL-SDR receiver
+ * but you can change the frequency and other vars to test with your hardware.
+ *
+ * Set your SDR software to monitor from 60 to 62 Mhz.
+ *
+ * This will set 60.0 Mhz in clock 0, put and alternating frequencies
+ * at 60.5 and 61.0 Mhz on CLK1 and CLK2 to show they are mutually exclusive.
+ *
+ * Then make a sweep from 60 to 62 Mhz on CLK2, with an stop every 200Khz
+ * and then a train of one second pulses will follow with varying power levels
+ *
+ * Take into account your XTAL error, see Si.correction(###) below
+ *
+ ***************************************************************************/
+
+#include "si5351mcu.h"
+
+// lib instantiation as "Si"
+Si5351mcu Si;
+
+// Stop every X Mhz for Y seconds to measure
+#define EVERY 200000 // stop every 200khz
+#define DANCE 3 // 3 seconds
+
+// some variables
+long freqStart = 60000000; // 60.0 MHz
+long freqStop = 62000000; // 62.0 MHz
+long step = 10000; // 10.0 kHz
+long freq = freqStart;
+
+
+void setup() {
+ // init the Si5351 lib
+ Si.init();
+
+ // For a different xtal (from the default of 27.00000 Mhz)
+ // just pass it on the init procedure, just like this
+ // Si.init(26570000);
+
+ // set & apply my calculated correction factor
+ Si.correction(-1250);
+
+ // pre-load some sweet spot freqs
+ Si.setFreq(0, freqStart);
+ Si.setFreq(1, freqStart);
+
+ // reset the PLLs
+ Si.reset();
+
+ // put a tone in the start freq on CLK0
+ Si.setFreq(0, freqStart);
+ Si.enable(0);
+
+ // make the dance on the two outputs
+ Si.setFreq(1, freqStart + 500000); // CLK1 output
+ Si.enable(1);
+ delay(3000);
+ // Si.disable(1); // no need to disable, enabling CLK2 disable this
+
+ Si.setFreq(2, freqStart + 1000000); // CLK2 output
+ Si.enable(2);
+ delay(3000);
+ //Si.disable(2); // no need to disable, enabling CLK1 disable this
+
+ Si.setFreq(1, freqStart + 500000); // CLK1 output
+ Si.enable(1);
+ delay(3000);
+ //Si.disable(1); // no need to disable, enabling CLK2 disable this
+
+ Si.setFreq(2, freqStart + 1000000); // CLK2 output
+ Si.enable(2);
+ delay(3000);
+ Si.disable(2); // this is the last in the dance, disable it
+
+ // shut down CLK0
+ Si.disable(0);
+
+ // set CLK2 to the start freq
+ Si.setFreq(2, freqStart); // it's disabled by now
+}
+
+
+void loop() {
+ // check for the stop to measure
+ if ((freq % EVERY) == 0) {
+ // it's time to flip-flop it
+
+ for (byte i = 0; i < 4; i++) {
+ // power off the clk2 output
+ Si.disable(2);
+ delay(500);
+ // power mod, the lib define some macros for that:
+ // SIOUT_2mA, SIOUT_4mA, SIOUT_6mA and SIOUT_8mA
+ //
+ // But they are incidentally matching the 0 to 3 count..
+ // so I will use the cycle counter for that (i)
+ //
+ // moreover, setting the power on an output will enable it
+ // so I will explicit omit the enable here
+ Si.setPower(2, i);
+ //Si.enable(2);
+
+ delay(1000);
+ }
+
+ // reset the power to low
+ Si.setPower(2, SIOUT_2mA);
+
+ // set it for the new cycle
+ freq += step;
+ } else {
+ // check if we are on the limits
+ if (freq <= freqStop) {
+ // no, set and increment
+ Si.setFreq(2,freq); // but it can be with CLK0 or CLK1 instead
+
+ // set it for the new cycle
+ freq += step;
+
+ // a short delay to slow things a little.
+ delay(50);
+ } else {
+ // we reached the limit, reset to start
+ freq = freqStart;
+ }
+ }
+}
diff --git a/3rdparty/Si5351mcu/examples/si5351_mcu_simple/si5351_mcu_simple.ino b/3rdparty/Si5351mcu/examples/si5351_mcu_simple/si5351_mcu_simple.ino
new file mode 100644
index 00000000..998fd027
--- /dev/null
+++ b/3rdparty/Si5351mcu/examples/si5351_mcu_simple/si5351_mcu_simple.ino
@@ -0,0 +1,72 @@
+/*
+ * si5351mcu - Si5351 library for Arduino, MCU tuned for size and click-less
+ *
+ * This is the packed simplest example.
+ *
+ * Copyright (C) 2017 Pavel Milanes
+ *
+ * Many chunk of codes are derived-from/copied from other libs
+ * all GNU GPL licenced:
+ * - Linux Kernel (www.kernel.org)
+ * - Hans Summers libs and demo code (qrp-labs.com)
+ * - Etherkit (NT7S) Si5351 libs on github
+ * - DK7IH example.
+ * - Jerry Gaffke integer routines for the bitx20 group
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+/***************************************************************************
+ * Just put two frequencies out in clk0 and clk1 at different power levels
+ *
+ * Take into account your XTAL error, see Si.correction(###) below
+ ***************************************************************************/
+
+#include "si5351mcu.h"
+
+// lib instantiation as "Si"
+Si5351mcu Si;
+
+// some variables
+long F1 = 60000000; // 60.0 MHz to CLK0
+long F2 = 60500000; // 60.5 MHz to CLK1
+
+void setup() {
+ // init the Si5351 lib
+ Si.init();
+
+ // For a different xtal (from the default of 27.00000 Mhz)
+ // just pass it on the init procedure, just like this
+ // Si.init(26570000);
+
+ // set & apply my calculated correction factor
+ Si.correction(-1250);
+
+ // set max power to both outputs
+ Si.setPower(0, SIOUT_8mA);
+ Si.setPower(1, SIOUT_2mA);
+
+ // Set frequencies
+ Si.setFreq(0, F1);
+ Si.setFreq(1, F2);
+
+ // reset the PLLs
+ Si.reset();
+}
+
+
+void loop() {
+ // do nothing
+}
diff --git a/3rdparty/Si5351mcu/keywords.txt b/3rdparty/Si5351mcu/keywords.txt
new file mode 100644
index 00000000..9468e2b1
--- /dev/null
+++ b/3rdparty/Si5351mcu/keywords.txt
@@ -0,0 +1,21 @@
+Si5351mcu KEYWORD1
+
+init KEYWORD2
+correction KEYWORD2
+i2cWrite KEYWORD2
+reset KEYWORD2
+setFreq KEYWORD2
+enable KEYWORD2
+disable KEYWORD2
+setPower KEYWORD2
+clkOn KEYWORD2
+
+SIXTAL LITERAL1
+SIADDR LITERAL1
+SIOUT_2mA LITERAL1
+SIOUT_4mA LITERAL1
+SIOUT_6mA LITERAL1
+SIOUT_8mA LITERAL1
+SICLK0_R LITERAL1
+SICLK12_R LITERAL1
+SI_OVERCLOCK LITERAL1
diff --git a/3rdparty/Si5351mcu/library.properties b/3rdparty/Si5351mcu/library.properties
new file mode 100644
index 00000000..5d8d5c91
--- /dev/null
+++ b/3rdparty/Si5351mcu/library.properties
@@ -0,0 +1,9 @@
+name=Si5351mcu
+version=0.6
+author=Pavel Milanes
+maintainer=Pavel Milanes
+sentence=A MCU friendly library for the Si5351A clock generator ICs from Silicon Labs.
+paragraph=This library is optimized for size over the Arduino platform; will allow you to control any of the three out of the Si5351 outputs (but only two at the same time). It's click FREE while tuning and really small.
+category=Device Control
+url=https://github.com/pavelmc/Si5351mcu
+architectures=*
diff --git a/3rdparty/Si5351mcu/licence.txt b/3rdparty/Si5351mcu/licence.txt
new file mode 100644
index 00000000..94a9ed02
--- /dev/null
+++ b/3rdparty/Si5351mcu/licence.txt
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/3rdparty/Si5351mcu/src/si5351mcu.cpp b/3rdparty/Si5351mcu/src/si5351mcu.cpp
new file mode 100644
index 00000000..29633bc8
--- /dev/null
+++ b/3rdparty/Si5351mcu/src/si5351mcu.cpp
@@ -0,0 +1,301 @@
+/*
+ * si5351mcu - Si5351 library for Arduino MCU tuned for size and click-less
+ *
+ * Copyright (C) 2017 Pavel Milanes
+ *
+ * Many chunk of codes are derived-from/copied from other libs
+ * all GNU GPL licenced:
+ * - Linux Kernel (www.kernel.org)
+ * - Hans Summers libs and demo code (qrp-labs.com)
+ * - Etherkit (NT7S) Si5351 libs on github
+ * - DK7IH example.
+ * - Jerry Gaffke integer routines for the bitx20 group
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "Arduino.h"
+#include "si5351mcu.h"
+
+// wire library loading, if not defined
+#ifndef WIRE_H
+ #include "Wire.h"
+#endif
+
+/*****************************************************************************
+ * This is the default init procedure, it set the Si5351 with this params:
+ * XTAL 27.000 Mhz
+ *****************************************************************************/
+ void Si5351mcu::init() {
+ // init with the default freq
+ init(int_xtal);
+}
+
+
+/*****************************************************************************
+ * This is the custom init procedure, it's used to pass a custom xtal
+ * and has the duty of init the I2C protocol handshake
+ *****************************************************************************/
+ void Si5351mcu::init(uint32_t nxtal) {
+ // set the new base xtal freq
+ base_xtal = int_xtal = nxtal;
+
+ // start I2C (wire) procedures
+ Wire.begin();
+
+ // power off all the outputs
+ off();
+}
+
+
+/*****************************************************************************
+ * This function set the freq of the corresponding clock.
+ *
+ * In my tests my Si5351 can work between 7,8 Khz and ~225 Mhz [~250 MHz with
+ * overclocking] as usual YMMV
+ *
+ * Click noise:
+ * - The lib has a reset programmed [aka: click noise] every time it needs to
+ * change the output divider of a particular MSynth, if you move in big steps
+ * this can lead to an increased rate of click noise per tunning step.
+ * - If you move at a pace of a few Hz each time the output divider will
+ * change at a low rate, hence less click noise per tunning step.
+ * - The output divider moves [change] faster at high frequencies, so at HF the
+ * clikc noise is at the real minimum possible.
+ *
+ * [See the README.md file for other details]
+ ****************************************************************************/
+void Si5351mcu::setFreq(uint8_t clk, unsigned long freq) {
+ uint8_t a, R = 1, shifts = 0;
+ uint32_t b, c, f, fvco, outdivider;
+ uint32_t MSx_P1, MSNx_P1, MSNx_P2, MSNx_P3;
+
+ // Overclock option
+ #ifdef SI_OVERCLOCK
+ // user a overclock setting for the VCO, max value in my hardware
+ // was 1.05 to 1.1 GHz, as usual YMMV [See README.md for details]
+ outdivider = SI_OVERCLOCK / freq;
+ #else
+ // normal VCO from the datasheet and AN
+ // With 900 MHz beeing the maximum internal PLL-Frequency
+ outdivider = 900000000 / freq;
+ #endif
+
+ // use additional Output divider ("R")
+ while (outdivider > 900) {
+ R = R * 2;
+ outdivider = outdivider / 2;
+ }
+
+ // finds the even divider which delivers the intended Frequency
+ if (outdivider % 2) outdivider--;
+
+ // Calculate the PLL-Frequency (given the even divider)
+ fvco = outdivider * R * freq;
+
+ // Convert the Output Divider to the bit-setting required in register 44
+ switch (R) {
+ case 1: R = 0; break;
+ case 2: R = 16; break;
+ case 4: R = 32; break;
+ case 8: R = 48; break;
+ case 16: R = 64; break;
+ case 32: R = 80; break;
+ case 64: R = 96; break;
+ case 128: R = 112; break;
+ }
+
+ // we have now the integer part of the output msynth
+ // the b & c is fixed below
+ MSx_P1 = 128 * outdivider - 512;
+
+ // calc the a/b/c for the PLL Msynth
+ /***************************************************************************
+ * We will use integer only on the b/c relation, and will >> 5 (/32) both
+ * to fit it on the 1048 k limit of C and keep the relation
+ * the most accurate possible, this works fine with xtals from
+ * 24 to 28 Mhz.
+ *
+ * This will give errors of about +/- 2 Hz maximum
+ * as per my test and simulations in the worst case, well below the
+ * XTAl ppm error...
+ *
+ * This will free more than 1K of the final eeprom
+ *
+ ****************************************************************************/
+ a = fvco / int_xtal;
+ b = (fvco % int_xtal) >> 5; // Integer par of the fraction
+ // scaled to match "c" limits
+ c = int_xtal >> 5; // "c" scaled to match it's limits
+ // in the register
+
+ // f is (128*b)/c to mimic the Floor(128*(b/c)) from the datasheet
+ f = (128 * b) / c;
+
+ // build the registers to write
+ MSNx_P1 = 128 * a + f - 512;
+ MSNx_P2 = 128 * b - f * c;
+ MSNx_P3 = c;
+
+ // PLLs and CLK# registers are allocated with a shift, we handle that with
+ // the shifts var to make code smaller
+ if (clk > 0 ) shifts = 8;
+
+ // plls, A & B registers separated by 8 bytes
+ i2cWrite(26 + shifts, (MSNx_P3 & 65280) >> 8); // Bits [15:8] of MSNx_P3 in register 26
+ i2cWrite(27 + shifts, MSNx_P3 & 255);
+ i2cWrite(28 + shifts, (MSNx_P1 & 196608) >> 16);
+ i2cWrite(29 + shifts, (MSNx_P1 & 65280) >> 8); // Bits [15:8] of MSNx_P1 in register 29
+ i2cWrite(30 + shifts, MSNx_P1 & 255); // Bits [7:0] of MSNx_P1 in register 30
+ i2cWrite(31 + shifts, ((MSNx_P3 & 983040) >> 12) | ((MSNx_P2 & 983040) >> 16)); // Parts of MSNx_P3 and MSNx_P1
+ i2cWrite(32 + shifts, (MSNx_P2 & 65280) >> 8); // Bits [15:8] of MSNx_P2 in register 32
+ i2cWrite(33 + shifts, MSNx_P2 & 255); // Bits [7:0] of MSNx_P2 in register 33
+
+ // Write the output divider msynth only if we need to, in this way we can
+ // speed up the frequency changes almost by half the time most of the time
+ // and the main goal is to avoid the nasty click noise on freq change
+ if (omsynth[clk] != outdivider) {
+ // CLK# registers are exactly 8 * clk# bytes shifted from a base register.
+ shifts = clk * 8;
+
+ // multisynths
+ i2cWrite(42 + shifts, 0); // Bits [15:8] of MS0_P3 (always 0) in register 42
+ i2cWrite(43 + shifts, 1); // Bits [7:0] of MS0_P3 (always 1) in register 43
+ // See datasheet, special trick when R=4
+ if (outdivider == 4) {
+ i2cWrite(44 + shifts, 12 | R);
+ i2cWrite(45 + shifts, 0); // Bits [15:8] of MSx_P1 must be 0
+ i2cWrite(46 + shifts, 0); // Bits [7:0] of MSx_P1 must be 0
+ } else {
+ i2cWrite(44 + shifts, ((MSx_P1 & 196608) >> 16) | R); // Bits [17:16] of MSx_P1 in bits [1:0] and R in [7:4]
+ i2cWrite(45 + shifts, (MSx_P1 & 65280) >> 8); // Bits [15:8] of MSx_P1 in register 45
+ i2cWrite(46 + shifts, MSx_P1 & 255); // Bits [7:0] of MSx_P1 in register 46
+ }
+ i2cWrite(47 + shifts, 0); // Bits [19:16] of MS0_P2 and MS0_P3 are always 0
+ i2cWrite(48 + shifts, 0); // Bits [15:8] of MS0_P2 are always 0
+ i2cWrite(49 + shifts, 0); // Bits [7:0] of MS0_P2 are always 0
+
+ // must reset the so called "PLL", in fact the output msynth
+ reset();
+
+ // keep track of the change
+ omsynth[clk] = (uint16_t)outdivider;
+ }
+}
+
+
+/*****************************************************************************
+ * Reset of the PLLs and multisynths output enable
+ *
+ * This must be called to soft reset the PLLs and cycle the output of the
+ * multisynths: this is the "click" noise source in the RF spectrum.
+ *
+ * So it must be avoided at all costs, so this lib just call it at the
+ * initialization of the PLLs and when a correction is applied
+ *
+ * If you are concerned with accuracy you can implement a reset every
+ * other Mhz to be sure it get exactly on spot.
+ ****************************************************************************/
+void Si5351mcu::reset(void) {
+ // This soft-resets PLL A & B (32 + 128) in just one step
+ i2cWrite(177, 0xA0);
+}
+
+
+/*****************************************************************************
+ * Function to disable all outputs
+ *
+ * The PLL are kept running, just the m-synths are powered off.
+ *
+ * This allows to keep the chip warm and exactly on freq the next time you
+ * enable an output.
+ ****************************************************************************/
+void Si5351mcu::off() {
+ // This disable all the CLK outputs
+ for (byte i=0; i<3; i++) disable(i);
+}
+
+
+/*****************************************************************************
+ * Function to set the correction in Hz over the Si5351 XTAL.
+ *
+ * This will call a reset of the PLLs and multi-synths so it will produce a
+ * click every time it's called
+ ****************************************************************************/
+void Si5351mcu::correction(int32_t diff) {
+ // apply some corrections to the xtal
+ int_xtal = base_xtal + diff;
+
+ // reset the PLLs to apply the correction
+ reset();
+}
+
+
+/*****************************************************************************
+ * This function enables the selected output
+ *
+ * Beware: ZERO is clock output enabled
+ *****************************************************************************/
+void Si5351mcu::enable(uint8_t clk) {
+ // var to handle the mask of the registers value
+ uint8_t m = SICLK0_R;
+ if (clk > 0) m = SICLK12_R;
+
+ // write the register value
+ i2cWrite(16 + clk, m + clkpower[clk]);
+
+ // 1 & 2 are mutually exclusive
+ if (clk == 1) disable(2);
+ if (clk == 2) disable(1);
+
+ // update the status of the clk
+ clkOn[clk] = 1;
+}
+
+
+/*****************************************************************************
+ * This function disables the selected output
+ *
+ * Beware: ONE is clock output disabled
+ * *****************************************************************************/
+void Si5351mcu::disable(uint8_t clk) {
+ // send
+ i2cWrite(16 + clk, 128);
+
+ // update the status of the clk
+ clkOn[clk] = 0;
+}
+
+
+/****************************************************************************
+ * Set the power output for each output independently
+ ***************************************************************************/
+void Si5351mcu::setPower(byte clk, byte power) {
+ // set the power to the correct var
+ clkpower[clk] = power;
+
+ // now enable the output to get it applied
+ enable(clk);
+}
+
+
+/****************************************************************************
+ * Private function to send the register data to the Si5351, arduino way.
+ ***************************************************************************/
+void Si5351mcu::i2cWrite(byte regist, byte value){
+ Wire.beginTransmission(SIADDR);
+ Wire.write(regist);
+ Wire.write(value);
+ Wire.endTransmission();
+}
diff --git a/3rdparty/Si5351mcu/src/si5351mcu.h b/3rdparty/Si5351mcu/src/si5351mcu.h
new file mode 100644
index 00000000..b5789c2b
--- /dev/null
+++ b/3rdparty/Si5351mcu/src/si5351mcu.h
@@ -0,0 +1,141 @@
+/*
+ * si5351mcu - Si5351 library for Arduino MCU tuned for size and click-less
+ *
+ * Copyright (C) 2017 Pavel Milanes
+ *
+ * Many chunk of codes are derived-from/copied from other libs
+ * all GNU GPL licenced:
+ * - Linux Kernel (www.kernel.org)
+ * - Hans Summers libs and demo code (qrp-labs.com)
+ * - Etherkit (NT7S) Si5351 libs on github
+ * - DK7IH example.
+ * - Jerry Gaffke integer routines for the bitx20 group
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+/****************************************************************************
+ * This lib tricks:
+ *
+ * CLK0 will use PLLA
+ * CLK1 will use PLLB
+ * CLK2 will use PLLB
+ *
+ * Lib defaults
+ * - XTAL is 27 Mhz.
+ * - Always put the internal 8pF across the xtal legs to GND
+ * - lowest power output (2mA)
+ * - After the init all outputs are off, you need to enable them in your code.
+ *
+ * The correction procedure is not for the PPM as other libs, this
+ * is just +/- Hz to the XTAL freq, you may get a click noise after
+ * applying a correction
+ *
+ * The init procedure is mandatory as it set the Xtal (the default or a custom
+ * one) and prepare the Wire (I2C) library for operation.
+ ****************************************************************************/
+
+#ifndef SI5351MCU_H
+#define SI5351MCU_H
+
+// rigor includes
+#include "Arduino.h"
+#include "Wire.h"
+
+// default I2C address of the Si5351
+#define SIADDR 0x60
+
+// register's power modifiers
+#define SIOUT_2mA 0
+#define SIOUT_4mA 1
+#define SIOUT_6mA 2
+#define SIOUT_8mA 3
+
+// registers base (2mA by default)
+#define SICLK0_R 76 // 0b1001100
+#define SICLK12_R 108 // 0b1101100
+
+
+class Si5351mcu {
+ public:
+ // default init procedure
+ void init(void);
+
+ // custom init procedure (XTAL in Hz);
+ void init(uint32_t);
+
+ // reset all PLLs
+ void reset(void);
+
+ // set CLKx(0..2) to freq (Hz)
+ void setFreq(uint8_t, uint32_t);
+
+ // pass a correction factor
+ void correction(int32_t);
+
+ // enable some CLKx output
+ void enable(uint8_t);
+
+ // disable some CLKx output
+ void disable(uint8_t);
+
+ // disable all outputs
+ void off(void);
+
+ // set power output to a specific clk
+ void setPower(uint8_t, uint8_t);
+
+ // var to check the clock state
+ bool clkOn[3] = {0, 0, 0};
+
+
+ private:
+ // used to talk with the chip, via Arduino Wire lib
+ void i2cWrite(uint8_t, uint8_t);
+
+ // base xtal freq, over this we apply the correction factor
+ // by default 27 MHz
+ uint32_t base_xtal = 27000000L;
+
+ // this is the work value, with the correction applied
+ // via the correction() procedure
+ uint32_t int_xtal = base_xtal;
+
+ // clk# power holders (2ma by default)
+ uint8_t clkpower[3] = {0, 0, 0};
+
+ // local var to keep track of when to reset the "pll"
+ /*********************************************************
+ * BAD CONCEPT on the datasheet and AN:
+ *
+ * The chip has a soft-reset for PLL A & B but in
+ * practice the PLL does not need to be reseted.
+ *
+ * Test shows that if you fix the Msynth output
+ * dividers and move any of the VCO from bottom to top
+ * the frequency moves smooth and clean, no reset needed
+ *
+ * The reset is needed when you changes the value of the
+ * Msynth output divider, even so it's not always needed
+ * so we use this var to keep track of all three and only
+ * reset the "PLL" when this value changes to be sure
+ *
+ * It's a word (16 bit) because the final max value is 900
+ *********************************************************/
+ uint16_t omsynth[3] = {0, 0, 0};
+};
+
+
+#endif //SI5351MCU_H
diff --git a/3rdparty/Si5351mcu/version b/3rdparty/Si5351mcu/version
new file mode 100644
index 00000000..ee6cdce3
--- /dev/null
+++ b/3rdparty/Si5351mcu/version
@@ -0,0 +1 @@
+0.6.1
diff --git a/3rdparty/WebSockets/LICENSE b/3rdparty/WebSockets/LICENSE
new file mode 100644
index 00000000..f166cc57
--- /dev/null
+++ b/3rdparty/WebSockets/LICENSE
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
\ No newline at end of file
diff --git a/3rdparty/WebSockets/README.md b/3rdparty/WebSockets/README.md
new file mode 100644
index 00000000..63eef3e2
--- /dev/null
+++ b/3rdparty/WebSockets/README.md
@@ -0,0 +1,98 @@
+WebSocket Server and Client for Arduino [](https://travis-ci.org/Links2004/arduinoWebSockets)
+===========================================
+
+a WebSocket Server and Client for Arduino based on RFC6455.
+
+
+##### Supported features of RFC6455 #####
+ - text frame
+ - binary frame
+ - connection close
+ - ping
+ - pong
+ - continuation frame
+
+##### Limitations #####
+ - max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define
+ - max output length has no limit (the hardware is the limit)
+ - Client send big frames with mask 0x00000000 (on AVR all frames)
+ - continuation frame reassembly need to be handled in the application code
+
+ ##### Limitations for Async #####
+ - Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround.
+ - wss / SSL is not possible.
+
+##### Supported Hardware #####
+ - ESP8266 [Arduino for ESP8266](https://github.com/esp8266/Arduino/)
+ - ESP32 [Arduino for ESP32](https://github.com/espressif/arduino-esp32)
+ - ESP31B
+ - Particle with STM32 ARM Cortex M3
+ - ATmega328 with Ethernet Shield (ATmega branch)
+ - ATmega328 with enc28j60 (ATmega branch)
+ - ATmega2560 with Ethernet Shield (ATmega branch)
+ - ATmega2560 with enc28j60 (ATmega branch)
+
+###### Note: ######
+
+ version 2.0 and up is not compatible with AVR/ATmega, check ATmega branch.
+
+ Arduino for AVR not supports std namespace of c++.
+
+### wss / SSL ###
+ supported for:
+ - wss client on the ESP8266
+ - wss / SSL is not natively supported in WebSocketsServer however it is possible to achieve secure websockets
+ by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a
+ sample Nginx server configuration file to enable this.
+
+### ESP Async TCP ###
+
+This libary can run in Async TCP mode on the ESP.
+
+The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define).
+
+[ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required.
+
+
+### High Level Client API ###
+
+ - `begin` : Initiate connection sequence to the websocket host.
+```
+void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
+void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
+ ```
+ - `onEvent`: Callback to handle for websocket events
+
+ ```
+ void onEvent(WebSocketClientEvent cbEvent);
+ ```
+
+ - `WebSocketClientEvent`: Handler for websocket events
+ ```
+ void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length)
+ ```
+Where `WStype_t type` is defined as:
+ ```
+ 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;
+ ```
+
+### Issues ###
+Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues
+
+[](https://gitter.im/Links2004/arduinoWebSockets?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+### License and credits ###
+
+The library is licensed under [LGPLv2.1](https://github.com/Links2004/arduinoWebSockets/blob/master/LICENSE)
+
+[libb64](http://libb64.sourceforge.net/) written by Chris Venter. It is distributed under Public Domain see [LICENSE](https://github.com/Links2004/arduinoWebSockets/blob/master/src/libb64/LICENSE).
diff --git a/3rdparty/WebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf b/3rdparty/WebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf
new file mode 100644
index 00000000..ec5aa89f
--- /dev/null
+++ b/3rdparty/WebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf
@@ -0,0 +1,83 @@
+# ESP8266 nginx SSL reverse proxy configuration file (tested and working on nginx v1.10.0)
+
+# proxy cache location
+proxy_cache_path /opt/etc/nginx/cache levels=1:2 keys_zone=ESP8266_cache:10m max_size=10g inactive=5m use_temp_path=off;
+
+# webserver proxy
+server {
+
+ # general server parameters
+ listen 50080;
+ server_name myDomain.net;
+ access_log /opt/var/log/nginx/myDomain.net.access.log;
+
+ # SSL configuration
+ ssl on;
+ ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem;
+ ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem;
+ ssl_session_cache builtin:1000 shared:SSL:10m;
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
+ ssl_prefer_server_ciphers on;
+
+ location / {
+
+ # proxy caching configuration
+ proxy_cache ESP8266_cache;
+ proxy_cache_revalidate on;
+ proxy_cache_min_uses 1;
+ proxy_cache_use_stale off;
+ proxy_cache_lock on;
+ # proxy_cache_bypass $http_cache_control;
+ # include the sessionId cookie value as part of the cache key - keeps the cache per user
+ # proxy_cache_key $proxy_host$request_uri$cookie_sessionId;
+
+ # header pass through configuration
+ proxy_set_header Host $host;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+
+ # ESP8266 custom headers which identify to the device that it's running through an SSL proxy
+ proxy_set_header X-SSL On;
+ proxy_set_header X-SSL-WebserverPort 50080;
+ proxy_set_header X-SSL-WebsocketPort 50081;
+
+ # extra debug headers
+ add_header X-Proxy-Cache $upstream_cache_status;
+ add_header X-Forwarded-For $proxy_add_x_forwarded_for;
+
+ # actual proxying configuration
+ proxy_ssl_session_reuse on;
+ # target the IP address of the device with proxy_pass
+ proxy_pass http://192.168.0.20;
+ proxy_read_timeout 90;
+ }
+ }
+
+# websocket proxy
+server {
+
+ # general server parameters
+ listen 50081;
+ server_name myDomain.net;
+ access_log /opt/var/log/nginx/myDomain.net.wss.access.log;
+
+ # SSL configuration
+ ssl on;
+ ssl_certificate /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem;
+ ssl_certificate_key /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem;
+ ssl_session_cache builtin:1000 shared:SSL:10m;
+ ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
+ ssl_ciphers HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
+ ssl_prefer_server_ciphers on;
+
+ location / {
+
+ # websocket upgrade tunnel configuration
+ proxy_pass http://192.168.0.20:81;
+ proxy_http_version 1.1;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection "Upgrade";
+ proxy_read_timeout 86400;
+ }
+ }
diff --git a/3rdparty/WebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino b/3rdparty/WebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino
new file mode 100644
index 00000000..9d49d149
--- /dev/null
+++ b/3rdparty/WebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino
@@ -0,0 +1,84 @@
+/*
+ * WebSocketClientAVR.ino
+ *
+ * Created on: 10.12.2015
+ *
+ */
+
+#include
+
+#include
+#include
+
+#include
+
+
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
+
+// Set the static IP address to use if the DHCP fails to assign
+IPAddress ip(192, 168, 0, 177);
+
+WebSocketsClient webSocket;
+
+
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ Serial.println("[WSc] Disconnected!\n");
+ break;
+ case WStype_CONNECTED:
+ {
+ Serial.print("[WSc] Connected to url: ");
+ Serial.println((char *)payload);
+ // send message to server when Connected
+ webSocket.sendTXT("Connected");
+ }
+ break;
+ case WStype_TEXT:
+ Serial.print("[WSc] get text: ");
+ Serial.println((char *)payload);
+ // send message to server
+ // webSocket.sendTXT("message here");
+ break;
+ case WStype_BIN:
+ Serial.print("[WSc] get binary length: ");
+ Serial.println(length);
+ // hexdump(payload, length);
+
+ // send data to server
+ // webSocket.sendBIN(payload, length);
+ break;
+ }
+
+}
+
+void setup()
+{
+ // Open serial communications and wait for port to open:
+ Serial.begin(115200);
+ while (!Serial) {}
+
+ // start the Ethernet connection:
+ if (Ethernet.begin(mac) == 0) {
+ Serial.println("Failed to configure Ethernet using DHCP");
+ // no point in carrying on, so do nothing forevermore:
+ // try to congifure using IP address instead of DHCP:
+ Ethernet.begin(mac, ip);
+ }
+
+ webSocket.begin("192.168.0.123", 8011);
+ webSocket.onEvent(webSocketEvent);
+
+}
+
+
+void loop()
+{
+ webSocket.loop();
+}
diff --git a/3rdparty/WebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino b/3rdparty/WebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino
new file mode 100644
index 00000000..5e5ead46
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino
@@ -0,0 +1,110 @@
+/*
+ * WebSocketClient.ino
+ *
+ * Created on: 24.05.2015
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+
+WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+#define USE_SERIAL Serial1
+
+void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
+ const uint8_t* src = (const uint8_t*) mem;
+ USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
+ for(uint32_t i = 0; i < len; i++) {
+ if(i % cols == 0) {
+ USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
+ }
+ USE_SERIAL.printf("%02X ", *src);
+ src++;
+ }
+ USE_SERIAL.printf("\n");
+}
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[WSc] Disconnected!\n");
+ break;
+ case WStype_CONNECTED:
+ USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
+
+ // send message to server when Connected
+ webSocket.sendTXT("Connected");
+ break;
+ case WStype_TEXT:
+ USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+ // send message to server
+ // webSocket.sendTXT("message here");
+ break;
+ case WStype_BIN:
+ USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+ hexdump(payload, length);
+
+ // send data to server
+ // webSocket.sendBIN(payload, length);
+ break;
+ case WStype_ERROR:
+ case WStype_FRAGMENT_TEXT_START:
+ case WStype_FRAGMENT_BIN_START:
+ case WStype_FRAGMENT:
+ case WStype_FRAGMENT_FIN:
+ break;
+ }
+
+}
+
+void setup() {
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //Serial.setDebugOutput(true);
+ USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ //WiFi.disconnect();
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ // server address, port and URL
+ webSocket.begin("192.168.0.123", 81, "/");
+
+ // event handler
+ webSocket.onEvent(webSocketEvent);
+
+ // use HTTP Basic Authorization this is optional remove if not needed
+ webSocket.setAuthorization("user", "Password");
+
+ // try ever 5000 again if connection has failed
+ webSocket.setReconnectInterval(5000);
+
+}
+
+void loop() {
+ webSocket.loop();
+}
diff --git a/3rdparty/WebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino b/3rdparty/WebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino
new file mode 100644
index 00000000..9d722427
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino
@@ -0,0 +1,106 @@
+/*
+ * WebSocketClientSSL.ino
+ *
+ * Created on: 10.12.2015
+ *
+ * note SSL is only possible with the ESP8266
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+
+WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+#define USE_SERIAL Serial1
+
+void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
+ const uint8_t* src = (const uint8_t*) mem;
+ USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
+ for(uint32_t i = 0; i < len; i++) {
+ if(i % cols == 0) {
+ USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
+ }
+ USE_SERIAL.printf("%02X ", *src);
+ src++;
+ }
+ USE_SERIAL.printf("\n");
+}
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[WSc] Disconnected!\n");
+ break;
+ case WStype_CONNECTED:
+ {
+ USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
+
+ // send message to server when Connected
+ webSocket.sendTXT("Connected");
+ }
+ break;
+ case WStype_TEXT:
+ USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+ // send message to server
+ // webSocket.sendTXT("message here");
+ break;
+ case WStype_BIN:
+ USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+ hexdump(payload, length);
+
+ // send data to server
+ // webSocket.sendBIN(payload, length);
+ break;
+ case WStype_ERROR:
+ case WStype_FRAGMENT_TEXT_START:
+ case WStype_FRAGMENT_BIN_START:
+ case WStype_FRAGMENT:
+ case WStype_FRAGMENT_FIN:
+ break;
+ }
+
+}
+
+void setup() {
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //Serial.setDebugOutput(true);
+ USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ //WiFi.disconnect();
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ webSocket.beginSSL("192.168.0.123", 81);
+ webSocket.onEvent(webSocketEvent);
+
+}
+
+void loop() {
+ webSocket.loop();
+}
diff --git a/3rdparty/WebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino b/3rdparty/WebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino
new file mode 100644
index 00000000..3e0d4f5b
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino
@@ -0,0 +1,104 @@
+/*
+ * WebSocketServer.ino
+ *
+ * Created on: 22.05.2015
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+
+#include
+
+WiFiMulti WiFiMulti;
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+#define USE_SERIAL Serial1
+
+void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
+ const uint8_t* src = (const uint8_t*) mem;
+ USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
+ for(uint32_t i = 0; i < len; i++) {
+ if(i % cols == 0) {
+ USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
+ }
+ USE_SERIAL.printf("%02X ", *src);
+ src++;
+ }
+ USE_SERIAL.printf("\n");
+}
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[%u] Disconnected!\n", num);
+ break;
+ case WStype_CONNECTED:
+ {
+ IPAddress ip = webSocket.remoteIP(num);
+ USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+
+ // send message to client
+ webSocket.sendTXT(num, "Connected");
+ }
+ break;
+ case WStype_TEXT:
+ USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+
+ // send message to client
+ // webSocket.sendTXT(num, "message here");
+
+ // send data to all connected clients
+ // webSocket.broadcastTXT("message here");
+ break;
+ case WStype_BIN:
+ USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
+ hexdump(payload, length);
+
+ // send message to client
+ // webSocket.sendBIN(num, payload, length);
+ break;
+ case WStype_ERROR:
+ case WStype_FRAGMENT_TEXT_START:
+ case WStype_FRAGMENT_BIN_START:
+ case WStype_FRAGMENT:
+ case WStype_FRAGMENT_FIN:
+ break;
+ }
+
+}
+
+void setup() {
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //Serial.setDebugOutput(true);
+ USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ webSocket.begin();
+ webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+ webSocket.loop();
+}
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino
new file mode 100644
index 00000000..5ee489cd
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino
@@ -0,0 +1,106 @@
+/*
+ * WebSocketClient.ino
+ *
+ * Created on: 24.05.2015
+ *
+ */
+
+#include
+
+#include
+#include
+
+#include
+
+#include
+
+ESP8266WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+#define USE_SERIAL Serial1
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[WSc] Disconnected!\n");
+ break;
+ case WStype_CONNECTED: {
+ USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
+
+ // send message to server when Connected
+ webSocket.sendTXT("Connected");
+ }
+ break;
+ case WStype_TEXT:
+ USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+ // send message to server
+ // webSocket.sendTXT("message here");
+ break;
+ case WStype_BIN:
+ USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+ hexdump(payload, length);
+
+ // send data to server
+ // webSocket.sendBIN(payload, length);
+ break;
+ case WStype_PING:
+ // pong will be send automatically
+ USE_SERIAL.printf("[WSc] get ping\n");
+ break;
+ case WStype_PONG:
+ // answer to a ping we send
+ USE_SERIAL.printf("[WSc] get pong\n");
+ break;
+ }
+
+}
+
+void setup() {
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //Serial.setDebugOutput(true);
+ USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ //WiFi.disconnect();
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ // server address, port and URL
+ webSocket.begin("192.168.0.123", 81, "/");
+
+ // event handler
+ webSocket.onEvent(webSocketEvent);
+
+ // use HTTP Basic Authorization this is optional remove if not needed
+ webSocket.setAuthorization("user", "Password");
+
+ // try ever 5000 again if connection has failed
+ webSocket.setReconnectInterval(5000);
+
+ // start heartbeat (optional)
+ // ping server every 15000 ms
+ // expect pong from server within 3000 ms
+ // consider connection disconnected if pong is not received 2 times
+ webSocket.enableHeartbeat(15000, 3000, 2);
+
+}
+
+void loop() {
+ webSocket.loop();
+}
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino
new file mode 100644
index 00000000..d45060e9
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino
@@ -0,0 +1,88 @@
+/*
+ * WebSocketClientSSL.ino
+ *
+ * Created on: 10.12.2015
+ *
+ * note SSL is only possible with the ESP8266
+ *
+ */
+
+#include
+
+#include
+#include
+
+#include
+
+#include
+
+ESP8266WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+
+#define USE_SERIAL Serial1
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[WSc] Disconnected!\n");
+ break;
+ case WStype_CONNECTED:
+ {
+ USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
+
+ // send message to server when Connected
+ webSocket.sendTXT("Connected");
+ }
+ break;
+ case WStype_TEXT:
+ USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+ // send message to server
+ // webSocket.sendTXT("message here");
+ break;
+ case WStype_BIN:
+ USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+ hexdump(payload, length);
+
+ // send data to server
+ // webSocket.sendBIN(payload, length);
+ break;
+ }
+
+}
+
+void setup() {
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //Serial.setDebugOutput(true);
+ USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ //WiFi.disconnect();
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ webSocket.beginSSL("192.168.0.123", 81);
+ webSocket.onEvent(webSocketEvent);
+
+}
+
+void loop() {
+ webSocket.loop();
+}
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino
new file mode 100644
index 00000000..a952e5de
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino
@@ -0,0 +1,125 @@
+/*
+ * WebSocketClientSocketIO.ino
+ *
+ * Created on: 06.06.2016
+ *
+ */
+
+#include
+
+#include
+#include
+
+#include
+
+#include
+#include
+
+#include
+
+ESP8266WiFiMulti WiFiMulti;
+SocketIOclient socketIO;
+
+#define USE_SERIAL Serial1
+
+void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
+ switch(type) {
+ case sIOtype_DISCONNECT:
+ USE_SERIAL.printf("[IOc] Disconnected!\n");
+ break;
+ case sIOtype_CONNECT:
+ USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload);
+ break;
+ case sIOtype_EVENT:
+ USE_SERIAL.printf("[IOc] get event: %s\n", payload);
+ break;
+ case sIOtype_ACK:
+ USE_SERIAL.printf("[IOc] get ack: %u\n", length);
+ hexdump(payload, length);
+ break;
+ case sIOtype_ERROR:
+ USE_SERIAL.printf("[IOc] get error: %u\n", length);
+ hexdump(payload, length);
+ break;
+ case sIOtype_BINARY_EVENT:
+ USE_SERIAL.printf("[IOc] get binary: %u\n", length);
+ hexdump(payload, length);
+ break;
+ case sIOtype_BINARY_ACK:
+ USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
+ hexdump(payload, length);
+ break;
+ }
+}
+
+void setup() {
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //Serial.setDebugOutput(true);
+ USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ // disable AP
+ if(WiFi.getMode() & WIFI_AP) {
+ WiFi.softAPdisconnect(true);
+ }
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ //WiFi.disconnect();
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ String ip = WiFi.localIP().toString();
+ USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
+
+ // server address, port and URL
+ socketIO.begin("10.11.100.100", 8880);
+
+ // event handler
+ socketIO.onEvent(socketIOEvent);
+}
+
+unsigned long messageTimestamp = 0;
+void loop() {
+ socketIO.loop();
+
+ uint64_t now = millis();
+
+ if(now - messageTimestamp > 2000) {
+ messageTimestamp = now;
+
+ // creat JSON message for Socket.IO (event)
+ DynamicJsonDocument doc(1024);
+ JsonArray array = doc.to();
+
+ // add evnet name
+ // Hint: socket.on('event_name', ....
+ array.add("event_name");
+
+ // add payload (parameters) for the event
+ JsonObject param1 = array.createNestedObject();
+ param1["now"] = now;
+
+ // JSON to String (serializion)
+ String output;
+ serializeJson(doc, output);
+
+ // Send event
+ socketIO.sendEVENT(output);
+
+ // Print JSON for debugging
+ USE_SERIAL.println(output);
+ }
+}
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino
new file mode 100644
index 00000000..a0eb011f
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino
@@ -0,0 +1,149 @@
+/*
+ WebSocketClientStomp.ino
+
+ Example for connecting and maintining a connection with a STOMP websocket connection.
+ In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html).
+
+ Created on: 25.09.2017
+ Author: Martin Becker , Contact: becker@informatik.uni-wuerzburg.de
+*/
+
+// PRE
+
+#define USE_SERIAL Serial
+
+
+// LIBRARIES
+
+#include
+#include
+
+#include
+#include
+
+
+// SETTINGS
+
+const char* wlan_ssid = "yourssid";
+const char* wlan_password = "somepassword";
+
+const char* ws_host = "the.host.net";
+const int ws_port = 80;
+
+// URL for STOMP endpoint.
+// For the default config of Spring's STOMP support, the default URL is "/socketentry/websocket".
+const char* stompUrl = "/socketentry/websocket"; // don't forget the leading "/" !!!
+
+
+// VARIABLES
+
+WebSocketsClient webSocket;
+
+
+// FUNCTIONS
+
+/**
+ * STOMP messages need to be NULL-terminated (i.e., \0 or \u0000).
+ * However, when we send a String or a char[] array without specifying
+ * a length, the size of the message payload is derived by strlen() internally,
+ * thus dropping any NULL values appended to the "msg"-String.
+ *
+ * To solve this, we first convert the String to a NULL terminated char[] array
+ * via "c_str" and set the length of the payload to include the NULL value.
+ */
+void sendMessage(String & msg) {
+ webSocket.sendTXT(msg.c_str(), msg.length() + 1);
+}
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+ switch (type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[WSc] Disconnected!\n");
+ break;
+ case WStype_CONNECTED:
+ {
+ USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
+
+ String msg = "CONNECT\r\naccept-version:1.1,1.0\r\nheart-beat:10000,10000\r\n\r\n";
+ sendMessage(msg);
+ }
+ break;
+ case WStype_TEXT:
+ {
+ // #####################
+ // handle STOMP protocol
+ // #####################
+
+ String text = (char*) payload;
+ USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+ if (text.startsWith("CONNECTED")) {
+
+ // subscribe to some channels
+
+ String msg = "SUBSCRIBE\nid:sub-0\ndestination:/user/queue/messages\n\n";
+ sendMessage(msg);
+ delay(1000);
+
+ // and send a message
+
+ msg = "SEND\ndestination:/app/message\n\n{\"user\":\"esp\",\"message\":\"Hello!\"}";
+ sendMessage(msg);
+ delay(1000);
+
+ } else {
+
+ // do something with messages
+
+ }
+
+ break;
+ }
+ case WStype_BIN:
+ USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+ hexdump(payload, length);
+
+ // send data to server
+ // webSocket.sendBIN(payload, length);
+ break;
+ }
+
+}
+
+void setup() {
+
+ // setup serial
+
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ // USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+
+
+ // connect to WiFi
+
+ USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ...");
+ WiFi.mode(WIFI_STA);
+ WiFi.begin(wlan_ssid, wlan_password);
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ USE_SERIAL.print(".");
+ }
+ USE_SERIAL.println(" success.");
+ USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP());
+
+
+ // connect to websocket
+ webSocket.begin(ws_host, ws_port, stompUrl);
+ webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
+ // webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
+ webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+ webSocket.loop();
+}
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino
new file mode 100644
index 00000000..cb0c45be
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino
@@ -0,0 +1,150 @@
+/*
+ WebSocketClientStompOverSockJs.ino
+
+ Example for connecting and maintining a connection with a SockJS+STOMP websocket connection.
+ In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html).
+
+ Created on: 18.07.2017
+ Author: Martin Becker , Contact: becker@informatik.uni-wuerzburg.de
+*/
+
+// PRE
+
+#define USE_SERIAL Serial
+
+
+// LIBRARIES
+
+#include
+#include
+
+#include
+#include
+
+
+// SETTINGS
+
+const char* wlan_ssid = "yourssid";
+const char* wlan_password = "somepassword";
+
+const char* ws_host = "the.host.net";
+const int ws_port = 80;
+
+// base URL for SockJS (websocket) connection
+// The complete URL will look something like this(cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36):
+// ws://://<3digits>//websocket
+// For the default config of Spring's SockJS/STOMP support, the default base URL is "/socketentry/".
+const char* ws_baseurl = "/socketentry/"; // don't forget leading and trailing "/" !!!
+
+
+// VARIABLES
+
+WebSocketsClient webSocket;
+
+
+// FUNCTIONS
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+ switch (type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[WSc] Disconnected!\n");
+ break;
+ case WStype_CONNECTED:
+ {
+ USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
+ }
+ break;
+ case WStype_TEXT:
+ {
+ // #####################
+ // handle SockJs+STOMP protocol
+ // #####################
+
+ String text = (char*) payload;
+
+ USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+ if (payload[0] == 'h') {
+
+ USE_SERIAL.println("Heartbeat!");
+
+ } else if (payload[0] == 'o') {
+
+ // on open connection
+ char *msg = "[\"CONNECT\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\"]";
+ webSocket.sendTXT(msg);
+
+ } else if (text.startsWith("a[\"CONNECTED")) {
+
+ // subscribe to some channels
+
+ char *msg = "[\"SUBSCRIBE\\nid:sub-0\\ndestination:/user/queue/messages\\n\\n\\u0000\"]";
+ webSocket.sendTXT(msg);
+ delay(1000);
+
+ // and send a message
+
+ msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]";
+ webSocket.sendTXT(msg);
+ delay(1000);
+ }
+
+ break;
+ }
+ case WStype_BIN:
+ USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+ hexdump(payload, length);
+
+ // send data to server
+ // webSocket.sendBIN(payload, length);
+ break;
+ }
+
+}
+
+void setup() {
+
+ // setup serial
+
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ // USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+
+
+ // connect to WiFi
+
+ USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ...");
+ WiFi.mode(WIFI_STA);
+ WiFi.begin(wlan_ssid, wlan_password);
+
+ while (WiFi.status() != WL_CONNECTED) {
+ delay(500);
+ USE_SERIAL.print(".");
+ }
+ USE_SERIAL.println(" success.");
+ USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP());
+
+
+ // #####################
+ // create socket url according to SockJS protocol (cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36)
+ // #####################
+ String socketUrl = ws_baseurl;
+ socketUrl += random(0, 999);
+ socketUrl += "/";
+ socketUrl += random(0, 999999); // should be a random string, but this works (see )
+ socketUrl += "/websocket";
+
+ // connect to websocket
+ webSocket.begin(ws_host, ws_port, socketUrl);
+ webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
+ // webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
+ webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+ webSocket.loop();
+}
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino
new file mode 100644
index 00000000..1ac3002d
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino
@@ -0,0 +1,86 @@
+/*
+ * WebSocketServer.ino
+ *
+ * Created on: 22.05.2015
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+
+ESP8266WiFiMulti WiFiMulti;
+
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+#define USE_SERIAL Serial1
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[%u] Disconnected!\n", num);
+ break;
+ case WStype_CONNECTED:
+ {
+ IPAddress ip = webSocket.remoteIP(num);
+ USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+
+ // send message to client
+ webSocket.sendTXT(num, "Connected");
+ }
+ break;
+ case WStype_TEXT:
+ USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+
+ // send message to client
+ // webSocket.sendTXT(num, "message here");
+
+ // send data to all connected clients
+ // webSocket.broadcastTXT("message here");
+ break;
+ case WStype_BIN:
+ USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
+ hexdump(payload, length);
+
+ // send message to client
+ // webSocket.sendBIN(num, payload, length);
+ break;
+ }
+
+}
+
+void setup() {
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //Serial.setDebugOutput(true);
+ USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ webSocket.begin();
+ webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+ webSocket.loop();
+}
+
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino
new file mode 100644
index 00000000..5fed1a95
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino
@@ -0,0 +1,132 @@
+/*
+ * WebSocketServerAllFunctionsDemo.ino
+ *
+ * Created on: 10.05.2018
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define LED_RED 15
+#define LED_GREEN 12
+#define LED_BLUE 13
+
+#define USE_SERIAL Serial
+
+ESP8266WiFiMulti WiFiMulti;
+
+ESP8266WebServer server(80);
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[%u] Disconnected!\n", num);
+ break;
+ case WStype_CONNECTED: {
+ IPAddress ip = webSocket.remoteIP(num);
+ USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+
+ // send message to client
+ webSocket.sendTXT(num, "Connected");
+ }
+ break;
+ case WStype_TEXT:
+ USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+
+ if(payload[0] == '#') {
+ // we get RGB data
+
+ // decode rgb data
+ uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
+
+ analogWrite(LED_RED, ((rgb >> 16) & 0xFF));
+ analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF));
+ analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF));
+ }
+
+ break;
+ }
+
+}
+
+void setup() {
+ //USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ pinMode(LED_RED, OUTPUT);
+ pinMode(LED_GREEN, OUTPUT);
+ pinMode(LED_BLUE, OUTPUT);
+
+ digitalWrite(LED_RED, 1);
+ digitalWrite(LED_GREEN, 1);
+ digitalWrite(LED_BLUE, 1);
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ // start webSocket server
+ webSocket.begin();
+ webSocket.onEvent(webSocketEvent);
+
+ if(MDNS.begin("esp8266")) {
+ USE_SERIAL.println("MDNS responder started");
+ }
+
+ // handle index
+ server.on("/", []() {
+ // send index.html
+ server.send(200, "text/html", "LED Control:
R: G: B: ");
+ });
+
+ server.begin();
+
+ // Add service to MDNS
+ MDNS.addService("http", "tcp", 80);
+ MDNS.addService("ws", "tcp", 81);
+
+ digitalWrite(LED_RED, 0);
+ digitalWrite(LED_GREEN, 0);
+ digitalWrite(LED_BLUE, 0);
+
+}
+
+unsigned long last_10sec = 0;
+unsigned int counter = 0;
+
+void loop() {
+ unsigned long t = millis();
+ webSocket.loop();
+ server.handleClient();
+
+ if((t - last_10sec) > 10 * 1000) {
+ counter++;
+ bool ping = (counter % 2);
+ int i = webSocket.connectedClients(ping);
+ USE_SERIAL.printf("%d Connected websocket clients ping: %d\n", i, ping);
+ last_10sec = millis();
+ }
+}
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino
new file mode 100644
index 00000000..84c9775d
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino
@@ -0,0 +1,94 @@
+/*
+ * WebSocketServer.ino
+ *
+ * Created on: 22.05.2015
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+
+ESP8266WiFiMulti WiFiMulti;
+
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+#define USE_SERIAL Serial
+
+String fragmentBuffer = "";
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[%u] Disconnected!\n", num);
+ break;
+ case WStype_CONNECTED: {
+ IPAddress ip = webSocket.remoteIP(num);
+ USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+
+ // send message to client
+ webSocket.sendTXT(num, "Connected");
+ }
+ break;
+ case WStype_TEXT:
+ USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+ break;
+ case WStype_BIN:
+ USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
+ hexdump(payload, length);
+ break;
+
+ // Fragmentation / continuation opcode handling
+ // case WStype_FRAGMENT_BIN_START:
+ case WStype_FRAGMENT_TEXT_START:
+ fragmentBuffer = (char*)payload;
+ USE_SERIAL.printf("[%u] get start start of Textfragment: %s\n", num, payload);
+ break;
+ case WStype_FRAGMENT:
+ fragmentBuffer += (char*)payload;
+ USE_SERIAL.printf("[%u] get Textfragment : %s\n", num, payload);
+ break;
+ case WStype_FRAGMENT_FIN:
+ fragmentBuffer += (char*)payload;
+ USE_SERIAL.printf("[%u] get end of Textfragment: %s\n", num, payload);
+ USE_SERIAL.printf("[%u] full frame: %s\n", num, fragmentBuffer.c_str());
+ break;
+ }
+
+}
+
+void setup() {
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //Serial.setDebugOutput(true);
+ USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ webSocket.begin();
+ webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+ webSocket.loop();
+}
+
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino
new file mode 100644
index 00000000..8bc646c4
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino
@@ -0,0 +1,86 @@
+/*
+ * WebSocketServerHttpHeaderValidation.ino
+ *
+ * Created on: 08.06.2016
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+
+ESP8266WiFiMulti WiFiMulti;
+
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+#define USE_SERIAL Serial1
+
+const unsigned long int validSessionId = 12345; //some arbitrary value to act as a valid sessionId
+
+/*
+ * Returns a bool value as an indicator to describe whether a user is allowed to initiate a websocket upgrade
+ * based on the value of a cookie. This function expects the rawCookieHeaderValue to look like this "sessionId=|"
+ */
+bool isCookieValid(String rawCookieHeaderValue) {
+
+ if (rawCookieHeaderValue.indexOf("sessionId") != -1) {
+ String sessionIdStr = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("sessionId=") + 10, rawCookieHeaderValue.indexOf("|"));
+ unsigned long int sessionId = strtoul(sessionIdStr.c_str(), NULL, 10);
+ return sessionId == validSessionId;
+ }
+ return false;
+}
+
+/*
+ * The WebSocketServerHttpHeaderValFunc delegate passed to webSocket.onValidateHttpHeader
+ */
+bool validateHttpHeader(String headerName, String headerValue) {
+
+ //assume a true response for any headers not handled by this validator
+ bool valid = true;
+
+ if(headerName.equalsIgnoreCase("Cookie")) {
+ //if the header passed is the Cookie header, validate it according to the rules in 'isCookieValid' function
+ valid = isCookieValid(headerValue);
+ }
+
+ return valid;
+}
+
+void setup() {
+ // USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //Serial.setDebugOutput(true);
+ USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ //connecting clients must supply a valid session cookie at websocket upgrade handshake negotiation time
+ const char * headerkeys[] = { "Cookie" };
+ size_t headerKeyCount = sizeof(headerkeys) / sizeof(char*);
+ webSocket.onValidateHttpHeader(validateHttpHeader, headerkeys, headerKeyCount);
+ webSocket.begin();
+}
+
+void loop() {
+ webSocket.loop();
+}
+
diff --git a/3rdparty/WebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino b/3rdparty/WebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino
new file mode 100644
index 00000000..8f32e753
--- /dev/null
+++ b/3rdparty/WebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino
@@ -0,0 +1,121 @@
+/*
+ * WebSocketServer_LEDcontrol.ino
+ *
+ * Created on: 26.11.2015
+ *
+ */
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define LED_RED 15
+#define LED_GREEN 12
+#define LED_BLUE 13
+
+#define USE_SERIAL Serial
+
+
+ESP8266WiFiMulti WiFiMulti;
+
+ESP8266WebServer server(80);
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+ switch(type) {
+ case WStype_DISCONNECTED:
+ USE_SERIAL.printf("[%u] Disconnected!\n", num);
+ break;
+ case WStype_CONNECTED: {
+ IPAddress ip = webSocket.remoteIP(num);
+ USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+
+ // send message to client
+ webSocket.sendTXT(num, "Connected");
+ }
+ break;
+ case WStype_TEXT:
+ USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+
+ if(payload[0] == '#') {
+ // we get RGB data
+
+ // decode rgb data
+ uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
+
+ analogWrite(LED_RED, ((rgb >> 16) & 0xFF));
+ analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF));
+ analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF));
+ }
+
+ break;
+ }
+
+}
+
+void setup() {
+ //USE_SERIAL.begin(921600);
+ USE_SERIAL.begin(115200);
+
+ //USE_SERIAL.setDebugOutput(true);
+
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+ USE_SERIAL.println();
+
+ for(uint8_t t = 4; t > 0; t--) {
+ USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+ USE_SERIAL.flush();
+ delay(1000);
+ }
+
+ pinMode(LED_RED, OUTPUT);
+ pinMode(LED_GREEN, OUTPUT);
+ pinMode(LED_BLUE, OUTPUT);
+
+ digitalWrite(LED_RED, 1);
+ digitalWrite(LED_GREEN, 1);
+ digitalWrite(LED_BLUE, 1);
+
+ WiFiMulti.addAP("SSID", "passpasspass");
+
+ while(WiFiMulti.run() != WL_CONNECTED) {
+ delay(100);
+ }
+
+ // start webSocket server
+ webSocket.begin();
+ webSocket.onEvent(webSocketEvent);
+
+ if(MDNS.begin("esp8266")) {
+ USE_SERIAL.println("MDNS responder started");
+ }
+
+ // handle index
+ server.on("/", []() {
+ // send index.html
+ server.send(200, "text/html", "LED Control:
R: G: B: ");
+ });
+
+ server.begin();
+
+ // Add service to MDNS
+ MDNS.addService("http", "tcp", 80);
+ MDNS.addService("ws", "tcp", 81);
+
+ digitalWrite(LED_RED, 0);
+ digitalWrite(LED_GREEN, 0);
+ digitalWrite(LED_BLUE, 0);
+
+}
+
+void loop() {
+ webSocket.loop();
+ server.handleClient();
+}
diff --git a/3rdparty/WebSockets/examples/particle/ParticleWebSocketClient/application.cpp b/3rdparty/WebSockets/examples/particle/ParticleWebSocketClient/application.cpp
new file mode 100644
index 00000000..461228f3
--- /dev/null
+++ b/3rdparty/WebSockets/examples/particle/ParticleWebSocketClient/application.cpp
@@ -0,0 +1,46 @@
+/* To compile using make CLI, create a folder under \firmware\user\applications and copy application.cpp there.
+* Then, copy src files under particleWebSocket folder.
+*/
+
+#include "application.h"
+#include "particleWebSocket/WebSocketsClient.h"
+
+WebSocketsClient webSocket;
+
+void webSocketEvent(WStype_t type, uint8_t* payload, size_t length)
+{
+ switch (type)
+ {
+ case WStype_DISCONNECTED:
+ Serial.printlnf("[WSc] Disconnected!");
+ break;
+ case WStype_CONNECTED:
+ Serial.printlnf("[WSc] Connected to URL: %s", payload);
+ webSocket.sendTXT("Connected\r\n");
+ break;
+ case WStype_TEXT:
+ Serial.printlnf("[WSc] get text: %s", payload);
+ break;
+ case WStype_BIN:
+ Serial.printlnf("[WSc] get binary length: %u", length);
+ break;
+ }
+}
+
+void setup()
+{
+ Serial.begin(9600);
+
+ WiFi.setCredentials("[SSID]", "[PASSWORD]", WPA2, WLAN_CIPHER_AES_TKIP);
+ WiFi.connect();
+
+ webSocket.begin("192.168.1.153", 85, "/ClientService/?variable=Test1212");
+ webSocket.onEvent(webSocketEvent);
+}
+
+void loop()
+{
+ webSocket.sendTXT("Hello world!");
+ delay(500);
+ webSocket.loop();
+}
diff --git a/3rdparty/WebSockets/library.json b/3rdparty/WebSockets/library.json
new file mode 100644
index 00000000..efbc51be
--- /dev/null
+++ b/3rdparty/WebSockets/library.json
@@ -0,0 +1,25 @@
+{
+ "name": "WebSockets",
+ "description": "WebSocket Server and Client for Arduino based on RFC6455",
+ "keywords": "wifi, http, web, server, client, websocket",
+ "authors": [
+ {
+ "name": "Markus Sattler",
+ "url": "https://github.com/Links2004",
+ "maintainer": true
+ }
+ ],
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/Links2004/arduinoWebSockets.git"
+ },
+ "version": "2.1.4",
+ "license": "LGPL-2.1",
+ "export": {
+ "exclude": [
+ "tests"
+ ]
+ },
+ "frameworks": "arduino",
+ "platforms": "atmelavr, espressif8266, espressif32"
+}
diff --git a/3rdparty/WebSockets/library.properties b/3rdparty/WebSockets/library.properties
new file mode 100644
index 00000000..2fe174b9
--- /dev/null
+++ b/3rdparty/WebSockets/library.properties
@@ -0,0 +1,9 @@
+name=WebSockets
+version=2.1.4
+author=Markus Sattler
+maintainer=Markus Sattler
+sentence=WebSockets for Arduino (Server + Client)
+paragraph=use 2.x.x for ESP and 1.3 for AVR
+category=Communication
+url=https://github.com/Links2004/arduinoWebSockets
+architectures=*
diff --git a/3rdparty/WebSockets/src/SocketIOclient.cpp b/3rdparty/WebSockets/src/SocketIOclient.cpp
new file mode 100644
index 00000000..e9467d45
--- /dev/null
+++ b/3rdparty/WebSockets/src/SocketIOclient.cpp
@@ -0,0 +1,174 @@
+/*
+ * SocketIOclient.cpp
+ *
+ * Created on: May 12, 2018
+ * Author: links
+ */
+
+#include "WebSockets.h"
+#include "WebSocketsClient.h"
+#include "SocketIOclient.h"
+
+SocketIOclient::SocketIOclient() {
+}
+
+SocketIOclient::~SocketIOclient() {
+}
+
+void SocketIOclient::begin(const char * host, uint16_t port, const char * url, const char * protocol) {
+ WebSocketsClient::beginSocketIO(host, port, url, protocol);
+ WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
+}
+
+void SocketIOclient::begin(String host, uint16_t port, String url, String protocol) {
+ WebSocketsClient::beginSocketIO(host, port, url, protocol);
+ WebSocketsClient::enableHeartbeat(60 * 1000, 90 * 1000, 5);
+}
+
+/**
+ * set callback function
+ * @param cbEvent SocketIOclientEvent
+ */
+void SocketIOclient::onEvent(SocketIOclientEvent cbEvent) {
+ _cbEvent = cbEvent;
+}
+
+bool SocketIOclient::isConnected(void) {
+ return WebSocketsClient::isConnected();
+}
+
+/**
+ * send text data to client
+ * @param num uint8_t client id
+ * @param payload uint8_t *
+ * @param length size_t
+ * @param headerToPayload bool (see sendFrame for more details)
+ * @return true if ok
+ */
+bool SocketIOclient::sendEVENT(uint8_t * payload, size_t length, bool headerToPayload) {
+ bool ret = false;
+ if(length == 0) {
+ length = strlen((const char *)payload);
+ }
+ if(clientIsConnected(&_client)) {
+ if(!headerToPayload) {
+ // webSocket Header
+ ret = WebSocketsClient::sendFrameHeader(&_client, WSop_text, length + 2, true);
+ // Engine.IO / Socket.IO Header
+ if(ret) {
+ uint8_t buf[3] = { eIOtype_MESSAGE, sIOtype_EVENT, 0x00 };
+ ret = WebSocketsClient::write(&_client, buf, 2);
+ }
+ if(ret && payload && length > 0) {
+ ret = WebSocketsClient::write(&_client, payload, length);
+ }
+ return ret;
+ } else {
+ // TODO implement
+ }
+
+ // return WebSocketsClient::sendFrame(&_client, WSop_text, payload, length, true, true, headerToPayload);
+ }
+ return false;
+}
+
+bool SocketIOclient::sendEVENT(const uint8_t * payload, size_t length) {
+ return sendEVENT((uint8_t *)payload, length);
+}
+
+bool SocketIOclient::sendEVENT(char * payload, size_t length, bool headerToPayload) {
+ return sendEVENT((uint8_t *)payload, length, headerToPayload);
+}
+
+bool SocketIOclient::sendEVENT(const char * payload, size_t length) {
+ return sendEVENT((uint8_t *)payload, length);
+}
+
+bool SocketIOclient::sendEVENT(String & payload) {
+ return sendEVENT((uint8_t *)payload.c_str(), payload.length());
+}
+
+void SocketIOclient::loop(void) {
+ WebSocketsClient::loop();
+ unsigned long t = millis();
+ if((t - _lastConnectionFail) > EIO_HEARTBEAT_INTERVAL) {
+ _lastConnectionFail = t;
+ DEBUG_WEBSOCKETS("[wsIOc] send ping\n");
+ WebSocketsClient::sendTXT(eIOtype_PING);
+ }
+}
+
+void SocketIOclient::handleCbEvent(WStype_t type, uint8_t * payload, size_t length) {
+ switch(type) {
+ case WStype_DISCONNECTED:
+ runIOCbEvent(sIOtype_DISCONNECT, NULL, 0);
+ DEBUG_WEBSOCKETS("[wsIOc] Disconnected!\n");
+ break;
+ case WStype_CONNECTED: {
+ DEBUG_WEBSOCKETS("[wsIOc] Connected to url: %s\n", payload);
+ // send message to server when Connected
+ // Engine.io upgrade confirmation message (required)
+ WebSocketsClient::sendTXT(eIOtype_UPGRADE);
+ runIOCbEvent(sIOtype_CONNECT, payload, length);
+ } break;
+ case WStype_TEXT: {
+ if(length < 1) {
+ break;
+ }
+
+ engineIOmessageType_t eType = (engineIOmessageType_t)payload[0];
+ switch(eType) {
+ case eIOtype_PING:
+ payload[0] = eIOtype_PONG;
+ DEBUG_WEBSOCKETS("[wsIOc] get ping send pong (%s)\n", payload);
+ WebSocketsClient::sendTXT(payload, length, false);
+ break;
+ case eIOtype_PONG:
+ DEBUG_WEBSOCKETS("[wsIOc] get pong\n");
+ break;
+ case eIOtype_MESSAGE: {
+ if(length < 2) {
+ break;
+ }
+ socketIOmessageType_t ioType = (socketIOmessageType_t)payload[1];
+ uint8_t * data = &payload[2];
+ size_t lData = length - 2;
+ switch(ioType) {
+ case sIOtype_EVENT:
+ DEBUG_WEBSOCKETS("[wsIOc] get event (%d): %s\n", lData, data);
+ break;
+ case sIOtype_CONNECT:
+ case sIOtype_DISCONNECT:
+ case sIOtype_ACK:
+ case sIOtype_ERROR:
+ case sIOtype_BINARY_EVENT:
+ case sIOtype_BINARY_ACK:
+ default:
+ DEBUG_WEBSOCKETS("[wsIOc] Socket.IO Message Type %c (%02X) is not implemented\n", ioType, ioType);
+ DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
+ break;
+ }
+
+ runIOCbEvent(ioType, data, lData);
+ } break;
+ case eIOtype_OPEN:
+ case eIOtype_CLOSE:
+ case eIOtype_UPGRADE:
+ case eIOtype_NOOP:
+ default:
+ DEBUG_WEBSOCKETS("[wsIOc] Engine.IO Message Type %c (%02X) is not implemented\n", eType, eType);
+ DEBUG_WEBSOCKETS("[wsIOc] get text: %s\n", payload);
+ break;
+ }
+ } break;
+
+ case WStype_BIN:
+ case WStype_FRAGMENT_TEXT_START:
+ case WStype_FRAGMENT_BIN_START:
+ case WStype_FRAGMENT:
+ case WStype_FRAGMENT_FIN:
+ case WStype_PING:
+ case WStype_PONG:
+ break;
+ }
+}
diff --git a/3rdparty/WebSockets/src/SocketIOclient.h b/3rdparty/WebSockets/src/SocketIOclient.h
new file mode 100644
index 00000000..64b8c87c
--- /dev/null
+++ b/3rdparty/WebSockets/src/SocketIOclient.h
@@ -0,0 +1,80 @@
+/**
+ * SocketIOclient.h
+ *
+ * Created on: May 12, 2018
+ * Author: links
+ */
+
+#ifndef SOCKETIOCLIENT_H_
+#define SOCKETIOCLIENT_H_
+
+#include "WebSockets.h"
+
+#define EIO_HEARTBEAT_INTERVAL 20000
+
+#define EIO_MAX_HEADER_SIZE (WEBSOCKETS_MAX_HEADER_SIZE + 1)
+#define SIO_MAX_HEADER_SIZE (EIO_MAX_HEADER_SIZE + 1)
+
+typedef enum {
+ eIOtype_OPEN = '0', ///< Sent from the server when a new transport is opened (recheck)
+ eIOtype_CLOSE = '1', ///< Request the close of this transport but does not shutdown the connection itself.
+ eIOtype_PING = '2', ///< Sent by the client. Server should answer with a pong packet containing the same data
+ eIOtype_PONG = '3', ///< Sent by the server to respond to ping packets.
+ eIOtype_MESSAGE = '4', ///< actual message, client and server should call their callbacks with the data
+ eIOtype_UPGRADE = '5', ///< Before engine.io switches a transport, it tests, if server and client can communicate over this transport. If this test succeed, the client sends an upgrade packets which requests the server to flush its cache on the old transport and switch to the new transport.
+ eIOtype_NOOP = '6', ///< A noop packet. Used primarily to force a poll cycle when an incoming websocket connection is received.
+} engineIOmessageType_t;
+
+typedef enum {
+ sIOtype_CONNECT = '0',
+ sIOtype_DISCONNECT = '1',
+ sIOtype_EVENT = '2',
+ sIOtype_ACK = '3',
+ sIOtype_ERROR = '4',
+ sIOtype_BINARY_EVENT = '5',
+ sIOtype_BINARY_ACK = '6',
+} socketIOmessageType_t;
+
+class SocketIOclient : protected WebSocketsClient {
+ public:
+#ifdef __AVR__
+ typedef void (*SocketIOclientEvent)(socketIOmessageType_t type, uint8_t * payload, size_t length);
+#else
+ typedef std::function SocketIOclientEvent;
+#endif
+
+ SocketIOclient(void);
+ virtual ~SocketIOclient(void);
+
+ void begin(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
+ void begin(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
+
+ bool isConnected(void);
+
+ void onEvent(SocketIOclientEvent cbEvent);
+
+ bool sendEVENT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
+ bool sendEVENT(const uint8_t * payload, size_t length = 0);
+ bool sendEVENT(char * payload, size_t length = 0, bool headerToPayload = false);
+ bool sendEVENT(const char * payload, size_t length = 0);
+ bool sendEVENT(String & payload);
+
+ void loop(void);
+
+ protected:
+ uint64_t _lastHeartbeat = 0;
+ SocketIOclientEvent _cbEvent;
+ virtual void runIOCbEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
+ if(_cbEvent) {
+ _cbEvent(type, payload, length);
+ }
+ }
+
+ // Handeling events from websocket layer
+ virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
+ handleCbEvent(type, payload, length);
+ }
+ void handleCbEvent(WStype_t type, uint8_t * payload, size_t length);
+};
+
+#endif /* SOCKETIOCLIENT_H_ */
diff --git a/3rdparty/WebSockets/src/WebSockets.cpp b/3rdparty/WebSockets/src/WebSockets.cpp
new file mode 100644
index 00000000..3a90a402
--- /dev/null
+++ b/3rdparty/WebSockets/src/WebSockets.cpp
@@ -0,0 +1,749 @@
+/**
+ * @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
+#endif
+
+extern "C" {
+#ifdef CORE_HAS_LIBB64
+#include
+#else
+#include "libb64/cencode_inc.h"
+#endif
+}
+
+#ifdef ESP8266
+#include
+#elif defined(ESP32)
+#include
+#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 buf uint8_t * ptr to the buffer for writing
+ * @param opcode WSopcode_t
+ * @param length size_t length of the payload
+ * @param mask bool add dummy mask to the frame (needed for web browser)
+ * @param maskkey uint8_t[4] key used for payload
+ * @param fin bool can be used to send data in more then one frame (set fin on the last frame)
+ */
+uint8_t WebSockets::createHeader(uint8_t * headerPtr, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin) {
+ uint8_t headerSize;
+ // calculate header Size
+ if(length < 126) {
+ headerSize = 2;
+ } else if(length < 0xFFFF) {
+ headerSize = 4;
+ } else {
+ headerSize = 10;
+ }
+
+ if(mask) {
+ headerSize += 4;
+ }
+
+ // create header
+
+ // byte 0
+ *headerPtr = 0x00;
+ if(fin) {
+ *headerPtr |= bit(7); ///< set Fin
+ }
+ *headerPtr |= opcode; ///< set opcode
+ headerPtr++;
+
+ // byte 1
+ *headerPtr = 0x00;
+ if(mask) {
+ *headerPtr |= bit(7); ///< set mask
+ }
+
+ if(length < 126) {
+ *headerPtr |= length;
+ headerPtr++;
+ } else if(length < 0xFFFF) {
+ *headerPtr |= 126;
+ headerPtr++;
+ *headerPtr = ((length >> 8) & 0xFF);
+ headerPtr++;
+ *headerPtr = (length & 0xFF);
+ headerPtr++;
+ } else {
+ // Normally we never get here (to less memory)
+ *headerPtr |= 127;
+ headerPtr++;
+ *headerPtr = 0x00;
+ headerPtr++;
+ *headerPtr = 0x00;
+ headerPtr++;
+ *headerPtr = 0x00;
+ headerPtr++;
+ *headerPtr = 0x00;
+ headerPtr++;
+ *headerPtr = ((length >> 24) & 0xFF);
+ headerPtr++;
+ *headerPtr = ((length >> 16) & 0xFF);
+ headerPtr++;
+ *headerPtr = ((length >> 8) & 0xFF);
+ headerPtr++;
+ *headerPtr = (length & 0xFF);
+ headerPtr++;
+ }
+
+ if(mask) {
+ *headerPtr = maskKey[0];
+ headerPtr++;
+ *headerPtr = maskKey[1];
+ headerPtr++;
+ *headerPtr = maskKey[2];
+ headerPtr++;
+ *headerPtr = maskKey[3];
+ headerPtr++;
+ }
+ return headerSize;
+}
+
+/**
+ *
+ * @param client WSclient_t * ptr to the client struct
+ * @param opcode WSopcode_t
+ * @param length size_t length of the payload
+ * @param fin bool can be used to send data in more then one frame (set fin on the last frame)
+ * @return true if ok
+ */
+bool WebSockets::sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length, bool fin) {
+ uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
+ uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
+
+ uint8_t headerSize = createHeader(&buffer[0], opcode, length, client->cIsClient, maskKey, fin);
+
+ if(write(client, &buffer[0], headerSize) != headerSize) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ *
+ * @param client WSclient_t * ptr to the client struct
+ * @param opcode WSopcode_t
+ * @param payload uint8_t * ptr to the payload
+ * @param length size_t length of the payload
+ * @param fin bool can be used to send data in more then one frame (set fin on the last frame)
+ * @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
+ * @return true if ok
+ */
+bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin, bool headerToPayload) {
+ if(client->tcp && !client->tcp->connected()) {
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num);
+ return false;
+ }
+
+ if(client->status != WSC_CONNECTED) {
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num);
+ return false;
+ }
+
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num);
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, client->cIsClient, length, headerToPayload);
+
+ if(opcode == WSop_text) {
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0)));
+ }
+
+ uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
+ uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
+
+ uint8_t headerSize;
+ uint8_t * headerPtr;
+ uint8_t * payloadPtr = payload;
+ bool useInternBuffer = false;
+ bool ret = true;
+
+ // calculate header Size
+ if(length < 126) {
+ headerSize = 2;
+ } else if(length < 0xFFFF) {
+ headerSize = 4;
+ } else {
+ headerSize = 10;
+ }
+
+ if(client->cIsClient) {
+ headerSize += 4;
+ }
+
+#ifdef WEBSOCKETS_USE_BIG_MEM
+ // only for ESP since AVR has less HEAP
+ // try to send data in one TCP package (only if some free Heap is there)
+ if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) {
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
+ uint8_t * dataPtr = (uint8_t *)malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
+ if(dataPtr) {
+ memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
+ headerToPayload = true;
+ useInternBuffer = true;
+ payloadPtr = dataPtr;
+ }
+ }
+#endif
+
+ // set Header Pointer
+ if(headerToPayload) {
+ // calculate offset in payload
+ headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
+ } else {
+ headerPtr = &buffer[0];
+ }
+
+ if(client->cIsClient && useInternBuffer) {
+ // if we use a Intern Buffer we can modify the data
+ // by this fact its possible the do the masking
+ for(uint8_t x = 0; x < sizeof(maskKey); x++) {
+ maskKey[x] = random(0xFF);
+ }
+ }
+
+ createHeader(headerPtr, opcode, length, client->cIsClient, maskKey, fin);
+
+ if(client->cIsClient && useInternBuffer) {
+ uint8_t * dataMaskPtr;
+
+ if(headerToPayload) {
+ dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
+ } else {
+ dataMaskPtr = payloadPtr;
+ }
+
+ for(size_t x = 0; x < length; x++) {
+ dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
+ }
+ }
+
+#ifndef NODEBUG_WEBSOCKETS
+ unsigned long start = micros();
+#endif
+
+ if(headerToPayload) {
+ // header has be added to payload
+ // payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings
+ // offset in payload is calculatetd 14 - headerSize
+ if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
+ ret = false;
+ }
+ } else {
+ // send header
+ if(write(client, &buffer[0], headerSize) != headerSize) {
+ ret = false;
+ }
+
+ if(payloadPtr && length > 0) {
+ // send payload
+ if(write(client, &payloadPtr[0], length) != length) {
+ ret = false;
+ }
+ }
+ }
+
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start));
+
+#ifdef WEBSOCKETS_USE_BIG_MEM
+ if(useInternBuffer && payloadPtr) {
+ free(payloadPtr);
+ }
+#endif
+
+ return ret;
+}
+
+/**
+ * callen when HTTP header is done
+ * @param client WSclient_t * ptr to the client struct
+ */
+void WebSockets::headerDone(WSclient_t * client) {
+ client->status = WSC_CONNECTED;
+ client->cWsRXsize = 0;
+ DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num);
+#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+ client->cHttpLine = "";
+ handleWebsocket(client);
+#endif
+}
+
+/**
+ * handle the WebSocket stream
+ * @param client WSclient_t * ptr to the client struct
+ */
+void WebSockets::handleWebsocket(WSclient_t * client) {
+ if(client->cWsRXsize == 0) {
+ handleWebsocketCb(client);
+ }
+}
+
+/**
+ * wait for
+ * @param client
+ * @param size
+ */
+bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
+ if(!client->tcp || !client->tcp->connected()) {
+ return false;
+ }
+
+ if(size > WEBSOCKETS_MAX_HEADER_SIZE) {
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size);
+ return false;
+ }
+
+ if(client->cWsRXsize >= size) {
+ return true;
+ }
+
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize);
+ readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) {
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok);
+ if(ok) {
+ client->cWsRXsize = size;
+ server->handleWebsocketCb(client);
+ } else {
+ DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num);
+ client->cWsRXsize = 0;
+ // timeout or error
+ server->clientDisconnect(client, 1002);
+ }
+ },
+ this, size, std::placeholders::_1, std::placeholders::_2));
+ return false;
+}
+
+void WebSockets::handleWebsocketCb(WSclient_t * client) {
+ if(!client->tcp || !client->tcp->connected()) {
+ return;
+ }
+
+ uint8_t * buffer = client->cWsHeader;
+
+ WSMessageHeader_t * header = &client->cWsHeaderDecode;
+ uint8_t * payload = NULL;
+
+ uint8_t headerLen = 2;
+
+ if(!handleWebsocketWaitFor(client, headerLen)) {
+ return;
+ }
+
+ // split first 2 bytes in the data
+ header->fin = ((*buffer >> 7) & 0x01);
+ header->rsv1 = ((*buffer >> 6) & 0x01);
+ header->rsv2 = ((*buffer >> 5) & 0x01);
+ header->rsv3 = ((*buffer >> 4) & 0x01);
+ header->opCode = (WSopcode_t)(*buffer & 0x0F);
+ buffer++;
+
+ header->mask = ((*buffer >> 7) & 0x01);
+ header->payloadLen = (WSopcode_t)(*buffer & 0x7F);
+ buffer++;
+
+ if(header->payloadLen == 126) {
+ headerLen += 2;
+ if(!handleWebsocketWaitFor(client, headerLen)) {
+ return;
+ }
+ header->payloadLen = buffer[0] << 8 | buffer[1];
+ buffer += 2;
+ } else if(header->payloadLen == 127) {
+ headerLen += 8;
+ // read 64bit integer as length
+ if(!handleWebsocketWaitFor(client, headerLen)) {
+ return;
+ }
+
+ if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
+ // really too big!
+ header->payloadLen = 0xFFFFFFFF;
+ } else {
+ header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
+ }
+ buffer += 8;
+ }
+
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num);
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode);
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen);
+
+ if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen);
+ clientDisconnect(client, 1009);
+ return;
+ }
+
+ if(header->mask) {
+ headerLen += 4;
+ if(!handleWebsocketWaitFor(client, headerLen)) {
+ return;
+ }
+ header->maskKey = buffer;
+ buffer += 4;
+ }
+
+ if(header->payloadLen > 0) {
+ // if text data we need one more
+ payload = (uint8_t *)malloc(header->payloadLen + 1);
+
+ if(!payload) {
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen);
+ clientDisconnect(client, 1011);
+ return;
+ }
+ readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload));
+ } else {
+ handleWebsocketPayloadCb(client, true, NULL);
+ }
+}
+
+void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) {
+ WSMessageHeader_t * header = &client->cWsHeaderDecode;
+ if(ok) {
+ if(header->payloadLen > 0) {
+ payload[header->payloadLen] = 0x00;
+
+ if(header->mask) {
+ //decode XOR
+ for(size_t i = 0; i < header->payloadLen; i++) {
+ payload[i] = (payload[i] ^ header->maskKey[i % 4]);
+ }
+ }
+ }
+
+ switch(header->opCode) {
+ case WSop_text:
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload);
+ // no break here!
+ case WSop_binary:
+ case WSop_continuation:
+ messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
+ break;
+ case WSop_ping:
+ // send pong back
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ping received (%s)\n", client->num, payload ? (const char *)payload : "");
+ sendFrame(client, WSop_pong, payload, header->payloadLen);
+ messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
+ break;
+ case WSop_pong:
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char *)payload : "");
+ client->pongReceived = true;
+ messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
+ break;
+ case WSop_close: {
+#ifndef NODEBUG_WEBSOCKETS
+ uint16_t reasonCode = 1000;
+ if(header->payloadLen >= 2) {
+ reasonCode = payload[0] << 8 | payload[1];
+ }
+#endif
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", 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));
+}
+
+/**
+ * enable ping/pong heartbeat process
+ * @param client WSclient_t *
+ * @param pingInterval uint32_t how often ping will be sent
+ * @param pongTimeout uint32_t millis after which pong should timout if not received
+ * @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
+ */
+void WebSockets::enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
+ if(client == NULL)
+ return;
+ client->pingInterval = pingInterval;
+ client->pongTimeout = pongTimeout;
+ client->disconnectTimeoutCount = disconnectTimeoutCount;
+ client->pongReceived = false;
+}
+
+/**
+ * handle ping/pong heartbeat timeout process
+ * @param client WSclient_t *
+ */
+void WebSockets::handleHBTimeout(WSclient_t * client) {
+ if(client->pingInterval) { // if heartbeat is enabled
+ uint32_t pi = millis() - client->lastPing;
+
+ if(client->pongReceived) {
+ client->pongTimeoutCount = 0;
+ } else {
+ if(pi > client->pongTimeout) { // pong not received in time
+ client->pongTimeoutCount++;
+ client->lastPing = millis() - client->pingInterval - 500; // force ping on the next run
+
+ DEBUG_WEBSOCKETS("[HBtimeout] pong TIMEOUT! lp=%d millis=%d pi=%d count=%d\n", client->lastPing, millis(), pi, client->pongTimeoutCount);
+
+ if(client->disconnectTimeoutCount && client->pongTimeoutCount >= client->disconnectTimeoutCount) {
+ DEBUG_WEBSOCKETS("[HBtimeout] count=%d, DISCONNECTING\n", client->pongTimeoutCount);
+ clientDisconnect(client);
+ }
+ }
+ }
+ }
+}
diff --git a/3rdparty/WebSockets/src/WebSockets.h b/3rdparty/WebSockets/src/WebSockets.h
new file mode 100644
index 00000000..a70768e7
--- /dev/null
+++ b/3rdparty/WebSockets/src/WebSockets.h
@@ -0,0 +1,331 @@
+/**
+ * @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
+#define bit(b) (1UL << (b)) // Taken directly from Arduino.h
+#else
+#include
+#include
+#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
+#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)
+#define NETWORK_ESP32_ETH (5)
+
+// max size of the WS Message Header
+#define WEBSOCKETS_MAX_HEADER_SIZE (14)
+
+#if !defined(WEBSOCKETS_NETWORK_TYPE)
+// select Network type based
+#if defined(ESP8266) || defined(ESP31B)
+#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
+//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC
+//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
+
+#elif defined(ESP32)
+#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32
+//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32_ETH
+#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
+#elif defined(ESP32)
+#include
+#include
+#elif defined(ESP31B)
+#include
+#else
+#error "network type ESP8266 ASYNC only possible on the ESP mcu!"
+#endif
+
+#include
+#include
+#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
+#else
+#include
+#endif
+#define WEBSOCKETS_NETWORK_CLASS WiFiClient
+#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
+#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
+
+#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
+
+#ifdef STM32_DEVICE
+#define WEBSOCKETS_NETWORK_CLASS TCPClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer
+#else
+#include
+#include
+#define WEBSOCKETS_NETWORK_CLASS EthernetClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
+#endif
+
+#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
+
+#include
+#define WEBSOCKETS_NETWORK_CLASS UIPClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer
+
+#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+
+#include
+#include
+#define WEBSOCKETS_NETWORK_CLASS WiFiClient
+#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
+#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
+
+#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32_ETH)
+
+#include
+#define WEBSOCKETS_NETWORK_CLASS WiFiClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
+
+#else
+#error "no network type selected!"
+#endif
+
+#ifdef WEBSOCKETS_NETWORK_SSL_CLASS
+#define HAS_SSL
+#endif
+
+// moves all Header strings to Flash (~300 Byte)
+#ifdef WEBSOCKETS_SAVE_RAM
+#define WEBSOCKETS_STRING(var) F(var)
+#else
+#define WEBSOCKETS_STRING(var) var
+#endif
+
+typedef enum {
+ WSC_NOT_CONNECTED,
+ WSC_HEADER,
+ WSC_CONNECTED
+} WSclientsStatus_t;
+
+typedef enum {
+ WStype_ERROR,
+ WStype_DISCONNECTED,
+ WStype_CONNECTED,
+ WStype_TEXT,
+ WStype_BIN,
+ WStype_FRAGMENT_TEXT_START,
+ WStype_FRAGMENT_BIN_START,
+ WStype_FRAGMENT,
+ WStype_FRAGMENT_FIN,
+ WStype_PING,
+ WStype_PONG,
+} WStype_t;
+
+typedef enum {
+ WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
+ WSop_text = 0x01, ///< %x1 denotes a text frame
+ WSop_binary = 0x02, ///< %x2 denotes a binary frame
+ ///< %x3-7 are reserved for further non-control frames
+ WSop_close = 0x08, ///< %x8 denotes a connection close
+ WSop_ping = 0x09, ///< %x9 denotes a ping
+ WSop_pong = 0x0A ///< %xA denotes a pong
+ ///< %xB-F are reserved for further control frames
+} WSopcode_t;
+
+typedef struct {
+ bool fin;
+ bool rsv1;
+ bool rsv2;
+ bool rsv3;
+
+ WSopcode_t opCode;
+ bool mask;
+
+ size_t payloadLen;
+
+ uint8_t * maskKey;
+} WSMessageHeader_t;
+
+typedef struct {
+ uint8_t num; ///< connection number
+
+ WSclientsStatus_t status;
+
+ WEBSOCKETS_NETWORK_CLASS * tcp;
+
+ bool isSocketIO; ///< client for socket.io server
+
+#if defined(HAS_SSL)
+ bool isSSL; ///< run in ssl mode
+ WEBSOCKETS_NETWORK_SSL_CLASS * 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
+
+ bool pongReceived;
+ uint32_t pingInterval; // how often ping will be sent, 0 means "heartbeat is not active"
+ uint32_t lastPing; // millis when last pong has been received
+ uint32_t pongTimeout; // interval in millis after which pong is considered to timeout
+ uint8_t disconnectTimeoutCount; // after how many subsequent pong timeouts discconnect will happen, 0 means "do not disconnect"
+ uint8_t pongTimeoutCount; // current pong timeout count
+
+#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+ String cHttpLine; ///< HTTP header lines
+#endif
+
+} WSclient_t;
+
+class WebSockets {
+ protected:
+#ifdef __AVR__
+ typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok);
+#else
+ typedef std::function WSreadWaitCb;
+#endif
+
+ virtual void clientDisconnect(WSclient_t * client) = 0;
+ virtual bool clientIsConnected(WSclient_t * client) = 0;
+
+ void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
+
+ virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0;
+
+ uint8_t createHeader(uint8_t * buf, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin);
+ bool sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length = 0, bool fin = true);
+ bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool fin = true, bool headerToPayload = false);
+
+ void headerDone(WSclient_t * client);
+
+ void handleWebsocket(WSclient_t * client);
+
+ bool handleWebsocketWaitFor(WSclient_t * client, size_t size);
+ void handleWebsocketCb(WSclient_t * client);
+ void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload);
+
+ String acceptKey(String & clientKey);
+ String base64_encode(uint8_t * data, size_t length);
+
+ bool readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb);
+ virtual size_t write(WSclient_t * client, uint8_t * out, size_t n);
+ size_t write(WSclient_t * client, const char * out);
+
+ void enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
+ void handleHBTimeout(WSclient_t * client);
+};
+
+#ifndef UNUSED
+#define UNUSED(var) (void)(var)
+#endif
+#endif /* WEBSOCKETS_H_ */
diff --git a/3rdparty/WebSockets/src/WebSocketsClient.cpp b/3rdparty/WebSockets/src/WebSocketsClient.cpp
new file mode 100644
index 00000000..82277ea7
--- /dev/null
+++ b/3rdparty/WebSockets/src/WebSocketsClient.cpp
@@ -0,0 +1,854 @@
+/**
+ * @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 defined(HAS_SSL)
+ _fingerprint = "";
+ _CA_cert = NULL;
+#endif
+
+ _client.num = 0;
+ _client.status = WSC_NOT_CONNECTED;
+ _client.tcp = NULL;
+#if defined(HAS_SSL)
+ _client.isSSL = false;
+ _client.ssl = NULL;
+#endif
+ _client.cUrl = url;
+ _client.cCode = 0;
+ _client.cIsUpgrade = false;
+ _client.cIsWebsocket = true;
+ _client.cKey = "";
+ _client.cAccept = "";
+ _client.cProtocol = protocol;
+ _client.cExtensions = "";
+ _client.cVersion = 0;
+ _client.base64Authorization = "";
+ _client.plainAuthorization = "";
+ _client.isSocketIO = false;
+
+ _client.lastPing = 0;
+ _client.pongReceived = false;
+ _client.pongTimeoutCount = 0;
+
+#ifdef ESP8266
+ randomSeed(RANDOM_REG32);
+#else
+ // todo find better seed
+ randomSeed(millis());
+#endif
+#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+ asyncConnect();
+#endif
+
+ _lastConnectionFail = 0;
+ _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 defined(HAS_SSL)
+void WebSocketsClient::beginSSL(const char * host, uint16_t port, const char * url, const char * fingerprint, const char * protocol) {
+ begin(host, port, url, protocol);
+ _client.isSSL = true;
+ _fingerprint = fingerprint;
+ _CA_cert = NULL;
+}
+
+void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) {
+ beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str(), protocol.c_str());
+}
+
+void WebSocketsClient::beginSslWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
+ begin(host, port, url, protocol);
+ _client.isSSL = true;
+ _fingerprint = "";
+ _CA_cert = CA_cert;
+}
+#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 defined(HAS_SSL)
+void WebSocketsClient::beginSocketIOSSL(const char * host, uint16_t port, const char * url, const char * protocol) {
+ begin(host, port, url, protocol);
+ _client.isSocketIO = true;
+ _client.isSSL = true;
+ _fingerprint = "";
+}
+
+void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) {
+ beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str());
+}
+
+void WebSocketsClient::beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url, const char * CA_cert, const char * protocol) {
+ begin(host, port, url, protocol);
+ _client.isSocketIO = true;
+ _client.isSSL = true;
+ _fingerprint = "";
+ _CA_cert = CA_cert;
+}
+#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 defined(HAS_SSL)
+ if(_client.isSSL) {
+ DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n");
+ if(_client.ssl) {
+ delete _client.ssl;
+ _client.ssl = NULL;
+ _client.tcp = NULL;
+ }
+ _client.ssl = new WEBSOCKETS_NETWORK_SSL_CLASS();
+ _client.tcp = _client.ssl;
+ if(_CA_cert) {
+ DEBUG_WEBSOCKETS("[WS-Client] setting CA certificate");
+#if defined(ESP32)
+ _client.ssl->setCACert(_CA_cert);
+#elif defined(ESP8266)
+ _client.ssl->setCACert((const uint8_t *)_CA_cert, strlen(_CA_cert) + 1);
+#else
+#error setCACert not implemented
+#endif
+ }
+ } else {
+ DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n");
+ if(_client.tcp) {
+ delete _client.tcp;
+ _client.tcp = NULL;
+ }
+ _client.tcp = new WEBSOCKETS_NETWORK_CLASS();
+ }
+#else
+ _client.tcp = new WEBSOCKETS_NETWORK_CLASS();
+#endif
+
+ if(!_client.tcp) {
+ DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
+ return;
+ }
+#if defined(ESP32)
+ if(_client.tcp->connect(_host.c_str(), _port, WEBSOCKETS_TCP_TIMEOUT)) {
+#else
+ if(_client.tcp->connect(_host.c_str(), _port)) {
+#endif
+ connectedCb();
+ _lastConnectionFail = 0;
+ } else {
+ connectFailedCb();
+ _lastConnectionFail = millis();
+ }
+ } else {
+ handleClientData();
+
+ if(_client.status == WSC_CONNECTED) {
+ handleHBPing();
+ handleHBTimeout(&_client);
+ }
+ }
+}
+#endif
+
+/**
+ * set callback function
+ * @param cbEvent WebSocketServerEvent
+ */
+void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) {
+ _cbEvent = cbEvent;
+}
+
+/**
+ * send text data to client
+ * @param num uint8_t client id
+ * @param payload uint8_t *
+ * @param length size_t
+ * @param headerToPayload bool (see sendFrame for more details)
+ * @return true if ok
+ */
+bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) {
+ if(length == 0) {
+ length = strlen((const char *)payload);
+ }
+ if(clientIsConnected(&_client)) {
+ return sendFrame(&_client, WSop_text, payload, length, true, headerToPayload);
+ }
+ return false;
+}
+
+bool WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) {
+ return sendTXT((uint8_t *)payload, length);
+}
+
+bool WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) {
+ return sendTXT((uint8_t *)payload, length, headerToPayload);
+}
+
+bool WebSocketsClient::sendTXT(const char * payload, size_t length) {
+ return sendTXT((uint8_t *)payload, length);
+}
+
+bool WebSocketsClient::sendTXT(String & payload) {
+ return sendTXT((uint8_t *)payload.c_str(), payload.length());
+}
+
+bool WebSocketsClient::sendTXT(char payload) {
+ uint8_t buf[WEBSOCKETS_MAX_HEADER_SIZE + 2] = { 0x00 };
+ buf[WEBSOCKETS_MAX_HEADER_SIZE] = payload;
+ return sendTXT(buf, 1, true);
+}
+
+/**
+ * send binary data to client
+ * @param num uint8_t client id
+ * @param payload uint8_t *
+ * @param length size_t
+ * @param headerToPayload bool (see sendFrame for more details)
+ * @return true if ok
+ */
+bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) {
+ if(clientIsConnected(&_client)) {
+ return sendFrame(&_client, WSop_binary, payload, length, true, headerToPayload);
+ }
+ return false;
+}
+
+bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
+ return sendBIN((uint8_t *)payload, length);
+}
+
+/**
+ * sends a WS ping to Server
+ * @param payload uint8_t *
+ * @param length size_t
+ * @return true if ping is send out
+ */
+bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) {
+ if(clientIsConnected(&_client)) {
+ bool sent = sendFrame(&_client, WSop_ping, payload, length);
+ if(sent)
+ _client.lastPing = millis();
+ return sent;
+ }
+ return false;
+}
+
+bool WebSocketsClient::sendPing(String & payload) {
+ return sendPing((uint8_t *)payload.c_str(), payload.length());
+}
+
+/**
+ * disconnect one client
+ * @param num uint8_t client id
+ */
+void WebSocketsClient::disconnect(void) {
+ if(clientIsConnected(&_client)) {
+ WebSockets::clientDisconnect(&_client, 1000);
+ }
+}
+
+/**
+ * set the Authorizatio for the http request
+ * @param user const char *
+ * @param password const char *
+ */
+void WebSocketsClient::setAuthorization(const char * user, const char * password) {
+ if(user && password) {
+ String auth = user;
+ auth += ":";
+ auth += password;
+ _client.base64Authorization = base64_encode((uint8_t *)auth.c_str(), auth.length());
+ }
+}
+
+/**
+ * set the Authorizatio for the http request
+ * @param auth const char * base64
+ */
+void WebSocketsClient::setAuthorization(const char * auth) {
+ if(auth) {
+ //_client.base64Authorization = auth;
+ _client.plainAuthorization = auth;
+ }
+}
+
+/**
+ * set extra headers for the http request;
+ * separate headers by "\r\n"
+ * @param extraHeaders const char * extraHeaders
+ */
+void WebSocketsClient::setExtraHeaders(const char * extraHeaders) {
+ _client.extraHeaders = extraHeaders;
+}
+
+/**
+ * set the reconnect Interval
+ * how long to wait after a connection initiate failed
+ * @param time in ms
+ */
+void WebSocketsClient::setReconnectInterval(unsigned long time) {
+ _reconnectInterval = time;
+}
+
+bool WebSocketsClient::isConnected(void) {
+ return (_client.status == WSC_CONNECTED);
+}
+
+//#################################################################################
+//#################################################################################
+//#################################################################################
+
+/**
+ *
+ * @param client WSclient_t * ptr to the client struct
+ * @param opcode WSopcode_t
+ * @param payload uint8_t *
+ * @param length size_t
+ */
+void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
+ WStype_t type = WStype_ERROR;
+
+ UNUSED(client);
+
+ switch(opcode) {
+ case WSop_text:
+ type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
+ break;
+ case WSop_binary:
+ type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
+ break;
+ case WSop_continuation:
+ type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
+ break;
+ case WSop_ping:
+ type = WStype_PING;
+ break;
+ case WSop_pong:
+ type = WStype_PONG;
+ break;
+ case WSop_close:
+ default:
+ break;
+ }
+
+ runCbEvent(type, payload, length);
+}
+
+/**
+ * Disconnect an client
+ * @param client WSclient_t * ptr to the client struct
+ */
+void WebSocketsClient::clientDisconnect(WSclient_t * client) {
+ bool event = false;
+
+#if(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(':') >= 0) {
+ String headerName = headerLine->substring(0, headerLine->indexOf(':'));
+ String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
+
+ // remove space in the beginning (RFC2616)
+ if(headerValue[0] == ' ') {
+ headerValue.remove(0, 1);
+ }
+
+ if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
+ if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) {
+ client->cIsUpgrade = true;
+ }
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
+ if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
+ client->cIsWebsocket = true;
+ }
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) {
+ client->cAccept = headerValue;
+ client->cAccept.trim(); // see rfc6455
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
+ client->cProtocol = headerValue;
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
+ client->cExtensions = headerValue;
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
+ client->cVersion = headerValue.toInt();
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) {
+ if(headerValue.indexOf(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) {
+ if(_client.tcp->available()) {
+ // read not needed data
+ DEBUG_WEBSOCKETS("[WS-Client][handleHeader] still data in buffer (%d), clean up.\n", _client.tcp->available());
+ while(_client.tcp->available() > 0) {
+ _client.tcp->read();
+ }
+ }
+ sendHeader(client);
+ } 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) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+ _client.tcp->setNoDelay(true);
+#endif
+
+#if defined(HAS_SSL)
+ 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;
+ }
+ } else if(_client.isSSL && !_CA_cert) {
+#if defined(wificlientbearssl_h) && !defined(USING_AXTLS) && !defined(wificlientsecure_h)
+ _client.ssl->setInsecure();
+#endif
+ }
+#endif
+
+ // send Header to Server
+ sendHeader(&_client);
+}
+
+void WebSocketsClient::connectFailedCb() {
+ DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Failed\n", _host.c_str(), _port);
+}
+
+#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+
+void WebSocketsClient::asyncConnect() {
+ DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n");
+
+ AsyncClient * tcpclient = new AsyncClient();
+
+ if(!tcpclient) {
+ DEBUG_WEBSOCKETS("[WS-Client] creating AsyncClient class failed!\n");
+ return;
+ }
+
+ tcpclient->onDisconnect([](void * obj, AsyncClient * c) {
+ c->free();
+ delete c;
+ });
+
+ tcpclient->onConnect(std::bind([](WebSocketsClient * ws, AsyncClient * tcp) {
+ ws->_client.tcp = new AsyncTCPbuffer(tcp);
+ if(!ws->_client.tcp) {
+ DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n");
+ ws->connectFailedCb();
+ return;
+ }
+ ws->connectedCb();
+ },
+ this, std::placeholders::_2));
+
+ tcpclient->onError(std::bind([](WebSocketsClient * ws, AsyncClient * tcp) {
+ ws->connectFailedCb();
+
+ // reconnect
+ ws->asyncConnect();
+ },
+ this, std::placeholders::_2));
+
+ if(!tcpclient->connect(_host.c_str(), _port)) {
+ connectFailedCb();
+ delete tcpclient;
+ }
+}
+
+#endif
+
+/**
+ * send heartbeat ping to server in set intervals
+ */
+void WebSocketsClient::handleHBPing() {
+ if(_client.pingInterval == 0)
+ return;
+ uint32_t pi = millis() - _client.lastPing;
+ if(pi > _client.pingInterval) {
+ DEBUG_WEBSOCKETS("[WS-Client] sending HB ping\n");
+ if(sendPing()) {
+ _client.lastPing = millis();
+ _client.pongReceived = false;
+ }
+ }
+}
+
+/**
+ * enable ping/pong heartbeat process
+ * @param pingInterval uint32_t how often ping will be sent
+ * @param pongTimeout uint32_t millis after which pong should timout if not received
+ * @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
+ */
+void WebSocketsClient::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
+ WebSockets::enableHeartbeat(&_client, pingInterval, pongTimeout, disconnectTimeoutCount);
+}
+
+/**
+ * disable ping/pong heartbeat process
+ */
+void WebSocketsClient::disableHeartbeat() {
+ _client.pingInterval = 0;
+}
diff --git a/3rdparty/WebSockets/src/WebSocketsClient.h b/3rdparty/WebSockets/src/WebSocketsClient.h
new file mode 100644
index 00000000..b6fe7387
--- /dev/null
+++ b/3rdparty/WebSockets/src/WebSocketsClient.h
@@ -0,0 +1,145 @@
+/**
+ * @file WebSocketsClient.h
+ * @date 20.05.2015
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the WebSockets for Arduino.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef WEBSOCKETSCLIENT_H_
+#define WEBSOCKETSCLIENT_H_
+
+#include "WebSockets.h"
+
+class WebSocketsClient : protected WebSockets {
+ public:
+#ifdef __AVR__
+ typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
+#else
+ typedef std::function 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 defined(HAS_SSL)
+ 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");
+ void beginSslWithCA(const char * host, uint16_t port, const char * url = "/", const char * CA_cert = NULL, const char * protocol = "arduino");
+#endif
+
+ void beginSocketIO(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
+ void beginSocketIO(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
+
+#if defined(HAS_SSL)
+ void beginSocketIOSSL(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
+ void beginSocketIOSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
+ void beginSocketIOSSLWithCA(const char * host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * CA_cert = NULL, const char * protocol = "arduino");
+#endif
+
+#if(WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+ void loop(void);
+#else
+ // Async interface not need a loop call
+ void loop(void) __attribute__((deprecated)) {}
+#endif
+
+ void onEvent(WebSocketClientEvent cbEvent);
+
+ bool sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
+ bool sendTXT(const uint8_t * payload, size_t length = 0);
+ bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false);
+ bool sendTXT(const char * payload, size_t length = 0);
+ bool sendTXT(String & payload);
+ bool sendTXT(char payload);
+
+ bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
+ bool sendBIN(const uint8_t * payload, size_t length);
+
+ bool sendPing(uint8_t * payload = NULL, size_t length = 0);
+ bool sendPing(String & payload);
+
+ void disconnect(void);
+
+ void setAuthorization(const char * user, const char * password);
+ void setAuthorization(const char * auth);
+
+ void setExtraHeaders(const char * extraHeaders = NULL);
+
+ void setReconnectInterval(unsigned long time);
+
+ void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
+ void disableHeartbeat();
+
+ protected:
+ String _host;
+ uint16_t _port;
+
+ bool isConnected(void);
+
+#if defined(HAS_SSL)
+ String _fingerprint;
+ const char * _CA_cert;
+#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();
+
+ void handleHBPing(); // send ping in specified intervals
+
+#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+ void asyncConnect();
+#endif
+
+ /**
+ * called for sending a Event to the app
+ * @param type WStype_t
+ * @param payload uint8_t *
+ * @param length size_t
+ */
+ virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
+ if(_cbEvent) {
+ _cbEvent(type, payload, length);
+ }
+ }
+};
+
+#endif /* WEBSOCKETSCLIENT_H_ */
diff --git a/3rdparty/WebSockets/src/WebSocketsServer.cpp b/3rdparty/WebSockets/src/WebSocketsServer.cpp
new file mode 100644
index 00000000..633e34c2
--- /dev/null
+++ b/3rdparty/WebSockets/src/WebSocketsServer.cpp
@@ -0,0 +1,852 @@
+/**
+ * @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_ping:
+ type = WStype_PING;
+ break;
+ case WSop_pong:
+ type = WStype_PONG;
+ break;
+ case WSop_close:
+ 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(':') >= 0) {
+ String headerName = headerLine->substring(0, headerLine->indexOf(':'));
+ String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
+
+ // remove space in the beginning (RFC2616)
+ if(headerValue[0] == ' ') {
+ headerValue.remove(0, 1);
+ }
+
+ if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
+ headerValue.toLowerCase();
+ if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) {
+ client->cIsUpgrade = true;
+ }
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
+ if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
+ client->cIsWebsocket = true;
+ }
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
+ client->cVersion = headerValue.toInt();
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) {
+ client->cKey = headerValue;
+ client->cKey.trim(); // see rfc6455
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
+ client->cProtocol = headerValue;
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
+ client->cExtensions = headerValue;
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) {
+ client->base64Authorization = headerValue;
+ } else {
+ client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue);
+ if(_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) {
+ client->cMandatoryHeadersCount++;
+ }
+ }
+
+ } else {
+ DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
+ }
+
+ (*headerLine) = "";
+#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+ client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&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);
+ }
+ }
+}
diff --git a/3rdparty/WebSockets/src/WebSocketsServer.h b/3rdparty/WebSockets/src/WebSocketsServer.h
new file mode 100644
index 00000000..60144a12
--- /dev/null
+++ b/3rdparty/WebSockets/src/WebSocketsServer.h
@@ -0,0 +1,206 @@
+/**
+ * @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 WebSocketServerEvent;
+ typedef std::function 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_ */
diff --git a/3rdparty/WebSockets/src/libb64/AUTHORS b/3rdparty/WebSockets/src/libb64/AUTHORS
new file mode 100644
index 00000000..af687375
--- /dev/null
+++ b/3rdparty/WebSockets/src/libb64/AUTHORS
@@ -0,0 +1,7 @@
+libb64: Base64 Encoding/Decoding Routines
+======================================
+
+Authors:
+-------
+
+Chris Venter chris.venter@gmail.com http://rocketpod.blogspot.com
diff --git a/3rdparty/WebSockets/src/libb64/LICENSE b/3rdparty/WebSockets/src/libb64/LICENSE
new file mode 100644
index 00000000..a6b56069
--- /dev/null
+++ b/3rdparty/WebSockets/src/libb64/LICENSE
@@ -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.
\ No newline at end of file
diff --git a/3rdparty/WebSockets/src/libb64/cdecode.c b/3rdparty/WebSockets/src/libb64/cdecode.c
new file mode 100644
index 00000000..e135da24
--- /dev/null
+++ b/3rdparty/WebSockets/src/libb64/cdecode.c
@@ -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
+#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
diff --git a/3rdparty/WebSockets/src/libb64/cdecode_inc.h b/3rdparty/WebSockets/src/libb64/cdecode_inc.h
new file mode 100644
index 00000000..d0d7f489
--- /dev/null
+++ b/3rdparty/WebSockets/src/libb64/cdecode_inc.h
@@ -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 */
diff --git a/3rdparty/WebSockets/src/libb64/cencode.c b/3rdparty/WebSockets/src/libb64/cencode.c
new file mode 100644
index 00000000..afe1463c
--- /dev/null
+++ b/3rdparty/WebSockets/src/libb64/cencode.c
@@ -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
+#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
diff --git a/3rdparty/WebSockets/src/libb64/cencode_inc.h b/3rdparty/WebSockets/src/libb64/cencode_inc.h
new file mode 100644
index 00000000..c1e3464a
--- /dev/null
+++ b/3rdparty/WebSockets/src/libb64/cencode_inc.h
@@ -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 */
diff --git a/3rdparty/WebSockets/src/libsha1/libsha1.c b/3rdparty/WebSockets/src/libsha1/libsha1.c
new file mode 100644
index 00000000..48f4df5a
--- /dev/null
+++ b/3rdparty/WebSockets/src/libsha1/libsha1.c
@@ -0,0 +1,202 @@
+/* from valgrind tests */
+
+/* ================ sha1.c ================ */
+/*
+SHA-1 in C
+By Steve Reid
+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
+#include
+#include
+
+#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
diff --git a/3rdparty/WebSockets/src/libsha1/libsha1.h b/3rdparty/WebSockets/src/libsha1/libsha1.h
new file mode 100644
index 00000000..ee3718e1
--- /dev/null
+++ b/3rdparty/WebSockets/src/libsha1/libsha1.h
@@ -0,0 +1,21 @@
+/* ================ sha1.h ================ */
+/*
+SHA-1 in C
+By Steve Reid
+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
diff --git a/3rdparty/WebSockets/tests/webSocket.html b/3rdparty/WebSockets/tests/webSocket.html
new file mode 100644
index 00000000..66a27089
--- /dev/null
+++ b/3rdparty/WebSockets/tests/webSocket.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+LED Control:
+
+R:
+G:
+B:
+
+
\ No newline at end of file
diff --git a/3rdparty/WebSockets/tests/webSocketServer/index.js b/3rdparty/WebSockets/tests/webSocketServer/index.js
new file mode 100644
index 00000000..389e1930
--- /dev/null
+++ b/3rdparty/WebSockets/tests/webSocketServer/index.js
@@ -0,0 +1,57 @@
+#!/usr/bin/env node
+var WebSocketServer = require('websocket').server;
+var http = require('http');
+
+var server = http.createServer(function(request, response) {
+ console.log((new Date()) + ' Received request for ' + request.url);
+ response.writeHead(404);
+ response.end();
+});
+server.listen(8011, function() {
+ console.log((new Date()) + ' Server is listening on port 8011');
+});
+
+wsServer = new WebSocketServer({
+ httpServer: server,
+ // You should not use autoAcceptConnections for production
+ // applications, as it defeats all standard cross-origin protection
+ // facilities built into the protocol and the browser. You should
+ // *always* verify the connection's origin and decide whether or not
+ // to accept it.
+ autoAcceptConnections: false
+});
+
+function originIsAllowed(origin) {
+ // put logic here to detect whether the specified origin is allowed.
+ return true;
+}
+
+wsServer.on('request', function(request) {
+
+ if (!originIsAllowed(request.origin)) {
+ // Make sure we only accept requests from an allowed origin
+ request.reject();
+ console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
+ return;
+ }
+
+ var connection = request.accept('arduino', request.origin);
+ console.log((new Date()) + ' Connection accepted.');
+
+ connection.on('message', function(message) {
+ if (message.type === 'utf8') {
+ console.log('Received Message: ' + message.utf8Data);
+ // connection.sendUTF(message.utf8Data);
+ }
+ else if (message.type === 'binary') {
+ console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
+ //connection.sendBytes(message.binaryData);
+ }
+ });
+
+ connection.on('close', function(reasonCode, description) {
+ console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
+ });
+
+ connection.sendUTF("Hallo Client!");
+});
diff --git a/3rdparty/WebSockets/tests/webSocketServer/package.json b/3rdparty/WebSockets/tests/webSocketServer/package.json
new file mode 100644
index 00000000..9538323e
--- /dev/null
+++ b/3rdparty/WebSockets/tests/webSocketServer/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "webSocketServer",
+ "version": "1.0.0",
+ "description": "WebSocketServer for testing",
+ "main": "index.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/Links2004/arduinoWebSockets"
+ },
+ "keywords": [
+ "esp8266",
+ "websocket",
+ "arduino"
+ ],
+ "author": "Markus Sattler",
+ "license": "LGPLv2",
+ "bugs": {
+ "url": "https://github.com/Links2004/arduinoWebSockets/issues"
+ },
+ "homepage": "https://github.com/Links2004/arduinoWebSockets",
+ "dependencies": {
+ "websocket": "^1.0.18"
+ }
+}
diff --git a/3rdparty/WebSockets/travis/common.sh b/3rdparty/WebSockets/travis/common.sh
new file mode 100644
index 00000000..be959faf
--- /dev/null
+++ b/3rdparty/WebSockets/travis/common.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+function build_sketches()
+{
+ local arduino=$1
+ local srcpath=$2
+ local platform=$3
+ local sketches=$(find $srcpath -name *.ino)
+ for sketch in $sketches; do
+ local sketchdir=$(dirname $sketch)
+ if [[ -f "$sketchdir/.$platform.skip" ]]; then
+ echo -e "\n\n ------------ Skipping $sketch ------------ \n\n";
+ continue
+ fi
+ echo -e "\n\n ------------ Building $sketch ------------ \n\n";
+ $arduino --verify $sketch;
+ local result=$?
+ if [ $result -ne 0 ]; then
+ echo "Build failed ($sketch) build verbose..."
+ $arduino --verify --verbose --preserve-temp-files $sketch
+ result=$?
+ fi
+ if [ $result -ne 0 ]; then
+ echo "Build failed ($1) $sketch"
+ return $result
+ fi
+ done
+}
+
+
+function get_core()
+{
+ echo Setup core for $1
+
+ cd $HOME/arduino_ide/hardware
+
+ if [ "$1" = "esp8266" ] ; then
+ mkdir esp8266com
+ cd esp8266com
+ git clone https://github.com/esp8266/Arduino.git esp8266
+ cd esp8266/tools
+ python get.py
+ fi
+
+ if [ "$1" = "esp32" ] ; then
+ mkdir espressif
+ cd espressif
+ git clone https://github.com/espressif/arduino-esp32.git esp32
+ cd esp32/tools
+ python get.py
+ fi
+
+}
diff --git a/GBS_CONTROL.png b/GBS_CONTROL.png
deleted file mode 100644
index 9759b046..00000000
Binary files a/GBS_CONTROL.png and /dev/null differ
diff --git a/Images/GBS_CONTROL.png b/Images/GBS_CONTROL.png
deleted file mode 100644
index 9759b046..00000000
Binary files a/Images/GBS_CONTROL.png and /dev/null differ
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..818433ec
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,674 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+ The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works. By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users. We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors. You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+ To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights. Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received. You must make sure that they, too, receive
+or can get the source code. And you must show them these terms so they
+know their rights.
+
+ Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+ For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software. For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+ Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so. This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software. The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable. Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products. If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+ Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary. To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ TERMS AND CONDITIONS
+
+ 0. Definitions.
+
+ "This License" refers to version 3 of the GNU General Public License.
+
+ "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+ "The Program" refers to any copyrightable work licensed under this
+License. Each licensee is addressed as "you". "Licensees" and
+"recipients" may be individuals or organizations.
+
+ To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy. The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+ A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+ To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+ To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies. Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+ An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License. If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+ 1. Source Code.
+
+ The "source code" for a work means the preferred form of the work
+for making modifications to it. "Object code" means any non-source
+form of a work.
+
+ A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+ The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form. A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+ The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities. However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work. For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+ The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+ The Corresponding Source for a work in source code form is that
+same work.
+
+ 2. Basic Permissions.
+
+ All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met. This License explicitly affirms your unlimited
+permission to run the unmodified Program. The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work. This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+ You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force. You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright. Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+ Conveying under any other circumstances is permitted solely under
+the conditions stated below. Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+ 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+ No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+ When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+ 4. Conveying Verbatim Copies.
+
+ You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+ You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+ 5. Conveying Modified Source Versions.
+
+ You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+ a) The work must carry prominent notices stating that you modified
+ it, and giving a relevant date.
+
+ b) The work must carry prominent notices stating that it is
+ released under this License and any conditions added under section
+ 7. This requirement modifies the requirement in section 4 to
+ "keep intact all notices".
+
+ c) You must license the entire work, as a whole, under this
+ License to anyone who comes into possession of a copy. This
+ License will therefore apply, along with any applicable section 7
+ additional terms, to the whole of the work, and all its parts,
+ regardless of how they are packaged. This License gives no
+ permission to license the work in any other way, but it does not
+ invalidate such permission if you have separately received it.
+
+ d) If the work has interactive user interfaces, each must display
+ Appropriate Legal Notices; however, if the Program has interactive
+ interfaces that do not display Appropriate Legal Notices, your
+ work need not make them do so.
+
+ A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit. Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+ 6. Conveying Non-Source Forms.
+
+ You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+ a) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by the
+ Corresponding Source fixed on a durable physical medium
+ customarily used for software interchange.
+
+ b) Convey the object code in, or embodied in, a physical product
+ (including a physical distribution medium), accompanied by a
+ written offer, valid for at least three years and valid for as
+ long as you offer spare parts or customer support for that product
+ model, to give anyone who possesses the object code either (1) a
+ copy of the Corresponding Source for all the software in the
+ product that is covered by this License, on a durable physical
+ medium customarily used for software interchange, for a price no
+ more than your reasonable cost of physically performing this
+ conveying of source, or (2) access to copy the
+ Corresponding Source from a network server at no charge.
+
+ c) Convey individual copies of the object code with a copy of the
+ written offer to provide the Corresponding Source. This
+ alternative is allowed only occasionally and noncommercially, and
+ only if you received the object code with such an offer, in accord
+ with subsection 6b.
+
+ d) Convey the object code by offering access from a designated
+ place (gratis or for a charge), and offer equivalent access to the
+ Corresponding Source in the same way through the same place at no
+ further charge. You need not require recipients to copy the
+ Corresponding Source along with the object code. If the place to
+ copy the object code is a network server, the Corresponding Source
+ may be on a different server (operated by you or a third party)
+ that supports equivalent copying facilities, provided you maintain
+ clear directions next to the object code saying where to find the
+ Corresponding Source. Regardless of what server hosts the
+ Corresponding Source, you remain obligated to ensure that it is
+ available for as long as needed to satisfy these requirements.
+
+ e) Convey the object code using peer-to-peer transmission, provided
+ you inform other peers where the object code and Corresponding
+ Source of the work are being offered to the general public at no
+ charge under subsection 6d.
+
+ A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+ A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling. In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage. For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product. A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+ "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source. The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+ If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information. But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+ The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed. Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+ Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+ 7. Additional Terms.
+
+ "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law. If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+ When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it. (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.) You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+ Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+ a) Disclaiming warranty or limiting liability differently from the
+ terms of sections 15 and 16 of this License; or
+
+ b) Requiring preservation of specified reasonable legal notices or
+ author attributions in that material or in the Appropriate Legal
+ Notices displayed by works containing it; or
+
+ c) Prohibiting misrepresentation of the origin of that material, or
+ requiring that modified versions of such material be marked in
+ reasonable ways as different from the original version; or
+
+ d) Limiting the use for publicity purposes of names of licensors or
+ authors of the material; or
+
+ e) Declining to grant rights under trademark law for use of some
+ trade names, trademarks, or service marks; or
+
+ f) Requiring indemnification of licensors and authors of that
+ material by anyone who conveys the material (or modified versions of
+ it) with contractual assumptions of liability to the recipient, for
+ any liability that these contractual assumptions directly impose on
+ those licensors and authors.
+
+ All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10. If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term. If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+ If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+ Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+ 8. Termination.
+
+ You may not propagate or modify a covered work except as expressly
+provided under this License. Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+ However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+ Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+ Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License. If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+ 9. Acceptance Not Required for Having Copies.
+
+ You are not required to accept this License in order to receive or
+run a copy of the Program. Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance. However,
+nothing other than this License grants you permission to propagate or
+modify any covered work. These actions infringe copyright if you do
+not accept this License. Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+ 10. Automatic Licensing of Downstream Recipients.
+
+ Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License. You are not responsible
+for enforcing compliance by third parties with this License.
+
+ An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations. If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+ You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License. For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+ 11. Patents.
+
+ A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based. The
+work thus licensed is called the contributor's "contributor version".
+
+ A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version. For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+ Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+ In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement). To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+ If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients. "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+ If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+ A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License. You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+ Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+ 12. No Surrender of Others' Freedom.
+
+ If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all. For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+ 13. Use with the GNU Affero General Public License.
+
+ Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work. The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+ 14. Revised Versions of this License.
+
+ The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation. If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+ If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+ Later license versions may give you additional or different
+permissions. However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+ 15. Disclaimer of Warranty.
+
+ THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. Limitation of Liability.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+ 17. Interpretation of Sections 15 and 16.
+
+ If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+
+Also add information on how to contact you by electronic and paper mail.
+
+ If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+ Copyright (C)
+ This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+ You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+.
+
+ The GNU General Public License does not permit incorporating your program
+into proprietary programs. If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License. But first, please read
+.
diff --git a/PersWiFiManager.cpp b/PersWiFiManager.cpp
new file mode 100644
index 00000000..ba1a4d09
--- /dev/null
+++ b/PersWiFiManager.cpp
@@ -0,0 +1,155 @@
+/* PersWiFiManager
+ version 3.0.1
+ https://r-downing.github.io/PersWiFiManager/
+
+ modified for inclusion in gbs-control
+ see /3rdparty/PersWiFiManager/ for original code and license
+*/
+#if defined(ESP8266)
+#include "PersWiFiManager.h"
+
+// #define WIFI_HTM_PROGMEM
+// #ifdef WIFI_HTM_PROGMEM
+// static const char wifi_htm[] PROGMEM = R"=====( ESP WiFi
+# gbs-control
-
-
-## Features:
+Gbscontrol is an alternative firmware for Tvia Trueview5725 based upscalers / video converter boards.
+Its growing list of features includes:
- very low lag
- sharp and defined upscaling, comparing well to other -expensive- units
- no synchronization loss switching 240p/480i (output runs independent from input, sync to display never drops)
@@ -29,11 +19,16 @@ Various variations are supported, such as the PlayStation 2's VGA modes that run
Gbscontrol is a continuation of previous work by dooklink, mybook4, Ian Stedman and others.
-Bob from RetroRGB did an [overview video on the project](https://www.youtube.com/watch?v=fmfR0XI5czI). This is a highly recommended watch!
-
+Bob from RetroRGB did an overview video on the project. This is a highly recommended watch!
+https://www.youtube.com/watch?v=fmfR0XI5czI
-Head over to the new [documentation](https://ramapcsx2.github.io/gbs-control/) for the setup guide to learn how to build and use it!
+Head over to the wiki for the setup guide to learn how to build and use it!
+https://github.com/ramapcsx2/gbs-control/wiki/Build-the-Hardware
Development threads:
https://shmups.system11.org/viewtopic.php?f=6&t=52172
https://circuit-board.de/forum/index.php/Thread/15601-GBS-8220-Custom-Firmware-in-Arbeit/
+
+If you like gbscontrol, you can now buy me a coffee.
+Cheers! :)
+
diff --git a/Wiki/Arcade-Boards.md b/Wiki/Arcade-Boards.md
deleted file mode 100644
index 6718afcb..00000000
--- a/Wiki/Arcade-Boards.md
+++ /dev/null
@@ -1,42 +0,0 @@
----
-sort: 15
----
-
-# Arcade Boards notes
-
-User @tracedebrake posted some comments regarding the use of arcade boards with the GBS scaler (or any other scaler).
-I've copied them here, so that arcade machine users are aware of what to look out for.
-
-> Hello @xuserv & @ramapcsx2!
->
-> Might I ask, @xuserv, if you have done every recommanded modifications to your GBS 8200, including soldeging a 100ohm resistor from CSYNC to ground & setting all 3 RGB potentiometer to the minimum (left)?
->
-> I'm asking this because normally, most arcade SuperGuns will not properly adjust your RGBS signals to be 75 ohms & 0.7v peak-to-peak levels compatible. Arcade video signals have the same timing as NTSC video but the electrical properties are different to a standard TV/video monitor. It's analog signals with TTL level amplitude (3-5 Vpp) while regular gaming consoles are made for home use so they output at 0.7v pk-to-pk for a 75ohms load (your tv) . Arcade monitors also have a very high inputs impedance to match those TTL voltage levels (1k-10k ohms).
->
-> This is why the GBS8200 has a high input resistor (1k) on CSYNC; it is made for arcade use by connecting your JAMMA harness directly to the RGBS pins.
->
-> By doing the recommanded mods (100ohm resistor + adjusting your pots total left (or removing them)), what you are actually doing is modifing your GBS8200 to be console compatible. If you want to use a SuperGun with a fully modified GBS8200, you need to make sure 1st that your SuperGun properly adjust the RGBS signals to TV levels using a buffer/driver, such as the HAS v3.1
->
-> This is exactly why some people blown their expensive OSSC or Framemeister; their SuperGun wasn't properly adjusting video signals.
-
-
-> The main issues with arcade boards are this:
->
-> - they never ever been designed to be played outside their cabinet
->
-> - there's was no video standard. Full-scale arcades manufacturers had not only the joy to create their own board from ground up but also the one of choosing the right monitor and fine tune it accordingly.
->
-> The JAMMA standard that came at the end of the 80s/early 90s was created to allow arcade center operators to use old cabinets again & again only by changing the game board, controls and cabinet side arts/marquees. But still, since no standard was ever made for video, arcade monitors had those adjustments pots mounted either at the back of the monitor or on an external harness like this one to render the monitor ''compatible'' with the new board. That was a pain to adjust right and sometimes the adjustment was buggy at best. Even the JAMMA standard itself wasn't respected for long: some games were designed to use more buttons than what the standard allowed (exemple Street Figther 2) by using dormant pins on the JAMMA connector or by adding new connectors on their boards; see kick harness.
->
-> All SuperGuns try to adapt that video chaos for what a 75ohm TV needs but it doesn't mean that they are doing it right. There is mostly 3 types of SuperGuns video designs around:
->
-> - Fixed resistor value: You can't just slap some resistors on the RGBS lines and attenuate them that way by hoping to create a voltage divider. RGBS output levels from arcade boards varies too much from a board to an other to use fixed values. Avoid at all cost (exemple: the horrible Vogatek.)
->
-> - Potentiometers: Better than having fixed values but same as above since you don't know for sure what you're doing. Having nice colors on your TV doesn't mean that you aren't flooding your AV equipment protection diodes, if they have any. This solution will mostly kill your AV (TV, scaler) as well in the long run. To me it's an avoid at all cost as well. (exemple: the RetroElectronik Pro gamer)
->
-> - Impedance matching: very high impedance, video-bandwidth amplifiers/buffers ARE NECESSARY to prevent RGB distorsion and provide a steady impedance match for 75 ohms AV inputs. Same with the sync but it needs to be buffered, attenuated and impedance matched as well. This is the only way to go. (exemple: the current HAS 3.1 (almost impossible to get), the future Sentinel and the open-source DIY Thibaut Varènes design)
-
-A small reiteration regarding the "RetroElectronik Pro" as referenced above - By user @cglmrfreeman
->I was using gbs-control directly with the RetroElectronik Pro Supergun and had a lot of noise in the black areas, as well as dark rolling waves across the screen when used with an HDMI converter. These flaws didn't appear using a VGA CRT monitor but I decided to do some testing anyway. Attaching the harness from the GBS unit directly to a JAMMA fingerboard resulted in significant improvements and no noise that I could make out.
->
->As the above states, there was no video standard for arcade boards, and JAMMA is nice, but if you can bypass your supergun, such as/specifically the RetroElectronik Pro, and connect directly into the GBS, I highly recommend that route.
\ No newline at end of file
diff --git a/Wiki/Build-the-Hardware.md b/Wiki/Build-the-Hardware.md
deleted file mode 100644
index be374545..00000000
--- a/Wiki/Build-the-Hardware.md
+++ /dev/null
@@ -1,62 +0,0 @@
----
-sort: 2
----
-
-## Intro
-Gbs-control is a replacement firmware for GBS8200 upscaler boards.
-It runs on the popular ESP8266 microcontroller and uses the Arduino plattform.
-
-GBS8200 upscalers can be bought on Ebay, at prices around $20.
-Ebay also has ESP8266 microcontroller boards for about $4.
-The ones called "Wemos D1" or "NodeMCU" work well and are recommended.
-
-## Basic Install
-You need the GBS upscaler, an ESP8266 board, a bit of cabling and a jumper or wire link for disabling the onboard processor, so that the ESP8266 can take over.
-Power for the ESP8266 can be provided by, for example:
-- using the power supply that powers the GBS > into the ESP8266 boards "Vin" (recommended, white connector next to power input)
-- using the GBS regulated Vcc (3.3V) > into the ESP8266 boards "3.3V" input (not recommended, but photo below shows this)
-
-Connect the ESP8266 board ground to a convenient ground point on the GBS.
-
-Connect the two I2C bus wires (GBS side: SDA and SCL) to the ESP8266 pins of the same name.
-ESP8266 boards do not have standardized pin names, but they follow some naming rules.
-- most boards label SDA and SCL directly
-- if they are not named (Wemos D1 Mini and many others): SCL is "D1", SDA is "D2"
-- if unsure, see if you can find your ESP8266 board [here](https://randomnerdtutorials.com/esp8266-pinout-reference-gpios/)
-- GBS side SDA goes to ESP8266 side SDA, same for SCL
-
-
-
-
-
-
-
-- Use a jumper to bridge the 2 pins below the first programming port (see picture above)
-### Connect DebugPin
-To enable automatic image position and timing adjustment, the ESP8266 needs to measure some timings.
-Carefully solder a wire from the pictured DebugPin to:
-- Wemos D1 / NodeMCU pin "D6"
-- some boards label this pin "D12" or "MISO"
-
-## Software setup
-Next, the ESP8266 needs to be programmed. Head over to the [software setup](./Software-Setup.html) page.
-
-## Troubleshooting
-
-### No Picture
-- Are SDA / SCL reversed? It's safe to reverse them and try again.
-- The debug pin does not have to be connected for gbscontrol to work at a basic level.
-- Forgot to install the jumper that disables the GBS original controller?
-- ~100 Ohm resistor to ground on Sync-in is installed?
-- when using a sync stripper: Is the LM1881 source voltage `5V`?
-
-
-
-```tip
-### To Debug Issues
-The Arduino IDE serial monitor shows debug information at `115200` baud.
-If your ESP8266 is connected to a computer via USB, you can access this serial monitor to find out more about the issue.
-In the Arduino IDE, you need to select an ESP8266 board that matches your hardware (if unsure, select "LOLIN(WEMOS) D1 R2 & mini").
-```
-
-
diff --git a/Wiki/ESP8266-Board-Adapter.md b/Wiki/ESP8266-Board-Adapter.md
deleted file mode 100644
index a9e8c6a5..00000000
--- a/Wiki/ESP8266-Board-Adapter.md
+++ /dev/null
@@ -1,55 +0,0 @@
----
-sort: 6
----
-# ESP8266 adapter
-
-```warning
-The adapter method places the ESP8266 very close to the TiVo5725 scaler chip, which exposes it to electromagnetic interference.
-As a result, the ESP8266 can have issues with its WiFi, resulting in limited access to the web interface.
-As such, I don't recommend using this adapter method.
-Instead, place the ESP8266 module right next to the GBS 8200 board, near the I2C connection headers.
-```
-
-## Old Article
-
-### Overview
-This is a method of mating a GBS board with a common ESP8266 development board, using a socket to attach to the original MCU.
-It provides all the required signals for the ESP8266 to take over control of the scaler.
-
-
-
-
-
-
-
-### Grinding down stand-offs
-
-
-
-
-
-
-
-### Inner socket surface has to be flat and even
-
-
-
-
-
-
-
-### Signal Locations
-
-
-
-
-
-
-
-### Mating Adapter with ESP8266 Dev Board
-
-
-
-
-
-
diff --git a/Wiki/GBS-8200-Variants.md b/Wiki/GBS-8200-Variants.md
deleted file mode 100644
index 9eabcc10..00000000
--- a/Wiki/GBS-8200-Variants.md
+++ /dev/null
@@ -1,49 +0,0 @@
----
-sort: 3
----
-
-# GBS Variations
-## Yellow Button "V5.0" or "2017"
-Those newer GBS-8200 boards have an LDO oscillation problem.
-It works, but there will be a lot of noise in the picture.
-The easy fix is to remove one SMD capacitor, circled in red.
-
-
-
-```tip
-A better fix would be to replace this capacitor with an electrolytic of 22uF to 47uF.
-```
-
-
-
-
-
-
-
-
-[Background](http://www.ti.com/product/LM1117/datasheet/application_and_implementation#snos4127440)
-The chosen SMD capacitor on these boards has far too little capacitance (120nF measured) AND has very little ESR. It causes the LDO to permanently oscillate at around 20Mhz. There is an electrolytic capacitor further down the line that is sufficient for stable operation of the LDO.
-
-## Original GBS-8220
-
-Same LDO oscillation problem, but here it is limited to setups that use a power supply of > 5.0V.
-I get wild oscillation at 7.6V from a common PSOne supply.
-The same C11 removal fix works here as well.
-
-
-
-
-
-
-
-These variants also use a special video driver for the VGA output.
-The driver IC has a charge pump that it uses to generate negative voltage.
-This would be a useful feature for driving YPbPr signals, but here they just seem to dump the current onto the ground plane (from what I can tell at least).
-That charge pump is generating noise however, and since no ferrite bead is used at location R58, a noise pattern easily develops in the image.
-With a bead installed and some extra SMD capacitors, the image is fine again.
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Wiki/Hardware-Mod-Library.md b/Wiki/Hardware-Mod-Library.md
deleted file mode 100644
index 2731a754..00000000
--- a/Wiki/Hardware-Mod-Library.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-sort: 7
----
-
-# Hardware Mods Library
-
-This page lists all of the known modifications which people have attempted on the GBS82XX series boards, what they do, and if they should be performed or not.
-Note that in most cases, gbscontrol will work without any of these modifications.
-
-|Key| |
-|---|---|
-| ! | Essential |
-| O | Optional / Recommended |
-| X | Not needed / Deprecated |
-
-|Mod Description | _GBS8220_|_GBS8200_ |_GBS8200(2017)_| Effect/Issue Fixed by Mod |
-|----------------|----------|---------|---------------|---------------------------|
-|Add 100ohm resistor across sync and ground for RGBs input | ! | ! | ! | Corrects sync level for 75 Ohm sources (such as game consoles) |
-|Replace R34 with 75 Ohm resistor | O |O |O | Permanent 75 Ohm termination, instead of the mod above |
-|Remove C11, optionally replace with 22uF (6.3V to 16V) electrolytic cap | O |O |O | [Stabilizes 1.8V LDO, removes noise from image](./GBS-8200-Variants.html) |
-|Remove the 3 RGB potentiometers, replace with direct link | O | O | O | [Improves Colors](./RGB-Potentiometers.html) |
-|Add 10uf / 22uf ceramic SMD capacitors in parallel to stock ones (x4) C23, C41(alternative: C43), C42, C48 | O |O |O | [Provide local charge reservoir to help power supply bypassing. May reduce noise and waves in the image](./Power-supply-bypass-capacitors.html) |
-|Add a Sync Stripper circuit to RGBs input | O |O |O | Try this if sync is never quite stable |
-|Replace R58 with 120 Ohm ferrite bead | O |X |X | For GBS8220 (2 output version); decouples charge pump |
-|Add 100n cap between R59 and R58 | O |X |X | For GBS8220 (2 output version); decouples charge pump |
-|Replace C33 and C35 with 1nf (1000pf) SMD capacitors (0605/0805 50V C0G) | X |X |X | [Deprecated Sync fix](./Sync-on-Green-Capacitor-Replacements.html) |
-|Foil tape over digital signal lines | X |X |X | This was fixed in software (SDRAM delays) |
-|Replace L1 L2 L3 with 220R 100v ferrite beads | X |X |X | Not recommended until testing has concluded |
diff --git a/Wiki/Inputs.-SCART-adapter,-optional-sync-stripper.md b/Wiki/Inputs.-SCART-adapter,-optional-sync-stripper.md
deleted file mode 100644
index 478d6e6e..00000000
--- a/Wiki/Inputs.-SCART-adapter,-optional-sync-stripper.md
+++ /dev/null
@@ -1,48 +0,0 @@
----
-sort: 8
----
-
-# SCART RGB to VGA adapter
-
-If you want to build an adapter, here is some good inspiration: https://tomdalby.com/other/gbs8200.html Use the "GBS-8200 SCART Circuit v2" picture as a guide and ignore the LM1881 chip, just connect the wires from the 8 pin JST HX connector to the female SCART socket then add the 100 Ohm resistor (if needed, see below for the details).
-
-The ArcadeForge Sync Strike is a solution that will work, if you don't want to build an adapter yourself.
-http://arcadeforge.net/Scaler-and-Strike-Devices/Sync-Strike::15.html?language=en
-
-
-
-```note
-Every GBS board comes with ~500 Ohm termination on the sync input. This termination is meant for (PC) VGA and most arcade boards.
-
-Csync from consoles should be fine without additional termination but the input may not behave well with other kinds of sync.
-
-If you want to use the RGBS input for regular video-level sync sources, the GBS requires one additional 100 Ohm resistor connected to ground from the sync input. Together with the ~500 Ohm factory resistor, this will bring the total termination closer to TV levels (75 Ohms).
-```
-
-
-
-## Using a Sync Stripper
-Compatibility with Sync-on-Composite or Sync-on-Luma can be improved by using a sync stripper, such as the LM1881.
-It is recommended that you build your circuit so that it has a 75 Ohm to ground (termination) resistor on the LM1881 video input, as well as a 470 Ohm series (attenuation) resistor on the sync output. You will no longer need the 100 ohm terminating resistor with this sync stripper built into your SCART adapter.
-The 75 Ohm resistor on the chip's input provides a properly-terminated connection for the source signal.
-The 470 Ohm series resistor on the CSync output of the LM1881 lowers the voltage to safe levels for the GBS.
-
-Your extracted Sync signal can now be connected to the "S" (sync input) pin of the GBS.
-
-User viletim shows a good LM1881 circuit here:
-https://shmups.system11.org/viewtopic.php?f=6&t=55948&p=1153713#p1153713
-https://shmups.system11.org/viewtopic.php?p=1153077#p1153077
-
-
-
-```note
-Note that his circuit omits the _Rset_ resistor and capacitor. This works for CSync generation, but the VSync output will not work. This is not a problem for this application, as we are only interested in the CSync signal.
-
-Also note that the LM1881 requires 5V to operate, but the GBS only makes 3.3V easily available on its headers.
-The 5V power for the LM1881 needs to be sourced elsewhere. The GBS board's power input may be a good option, assuming your DC power supply is decent.
-
-One user has successfully used the sync stripper at just 3.3V with the output 470 Ohm resistor removed.
-This is not recommended though, since the LM1881 will have a hard time with some signals when undervolted like this.
-```
-
-
diff --git a/Wiki/OLED-Add-On-Setup-Info.md b/Wiki/OLED-Add-On-Setup-Info.md
deleted file mode 100644
index e5f4a4f5..00000000
--- a/Wiki/OLED-Add-On-Setup-Info.md
+++ /dev/null
@@ -1,85 +0,0 @@
----
-sort: 9
----
-
-# OLED Addon
-
-
-
-### NOTE: This OLED Add-On is completely optional to GBS-Control. If you choose not to install it, your GBS-Control board will still operate normally.
-
-
-
-
-
-
-```warning
-This addon is not meant to replace the Web UI and was/is just a means to help select the more common settings. This is still a work-in-progress to add more features in the future.
-```
-
-
-
-## Installation:
-- Materials Needed:
- * 1x I2C 0.96" OLED Screen
- * 1x Rotary Encoder (EC11)
- * 2x 0.47uF Capacitors (50V, Electrolytic)
-
-- Schematic:
-
-
-
-```note
-This is just a general/draft "how-to" schematic.
-```
-
-
-
-
-
-## General Usage
-
-After the boot logo splash screen, you'll be presented with a simple menu system.
-
-
-
-
-
-
-
-By rotating left or right on the encoder, the indicator will move up or down. Pressing the encoder will select the option.
- (If the indicator is acting odd, you may have the CLK and DATA pins of the encoder reversed)
-
-Each submenu will have a _BACK_ option which upon selecting will go back to the main menu, for example:
-
-
-
-
-
-
-
-The only menu that this is slightly different is the _Current Settings_ menu. Within this menu you will be able see various specs that are currently being utilized. While in this menu press the encoder once without rotation and this will bring you back to the main menu.
-
-
-
-
-
-
-
-## Additional Information
-
-### OLED:
-- Communication method is very important for this! Please, try to get a hold of an **I2C** not a **SPI** screen.
-- At the moment, this was designed (font/text alignments) for the 0.96" variant of screens. Any other size will produce "interesting" results.
-- Color configurations will be found in various listings/datasheets for screens (BLUE,BLUE/YELLOW,WHITE). While any color configuration will work, it's recommended to stick to a single color option to achieve a cleaner look. Case in point shown below.
-
-
-
-
-
-
-
-### Rotary Encoders:
-- This type of encoder has many applications and benefits to its design. However, one major downside is that it tends to be a _noisy_ device. Along with the ISR and filter the goal is to not have many wrong inputs occur while rotation or selection.
-- Any general EC11 model of encoder will work. Pretty much "EC11 Rotary Encoder" on Amazon/Ebay will yield the correct results. It's a fairly inexpensive and common part
-- Breakout board versions of this part should work, but isn't entirely recommended. Mainly coming down to cost and the fact that there could be many variations of said breakout boards out there. Meaning, potentially differing resistor/capacitor values for the filtering circuit. This *could* lead to disruption of the ISR. Tests were done to ensure the capacitors/resistors worked well together with the ISR. If you choose to use a breakout then omit the capacitors and pull-up resistors.
diff --git a/Wiki/Power-supply-bypass-capacitors.md b/Wiki/Power-supply-bypass-capacitors.md
deleted file mode 100644
index 23750eae..00000000
--- a/Wiki/Power-supply-bypass-capacitors.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-sort: 10
----
-# Power Supply Bypass capacitors
-
-One recommended modification to the stock GBS board is to add more capacitance for power supply bypassing.
-This step can reduce some forms of visible noise.
-
-Forum post: https://shmups.system11.org/viewtopic.php?p=1349983#p1349983
-> Let's settle on 10uF for the large local storage caps, as more capacity seems to be at the upper end of current market offers.
-> We want as high a voltage rating as possible, but that increases size. We want to limit size to 0805, so they can still be soldered besides the 0603 stocks.
-> Ebay has some cheap listings for:
-> 0805: 10V
-> 0603: 6.3V
-> Actual voltages are 3.3V for the main supply (C23 C41 C48), and 1.8V for the digital supply (C42).
-
-Examples of capacitors installed in parallel:
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Wiki/README.md b/Wiki/README.md
deleted file mode 100644
index c8892e96..00000000
--- a/Wiki/README.md
+++ /dev/null
@@ -1,12 +0,0 @@
----
-sort: 1
----
-
-
-# Welcome to the gbs-control wiki!
-
-
-
-{% include list.liquid all=true %}
-
-
\ No newline at end of file
diff --git a/Wiki/RGB-Potentiometers.md b/Wiki/RGB-Potentiometers.md
deleted file mode 100644
index d0466232..00000000
--- a/Wiki/RGB-Potentiometers.md
+++ /dev/null
@@ -1,35 +0,0 @@
----
-sort: 4
----
-
-## RGB Potentiometers
-
-Please turn these 3 potentiometers all the way to the left. This adjusts the colors to the correct level.
-
-
-
-
-
-
-
-The potentiometers should ideally be removed and their old inputs / outputs be bridged.
-This would correct the RGB termination resistance to the TV standard `75` Ohm.
-
-With the potentiometers in the path, that termination is closer to `65` Ohm, which results in a slightly darker picture.
-Removing these can be tricky though, since you can't reach the pins with a clipper. Desoldering is recommended.
-
-
-
-```note
-This is not a necessary modification though. The brightness can be corrected with the software or on the display.
-```
-
-
-
-Here is how the connections can be bridged:
-
-
-
-
-
-
\ No newline at end of file
diff --git a/Wiki/Si5351-Clock-Generator-install-notes.md b/Wiki/Si5351-Clock-Generator-install-notes.md
deleted file mode 100644
index 8e11f68f..00000000
--- a/Wiki/Si5351-Clock-Generator-install-notes.md
+++ /dev/null
@@ -1,45 +0,0 @@
----
-sort: 5
----
-
-# Si5351 Clock Generator install notes
-
-The Si5351 Clock Generator board is controlled by I2C to output precise frequencies from <8KHz up to 150+ MHz. Input VCC is 3-5v and output is 3vpp. The GBS-Control firmware automatically supports this board when installed as follows:
-
-Solder a wire from the centre pad of CLK0 to TrueView pin `40` (PCLKIN). Check for continuity and resistance. Remember only the centre pad for `CLK0` is for signal. In my board I have used prepared AWG32 enamelled copper wire.
-
-With the scaler board oriented with text facing you, the left side of either C47 or C48 can be used for ground when soldered to the first or third pad of CLK2. I used AWG30 Kynar with the smallest amount of exposed wire, flux and a small amount solder.
-
-For powering the clock gen, solder a wire from the positive side of either C47 or C48 to the unlabelled capacitor shown on the clock gen board. It is most likely a filtering capacitor for the clock’s LDO 3.3v output. Again I have used AWG30 Kynar wire and clear varnish for fixation to the PCB.
-
-The Myson Controller (MTV230) is connected via
-- Pin `25` (P1.0) to Si5351 `SCL`
-- Pin `26` (P1.1) to Si5351 `SDA`
-
-This can be achieved by soldering directly to each controller IC leg as shown.
-Alternatively one use the output side (right) of **R10 for SCL** and **R37 for SDA**. Add strain relief or fixation.
-
-You can solder these wires directly to the same SDA/SCL lines on the ESP8266 too.
-
-To test the clock generator is working, open the GBS Control web gui. Go to Preferences and scroll down to Activate FrameTime lock. Press on the FrameTime Lock button and the console will read “Active FrameTime Lock not necessary with external clock gen installed”. Video output should be pristine and free of horizontal tearing when high output resolution presets are used.
-
-
-
-
-
-
-
-
-
-```tip
-If you don't want to place the clock generator to the heat sink, then you can alternatively place it to where the potentiometers used to be. If you do the bridges on the bottom of the GBS board, then this leaves you with some blank space where you can attach the clock generator.
-
-You have to use a small piece of insulating material (I used 2mm and 0.5mm FR4 which is the material circuit boards are made from but without any copper on it) to raise the clock generator up a bit. And preferably use double sided tape for mounting as this can be removed if you have to for any reason. If you remove a little bit of the clock generator board to make it shift closer to pin 40 of the TrueView IC, then the "GND" pad of the clock generator actually matches a GND pad on the GBS board which allows to connect GND by just sticking a wire through both boards. This provides good, low impedance, ground coupling between the two boards.
-
-This is more a technique for advanced builders. If you don't have the right tools, then it is OK if you place the board to the top of the heatsink.
-
-
-
-```
-
-
\ No newline at end of file
diff --git a/Wiki/Software-Setup.md b/Wiki/Software-Setup.md
deleted file mode 100644
index 7db192f7..00000000
--- a/Wiki/Software-Setup.md
+++ /dev/null
@@ -1,185 +0,0 @@
----
-sort: 11
----
-
-# Software Setup
-
-## Arduino Software (tested with 1.8.11)
-Download and install the [Arduino software (IDE)](https://www.arduino.cc/en/Main/Software)
-
-## ESP8266 support for Arduino IDE
-In the Arduino IDE open the preferences dialog and enter the following URL as "Additional Boards Manger URL":
-http://arduino.esp8266.com/stable/package_esp8266com_index.json
-
-
-
-
-
-
-
-Go to __Tools__ > __Board:__ > __Boards Manager ...__, search for "esp8266" and install "esp8266 by ESP8266 Community",
-version 2.6.3
-
-
-
-```warning
-Do not use version `3.0.0` or newer, it is reported to break things!:
-```
-
-
-
-
-
-
-
-
-
-Now go to __Tools__ > __Board:__ and choose your ESP8266 board.
-
-
-
-```note
-If you aren't sure which board you have, select "LOLIN(WEMOS) D1 R2 & mini".
-```
-
-
-
-Set CPU frequency to 160 MHz and Flash size to "4MB (FS:1MB OTA:~1019KB)".
-
-```danger
-(Don't skip this step, or you won't be able to save custom configurations later.)
-```
-
-Set the `IwIP Variant` to `v2 Lower Memory`.
-Plug your board into a free USB port on your PC and select the detected COM port in the Arduino IDE.
-
-### ESP8266 IDE Settings
-
-
-
-
-
-
-
-## Used Libraries
-
-### ESPAsyncTCP and ESPAsyncWebServer by me-no-dev
-
-Download these 2 libraries from Github (Button that looks like this ) and extract to Arduino libraries folder.
-(On Windows: "Documents\Arduino\libraries" or full path: "C:\Users\rama\Documents\Arduino\libraries")
-
-https://github.com/me-no-dev/ESPAsyncTCP
-https://github.com/me-no-dev/ESPAsyncWebServer
-
-Direct Downloads:
-
-
`;
+ }, "");
+ })
+ .then((html) => {
+ GBSControl.ui.wifiStaButton.removeAttribute("disabled");
+
+ if (html.length) {
+ GBSControl.ui.wifiListTable.innerHTML = html;
+ GBSControl.ui.wifiList.removeAttribute("hidden");
+ GBSControl.ui.wifiConnect.setAttribute("hidden", "");
+ }
+ });
+};
+
+const wifiSelectSSID = (event: Event) => {
+ (GBSControl.ui
+ .wifiSSDInput as HTMLInputElement).value = (event.target as HTMLElement).parentElement.getAttribute(
+ "gbs-ssid"
+ );
+ GBSControl.ui.wifiPasswordInput.classList.remove("gbs-wifi__input--error");
+ GBSControl.ui.wifiList.setAttribute("hidden", "");
+ GBSControl.ui.wifiConnect.removeAttribute("hidden");
+};
+
+const wifiSetAPMode = () => {
+ if (GBSControl.wifi.mode === "ap") {
+ return;
+ }
+
+ const formData = new FormData();
+ formData.append("n", "dummy");
+
+ fetch("/wifi/connect", {
+ method: "POST",
+ body: formData,
+ }).then(() => {
+ gbsAlert(
+ "Switching to AP mode. Please connect to gbscontrol SSID and then click OK"
+ )
+ .then(() => {
+ window.location.href = "http://192.168.4.1";
+ })
+ .catch(() => {});
+ });
+};
+
+/** button click management */
+const controlClick = (control: HTMLButtonElement) => () => {
+ const controlKey = control.getAttribute("gbs-control-key");
+ const target = GBSControl.controlKeysMobile[GBSControl.controlKeysMobileMode];
+
+ switch (target.type) {
+ case "loadDoc":
+ loadDoc(target[controlKey]);
+ break;
+ case "loadUser":
+ loadUser(target[controlKey]);
+ break;
+ }
+};
+
+const controlMouseDown = (control: HTMLButtonElement) => () => {
+ clearInterval(control["__interval"]);
+
+ const click = controlClick(control);
+ click();
+ control["__interval"] = setInterval(click, 300);
+};
+
+const controlMouseUp = (control: HTMLButtonElement) => () => {
+ clearInterval(control["__interval"]);
+};
+
+/** inits */
+const initMenuButtons = () => {
+ const menuButtons = nodelistToArray(
+ document.querySelector(".gbs-menu").querySelectorAll("button")
+ );
+ const sections = nodelistToArray(
+ document.querySelectorAll("section")
+ );
+ const scroll = document.querySelector(".gbs-scroll");
+
+ menuButtons.forEach((button) =>
+ button.addEventListener("click", () => {
+ const section = button.getAttribute("gbs-section");
+
+ sections.forEach((section) => section.setAttribute("hidden", ""));
+ document
+ .querySelector(`section[name="${section}"]`)
+ .removeAttribute("hidden");
+
+ menuButtons.forEach((btn) => btn.removeAttribute("active"));
+ button.setAttribute("active", "");
+ scroll.scrollTo(0, 1);
+ })
+ );
+};
+
+const initGBSButtons = () => {
+ const actions = {
+ user: loadUser,
+ action: loadDoc,
+ setSlot,
+ };
+
+ const buttons = nodelistToArray(
+ document.querySelectorAll("[gbs-click]")
+ );
+
+ buttons.forEach((button) => {
+ const clickMode = button.getAttribute("gbs-click");
+ const message = button.getAttribute("gbs-message");
+ const messageType = button.getAttribute("gbs-message-type");
+ const action = actions[messageType];
+
+ if (clickMode === "normal") {
+ button.addEventListener("click", () => {
+ action(message);
+ });
+ }
+
+ if (clickMode === "repeat") {
+ const callback = () => {
+ action(message);
+ };
+
+ button.addEventListener(
+ !("ontouchstart" in window) ? "mousedown" : "touchstart",
+ () => {
+ callback();
+ clearInterval(button["__interval"]);
+ button["__interval"] = setInterval(callback, 300);
+ }
+ );
+ button.addEventListener(
+ !("ontouchstart" in window) ? "mouseup" : "touchend",
+ () => {
+ clearInterval(button["__interval"]);
+ }
+ );
+ }
+ });
+};
+
+const initClearButton = () => {
+ GBSControl.ui.outputClear.addEventListener("click", () => {
+ GBSControl.ui.terminal.value = "";
+ });
+};
+
+const initControlMobileKeys = () => {
+ const controls = document.querySelectorAll("[gbs-control-target]");
+ const controlsKeys = document.querySelectorAll("[gbs-control-key]");
+
+ controls.forEach((control) => {
+ control.addEventListener("click", () => {
+ GBSControl.controlKeysMobileMode = control.getAttribute(
+ "gbs-control-target"
+ );
+ controls.forEach((crtl) => {
+ crtl.removeAttribute("active");
+ });
+ control.setAttribute("active", "");
+ });
+ });
+
+ controlsKeys.forEach((control) => {
+ control.addEventListener(
+ !("ontouchstart" in window) ? "mousedown" : "touchstart",
+ controlMouseDown(control as HTMLButtonElement)
+ );
+ control.addEventListener(
+ !("ontouchstart" in window) ? "mouseup" : "touchend",
+ controlMouseUp(control as HTMLButtonElement)
+ );
+ });
+};
+
+const initLegendHelpers = () => {
+ nodelistToArray(
+ document.querySelectorAll(".gbs-fieldset__legend--help")
+ ).forEach((e) => {
+ e.addEventListener("click", () => {
+ document.body.classList.toggle("gbs-help-hide");
+ });
+ });
+};
+
+const initUnloadListener = () => {
+ window.addEventListener("unload", () => {
+ clearInterval(GBSControl.wsCheckTimer);
+ if (GBSControl.ws) {
+ if (GBSControl.ws.readyState == 0 || GBSControl.ws.readyState == 1) {
+ GBSControl.ws.close();
+ }
+ }
+ });
+};
+
+const initSlotButtons = () => {
+ GBSControl.ui.slotContainer.innerHTML = getSlotsHTML();
+ GBSControl.ui.slotButtonList = nodelistToArray(
+ document.querySelectorAll('[gbs-role="slot"]')
+ ) as HTMLElement[];
+};
+
+const initUIElements = () => {
+ GBSControl.ui = {
+ terminal: document.getElementById("outputTextArea"),
+ webSocketConnectionWarning: document.getElementById("websocketWarning"),
+ presetButtonList: nodelistToArray(
+ document.querySelectorAll("[gbs-role='preset']")
+ ) as HTMLElement[],
+ slotButtonList: nodelistToArray(
+ document.querySelectorAll('[gbs-role="slot"]')
+ ) as HTMLElement[],
+ toggleList: document.querySelectorAll("[gbs-toggle]"),
+ toggleSwichList: document.querySelectorAll("[gbs-toggle-switch]"),
+ wifiList: document.querySelector("[gbs-wifi-list]"),
+ wifiListTable: document.querySelector(".gbs-wifi__list"),
+ wifiConnect: document.querySelector(".gsb-wifi__connect"),
+ wifiConnectButton: document.querySelector("[gbs-wifi-connect-button]"),
+ wifiSSDInput: document.querySelector('[gbs-input="ssid"]'),
+ wifiPasswordInput: document.querySelector('[gbs-input="password"]'),
+ wifiApButton: document.querySelector("[gbs-wifi-ap]"),
+ wifiStaButton: document.querySelector("[gbs-wifi-station]"),
+ wifiStaSSID: document.querySelector("[gbs-wifi-station-ssid]"),
+ loader: document.querySelector(".gbs-loader"),
+ progressBackup: document.querySelector("[gbs-progress-backup]"),
+ progressRestore: document.querySelector("[gbs-progress-restore]"),
+ outputClear: document.querySelector("[gbs-output-clear]"),
+ slotContainer: document.querySelector("[gbs-slot-html]"),
+ backupButton: document.querySelector(".gbs-backup-button"),
+ backupInput: document.querySelector(".gbs-backup-input"),
+ developerSwitch: document.querySelector("[gbs-dev-switch]"),
+ customSlotFilters: document.querySelector("[gbs-slot-custom-filters]"),
+ alert: document.querySelector('section[name="alert"]'),
+ alertOk: document.querySelector("[gbs-alert-ok]"),
+ alertContent: document.querySelector("[gbs-alert-content]"),
+ prompt: document.querySelector('section[name="prompt"]'),
+ promptOk: document.querySelector("[gbs-prompt-ok]"),
+ promptCancel: document.querySelector("[gbs-prompt-cancel]"),
+ promptContent: document.querySelector("[gbs-prompt-content]"),
+ promptInput: document.querySelector('[gbs-input="prompt-input"]'),
+ };
+};
+
+const initGeneralListeners = () => {
+ window.addEventListener("resize", () => {
+ updateViewPort();
+ });
+
+ GBSControl.ui.backupInput.addEventListener("change", (event) => {
+ const fileList: FileList = event.target["files"];
+ readLocalFile(fileList[0]);
+ GBSControl.ui.backupInput.value = "";
+ });
+
+ GBSControl.ui.backupButton.addEventListener("click", doBackup);
+ GBSControl.ui.wifiListTable.addEventListener("click", wifiSelectSSID);
+ GBSControl.ui.wifiConnectButton.addEventListener("click", wifiConnect);
+ GBSControl.ui.wifiApButton.addEventListener("click", wifiSetAPMode);
+ GBSControl.ui.wifiStaButton.addEventListener("click", wifiScanSSID);
+ GBSControl.ui.developerSwitch.addEventListener("click", toggleDeveloperMode);
+ GBSControl.ui.customSlotFilters.addEventListener(
+ "click",
+ toggleCustomSlotFilters
+ );
+
+ GBSControl.ui.alertOk.addEventListener("click", () => {
+ GBSControl.ui.alert.setAttribute("hidden", "");
+ gbsAlertPromise.resolve();
+ });
+
+ GBSControl.ui.promptOk.addEventListener("click", () => {
+ GBSControl.ui.prompt.setAttribute("hidden", "");
+ const value = GBSControl.ui.promptInput.value;
+ if (value !== undefined || value.length > 0) {
+ gbsPromptPromise.resolve(GBSControl.ui.promptInput.value);
+ } else {
+ gbsPromptPromise.reject();
+ }
+ });
+
+ GBSControl.ui.promptCancel.addEventListener("click", () => {
+ GBSControl.ui.prompt.setAttribute("hidden", "");
+ gbsPromptPromise.reject();
+ });
+
+ GBSControl.ui.promptInput.addEventListener("keydown", (event: any) => {
+ if (event.keyCode === 13) {
+ GBSControl.ui.prompt.setAttribute("hidden", "");
+ const value = GBSControl.ui.promptInput.value;
+ if (value !== undefined || value.length > 0) {
+ gbsPromptPromise.resolve(GBSControl.ui.promptInput.value);
+ } else {
+ gbsPromptPromise.reject();
+ }
+ }
+ if (event.keyCode === 27) {
+ gbsPromptPromise.reject();
+ }
+ });
+};
+
+const initDeveloperMode = () => {
+ const devMode = GBSStorage.read("developerMode") as boolean;
+ if (devMode === undefined) {
+ GBSStorage.write("developerMode", false);
+ updateDeveloperMode(false);
+ } else {
+ updateDeveloperMode(devMode);
+ }
+};
+
+const gbsAlertPromise = {
+ resolve: null,
+ reject: null,
+};
+
+const alertKeyListener = (event: any) => {
+ if (event.keyCode === 13) {
+ gbsAlertPromise.resolve();
+ }
+ if (event.keyCode === 27) {
+ gbsAlertPromise.reject();
+ }
+};
+
+const gbsAlert = (text: string) => {
+ GBSControl.ui.alertContent.textContent = text;
+ GBSControl.ui.alert.removeAttribute("hidden");
+ document.addEventListener("keyup", alertKeyListener);
+ return new Promise((resolve, reject) => {
+ gbsAlertPromise.resolve = (e) => {
+ document.removeEventListener("keyup", alertKeyListener);
+ GBSControl.ui.alert.setAttribute("hidden", "");
+ return resolve(e);
+ };
+ gbsAlertPromise.reject = () => {
+ document.removeEventListener("keyup", alertKeyListener);
+ GBSControl.ui.alert.setAttribute("hidden", "");
+ return reject();
+ };
+ });
+};
+
+const gbsPromptPromise = {
+ resolve: null,
+ reject: null,
+};
+
+const gbsPrompt = (text: string, defaultValue = "") => {
+ GBSControl.ui.promptContent.textContent = text;
+ GBSControl.ui.prompt.removeAttribute("hidden");
+ GBSControl.ui.promptInput.value = defaultValue;
+
+ return new Promise((resolve, reject) => {
+ gbsPromptPromise.resolve = resolve;
+ gbsPromptPromise.reject = reject;
+ GBSControl.ui.promptInput.focus();
+ });
+};
+
+const initUI = () => {
+ updateCustomSlotFilters();
+ initGeneralListeners();
+ updateViewPort();
+ initSlotButtons();
+ initLegendHelpers();
+ initMenuButtons();
+ initGBSButtons();
+ initClearButton();
+ initControlMobileKeys();
+ initUnloadListener();
+ initDeveloperMode();
+};
+
+const main = () => {
+ const ip = location.hostname;
+ GBSControl.serverIP = ip;
+ GBSControl.webSocketServerUrl = `ws://${ip}:81/`;
+ document
+ .querySelector(".gbs-loader img")
+ .setAttribute(
+ "src",
+ document.head
+ .querySelector('[rel="apple-touch-icon"]')
+ .getAttribute("href")
+ );
+ fetchSlotNamesAndInit();
+};
+
+main();
diff --git a/public/src/manifest.json b/public/src/manifest.json
new file mode 100644
index 00000000..776c68da
--- /dev/null
+++ b/public/src/manifest.json
@@ -0,0 +1,26 @@
+{
+ "short_name": "GBSControl",
+ "name": "GBSControl",
+ "lang": "en",
+ "description": "GBSControl Remote",
+ "background_color": "#202020",
+ "theme_color": "#00c0fb",
+ "dir": "ltr",
+ "display": "fullscreen",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "${icon1024}",
+ "type": "image/png",
+ "sizes": "1024x1024",
+ "purpose": "maskable"
+ },
+ {
+ "src": "${icon1024}",
+ "type": "image/png",
+ "sizes": "1024x1024",
+ "purpose": "any"
+ }
+ ]
+}
diff --git a/public/src/style.css b/public/src/style.css
new file mode 100644
index 00000000..5587c6f7
--- /dev/null
+++ b/public/src/style.css
@@ -0,0 +1,663 @@
+/** variables */
+:root {
+ --color-black: black;
+ --color-cod-gray-light: #181818;
+ --color-cod-gray: #101010;
+ --color-mine-shaft-light: #303030;
+ --color-mine-shaft: #202020;
+ --color-primary-rgba: 0, 192, 251;
+ --color-primary: #00c0fb;
+ --color-scorpion: #808080;
+ --color-secondary-rgba: 234, 182, 56;
+ --color-secondary: #eab638;
+ --color-silver-chalice: #aaa;
+ --color-warning: red;
+ --color-white: white;
+ --viewport-height: 100%;
+
+ /*GBC
+ --color-black: black;
+ --color-cod-gray-light: #181818;
+ --color-cod-gray: #101010;
+ --color-mine-shaft-light: #0f380f;
+ --color-mine-shaft: #306230;
+ --color-primary-rgba: 155,188,15;
+ --color-primary: #9bbc0f;
+ --color-scorpion: #808080;
+ --color-secondary-rgba: 155,188,15;
+ --color-secondary: #9bbc0f;
+ --color-silver-chalice: black;
+ --color-warning: black;
+ --color-white: white;
+ */
+}
+
+/* latin */
+@font-face {
+ font-family: "Oswald";
+ font-style: normal;
+ font-weight: 300;
+ font-display: swap;
+ src: url(data:font/woff2;base64,${oswald}) format("woff2");
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
+ U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215,
+ U+FEFF, U+FFFD;
+}
+
+/* fallback */
+@font-face {
+ font-family: "Material Icons";
+ font-style: normal;
+ font-weight: 400;
+ src: url(data:font/woff2;base64,${material}) format("woff2");
+}
+
+html,
+body {
+ height: 100%;
+ touch-action: manipulation;
+ width: 100%;
+}
+
+body {
+ background-color: var(--color-cod-gray);
+ font-family: "Oswald", sans-serif;
+ height: 100%;
+ margin: 0;
+ overflow: hidden;
+ padding: 0;
+ width: 100%;
+}
+
+* {
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+}
+
+/** main container */
+.gbs-container {
+ background-color: var(--color-mine-shaft);
+ display: inline-block;
+ height: 100%;
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 414px;
+ overflow: hidden;
+ position: relative;
+ width: 100%;
+}
+
+/** main screen scrollable area */
+.gbs-scroll {
+ height: calc(100% - 54px);
+ margin-bottom: 148px;
+ overflow-x: hidden;
+ overflow-y: auto;
+}
+
+.gbs-scroll::-webkit-scrollbar {
+ width: 2px;
+}
+
+.gbs-scroll::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.gbs-scroll::-webkit-scrollbar-thumb {
+ background-color: var(--color-primary);
+ border-radius: 20px;
+}
+
+/* buttons */
+.gbs-button {
+ appearance: none;
+ background-color: var(--color-mine-shaft-light);
+ border-radius: 4px;
+ border: 1px dashed rgba(var(--color-primary-rgba), 0.2);
+ color: rgba(var(--color-primary-rgba), 0.6);
+ cursor: pointer;
+ flex-grow: 1;
+ font-family: inherit;
+ font-size: 14px;
+ font-weight: 300;
+ margin: 2px;
+ outline: 0;
+ overflow: hidden;
+ padding: 4px 8px;
+ position: relative;
+ text-align: right;
+ text-overflow: ellipsis;
+ transition: all 0.2s linear;
+ user-select: none;
+ white-space: nowrap;
+}
+
+.gbs-button[disabled] {
+ opacity: 0.3;
+ pointer-events: none;
+}
+
+.gbs-button[active] {
+ border: 1px solid var(--color-primary);
+ box-shadow: inset 0 0 6px 4px rgba(var(--color-primary-rgba), 0.2),
+ 0 0 6px 4px rgba(var(--color-primary-rgba), 0.2);
+ color: var(--color-primary);
+}
+
+.gbs-button__secondary {
+ border: 1px dashed rgba(var(--color-secondary-rgba), 0.3);
+ color: rgba(var(--color-secondary-rgba), 0.7);
+}
+
+.gbs-button__secondary[active] {
+ background-color: rgba(var(--color-secondary-rgba), 0.7);
+ border: 1px solid rgba(var(--color-secondary-rgba), 0.3);
+ box-shadow: inset 0 0 6px 4px rgba(var(--color-secondary-rgba), 0.2),
+ 0 0 6px 8px rgba(var(--color-secondary-rgba), 0.2);
+ color: var(--color-black);
+ transform: scale(0.98);
+}
+
+.gbs-button__menu {
+ margin: 2px;
+ width: 100%;
+ text-align: center;
+}
+
+.gbs-button__preset {
+ flex-grow: 1;
+ font-size: 12px;
+ height: 44px;
+ line-height: 12px;
+ margin: 1px;
+ padding: 4px;
+ text-align: right;
+ vertical-align: top;
+ white-space: initial;
+}
+
+.gbs-button__slot {
+ flex-grow: 1;
+ line-height: 16px;
+ margin: 4px;
+ min-height: 56px;
+ padding: 8px;
+ text-align: right;
+ width: 132px;
+}
+
+.gbs-button__slot::before {
+ color: var(--color-secondary);
+ content: attr(gbs-name);
+ font-size: 16px;
+ white-space: normal;
+}
+
+.gbs-button__slot::after {
+ color: var(--color-primary);
+ content: attr(gbs-meta);
+ display: block;
+ font-size: 10px;
+ line-height: 14px;
+ white-space: pre;
+}
+
+.gbs-button__resolution {
+ flex-grow: 1;
+ font-size: 14px;
+ height: 52px;
+ line-height: 14px;
+ margin: 1px;
+ min-width: 40px;
+ padding: 4px;
+ text-align: right;
+ vertical-align: top;
+ white-space: initial;
+}
+
+.gbs-button__resolution--center {
+ text-align: center;
+}
+
+.gbs-button__resolution--pass-through {
+ font-size: 10px;
+ line-height: 10px;
+ position: relative;
+ top: -4px;
+}
+
+.gbs-button__control {
+ flex-grow: 1;
+ height: 68px;
+ margin: 2px;
+ padding: 6px;
+ text-align: right;
+ vertical-align: top;
+ white-space: initial;
+ width: 84px;
+}
+
+.gbs-button__control-action {
+ flex-grow: 1;
+ height: 56px;
+ line-height: 1;
+ margin: 4px;
+ text-align: right;
+}
+
+/* menu */
+.gbs-menu {
+ background-color: var(--color-cod-gray-light);
+ border-top: 1px solid #282828;
+ bottom: 0;
+ box-sizing: border-box;
+ color: var(--color-primary);
+ display: flex;
+ height: 54px;
+ padding: 8px;
+ position: absolute;
+ text-align: center;
+ width: 100%;
+}
+
+.gbs-menu__logo {
+ display: none;
+ margin: auto;
+ margin-right: 8px;
+ width: 200%;
+}
+
+/* fieldset */
+.gbs-fieldset {
+ border-radius: 4px;
+ border: 1px solid rgba(var(--color-primary-rgba), 0.1);
+ box-sizing: border-box;
+ color: var(--color-primary);
+ font-size: 15px;
+ margin: 8px;
+ padding: 8px;
+ position: relative;
+}
+
+.gbs-fieldset__legend {
+ display: inline-block !important;
+ font-size: 16px;
+ position: relative;
+ text-align: left;
+ vertical-align: top;
+}
+
+.gbs-fieldset__legend--help::after {
+ color: var(--color-secondary);
+ content: "help_center";
+ cursor: pointer;
+ font-family: "Material Icons";
+ font-size: 24px;
+ position: relative;
+ top: 8px;
+}
+
+.gbs-show-help .gbs-fieldset__legend::after {
+ color: var(--color-scorpion);
+}
+
+.gbs-fieldset__legend div {
+ display: inline-block;
+ vertical-align: middle;
+}
+
+/** table */
+
+.gbs-preferences {
+ width: 100%;
+}
+
+.gbs-preferences td {
+ color: var(--color-silver-chalice);
+ text-align: left;
+}
+
+.gbs-preferences td[toggle-switch],
+.gbs-preferences td.gbs-icon {
+ cursor: pointer;
+}
+
+.gbs-preferences tr[active] td {
+ color: var(--color-primary);
+}
+
+.gbs-preferences tr td.gbs-preferences__child {
+ color: var(--color-secondary);
+}
+
+.gbs-preferences tr[active] td.gbs-preferences__child {
+ color: var(--color-secondary);
+}
+
+.gbs-preferences td em {
+ color: var(--color-silver-chalice);
+ display: block;
+ display: none;
+ font-size: 12px;
+ font-style: normal;
+ line-height: 16px;
+ margin-bottom: 16px;
+ margin-top: 4px;
+}
+
+.gbs-preferences .gbs-icon {
+ font-size: 32px;
+}
+
+/* sections */
+
+.gbs-resolution {
+ display: flex;
+}
+
+.gbs-presets {
+ border-radius: 8px;
+ display: flex;
+ flex-wrap: wrap;
+ height: calc(var(--viewport-height) - 310px);
+ justify-content: space-around;
+ margin-bottom: 16px;
+ overflow-y: scroll;
+ overflow: hidden;
+}
+
+.gbs-controls__desktop {
+ display: none;
+}
+
+.gbs-output-hide .gbs-output {
+ display: none;
+}
+
+.gbs-output {
+ position: relative;
+}
+
+.gbs-output__textarea {
+ appearance: none;
+ background-color: var(--color-black);
+ border-radius: 8px;
+ border: none;
+ box-sizing: border-box;
+ color: var(--color-white);
+ flex-grow: 1;
+ height: 400px;
+ outline: 0;
+ padding: 16px;
+ resize: none;
+ scrollbar-color: var(--color-primary) var(--color-black);
+ scrollbar-width: thin;
+}
+
+.gbs-output__textarea::-webkit-scrollbar {
+ border-radius: 20px;
+ width: 20px;
+}
+
+.gbs-output__textarea::-webkit-scrollbar-track {
+ background-color: var(--color-black);
+ border-radius: 20px;
+}
+
+.gbs-output__textarea::-webkit-scrollbar-thumb {
+ background-color: var(--color-primary);
+ border-radius: 20px;
+ border: 7px solid var(--color-black);
+}
+
+.gbs-loader {
+ align-items: center;
+ background-color: var(--color-mine-shaft);
+ display: flex;
+ height: 100%;
+ justify-content: center;
+ left: 0;
+ position: fixed;
+ top: 0;
+ width: 100%;
+}
+
+.gbs-loader img {
+ border-radius: 16px;
+ box-shadow: 0 0 64px 4px var(--color-primary);
+ width: 160px;
+}
+
+.gbs-loader,
+body {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 304 304' width='152' height='152'%3E%3Cpath fill='%2300c0fb' fill-opacity='0.4' d='M44.1 224a5 5 0 1 1 0 2H0v-2h44.1zm160 48a5 5 0 1 1 0 2H82v-2h122.1zm57.8-46a5 5 0 1 1 0-2H304v2h-42.1zm0 16a5 5 0 1 1 0-2H304v2h-42.1zm6.2-114a5 5 0 1 1 0 2h-86.2a5 5 0 1 1 0-2h86.2zm-256-48a5 5 0 1 1 0 2H0v-2h12.1zm185.8 34a5 5 0 1 1 0-2h86.2a5 5 0 1 1 0 2h-86.2zM258 12.1a5 5 0 1 1-2 0V0h2v12.1zm-64 208a5 5 0 1 1-2 0v-54.2a5 5 0 1 1 2 0v54.2zm48-198.2V80h62v2h-64V21.9a5 5 0 1 1 2 0zm16 16V64h46v2h-48V37.9a5 5 0 1 1 2 0zm-128 96V208h16v12.1a5 5 0 1 1-2 0V210h-16v-76.1a5 5 0 1 1 2 0zm-5.9-21.9a5 5 0 1 1 0 2H114v48H85.9a5 5 0 1 1 0-2H112v-48h12.1zm-6.2 130a5 5 0 1 1 0-2H176v-74.1a5 5 0 1 1 2 0V242h-60.1zm-16-64a5 5 0 1 1 0-2H114v48h10.1a5 5 0 1 1 0 2H112v-48h-10.1zM66 284.1a5 5 0 1 1-2 0V274H50v30h-2v-32h18v12.1zM236.1 176a5 5 0 1 1 0 2H226v94h48v32h-2v-30h-48v-98h12.1zm25.8-30a5 5 0 1 1 0-2H274v44.1a5 5 0 1 1-2 0V146h-10.1zm-64 96a5 5 0 1 1 0-2H208v-80h16v-14h-42.1a5 5 0 1 1 0-2H226v18h-16v80h-12.1zm86.2-210a5 5 0 1 1 0 2H272V0h2v32h10.1zM98 101.9V146H53.9a5 5 0 1 1 0-2H96v-42.1a5 5 0 1 1 2 0zM53.9 34a5 5 0 1 1 0-2H80V0h2v34H53.9zm60.1 3.9V66H82v64H69.9a5 5 0 1 1 0-2H80V64h32V37.9a5 5 0 1 1 2 0zM101.9 82a5 5 0 1 1 0-2H128V37.9a5 5 0 1 1 2 0V82h-28.1zm16-64a5 5 0 1 1 0-2H146v44.1a5 5 0 1 1-2 0V18h-26.1zm102.2 270a5 5 0 1 1 0 2H98v14h-2v-16h124.1zM242 149.9V160h16v34h-16v62h48v48h-2v-46h-48v-66h16v-30h-16v-12.1a5 5 0 1 1 2 0zM53.9 18a5 5 0 1 1 0-2H64V2H48V0h18v18H53.9zm112 32a5 5 0 1 1 0-2H192V0h50v2h-48v48h-28.1zm-48-48a5 5 0 0 1-9.8-2h2.07a3 3 0 1 0 5.66 0H178v34h-18V21.9a5 5 0 1 1 2 0V32h14V2h-58.1zm0 96a5 5 0 1 1 0-2H137l32-32h39V21.9a5 5 0 1 1 2 0V66h-40.17l-32 32H117.9zm28.1 90.1a5 5 0 1 1-2 0v-76.51L175.59 80H224V21.9a5 5 0 1 1 2 0V82h-49.59L146 112.41v75.69zm16 32a5 5 0 1 1-2 0v-99.51L184.59 96H300.1a5 5 0 0 1 3.9-3.9v2.07a3 3 0 0 0 0 5.66v2.07a5 5 0 0 1-3.9-3.9H185.41L162 121.41v98.69zm-144-64a5 5 0 1 1-2 0v-3.51l48-48V48h32V0h2v50H66v55.41l-48 48v2.69zM50 53.9v43.51l-48 48V208h26.1a5 5 0 1 1 0 2H0v-65.41l48-48V53.9a5 5 0 1 1 2 0zm-16 16V89.41l-34 34v-2.82l32-32V69.9a5 5 0 1 1 2 0zM12.1 32a5 5 0 1 1 0 2H9.41L0 43.41V40.6L8.59 32h3.51zm265.8 18a5 5 0 1 1 0-2h18.69l7.41-7.41v2.82L297.41 50H277.9zm-16 160a5 5 0 1 1 0-2H288v-71.41l16-16v2.82l-14 14V210h-28.1zm-208 32a5 5 0 1 1 0-2H64v-22.59L40.59 194H21.9a5 5 0 1 1 0-2H41.41L66 216.59V242H53.9zm150.2 14a5 5 0 1 1 0 2H96v-56.6L56.6 162H37.9a5 5 0 1 1 0-2h19.5L98 200.6V256h106.1zm-150.2 2a5 5 0 1 1 0-2H80v-46.59L48.59 178H21.9a5 5 0 1 1 0-2H49.41L82 208.59V258H53.9zM34 39.8v1.61L9.41 66H0v-2h8.59L32 40.59V0h2v39.8zM2 300.1a5 5 0 0 1 3.9 3.9H3.83A3 3 0 0 0 0 302.17V256h18v48h-2v-46H2v42.1zM34 241v63h-2v-62H0v-2h34v1zM17 18H0v-2h16V0h2v18h-1zm273-2h14v2h-16V0h2v16zm-32 273v15h-2v-14h-14v14h-2v-16h18v1zM0 92.1A5.02 5.02 0 0 1 6 97a5 5 0 0 1-6 4.9v-2.07a3 3 0 1 0 0-5.66V92.1zM80 272h2v32h-2v-32zm37.9 32h-2.07a3 3 0 0 0-5.66 0h-2.07a5 5 0 0 1 9.8 0zM5.9 0A5.02 5.02 0 0 1 0 5.9V3.83A3 3 0 0 0 3.83 0H5.9zm294.2 0h2.07A3 3 0 0 0 304 3.83V5.9a5 5 0 0 1-3.9-5.9zm3.9 300.1v2.07a3 3 0 0 0-1.83 1.83h-2.07a5 5 0 0 1 3.9-3.9zM97 100a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-48 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 48a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 96a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-144a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-96 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm96 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-32 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM49 36a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-32 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM33 68a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-48a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 240a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm80-176a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 48a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm112 176a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-16 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM17 180a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0 16a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm0-32a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16 0a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM17 84a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm32 64a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm16-16a3 3 0 1 0 0-6 3 3 0 0 0 0 6z'%3E%3C/path%3E%3C/svg%3E");
+}
+
+.gbs-wifi__list {
+ text-align: left;
+}
+
+.gbs-wifi__list tr {
+ height: 38px;
+ line-height: 38px;
+}
+
+.gbs-wifi__list td {
+ line-height: 18px;
+ vertical-align: top;
+}
+
+.gbs-wifi__list .gbs-icon ~ td {
+ width: 100%;
+}
+
+.gbs-wifi__list td ~ td ~ td {
+ color: var(--color-secondary);
+}
+
+.gbs-wifi__input {
+ flex-grow: 1;
+ height: 38px;
+}
+
+.gbs-wifi__input--error {
+ animation: gbs-border-blinker 0.5s linear infinite;
+}
+
+input {
+ -webkit-user-select: initial !important;
+ user-select: initial !important;
+}
+
+.gbs-wifi-warning {
+ animation: gbs-blinker 0.5s linear infinite;
+ color: var(--color-warning);
+ position: absolute;
+ right: 18px;
+ top: 10px;
+ z-index: 1;
+}
+
+/* modals */
+
+section[name="prompt"],
+section[name="alert"] {
+ background-color: rgba(0, 0, 0, 0.96);
+ bottom: 0;
+ left: 0;
+ position: absolute;
+ right: 0;
+ top: 0;
+}
+
+.gbs-modal__message {
+ text-align: right;
+ margin-top: 16px;
+ margin-bottom: 32px;
+}
+
+/* HELPERS */
+[hidden] {
+ display: none !important;
+}
+
+[gbs-progress]::before {
+ content: attr(gbs-progress);
+}
+
+.gbs-backup-input {
+ cursor: pointer;
+ height: 100%;
+ left: 0;
+ opacity: 0;
+ position: absolute;
+ top: 0;
+ width: 100%;
+}
+
+.gbs-button[gbs-name="Empty"] ~ .gbs-button[gbs-name="Empty"] {
+ display: none;
+}
+
+.gbs-help {
+ color: var(--color-scorpion);
+ display: flex;
+ flex-direction: column;
+ font-size: 14px;
+ line-height: 18px;
+ margin: 8px 0 16px 0;
+ padding-left: 28px;
+ text-align: left;
+ white-space: pre-line;
+}
+
+.gbs-help-hide .gbs-help {
+ display: none;
+}
+
+.gbs-icon {
+ -webkit-font-feature-settings: "liga";
+ -webkit-font-smoothing: antialiased;
+ direction: ltr;
+ display: inline-block;
+ font-family: "Material Icons";
+ font-size: 24px;
+ font-style: normal;
+ font-weight: normal;
+ letter-spacing: normal;
+ line-height: 1;
+ text-transform: none;
+ white-space: nowrap;
+ word-wrap: normal;
+}
+
+.gbs-flex {
+ display: flex;
+}
+
+.gbs-margin__bottom--16 {
+ margin-bottom: 16px;
+}
+
+.gbs-padding__left-16 {
+ padding-left: 16px;
+}
+
+.gbs-padding__hor-16 {
+ padding-left: 16px;
+ padding-right: 16px;
+}
+
+@media (min-width: 415px) {
+ .gbs-button:hover {
+ border: 1px solid var(--color-primary);
+ box-shadow: inset 0 0 6px 4px rgba(var(--color-primary-rgba), 0.2),
+ 0 0 6px 4px rgba(var(--color-primary-rgba), 0.2);
+ color: var(--color-primary);
+ }
+
+ .gbs-button__secondary:hover {
+ background-color: rgba(var(--color-secondary-rgba), 0.7);
+ border: 1px solid rgba(var(--color-secondary-rgba), 0.3);
+ box-shadow: inset 0 0 6px 4px rgba(var(--color-secondary-rgba), 0.2),
+ 0 0 6px 8px rgba(var(--color-secondary-rgba), 0.2);
+ color: var(--color-black);
+ transform: scale(0.98);
+ }
+
+ .gbs-button__control {
+ height: 60px;
+ }
+
+ .gbs-menu {
+ border-bottom: 1px solid rgba(var(--color-primary-rgba), 0.4);
+ bottom: initial;
+ padding-left: 8px;
+ padding-right: 8px;
+ position: relative;
+ top: 0;
+ }
+
+ .gbs-menu__logo {
+ display: inline-block;
+ }
+
+ .gbs-controls {
+ display: none;
+ }
+
+ .gbs-controls__desktop {
+ display: block;
+ }
+
+ .gbs-presets {
+ height: calc(100vh - 310px);
+ }
+
+ .gbs-output {
+ background-color: var(--color-mine-shaft-light);
+ height: 100vh;
+ left: 414px;
+ position: fixed;
+ top: 0px;
+ width: calc(100vw - 414px);
+ z-index: 1;
+ }
+
+ .gbs-output__textarea {
+ height: calc(100vh - 114px);
+ }
+
+ .gbs-output-hide .gbs-container {
+ border-radius: 16px;
+ box-shadow: 0 0 64px 4px rgba(var(--color-primary-rgba), 0.4);
+ display: block;
+ height: calc(100vh - 64px);
+ margin-top: 32px;
+ }
+
+ .gbs-output-hide .gbs-presets {
+ height: calc(100vh - 374px);
+ }
+
+ .gbs-fieldset-output {
+ height: calc(100vh - 16px);
+ }
+}
+
+@keyframes gbs-border-blinker {
+ 50% {
+ border: 1px solid var(--color-warning);
+ }
+}
+
+@keyframes gbs-blinker {
+ 50% {
+ opacity: 0;
+ }
+}
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 6c5d5d44..00000000
--- a/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-sphinx-rtd-theme
diff --git a/rgbhv.h b/rgbhv.h
new file mode 100644
index 00000000..f7bfae07
--- /dev/null
+++ b/rgbhv.h
@@ -0,0 +1,595 @@
+const uint8_t rgbhv[] PROGMEM = {
+44,
+164,
+0,
+20,
+37,
+5,
+127,
+23,
+42,
+3,
+0,
+0,
+0,
+0,
+0,
+44,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+96,
+128,
+64,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+192,
+1,
+4,
+0,
+3,
+128,
+0,
+47,
+0,
+255,
+255,
+255,
+255,
+131,
+0,
+207,
+0,
+20,
+224,
+31,
+0,
+0,
+0,
+138,
+3,
+3,
+0,
+168,
+0,
+4,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+202,
+0,
+128,
+0,
+63,
+0,
+128,
+44,
+204,
+0,
+0,
+0,
+0,
+1,
+192,
+0,
+0,
+1,
+192,
+0,
+0,
+1,
+192,
+0,
+0,
+1,
+192,
+0,
+0,
+1,
+192,
+0,
+0,
+1,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+160,
+38,
+65,
+62,
+178,
+154,
+78,
+214,
+177,
+142,
+124,
+99,
+139,
+118,
+112,
+98,
+133,
+105,
+83,
+72,
+93,
+148,
+178,
+70,
+198,
+238,
+140,
+98,
+118,
+156,
+0,
+0,
+1,
+0,
+0,
+12,
+202,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+255,
+3,
+204,
+0,
+0,
+0,
+0,
+0,
+7,
+0,
+140,
+4,
+220,
+0,
+0,
+72,
+32,
+128,
+142,
+0,
+0,
+0,
+12,
+4,
+11,
+4,
+0,
+16,
+0,
+0,
+0,
+16,
+80,
+2,
+0,
+0,
+4,
+0,
+76,
+12,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+52,
+127,
+153,
+56,
+3,
+11,
+4,
+100,
+128,
+4,
+143,
+0,
+0,
+0,
+2,
+72,
+133,
+62,
+194,
+131,
+78,
+192,
+3,
+0,
+10,
+228,
+69,
+193,
+67,
+60,
+194,
+131,
+84,
+192,
+139,
+62,
+216,
+178,
+32,
+1,
+0,
+0,
+0,
+0,
+16,
+0,
+0,
+0,
+0,
+0,
+8,
+3,
+0,
+207,
+38,
+7,
+17,
+143,
+224,
+47,
+32,
+240,
+64,
+58,
+127,
+0,
+0,
+128,
+30,
+41,
+0,
+0,
+254,
+1,
+1,
+0,
+128,
+0,
+7,
+3,
+0,
+0,
+250,
+20,
+249,
+24,
+249,
+16,
+249,
+32,
+249,
+10,
+27,
+30,
+32,
+0,
+112,
+8,
+36,
+10,
+139,
+0,
+26,
+0,
+0,
+26,
+0,
+196,
+63,
+4,
+4,
+155,
+128,
+9,
+233,
+255,
+127,
+64,
+210,
+13,
+216,
+255,
+63,
+0,
+0,
+0,
+0,
+8,
+0,
+188,
+5,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+135,
+48,
+0,
+0,
+48,
+119,
+66,
+51,
+1,
+148,
+17,
+127,
+0,
+4,
+0,
+6,
+0,
+146,
+7,
+1,
+150,
+5,
+0,
+0,
+0,
+0,
+0,
+119,
+0,
+0,
+0,
+0,
+0,
+3,
+1,
+31,
+0,
+184,
+3,
+255,
+255,
+31,
+0,
+132,
+30,
+0,
+128,
+0,
+0,
+31,
+224,
+0,
+1,
+0,
+0,
+184,
+204,
+184,
+0,
+2,
+0,
+0,
+3,
+0,
+0,
+0,
+104,
+0,
+255,
+255,
+7,
+255,
+255,
+7,
+0,
+68,
+0,
+96,
+40,
+62,
+192,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+204,
+0,
+204,
+76,
+0,
+0,
+0,
+0,
+216,
+0,
+84,
+49,
+0,
+0,
+63,
+63,
+63,
+127,
+127,
+127,
+0,
+0,
+0,
+0,
+0,
+178,
+132,
+3,
+0,
+0,
+47,
+5,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+4,
+210,
+32,
+15,
+0,
+64,
+0,
+5,
+0,
+0,
+0,
+15,
+0,
+0,
+4,
+0,
+4,
+0,
+47,
+0,
+40,
+3,
+21,
+0,
+4,
+0,
+0,
+10,
+0,
+0,
+0,
+240,
+3,
+11,
+0,
+0,
+16,
+0,
+0,
+0,
+5,
+0,
+4,
+0,
+52,
+0,
+0,
+0,
+0,
+0,
+2,
+0,
+6,
+0,
+80,
+54,
+68,
+5,
+192,
+1,
+200,
+6,
+1,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+0,
+};
+
diff --git a/src/LICENSE b/src/LICENSE
new file mode 100644
index 00000000..f166cc57
--- /dev/null
+++ b/src/LICENSE
@@ -0,0 +1,502 @@
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL. It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it. You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+ When we speak of free software, we are referring to freedom of use,
+not price. Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+ To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights. These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ To protect each distributor, we want to make it very clear that
+there is no warranty for the free library. Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+ Finally, software patents pose a constant threat to the existence of
+any free program. We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder. Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+ Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License. This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License. We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+ When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library. The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom. The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+ We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License. It also provides other free software developers Less
+of an advantage over competing non-free programs. These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries. However, the Lesser license provides advantages in certain
+special circumstances.
+
+ For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard. To achieve this, non-free programs must be
+allowed to use the library. A more frequent case is that a free
+library does the same job as widely used non-free libraries. In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+ In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software. For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+ Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+ 6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (1) uses at run time a
+ copy of the library already present on the user's computer system,
+ rather than copying library functions into the executable, and (2)
+ will operate properly with a modified version of the library, if
+ the user installs one, as long as the modified version is
+ interface-compatible with the version that the work was made with.
+
+ c) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ d) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ e) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+ 7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+
+ Copyright (C)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with this library; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ , 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
\ No newline at end of file
diff --git a/src/README.md b/src/README.md
new file mode 100644
index 00000000..63eef3e2
--- /dev/null
+++ b/src/README.md
@@ -0,0 +1,98 @@
+WebSocket Server and Client for Arduino [](https://travis-ci.org/Links2004/arduinoWebSockets)
+===========================================
+
+a WebSocket Server and Client for Arduino based on RFC6455.
+
+
+##### Supported features of RFC6455 #####
+ - text frame
+ - binary frame
+ - connection close
+ - ping
+ - pong
+ - continuation frame
+
+##### Limitations #####
+ - max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define
+ - max output length has no limit (the hardware is the limit)
+ - Client send big frames with mask 0x00000000 (on AVR all frames)
+ - continuation frame reassembly need to be handled in the application code
+
+ ##### Limitations for Async #####
+ - Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`. See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround.
+ - wss / SSL is not possible.
+
+##### Supported Hardware #####
+ - ESP8266 [Arduino for ESP8266](https://github.com/esp8266/Arduino/)
+ - ESP32 [Arduino for ESP32](https://github.com/espressif/arduino-esp32)
+ - ESP31B
+ - Particle with STM32 ARM Cortex M3
+ - ATmega328 with Ethernet Shield (ATmega branch)
+ - ATmega328 with enc28j60 (ATmega branch)
+ - ATmega2560 with Ethernet Shield (ATmega branch)
+ - ATmega2560 with enc28j60 (ATmega branch)
+
+###### Note: ######
+
+ version 2.0 and up is not compatible with AVR/ATmega, check ATmega branch.
+
+ Arduino for AVR not supports std namespace of c++.
+
+### wss / SSL ###
+ supported for:
+ - wss client on the ESP8266
+ - wss / SSL is not natively supported in WebSocketsServer however it is possible to achieve secure websockets
+ by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a
+ sample Nginx server configuration file to enable this.
+
+### ESP Async TCP ###
+
+This libary can run in Async TCP mode on the ESP.
+
+The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define).
+
+[ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required.
+
+
+### High Level Client API ###
+
+ - `begin` : Initiate connection sequence to the websocket host.
+```
+void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
+void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
+ ```
+ - `onEvent`: Callback to handle for websocket events
+
+ ```
+ void onEvent(WebSocketClientEvent cbEvent);
+ ```
+
+ - `WebSocketClientEvent`: Handler for websocket events
+ ```
+ void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length)
+ ```
+Where `WStype_t type` is defined as:
+ ```
+ 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;
+ ```
+
+### Issues ###
+Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues
+
+[](https://gitter.im/Links2004/arduinoWebSockets?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+### License and credits ###
+
+The library is licensed under [LGPLv2.1](https://github.com/Links2004/arduinoWebSockets/blob/master/LICENSE)
+
+[libb64](http://libb64.sourceforge.net/) written by Chris Venter. It is distributed under Public Domain see [LICENSE](https://github.com/Links2004/arduinoWebSockets/blob/master/src/libb64/LICENSE).
diff --git a/src/WebSockets.cpp b/src/WebSockets.cpp
new file mode 100644
index 00000000..3a90a402
--- /dev/null
+++ b/src/WebSockets.cpp
@@ -0,0 +1,749 @@
+/**
+ * @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
+#endif
+
+extern "C" {
+#ifdef CORE_HAS_LIBB64
+#include
+#else
+#include "libb64/cencode_inc.h"
+#endif
+}
+
+#ifdef ESP8266
+#include
+#elif defined(ESP32)
+#include
+#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 buf uint8_t * ptr to the buffer for writing
+ * @param opcode WSopcode_t
+ * @param length size_t length of the payload
+ * @param mask bool add dummy mask to the frame (needed for web browser)
+ * @param maskkey uint8_t[4] key used for payload
+ * @param fin bool can be used to send data in more then one frame (set fin on the last frame)
+ */
+uint8_t WebSockets::createHeader(uint8_t * headerPtr, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin) {
+ uint8_t headerSize;
+ // calculate header Size
+ if(length < 126) {
+ headerSize = 2;
+ } else if(length < 0xFFFF) {
+ headerSize = 4;
+ } else {
+ headerSize = 10;
+ }
+
+ if(mask) {
+ headerSize += 4;
+ }
+
+ // create header
+
+ // byte 0
+ *headerPtr = 0x00;
+ if(fin) {
+ *headerPtr |= bit(7); ///< set Fin
+ }
+ *headerPtr |= opcode; ///< set opcode
+ headerPtr++;
+
+ // byte 1
+ *headerPtr = 0x00;
+ if(mask) {
+ *headerPtr |= bit(7); ///< set mask
+ }
+
+ if(length < 126) {
+ *headerPtr |= length;
+ headerPtr++;
+ } else if(length < 0xFFFF) {
+ *headerPtr |= 126;
+ headerPtr++;
+ *headerPtr = ((length >> 8) & 0xFF);
+ headerPtr++;
+ *headerPtr = (length & 0xFF);
+ headerPtr++;
+ } else {
+ // Normally we never get here (to less memory)
+ *headerPtr |= 127;
+ headerPtr++;
+ *headerPtr = 0x00;
+ headerPtr++;
+ *headerPtr = 0x00;
+ headerPtr++;
+ *headerPtr = 0x00;
+ headerPtr++;
+ *headerPtr = 0x00;
+ headerPtr++;
+ *headerPtr = ((length >> 24) & 0xFF);
+ headerPtr++;
+ *headerPtr = ((length >> 16) & 0xFF);
+ headerPtr++;
+ *headerPtr = ((length >> 8) & 0xFF);
+ headerPtr++;
+ *headerPtr = (length & 0xFF);
+ headerPtr++;
+ }
+
+ if(mask) {
+ *headerPtr = maskKey[0];
+ headerPtr++;
+ *headerPtr = maskKey[1];
+ headerPtr++;
+ *headerPtr = maskKey[2];
+ headerPtr++;
+ *headerPtr = maskKey[3];
+ headerPtr++;
+ }
+ return headerSize;
+}
+
+/**
+ *
+ * @param client WSclient_t * ptr to the client struct
+ * @param opcode WSopcode_t
+ * @param length size_t length of the payload
+ * @param fin bool can be used to send data in more then one frame (set fin on the last frame)
+ * @return true if ok
+ */
+bool WebSockets::sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length, bool fin) {
+ uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
+ uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
+
+ uint8_t headerSize = createHeader(&buffer[0], opcode, length, client->cIsClient, maskKey, fin);
+
+ if(write(client, &buffer[0], headerSize) != headerSize) {
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ *
+ * @param client WSclient_t * ptr to the client struct
+ * @param opcode WSopcode_t
+ * @param payload uint8_t * ptr to the payload
+ * @param length size_t length of the payload
+ * @param fin bool can be used to send data in more then one frame (set fin on the last frame)
+ * @param headerToPayload bool set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
+ * @return true if ok
+ */
+bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin, bool headerToPayload) {
+ if(client->tcp && !client->tcp->connected()) {
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num);
+ return false;
+ }
+
+ if(client->status != WSC_CONNECTED) {
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num);
+ return false;
+ }
+
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num);
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, client->cIsClient, length, headerToPayload);
+
+ if(opcode == WSop_text) {
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0)));
+ }
+
+ uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
+ uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
+
+ uint8_t headerSize;
+ uint8_t * headerPtr;
+ uint8_t * payloadPtr = payload;
+ bool useInternBuffer = false;
+ bool ret = true;
+
+ // calculate header Size
+ if(length < 126) {
+ headerSize = 2;
+ } else if(length < 0xFFFF) {
+ headerSize = 4;
+ } else {
+ headerSize = 10;
+ }
+
+ if(client->cIsClient) {
+ headerSize += 4;
+ }
+
+#ifdef WEBSOCKETS_USE_BIG_MEM
+ // only for ESP since AVR has less HEAP
+ // try to send data in one TCP package (only if some free Heap is there)
+ if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) {
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
+ uint8_t * dataPtr = (uint8_t *)malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
+ if(dataPtr) {
+ memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
+ headerToPayload = true;
+ useInternBuffer = true;
+ payloadPtr = dataPtr;
+ }
+ }
+#endif
+
+ // set Header Pointer
+ if(headerToPayload) {
+ // calculate offset in payload
+ headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
+ } else {
+ headerPtr = &buffer[0];
+ }
+
+ if(client->cIsClient && useInternBuffer) {
+ // if we use a Intern Buffer we can modify the data
+ // by this fact its possible the do the masking
+ for(uint8_t x = 0; x < sizeof(maskKey); x++) {
+ maskKey[x] = random(0xFF);
+ }
+ }
+
+ createHeader(headerPtr, opcode, length, client->cIsClient, maskKey, fin);
+
+ if(client->cIsClient && useInternBuffer) {
+ uint8_t * dataMaskPtr;
+
+ if(headerToPayload) {
+ dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
+ } else {
+ dataMaskPtr = payloadPtr;
+ }
+
+ for(size_t x = 0; x < length; x++) {
+ dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
+ }
+ }
+
+#ifndef NODEBUG_WEBSOCKETS
+ unsigned long start = micros();
+#endif
+
+ if(headerToPayload) {
+ // header has be added to payload
+ // payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings
+ // offset in payload is calculatetd 14 - headerSize
+ if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
+ ret = false;
+ }
+ } else {
+ // send header
+ if(write(client, &buffer[0], headerSize) != headerSize) {
+ ret = false;
+ }
+
+ if(payloadPtr && length > 0) {
+ // send payload
+ if(write(client, &payloadPtr[0], length) != length) {
+ ret = false;
+ }
+ }
+ }
+
+ DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start));
+
+#ifdef WEBSOCKETS_USE_BIG_MEM
+ if(useInternBuffer && payloadPtr) {
+ free(payloadPtr);
+ }
+#endif
+
+ return ret;
+}
+
+/**
+ * callen when HTTP header is done
+ * @param client WSclient_t * ptr to the client struct
+ */
+void WebSockets::headerDone(WSclient_t * client) {
+ client->status = WSC_CONNECTED;
+ client->cWsRXsize = 0;
+ DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num);
+#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+ client->cHttpLine = "";
+ handleWebsocket(client);
+#endif
+}
+
+/**
+ * handle the WebSocket stream
+ * @param client WSclient_t * ptr to the client struct
+ */
+void WebSockets::handleWebsocket(WSclient_t * client) {
+ if(client->cWsRXsize == 0) {
+ handleWebsocketCb(client);
+ }
+}
+
+/**
+ * wait for
+ * @param client
+ * @param size
+ */
+bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
+ if(!client->tcp || !client->tcp->connected()) {
+ return false;
+ }
+
+ if(size > WEBSOCKETS_MAX_HEADER_SIZE) {
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size);
+ return false;
+ }
+
+ if(client->cWsRXsize >= size) {
+ return true;
+ }
+
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize);
+ readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) {
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok);
+ if(ok) {
+ client->cWsRXsize = size;
+ server->handleWebsocketCb(client);
+ } else {
+ DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num);
+ client->cWsRXsize = 0;
+ // timeout or error
+ server->clientDisconnect(client, 1002);
+ }
+ },
+ this, size, std::placeholders::_1, std::placeholders::_2));
+ return false;
+}
+
+void WebSockets::handleWebsocketCb(WSclient_t * client) {
+ if(!client->tcp || !client->tcp->connected()) {
+ return;
+ }
+
+ uint8_t * buffer = client->cWsHeader;
+
+ WSMessageHeader_t * header = &client->cWsHeaderDecode;
+ uint8_t * payload = NULL;
+
+ uint8_t headerLen = 2;
+
+ if(!handleWebsocketWaitFor(client, headerLen)) {
+ return;
+ }
+
+ // split first 2 bytes in the data
+ header->fin = ((*buffer >> 7) & 0x01);
+ header->rsv1 = ((*buffer >> 6) & 0x01);
+ header->rsv2 = ((*buffer >> 5) & 0x01);
+ header->rsv3 = ((*buffer >> 4) & 0x01);
+ header->opCode = (WSopcode_t)(*buffer & 0x0F);
+ buffer++;
+
+ header->mask = ((*buffer >> 7) & 0x01);
+ header->payloadLen = (WSopcode_t)(*buffer & 0x7F);
+ buffer++;
+
+ if(header->payloadLen == 126) {
+ headerLen += 2;
+ if(!handleWebsocketWaitFor(client, headerLen)) {
+ return;
+ }
+ header->payloadLen = buffer[0] << 8 | buffer[1];
+ buffer += 2;
+ } else if(header->payloadLen == 127) {
+ headerLen += 8;
+ // read 64bit integer as length
+ if(!handleWebsocketWaitFor(client, headerLen)) {
+ return;
+ }
+
+ if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
+ // really too big!
+ header->payloadLen = 0xFFFFFFFF;
+ } else {
+ header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
+ }
+ buffer += 8;
+ }
+
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num);
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode);
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen);
+
+ if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen);
+ clientDisconnect(client, 1009);
+ return;
+ }
+
+ if(header->mask) {
+ headerLen += 4;
+ if(!handleWebsocketWaitFor(client, headerLen)) {
+ return;
+ }
+ header->maskKey = buffer;
+ buffer += 4;
+ }
+
+ if(header->payloadLen > 0) {
+ // if text data we need one more
+ payload = (uint8_t *)malloc(header->payloadLen + 1);
+
+ if(!payload) {
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen);
+ clientDisconnect(client, 1011);
+ return;
+ }
+ readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload));
+ } else {
+ handleWebsocketPayloadCb(client, true, NULL);
+ }
+}
+
+void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) {
+ WSMessageHeader_t * header = &client->cWsHeaderDecode;
+ if(ok) {
+ if(header->payloadLen > 0) {
+ payload[header->payloadLen] = 0x00;
+
+ if(header->mask) {
+ //decode XOR
+ for(size_t i = 0; i < header->payloadLen; i++) {
+ payload[i] = (payload[i] ^ header->maskKey[i % 4]);
+ }
+ }
+ }
+
+ switch(header->opCode) {
+ case WSop_text:
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload);
+ // no break here!
+ case WSop_binary:
+ case WSop_continuation:
+ messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
+ break;
+ case WSop_ping:
+ // send pong back
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ping received (%s)\n", client->num, payload ? (const char *)payload : "");
+ sendFrame(client, WSop_pong, payload, header->payloadLen);
+ messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
+ break;
+ case WSop_pong:
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char *)payload : "");
+ client->pongReceived = true;
+ messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
+ break;
+ case WSop_close: {
+#ifndef NODEBUG_WEBSOCKETS
+ uint16_t reasonCode = 1000;
+ if(header->payloadLen >= 2) {
+ reasonCode = payload[0] << 8 | payload[1];
+ }
+#endif
+ DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", 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));
+}
+
+/**
+ * enable ping/pong heartbeat process
+ * @param client WSclient_t *
+ * @param pingInterval uint32_t how often ping will be sent
+ * @param pongTimeout uint32_t millis after which pong should timout if not received
+ * @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
+ */
+void WebSockets::enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount) {
+ if(client == NULL)
+ return;
+ client->pingInterval = pingInterval;
+ client->pongTimeout = pongTimeout;
+ client->disconnectTimeoutCount = disconnectTimeoutCount;
+ client->pongReceived = false;
+}
+
+/**
+ * handle ping/pong heartbeat timeout process
+ * @param client WSclient_t *
+ */
+void WebSockets::handleHBTimeout(WSclient_t * client) {
+ if(client->pingInterval) { // if heartbeat is enabled
+ uint32_t pi = millis() - client->lastPing;
+
+ if(client->pongReceived) {
+ client->pongTimeoutCount = 0;
+ } else {
+ if(pi > client->pongTimeout) { // pong not received in time
+ client->pongTimeoutCount++;
+ client->lastPing = millis() - client->pingInterval - 500; // force ping on the next run
+
+ DEBUG_WEBSOCKETS("[HBtimeout] pong TIMEOUT! lp=%d millis=%d pi=%d count=%d\n", client->lastPing, millis(), pi, client->pongTimeoutCount);
+
+ if(client->disconnectTimeoutCount && client->pongTimeoutCount >= client->disconnectTimeoutCount) {
+ DEBUG_WEBSOCKETS("[HBtimeout] count=%d, DISCONNECTING\n", client->pongTimeoutCount);
+ clientDisconnect(client);
+ }
+ }
+ }
+ }
+}
diff --git a/src/WebSockets.h b/src/WebSockets.h
new file mode 100644
index 00000000..d0813d30
--- /dev/null
+++ b/src/WebSockets.h
@@ -0,0 +1,331 @@
+/**
+ * @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
+#define bit(b) (1UL << (b)) // Taken directly from Arduino.h
+#else
+#include
+#include
+#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
+#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)
+#define NETWORK_ESP32_ETH (5)
+
+// max size of the WS Message Header
+#define WEBSOCKETS_MAX_HEADER_SIZE (14)
+
+#if !defined(WEBSOCKETS_NETWORK_TYPE)
+// select Network type based
+#if defined(ESP8266) || defined(ESP31B)
+//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
+#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC
+//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
+
+#elif defined(ESP32)
+#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32
+//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32_ETH
+#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
+#elif defined(ESP32)
+#include
+#include
+#elif defined(ESP31B)
+#include
+#else
+#error "network type ESP8266 ASYNC only possible on the ESP mcu!"
+#endif
+
+#include
+#include
+#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
+#else
+#include
+#endif
+#define WEBSOCKETS_NETWORK_CLASS WiFiClient
+#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
+#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
+
+#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
+
+#ifdef STM32_DEVICE
+#define WEBSOCKETS_NETWORK_CLASS TCPClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer
+#else
+#include
+#include
+#define WEBSOCKETS_NETWORK_CLASS EthernetClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
+#endif
+
+#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
+
+#include
+#define WEBSOCKETS_NETWORK_CLASS UIPClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer
+
+#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+
+#include
+#include
+#define WEBSOCKETS_NETWORK_CLASS WiFiClient
+#define WEBSOCKETS_NETWORK_SSL_CLASS WiFiClientSecure
+#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
+
+#elif(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32_ETH)
+
+#include
+#define WEBSOCKETS_NETWORK_CLASS WiFiClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
+
+#else
+#error "no network type selected!"
+#endif
+
+#ifdef WEBSOCKETS_NETWORK_SSL_CLASS
+#define HAS_SSL
+#endif
+
+// moves all Header strings to Flash (~300 Byte)
+#ifdef WEBSOCKETS_SAVE_RAM
+#define WEBSOCKETS_STRING(var) F(var)
+#else
+#define WEBSOCKETS_STRING(var) var
+#endif
+
+typedef enum {
+ WSC_NOT_CONNECTED,
+ WSC_HEADER,
+ WSC_CONNECTED
+} WSclientsStatus_t;
+
+typedef enum {
+ WStype_ERROR,
+ WStype_DISCONNECTED,
+ WStype_CONNECTED,
+ WStype_TEXT,
+ WStype_BIN,
+ WStype_FRAGMENT_TEXT_START,
+ WStype_FRAGMENT_BIN_START,
+ WStype_FRAGMENT,
+ WStype_FRAGMENT_FIN,
+ WStype_PING,
+ WStype_PONG,
+} WStype_t;
+
+typedef enum {
+ WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
+ WSop_text = 0x01, ///< %x1 denotes a text frame
+ WSop_binary = 0x02, ///< %x2 denotes a binary frame
+ ///< %x3-7 are reserved for further non-control frames
+ WSop_close = 0x08, ///< %x8 denotes a connection close
+ WSop_ping = 0x09, ///< %x9 denotes a ping
+ WSop_pong = 0x0A ///< %xA denotes a pong
+ ///< %xB-F are reserved for further control frames
+} WSopcode_t;
+
+typedef struct {
+ bool fin;
+ bool rsv1;
+ bool rsv2;
+ bool rsv3;
+
+ WSopcode_t opCode;
+ bool mask;
+
+ size_t payloadLen;
+
+ uint8_t * maskKey;
+} WSMessageHeader_t;
+
+typedef struct {
+ uint8_t num; ///< connection number
+
+ WSclientsStatus_t status;
+
+ WEBSOCKETS_NETWORK_CLASS * tcp;
+
+ bool isSocketIO; ///< client for socket.io server
+
+#if defined(HAS_SSL)
+ bool isSSL; ///< run in ssl mode
+ WEBSOCKETS_NETWORK_SSL_CLASS * 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
+
+ bool pongReceived;
+ uint32_t pingInterval; // how often ping will be sent, 0 means "heartbeat is not active"
+ uint32_t lastPing; // millis when last pong has been received
+ uint32_t pongTimeout; // interval in millis after which pong is considered to timeout
+ uint8_t disconnectTimeoutCount; // after how many subsequent pong timeouts discconnect will happen, 0 means "do not disconnect"
+ uint8_t pongTimeoutCount; // current pong timeout count
+
+#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+ String cHttpLine; ///< HTTP header lines
+#endif
+
+} WSclient_t;
+
+class WebSockets {
+ protected:
+#ifdef __AVR__
+ typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok);
+#else
+ typedef std::function WSreadWaitCb;
+#endif
+
+ virtual void clientDisconnect(WSclient_t * client) = 0;
+ virtual bool clientIsConnected(WSclient_t * client) = 0;
+
+ void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
+
+ virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0;
+
+ uint8_t createHeader(uint8_t * buf, WSopcode_t opcode, size_t length, bool mask, uint8_t maskKey[4], bool fin);
+ bool sendFrameHeader(WSclient_t * client, WSopcode_t opcode, size_t length = 0, bool fin = true);
+ bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool fin = true, bool headerToPayload = false);
+
+ void headerDone(WSclient_t * client);
+
+ void handleWebsocket(WSclient_t * client);
+
+ bool handleWebsocketWaitFor(WSclient_t * client, size_t size);
+ void handleWebsocketCb(WSclient_t * client);
+ void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload);
+
+ String acceptKey(String & clientKey);
+ String base64_encode(uint8_t * data, size_t length);
+
+ bool readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb);
+ virtual size_t write(WSclient_t * client, uint8_t * out, size_t n);
+ size_t write(WSclient_t * client, const char * out);
+
+ void enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
+ void handleHBTimeout(WSclient_t * client);
+};
+
+#ifndef UNUSED
+#define UNUSED(var) (void)(var)
+#endif
+#endif /* WEBSOCKETS_H_ */
diff --git a/src/WebSocketsServer.cpp b/src/WebSocketsServer.cpp
new file mode 100644
index 00000000..633e34c2
--- /dev/null
+++ b/src/WebSocketsServer.cpp
@@ -0,0 +1,852 @@
+/**
+ * @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_ping:
+ type = WStype_PING;
+ break;
+ case WSop_pong:
+ type = WStype_PONG;
+ break;
+ case WSop_close:
+ 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(':') >= 0) {
+ String headerName = headerLine->substring(0, headerLine->indexOf(':'));
+ String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
+
+ // remove space in the beginning (RFC2616)
+ if(headerValue[0] == ' ') {
+ headerValue.remove(0, 1);
+ }
+
+ if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
+ headerValue.toLowerCase();
+ if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) {
+ client->cIsUpgrade = true;
+ }
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
+ if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
+ client->cIsWebsocket = true;
+ }
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
+ client->cVersion = headerValue.toInt();
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) {
+ client->cKey = headerValue;
+ client->cKey.trim(); // see rfc6455
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
+ client->cProtocol = headerValue;
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
+ client->cExtensions = headerValue;
+ } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) {
+ client->base64Authorization = headerValue;
+ } else {
+ client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue);
+ if(_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) {
+ client->cMandatoryHeadersCount++;
+ }
+ }
+
+ } else {
+ DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
+ }
+
+ (*headerLine) = "";
+#if(WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+ client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&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);
+ }
+ }
+}
diff --git a/src/WebSocketsServer.h b/src/WebSocketsServer.h
new file mode 100644
index 00000000..a517da64
--- /dev/null
+++ b/src/WebSocketsServer.h
@@ -0,0 +1,206 @@
+/**
+ * @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 (2)
+#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 WebSocketServerEvent;
+ typedef std::function 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_ */
diff --git a/src/si5351mcu.cpp b/src/si5351mcu.cpp
new file mode 100644
index 00000000..44fe98c4
--- /dev/null
+++ b/src/si5351mcu.cpp
@@ -0,0 +1,301 @@
+/*
+ * si5351mcu - Si5351 library for Arduino MCU tuned for size and click-less
+ *
+ * Copyright (C) 2017 Pavel Milanes
+ *
+ * Many chunk of codes are derived-from/copied from other libs
+ * all GNU GPL licenced:
+ * - Linux Kernel (www.kernel.org)
+ * - Hans Summers libs and demo code (qrp-labs.com)
+ * - Etherkit (NT7S) Si5351 libs on github
+ * - DK7IH example.
+ * - Jerry Gaffke integer routines for the bitx20 group
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "Arduino.h"
+#include "si5351mcu.h"
+
+// wire library loading, if not defined
+#ifndef WIRE_H
+ #include "Wire.h"
+#endif
+
+/*****************************************************************************
+ * This is the default init procedure, it set the Si5351 with this params:
+ * XTAL 27.000 Mhz
+ *****************************************************************************/
+ void Si5351mcu::init() {
+ // init with the default freq
+ init(int_xtal);
+}
+
+
+/*****************************************************************************
+ * This is the custom init procedure, it's used to pass a custom xtal
+ * and has the duty of init the I2C protocol handshake
+ *****************************************************************************/
+ void Si5351mcu::init(uint32_t nxtal) {
+ // set the new base xtal freq
+ base_xtal = int_xtal = nxtal;
+
+ // start I2C (wire) procedures
+ Wire.begin();
+
+ // power off all the outputs
+ off();
+}
+
+
+/*****************************************************************************
+ * This function set the freq of the corresponding clock.
+ *
+ * In my tests my Si5351 can work between 7,8 Khz and ~225 Mhz [~250 MHz with
+ * overclocking] as usual YMMV
+ *
+ * Click noise:
+ * - The lib has a reset programmed [aka: click noise] every time it needs to
+ * change the output divider of a particular MSynth, if you move in big steps
+ * this can lead to an increased rate of click noise per tunning step.
+ * - If you move at a pace of a few Hz each time the output divider will
+ * change at a low rate, hence less click noise per tunning step.
+ * - The output divider moves [change] faster at high frequencies, so at HF the
+ * clikc noise is at the real minimum possible.
+ *
+ * [See the README.md file for other details]
+ ****************************************************************************/
+void Si5351mcu::setFreq(uint8_t clk, uint32_t freq) {
+ uint8_t a, R = 1, shifts = 0;
+ uint32_t b, c, f, fvco, outdivider;
+ uint32_t MSx_P1, MSNx_P1, MSNx_P2, MSNx_P3;
+
+ // Overclock option
+ #ifdef SI_OVERCLOCK
+ // user a overclock setting for the VCO, max value in my hardware
+ // was 1.05 to 1.1 GHz, as usual YMMV [See README.md for details]
+ outdivider = SI_OVERCLOCK / freq;
+ #else
+ // normal VCO from the datasheet and AN
+ // With 900 MHz beeing the maximum internal PLL-Frequency
+ outdivider = 900000000 / freq;
+ #endif
+
+ // use additional Output divider ("R")
+ while (outdivider > 900) {
+ R = R * 2;
+ outdivider = outdivider / 2;
+ }
+
+ // finds the even divider which delivers the intended Frequency
+ if (outdivider % 2) outdivider--;
+
+ // Calculate the PLL-Frequency (given the even divider)
+ fvco = outdivider * R * freq;
+
+ // Convert the Output Divider to the bit-setting required in register 44
+ switch (R) {
+ case 1: R = 0; break;
+ case 2: R = 16; break;
+ case 4: R = 32; break;
+ case 8: R = 48; break;
+ case 16: R = 64; break;
+ case 32: R = 80; break;
+ case 64: R = 96; break;
+ case 128: R = 112; break;
+ }
+
+ // we have now the integer part of the output msynth
+ // the b & c is fixed below
+ MSx_P1 = 128 * outdivider - 512;
+
+ // calc the a/b/c for the PLL Msynth
+ /***************************************************************************
+ * We will use integer only on the b/c relation, and will >> 5 (/32) both
+ * to fit it on the 1048 k limit of C and keep the relation
+ * the most accurate possible, this works fine with xtals from
+ * 24 to 28 Mhz.
+ *
+ * This will give errors of about +/- 2 Hz maximum
+ * as per my test and simulations in the worst case, well below the
+ * XTAl ppm error...
+ *
+ * This will free more than 1K of the final eeprom
+ *
+ ****************************************************************************/
+ a = fvco / int_xtal;
+ b = (fvco % int_xtal) >> 5; // Integer par of the fraction
+ // scaled to match "c" limits
+ c = int_xtal >> 5; // "c" scaled to match it's limits
+ // in the register
+
+ // f is (128*b)/c to mimic the Floor(128*(b/c)) from the datasheet
+ f = (128 * b) / c;
+
+ // build the registers to write
+ MSNx_P1 = 128 * a + f - 512;
+ MSNx_P2 = 128 * b - f * c;
+ MSNx_P3 = c;
+
+ // PLLs and CLK# registers are allocated with a shift, we handle that with
+ // the shifts var to make code smaller
+ if (clk > 0 ) shifts = 8;
+
+ // plls, A & B registers separated by 8 bytes
+ i2cWrite(26 + shifts, (MSNx_P3 & 65280) >> 8); // Bits [15:8] of MSNx_P3 in register 26
+ i2cWrite(27 + shifts, MSNx_P3 & 255);
+ i2cWrite(28 + shifts, (MSNx_P1 & 196608) >> 16);
+ i2cWrite(29 + shifts, (MSNx_P1 & 65280) >> 8); // Bits [15:8] of MSNx_P1 in register 29
+ i2cWrite(30 + shifts, MSNx_P1 & 255); // Bits [7:0] of MSNx_P1 in register 30
+ i2cWrite(31 + shifts, ((MSNx_P3 & 983040) >> 12) | ((MSNx_P2 & 983040) >> 16)); // Parts of MSNx_P3 and MSNx_P1
+ i2cWrite(32 + shifts, (MSNx_P2 & 65280) >> 8); // Bits [15:8] of MSNx_P2 in register 32
+ i2cWrite(33 + shifts, MSNx_P2 & 255); // Bits [7:0] of MSNx_P2 in register 33
+
+ // Write the output divider msynth only if we need to, in this way we can
+ // speed up the frequency changes almost by half the time most of the time
+ // and the main goal is to avoid the nasty click noise on freq change
+ if (omsynth[clk] != outdivider) {
+ // CLK# registers are exactly 8 * clk# bytes shifted from a base register.
+ shifts = clk * 8;
+
+ // multisynths
+ i2cWrite(42 + shifts, 0); // Bits [15:8] of MS0_P3 (always 0) in register 42
+ i2cWrite(43 + shifts, 1); // Bits [7:0] of MS0_P3 (always 1) in register 43
+ // See datasheet, special trick when R=4
+ if (outdivider == 4) {
+ i2cWrite(44 + shifts, 12 | R);
+ i2cWrite(45 + shifts, 0); // Bits [15:8] of MSx_P1 must be 0
+ i2cWrite(46 + shifts, 0); // Bits [7:0] of MSx_P1 must be 0
+ } else {
+ i2cWrite(44 + shifts, ((MSx_P1 & 196608) >> 16) | R); // Bits [17:16] of MSx_P1 in bits [1:0] and R in [7:4]
+ i2cWrite(45 + shifts, (MSx_P1 & 65280) >> 8); // Bits [15:8] of MSx_P1 in register 45
+ i2cWrite(46 + shifts, MSx_P1 & 255); // Bits [7:0] of MSx_P1 in register 46
+ }
+ i2cWrite(47 + shifts, 0); // Bits [19:16] of MS0_P2 and MS0_P3 are always 0
+ i2cWrite(48 + shifts, 0); // Bits [15:8] of MS0_P2 are always 0
+ i2cWrite(49 + shifts, 0); // Bits [7:0] of MS0_P2 are always 0
+
+ // must reset the so called "PLL", in fact the output msynth
+ reset();
+
+ // keep track of the change
+ omsynth[clk] = (uint16_t)outdivider;
+ }
+}
+
+
+/*****************************************************************************
+ * Reset of the PLLs and multisynths output enable
+ *
+ * This must be called to soft reset the PLLs and cycle the output of the
+ * multisynths: this is the "click" noise source in the RF spectrum.
+ *
+ * So it must be avoided at all costs, so this lib just call it at the
+ * initialization of the PLLs and when a correction is applied
+ *
+ * If you are concerned with accuracy you can implement a reset every
+ * other Mhz to be sure it get exactly on spot.
+ ****************************************************************************/
+void Si5351mcu::reset(void) {
+ // This soft-resets PLL A & B (32 + 128) in just one step
+ i2cWrite(177, 0xA0);
+}
+
+
+/*****************************************************************************
+ * Function to disable all outputs
+ *
+ * The PLL are kept running, just the m-synths are powered off.
+ *
+ * This allows to keep the chip warm and exactly on freq the next time you
+ * enable an output.
+ ****************************************************************************/
+void Si5351mcu::off() {
+ // This disable all the CLK outputs
+ for (byte i=0; i<3; i++) disable(i);
+}
+
+
+/*****************************************************************************
+ * Function to set the correction in Hz over the Si5351 XTAL.
+ *
+ * This will call a reset of the PLLs and multi-synths so it will produce a
+ * click every time it's called
+ ****************************************************************************/
+void Si5351mcu::correction(int32_t diff) {
+ // apply some corrections to the xtal
+ int_xtal = base_xtal + diff;
+
+ // reset the PLLs to apply the correction
+ reset();
+}
+
+
+/*****************************************************************************
+ * This function enables the selected output
+ *
+ * Beware: ZERO is clock output enabled
+ *****************************************************************************/
+void Si5351mcu::enable(uint8_t clk) {
+ // var to handle the mask of the registers value
+ uint8_t m = SICLK0_R;
+ if (clk > 0) m = SICLK12_R;
+
+ // write the register value
+ i2cWrite(16 + clk, m + clkpower[clk]);
+
+ // 1 & 2 are mutually exclusive
+ if (clk == 1) disable(2);
+ if (clk == 2) disable(1);
+
+ // update the status of the clk
+ clkOn[clk] = 1;
+}
+
+
+/*****************************************************************************
+ * This function disables the selected output
+ *
+ * Beware: ONE is clock output disabled
+ * *****************************************************************************/
+void Si5351mcu::disable(uint8_t clk) {
+ // send
+ i2cWrite(16 + clk, 128);
+
+ // update the status of the clk
+ clkOn[clk] = 0;
+}
+
+
+/****************************************************************************
+ * Set the power output for each output independently
+ ***************************************************************************/
+void Si5351mcu::setPower(byte clk, byte power) {
+ // set the power to the correct var
+ clkpower[clk] = power;
+
+ // now enable the output to get it applied
+ enable(clk);
+}
+
+
+/****************************************************************************
+ * Private function to send the register data to the Si5351, arduino way.
+ ***************************************************************************/
+void Si5351mcu::i2cWrite(byte regist, byte value){
+ Wire.beginTransmission(SIADDR);
+ Wire.write(regist);
+ Wire.write(value);
+ Wire.endTransmission();
+}
diff --git a/src/si5351mcu.h b/src/si5351mcu.h
new file mode 100644
index 00000000..b5789c2b
--- /dev/null
+++ b/src/si5351mcu.h
@@ -0,0 +1,141 @@
+/*
+ * si5351mcu - Si5351 library for Arduino MCU tuned for size and click-less
+ *
+ * Copyright (C) 2017 Pavel Milanes
+ *
+ * Many chunk of codes are derived-from/copied from other libs
+ * all GNU GPL licenced:
+ * - Linux Kernel (www.kernel.org)
+ * - Hans Summers libs and demo code (qrp-labs.com)
+ * - Etherkit (NT7S) Si5351 libs on github
+ * - DK7IH example.
+ * - Jerry Gaffke integer routines for the bitx20 group
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+
+/****************************************************************************
+ * This lib tricks:
+ *
+ * CLK0 will use PLLA
+ * CLK1 will use PLLB
+ * CLK2 will use PLLB
+ *
+ * Lib defaults
+ * - XTAL is 27 Mhz.
+ * - Always put the internal 8pF across the xtal legs to GND
+ * - lowest power output (2mA)
+ * - After the init all outputs are off, you need to enable them in your code.
+ *
+ * The correction procedure is not for the PPM as other libs, this
+ * is just +/- Hz to the XTAL freq, you may get a click noise after
+ * applying a correction
+ *
+ * The init procedure is mandatory as it set the Xtal (the default or a custom
+ * one) and prepare the Wire (I2C) library for operation.
+ ****************************************************************************/
+
+#ifndef SI5351MCU_H
+#define SI5351MCU_H
+
+// rigor includes
+#include "Arduino.h"
+#include "Wire.h"
+
+// default I2C address of the Si5351
+#define SIADDR 0x60
+
+// register's power modifiers
+#define SIOUT_2mA 0
+#define SIOUT_4mA 1
+#define SIOUT_6mA 2
+#define SIOUT_8mA 3
+
+// registers base (2mA by default)
+#define SICLK0_R 76 // 0b1001100
+#define SICLK12_R 108 // 0b1101100
+
+
+class Si5351mcu {
+ public:
+ // default init procedure
+ void init(void);
+
+ // custom init procedure (XTAL in Hz);
+ void init(uint32_t);
+
+ // reset all PLLs
+ void reset(void);
+
+ // set CLKx(0..2) to freq (Hz)
+ void setFreq(uint8_t, uint32_t);
+
+ // pass a correction factor
+ void correction(int32_t);
+
+ // enable some CLKx output
+ void enable(uint8_t);
+
+ // disable some CLKx output
+ void disable(uint8_t);
+
+ // disable all outputs
+ void off(void);
+
+ // set power output to a specific clk
+ void setPower(uint8_t, uint8_t);
+
+ // var to check the clock state
+ bool clkOn[3] = {0, 0, 0};
+
+
+ private:
+ // used to talk with the chip, via Arduino Wire lib
+ void i2cWrite(uint8_t, uint8_t);
+
+ // base xtal freq, over this we apply the correction factor
+ // by default 27 MHz
+ uint32_t base_xtal = 27000000L;
+
+ // this is the work value, with the correction applied
+ // via the correction() procedure
+ uint32_t int_xtal = base_xtal;
+
+ // clk# power holders (2ma by default)
+ uint8_t clkpower[3] = {0, 0, 0};
+
+ // local var to keep track of when to reset the "pll"
+ /*********************************************************
+ * BAD CONCEPT on the datasheet and AN:
+ *
+ * The chip has a soft-reset for PLL A & B but in
+ * practice the PLL does not need to be reseted.
+ *
+ * Test shows that if you fix the Msynth output
+ * dividers and move any of the VCO from bottom to top
+ * the frequency moves smooth and clean, no reset needed
+ *
+ * The reset is needed when you changes the value of the
+ * Msynth output divider, even so it's not always needed
+ * so we use this var to keep track of all three and only
+ * reset the "PLL" when this value changes to be sure
+ *
+ * It's a word (16 bit) because the final max value is 900
+ *********************************************************/
+ uint16_t omsynth[3] = {0, 0, 0};
+};
+
+
+#endif //SI5351MCU_H
diff --git a/tv5725.h b/tv5725.h
new file mode 100644
index 00000000..f2a1a4d1
--- /dev/null
+++ b/tv5725.h
@@ -0,0 +1,806 @@
+#ifndef TV5725_H_
+#define TV5725_H_
+
+#include "tw.h"
+
+#define GBS_ADDR 0x17 // 7 bit GBS I2C Address
+
+namespace detail {
+struct TVAttrs {
+ // Segment register at 0xf0, no bit offset, 8 bits, initial value assumed invalid
+ static const uint8_t SegByteOffset = 0xf0;
+ static const uint8_t SegBitOffset = 0;
+ static const uint8_t SegBitWidth = 8;
+ static const uint8_t SegInitial = 0xff;
+};
+}
+
+template
+class TV5725 : public tw::SegmentedSlave {
+ private:
+ // Stupid template boilerplate
+ typedef tw::SegmentedSlave Base;
+ using typename Base::SegValue;
+ template