diff --git a/ESP-1ch-Gateway-v5.0-master/.gitignore b/ESP-1ch-Gateway-v5.0-master/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/ESP-1ch-Gateway-v5.0-master/CHANGELOG.md b/ESP-1ch-Gateway-v5.0-master/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..c1c66a1055ccaafe08e7d83c20e6d8f0f80d7efd
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/CHANGELOG.md
@@ -0,0 +1,230 @@
+# Single Channel LoRaWAN Gateway
+
+Version 5.3.3, August 25, 2018  
+Author: M. Westenberg (mw12554@hotmail.com)  
+Copyright: M. Westenberg (mw12554@hotmail.com)  
+
+All rights reserved. This program and the accompanying materials are made available under the terms 
+of the MIT License which accompanies this distribution, and is available at
+https://opensource.org/licenses/mit-license.php  
+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.
+
+Maintained by Maarten Westenberg (mw12554@hotmail.com)
+
+
+
+# Release Notes
+
+New features in version 5.3.3 (August 25, 2018)
+- Bug Fixing SPIFFS Format in GUI
+- Included a confirm dialog in RESET, BOOT and STATISTICS buttons for user to confirm
+- Repaired WlanConnect issues
+- Improved Downlink function. Work for SF8-SF10. Does not work reliable for SF7, SF11, SF12
+- Provided documentation button o top of page
+- Added expert mode button in GUI (Wifi, System and Interrupt data is only shown in expert mode)
+- bug fixes and documentation
+
+New features in version 5.3.2 (July 07, 2018)
+- Support for local decoding of sensor messages received by the gateway. 
+	Use #define _LOCALSERVER 1, to enable this functionality. Also specify for each node that you want
+	to inspect the messages from the NwkSKey and AppSKey in the sensor.h file.
+	NOTE: the heap of the ESP32 is much larger than of the ESP8266. SO please be careful not to add too many 
+		features to the "old" gateway modules
+- As a result reworked the sensor functions and changes such as adding/changing DevAddrm NwkSKEY 
+	and AppSKey parameters to several functions
+- Several in-line documentaton enhancements and typos were fixed
+
+New features in version 5.3.1 (June 30, 2018)
+- Included support for T-Beam board including on board GPS sensor (_sensor.ino). #define GATEWAYNODE 1 will
+	turn the gateway into a node as well. Remember to set the address etc in ESP-sc-gway.h.
+- First version to explore possibilities of 433 MHz LoRa frequencies. 
+	Included _LFREQ setting in the ESP-sc-gway.h file
+- Changes to the WiFi inplementation. The gateway does now store the SSID and poassword.
+- 
+
+New features in version 5.3.0 (June 20, 2018)
+- Connect to both public and private routers
+
+
+New features in version 5.2.1 (June 6, 2018)
+- Repair the downlink functions
+- Repair sersor functions
+- Bufgixes
+
+New features in version 5.2.0 (May 30, 2018)
+- Enable support for ESP32 from TTGO, several code changes where ESP32 differs from ESP8266. 
+OLED is supported but NOT tested. Some hardware specific reporting functions of the WebGUI do not work yet.
+- Include new ESP32WebServer library for support of ESP32
+- Made pin configuration definitions in Gateway.h file, and support in loraModem.h and .ino files.
+
+New features in version 5.1.1 (May 17, 2018)
+- The LOG button in the GUI now opens a txt .CSV file in the browser with loggin details.
+- Improved debugging in WebGUI, not only based on debug level but also on part of the software we want to debug.
+- Clean up of StateMachine
+- Enable filesystem formatting from the GUI
+
+New features in version 5.1.0 (May 03, 2018)
+- Improved debuggin in WebGUI, not only based on debug level but also on part of the software we want to debug.
+- Clean up of StateMachine
+
+New features in version 5.0.9 (Apr 08, 2018)
+- In statistics overview the option is added to specify names for known nodes (in ESP-sc-gateway.h file)
+- Keep track of the amount of messages per channel (only 3 channels supported).
+- Use the SPIFFS filesystem to provide log statistics of messages. Use the GUI and on the top select log. 
+The log data is displayed in the USB Serial area (for the moment).
+- Remove a lot of the debug==1 and debug==2 messages as they are not useful.
+
+New features in version 5.0.8 (Mar 26, 2018)
+- Simplified State machine and removed unnecessary code
+- Changed the WiFI Disconnect code (bug in SDK). When WiFi.begin() is executed, 
+  the previous accesspoint is not deleted. But the ESP8266 does also not connect 
+  to it anymore. The workaround restarts the WiFI completely.
+- Repair the bug causing the Channel setting to switch back to channel 0 when the RFM95 modem is reset 
+  (after upstream message)
+- Introduced the _utils.ino with Serial line utilities
+- Documentation Changes
+- Small bug fixes
+- Removal of unused global variables
+
+New features in version 5.0.7 (Feb 12, 2018)
+- On low debug value (0) we show the time in the rx status message on 
+- WlanConnect function updated
+
+New features in version 5.0.7 (Feb 24, 2018)
+- Changed WlaConnect function to not hang and give more debug info
+- Made the change to display correct MAC address info
+
+New features in version 5.0.6 (Feb 11, 2018)
+- All timer functions that show lists on website etc are now based on now() en NTP, 
+the realtime functions needed for LoRa messages are still based on micros() or millis()
+- Change some USB debug messages
+- Added to the documentation of the README.md
+
+New Features in version 5.0.5 (Feb 2, 2018)
+- Change timer functions to now() and secons instead of millis() as the latter one overflows once 
+every 50 days.
+- Add more debug information
+- Simplified and enhanced the State Machine function
+
+New features in version 5.0.4 (January 1, 2018)
+- Cleanup of the State machine
+- Separate file for oLED work, support for 1.3" SH3006 chips based oLED.
+- Still not supported: Multi Frequency works, but with loss of #packages, 
+  and some packages are recognizeg at the wrong frequency (but since they are so close that could happen).
+- In-line documenattion cleaned up
+
+New features in version 5.0.1 (November 18, 2017)
+- Changed the state machine to run in user space only
+- No Watchdog Resets anymore
+- For each SF, percentage of such packages received of total packages
+- OTAA and downlink work (again) although not always
+- Nober of packages per hour displayed in webserver
+- All Serial communication only when DUSB==1 is defined at compile time
+
+New features in version 4.0.9 (August 11, 2017)
+
+- This release contains updates for memory leaks in several Gateway files
+- Also changes in OLED functions
+
+New features in version 4.0.8 (August 05, 2017)
+
+- This release updates for memory leaks in NTP routines (see ESP-sc-gway.h file for NTP_INTR
+- OLED support contributed by Dorijan Morelj (based on Adreas Spies' release)
+
+New features in version 4.0.7 (July 22, 2017)
+
+- This release contains merely updates to memory leaks and patches to avoid chip resets etc.
+- The webinterface allows the user to see more parameters and has buttons to set/reset these parameters.
+- By setting debug >=2, the webinterface will display more information.
+- The gateway allows OTA (Over the Air) update. Please have an Apple "Bonjour" somewhere on your network 
+(included in iTunes) and you will see the network port in the "Port" section of your IDE.
+
+New features in version 4.0.4 (June 24, 2017):
+
+- Review of the _wwwServer.ino file. Repaired some of the bugs causing crashes of the webserver.
+- Updated the README.md file with more cofniguration information
+
+New features in version 4.0.3 (June 22, 2017):
+
+- Added CMAC functions so that the sensor functions work as expected over TTN
+- Webserver prints a page in chunks now, so that memory usage is lower and more heap is left over for variables
+- Webserver does refresh every 60 seconds
+- Implemented suggested change of M. for answer to PKT_PULL_RESP
+- Updated README.md to correctly displa all headers
+- Several small bug fixes
+
+New features in version 4.0.0 (January 26, 2017)):
+
+- Implement both CAD (Channel Activity Detection) and HOP functions (HOP being experimental)
+- Message history visible in web interface
+- Repaired the WWW server memory leak (due to String assignments)
+- Still works on one interrupt line (GPIO15), or can be configured to work with 2 interrupt lines for dio0 and dio1
+  for two or more interrupt lines (better performance for automatic SF setting?)
+- Webserver with debug level 3 and level 4 (for interrupt testing).
+  dynamic setting thorugh the web interface. Level 3 and 4 will show more info
+  on sf, rssi, interrupt flags etc.
+- Tested on Arduino IDE 1.18.0
+- See http://things4u.github.io for documentation
+
+New features in version 3.3.0 (January 1, 2017)):
+
+- Redesign of the Webserver interface
+- Use of the SPIFFS filesystem to store SSID, Frequency, Spreading Factor and Framecounter to survice reboots and resets of the ESP8266
+- Possibility to set the Spreading Factor dynamically throug the web interface
+- Possibility to set the Frequency in the web interface
+- Reset the Framecounter in te webinterface
+
+New features in version 3.2.2 (December 29, 2016)):
+
+- Repair the situation where WIFIMANAGER was set to 0 in the ESP-sc-gway.h file. The sketch would not compile which is now repaired
+- The compiler would issue a set of warnings related to the ssid and passw setting in the ESP-sc-gway.h file. Compiler was complaining (and it should) because char* were statically initialised and modified in the code.
+
+New features in version 3.2.1 (December 20, 2016)):
+
+- Repair the status messages to the server. All seconds, minutes, hours etc. are now reported in 2 digits. The year is reported in 4 digits.
+
+New features in version 3.2.0 (December 08, 2016)):
+
+- Several bugfixes
+
+New features in version 3.1 (September 29, 2016)):
+
+- In the ESP-sc-gway.h it is possible to set the gateway as sensor node as well. Just set the DevAddr and AppSKey in the _sensor.ino file and be able to forward any sensor or other values to the server as if they were coming from a LoRa node.
+- If the #define _STRICT_1CH is set (to 1) then the system will be able to send downlink messages to LoRa nodes that are strict 1-channel devices (all frequencies but frequency 0 are disabled and Spreading Factor (SF) is fixed to one value).
+- Code clean-up. The code has been made smaller in the area of loraWait() functions and where the radio is initiated for receiving of transmitting messages.
+- Several small bug fixes
+- Licensing, the license has been changed to MIT
+
+New features in version 3.0 (September 27, 2016):
+
+- WiFiManager support
+- Limited SPIFF (filesystem) support for persistent data storage
+- Added functions to webserver. Webserver port now 80 by default (!)
+
+Other
+
+- Supports ABP nodes (TeensyLC and Arduino Pro-mini)
+- Supports OTAA functions on TeensyLC and Arduino Pro-Mini (not all of them) for SF7 and SF8.
+- Supports SF7, SF8. SF7 is tested for downstream communication
+- Listens on configurable frequency and spreading factor
+- Send status updates to server (keepalive)
+- PULL_DATA messages to server
+- It can forward messages to two servers at the same time (and read from them as well)
+- DNS support for server lookup
+- NTP Support for time sync with internet time servers
+- Webserver support (default port 8080)
+- .h header file for configuration
+
+Not (yet) supported:
+
+- SF7BW250 modulation
+- FSK modulation
+- RX2 timeframe messages at frequency 869,525 MHz are not (yet) supported.
+- SF9-SF12 downlink messaging available but needs more testing
+
+
+# License
+
+The source files of the gateway sketch in this repository is made available under the MIT
+license. The libraries included in this repository are included for convenience only and all have their own license, and are not part of the ESP 1ch gateway code.
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/ESP-sc-gway.h b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/ESP-sc-gway.h
new file mode 100644
index 0000000000000000000000000000000000000000..37a7f6ac8e503696e955954896f30c8cb0d86cd8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/ESP-sc-gway.h
@@ -0,0 +1,272 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3 H
+// Date: 2018-08-25
+//
+// Based on work done by Thomas Telkamp for Raspberry PI 1ch gateway and many others.
+// Contibutions of Dorijan Morelj and Andreas Spies for OLED support.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains a number of compile-time settings that can be set on (=1) or off (=0)
+// The disadvantage of compile time is minor compared to the memory gain of not having
+// too much code compiled and loaded on your ESP8266.
+//
+// ----------------------------------------------------------------------------------------
+
+#define VERSION "V.5.3.3.H; 180825a"
+
+// This value of DEBUG determines whether some parts of code get compiled.
+// Also this is the initial value of debug parameter. 
+// The value can be changed using the admin webserver
+// For operational use, set initial DEBUG vaulue 0
+#define DEBUG 1
+
+// Debug message will be put on Serial is this one is set.
+// If set to 0, not USB Serial prints are done
+// Set to 1 it will prinr all user level messages (with correct debug set)
+// If set to 2 it will also print interrupt messages (not recommended)
+#define DUSB 1
+
+// Define whether we should do a formatting of SPIFFS when starting the gateway
+// This is usually a good idea if the webserver is interrupted halfway a writing
+// operation.
+// Normally, value 0 is a good default.
+#define _SPIFF_FORMAT 0
+
+// Define the LoRa Frequncy band that is used. TTN Supported values are 925MHz, 868MHz and 433MHz.
+// So supported values are: 433 868 915
+#define _LFREQ 915
+
+// The spreading factor is the most important parameter to set for a single channel
+// gateway. It specifies the speed/datarate in which the gateway and node communicate.
+// As the name says, in principle the single channel gateway listens to one channel/frequency
+// and to one spreading factor only.
+// This parameters contains the default value of SF, the actual version can be set with
+// the webserver and it will be stored in SPIFF
+// NOTE: The frequency is set in the loraModem.h file and is default 868.100000 MHz.
+#define _SPREADING SF9
+
+// Channel Activity Detection
+// This function will scan for valid LoRa headers and determine the Spreading 
+// factor accordingly. If set to 1 we will use this function which means the 
+// 1-channel gateway will become even more versatile. If set to 0 we will use the
+// continuous listen mode.
+// Using this function means that we HAVE to use more dio pins on the RFM95/sx1276
+// device and also connect enable dio1 to detect this state. 
+#define _CAD 1
+
+// Definitions for the admin webserver.
+// A_SERVER determines whether or not the admin webpage is included in the sketch.
+// Normally, leave it in!
+#define A_SERVER 1				// Define local WebServer only if this define is set
+#define A_REFRESH 1				// Allow the webserver refresh or not?
+#define A_SERVERPORT 80			// Local webserver port (normally 80)
+#define A_MAXBUFSIZE 192		// Must be larger than 128, but small enough to work
+
+// Definitions for over the air updates. At the moment we support OTA with IDE
+// Make sure that tou have installed Python version 2.7 and have Bonjour in your network.
+// Bonjour is included in iTunes (which is free) and OTA is recommended to install 
+// the firmware on your router witout having to be really close to the gateway and 
+// connect with USB.
+#define A_OTA 1
+
+// We support a few pin-out configurations out-of-the-box: HALLARD, COMPRESULT and TTGO ESP32.
+// If you use one of these two, just set the parameter to the right value.
+// If your pin definitions are different, update the loraModem.h file to reflect these settings.
+//	1: HALLARD
+//	2: COMRESULT pin out
+//	3: ESP32 Wemos pin out
+//	4: ESP32 TTGO pinning (should work for 433 and OLED too).
+//	5: ESP32 TTGO EU433 MHz with OLED
+//	6: Other, define your own in loraModem.h
+#define _PIN_OUT 6
+
+// Gather statistics on sensor and Wifi status
+// 0= No statistics
+// 1= Keep track of messages statistics, number determined by MAX_STAT
+// 2= Option 1 + Keep track of messages received PER each SF (default)
+// 3= See Option 2, but with extra channel info (Do not use when no Hopping is done)
+#define STATISTICS 3
+
+// Maximum number of statistics records gathered. 20 is a good maximum (memory intensive)
+#define MAX_STAT 20
+
+// Single channel gateways if they behave strict should only use one frequency 
+// channel and one spreading factor. However, the TTN backend replies on RX2 
+// timeslot for spreading factors SF9-SF12. 
+// Also, the server will respond with SF12 in the RX2 timeslot.
+// If the 1ch gateway is working in and for nodes that ONLY transmit and receive on the set
+// and agreed frequency and spreading factor. make sure to set STRICT to 1.
+// In this case, the frequency and spreading factor for downlink messages is adapted by this
+// gateway
+// NOTE: If your node has only one frequency enabled and one SF, you must set this to 1
+//		in order to receive downlink messages
+// NOTE: In all other cases, value 0 works for most gateways with CAD enabled
+#define _STRICT_1CH	0
+
+// Allows configuration through WifiManager AP setup. Must be 0 or 1					
+#define WIFIMANAGER 0
+
+// Define the name of the accesspoint if the gateway is in accesspoint mode (is
+// getting WiFi SSID and password using WiFiManager)
+#define AP_NAME "YourName"
+#define AP_PASSWD "YourPassword"
+
+// This section defines whether we use the gateway as a repeater
+// For his, we use another output channle as the channel (default==0) we are 
+// receiving the messages on.
+#define REPEATER 0
+
+// Will we use Mutex or not?
+// +SPI is input for SPI, SPO is output for SPI
+#define MUTEX 0
+
+// Define if OLED Display is connected to I2C bus. Note that defining an OLED display does not
+// impact performance very much, certainly if no OLED is connected. Wrong OLED will not show
+// sensible results on display
+// OLED==0; No OLED display connected
+// OLED==1; 0.9 Oled Screen based on SSD1306
+// OLED==2;	1"3 Oled screens for Wemos, 128x64 SH1106
+#define OLED 0
+
+
+// Define whether we want to manage the gateway over UDP (next to management 
+// thru webinterface).
+// This will allow us to send messages over the UDP connection to manage the gateway 
+// and its parameters. Sometimes the gateway is not accesible from remote, 
+// in this case we would allow it to use the SERVER UDP connection to receive 
+// messages as well.
+// NOTE: Be aware that these messages are NOT LoRa and NOT LoRa Gateway spec compliant.
+//	However that should not interfere with regular gateway operation but instead offer 
+//	functions to set/reset certain parameters from remote.
+#define GATEWAYMGT 0
+
+// Do extensive loggin
+// Use the ESP8266 SPIFS filesystem to do extensive logging.
+// We must take care that the filesystem never(!) is full, and for that purpose we
+// rather have new records/line of statistics than very old.
+// Of course we must store enough records to make the filesystem work
+//
+#define STAT_LOG 1
+
+
+// Name of he configfile in SPIFFs	filesystem
+// In this file we store the configuration and other relevant info that should
+// survive a reboot of the gateway		
+#define CONFIGFILE "/gwayConfig.txt"
+
+// Set the Server Settings (IMPORTANT)
+#define _LOCUDPPORT 1700					// UDP port of gateway! Often 1700 or 1701 is used for upstream comms
+
+// Timing
+#define _MSG_INTERVAL 15					// Reset timer in seconds
+#define _PULL_INTERVAL 55					// PULL_DATA messages to server to get downstream in milliseconds
+#define _STAT_INTERVAL 120					// Send a 'stat' message to server
+#define _NTP_INTERVAL 3600					// How often do we want time NTP synchronization
+#define _WWW_INTERVAL	60					// Number of seconds before we refresh the WWW page
+
+// MQTT definitions, these settings should be standard for TTN
+// and need not changing
+//#define _TTNPORT 1700						// Standard port for TTN
+//#define _TTNSERVER "router.eu.thethings.network"
+
+// If you have a second back-end server defined such as Semtech or loriot.io
+// your can define _THINGPORT and _THINGSERVER with your own value.
+// If not, make sure that you do not defined these, which will save CPU time
+// Port is UDP port in this program
+//
+// Default for testing: Switched off
+//#define _THINGPORT <port>					// e.g. 1700
+//#define _THINGSERVER "<dns.server.com>"	// Server URL of the LoRa-udp.js handler
+
+// This defines whether or not we would use the gateway as 
+// as sort of backend system which decodes messages (see sensor.h file)
+#define _LOCALSERVER 0						// See server definitions for decodes
+
+// Gateway Ident definitions
+#define _DESCRIPTION "Group2"			// Name of the gateway
+#define _EMAIL "stabrizchi2@huskers.unl.edu"		// Owner
+#define _PLATFORM "ESP8266"
+#define _LAT 52.0
+#define _LON 5.0
+#define _ALT 1								// Altitude
+
+// ntp
+// Please add daylight saving time to NTP_TIMEZONES when desired
+#define NTP_TIMESERVER "nl.pool.ntp.org"	// Country and region specific
+#define NTP_TIMEZONES	2					// How far is our Timezone from UTC (excl daylight saving/summer time)
+#define SECS_IN_HOUR	3600
+#define NTP_INTR 0							// Do NTP processing with interrupts or in loop();
+
+
+// Defines whether the gateway will also report sensor/status value on MQTT
+// after all, a gateway can be a node to the system as well
+// Set its LoRa address and key below in this file
+// See spec. para 4.3.2
+#define GATEWAYNODE 0
+#define _CHECK_MIC 0
+
+#if GATEWAYNODE==1
+#define _DEVADDR { 0x26, 0x01, 0x01, 0x01 }
+#define _APPSKEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define _NWKSKEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+#define _SENSOR_INTERVAL 300
+// For ESP32 based TTGO boards these two are normally included
+#define _GPS 0
+#define _BATTERY 0
+#endif
+
+// Define the correct radio type that you are using
+#define CFG_sx1276_radio		
+//#define CFG_sx1272_radio
+
+// Serial Port speed
+#define _BAUDRATE 115200					// Works for debug messages to serial momitor
+
+// We can put the gateway in such a mode that it will (only) recognize
+// nodes that are put in a list of trusted nodes 
+// Values:
+// 0: Do not use names for trusted Nodes
+// 1: Use the nodes as a translation tabel for hex codes to names (in TLN)
+// 2: Same as 1, but is nodes NOT in the nodes list below they are NOT
+//		forwarded or counted! (not yet fully implemented)
+#define TRUSTED_NODES 1
+#define TRUSTED_DECODE 1
+
+
+// Wifi definitions
+// WPA is an array with SSID and password records. Set WPA size to number of entries in array
+// When using the WiFiManager, we will overwrite the first entry with the 
+// accesspoint we last connected to with WifiManager
+// NOTE: Structure needs at least one (empty) entry.
+//		So WPASIZE must be >= 1
+struct wpas {
+	char login[32];							// Maximum Buffer Size (and allocated memory)
+	char passw[64];
+};
+
+// Please fill in at least ONE SSID and password from your own WiFI network
+// below. This is needed to get the gateway working
+// Note: DO NOT use the first and the last line of the stucture, these should be empty strings and
+//	the first line in te struct is reserved for WifiManager.
+//
+wpas wpa[] = {
+	{ "" , "" },							// Reserved for WiFi Manager
+	{ "NU-IoT", "vvfzfjrz" }
+};
+
+// For asserting and testing the following defines are used.
+//
+#if !defined(CFG_noassert)
+#define ASSERT(cond) if(!(cond)) gway_failed(__FILE__, __LINE__)
+#else
+#define ASSERT(cond) /**/
+#endif
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/ESP-sc-gway.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/ESP-sc-gway.ino
new file mode 100644
index 0000000000000000000000000000000000000000..ea8cda57931f8a08c1445be0874d09de9602e854
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/ESP-sc-gway.ino
@@ -0,0 +1,1485 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// Based on work done by Thomas Telkamp for Raspberry PI 1-ch gateway and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// The protocols and specifications used for this 1ch gateway: 
+// 1. LoRA Specification version V1.0 and V1.1 for Gateway-Node communication
+//	
+// 2. Semtech Basic communication protocol between Lora gateway and server version 3.0.0
+//	https://github.com/Lora-net/packet_forwarder/blob/master/PROTOCOL.TXT
+//
+// Notes: 
+// - Once call gethostbyname() to get IP for services, after that only use IP
+//	 addresses (too many gethost name makes the ESP unstable)
+// - Only call yield() in main stream (not for background NTP sync). 
+//
+// ----------------------------------------------------------------------------------------
+
+#include "ESP-sc-gway.h"						// This file contains configuration of GWay
+#include "Esp32MQTTClient.h"
+#include <HTTPClient.h>
+#include <Arduino.h>
+#if defined (ARDUINO_ARCH_ESP32) || defined(ESP32)
+#define ESP32_ARCH 1
+#endif
+
+#include <Esp.h>								// ESP8266 specific IDE functions
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <cstdlib>
+#include <sys/time.h>
+#include <cstring>
+#include <string>								// C++ specific string functions
+
+#include <SPI.h>								// For the RFM95 bus
+#include <TimeLib.h>							// http://playground.arduino.cc/code/time
+#include <DNSServer.h>							// Local DNSserver
+#include <ArduinoJson.h>
+#include <FS.h>									// ESP8266 Specific
+#include <WiFiUdp.h>
+#include <pins_arduino.h>
+#include <gBase64.h>							// https://github.com/adamvr/arduino-base64 (changed the name)
+
+// Local include files
+#include "loraModem.h"
+#include "loraFiles.h"
+#include "sensor.h"
+#include "oLED.h"
+
+extern "C" {
+#include "lwip/err.h"
+#include "lwip/dns.h"
+}
+
+#if WIFIMANAGER==1
+#include <WiFiManager.h>						// Library for ESP WiFi config through an AP
+#endif
+
+#if (GATEWAYNODE==1) || (_LOCALSERVER==1)
+#include "AES-128_V10.h"
+#endif
+
+
+// ----------- Specific ESP32 stuff --------------
+#if ESP32_ARCH==1								// IF ESP32
+
+#include "WiFi.h"
+#include <WiFiClient.h>
+#include <ESPmDNS.h>
+#include <SPIFFS.h>
+#if A_SERVER==1
+#include <ESP32WebServer.h>						// Dedicated Webserver for ESP32
+#include <Streaming.h>          				// http://arduiniana.org/libraries/streaming/
+#endif
+#if A_OTA==1
+#include <ESP32httpUpdate.h>					// Not yet available
+#include <ArduinoOTA.h>
+#endif//OTA
+
+// ----------- Generic ESP8266 stuff --------------
+#else
+
+#include <ESP8266WiFi.h>						// Which is specific for ESP8266
+#include <ESP8266mDNS.h>
+extern "C" {
+#include "user_interface.h"
+#include "c_types.h"
+}
+#if A_SERVER==1
+#include <ESP8266WebServer.h>
+#include <Streaming.h>          				// http://arduiniana.org/libraries/streaming/
+#endif //A_SERVER
+#if A_OTA==1
+#include <ESP8266httpUpdate.h>
+#include <ArduinoOTA.h>
+#endif//OTA
+
+#endif//ESP_ARCH
+
+// ----------- Declaration of vars --------------
+static const char* connectionString = "HostName=SepIoTHub.azure-devices.net;DeviceId=Node1;SharedAccessKey=dZfUU6HJc+TCioN8sbiCgSb/zVK0bgEOiTLKryj6lPA=";
+uint8_t debug=1;								// Debug level! 0 is no msgs, 1 normal, 2 extensive
+uint8_t pdebug=0xFF;							// Allow all atterns (departments)
+
+#if GATEWAYNODE==1
+#if _GPS==1
+#include <TinyGPS++.h>
+TinyGPSPlus gps;
+HardwareSerial Serial1(1);
+#endif
+#endif
+
+// You can switch webserver off if not necessary but probably better to leave it in.
+#if A_SERVER==1
+#if ESP32_ARCH==1
+	ESP32WebServer server(A_SERVERPORT);
+#else
+	ESP8266WebServer server(A_SERVERPORT);
+#endif
+#endif
+using namespace std;
+
+byte currentMode = 0x81;
+
+bool sx1272 = true;								// Actually we use sx1276/RFM95
+
+uint32_t cp_nb_rx_rcv;							// Number of messages received by gateway
+uint32_t cp_nb_rx_ok;							// Number of messages received OK
+uint32_t cp_nb_rx_bad;							// Number of messages received bad
+uint32_t cp_nb_rx_nocrc;						// Number of messages without CRC
+uint32_t cp_up_pkt_fwd;
+
+uint8_t MAC_array[6];
+
+// ----------------------------------------------------------------------------
+//
+// Configure these values only if necessary!
+//
+// ----------------------------------------------------------------------------
+
+// Set spreading factor (SF7 - SF12)
+sf_t sf 			= _SPREADING;
+sf_t sfi 			= _SPREADING;				// Initial value of SF
+
+// Set location, description and other configuration parameters
+// Defined in ESP-sc_gway.h
+//
+float lat			= _LAT;						// Configuration specific info...
+float lon			= _LON;
+int   alt			= _ALT;
+char platform[24]	= _PLATFORM; 				// platform definition
+char email[40]		= _EMAIL;    				// used for contact email
+char description[64]= _DESCRIPTION;				// used for free form description 
+
+// define servers
+
+IPAddress ntpServer;							// IP address of NTP_TIMESERVER
+IPAddress ttnServer;							// IP Address of thethingsnetwork server
+IPAddress thingServer;
+
+WiFiUDP Udp;
+
+time_t startTime = 0;							// The time in seconds since 1970 that the server started
+												// be aware that UTP time has to succeed for meaningful values.
+												// We use this variable since millis() is reset every 50 days...
+uint32_t eventTime = 0;							// Timing of _event to change value (or not).
+uint32_t sendTime = 0;							// Time that the last message transmitted
+uint32_t doneTime = 0;							// Time to expire when CDDONE takes too long
+uint32_t statTime = 0;							// last time we sent a stat message to server
+uint32_t pulltime = 0;							// last time we sent a pull_data request to server
+//uint32_t lastTmst = 0;							// Last activity Timer
+
+#if A_SERVER==1
+uint32_t wwwtime = 0;
+#endif
+#if NTP_INTR==0
+uint32_t ntptimer = 0;
+#endif
+
+#define TX_BUFF_SIZE  1024						// Upstream buffer to send to MQTT
+#define RX_BUFF_SIZE  1024						// Downstream received from MQTT
+#define STATUS_SIZE	  512						// Should(!) be enough based on the static text .. was 1024
+
+#if GATEWAYNODE==1
+uint16_t frameCount=0;							// We write this to SPIFF file
+#endif
+
+// volatile bool inSPI This initial value of mutex is to be free,
+// which means that its value is 1 (!)
+// 
+int mutexSPI = 1;
+
+// ----------------------------------------------------------------------------
+// FORWARD DECARATIONS
+// These forward declarations are done since other .ino fils are linked by the
+// compiler/linker AFTER the main ESP-sc-gway.ino file. 
+// And espcesially when calling functions with ICACHE_RAM_ATTR the complier 
+// does not want this.
+// Solution can also be to pecify less STRICT compile options in Makefile
+// ----------------------------------------------------------------------------
+
+void ICACHE_RAM_ATTR Interrupt_0();
+void ICACHE_RAM_ATTR Interrupt_1();
+
+int sendPacket(uint8_t *buf, uint8_t length);		// _txRx.ino
+void setupWWW();									// _wwwServer.ino
+void SerialTime();									// _utils.ino
+static void printIP(IPAddress ipa, const char sep, String& response);	// _wwwServer.ino
+
+void init_oLED();									// oLED.ino
+void acti_oLED();
+void addr_oLED();
+
+void setupOta(char *hostname);
+void initLoraModem();								// _loraModem.ino
+void cadScanner();
+void rxLoraModem();									// _loraModem.ino
+void writeRegister(uint8_t addr, uint8_t value);	// _loraModem.ino
+
+void stateMachine();								// _stateMachine.ino
+
+void SerialStat(uint8_t intr);						// _utils.ino
+
+#if MUTEX==1
+// Forward declarations
+void ICACHE_FLASH_ATTR CreateMutux(int *mutex);
+bool ICACHE_FLASH_ATTR GetMutex(int *mutex);
+void ICACHE_FLASH_ATTR ReleaseMutex(int *mutex);
+#endif
+
+// ----------------------------------------------------------------------------
+// DIE is not use actively in the source code anymore.
+// It is replaced by a Serial.print command so we know that we have a problem
+// somewhere.
+// There are at least 3 other ways to restart the ESP. Pick one if you want.
+// ----------------------------------------------------------------------------
+void die(const char *s)
+{
+	Serial.println(s);
+	if (debug>=2) Serial.flush();
+
+	delay(50);
+	// system_restart();						// SDK function
+	// ESP.reset();				
+	abort();									// Within a second
+}
+
+// ----------------------------------------------------------------------------
+// gway_failed is a function called by ASSERT in ESP-sc-gway.h
+//
+// ----------------------------------------------------------------------------
+void gway_failed(const char *file, uint16_t line) {
+	Serial.print(F("Program failed in file: "));
+	Serial.print(file);
+	Serial.print(F(", line: "));
+	Serial.println(line);
+	if (debug>=2) Serial.flush();
+}
+
+// ----------------------------------------------------------------------------
+// Print leading '0' digits for hours(0) and second(0) when
+// printing values less than 10
+// ----------------------------------------------------------------------------
+void printDigits(unsigned long digits)
+{
+    // utility function for digital clock display: prints leading 0
+    if(digits < 10)
+        Serial.print(F("0"));
+    Serial.print(digits);
+}
+
+// ----------------------------------------------------------------------------
+// Print utin8_t values in HEX with leading 0 when necessary
+// ----------------------------------------------------------------------------
+void printHexDigit(uint8_t digit)
+{
+    // utility function for printing Hex Values with leading 0
+    if(digit < 0x10)
+        Serial.print('0');
+    Serial.print(digit,HEX);
+}
+
+
+// ----------------------------------------------------------------------------
+// Print the current time
+// ----------------------------------------------------------------------------
+static void printTime() {
+	switch (weekday())
+	{
+		case 1: Serial.print(F("Sunday")); break;
+		case 2: Serial.print(F("Monday")); break;
+		case 3: Serial.print(F("Tuesday")); break;
+		case 4: Serial.print(F("Wednesday")); break;
+		case 5: Serial.print(F("Thursday")); break;
+		case 6: Serial.print(F("Friday")); break;
+		case 7: Serial.print(F("Saturday")); break;
+		default: Serial.print(F("ERROR")); break;
+	}
+	Serial.print(F(" "));
+	printDigits(hour());
+	Serial.print(F(":"));
+	printDigits(minute());
+	Serial.print(F(":"));
+	printDigits(second());
+	return;
+}
+
+
+// ----------------------------------------------------------------------------
+// Convert a float to string for printing
+// Parameters:
+//	f is float value to convert
+//	p is precision in decimal digits
+//	val is character array for results
+// ----------------------------------------------------------------------------
+void ftoa(float f, char *val, int p) {
+	int j=1;
+	int ival, fval;
+	char b[7] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+	
+	for (int i=0; i< p; i++) { j= j*10; }
+
+	ival = (int) f;								// Make integer part
+	fval = (int) ((f- ival)*j);					// Make fraction. Has same sign as integer part
+	if (fval<0) fval = -fval;					// So if it is negative make fraction positive again.
+												// sprintf does NOT fit in memory
+	if ((f<0) && (ival == 0)) strcat(val, "-");
+	strcat(val,itoa(ival,b,10));				// Copy integer part first, base 10, null terminated
+	strcat(val,".");							// Copy decimal point
+	
+	itoa(fval,b,10);							// Copy fraction part base 10
+	for (int i=0; i<(p-strlen(b)); i++) {
+		strcat(val,"0"); 						// first number of 0 of faction?
+	}
+	
+	// Fraction can be anything from 0 to 10^p , so can have less digits
+	strcat(val,b);
+}
+
+// ============================================================================
+// NTP TIME functions
+
+
+
+// ----------------------------------------------------------------------------
+// Send the request packet to the NTP server.
+//
+// ----------------------------------------------------------------------------
+int sendNtpRequest(IPAddress timeServerIP) {
+	const int NTP_PACKET_SIZE = 48;				// Fixed size of NTP record
+	byte packetBuffer[NTP_PACKET_SIZE];
+
+	memset(packetBuffer, 0, NTP_PACKET_SIZE);	// Zeroise the buffer.
+	
+	packetBuffer[0]  = 0b11100011;   			// LI, Version, Mode
+	packetBuffer[1]  = 0;						// Stratum, or type of clock
+	packetBuffer[2]  = 6;						// Polling Interval
+	packetBuffer[3]  = 0xEC;					// Peer Clock Precision
+	// 8 bytes of zero for Root Delay & Root Dispersion
+	packetBuffer[12] = 49;
+	packetBuffer[13] = 0x4E;
+	packetBuffer[14] = 49;
+	packetBuffer[15] = 52;	
+
+	
+	if (!sendUdp( (IPAddress) timeServerIP, (int) 123, packetBuffer, NTP_PACKET_SIZE)) {
+		gwayConfig.ntpErr++;
+		gwayConfig.ntpErrTime = now();
+		return(0);	
+	}
+	return(1);
+}
+
+
+// ----------------------------------------------------------------------------
+// Get the NTP time from one of the time servers
+// Note: As this function is called from SyncINterval in the background
+//	make sure we have no blocking calls in this function
+// ----------------------------------------------------------------------------
+time_t getNtpTime()
+{
+	gwayConfig.ntps++;
+	
+    if (!sendNtpRequest(ntpServer))					// Send the request for new time
+	{
+		if (( debug>=0 ) && ( pdebug & P_MAIN ))
+			Serial.println(F("M sendNtpRequest failed"));
+		return(0);
+	}
+	
+	const int NTP_PACKET_SIZE = 48;					// Fixed size of NTP record
+	byte packetBuffer[NTP_PACKET_SIZE];
+	memset(packetBuffer, 0, NTP_PACKET_SIZE);		// Set buffer cntents to zero
+
+    uint32_t beginWait = millis();
+	delay(10);
+    while (millis() - beginWait < 1500) 
+	{
+		int size = Udp.parsePacket();
+		if ( size >= NTP_PACKET_SIZE ) {
+		
+			if (Udp.read(packetBuffer, NTP_PACKET_SIZE) < NTP_PACKET_SIZE) {
+				break;
+			}
+			else {
+				// Extract seconds portion.
+				unsigned long secs;
+				secs  = packetBuffer[40] << 24;
+				secs |= packetBuffer[41] << 16;
+				secs |= packetBuffer[42] <<  8;
+				secs |= packetBuffer[43];
+				// UTC is 1 TimeZone correction when no daylight saving time
+				return(secs - 2208988800UL + NTP_TIMEZONES * SECS_IN_HOUR);
+			}
+			Udp.flush();	
+		}
+		delay(100);									// Wait 100 millisecs, allow kernel to act when necessary
+    }
+
+	Udp.flush();
+	
+	// If we are here, we could not read the time from internet
+	// So increase the counter
+	gwayConfig.ntpErr++;
+	gwayConfig.ntpErrTime = now();
+#if DUSB>=1
+	if (( debug>=0 ) && ( pdebug & P_MAIN )) {
+		Serial.println(F("M getNtpTime:: read failed"));
+	}
+#endif
+	return(0); 										// return 0 if unable to get the time
+}
+
+// ----------------------------------------------------------------------------
+// Set up regular synchronization of NTP server and the local time.
+// ----------------------------------------------------------------------------
+#if NTP_INTR==1
+void setupTime() {
+  setSyncProvider(getNtpTime);
+  setSyncInterval(_NTP_INTERVAL);
+}
+#endif
+
+
+// ============================================================================
+// UDP  FUNCTIONS
+
+
+// ----------------------------------------------------------------------------
+// Read DOWN a package from UDP socket, can come from any server
+// Messages are received when server responds to gateway requests from LoRa nodes 
+// (e.g. JOIN requests etc.) or when server has downstream data.
+// We respond only to the server that sent us a message!
+// Note: So normally we can forget here about codes that do upstream
+// Parameters:
+//	Packetsize: size of the buffer to read, as read by loop() calling function
+//
+// Returns:
+//	-1 or false if not read
+//	Or number of characters read is success
+//
+// ----------------------------------------------------------------------------
+int readUdp(int packetSize)
+{
+	uint8_t protocol;
+	uint16_t token;
+	uint8_t ident; 
+	uint8_t buff[32]; 						// General buffer to use for UDP, set to 64
+	uint8_t buff_down[RX_BUFF_SIZE];		// Buffer for downstream
+
+//	if ((WiFi.status() != WL_CONNECTED) &&& (WlanConnect(10) < 0)) {
+	if (WlanConnect(10) < 0) {
+#if DUSB>=1
+			Serial.print(F("readdUdp: ERROR connecting to WLAN"));
+			if (debug>=2) Serial.flush();
+#endif
+			Udp.flush();
+			yield();
+			return(-1);
+	}
+
+	yield();
+	
+	if (packetSize > RX_BUFF_SIZE) {
+#if DUSB>=1
+		Serial.print(F("readUDP:: ERROR package of size: "));
+		Serial.println(packetSize);
+#endif
+		Udp.flush();
+		return(-1);
+	}
+  
+	// We assume here that we know the originator of the message
+	// In practice however this can be any sender!
+	if (Udp.read(buff_down, packetSize) < packetSize) {
+#if DUSB>=1
+		Serial.println(F("A readUsb:: Reading less chars"));
+		return(-1);
+#endif
+	}
+
+	// Remote Address should be known
+	IPAddress remoteIpNo = Udp.remoteIP();
+
+	// Remote port is either of the remote TTN server or from NTP server (=123)
+	unsigned int remotePortNo = Udp.remotePort();
+
+	if (remotePortNo == 123) {
+		// This is an NTP message arriving
+#if DUSB>=1
+		if ( debug>=0 ) {
+			Serial.println(F("A readUdp:: NTP msg rcvd"));
+		}
+#endif
+		gwayConfig.ntpErr++;
+		gwayConfig.ntpErrTime = now();
+		return(0);
+	}
+	
+	// If it is not NTP it must be a LoRa message for gateway or node
+	else {
+		uint8_t *data = (uint8_t *) ((uint8_t *)buff_down + 4);
+		protocol = buff_down[0];
+		token = buff_down[2]*256 + buff_down[1];
+		ident = buff_down[3];
+
+#if DUSB>=1
+	if ((debug>1) && (pdebug & P_MAIN)) {
+		Serial.print(F("M readUdp:: message waiting="));
+		Serial.print(ident);
+		Serial.println();
+	}
+#endif
+		// now parse the message type from the server (if any)
+		switch (ident) {
+
+		// This message is used by the gateway to send sensor data to the
+		// server. As this function is used for downstream only, this option
+		// will never be selected but is included as a reference only
+		case PKT_PUSH_DATA: // 0x00 UP
+#if DUSB>=1
+			if (debug >=1) {
+				Serial.print(F("PKT_PUSH_DATA:: size ")); Serial.print(packetSize);
+				Serial.print(F(" From ")); Serial.print(remoteIpNo);
+				Serial.print(F(", port ")); Serial.print(remotePortNo);
+				Serial.print(F(", data: "));
+				for (int i=0; i<packetSize; i++) {
+					Serial.print(buff_down[i],HEX);
+					Serial.print(':');
+				}
+				Serial.println();
+				if (debug>=2) Serial.flush();
+			}
+#endif
+		break;
+	
+		// This message is sent by the server to acknoledge receipt of a
+		// (sensor) message sent with the code above.
+		case PKT_PUSH_ACK:	// 0x01 DOWN
+#if DUSB>=1
+			if (( debug>=2) && (pdebug & P_MAIN )) {
+				Serial.print(F("M PKT_PUSH_ACK:: size ")); 
+				Serial.print(packetSize);
+				Serial.print(F(" From ")); 
+				Serial.print(remoteIpNo);
+				Serial.print(F(", port ")); 
+				Serial.print(remotePortNo);
+				Serial.print(F(", token: "));
+				Serial.println(token, HEX);
+				Serial.println();
+			}
+#endif
+		break;
+	
+		case PKT_PULL_DATA:	// 0x02 UP
+#if DUSB>=1
+			Serial.print(F(" Pull Data"));
+			Serial.println();
+#endif
+		break;
+	
+		// This message type is used to confirm OTAA message to the node
+		// XXX This message format may also be used for other downstream communucation
+		case PKT_PULL_RESP:	// 0x03 DOWN
+#if DUSB>=1
+			if (( debug>=0 ) && ( pdebug & P_MAIN )) {
+				Serial.println(F("M readUdp:: PKT_PULL_RESP received"));
+			}
+#endif
+//			lastTmst = micros();					// Store the tmst this package was received
+			
+			// Send to the LoRa Node first (timing) and then do reporting to Serial
+			_state=S_TX;
+			sendTime = micros();					// record when we started sending the message
+			
+			if (sendPacket(data, packetSize-4) < 0) {
+#if DUSB>=1
+				if ( debug>=0 ) {
+					Serial.println(F("A readUdp:: Error: PKT_PULL_RESP sendPacket failed"));
+				}
+#endif
+				return(-1);
+			}
+
+			// Now respond with an PKT_TX_ACK; 0x04 UP
+			buff[0]=buff_down[0];
+			buff[1]=buff_down[1];
+			buff[2]=buff_down[2];
+			//buff[3]=PKT_PULL_ACK;					// Pull request/Change of Mogyi
+			buff[3]=PKT_TX_ACK;
+			buff[4]=MAC_array[0];
+			buff[5]=MAC_array[1];
+			buff[6]=MAC_array[2];
+			buff[7]=0xFF;
+			buff[8]=0xFF;
+			buff[9]=MAC_array[3];
+			buff[10]=MAC_array[4];
+			buff[11]=MAC_array[5];
+			buff[12]=0;
+#if DUSB>=1
+			if (( debug >= 2 ) && ( pdebug & P_MAIN )) {
+				Serial.println(F("M readUdp:: TX buff filled"));
+			}
+#endif
+			// Only send the PKT_PULL_ACK to the UDP socket that just sent the data!!!
+			Udp.beginPacket(remoteIpNo, remotePortNo);
+			if (Udp.write((unsigned char *)buff, 12) != 12) {
+#if DUSB>=1
+				if (debug>=0)
+					Serial.println("A readUdp:: Error: PKT_PULL_ACK UDP write");
+#endif
+			}
+			else {
+#if DUSB>=1
+				if (( debug>=0 ) && ( pdebug & P_TX )) {
+					Serial.print(F("M PKT_TX_ACK:: micros="));
+					Serial.println(micros());
+				}
+#endif
+			}
+
+			if (!Udp.endPacket()) {
+#if DUSB>=1
+				if (( debug>=0 ) && ( pdebug & P_MAIN )) {
+					Serial.println(F("M PKT_PULL_DATALL Error Udp.endpaket"));
+				}
+#endif
+			}
+			
+			yield();
+#if DUSB>=1
+			if (( debug >=1 ) && (pdebug & P_MAIN )) {
+				Serial.print(F("M PKT_PULL_RESP:: size ")); 
+				Serial.print(packetSize);
+				Serial.print(F(" From ")); 
+				Serial.print(remoteIpNo);
+				Serial.print(F(", port ")); 
+				Serial.print(remotePortNo);	
+				Serial.print(F(", data: "));
+				data = buff_down + 4;
+				data[packetSize] = 0;
+				Serial.print((char *)data);
+				Serial.println(F("..."));
+			}
+#endif		
+		break;
+	
+		case PKT_PULL_ACK:	// 0x04 DOWN; the server sends a PULL_ACK to confirm PULL_DATA receipt
+#if DUSB>=1
+			if (( debug >= 2 ) && (pdebug & P_MAIN )) {
+				Serial.print(F("M PKT_PULL_ACK:: size ")); Serial.print(packetSize);
+				Serial.print(F(" From ")); Serial.print(remoteIpNo);
+				Serial.print(F(", port ")); Serial.print(remotePortNo);	
+				Serial.print(F(", data: "));
+				for (int i=0; i<packetSize; i++) {
+					Serial.print(buff_down[i],HEX);
+					Serial.print(':');
+				}
+				Serial.println();
+			}
+#endif
+		break;
+	
+		default:
+#if GATEWAYMGT==1
+			// For simplicity, we send the first 4 bytes too
+			gateway_mgt(packetSize, buff_down);
+#else
+
+#endif
+#if DUSB>=1
+			Serial.print(F(", ERROR ident not recognized="));
+			Serial.println(ident);
+#endif
+		break;
+		}
+#if DUSB>=2
+		if (debug>=1) {
+			Serial.print(F("readUdp:: returning=")); 
+			Serial.println(packetSize);
+		}
+#endif
+		// For downstream messages
+		return packetSize;
+	}
+}//readUdp
+
+
+// ----------------------------------------------------------------------------
+// Send UP an UDP/DGRAM message to the MQTT server
+// If we send to more than one host (not sure why) then we need to set sockaddr 
+// before sending.
+// Parameters:
+//	IPAddress
+//	port
+//	msg *
+//	length (of msg)
+// return values:
+//	0: Error
+//	1: Success
+// ----------------------------------------------------------------------------
+int sendUdp(IPAddress server, int port, uint8_t *msg, int length) {
+
+	// Check whether we are conected to Wifi and the internet
+	if (WlanConnect(3) < 0) {
+#if DUSB>=1
+		if (( debug>=0 ) && ( pdebug & P_MAIN )) {
+			Serial.print(F("M sendUdp: ERROR connecting to WiFi"));
+			Serial.flush();
+		}
+#endif
+		Udp.flush();
+		yield();
+		return(0);
+	}
+
+	yield();
+
+	//send the update
+#if DUSB>=1
+	if (( debug>=3 ) && ( pdebug & P_MAIN )) {
+		Serial.println(F("M WiFi connected"));
+	}
+#endif	
+	if (!Udp.beginPacket(server, (int) port)) {
+#if DUSB>=1
+		if (( debug>=1 ) && ( pdebug & P_MAIN )) {
+			Serial.println(F("M sendUdp:: Error Udp.beginPacket"));
+		}
+#endif
+		return(0);
+	}
+	
+	yield();
+	
+
+	if (Udp.write((unsigned char *)msg, length) != length) {
+#if DUSB>=1
+		if (( debug<=1 ) && ( pdebug & P_MAIN )) {
+			Serial.println(F("M sendUdp:: Error write"));
+		}
+#endif
+		Udp.endPacket();						// Close UDP
+		return(0);								// Return error
+	}
+	
+	yield();
+	
+	if (!Udp.endPacket()) {
+#if DUSB>=1
+		if (debug>=1) {
+			Serial.println(F("sendUdp:: Error Udp.endPacket"));
+			Serial.flush();
+		}
+#endif
+		return(0);
+	}
+	return(1);
+}//sendUDP
+
+// ----------------------------------------------------------------------------
+// UDPconnect(): connect to UDP (which is a local thing, after all UDP 
+// connections do not exist.
+// Parameters:
+//	<None>
+// Returns
+//	Boollean indicating success or not
+// ----------------------------------------------------------------------------
+bool UDPconnect() {
+
+	bool ret = false;
+	unsigned int localPort = _LOCUDPPORT;			// To listen to return messages from WiFi
+#if DUSB>=1
+	if (debug>=1) {
+		Serial.print(F("Local UDP port="));
+		Serial.println(localPort);
+	}
+#endif	
+	if (Udp.begin(localPort) == 1) {
+#if DUSB>=1
+		if (debug>=1) Serial.println(F("Connection successful"));
+#endif
+		ret = true;
+	}
+	else{
+#if DUSB>=1
+		if (debug>=1) Serial.println("Connection failed");
+#endif
+	}
+	return(ret);
+}//udpConnect
+
+
+// ----------------------------------------------------------------------------
+// Send UP periodic Pull_DATA message to server to keepalive the connection
+// and to invite the server to send downstream messages when these are available
+// *2, par. 5.2
+//	- Protocol Version (1 byte)
+//	- Random Token (2 bytes)
+//	- PULL_DATA identifier (1 byte) = 0x02
+//	- Gateway unique identifier (8 bytes) = MAC address
+// ----------------------------------------------------------------------------
+//void pullData() {
+//
+//    uint8_t pullDataReq[12]; 								// status report as a JSON object
+//    int pullIndex=0;
+//	int i;
+//	
+//	uint8_t token_h = (uint8_t)rand(); 						// random token
+//    uint8_t token_l = (uint8_t)rand();						// random token
+//	
+//    // pre-fill the data buffer with fixed fields
+//    pullDataReq[0]  = PROTOCOL_VERSION;						// 0x01
+//    pullDataReq[1]  = token_h;
+//    pullDataReq[2]  = token_l;
+//    pullDataReq[3]  = PKT_PULL_DATA;						// 0x02
+//	// READ MAC ADDRESS OF ESP8266, and return unique Gateway ID consisting of MAC address and 2bytes 0xFF
+//    pullDataReq[4]  = MAC_array[0];
+//    pullDataReq[5]  = MAC_array[1];
+//    pullDataReq[6]  = MAC_array[2];
+//    pullDataReq[7]  = 0xFF;
+//    pullDataReq[8]  = 0xFF;
+//    pullDataReq[9]  = MAC_array[3];
+//    pullDataReq[10] = MAC_array[4];
+//    pullDataReq[11] = MAC_array[5];
+//    //pullDataReq[12] = 0/00; 								// add string terminator, for safety
+//	
+//    pullIndex = 12;											// 12-byte header
+//	
+//    //send the update
+//	
+//	uint8_t *pullPtr;
+//	pullPtr = pullDataReq,
+//#ifdef _TTNSERVER
+//    sendUdp(ttnServer, _TTNPORT, pullDataReq, pullIndex);
+//	yield();
+//#endif
+//
+//#if DUSB>=1
+//	if (pullPtr != pullDataReq) {
+//		Serial.println(F("pullPtr != pullDatReq"));
+//		Serial.flush();
+//	}
+//
+//#endif
+//#ifdef _THINGSERVER
+//	sendUdp(thingServer, _THINGPORT, pullDataReq, pullIndex);
+//#endif
+//
+//#if DUSB>=1
+//    if (( debug>=2 ) && ( pdebug & P_MAIN )) {
+//		yield();
+//		Serial.print(F("M PKT_PULL_DATA request, len=<"));
+//		Serial.print(pullIndex);
+//		Serial.print(F("> "));
+//		for (i=0; i<pullIndex; i++) {
+//			Serial.print(pullDataReq[i],HEX);				// debug: display JSON stat
+//			Serial.print(':');
+//		}
+//		Serial.println();
+//		if (debug>=2) Serial.flush();
+//	}
+//#endif
+//	return;
+//}//pullData
+
+
+// ----------------------------------------------------------------------------
+// Send UP periodic status message to server even when we do not receive any
+// data. 
+// Parameters:
+//	- <none>
+// ----------------------------------------------------------------------------
+void sendstat() {
+
+    uint8_t status_report[STATUS_SIZE]; 					// status report as a JSON object
+    char stat_timestamp[32];								// XXX was 24
+    time_t t;
+	char clat[10]={0};
+	char clon[10]={0};
+
+    int stat_index=0;
+	uint8_t token_h   = (uint8_t)rand(); 					// random token
+    uint8_t token_l   = (uint8_t)rand();					// random token
+	
+    // pre-fill the data buffer with fixed fields
+    status_report[0]  = PROTOCOL_VERSION;					// 0x01
+	status_report[1]  = token_h;
+    status_report[2]  = token_l;
+    status_report[3]  = PKT_PUSH_DATA;						// 0x00
+	
+	// READ MAC ADDRESS OF ESP8266, and return unique Gateway ID consisting of MAC address and 2bytes 0xFF
+    status_report[4]  = MAC_array[0];
+    status_report[5]  = MAC_array[1];
+    status_report[6]  = MAC_array[2];
+    status_report[7]  = 0xFF;
+    status_report[8]  = 0xFF;
+    status_report[9]  = MAC_array[3];
+    status_report[10] = MAC_array[4];
+    status_report[11] = MAC_array[5];
+
+    stat_index = 12;										// 12-byte header
+	
+    t = now();												// get timestamp for statistics
+	
+	// XXX Using CET as the current timezone. Change to your timezone	
+	sprintf(stat_timestamp, "%04d-%02d-%02d %02d:%02d:%02d CET", year(),month(),day(),hour(),minute(),second());
+	yield();
+	
+	ftoa(lat,clat,5);										// Convert lat to char array with 5 decimals
+	ftoa(lon,clon,5);										// As IDE CANNOT prints floats
+	
+	// Build the Status message in JSON format, XXX Split this one up...
+	delay(1);
+	
+    int j = snprintf((char *)(status_report + stat_index), STATUS_SIZE-stat_index, 
+		"{\"stat\":{\"time\":\"%s\",\"lati\":%s,\"long\":%s,\"alti\":%i,\"rxnb\":%u,\"rxok\":%u,\"rxfw\":%u,\"ackr\":%u.0,\"dwnb\":%u,\"txnb\":%u,\"pfrm\":\"%s\",\"mail\":\"%s\",\"desc\":\"%s\"}}", 
+		stat_timestamp, clat, clon, (int)alt, cp_nb_rx_rcv, cp_nb_rx_ok, cp_up_pkt_fwd, 0, 0, 0, platform, email, description);
+		
+	yield();												// Give way to the internal housekeeping of the ESP8266
+
+    stat_index += j;
+    status_report[stat_index] = 0; 							// add string terminator, for safety
+
+#if DUSB>=1
+    if (( debug>=2 ) && ( pdebug & P_MAIN )) {
+		Serial.print(F("M stat update: <"));
+		Serial.print(stat_index);
+		Serial.print(F("> "));
+		Serial.println((char *)(status_report+12));			// DEBUG: display JSON stat
+	}
+#endif	
+	if (stat_index > STATUS_SIZE) {
+#if DUSB>=1
+		Serial.println(F("A sendstat:: ERROR buffer too big"));
+#endif
+		return;
+	}
+	
+    //send the update
+#ifdef _TTNSERVER
+    sendUdp(ttnServer, _TTNPORT, status_report, stat_index);
+	yield();
+#endif
+
+#ifdef _THINGSERVER
+	sendUdp(thingServer, _THINGPORT, status_report, stat_index);
+#endif
+	return;
+}//sendstat
+
+
+
+// ============================================================================
+// MAIN PROGRAM CODE (SETUP AND LOOP)
+
+
+// ----------------------------------------------------------------------------
+// Setup code (one time)
+// _state is S_INIT
+// ----------------------------------------------------------------------------
+void setup() {
+
+	char MAC_char[19];								// XXX Unbelievable
+	MAC_char[18] = 0;
+
+	Serial.begin(9600);						// As fast as possible for bus
+	delay(100);	
+
+#if _GPS==1
+	// Pins are define in LoRaModem.h together with other pins
+	Serial1.begin(9600, SERIAL_8N1, GPS_TX, GPS_RX);// PIN 12-TX 15-RX
+#endif
+
+#ifdef ESP32
+#if DUSB>=1
+	Serial.print(F("ESP32 defined, freq="));
+#if _LFREQ==433
+	Serial.print(freqs[0]);
+	Serial.print(F(" EU433"));
+#elif _LFREQ==868
+	Serial.print(freqs[0]);
+	Serial.print(F(" EU868"));
+#endif
+	Serial.println();
+#endif
+#endif
+#ifdef ARDUINO_ARCH_ESP32
+#if DUSB>=1
+	Serial.println(F("ARDUINO_ARCH_ESP32 defined"));
+#endif
+#endif
+
+
+#if DUSB>=1
+	Serial.flush();
+
+	delay(500);
+
+	if (SPIFFS.begin()) {
+		Serial.println(F("SPIFFS init success"));
+	}
+	else {
+	}
+#endif	
+#if _SPIFF_FORMAT>=1
+#if DUSB>=1
+	if (( debug >= 0 ) && ( pdebug & P_MAIN )) {
+		Serial.println(F("M Format Filesystem ... "));
+	}
+#endif
+	SPIFFS.format();								// Normally disabled. Enable only when SPIFFS corrupt
+#if DUSB>=1
+	if (( debug >= 0 ) && ( pdebug & P_MAIN )) {
+		Serial.println(F("Done"));
+	}
+#endif
+#endif
+
+	Serial.print(F("Assert="));
+#if defined CFG_noassert
+	Serial.println(F("No Asserts"));
+#else
+	Serial.println(F("Do Asserts"));
+#endif
+
+#if OLED>=1
+	init_oLED();
+#endif
+
+	delay(500);
+	yield();
+#if DUSB>=1	
+	if (debug>=1) {
+		Serial.print(F("debug=")); 
+		Serial.println(debug);
+		yield();
+	}
+#endif
+
+	WiFi.mode(WIFI_STA);
+	WiFi.setAutoConnect(true);
+	//WiFi.begin();
+	
+	WlanReadWpa();								// Read the last Wifi settings from SPIFFS into memory
+
+	WiFi.macAddress(MAC_array);
+	
+    sprintf(MAC_char,"%02x:%02x:%02x:%02x:%02x:%02x",
+		MAC_array[0],MAC_array[1],MAC_array[2],MAC_array[3],MAC_array[4],MAC_array[5]);
+	Serial.print("MAC: ");
+    Serial.print(MAC_char);
+	Serial.print(F(", len="));
+	Serial.println(strlen(MAC_char));
+
+	// We start by connecting to a WiFi network, set hostname
+	char hostname[12];
+
+	// Setup WiFi UDP connection. Give it some time and retry x times..
+	while (WlanConnect(0) <= 0) {
+		Serial.print(F("Error Wifi network connect "));
+		Serial.println();
+		yield();
+	}	
+	
+	// After there is a WiFi router connection, we can also set the hostname.
+#if ESP32_ARCH==1
+	sprintf(hostname, "%s%02x%02x%02x", "esp32-", MAC_array[3], MAC_array[4], MAC_array[5]);
+	WiFi.setHostname( hostname );
+#else
+	sprintf(hostname, "%s%02x%02x%02x", "esp8266-", MAC_array[3], MAC_array[4], MAC_array[5]);
+	wifi_station_set_hostname( hostname );
+#endif	
+
+	
+	Serial.print(F("Host "));
+#if ESP32_ARCH==1
+	Serial.print(WiFi.getHostname());
+#else
+	Serial.print(wifi_station_get_hostname());
+#endif
+	Serial.print(F(" WiFi Connected to "));
+	Serial.print(WiFi.SSID());
+	Serial.print(F(" on IP="));
+	Serial.print(WiFi.localIP());
+	Serial.println();
+	delay(200);
+	
+	// If we are here we are connected to WLAN
+	// So now test the UDP function
+	if (!UDPconnect()) {
+		Serial.println(F("Error UDPconnect"));
+	}
+	delay(200);
+	
+	// Pins are defined and set in loraModem.h
+    pinMode(pins.ss, OUTPUT);
+	pinMode(pins.rst, OUTPUT);
+    pinMode(pins.dio0, INPUT);								// This pin is interrupt
+	pinMode(pins.dio1, INPUT);								// This pin is interrupt
+	//pinMode(pins.dio2, INPUT);
+
+	// Init the SPI pins
+#if ESP32_ARCH==1
+	SPI.begin(SCK, MISO, MOSI, SS);
+#else
+	SPI.begin();
+#endif
+
+	delay(500);
+	
+	// We choose the Gateway ID to be the Ethernet Address of our Gateway card
+    // display results of getting hardware address
+	// 
+    Serial.print("Gateway ID: ");
+	printHexDigit(MAC_array[0]);
+    printHexDigit(MAC_array[1]);
+    printHexDigit(MAC_array[2]);
+	printHexDigit(0xFF);
+	printHexDigit(0xFF);
+    printHexDigit(MAC_array[3]);
+    printHexDigit(MAC_array[4]);
+    printHexDigit(MAC_array[5]);
+
+    Serial.print(", Listening at SF");
+	Serial.print(sf);
+	Serial.print(" on ");
+	Serial.print((double)freq/1000000);
+	Serial.println(" Mhz.");
+
+	if (!WiFi.hostByName(NTP_TIMESERVER, ntpServer))		// Get IP address of Timeserver
+	{
+		die("Setup:: ERROR hostByName NTP");
+	};
+	delay(100);
+#ifdef _TTNSERVER
+	if (!WiFi.hostByName(_TTNSERVER, ttnServer))			// Use DNS to get server IP once
+	{
+		die("Setup:: ERROR hostByName TTN");
+	};
+	delay(100);
+#endif
+#ifdef _THINGSERVER
+	if (!WiFi.hostByName(_THINGSERVER, thingServer))
+	{
+		die("Setup:: ERROR hostByName THING");
+	}
+	delay(100);
+#endif
+
+	// The Over the AIr updates are supported when we have a WiFi connection.
+	// The NTP time setting does not have to be precise for this function to work.
+#if A_OTA==1
+	setupOta(hostname);										// Uses wwwServer 
+#endif
+
+	// Set the NTP Time
+	// As long as the time has not been set we try to set the time.
+#if NTP_INTR==1
+	setupTime();											// Set NTP time host and interval
+#else
+	// If not using the standard libraries, do a manual setting
+	// of the time. This meyhod works more reliable than the 
+	// interrupt driven method.
+	
+	//setTime((time_t)getNtpTime());
+	while (timeStatus() == timeNotSet) {
+#if DUSB>=1
+		if (( debug>=0 ) && ( pdebug & P_MAIN )) 
+			Serial.println(F("M setupTime:: Time not set (yet)"));
+#endif
+		delay(500);
+		time_t newTime;
+		newTime = (time_t)getNtpTime();
+		if (newTime != 0) setTime(newTime);
+	}
+	// When we are here we succeeded in getting the time
+	startTime = now();										// Time in seconds
+#if DUSB>=1
+	Serial.print("Time: "); printTime();
+	Serial.println();
+#endif
+	writeGwayCfg(CONFIGFILE );
+#if DUSB>=1
+	Serial.println(F("Gateway configuration saved"));
+#endif
+#endif //NTP_INTR
+
+#if A_SERVER==1	
+	// Setup the webserver
+	setupWWW();
+#endif
+
+	delay(100);												// Wait after setup
+	
+	// Setup ad initialise LoRa state machine of _loramModem.ino
+	_state = S_INIT;
+	initLoraModem();
+	
+	if (_cad) {
+		_state = S_SCAN;
+		sf = SF7;
+		cadScanner();										// Always start at SF7
+	}
+	else { 
+		_state = S_RX;
+		rxLoraModem();
+	}
+	LoraUp.payLoad[0]= 0;
+	LoraUp.payLength = 0;						// Init the length to 0
+
+	// init interrupt handlers, which are shared for GPIO15 / D8, 
+	// we switch on HIGH interrupts
+	if (pins.dio0 == pins.dio1) {
+		//SPI.usingInterrupt(digitalPinToInterrupt(pins.dio0));
+		attachInterrupt(pins.dio0, Interrupt_0, RISING);		// Share interrupts
+	}
+	// Or in the traditional Comresult case
+	else {
+		//SPI.usingInterrupt(digitalPinToInterrupt(pins.dio0));
+		//SPI.usingInterrupt(digitalPinToInterrupt(pins.dio1));
+		attachInterrupt(pins.dio0, Interrupt_0, RISING);	// Separate interrupts
+		attachInterrupt(pins.dio1, Interrupt_1, RISING);	// Separate interrupts		
+	}
+	
+	writeConfig( CONFIGFILE, &gwayConfig);					// Write config
+
+	// activate OLED display
+#if OLED>=1
+	acti_oLED();
+	addr_oLED();
+#endif
+
+if (!Esp32MQTTClient_Init((const uint8_t*)connectionString)){
+Serial.println("Initializing IoT hub failed.");
+return;}
+else{
+Serial.println("Initializing IoT hub success.");}
+	Serial.println(F("--------------------------------------"));
+}//setup
+
+
+
+// ----------------------------------------------------------------------------
+// LOOP
+// This is the main program that is executed time and time again.
+// We need to give way to the backend WiFi processing that 
+// takes place somewhere in the ESP8266 firmware and therefore
+// we include yield() statements at important points.
+//
+// Note: If we spend too much time in user processing functions
+//	and the backend system cannot do its housekeeping, the watchdog
+// function will be executed which means effectively that the 
+// program crashes.
+// We use yield() a lot to avoid ANY watch dog activity of the program.
+//
+// NOTE2: For ESP make sure not to do large array declarations in loop();
+// ----------------------------------------------------------------------------
+void loop ()
+{
+	uint32_t uSeconds;									// micro seconds
+	int packetSize;
+	uint32_t nowSeconds = now();
+	
+	// check for event value, which means that an interrupt has arrived.
+	// In this case we handle the interrupt ( e.g. message received)
+	// in userspace in loop().
+	//
+	stateMachine();									// do the state machine
+	
+	// After a quiet period, make sure we reinit the modem and state machine.
+	// The interval is in seconds (about 15 seconds) as this re-init
+	// is a heavy operation. 
+	// SO it will kick in if there are not many messages for the gateway.
+	// Note: Be careful that it does not happen too often in normal operation.
+	//
+	if ( ((nowSeconds - statr[0].tmst) > _MSG_INTERVAL ) &&
+		(msgTime <= statr[0].tmst) ) 
+	{
+#if DUSB>=1
+		if (( debug>=1 ) && ( pdebug & P_MAIN )) {
+			Serial.print("M REINIT:: ");
+			Serial.print( _MSG_INTERVAL );
+			Serial.print(F(" "));
+			SerialStat(0);
+		}
+#endif
+
+		// startReceiver() ??
+		if ((_cad) || (_hop)) {
+			_state = S_SCAN;
+			sf = SF7;
+			cadScanner();
+		}
+		else {
+			_state = S_RX;
+			rxLoraModem();
+		}
+		writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
+		writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);			// Reset all interrupt flags
+		msgTime = nowSeconds;
+	}
+
+#if A_SERVER==1
+	// Handle the Web server part of this sketch. Mainly used for administration 
+	// and monitoring of the node. This function is important so it is called at the
+	// start of the loop() function.
+	yield();
+	server.handleClient();
+#endif
+
+#if A_OTA==1
+	// Perform Over the Air (OTA) update if enabled and requested by user.
+	// It is important to put this function early in loop() as it is
+	// not called frequently but it should always run when called.
+	//
+	yield();
+	ArduinoOTA.handle();
+#endif
+
+	// I event is set, we know that we have a (soft) interrupt.
+	// After all necessary web/OTA services are scanned, we will
+	// reloop here for timing purposes. 
+	// Do as less yield() as possible.
+	// XXX 180326
+	if (_event == 1) {
+		return;
+	}
+	else yield();
+
+	
+	// If we are not connected, try to connect.
+	// We will not read Udp in this loop cycle then
+	if (WlanConnect(1) < 0) {
+#if DUSB>=1
+		if (( debug >= 0 ) && ( pdebug & P_MAIN ))
+			Serial.println(F("M ERROR reconnect WLAN"));
+#endif
+		yield();
+		return;										// Exit loop if no WLAN connected
+	}
+	
+	// So if we are connected 
+	// Receive UDP PUSH_ACK messages from server. (*2, par. 3.3)
+	// This is important since the TTN broker will return confirmation
+	// messages on UDP for every message sent by the gateway. So we have to consume them.
+	// As we do not know when the server will respond, we test in every loop.
+	//
+	else {
+		while( (packetSize = Udp.parsePacket()) > 0) {
+#if DUSB>=2
+			Serial.println(F("loop:: readUdp calling"));
+#endif
+			// DOWNSTREAM
+			// Packet may be PKT_PUSH_ACK (0x01), PKT_PULL_ACK (0x03) or PKT_PULL_RESP (0x04)
+			// This command is found in byte 4 (buffer[3])
+			if (readUdp(packetSize) <= 0) {
+#if DUSB>=1
+				if (( debug>0 ) && ( pdebug & P_MAIN ))
+					Serial.println(F("M readUDP error"));
+#endif
+				break;
+			}
+			// Now we know we succesfully received message from host
+			else {
+				//_event=1;								// Could be done double if more messages received
+			}
+		}
+	}
+	
+	yield();					// XXX 26/12/2017
+
+	// stat PUSH_DATA message (*2, par. 4)
+	//	
+
+    if ((nowSeconds - statTime) >= _STAT_INTERVAL) {	// Wake up every xx seconds
+#if DUSB>=1
+		if (( debug>=1 ) && ( pdebug & P_MAIN )) {
+			Serial.print(F("M STAT:: ..."));
+			Serial.flush();
+		}
+#endif
+        sendstat();										// Show the status message and send to server
+#if DUSB>=1
+		if (( debug>=1 ) && ( pdebug & P_MAIN )) {
+			Serial.println(F(" done"));
+			if (debug>=2) Serial.flush();
+		}
+#endif	
+
+		// If the gateway behaves like a node, we do from time to time
+		// send a node message to the backend server.
+		// The Gateway nod emessage has nothing to do with the STAT_INTERVAL
+		// message but we schedule it in the same frequency.
+		//
+#if GATEWAYNODE==1
+		if (gwayConfig.isNode) {
+			// Give way to internal some Admin if necessary
+			yield();
+			
+			// If the 1ch gateway is a sensor itself, send the sensor values
+			// could be battery but also other status info or sensor info
+		
+			if (sensorPacket() < 0) {
+#if DUSB>=1
+				Serial.println(F("sensorPacket: Error"));
+#endif
+			}
+		}
+#endif
+		statTime = nowSeconds;
+    }
+	
+	yield();
+
+	
+	// send PULL_DATA message (*2, par. 4)
+	//
+	nowSeconds = now();
+    if ((nowSeconds - pulltime) >= _PULL_INTERVAL) {	// Wake up every xx seconds
+#if DUSB>=1
+		if (( debug>=2) && ( pdebug & P_MAIN )) {
+			Serial.println(F("M PULL"));
+			if (debug>=1) Serial.flush();
+		}
+#endif
+//        pullData();										// Send PULL_DATA message to server
+		startReceiver();
+	
+		pulltime = nowSeconds;
+    }
+
+	
+	// If we do our own NTP handling (advisable)
+	// We do not use the timer interrupt but use the timing
+	// of the loop() itself which is better for SPI
+#if NTP_INTR==0
+	// Set the time in a manual way. Do not use setSyncProvider
+	// as this function may collide with SPI and other interrupts
+	yield();											// 26/12/2017
+	nowSeconds = now();
+	if (nowSeconds - ntptimer >= _NTP_INTERVAL) {
+		yield();
+		time_t newTime;
+		newTime = (time_t)getNtpTime();
+		if (newTime != 0) setTime(newTime);
+		ntptimer = nowSeconds;
+	}
+#endif
+	
+
+}//loop
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/LICENSE.md b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..72f18b41cd5f7ac601fc47875143affbbbf01d3d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/LICENSE.md
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016, 2017, 2018 Maarten Westenberg (mw12554@hotmail.com)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/LICENSE.txt b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/LICENSE.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a4eee4a8bc60b3f7dad964f8c1a403af789f935f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Maarten Westenberg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_WiFi.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_WiFi.ino
new file mode 100644
index 0000000000000000000000000000000000000000..697e863d8e78361a3dd53249f34485450dfba2d0
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_WiFi.ino
@@ -0,0 +1,372 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains the LoRa filesystem specific code
+
+// ----------------------------------------------------------------------------
+// WLANSTATUS prints the status of the Wlan.
+// The status of the Wlan "connection" can change if we have no relation
+// with the well known router anymore. Normally this relation is preserved
+// but sometimes we have to reconfirm to the router again and we get the same
+// address too.
+// So, if the router is still in range we can "survive" with the same address
+// and we may have to renew the "connection" from time to time.
+// But when we loose the SSID connection, we may have to look for another router.
+//
+// Parameters: <none>
+// Return value: Returns 1 when still WL_CONNETED, otherwise returns 0
+// ----------------------------------------------------------------------------
+int WlanStatus() {
+
+	switch (WiFi.status()) {
+		case WL_CONNECTED:
+#if DUSB>=1
+			if ( debug>=0 ) {
+				Serial.print(F("A WlanStatus:: CONNECTED to "));				// 3
+				Serial.println(WiFi.SSID());
+			}
+#endif
+			WiFi.setAutoReconnect(true);				// Reconenct to this AP if DISCONNECTED
+			return(1);
+			break;
+
+		// In case we get disconnected from the AP we loose the IP address.
+		// The ESP is configured to reconnect to the last router in memory.
+		case WL_DISCONNECTED:
+#if DUSB>=1
+			if ( debug>=0 ) {
+				Serial.print(F("A WlanStatus:: DISCONNECTED, IP="));			// 6
+				Serial.println(WiFi.localIP());
+			}
+#endif
+			//while (! WiFi.isConnected() ) {
+				// Wait
+				delay(1);
+			//}
+			return(0);
+			break;
+
+		// When still pocessing
+		case WL_IDLE_STATUS:
+#if DUSB>=1
+			if ( debug>=0 ) {
+				Serial.println(F("A WlanStatus:: IDLE"));					// 0
+			}
+#endif
+			break;
+		
+		// This code is generated as soonas the AP is out of range
+		// Whene detected, the program will search for a better AP in range
+		case WL_NO_SSID_AVAIL:
+#if DUSB>=1
+			if ( debug>=0 )
+				Serial.println(F("WlanStatus:: NO SSID"));					// 1
+#endif
+			break;
+			
+		case WL_CONNECT_FAILED:
+#if DUSB>=1
+			if ( debug>=0 )
+				Serial.println(F("A WlanStatus:: FAILED"));					// 4
+#endif
+			break;
+			
+		// Never seen this code
+		case WL_SCAN_COMPLETED:
+#if DUSB>=1
+			if ( debug>=0 )
+				Serial.println(F("A WlanStatus:: SCAN COMPLETE"));			// 2
+#endif
+			break;
+			
+		// Never seen this code
+		case WL_CONNECTION_LOST:
+#if DUSB>=1
+			if ( debug>=0 )
+				Serial.println(F("A WlanStatus:: LOST"));					// 5
+#endif
+			break;
+			
+		// This code is generated for example when WiFi.begin() has not been called
+		// before accessing WiFi functions
+		case WL_NO_SHIELD:
+#if DUSB>=1
+			if ( debug>=0 )
+				Serial.println(F("A WlanStatus:: WL_NO_SHIELD"));				// 
+#endif
+			break;
+			
+		default:
+#if DUSB>=1
+			if ( debug>=0 ) {
+				Serial.print(F("A WlanStatus Error:: code="));
+				Serial.println(WiFi.status());								// 255 means ERROR
+			}
+#endif
+			break;
+	}
+	return(-1);
+	
+} // WlanStatus
+
+// ----------------------------------------------------------------------------
+// config.txt is a text file that contains lines(!) with WPA configuration items
+// Each line contains an KEY vaue pair describing the gateway configuration
+//
+// ----------------------------------------------------------------------------
+int WlanReadWpa() {
+	
+	readConfig( CONFIGFILE, &gwayConfig);
+
+	if (gwayConfig.sf != (uint8_t) 0) sf = (sf_t) gwayConfig.sf;
+	ifreq = gwayConfig.ch;
+	debug = gwayConfig.debug;
+	pdebug = gwayConfig.pdebug;
+	_cad = gwayConfig.cad;
+	_hop = gwayConfig.hop;
+	gwayConfig.boots++;							// Every boot of the system we increase the reset
+	
+#if GATEWAYNODE==1
+	if (gwayConfig.fcnt != (uint8_t) 0) frameCount = gwayConfig.fcnt+10;
+#endif
+	
+#if WIFIMANAGER==1
+	String ssid=gwayConfig.ssid;
+	String pass=gwayConfig.pass;
+
+	char ssidBuf[ssid.length()+1];
+	ssid.toCharArray(ssidBuf,ssid.length()+1);
+	char passBuf[pass.length()+1];
+	pass.toCharArray(passBuf,pass.length()+1);
+	Serial.print(F("WlanReadWpa: ")); Serial.print(ssidBuf); Serial.print(F(", ")); Serial.println(passBuf);
+	
+	strcpy(wpa[0].login, ssidBuf);				// XXX changed from wpa[0][0] = ssidBuf
+	strcpy(wpa[0].passw, passBuf);
+	
+	Serial.print(F("WlanReadWpa: <")); 
+	Serial.print(wpa[0].login); 				// XXX
+	Serial.print(F(">, <")); 
+	Serial.print(wpa[0].passw);
+	Serial.println(F(">"));
+#endif
+
+}
+
+
+// ----------------------------------------------------------------------------
+// Print the WPA data of last WiFiManager to the config file
+// ----------------------------------------------------------------------------
+#if WIFIMANAGER==1
+int WlanWriteWpa( char* ssid, char *pass) {
+
+#if DUSB>=1
+	if (( debug >=0 ) && ( pdebug & P_MAIN )) {
+		Serial.print(F("M WlanWriteWpa:: ssid=")); 
+		Serial.print(ssid);
+		Serial.print(F(", pass=")); 
+		Serial.print(pass); 
+		Serial.println();
+	}
+#endif
+	// Version 3.3 use of config file
+	String s((char *) ssid);
+	gwayConfig.ssid = s;
+	
+	String p((char *) pass);
+	gwayConfig.pass = p;
+
+#if GATEWAYNODE==1	
+	gwayConfig.fcnt = frameCount;
+#endif
+	gwayConfig.ch = ifreq;
+	gwayConfig.sf = sf;
+	gwayConfig.cad = _cad;
+	gwayConfig.hop = _hop;
+	
+	writeConfig( CONFIGFILE, &gwayConfig);
+	return 1;
+}
+#endif
+
+
+
+// ----------------------------------------------------------------------------
+// Function to join the Wifi Network
+//	It is a matter of returning to the main loop() asap and make sure in next loop
+//	the reconnect is done first thing. By default the system will reconnect to the
+// samen SSID as it was connected to before.
+// Parameters:
+//		int maxTry: Number of retries we do:
+//		0: Used during Setup first CONNECT
+//		1: Try once and if unsuccessful return(1);
+//		x: Try x times
+//
+//  Returns:
+//		On failure: Return -1
+//		On connect: return 1
+//		On Disconnect state: return 0
+//
+//  XXX After a few retries, the ESP8266 should be reset. Note: Switching between 
+//	two SSID's does the trick. Rettrying the same SSID does not.
+//	Workaround is found below: Let the ESP8266 forget the SSID
+//
+//  NOTE: The Serial works only on debug setting and not on pdebug. This is 
+//	because WiFi problems would make webserver (which works on WiFI) useless.
+// ----------------------------------------------------------------------------
+int WlanConnect(int maxTry) {
+  
+#if WIFIMANAGER==1
+	WiFiManager wifiManager;
+#endif
+
+	unsigned char agains = 0;
+	unsigned char wpa_index = (WIFIMANAGER >0 ? 0 : 1);		// Skip over first record for WiFiManager
+
+	// The initial setup() call is done with parameter 0
+	// We clear the WiFi memory and start with previous AP.
+	//
+	if (maxTry==0) {
+		Serial.println(F("WlanConnect:: Init para 0"));
+		WiFi.persistent(false);
+		WiFi.mode(WIFI_OFF);   // this is a temporary line, to be removed after SDK update to 1.5.4
+		if (gwayConfig.ssid.length() >0) {
+			WiFi.begin(gwayConfig.ssid.c_str(), gwayConfig.pass.c_str());
+			delay(100);
+		}
+	}
+	
+	// So try to connect to WLAN as long as we are not connected.
+	// The try parameters tells us how many times we try before giving up
+	// Value 0 is reserved for setup() first time connect
+	int i=0;
+
+	while ( (WiFi.status() != WL_CONNECTED) && ( i<= maxTry ) )
+	{
+
+		// We try every SSID in wpa array until success
+		for (int j=wpa_index; (j< (sizeof(wpa)/sizeof(wpa[0]))) && (WiFi.status() != WL_CONNECTED ); j++)
+		{
+			// Start with well-known access points in the list
+			char *ssid		= wpa[j].login;
+			char *password	= wpa[j].passw;
+#if DUSB>=1
+			if (debug>=0)  {
+				Serial.print(i);
+				Serial.print(':');
+				Serial.print(j); 
+				Serial.print(':');
+				Serial.print(sizeof(wpa)/sizeof(wpa[0]));
+				Serial.print(F(". WiFi connect SSID=")); 
+				Serial.print(ssid);
+				if ( debug>=1 ) {
+					Serial.print(F(", pass="));
+					Serial.print(password);
+				}
+				Serial.println();
+			}
+#endif		
+			// Count the number of times we call WiFi.begin
+			gwayConfig.wifis++;
+
+
+
+			WiFi.mode(WIFI_STA);
+			delay(1000);
+			WiFi.begin(ssid, password);
+			delay(8000);
+			
+			// Check the connection status again, return values
+			// 1 = CONNECTED
+			// 0 = DISCONNECTED (will reconnect)
+			// -1 = No SSID or other cause			
+			int stat = WlanStatus();
+			if ( stat == 1) {
+				writeGwayCfg(CONFIGFILE);					// XXX Write configuration to SPIFFS
+				return(1);
+			}
+		
+			// We increase the time for connect but try the same SSID
+			// We try for 10 times
+			agains=1;
+			while (((WiFi.status()) != WL_CONNECTED) && (agains < 10)) {
+				agains++;
+				delay(agains*500);
+#if DUSB>=1
+				if ( debug>=0 ) {
+					Serial.print(".");
+				}
+#endif
+			}
+#if DUSB>=1
+			Serial.println();
+#endif		
+			//if ( WiFi.status() == WL_DISCONNECTED) return(0);				// XXX 180811 removed
+
+
+			// Make sure that we can connect to different AP's than 1
+			// this is a patch. Normally we connect to previous one.
+			WiFi.persistent(false);
+			WiFi.mode(WIFI_OFF);   // this is a temporary line, to be removed after SDK update to 1.5.4
+
+		} //for next WPA defined AP
+	  
+		i++;			// Number of times we try to connect
+	} //while
+
+	
+	// If we are not connected to a well known AP
+	// we can invoike WIFIMANAGER or else return unsuccessful.
+	if (WiFi.status() != WL_CONNECTED) {
+#if WIFIMANAGER==1
+#if DUSB>=1
+		Serial.println(F("Starting Access Point Mode"));
+		Serial.print(F("Connect Wifi to accesspoint: "));
+		Serial.print(AP_NAME);
+		Serial.print(F(" and connect to IP: 192.168.4.1"));
+		Serial.println();
+#endif
+		wifiManager.autoConnect(AP_NAME, AP_PASSWD );
+		//wifiManager.startConfigPortal(AP_NAME, AP_PASSWD );
+		// At this point, there IS a Wifi Access Point found and connected
+		// We must connect to the local SPIFFS storage to store the access point
+		//String s = WiFi.SSID();
+		//char ssidBuf[s.length()+1];
+		//s.toCharArray(ssidBuf,s.length()+1);
+		// Now look for the password
+		struct station_config sta_conf;
+		wifi_station_get_config(&sta_conf);
+
+		//WlanWriteWpa(ssidBuf, (char *)sta_conf.password);
+		WlanWriteWpa((char *)sta_conf.ssid, (char *)sta_conf.password);
+#else
+#if DUSB>=1
+		if (debug>=0) {
+			Serial.println(F("WlanConnect:: Not connected after all"));
+			Serial.print(F("WLAN retry="));
+			Serial.print(i);
+			Serial.print(F(" , stat="));
+			Serial.print(WiFi.status() );						// Status. 3 is WL_CONNECTED
+			Serial.println();
+		}
+#endif// DUSB
+		return(-1);
+#endif
+	}
+
+	yield();
+	return(1);
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_gatewayMgt.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_gatewayMgt.ino
new file mode 100644
index 0000000000000000000000000000000000000000..098adb0bf9776811d12158a82df66641bd5da32b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_gatewayMgt.ino
@@ -0,0 +1,84 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg 
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// Based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+// and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg
+//
+// This file contains the functions to do management over UDP
+// We could make use of the LoRa message function for the Gateway sensor
+// itself. However the functions defined in this file are not sensor
+// functions and activating them through the LoRa interface would add
+// no value and make the code more complex.
+//
+// So advantage: Simple, and does not mess with TTN setup.
+//
+// Disadvantage of course is that you need to setup you own small backend
+// function to exchange messages with the gateway, as TTN won't do this.
+//
+// XXX But, if necessary we can always add this later.
+
+#if GATEWAYMGT==1
+
+#if !defined _THINGPORT
+#error "The management functions needs _THINGPORT defined (and not over _TTNPORT)"
+#endif
+
+
+
+// ----------------------------------------------------------------------------
+// Ths function gateway_mgt is called in the UDP Receive function after
+// all well-known LoRa Gateway messages are scanned.
+//
+// As part of this function we will listen for another set of messages
+// that is defined in loraModem.h.
+// All opCodes start with 0x1y for at leaving opcodes 0x00 to 0x0F to the
+// pure Gateway protocol
+//
+// Incoming mesage format:
+//	buf[0]-buf[2], These are 0x00 or dont care
+//	buf[3], contains opcode
+//	buf[4]-buf[7], contains parameter max. 4 bytes.
+//
+// Upstream Message format:
+//
+// ----------------------------------------------------------------------------
+void gateway_mgt(uint8_t size, uint8_t *buff) {
+
+	uint8_t opcode = buff[3];
+	
+	switch (opcode) {
+		case MGT_RESET:
+			Serial.println(F("gateway_mgt:: RESET"));
+			// No further parameters, just reset the GWay
+			setup();								// Call the sketch setup function
+			// Send Ack to server
+			
+		break;
+		case MGT_SET_SF:
+			Serial.println(F("gateway_mgt:: SET SF"));
+			// byte [4] contains desired SF code (7 for SF7 and 12 for SF12)
+		break;
+		case MGT_SET_FREQ:
+			Serial.println(F("gateway_mgt:: SET FREQ"));
+			// Byte [4] contains index of Frequency
+		break;
+		default:
+			Serial.print(F("gateway_mgt:: Unknown UDP code=")); 
+			Serial.println(opcode);
+			return;
+		break;
+	}
+}
+
+#endif //GATEWAYMGT==1
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_loraFiles.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_loraFiles.ino
new file mode 100644
index 0000000000000000000000000000000000000000..840545662d31f648ad34c737277a0e84e6efb031
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_loraFiles.ino
@@ -0,0 +1,428 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3		
+// Date: 2018-08-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains the LoRa filesystem specific code
+
+
+// ============================================================================
+// LORA SPIFFS FILESYSTEM FUNCTIONS
+//
+// The LoRa supporting functions are in the section below
+
+// ----------------------------------------------------------------------------
+// Supporting function to readConfig
+// ----------------------------------------------------------------------------
+void id_print (String id, String val) {
+#if DUSB>=1
+	if (( debug>=0 ) && ( pdebug & P_MAIN )) {
+		Serial.print(id);
+		Serial.print(F("=\t"));
+		Serial.println(val);
+	}
+#endif
+}
+
+// ----------------------------------------------------------------------------
+// INITCONFIG; Init the gateway configuration file
+// Espcecially when calling SPIFFS.format() the gateway is left in an init
+// which is not very well defined. This function will init some of the settings
+// to well known settings.
+// ----------------------------------------------------------------------------
+int initConfig(struct espGwayConfig *c) {
+	(*c).ch = 0;
+	(*c).sf = _SPREADING;
+	(*c).debug = 1;
+	(*c).pdebug = P_GUI;
+	(*c).cad = _CAD;
+	(*c).hop = false;
+	(*c).expert = false;
+}
+
+
+// ----------------------------------------------------------------------------
+// Read the gateway configuration file
+// ----------------------------------------------------------------------------
+int readConfig(const char *fn, struct espGwayConfig *c) {
+
+	int tries = 0;
+#if DUSB>=1
+	Serial.println(F("readConfig:: Starting "));
+#endif
+	if (!SPIFFS.exists(fn)) {
+#if DUSB>=1
+		if (( debug>=0 ) && ( pdebug & P_MAIN ))
+		Serial.print(F("M ERR:: readConfig, file="));
+		Serial.print(fn);
+		Serial.println(F(" does not exist .. Formatting"));
+#endif
+		SPIFFS.format();
+		initConfig(c);
+		return(-1);
+	}
+
+	File f = SPIFFS.open(fn, "r");
+	if (!f) {
+		Serial.println(F("ERROR:: SPIFFS open failed"));
+		return(-1);
+	}
+
+	while (f.available()) {
+		
+#if DUSB>=1
+		if (( debug>=0 ) && ( pdebug & P_MAIN )) {
+			Serial.print('.');
+		}
+#endif
+		// If we wait for more than 10 times, reformat the filesystem
+		// We do this so that the system will be responsive (over OTA for example).
+		//
+		if (tries >= 10) {
+			f.close();
+#if DUSB>=1
+			if (( debug>=0 ) && ( pdebug & P_MAIN ))
+				Serial.println(F("Formatting"));
+#endif
+			SPIFFS.format();
+			initConfig(c);
+			f = SPIFFS.open(fn, "r");
+			tries = 0;
+		}
+		
+		String id =f.readStringUntil('=');						// C++ thing
+		String val=f.readStringUntil('\n');
+		
+		if (id == "SSID") {										// WiFi SSID
+			id_print(id, val);
+			(*c).ssid = val;									// val contains ssid, we do NO check
+		}
+		else if (id == "PASS") { 								// WiFi Password
+			id_print(id, val); 
+			(*c).pass = val;
+		}
+		else if (id == "CH") { 									// Frequency Channel
+			id_print(id,val); 
+			(*c).ch = (uint32_t) val.toInt();
+		}
+		else if (id == "SF") { 									// Spreading Factor
+			id_print(id, val);
+			(*c).sf = (uint32_t) val.toInt();
+		}
+		else if (id == "FCNT") {								// Frame Counter
+			id_print(id, val);
+			(*c).fcnt = (uint32_t) val.toInt();
+		}
+		else if (id == "DEBUG") {								// Debug Level
+			id_print(id, val);
+			(*c).debug = (uint8_t) val.toInt();
+		}
+		else if (id == "PDEBUG") {								// pDebug Pattern
+			Serial.print(F("PDEBUG=")); Serial.println(val);
+			(*c).pdebug = (uint8_t) val.toInt();
+		}
+		else if (id == "CAD") {									// CAD setting
+			Serial.print(F("CAD=")); Serial.println(val);
+			(*c).cad = (uint8_t) val.toInt();
+		}
+		else if (id == "HOP") {									// HOP setting
+			Serial.print(F("HOP=")); Serial.println(val);
+			(*c).hop = (uint8_t) val.toInt();
+		}
+		else if (id == "BOOTS") {								// BOOTS setting
+			id_print(id, val);
+			(*c).boots = (uint8_t) val.toInt();
+		}
+		else if (id == "RESETS") {								// RESET setting
+			id_print(id, val);
+			(*c).resets = (uint8_t) val.toInt();
+		}
+		else if (id == "WIFIS") {								// WIFIS setting
+			id_print(id, val);
+			(*c).wifis = (uint8_t) val.toInt();
+		}
+		else if (id == "VIEWS") {								// VIEWS setting
+			id_print(id, val);
+			(*c).views = (uint8_t) val.toInt();
+		}
+		else if (id == "NODE") {								// NODE setting
+			id_print(id, val);
+			(*c).isNode = (uint8_t) val.toInt();
+		}
+		else if (id == "REFR") {								// REFR setting
+			id_print(id, val);
+			(*c).refresh = (uint8_t) val.toInt();
+		}
+		else if (id == "REENTS") {								// REENTS setting
+			id_print(id, val);
+			(*c).reents = (uint8_t) val.toInt();
+		}
+		else if (id == "NTPERR") {								// NTPERR setting
+			id_print(id, val);
+			(*c).ntpErr = (uint8_t) val.toInt();
+		}
+		else if (id == "NTPETIM") {								// NTPERR setting
+			id_print(id, val);
+			(*c).ntpErrTime = (uint32_t) val.toInt();
+		}
+		else if (id == "NTPS") {								// NTPS setting
+			id_print(id, val);
+			(*c).ntps = (uint8_t) val.toInt();
+		}
+		else if (id == "FILENO") {								// log FILENO setting
+			id_print(id, val);
+			(*c).logFileNo = (uint8_t) val.toInt();
+		}
+		else if (id == "FILEREC") {								// FILEREC setting
+			id_print(id, val);
+			(*c).logFileRec = (uint16_t) val.toInt();
+		}
+		else if (id == "FILENUM") {								// FILEREC setting
+			id_print(id, val);
+			(*c).logFileNum = (uint16_t) val.toInt();
+		}
+		else if (id == "EXPERT") {								// FILEREC setting
+			id_print(id, val);
+			(*c).expert = (uint8_t) val.toInt();
+		}
+		else {
+			tries++;
+		}
+	}
+	f.close();
+#if DUSB>=1
+	if (debug>=0) {
+		Serial.println('#');
+	}
+#endif
+	Serial.println();
+	return(1);
+}
+
+// ----------------------------------------------------------------------------
+// Write the current gateway configuration to SPIFFS. First copy all the
+// separate data items to the gwayConfig structure
+//
+// ----------------------------------------------------------------------------
+int writeGwayCfg(const char *fn) {
+
+	gwayConfig.ssid = WiFi.SSID();
+	gwayConfig.pass = WiFi.psk();						// XXX We should find a way to store the password too
+	gwayConfig.ch = ifreq;								// Frequency Index
+	gwayConfig.sf = (uint8_t) sf;						// Spreading Factor
+	gwayConfig.debug = debug;
+	gwayConfig.pdebug = pdebug;
+	gwayConfig.cad = _cad;
+	gwayConfig.hop = _hop;
+#if GATEWAYNODE==1
+	gwayConfig.fcnt = frameCount;
+#endif
+	return(writeConfig(fn, &gwayConfig));
+}
+
+// ----------------------------------------------------------------------------
+// Write the configuration as found in the espGwayConfig structure
+// to SPIFFS
+// Parameters:
+//		fn; Filename
+//		c; struct config
+// Returns:
+//		1 when successful, -1 on error
+// ----------------------------------------------------------------------------
+int writeConfig(const char *fn, struct espGwayConfig *c) {
+
+	if (!SPIFFS.exists(fn)) {
+		Serial.print("WARNING:: writeConfig, file not exists, formatting ");
+		SPIFFS.format();
+		initConfig(c);		// XXX make all initial declarations here if config vars need to have a value
+		Serial.println(fn);
+	}
+	File f = SPIFFS.open(fn, "w");
+	if (!f) {
+		Serial.print("ERROR:: writeConfig, open file=");
+		Serial.print(fn);
+		Serial.println();
+		return(-1);
+	}
+
+	f.print("SSID"); f.print('='); f.print((*c).ssid); f.print('\n'); 
+	f.print("PASS"); f.print('='); f.print((*c).pass); f.print('\n');
+	f.print("CH"); f.print('='); f.print((*c).ch); f.print('\n');
+	f.print("SF");   f.print('='); f.print((*c).sf);   f.print('\n');
+	f.print("FCNT"); f.print('='); f.print((*c).fcnt); f.print('\n');
+	f.print("DEBUG"); f.print('='); f.print((*c).debug); f.print('\n');
+	f.print("PDEBUG"); f.print('='); f.print((*c).pdebug); f.print('\n');
+	f.print("CAD");  f.print('='); f.print((*c).cad); f.print('\n');
+	f.print("HOP");  f.print('='); f.print((*c).hop); f.print('\n');
+	f.print("NODE");  f.print('='); f.print((*c).isNode); f.print('\n');
+	f.print("BOOTS");  f.print('='); f.print((*c).boots); f.print('\n');
+	f.print("RESETS");  f.print('='); f.print((*c).resets); f.print('\n');
+	f.print("WIFIS");  f.print('='); f.print((*c).wifis); f.print('\n');
+	f.print("VIEWS");  f.print('='); f.print((*c).views); f.print('\n');
+	f.print("REFR");  f.print('='); f.print((*c).refresh); f.print('\n');
+	f.print("REENTS");  f.print('='); f.print((*c).reents); f.print('\n');
+	f.print("NTPETIM");  f.print('='); f.print((*c).ntpErrTime); f.print('\n');
+	f.print("NTPERR");  f.print('='); f.print((*c).ntpErr); f.print('\n');
+	f.print("NTPS");  f.print('='); f.print((*c).ntps); f.print('\n');
+	f.print("FILEREC");  f.print('='); f.print((*c).logFileRec); f.print('\n');
+	f.print("FILENO");  f.print('='); f.print((*c).logFileNo); f.print('\n');
+	f.print("FILENUM");  f.print('='); f.print((*c).logFileNum); f.print('\n');
+	f.print("EXPERT");  f.print('='); f.print((*c).expert); f.print('\n');
+	
+	f.close();
+	return(1);
+}
+
+// ----------------------------------------------------------------------------
+// Add a line with statistics to the log.
+//
+// We put the check in the function to protect against calling 
+// the function without STAT_LOG being proper defined
+// ToDo: Store the fileNo and the fileRec in the status file to save for 
+// restarts
+// Parameters:
+//		line; char array with characters to write to log
+//		cnt;
+// Returns:
+//		<none>
+// ----------------------------------------------------------------------------
+void addLog(const unsigned char * line, int cnt) 
+{
+#if STAT_LOG==1
+	char fn[16];
+	
+	if (gwayConfig.logFileRec > LOGFILEREC) {		// Have to make define for this
+		gwayConfig.logFileRec = 0;					// INn new logFile start ith record 0
+		gwayConfig.logFileNo++;						// Increase file ID
+		gwayConfig.logFileNum++;					// Increase number of log files
+	}
+	gwayConfig.logFileRec++;
+	
+	// If we have too many logfies, delete the oldest
+	//
+	if (gwayConfig.logFileNum > LOGFILEMAX){
+		sprintf(fn,"/log-%d", gwayConfig.logFileNo - LOGFILEMAX);
+#if DUSB>=1
+		if (( debug>=0 ) && ( pdebug & P_GUI )) {
+			Serial.print(F("G addLog:: Too many logfile, deleting="));
+			Serial.println(fn);
+		}
+#endif
+		SPIFFS.remove(fn);
+		gwayConfig.logFileNum--;
+	}
+	
+	// Make sure we have the right fileno
+	sprintf(fn,"/log-%d", gwayConfig.logFileNo); 
+	
+	// If there is no SPIFFS, Error
+	// Make sure to write the config record/line also
+	if (!SPIFFS.exists(fn)) {
+#if DUSB>=1
+		if (( debug >= 1 ) && ( pdebug & P_GUI )) {
+			Serial.print(F("G ERROR:: addLog:: file="));
+			Serial.print(fn);
+			Serial.print(F(" does not exist .. rec="));
+			Serial.print(gwayConfig.logFileRec);
+			Serial.println();
+		}
+#endif
+	}
+	
+	File f = SPIFFS.open(fn, "a");
+	if (!f) {
+#if DUSB>=1
+		if (( debug>=1 ) && ( pdebug & P_GUI )) {
+			Serial.println("G file open failed=");
+			Serial.println(fn);
+		}
+#endif
+		return;									// If file open failed, return
+	}
+	
+	int i;
+#if DUSB>=1
+	if (( debug>=1 ) && ( pdebug & P_GUI )) {
+		Serial.print(F("G addLog:: fileno="));
+		Serial.print(gwayConfig.logFileNo);
+		Serial.print(F(", rec="));
+		Serial.print(gwayConfig.logFileRec);
+
+		Serial.print(F(": "));
+
+		for (i=0; i< 12; i++) {				// The first 12 bytes contain non printable characters
+			Serial.print(line[i],HEX);
+			Serial.print(' ');
+		}
+		Serial.print((char *) &line[i]);	// The rest if the buffer contains ascii
+
+		Serial.println();
+	}
+#endif //DUSB
+
+
+	for (i=0; i< 12; i++) {					// The first 12 bytes contain non printable characters
+	//	f.print(line[i],HEX);
+		f.print('*');
+	}
+	f.write(&(line[i]), cnt-12);				// write/append the line to the file
+	f.print('\n');
+	f.close();								// Close the file after appending to it
+
+#endif //STAT_LOG
+}
+
+// ----------------------------------------------------------------------------
+// Print (all) logfiles
+//
+// ----------------------------------------------------------------------------
+void printLog()
+{
+	char fn[16];
+	int i=0;
+#if DUSB>=1
+	while (i< LOGFILEMAX ) {
+		sprintf(fn,"/log-%d", gwayConfig.logFileNo - i);
+		if (!SPIFFS.exists(fn)) break;		// break the loop
+
+		// Open the file for reading
+		File f = SPIFFS.open(fn, "r");
+		
+		int j;
+		for (j=0; j<LOGFILEREC; j++) {
+			
+			String s=f.readStringUntil('\n');
+			if (s.length() == 0) break;
+
+			Serial.println(s.substring(12));			// Skip the first 12 Gateway specific binary characters
+			yield();
+		}
+		i++;
+	}
+#endif
+} //printLog
+
+
+// ----------------------------------------------------------------------------
+// listDir
+//	List the directory and put it in
+// ----------------------------------------------------------------------------
+void listDir(char * dir) 
+{
+#if DUSB>=1
+	
+#endif
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_loraModem.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_loraModem.ino
new file mode 100644
index 0000000000000000000000000000000000000000..0d0c45b38529d318b8b2054d696c4bf1eebc0a91
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_loraModem.ino
@@ -0,0 +1,1123 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains the LoRa modem specific code enabling to receive
+// and transmit packages/messages.
+// ========================================================================================
+//
+//
+//
+// SPI AND INTERRUPTS
+// The RFM96/SX1276 communicaties with the ESP8266 by means of interrupts 
+// and SPI interface. The SPI interface is bidirectional and allows both
+// parties to simultaneous write and read to registers.
+// Major drawback is that access is not protected for interrupt and non-
+// interrupt access. This means that when a program in loop() and a program
+// in interrupt do access the readregister and writeRegister() function
+// at teh same time that probably an error will occur.
+// Therefore it is best to Either not use interrupts AT all (like LMIC)
+// or only use these functions in inteerupts and to further processing
+// in the main loop() program.
+//
+// ============================================================================
+
+
+// ----------------------------------------------------------------------------
+// Mutex definitiona
+//
+// ----------------------------------------------------------------------------
+#if MUTEX==1
+	void CreateMutux(int *mutex) {
+		*mutex=1;
+	}
+
+#define LIB_MUTEX 1
+#if LIB_MUTEX==1
+	bool GetMutex(int *mutex) {
+		//noInterrupts();
+		if (*mutex==1) { 
+			*mutex=0; 
+			//interrupts(); 
+			return(true); 
+		}
+		//interrupts();
+		return(false);
+	}
+#else	
+	bool GetMutex(int *mutex) {
+
+	int iOld = 1, iNew = 0;
+
+	asm volatile (
+		"rsil a15, 1\n"    // read and set interrupt level to 1
+		"l32i %0, %1, 0\n" // load value of mutex
+		"bne %0, %2, 1f\n" // compare with iOld, branch if not equal
+		"s32i %3, %1, 0\n" // store iNew in mutex
+		"1:\n"             // branch target
+		"wsr.ps a15\n"     // restore program state
+		"rsync\n"
+		: "=&r" (iOld)
+		: "r" (mutex), "r" (iOld), "r" (iNew)
+		: "a15", "memory"
+	);
+	return (bool)iOld;
+}
+#endif
+
+	void ReleaseMutex(int *mutex) {
+		*mutex=1;
+	}
+	
+#endif //MUTEX==1
+
+// ----------------------------------------------------------------------------
+// Read one byte value, par addr is address
+// Returns the value of register(addr)
+// 
+// The SS (Chip select) pin is used to make sure the RFM95 is selected
+// The variable is for obvious reasons valid for read and write traffic at the
+// same time. Since both read and write mean that we write to the SPI interface.
+// Parameters:
+//	Address: SPI address to read from. Type uint8_t
+// Return:
+//	Value read from address
+// ----------------------------------------------------------------------------
+
+// define the SPI settings for reading messages
+SPISettings readSettings(SPISPEED, MSBFIRST, SPI_MODE0);
+
+uint8_t readRegister(uint8_t addr)
+{
+
+	SPI.beginTransaction(readSettings);				
+    digitalWrite(pins.ss, LOW);					// Select Receiver
+	SPI.transfer(addr & 0x7F);
+	uint8_t res = (uint8_t) SPI.transfer(0x00);
+    digitalWrite(pins.ss, HIGH);				// Unselect Receiver
+	SPI.endTransaction();
+    return((uint8_t) res);
+}
+
+
+// ----------------------------------------------------------------------------
+// Write value to a register with address addr. 
+// Function writes one byte at a time.
+// Parameters:
+//	addr: SPI address to write to
+//	value: The value to write to address
+// Returns:
+//	<void>
+// ----------------------------------------------------------------------------
+
+// define the settings for SPI writing
+SPISettings writeSettings(SPISPEED, MSBFIRST, SPI_MODE0);
+
+void writeRegister(uint8_t addr, uint8_t value)
+{
+	SPI.beginTransaction(writeSettings);
+	digitalWrite(pins.ss, LOW);					// Select Receiver
+	
+	SPI.transfer((addr | 0x80) & 0xFF);
+	SPI.transfer(value & 0xFF);
+	//delayMicroseconds(10);
+	
+    digitalWrite(pins.ss, HIGH);				// Unselect Receiver
+	
+	SPI.endTransaction();
+}
+
+
+// ----------------------------------------------------------------------------
+// Write a buffer to a register with address addr. 
+// Function writes one byte at a time.
+// Parameters:
+//	addr: SPI address to write to
+//	value: The value to write to address
+// Returns:
+//	<void>
+// ----------------------------------------------------------------------------
+
+void writeBuffer(uint8_t addr, uint8_t *buf, uint8_t len)
+{
+	//noInterrupts();							// XXX
+	
+	SPI.beginTransaction(writeSettings);
+	digitalWrite(pins.ss, LOW);					// Select Receiver
+	
+	SPI.transfer((addr | 0x80) & 0xFF);			// write buffer address
+	for (uint8_t i=0; i<len; i++) {
+		SPI.transfer(buf[i] & 0xFF);
+	}
+    digitalWrite(pins.ss, HIGH);				// Unselect Receiver
+	
+	SPI.endTransaction();
+}
+
+// ----------------------------------------------------------------------------
+//  setRate is setting rate and spreading factor and CRC etc. for transmission
+//		Modem Config 1 (MC1) == 0x72 for sx1276
+//		Modem Config 2 (MC2) == (CRC_ON) | (sf<<4)
+//		Modem Config 3 (MC3) == 0x04 | (optional SF11/12 LOW DATA OPTIMIZE 0x08)
+//		sf == SF7 default 0x07, (SF7<<4) == SX72_MC2_SF7
+//		bw == 125 == 0x70
+//		cr == CR4/5 == 0x02
+//		CRC_ON == 0x04
+// ----------------------------------------------------------------------------
+
+void setRate(uint8_t sf, uint8_t crc) 
+{
+	uint8_t mc1=0, mc2=0, mc3=0;
+#if DUSB>=2
+	if ((sf<SF7) || (sf>SF12)) {
+		if (( debug>=1 ) && ( pdebug & P_RADIO )) {
+			Serial.print(F("setRate:: SF="));
+			Serial.println(sf);
+		}
+		return;
+	}
+#endif
+	// Set rate based on Spreading Factor etc
+    if (sx1272) {
+		mc1= 0x0A;				// SX1276_MC1_BW_250 0x80 | SX1276_MC1_CR_4_5 0x02
+		mc2= ((sf<<4) | crc) % 0xFF;
+		// SX1276_MC1_BW_250 0x80 | SX1276_MC1_CR_4_5 0x02 | SX1276_MC1_IMPLICIT_HEADER_MODE_ON 0x01
+        if (sf == SF11 || sf == SF12) { mc1= 0x0B; }			        
+    }
+	
+	// For sx1276 chips is the CRC ON is 
+	else {
+		if (sf==SF8) {
+			mc1= 0x78;				// SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_8==0x08
+		}
+		else {
+			mc1= 0x72;				// SX1276_MC1_BW_125==0x70 | SX1276_MC1_CR_4_5==0x02
+		}
+		mc2= ((sf<<4) | crc) & 0xFF; // crc is 0x00 or 0x04==SX1276_MC2_RX_PAYLOAD_CRCON
+		mc3= 0x04;				// 0x04; SX1276_MC3_AGCAUTO
+        if (sf == SF11 || sf == SF12) { mc3|= 0x08; }		// 0x08 | 0x04
+    }
+	
+	// Implicit Header (IH), for class b beacons (&& SF6)
+	//if (getIh(LMIC.rps)) {
+    //   mc1 |= SX1276_MC1_IMPLICIT_HEADER_MODE_ON;
+    //    writeRegister(REG_PAYLOAD_LENGTH, getIh(LMIC.rps)); // required length
+    //}
+	
+	writeRegister(REG_MODEM_CONFIG1, (uint8_t) mc1);
+	writeRegister(REG_MODEM_CONFIG2, (uint8_t) mc2);
+	writeRegister(REG_MODEM_CONFIG3, (uint8_t) mc3);
+	
+	// Symbol timeout settings
+    if (sf == SF10 || sf == SF11 || sf == SF12) {
+        writeRegister(REG_SYMB_TIMEOUT_LSB, (uint8_t) 0x05);
+    } else {
+        writeRegister(REG_SYMB_TIMEOUT_LSB, (uint8_t) 0x08);
+    }
+	return;
+}
+
+
+// ----------------------------------------------------------------------------
+// Set the frequency for our gateway
+// The function has no parameter other than the freq setting used in init.
+// Since we are usin a 1ch gateway this value is set fixed.
+// ----------------------------------------------------------------------------
+
+void  setFreq(uint32_t freq)
+{
+    // set frequency
+    uint64_t frf = ((uint64_t)freq << 19) / 32000000;
+    writeRegister(REG_FRF_MSB, (uint8_t)(frf>>16) );
+    writeRegister(REG_FRF_MID, (uint8_t)(frf>> 8) );
+    writeRegister(REG_FRF_LSB, (uint8_t)(frf>> 0) );
+	
+	return;
+}
+
+
+// ----------------------------------------------------------------------------
+//	Set Power for our gateway
+// ----------------------------------------------------------------------------
+void setPow(uint8_t powe)
+{
+	if (powe >= 16) powe = 15;
+	//if (powe >= 15) powe = 14;
+	else if (powe < 2) powe =2;
+	
+	ASSERT((powe>=2)&&(powe<=15));
+	
+	uint8_t pac = (0x80 | (powe & 0xF)) & 0xFF;
+	writeRegister(REG_PAC, (uint8_t)pac);								// set 0x09 to pac
+	
+	// XXX Power settings for CFG_sx1272 are different
+	
+	return;
+}
+
+
+// ----------------------------------------------------------------------------
+// Used to set the radio to LoRa mode (transmitter)
+// Please note that this mode can only be set in SLEEP mode and not in Standby.
+// Also there should be not need to re-init this mode is set in the setup() 
+// function.
+// For high freqs (>860 MHz) we need to & with 0x08 otherwise with 0x00
+// ----------------------------------------------------------------------------
+
+//void ICACHE_RAM_ATTR opmodeLora()
+//{
+//#ifdef CFG_sx1276_radio
+//       uint8_t u = OPMODE_LORA | 0x80;   					// TBD: sx1276 high freq 
+//#else // SX-1272
+//	    uint8_t u = OPMODE_LORA | 0x08;
+//#endif
+//    writeRegister(REG_OPMODE, (uint8_t) u);
+//}
+
+
+// ----------------------------------------------------------------------------
+// Set the opmode to a value as defined on top
+// Values are 0x00 to 0x07
+// The value is set for the lowest 3 bits, the other bits are as before.
+// ----------------------------------------------------------------------------
+void  opmode(uint8_t mode)
+{
+	if (mode == OPMODE_LORA) 
+		writeRegister(REG_OPMODE, (uint8_t) mode);
+	else
+		writeRegister(REG_OPMODE, (uint8_t)((readRegister(REG_OPMODE) & ~OPMODE_MASK) | mode));
+}
+
+// ----------------------------------------------------------------------------
+// Hop to next frequency as defined by NUM_HOPS
+// This function should only be used for receiver operation. The current
+// receiver frequency is determined by ifreq index like so: freqs[ifreq] 
+// ----------------------------------------------------------------------------
+void hop() {
+
+	// 1. Set radio to standby
+	opmode(OPMODE_STANDBY);
+		
+	// 3. Set frequency based on value in freq		
+	ifreq = (ifreq + 1) % NUM_HOPS ;							// Increment the freq round robin
+	freq = freqs[ifreq];
+	setFreq(freqs[ifreq]);
+
+	// 4. Set spreading Factor
+	sf = SF7;													// Starting the new frequency 
+	setRate(sf, 0x40);											// set the sf to SF7 
+		
+	// Low Noise Amplifier used in receiver
+	writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  			// 0x0C, 0x23
+	
+	// 7. set sync word
+	writeRegister(REG_SYNC_WORD, (uint8_t) 0x34);				// set 0x39 to 0x34 LORA_MAC_PREAMBLE
+	
+	// prevent node to node communication
+	writeRegister(REG_INVERTIQ,0x27);							// 0x33, 0x27; to reset from TX
+	
+	// Max Payload length is dependent on 256 byte buffer. At startup TX starts at
+	// 0x80 and RX at 0x00. RX therefore maximized at 128 Bytes
+	writeRegister(REG_MAX_PAYLOAD_LENGTH,MAX_PAYLOAD_LENGTH);	// set 0x23 to 0x80==128 bytes
+	writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH);			// 0x22, 0x40==64Byte long
+	
+	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD));	// set reg 0x0D to 0x0F
+	writeRegister(REG_HOP_PERIOD,0x00);							// reg 0x24, set to 0x00
+
+	// 5. Config PA Ramp up time								// set reg 0x0A  
+	writeRegister(REG_PARAMP, (readRegister(REG_PARAMP) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec
+	
+	// Set 0x4D PADAC for SX1276 ; XXX register is 0x5a for sx1272
+	writeRegister(REG_PADAC_SX1276,  0x84); 					// set 0x4D (PADAC) to 0x84
+	//writeRegister(REG_PADAC, readRegister(REG_PADAC) | 0x4);
+	
+	// 8. Reset interrupt Mask, enable all interrupts
+	writeRegister(REG_IRQ_FLAGS_MASK, 0x00);
+		
+	// 9. clear all radio IRQ flags
+    writeRegister(REG_IRQ_FLAGS, 0xFF);
+	
+	// Be aware that micros() has increased significantly from calling 
+	// the hop function until printed below
+	//
+#if DUSB>=1
+	if (( debug>=2 ) && ( pdebug & P_RADIO )){
+			Serial.print(F("hop:: hopTime:: "));
+			Serial.print(micros() - hopTime);
+			Serial.print(F(", "));
+			SerialStat(0);
+	}
+#endif
+	// Remember the last time we hop
+	hopTime = micros();									// At what time did we hop
+}
+	
+
+// ----------------------------------------------------------------------------
+// This LoRa function reads a message from the LoRa transceiver
+// on Success: returns message length read when message correctly received
+// on Failure: it returns a negative value on error (CRC error for example).
+// UP function
+// This is the "lowlevel" receive function called by stateMachine()
+// dealing with the radio specific LoRa functions
+//
+// Parameters:
+//		Payload: uint8_t[] message. when message is read it is returned in payload.
+// Returns:
+//		Length of payload received
+//
+// 9 bytes header
+// followed by data N bytes
+// 4 bytes MIC end
+// ----------------------------------------------------------------------------
+uint8_t receivePkt(uint8_t *payload)
+{
+    uint8_t irqflags = readRegister(REG_IRQ_FLAGS);			// 0x12; read back flags
+
+    cp_nb_rx_rcv++;											// Receive statistics counter
+
+	uint8_t crcUsed = readRegister(REG_HOP_CHANNEL);
+	if (crcUsed & 0x40) {
+#if DUSB>=1
+		if (( debug>=2) && (pdebug & P_RX )) {
+			Serial.println(F("R rxPkt:: CRC used"));
+		}
+#endif
+	}
+	
+    //  Check for payload IRQ_LORA_CRCERR_MASK=0x20 set
+    if (irqflags & IRQ_LORA_CRCERR_MASK)
+    {
+#if DUSB>=1
+        if (( debug>=0) && ( pdebug & P_RADIO )) {
+			Serial.print(F("rxPkt:: Err CRC, ="));
+			SerialTime();
+			Serial.println();
+		}
+#endif
+		return 0;
+    }
+	
+	// Is header OK?
+	// Please note that if we reset the HEADER interrupt in RX,
+	// that we would here conclude that ther eis no HEADER
+	else if ((irqflags & IRQ_LORA_HEADER_MASK) == false)
+    {
+#if DUSB>=1
+        if (( debug>=0) && ( pdebug & P_RADIO )) {
+			Serial.println(F("rxPkt:: Err HEADER"));
+		}
+#endif
+		// Reset VALID-HEADER flag 0x10
+        writeRegister(REG_IRQ_FLAGS, (uint8_t)(IRQ_LORA_HEADER_MASK  | IRQ_LORA_RXDONE_MASK));	// 0x12; clear HEADER (== 0x10) flag
+        return 0;
+    }
+	
+	// If there are no error messages, read the buffer from the FIFO
+	// This means "Set FifoAddrPtr to FifoRxBaseAddr"
+	else {
+        cp_nb_rx_ok++;													// Receive OK statistics counter
+
+		if (readRegister(REG_FIFO_RX_CURRENT_ADDR) != readRegister(REG_FIFO_RX_BASE_AD)) {
+			if (( debug>=0 ) && ( pdebug & P_RADIO )) {
+				Serial.print(F("RX BASE <"));
+				Serial.print(readRegister(REG_FIFO_RX_BASE_AD));
+				Serial.print(F("> != RX CURRENT <"));
+				Serial.print(readRegister(REG_FIFO_RX_CURRENT_ADDR));
+				Serial.print(F(">"));
+				Serial.println();
+			}
+		}
+		
+        //uint8_t currentAddr = readRegister(REG_FIFO_RX_CURRENT_ADDR);	// 0x10
+		uint8_t currentAddr = readRegister(REG_FIFO_RX_BASE_AD);		// 0x0F
+        uint8_t receivedCount = readRegister(REG_RX_NB_BYTES);			// 0x13; How many bytes were read
+#if DUSB>=1
+		if ((debug>=0) && (currentAddr > 64)) {
+			Serial.print(F("rxPkt:: Rx addr>64"));
+			Serial.println(currentAddr);
+		}
+#endif
+        writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) currentAddr);		// 0x0D 
+
+		if (receivedCount > PAYLOAD_LENGTH) {
+#if DUSB>=1
+			if (( debug>=0 ) & ( pdebug & P_RADIO )) {
+				Serial.print(F("rxPkt:: receivedCount="));
+				Serial.println(receivedCount);
+			}
+#endif
+			receivedCount=PAYLOAD_LENGTH;
+		}
+
+        for(int i=0; i < receivedCount; i++)
+        {
+            payload[i] = readRegister(REG_FIFO);			// 0x00, FIFO will auto shift register
+        }
+
+		writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);		// Reset ALL interrupts
+		
+		// A long as DUSB is enabled, and RX debug messages are selected,
+		//the received packet is displayed on the output.
+#if DUSB>=1
+		if (( debug>=0 ) && ( pdebug & P_RX )){
+		
+			Serial.print(F("rxPkt:: t="));
+			SerialTime();
+			
+			Serial.print(F(", f="));
+			Serial.print(ifreq);
+			Serial.print(F(", sf="));
+			Serial.print(sf);
+			Serial.print(F(", a="));
+			if (payload[4]<0x10) Serial.print('0'); Serial.print(payload[4], HEX);
+			if (payload[3]<0x10) Serial.print('0'); Serial.print(payload[3], HEX);
+			if (payload[2]<0x10) Serial.print('0'); Serial.print(payload[2], HEX);
+			if (payload[1]<0x10) Serial.print('0'); Serial.print(payload[1], HEX);
+			Serial.print(F(", flags="));
+			Serial.print(irqflags,HEX);
+			Serial.print(F(", addr="));
+			Serial.print(currentAddr);
+			Serial.print(F(", len="));
+			Serial.print(receivedCount);
+
+			// If debug level 1 is specified, we display the content of the message as well
+			// We need to decode the message as well will it make any sense
+
+			if (debug>=1)  {							// Must be 1 for operational use
+#if _TRUSTED_DECODE==2
+				int index;								// The index of the codex struct to decode
+				String response="";
+
+				uint8_t data[receivedCount];
+				
+				uint8_t DevAddr [4];
+					DevAddr[0] = payload[4];
+					DevAddr[1] = payload[3];
+					DevAddr[2] = payload[2];
+					DevAddr[3] = payload[1];
+				
+				if ((index = inDecodes((char *)(payload+1))) >=0 ) {
+					Serial.print(F(", Ind="));
+					Serial.print(index);
+					//Serial.println();
+				}
+				else if (debug>=1) {
+					Serial.print(F(", No Index"));
+					Serial.println();
+					return(receivedCount);
+				}	
+
+				// ------------------------------
+				
+				Serial.print(F(", data="));
+				for (int i=0; i<receivedCount; i++) { data[i] = payload[i]; }		// Copy array
+				
+				//for (int i=0; i<receivedCount; i++) {
+				//	if (payload[i]<=0xF) Serial.print('0');
+				//	Serial.print(payload[i], HEX);
+				//	Serial.print(' ');
+				//}
+
+
+
+				uint16_t frameCount=payload[7]*256 + payload[6];
+				
+				// The message received has a length, but data starts at byte 9, and stops 4 bytes
+				// before the end since those are MIC bytes
+				uint8_t CodeLength = encodePacket((uint8_t *)(data + 9), receivedCount-9-4, (uint16_t)frameCount, DevAddr, decodes[index].appKey, 0);
+
+				Serial.print(F("- NEW fc="));
+				Serial.print(frameCount);
+				Serial.print(F(", addr="));
+				
+				for (int i=0; i<4; i++) {
+					if (DevAddr[i]<=0xF) Serial.print('0');
+					Serial.print(DevAddr[i], HEX);
+					Serial.print(' ');
+				}
+				
+				Serial.print(F(", len="));
+				Serial.print(CodeLength);
+				Serial.print(F(", data="));
+
+				for (int i=0; i<receivedCount; i++) {
+					if (data[i]<=0xF) Serial.print('0');
+					Serial.print(data[i], HEX);
+					Serial.print(' ');
+				}
+#endif // _TRUSTED_DECODE
+			}
+			
+			Serial.println();
+			
+			if (debug>=2) Serial.flush();
+		}
+#endif //DUSB
+		return(receivedCount);
+    }
+
+	writeRegister(REG_IRQ_FLAGS, (uint8_t) (
+		IRQ_LORA_RXDONE_MASK | 
+		IRQ_LORA_RXTOUT_MASK |
+		IRQ_LORA_HEADER_MASK | 
+		IRQ_LORA_CRCERR_MASK));							// 0x12; Clear RxDone IRQ_LORA_RXDONE_MASK
+    return 0;
+} //receivePkt
+	
+	
+	
+// ----------------------------------------------------------------------------
+// This DOWN function sends a payload to the LoRa node over the air
+// Radio must go back in standby mode as soon as the transmission is finished
+// 
+// NOTE:: writeRegister functions should not be used outside interrupts
+// ----------------------------------------------------------------------------
+bool sendPkt(uint8_t *payLoad, uint8_t payLength)
+{
+#if DUSB>=2
+	if (payLength>=128) {
+		if (debug>=1) {
+			Serial.print("sendPkt:: len=");
+			Serial.println(payLength);
+		}
+		return false;
+	}
+#endif
+	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD));	// 0x0D, 0x0E
+	
+	writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) payLength);				// 0x22
+	payLoad[payLength] = 0x00;
+	writeBuffer(REG_FIFO, (uint8_t *) payLoad, payLength);
+	return true;
+}
+
+// ----------------------------------------------------------------------------
+// loraWait()
+// This function implements the wait protocol needed for downstream transmissions.
+// Note: Timing of downstream and JoinAccept messages is VERY critical.
+//
+// As the ESP8266 watchdog will not like us to wait more than a few hundred
+// milliseconds (or it will kick in) we have to implement a simple way to wait
+// time in case we have to wait seconds before sending messages (e.g. for OTAA 5 or 6 seconds)
+// Without it, the system is known to crash in half of the cases it has to wait for 
+// JOIN-ACCEPT messages to send.
+//
+// This function uses a combination of delay() statements and delayMicroseconds().
+// As we use delay() only when there is still enough time to wait and we use micros()
+// to make sure that delay() did not take too much time this works.
+// 
+// Parameter: uint32-t tmst gives the micros() value when transmission should start. (!!!)
+// Note: We assume LoraDown.sfTx contains the SF we will use for downstream message.
+// ----------------------------------------------------------------------------
+
+void loraWait(const uint32_t timestamp)
+{
+	uint32_t startMics = micros();						// Start of the loraWait function
+	uint32_t tmst = timestamp;
+// XXX
+	int32_t adjust=0;
+	switch (LoraDown.sfTx) {
+		case 7: adjust= 60000; break;					// Make time for SF7 longer 
+		case 8: break;									// Around 60ms
+		case 9: break;
+		case 10: break;
+		case 11: break;
+		case 12: break;
+		default:
+#if DUSB>=1
+		if (( debug>=1 ) && ( pdebug & P_TX )) {
+			Serial.print(F("T loraWait:: unknown SF="));
+			Serial.print(LoraDown.sfTx);
+		}
+#endif
+	}
+	tmst = tmst + txDelay + adjust;						// tmst based on txDelay and spreading factor
+	uint32_t waitTime = tmst - micros();
+	if (waitTime<0) {
+		Serial.println(F("loraWait:: Error wait time < 0"));
+		return;
+	}
+	
+	// For larger delay times we use delay() since that is for > 15ms
+	// This is the most efficient way
+	while (waitTime > 16000) {
+		delay(15);										// ms delay including yield, slightly shorter
+		waitTime= tmst - micros();
+	}
+	// The remaining wait time is less tan 15000 uSecs
+	// And we use delayMicroseconds() to wait
+	if (waitTime>0) delayMicroseconds(waitTime);
+
+#if DUSB>=1
+	else if ((waitTime+20) < 0) {
+		Serial.println(F("loraWait:: TOO LATE"));		// Never happens
+	}
+
+	if (( debug>=2 ) && ( pdebug & P_TX )) { 
+		Serial.print(F("T start: ")); 
+		Serial.print(startMics);
+		Serial.print(F(", tmst: "));					// tmst
+		Serial.print(tmst);
+		Serial.print(F(", end: "));						// This must be micros(), and equal to tmst
+		Serial.print(micros());
+		Serial.print(F(", waited: "));
+		Serial.print(tmst - startMics);
+		Serial.print(F(", delay="));
+		Serial.print(txDelay);
+		Serial.println();
+		if (debug>=2) Serial.flush();
+	}
+#endif
+}
+
+
+// ----------------------------------------------------------------------------
+// txLoraModem
+// Init the transmitter and transmit the buffer
+// After successful transmission (dio0==1) TxDone re-init the receiver
+//
+//	crc is set to 0x00 for TX
+//	iiq is set to 0x27 (or 0x40 based on ipol value in txpkt)
+//
+//	1. opmode Lora (only in Sleep mode)
+//	2. opmode StandBY
+//	3. Configure Modem
+//	4. Configure Channel
+//	5. write PA Ramp
+//	6. config Power
+//	7. RegLoRaSyncWord LORA_MAC_PREAMBLE
+//	8. write REG dio mapping (dio0)
+//	9. write REG IRQ flags
+// 10. write REG IRQ mask
+// 11. write REG LoRa Fifo Base Address
+// 12. write REG LoRa Fifo Addr Ptr
+// 13. write REG LoRa Payload Length
+// 14. Write buffer (byte by byte)
+// 15. Wait until the right time to transmit has arrived
+// 16. opmode TX
+// ----------------------------------------------------------------------------
+
+void txLoraModem(uint8_t *payLoad, uint8_t payLength, uint32_t tmst, uint8_t sfTx,
+						uint8_t powe, uint32_t freq, uint8_t crc, uint8_t iiq)
+{
+#if DUSB>=2
+	if (debug>=1) {
+		// Make sure that all serial stuff is done before continuing
+		Serial.print(F("txLoraModem::"));
+		Serial.print(F("  powe: ")); Serial.print(powe);
+		Serial.print(F(", freq: ")); Serial.print(freq);
+		Serial.print(F(", crc: ")); Serial.print(crc);
+		Serial.print(F(", iiq: 0X")); Serial.print(iiq,HEX);
+		Serial.println();
+		if (debug>=2) Serial.flush();
+	}
+#endif
+	_state = S_TX;
+		
+	// 1. Select LoRa modem from sleep mode
+	//opmode(OPMODE_LORA);									// set register 0x01 to 0x80
+	
+	// Assert the value of the current mode
+	ASSERT((readRegister(REG_OPMODE) & OPMODE_LORA) != 0);
+	
+	// 2. enter standby mode (required for FIFO loading))
+	opmode(OPMODE_STANDBY);									// set 0x01 to 0x01
+	
+	// 3. Init spreading factor and other Modem setting
+	setRate(sfTx, crc);
+	
+	// Frquency hopping
+	//writeRegister(REG_HOP_PERIOD, (uint8_t) 0x00);		// set 0x24 to 0x00 only for receivers
+	
+	// 4. Init Frequency, config channel
+	setFreq(freq);
+
+	// 6. Set power level, REG_PAC
+	setPow(powe);
+	
+	// 7. prevent node to node communication
+	writeRegister(REG_INVERTIQ, (uint8_t) iiq);						// 0x33, (0x27 or 0x40)
+	
+	// 8. set the IRQ mapping DIO0=TxDone DIO1=NOP DIO2=NOP (or lesss for 1ch gateway)
+    writeRegister(REG_DIO_MAPPING_1, (uint8_t)(
+		MAP_DIO0_LORA_TXDONE | 
+		MAP_DIO1_LORA_NOP | 
+		MAP_DIO2_LORA_NOP |
+		MAP_DIO3_LORA_CRC));
+	
+	// 9. clear all radio IRQ flags
+    writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
+	
+	// 10. mask all IRQs but TxDone
+    writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~IRQ_LORA_TXDONE_MASK);
+	
+	// txLora
+	opmode(OPMODE_FSTX);									// set 0x01 to 0x02 (actual value becomes 0x82)
+	
+	// 11, 12, 13, 14. write the buffer to the FiFo
+	sendPkt(payLoad, payLength);
+
+	// 15. wait extra delay out. The delayMicroseconds timer is accurate until 16383 uSec.
+	loraWait(tmst);
+	
+	//Set the base addres of the transmit buffer in FIFO
+	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_TX_BASE_AD));	// set 0x0D to 0x0F (contains 0x80);	
+	
+	//For TX we have to set the PAYLOAD_LENGTH
+	writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) payLength);		// set 0x22, max 0x40==64Byte long
+	
+	//For TX we have to set the MAX_PAYLOAD_LENGTH
+	writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH);	// set 0x22, max 0x40==64Byte long
+	
+	// Reset the IRQ register
+	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);			// Clear the mask
+	writeRegister(REG_IRQ_FLAGS, (uint8_t) IRQ_LORA_TXDONE_MASK);// set 0x12 to 0x08, clear TXDONE
+	
+	// 16. Initiate actual transmission of FiFo
+	opmode(OPMODE_TX);											// set 0x01 to 0x03 (actual value becomes 0x83)
+	
+}// txLoraModem
+
+
+// ----------------------------------------------------------------------------
+// Setup the LoRa receiver on the connected transceiver.
+// - Determine the correct transceiver type (sx1272/RFM92 or sx1276/RFM95)
+// - Set the frequency to listen to (1-channel remember)
+// - Set Spreading Factor (standard SF7)
+// The reset RST pin might not be necessary for at least the RGM95 transceiver
+//
+// 1. Put the radio in LoRa mode
+// 2. Put modem in sleep or in standby
+// 3. Set Frequency
+// 4. Spreading Factor
+// 5. Set interrupt mask
+// 6. Clear all interrupt flags
+// 7. Set opmode to OPMODE_RX
+// 8. Set _state to S_RX
+// 9. Reset all interrupts
+// ----------------------------------------------------------------------------
+
+void rxLoraModem()
+{
+	// 1. Put system in LoRa mode
+	//opmode(OPMODE_LORA);										// Is already so
+	
+	// 2. Put the radio in sleep mode
+	opmode(OPMODE_STANDBY);									// CAD set 0x01 to 0x00
+	
+	// 3. Set frequency based on value in freq
+	setFreq(freqs[ifreq]);										// set to 868.1MHz
+
+	// 4. Set spreading Factor and CRC
+    setRate(sf, 0x04);
+	
+	// prevent node to node communication
+	writeRegister(REG_INVERTIQ, (uint8_t) 0x27);				// 0x33, 0x27; to reset from TX
+	
+	// Max Payload length is dependent on 256 byte buffer. 
+	// At startup TX starts at 0x80 and RX at 0x00. RX therefore maximized at 128 Bytes
+	//For TX we have to set the PAYLOAD_LENGTH
+    //writeRegister(REG_PAYLOAD_LENGTH, (uint8_t) PAYLOAD_LENGTH);	// set 0x22, 0x40==64Byte long
+
+	// Set CRC Protection or MAX payload protection
+	//writeRegister(REG_MAX_PAYLOAD_LENGTH, (uint8_t) MAX_PAYLOAD_LENGTH);	// set 0x23 to 0x80==128
+	
+	//Set the start address for the FiFO (Which should be 0)
+	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD));	// set 0x0D to 0x0F (contains 0x00);
+	
+	// Low Noise Amplifier used in receiver
+	writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  						// 0x0C, 0x23
+	
+	// Accept no interrupts except RXDONE, RXTOUT en RXCRC
+	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
+		IRQ_LORA_RXDONE_MASK | 
+		IRQ_LORA_RXTOUT_MASK | 
+		IRQ_LORA_HEADER_MASK | 
+		IRQ_LORA_CRCERR_MASK));
+
+	// set frequency hopping
+	if (_hop) {
+		//writeRegister(REG_HOP_PERIOD, 0x01);					// 0x24, 0x01 was 0xFF
+		writeRegister(REG_HOP_PERIOD,0x00);						// 0x24, 0x00 was 0xFF
+	}
+	else {
+		writeRegister(REG_HOP_PERIOD,0x00);						// 0x24, 0x00 was 0xFF
+	}
+	// Set RXDONE interrupt to dio0
+	writeRegister(REG_DIO_MAPPING_1, (uint8_t)(
+			MAP_DIO0_LORA_RXDONE | 
+			MAP_DIO1_LORA_RXTOUT |
+			MAP_DIO2_LORA_NOP |			
+			MAP_DIO3_LORA_CRC));
+
+	// Set the opmode to either single or continuous receive. The first is used when
+	// every message can come on a different SF, the second when we have fixed SF
+	if (_cad) {
+		// cad Scanner setup, set _state to S_RX
+		// Set Single Receive Mode, goes in STANDBY mode after receipt
+		_state= S_RX;
+		opmode(OPMODE_RX_SINGLE);								// 0x80 | 0x06 (listen one message)
+	}
+	else {
+		// Set Continous Receive Mode, usefull if we stay on one SF
+		_state= S_RX;
+		if (_hop) Serial.println(F("rxLoraModem:: ERROR continuous receive in hop mode"));
+		opmode(OPMODE_RX);										// 0x80 | 0x05 (listen)
+	}
+	
+	// 9. clear all radio IRQ flags
+    writeRegister(REG_IRQ_FLAGS, 0xFF);
+	
+	return;
+}// rxLoraModem
+
+
+// ----------------------------------------------------------------------------
+// function cadScanner()
+//
+// CAD Scanner will scan on the given channel for a valid Symbol/Preamble signal.
+// So instead of receiving continuous on a given channel/sf combination
+// we will wait on the given channel and scan for a preamble. Once received
+// we will set the radio to the SF with best rssi (indicating reception on that sf).
+// The function sets the _state to S_SCAN
+// NOTE: DO not set the frequency here but use the frequency hopper
+// ----------------------------------------------------------------------------
+void cadScanner()
+{
+	// 1. Put system in LoRa mode (which destroys all other nodes(
+	//opmode(OPMODE_LORA);
+	
+	// 2. Put the radio in sleep mode
+	opmode(OPMODE_STANDBY);										// Was old value
+	
+	// 3. Set frequency based on value in ifreq					// XXX New, might be needed when receiving down
+	setFreq(freqs[ifreq]);
+
+	// For every time we start the scanner, we set the SF to the begin value
+	//sf = SF7;													// XXX 180501 Not by default
+	
+	// 4. Set spreading Factor and CRC
+	setRate(sf, 0x04);
+	
+	// listen to LORA_MAC_PREAMBLE
+	writeRegister(REG_SYNC_WORD, (uint8_t) 0x34);				// set reg 0x39 to 0x34
+	
+	// Set the interrupts we want to listen to
+	writeRegister(REG_DIO_MAPPING_1, (uint8_t)(
+		MAP_DIO0_LORA_CADDONE | 
+		MAP_DIO1_LORA_CADDETECT | 
+		MAP_DIO2_LORA_NOP | 
+		MAP_DIO3_LORA_CRC ));
+	
+	// Set the mask for interrupts (we do not want to listen to) except for
+	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
+		IRQ_LORA_CDDONE_MASK | 
+		IRQ_LORA_CDDETD_MASK | 
+		IRQ_LORA_CRCERR_MASK | 
+		IRQ_LORA_HEADER_MASK));
+	
+	// Set the opMode to CAD
+	opmode(OPMODE_CAD);
+
+	// Clear all relevant interrupts
+	//writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF );						// May work better, clear ALL interrupts
+	
+	// If we are here. we either might have set the SF or we have a timeout in which
+	// case the receive is started just as normal.
+	return;
+	
+}// cadScanner
+
+
+// ----------------------------------------------------------------------------
+// First time initialisation of the LoRa modem
+// Subsequent changes to the modem state etc. done by txLoraModem or rxLoraModem
+// After initialisation the modem is put in rx mode (listen)
+// ----------------------------------------------------------------------------
+void initLoraModem()
+{
+	_state = S_INIT;
+#if ESP32_ARCH==1
+	digitalWrite(pins.rst, LOW);
+	delayMicroseconds(10000);
+    digitalWrite(pins.rst, HIGH);
+	delayMicroseconds(10000);
+	digitalWrite(pins.ss, HIGH);
+#if DUSB>=1
+
+#endif
+
+#else
+	// Reset the transceiver chip with a pulse of 10 mSec
+	digitalWrite(pins.rst, HIGH);
+	delayMicroseconds(10000);
+    digitalWrite(pins.rst, LOW);
+	delayMicroseconds(10000);
+#endif
+	// 2. Set radio to sleep
+	opmode(OPMODE_SLEEP);										// set register 0x01 to 0x00
+
+	// 1 Set LoRa Mode
+	opmode(OPMODE_LORA);										// set register 0x01 to 0x80
+	
+	// 3. Set frequency based on value in freq
+	//ifreq = 0; 												// XXX 180326
+	freq=freqs[ifreq];
+	setFreq(freq);												// set to 868.1MHz or the last saved frequency
+	
+	// 4. Set spreading Factor
+    setRate(sf, 0x04);
+	
+	// Low Noise Amplifier used in receiver
+    writeRegister(REG_LNA, (uint8_t) LNA_MAX_GAIN);  			// 0x0C, 0x23
+#if _PIN_OUT==4
+	delay(1);
+#endif
+    uint8_t version = readRegister(REG_VERSION);				// Read the LoRa chip version id
+    if (version == 0x22) {
+        // sx1272
+#if DUSB>=2
+        Serial.println(F("WARNING:: SX1272 detected"));
+#endif
+        sx1272 = true;
+    } 
+	
+	else if (version == 0x12) {
+        // sx1276?
+#if DUSB>=2
+            if (debug >=1) 
+				Serial.println(F("SX1276 starting"));
+#endif
+            sx1272 = false;
+	}
+	else {
+		// Normally this means that we connected the wrong type of board and
+		// therefore specified the wrong type of wiring/pins to the software
+		// Maybe this issue can be resolved of we try one of the other defined 
+		// boards. (Comresult or Hallard or ...)
+#if DUSB>=1
+		Serial.print(F("Unknown transceiver="));
+		Serial.print(version,HEX);
+		Serial.print(F(", pins.rst =")); Serial.print(pins.rst);
+		Serial.print(F(", pins.ss  =")); Serial.print(pins.ss);
+		Serial.print(F(", pins.dio0 =")); Serial.print(pins.dio0);
+		Serial.print(F(", pins.dio1 =")); Serial.print(pins.dio1);
+		Serial.print(F(", pins.dio2 =")); Serial.print(pins.dio2);
+		Serial.println();
+		Serial.flush();
+#endif
+		die("");												// Maybe first try another kind of receiver
+    }
+	// If we are here, the chip is recognized successfully
+	
+	// 7. set sync word
+	writeRegister(REG_SYNC_WORD, (uint8_t) 0x34);				// set 0x39 to 0x34 LORA_MAC_PREAMBLE
+	
+	// prevent node to node communication
+	writeRegister(REG_INVERTIQ,0x27);							// 0x33, 0x27; to reset from TX
+	
+	// Max Payload length is dependent on 256 byte buffer. At startup TX starts at
+	// 0x80 and RX at 0x00. RX therefore maximized at 128 Bytes
+	writeRegister(REG_MAX_PAYLOAD_LENGTH,MAX_PAYLOAD_LENGTH);	// set 0x23 to 0x80==128 bytes
+	writeRegister(REG_PAYLOAD_LENGTH,PAYLOAD_LENGTH);			// 0x22, 0x40==64Byte long
+	
+	writeRegister(REG_FIFO_ADDR_PTR, (uint8_t) readRegister(REG_FIFO_RX_BASE_AD));	// set reg 0x0D to 0x0F
+	writeRegister(REG_HOP_PERIOD,0x00);							// reg 0x24, set to 0x00
+
+	// 5. Config PA Ramp up time								// set reg 0x0A  
+	writeRegister(REG_PARAMP, (readRegister(REG_PARAMP) & 0xF0) | 0x08); // set PA ramp-up time 50 uSec
+	
+	// Set 0x4D PADAC for SX1276 ; XXX register is 0x5a for sx1272
+	writeRegister(REG_PADAC_SX1276,  0x84); 					// set 0x4D (PADAC) to 0x84
+	//writeRegister(REG_PADAC, readRegister(REG_PADAC) | 0x4);
+	
+	// Reset interrupt Mask, enable all interrupts
+	writeRegister(REG_IRQ_FLAGS_MASK, 0x00);
+	
+	// 9. clear all radio IRQ flags
+    writeRegister(REG_IRQ_FLAGS, 0xFF);
+}// initLoraModem
+
+
+// ----------------------------------------------------------------------------
+// Void function startReceiver.
+// This function starts the receiver loop of the LoRa service.
+// It starts the LoRa modem with initLoraModem(), and then starts
+// the receiver either in single message (CAD) of in continuous
+// reception (STD).
+// ----------------------------------------------------------------------------
+void startReceiver() {
+	initLoraModem();								// XXX 180326, after adapting this function 
+	if (_cad) {
+#if DUSB>=1
+		if (( debug>=1 ) && ( pdebug & P_SCAN )) {
+			Serial.println(F("S PULL:: _state set to S_SCAN"));
+			if (debug>=2) Serial.flush();
+		}
+#endif
+		_state = S_SCAN;
+		sf = SF7;
+		cadScanner();
+	}
+	else {
+		_state = S_RX;
+		rxLoraModem();
+	}
+	writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
+	writeRegister(REG_IRQ_FLAGS, 0xFF);				// Reset all interrupt flags
+}
+
+
+// ----------------------------------------------------------------------------
+// Interrupt_0 Handler.
+// Both interrupts DIO0 and DIO1 are mapped on GPIO15. Se we have to look at 
+// the interrupt flags to see which interrupt(s) are called.
+//
+// NOTE:: This method may work not as good as just using more GPIO pins on 
+//  the ESP8266 mcu. But in practice it works good enough
+// ----------------------------------------------------------------------------
+void ICACHE_RAM_ATTR Interrupt_0()
+{
+	_event=1;
+}
+
+
+// ----------------------------------------------------------------------------
+// Interrupt handler for DIO1 having High Value
+// As DIO0 and DIO1 may be multiplexed on one GPIO interrupt handler
+// (as we do) we have to be careful only to call the right Interrupt_x
+// handler and clear the corresponding interrupts for that dio.
+// NOTE: Make sure all Serial communication is only for debug level 3 and up.
+// Handler for:
+//		- CDDETD
+//		- RXTIMEOUT
+//		- (RXDONE error only)
+// ----------------------------------------------------------------------------
+void ICACHE_RAM_ATTR Interrupt_1()
+{
+	_event=1;
+}
+
+// ----------------------------------------------------------------------------
+// Frequency Hopping Channel (FHSS) dio2
+// ----------------------------------------------------------------------------
+void ICACHE_RAM_ATTR Interrupt_2() 
+{
+	_event=1;
+}
+
+
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_oLED.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_oLED.ino
new file mode 100644
index 0000000000000000000000000000000000000000..6e399b9fc3ab78d0f282f2089ede417192dd4659
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_oLED.ino
@@ -0,0 +1,99 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains the state machine code enabling to receive
+// and transmit packages/messages.
+// ========================================================================================
+//
+
+#if OLED>=1
+
+
+// ----------------------------------------------------------------	
+// Initilize the OLED functions.
+//
+// ----------------------------------------------------------------
+void init_oLED() 
+{
+#if defined OLED_RST
+	pinMode(OLED_RST,OUTPUT);
+	digitalWrite(OLED_RST, LOW); 	// low to reset OLED
+	delay(50); 
+	digitalWrite(OLED_RST, HIGH); 	// must be high to turn on OLED
+	delay(50);
+#else
+#endif
+	// Initialising the UI will init the display too.
+	display.init();
+	display.flipScreenVertically();
+	display.setFont(ArialMT_Plain_24);
+	display.setTextAlignment(TEXT_ALIGN_LEFT);
+	display.drawString(0, 24, "STARTING");
+	display.display();
+}
+
+// ----------------------------------------------------------------
+// Activate the OLED
+//
+// ----------------------------------------------------------------
+void acti_oLED() 
+{
+	// Initialising the UI will init the display too.
+	display.clear();
+	
+#if OLED==1
+	display.setFont(ArialMT_Plain_16);
+	display.drawString(0, 16, "READY,  SSID=");
+	display.drawString(0, 32, WiFi.SSID());
+#elif OLED==2
+	display.setFont(ArialMT_Plain_16);
+	display.drawString(0, 16, "READY,  SSID=");
+	display.drawString(0, 32, WiFi.SSID());
+#endif
+
+	display.display();
+}
+
+// ----------------------------------------------------------------
+// Print a message on the OLED.
+// Note: The whole message must fit in the buffer
+//
+// ----------------------------------------------------------------
+void msg_oLED(String tim, String sf) {
+    display.clear();
+    display.setFont(ArialMT_Plain_16);
+    display.setTextAlignment(TEXT_ALIGN_LEFT);
+	
+	display.drawString(0, 48, "LEN: " );
+//    display.drawString(40, 48, String((int)messageLength) );
+    display.display();
+	yield();
+}
+
+// ----------------------------------------------------------------
+// Print the OLED address in use
+//
+// ----------------------------------------------------------------
+void addr_oLED() 
+{
+	Serial.print(F("OLED_ADDR=0x"));
+	Serial.println(OLED_ADDR, HEX);
+}
+
+
+
+#endif
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_otaServer.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_otaServer.ino
new file mode 100644
index 0000000000000000000000000000000000000000..dd4adb7d03302fe4f29214f38de0797e167a59e1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_otaServer.ino
@@ -0,0 +1,121 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains the ota code for the ESP Single Channel Gateway.
+
+// Provide OTA server funcionality so the 1ch gateway can be updated 
+// over the air.
+// This code uses the ESPhttpServer functions to update the gateway.
+
+#if A_OTA==1
+
+//extern ArduinoOTAClass ArduinoOTA;
+
+// Make sure that webserver is running before continuing
+
+// ----------------------------------------------------------------------------
+// setupOta
+// Function to run in the setup() function to initialise the update function
+// ----------------------------------------------------------------------------
+void setupOta(char *hostname) {
+
+	ArduinoOTA.begin();
+#if DUSB>=1
+	Serial.println(F("setupOta:: Started"));
+#endif	
+	// Hostname defaults to esp8266-[ChipID]
+	ArduinoOTA.setHostname(hostname);
+	
+	ArduinoOTA.onStart([]() {
+		String type;
+		// XXX version mismatch of platform.io and ArduinoOtaa
+		// see https://github.com/esp8266/Arduino/issues/3020
+		//if (ArduinoOTA.getCommand() == U_FLASH)
+			type = "sketch";
+		//else // U_SPIFFS
+		//	type = "filesystem";
+
+		// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
+		Serial.println("Start updating " + type);
+	});
+	
+	ArduinoOTA.onEnd([]() {
+		Serial.println("\nEnd");
+	});
+	
+	ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
+		Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
+	});
+	
+	ArduinoOTA.onError([](ota_error_t error) {
+		Serial.printf("Error[%u]: ", error);
+		if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
+		else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
+		else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
+		else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
+		else if (error == OTA_END_ERROR) Serial.println("End Failed");
+	});
+	
+#if DUSB>=1
+	Serial.println("Ready");
+	Serial.print("IP address: ");
+	Serial.println(WiFi.localIP());
+#endif
+	
+	// Only if the Webserver is active also
+#if A_SERVER==2										// Displayed for the moment
+	ESPhttpUpdate.rebootOnUpdate(false);
+   
+	server.on("/esp", HTTP_POST, [&](){
+   
+      HTTPUpdateResult ret = ESPhttpUpdate.update(server.arg("firmware"), "1.0.0");
+	  
+      switch(ret) {
+        case HTTP_UPDATE_FAILED:
+            //PREi::sendJSON(500, "Update failed.");
+			Serial.println(F("Update failed"));
+            break;
+        case HTTP_UPDATE_NO_UPDATES:
+            //PREi::sendJSON(304, "Update not necessary.");
+			Serial.println(F("Update not necessary"));
+            break;
+        case HTTP_UPDATE_OK:
+            //PREi::sendJSON(200, "Update started.");
+			Serial.println(F("Update started"));
+            ESP.restart();
+            break;
+		default:
+			Serial.println(F("setupOta:: Unknown ret="));
+      }
+	});
+#endif
+}
+
+
+// ----------------------------------------------------------------------------
+//
+//
+// ----------------------------------------------------------------------------
+void updateOtaa() {
+
+	String response="";
+	printIP((IPAddress)WiFi.localIP(),'.',response);
+	
+	ESPhttpUpdate.update(response, 80, "/arduino.bin");
+
+}
+
+
+#endif
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_repeater.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_repeater.ino
new file mode 100644
index 0000000000000000000000000000000000000000..ed96bfca644075a57e740d94f49e074eabd75532
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_repeater.ino
@@ -0,0 +1,46 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg
+// Verison 5.3.3
+// Date: 2018-08-25
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains code for using the single channel gateway also as a sensor node. 
+// Please specify the DevAddr and the AppSKey below (and on your LoRa backend).
+// Also you will have to choose what sensors to forward to your application.
+//
+// ============================================================================
+		
+#if REPEATER==1
+
+#define _ICHAN 0
+#define _OCHAN 1
+
+#ifdef _TTNSERVER
+#error "Please undefined _THINGSERVER, for REAPETR shutdown WiFi"
+#endif
+
+// Send a LoRa message out from the gateway transmitter
+// XXX Maybe we should block the received ontul the message is transmitter
+
+int sendLora(char *msg, int len) {
+	// Check whete len is not exceeding maximum length
+	Serial.print("sendLora:: ");
+	
+	for (int i=0; i< len; i++) {
+		Serial.print(msg[1],HEX);
+		Serial.print('.');
+	}
+	
+	if (debug>=2) Serial.flush();
+	return(1);
+}
+
+#endif //REPEATER==1
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_sensor.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_sensor.ino
new file mode 100644
index 0000000000000000000000000000000000000000..496d695196f958a658ec3a0cf909fd7069bdbbc9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_sensor.ino
@@ -0,0 +1,671 @@
+// sensor.ino; 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg
+// Verison 5.3.3
+// Date: 2018-08-25
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains code for using the single channel gateway also as a sensor node. 
+// Please specify the DevAddr and the AppSKey below (and on your LoRa backend).
+// Also you will have to choose what sensors to forward to your application.
+//
+// Note: disable sensors not used in ESP-sc-gway.h
+//	- The GPS is included on TTGO T-Beam ESP32 boards by default.
+//	- The battery sensor works by connecting the VCC pin to A0 analog port
+// ============================================================================
+	
+#if GATEWAYNODE==1
+
+#include "LoRaCode.h"
+
+unsigned char DevAddr[4]  = _DEVADDR ;				// see ESP-sc-gway.h
+
+
+// Only used by GPS sensor code
+#if _GPS==1
+// ----------------------------------------------------------------------------
+// Smartdelay is a function to delay processing but in the loop get info 
+// from the GPS device
+// ----------------------------------------------------------------------------
+static void smartDelay(unsigned long ms)                
+{
+  unsigned long start = millis();
+  do
+  {
+    while (Serial1.available())
+      gps.encode(Serial1.read());
+  } while (millis() - start < ms);
+}
+#endif //_GPS
+
+
+
+
+
+// ----------------------------------------------------------------------------
+// LoRaSensors() is a function that puts sensor values in the MACPayload and 
+// sends these values up to the server. For the server it is impossible to know 
+// whther or not the message comes from a LoRa node or from the gateway.
+//
+// The example code below adds a battery value in lCode (encoding protocol) but
+// of-course you can add any byte string you wish
+//
+// Parameters: 
+//	- buf: contains the buffer to put the sensor values in (max==xx);
+// Returns:
+//	- The amount of sensor characters put in the buffer
+//
+// NOTE: The code in LoRaSensors() is provided as an example only.
+//	The amount of sensor values as well as their message layout may differ
+//	for each implementation.
+//	Also, the message format used by this gateway is LoraCode, a message format
+//	developed by me for sensor values. Each value is uniquely coded with an
+//	id and a value, and the total message contains its length (less than 64 bytes)
+//	and a parity value in byte[0] bit 7.
+// ----------------------------------------------------------------------------
+static int LoRaSensors(uint8_t *buf) {
+	
+	uint8_t tchars = 1;
+	buf[0] = 0x86;									// 134; User code <lCode + len==3 + Parity
+
+#if DUSB>=1
+	if (debug>=0)
+		Serial.print(F("LoRaSensors:: "));
+#endif
+
+#if _BATTERY==1
+#if DUSB>=1
+	if (debug>=0)
+		Serial.print(F("Battery "));
+#endif
+#if defined(ARDUINO_ARCH_ESP8266) || defined(ESP32)
+	// For ESP there is no standard battery library
+	// What we do is to measure GPIO35 pin which has a 100K voltage divider
+	pinMode(35, INPUT);
+#if defined(ESP32)
+	int devider=4095;
+#else
+	int devider=1023;
+#endif //ESP32
+	float volts=3.3 * analogRead(35) / 4095 * 2;	// T_Beam connects to GPIO35
+#else
+	// For ESP8266 no sensor defined
+	float volts=0;
+#endif
+	tchars += lcode.eBattery(volts, buf + tchars);
+#endif
+
+#if _GPS==1
+#if DUSB>=1
+	if (debug>=0)
+		Serial.print(F("M GPS "));
+
+	if (( debug>=1 ) && ( pdebug & P_MAIN )) {
+		Serial.print("\tLatitude  : ");
+		Serial.println(gps.location.lat(), 5);
+		Serial.print("\tLongitude : ");
+		Serial.println(gps.location.lng(), 4);
+		Serial.print("\tSatellites: ");
+		Serial.println(gps.satellites.value());
+		Serial.print("\tAltitude  : ");
+		Serial.print(gps.altitude.feet() / 3.2808);
+		Serial.println("M");
+		Serial.print("\tTime      : ");
+		Serial.print(gps.time.hour());
+		Serial.print(":");
+		Serial.print(gps.time.minute());
+		Serial.print(":");
+		Serial.println(gps.time.second());
+	}
+#endif
+
+	smartDelay(1000);
+	
+	if (millis() > 5000 && gps.charsProcessed() < 10) {
+#if DUSB>=1
+		Serial.println(F("No GPS data received: check wiring"));
+#endif
+		return(0);
+	}
+	
+	// Assuming we have a value, put it in the buf
+	// The layout of this message is specific to the user,
+	// so adapt as needed.
+	tchars += lcode.eGpsL(gps.location.lat(), gps.location.lng(), gps.altitude.value(),
+                       gps.satellites.value(), buf + tchars);
+
+#endif
+
+#if DUSB>=1
+	if (debug>=0)
+		Serial.println();
+#endif
+
+	// If all sensor data is encoded, we encode the buffer	
+	lcode.eMsg(buf, tchars);								// Fill byte 0 with bytecount and Parity
+	
+	return(tchars);	// return the number of bytes added to payload
+}
+
+
+// ----------------------------------------------------------------------------
+// XOR()
+// perform x-or function for buffer and key
+// Since we do this ONLY for keys and X, Y we know that we need to XOR 16 bytes.
+//
+// ----------------------------------------------------------------------------
+static void mXor(uint8_t *buf, uint8_t *key) {
+	for (uint8_t i = 0; i < 16; ++i) buf[i] ^= key[i];
+}
+
+
+// ----------------------------------------------------------------------------
+// SHIFT-LEFT
+// Shift the buffer buf left one bit
+// Parameters:
+//	- buf: An array of uint8_t bytes
+//	- len: Length of the array in bytes
+// ----------------------------------------------------------------------------
+static void shift_left(uint8_t * buf, uint8_t len) {
+    while (len--) {
+        uint8_t next = len ? buf[1] : 0;			// len 0 to 15
+
+        uint8_t val = (*buf << 1);
+        if (next & 0x80) val |= 0x01;
+        *buf++ = val;
+    }
+}
+
+
+// ----------------------------------------------------------------------------
+// generate_subkey
+// RFC 4493, para 2.3
+// ----------------------------------------------------------------------------
+static void generate_subkey(uint8_t *key, uint8_t *k1, uint8_t *k2) {
+
+	memset(k1, 0, 16);								// Fill subkey1 with 0x00
+	
+	// Step 1: Assume k1 is an all zero block
+	AES_Encrypt(k1,key);
+	
+	// Step 2: Analyse outcome of Encrypt operation (in k1), generate k1
+	if (k1[0] & 0x80) {
+		shift_left(k1,16);
+		k1[15] ^= 0x87;
+	}
+	else {
+		shift_left(k1,16);
+	}
+	
+	// Step 3: Generate k2
+	for (uint8_t i=0; i<16; i++) k2[i]=k1[i];
+	if (k1[0] & 0x80) {								// use k1(==k2) according rfc 
+		shift_left(k2,16);
+		k2[15] ^= 0x87;
+	}
+	else {
+		shift_left(k2,16);
+	}
+	
+	// step 4: Done, return k1 and k2
+	return;
+}
+
+
+// ----------------------------------------------------------------------------
+// MICPACKET()
+// Provide a valid MIC 4-byte code (par 2.4 of spec, RFC4493)
+// 		see also https://tools.ietf.org/html/rfc4493
+//
+// Although our own handler may choose not to interpret the last 4 (MIC) bytes
+// of a PHYSPAYLOAD physical payload message of in internal sensor,
+// The official TTN (and other) backends will intrpret the complete message and
+// conclude that the generated message is bogus.
+// So we sill really simulate internal messages coming from the -1ch gateway
+// to come from a real sensor and append 4 MIC bytes to every message that are 
+// perfectly legimate
+// Parameters:
+//	- data:			uint8_t array of bytes = ( MHDR | FHDR | FPort | FRMPayload )
+//	- len:			8=bit length of data, normally less than 64 bytes
+//	- FrameCount:	16-bit framecounter
+//	- dir:			0=up, 1=down
+//
+// B0 = ( 0x49 | 4 x 0x00 | Dir | 4 x DevAddr | 4 x FCnt |  0x00 | len )
+// MIC is cmac [0:3] of ( aes128_cmac(NwkSKey, B0 | Data )
+//
+// ----------------------------------------------------------------------------
+uint8_t micPacket(uint8_t *data, uint8_t len, uint16_t FrameCount, uint8_t * NwkSKey, uint8_t dir) {
+
+
+	//uint8_t NwkSKey[16] = _NWKSKEY;
+	uint8_t Block_B[16];
+	uint8_t X[16];
+	uint8_t Y[16];
+	
+	// ------------------------------------
+	// build the B block used by the MIC process
+	Block_B[0]= 0x49;						// 1 byte MIC code
+			
+	Block_B[1]= 0x00;						// 4 byte 0x00
+	Block_B[2]= 0x00;
+	Block_B[3]= 0x00;
+	Block_B[4]= 0x00;
+	
+	Block_B[5]= dir;						// 1 byte Direction
+	
+	Block_B[6]= DevAddr[3];					// 4 byte DevAddr
+	Block_B[7]= DevAddr[2];
+	Block_B[8]= DevAddr[1];
+	Block_B[9]= DevAddr[0];
+	
+	Block_B[10]= (FrameCount & 0x00FF);		// 4 byte FCNT
+	Block_B[11]= ((FrameCount >> 8) & 0x00FF);
+	Block_B[12]= 0x00; 						// Frame counter upper Bytes
+	Block_B[13]= 0x00;						// These are not used so are 0
+	
+	Block_B[14]= 0x00;						// 1 byte 0x00
+	
+	Block_B[15]= len;						// 1 byte len
+	
+	// ------------------------------------
+	// Step 1: Generate the subkeys
+	//
+	uint8_t k1[16];
+	uint8_t k2[16];
+	generate_subkey(NwkSKey, k1, k2);
+	
+	// ------------------------------------
+	// Copy the data to a new buffer which is prepended with Block B0
+	//
+	uint8_t micBuf[len+16];					// B0 | data
+	for (uint8_t i=0; i<16; i++) micBuf[i]=Block_B[i];
+	for (uint8_t i=0; i<len; i++) micBuf[i+16]=data[i];
+	
+	// ------------------------------------
+	// Step 2: Calculate the number of blocks for CMAC
+	//
+	uint8_t numBlocks = len/16 + 1;			// Compensate for B0 block
+	if ((len % 16)!=0) numBlocks++;			// If we have only a part block, take it all
+	
+	// ------------------------------------
+	// Step 3: Calculate padding is necessary
+	//
+	uint8_t restBits = len%16;				// if numBlocks is not a multiple of 16 bytes
+	
+	
+	// ------------------------------------
+	// Step 5: Make a buffer of zeros
+	//
+	memset(X, 0, 16);
+	
+	// ------------------------------------
+	// Step 6: Do the actual encoding according to RFC
+	//
+	for(uint8_t i= 0x0; i < (numBlocks - 1); i++) {
+		for (uint8_t j=0; j<16; j++) Y[j] = micBuf[(i*16)+j];
+		mXor(Y, X);
+		AES_Encrypt(Y, NwkSKey);
+		for (uint8_t j=0; j<16; j++) X[j] = Y[j];
+	}
+	
+
+	// ------------------------------------
+	// Step 4: If there is a rest Block, padd it
+	// Last block. We move step 4 to the end as we need Y
+	// to compute the last block
+	// 
+	if (restBits) {
+		for (uint8_t i=0; i<16; i++) {
+			if (i< restBits) Y[i] = micBuf[((numBlocks-1)*16)+i];
+			if (i==restBits) Y[i] = 0x80;
+			if (i> restBits) Y[i] = 0x00;
+		}
+		mXor(Y, k2);
+	}
+	else {
+		for (uint8_t i=0; i<16; i++) {
+			Y[i] = micBuf[((numBlocks-1)*16)+i];
+		}
+		mXor(Y, k1);
+	}
+	mXor(Y, X);
+	AES_Encrypt(Y,NwkSKey);
+	
+	// ------------------------------------
+	// Step 7: done, return the MIC size. 
+	// Only 4 bytes are returned (32 bits), which is less than the RFC recommends.
+	// We return by appending 4 bytes to data, so there must be space in data array.
+	//
+	data[len+0]=Y[0];
+	data[len+1]=Y[1];
+	data[len+2]=Y[2];
+	data[len+3]=Y[3];
+	return 4;
+}
+
+
+#if _CHECK_MIC==1
+// ----------------------------------------------------------------------------
+// CHECKMIC
+// Function to check the MIC computed for existing messages and for new messages
+// Parameters:
+//	- buf: LoRa buffer to check in bytes, last 4 bytes contain the MIC
+//	- len: Length of buffer in bytes
+//	- key: Key to use for MIC. Normally this is the NwkSKey
+//
+// ----------------------------------------------------------------------------
+static void checkMic(uint8_t *buf, uint8_t len, uint8_t *key) {
+	uint8_t cBuf[len+1];
+	uint8_t NwkSKey[16] = _NWKSKEY;
+	
+	if (debug>=2) {
+		Serial.print(F("old="));
+		for (uint8_t i=0; i<len; i++) { 
+			printHexDigit(buf[i]); 
+			Serial.print(' '); 
+		}
+		Serial.println();
+	}	
+	for (uint8_t i=0; i<len-4; i++) cBuf[i] = buf[i];
+	len -=4;
+	
+	uint16_t FrameCount = ( cBuf[7] * 256 ) + cBuf[6];
+	len += micPacket(cBuf, len, FrameCount, NwkSKey, 0);
+	
+	if (debug>=2) {
+		Serial.print(F("new="));
+		for (uint8_t i=0; i<len; i++) { 
+			printHexDigit(cBuf[i]); 
+			Serial.print(' '); 
+		}
+		Serial.println();
+	}
+	// Mic is only checked, but len is not corrected
+}
+#endif //_CHECK_MIC
+
+// ----------------------------------------------------------------------------
+// SENSORPACKET
+// The gateway may also have local sensors that need reporting.
+// We will generate a message in gateway-UDP format for upStream messaging
+// so that for the backend server it seems like a LoRa node has reported a
+// sensor value.
+//
+// NOTE: We do not need ANY LoRa functions here since we are on the gateway.
+// We only need to send a gateway message upstream that looks like a node message.
+//
+// NOTE:: This function does encrypt the sensorpayload, and the backend
+//		picks it up fine as decoder thinks it is a MAC message.
+//
+// Par 4.0 LoraWan spec:
+//	PHYPayload =	( MHDR | MACPAYLOAD | MIC ) 
+// which is equal to
+//					( MHDR | ( FHDR | FPORT | FRMPAYLOAD ) | MIC )
+//
+//	This function makes the totalpackage and calculates MIC
+// The maximum size of the message is: 12 + ( 9 + 2 + 64 ) + 4	
+// So message size should be lass than 128 bytes if Payload is limited to 64 bytes.
+//
+// return value:
+//	- On success returns the number of bytes to send
+//	- On error returns -1
+// ----------------------------------------------------------------------------
+int sensorPacket() {
+
+	uint8_t buff_up[512];								// Declare buffer here to avoid exceptions
+	uint8_t message[64]={ 0 };							// Payload, init to 0
+	uint8_t mlength = 0;
+	uint32_t tmst = micros();
+	struct LoraUp LUP;
+	uint8_t NwkSKey[16] = _NWKSKEY;
+	uint8_t AppSKey[16] = _APPSKEY;
+	uint8_t DevAddr[4]  = _DEVADDR;
+	
+	// Init the other LoraUp fields
+	LUP.sf = 8;											// Send with SF8
+	LUP.prssi = -50;
+	LUP.rssicorr = 139;
+	LUP.snr = 0;
+	
+	// In the next few bytes the fake LoRa message must be put
+	// PHYPayload = MHDR | MACPAYLOAD | MIC
+	// MHDR, 1 byte
+	// MIC, 4 bytes
+	
+	// ------------------------------
+	// MHDR (Para 4.2), bit 5-7 MType, bit 2-4 RFU, bit 0-1 Major
+	LUP.payLoad[0] = 0x40;								// MHDR 0x40 == unconfirmed up message,
+														// FRU and major are 0
+	
+	// -------------------------------
+	// FHDR consists of 4 bytes addr, 1 byte Fctrl, 2 byte FCnt, 0-15 byte FOpts
+	// We support ABP addresses only for Gateways
+	LUP.payLoad[1] = DevAddr[3];						// Last byte[3] of address
+	LUP.payLoad[2] = DevAddr[2];
+	LUP.payLoad[3] = DevAddr[1];
+	LUP.payLoad[4] = DevAddr[0];						// First byte[0] of Dev_Addr
+	
+	LUP.payLoad[5] = 0x00;								// FCtrl is normally 0
+	LUP.payLoad[6] = frameCount % 0x100;				// LSB
+	LUP.payLoad[7] = frameCount / 0x100;				// MSB
+
+	// -------------------------------
+	// FPort, either 0 or 1 bytes. Must be != 0 for non MAC messages such as user payload
+	//
+	LUP.payLoad[8] = 0x01;								// FPort must not be 0
+	LUP.payLength  = 9;
+	
+	// FRMPayload; Payload will be AES128 encoded using AppSKey
+	// See LoRa spec para 4.3.2
+	// You can add any byte string below based on you personal choice of sensors etc.
+	//
+	
+	// Payload bytes in this example are encoded in the LoRaCode(c) format
+	uint8_t PayLength = LoRaSensors((uint8_t *)(LUP.payLoad + LUP.payLength));
+
+#if DUSB>=1
+	if ((debug>=2) && (pdebug & P_RADIO )) {
+		Serial.print(F("old: "));
+		for (int i=0; i<PayLength; i++) {
+			Serial.print(LUP.payLoad[i],HEX);
+			Serial.print(' ');
+		}
+		Serial.println();
+	}
+#endif	
+	
+	// we have to include the AES functions at this stage in order to generate LoRa Payload.
+	uint8_t CodeLength = encodePacket((uint8_t *)(LUP.payLoad + LUP.payLength), PayLength, (uint16_t)frameCount, DevAddr, AppSKey, 0);
+
+#if DUSB>=1
+	if ((debug>=2) && (pdebug & P_RADIO )) {
+		Serial.print(F("new: "));
+		for (int i=0; i<CodeLength; i++) {
+			Serial.print(LUP.payLoad[i],HEX);
+			Serial.print(' ');
+		}
+		Serial.println();
+	}
+#endif
+
+	LUP.payLength += CodeLength;								// length inclusive sensor data
+	
+	// MIC, Message Integrity Code
+	// As MIC is used by TTN (and others) we have to make sure that
+	// framecount is valid and the message is correctly encrypted.
+	// Note: Until MIC is done correctly, TTN does not receive these messages
+	//		 The last 4 bytes are MIC bytes.
+	//
+	LUP.payLength += micPacket((uint8_t *)(LUP.payLoad), LUP.payLength, (uint16_t)frameCount, NwkSKey, 0);
+
+#if DUSB>=1
+	if ((debug>=2) && (pdebug & P_RADIO )) {
+		Serial.print(F("mic: "));
+		for (int i=0; i<LUP.payLength; i++) {
+			Serial.print(LUP.payLoad[i],HEX);
+			Serial.print(' ');
+		}
+		Serial.println();
+	}
+#endif
+
+	// So now our package is ready, and we can send it up through the gateway interface
+	// Note Be aware that the sensor message (which is bytes) in message will be
+	// be expanded if the server expects JSON messages.
+	//
+	int buff_index = buildPacket(tmst, buff_up, LUP, true);
+	
+	frameCount++;
+    cp_nb_rx_rcv++;	
+	
+	// In order to save the memory, we only write the framecounter
+	// to EEPROM every 10 values. It also means that we will invalidate
+	// 10 value when restarting the gateway.
+	//
+	if (( frameCount % 10)==0) writeGwayCfg(CONFIGFILE);
+	
+	if (buff_index > 512) {
+		if (debug>0) Serial.println(F("sensorPacket:: ERROR buffer size too large"));
+		return(-1);
+	}
+
+#ifdef _TTNSERVER	
+	if (!sendUdp(ttnServer, _TTNPORT, buff_up, buff_index)) {
+		return(-1);
+	}
+#endif
+#ifdef _THINGSERVER
+	if (!sendUdp(thingServer, _THINGPORT, buff_up, buff_index)) {
+		return(-1);
+	}
+#endif
+
+#if DUSB>=1
+	// If all is right, we should after decoding (which is the same as encoding) get
+	// the original message back again.
+	if ((debug>=2) && (pdebug & P_RADIO )) {
+		CodeLength = encodePacket((uint8_t *)(LUP.payLoad + 9), PayLength, (uint16_t)frameCount-1, DevAddr, AppSKey, 0);
+		Serial.print(F("rev: "));
+		for (int i=0; i<CodeLength; i++) {
+			Serial.print(LUP.payLoad[i],HEX);
+			Serial.print(' ');
+		}
+		Serial.print(F(", addr="));
+		for (int i=0; i<4; i++) {
+			Serial.print(DevAddr[i],HEX);
+			Serial.print(' ');
+		}
+		Serial.println();
+	}
+#endif
+
+	if (_cad) {
+		// Set the state to CAD scanning after sending a packet
+		_state = S_SCAN;						// Inititialise scanner
+		sf = SF7;
+		cadScanner();
+	}
+	else {
+		// Reset all RX lora stuff
+		_state = S_RX;
+		rxLoraModem();	
+	}
+		
+	return(buff_index);
+}
+
+#endif //GATEWAYNODE==1
+
+#if (GATEWAYNODE==1) || (_LOCALSERVER==1)
+// ----------------------------------------------------------------------------
+// ENCODEPACKET
+// In Sensor mode, we have to encode the user payload before sending.
+// The same applies to decoding packages in the payload for _LOCALSERVER.
+// The library files for AES are added to the library directory in AES.
+// For the moment we use the AES library made by ideetron as this library
+// is also used in the LMIC stack and is small in size.
+//
+// The function below follows the LoRa spec exactly.
+//
+// The resulting mumber of Bytes is returned by the functions. This means
+// 16 bytes per block, and as we add to the last block we also return 16
+// bytes for the last block.
+//
+// The LMIC code does not do this, so maybe we shorten the last block to only
+// the meaningful bytes in the last block. This means that encoded buffer
+// is exactly as big as the original message.
+//
+// NOTE:: Be aware that the LICENSE of the used AES library files 
+//	that we call with AES_Encrypt() is GPL3. It is used as-is,
+//  but not part of this code.
+//
+// cmac = aes128_encrypt(K, Block_A[i])
+// ----------------------------------------------------------------------------
+uint8_t encodePacket(uint8_t *Data, uint8_t DataLength, uint16_t FrameCount, uint8_t *DevAddr, uint8_t *AppSKey, uint8_t Direction) {
+
+#if DUSB>=1
+	if (( debug>=2 ) && ( pdebug & P_GUI )) {
+		Serial.print(F("G encodePacket:: DevAddr="));
+		for (int i=0; i<4; i++ ) { Serial.print(DevAddr[i],HEX); Serial.print(' '); }
+		Serial.print(F("G encodePacket:: AppSKey="));
+		for (int i=0; i<16; i++ ) { Serial.print(AppSKey[i],HEX); Serial.print(' '); }
+		Serial.println();
+	}
+#endif
+
+	//unsigned char AppSKey[16] = _APPSKEY ;	// see ESP-sc-gway.h
+	uint8_t i, j;
+	uint8_t Block_A[16];
+	uint8_t bLen=16;						// Block length is 16 except for last block in message
+		
+	uint8_t restLength = DataLength % 16;	// We work in blocks of 16 bytes, this is the rest
+	uint8_t numBlocks  = DataLength / 16;	// Number of whole blocks to encrypt
+	if (restLength>0) numBlocks++;			// And add block for the rest if any
+
+	for(i = 1; i <= numBlocks; i++) {
+		Block_A[0] = 0x01;
+		
+		Block_A[1] = 0x00; 
+		Block_A[2] = 0x00; 
+		Block_A[3] = 0x00; 
+		Block_A[4] = 0x00;
+
+		Block_A[5] = Direction;				// 0 is uplink
+
+		Block_A[6] = DevAddr[3];			// Only works for and with ABP
+		Block_A[7] = DevAddr[2];
+		Block_A[8] = DevAddr[1];
+		Block_A[9] = DevAddr[0];
+
+		Block_A[10] = (FrameCount & 0x00FF);
+		Block_A[11] = ((FrameCount >> 8) & 0x00FF);
+		Block_A[12] = 0x00; 				// Frame counter upper Bytes
+		Block_A[13] = 0x00;					// These are not used so are 0
+
+		Block_A[14] = 0x00;
+
+		Block_A[15] = i;
+
+		// Encrypt and calculate the S
+		AES_Encrypt(Block_A, AppSKey);
+		
+		// Last block? set bLen to rest
+		if ((i == numBlocks) && (restLength>0)) bLen = restLength;
+		
+		for(j = 0; j < bLen; j++) {
+			*Data = *Data ^ Block_A[j];
+			Data++;
+		}
+	}
+	//return(numBlocks*16);			// Do we really want to return all 16 bytes in lastblock
+	return(DataLength);				// or only 16*(numBlocks-1)+bLen;
+}
+
+#endif
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_stateMachine.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_stateMachine.ino
new file mode 100644
index 0000000000000000000000000000000000000000..c223faafb81b3a8c0e6e58c0ca01fa40e66185b1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_stateMachine.ino
@@ -0,0 +1,948 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains the state machine code enabling to receive
+// and transmit packages/messages.
+// ========================================================================================
+//
+
+
+// ----------------------------------------------------------------------------
+// stateMachine handler of the state machine.
+// We use ONE state machine for all kind of interrupts. This assures that we take
+// the correct action upon receiving an interrupt.
+//
+// _event is the software interrupt: If set this function is executed from loop(),
+// the function should itself take care of setting or resetting the variable.
+//
+// STATE MACHINE
+// The program uses the following state machine (in _state), all states
+// are done in interrupt routine, only the follow-up of S-RXDONE is done
+// in the main loop() program. This is because otherwise the interrupt processing
+// would take too long to finish
+//
+// So _state has one of the following state values:
+//
+// S-INIT=0, 	The commands in this state are executed only once
+//	- Goto S_SCAN
+//
+// S-SCAN, 		CadScanner() part
+//	- Upon CDDECT (int1) goto S_RX, 
+//	- upon CDDONE (int0) goto S_CAD, walk through all SF until CDDETD
+//	- Else stay in SCAN state
+//
+// S-CAD, 		
+//	- Upon CDDECT (int1) goto S_RX, 
+//	- Upon CDDONE (int0) goto S_SCAN, start with SF7 recognition again
+//
+// S-RX, 		Received CDDECT so message detected, RX cycle started. 
+//	- Upon RXDONE (int0) package read. If read ok continue to read message
+//	- upon RXTOUT (int1) error, goto S_SCAN
+//
+// S-TX			Transmitting a message
+//	- Upon TXDONE goto S_SCAN
+//
+// S-TXDONE		Transmission complete by loop() now again in interrupt
+//	- Set the Mask
+//	- reset the Flags
+//	- Goto either SCAN or RX
+//
+// This interrupt routine has been kept as simple and short as possible.
+// If we receive an interrupt that does not below to a _state then print error.
+// _event is a special variable which indicate that an interrupt event has happened
+//	and we need to take action OR that we generate a soft interrupt for state machine.
+// 
+// NOTE: We may clear the interrupt but leave the flag for the moment. 
+//	The eventHandler should take care of repairing flags between interrupts.
+// ----------------------------------------------------------------------------
+
+void stateMachine()
+{
+	// Determine what interrupt flags are set
+	//
+	uint8_t flags = readRegister(REG_IRQ_FLAGS);
+	uint8_t mask  = readRegister(REG_IRQ_FLAGS_MASK);
+	uint8_t intr  = flags & ( ~ mask );				// Only react on non masked interrupts
+	uint8_t rssi;
+	_event=0;										// Reset the interrupt detector	
+	
+#if DUSB>=1
+	if (intr != flags) {
+		Serial.print(F("FLAG  ::"));
+		SerialStat(intr);
+	}
+#endif
+
+	// If Hopping is selected AND if there is NO event interrupt detected 
+	// and the state machine is called anyway
+	// then we know its a soft interrupt and we do nothing and return to main loop.
+	//
+	if ((_hop) && (intr == 0x00) )
+	{
+		// eventWait is the time since we have had a CDDETD event (preamble detected)
+		// If we are not in scanning state, and there will be an interrupt coming,
+		// In state S_RX it could be RXDONE in which case allow kernel time
+		//
+		if ((_state == S_SCAN) || (_state == S_CAD)) {
+
+			_event=0;
+		
+			uint32_t eventWait = EVENT_WAIT;
+			switch (_state) {
+				case S_INIT:	eventWait = 0; break;
+				// Next two are most important
+				case S_SCAN:	eventWait = EVENT_WAIT * 1; break;
+				case S_CAD:		eventWait = EVENT_WAIT * 1; break;
+				
+				case S_RX:		eventWait = EVENT_WAIT * 8; break;
+				case S_TX:		eventWait = EVENT_WAIT * 1; break;
+				case S_TXDONE:	eventWait = EVENT_WAIT * 4; break;
+				default:
+					eventWait=0;
+#if DUSB>=1
+					Serial.print(F("DEFAULT :: "));
+					SerialStat(intr);
+#endif
+			}
+			
+			// doneWait is the time that we received CDDONE interrupt
+			// So we init the wait time for RXDONE based on the current SF.
+			// As for highter CF it takes longer to receive symbols
+			// Assume symbols in SF8 take twice the time of SF7
+			//
+			uint32_t doneWait = DONE_WAIT;			// Initial value
+			switch (sf) {
+				case SF7: 	break;
+				case SF8: 	doneWait *= 2;	break;
+				case SF9: 	doneWait *= 4;	break;
+				case SF10:	doneWait *= 8;	break;
+				case SF11:	doneWait *= 16;	break;
+				case SF12:	doneWait *= 32;	break;
+				default:
+					doneWait *= 1;
+#if DUSB>=1
+					if (( debug>=0 ) && ( pdebug & P_PRE )) {
+						Serial.print(F("PRE:: DEF set"));
+						Serial.println();
+					}
+#endif
+					break;
+			}
+
+			// If micros is starting over again after 51 minutes 
+			// it's value is smaller than an earlier value of eventTime/doneTime
+			//
+			if (eventTime > micros())	eventTime=micros();
+			if (doneTime > micros())	doneTime=micros();
+
+			if (((micros() - doneTime) > doneWait ) &&
+				(( _state == S_SCAN ) || ( _state == S_CAD )))
+			{
+				_state = S_SCAN;
+				hop();								// increment ifreq = (ifreq + 1) % NUM_HOPS ;
+				cadScanner();						// Reset to initial SF, leave frequency "freqs[ifreq]"
+#if DUSB>=1
+				if (( debug >= 1 ) && ( pdebug & P_PRE )) {
+					Serial.print(F("DONE  :: "));
+					SerialStat(intr);
+				}
+#endif
+				eventTime=micros();					// reset the timer on timeout
+				doneTime=micros();					// reset the timer on timeout
+				return;
+			}
+			// If timeout occurs and still no _event, then hop
+			// and start scanning again
+			//
+			if ((micros() - eventTime) > eventWait ) 
+			{
+				_state = S_SCAN;
+				hop();								// increment ifreq = (ifreq + 1) % NUM_HOPS ;
+				cadScanner();						// Reset to initial SF, leave frequency "freqs[ifreq]"
+#if DUSB>=1
+				if (( debug >= 2 ) && ( pdebug & P_PRE )) {
+					Serial.print(F("HOP ::  "));
+					SerialStat(intr);
+				}
+#endif
+				eventTime=micros();					// reset the timer on timeout
+				doneTime=micros();					// reset the timer on timeout
+				return;
+			}
+			
+		
+			// If we are here, NO timeout has occurred 
+			// So we need to return to the main State Machine
+			// as there was NO interrupt
+#if DUSB>=1
+			if (( debug>=3 ) && ( pdebug & P_PRE )) {
+				Serial.print(F("PRE:: eventTime="));
+				Serial.print(eventTime);
+				Serial.print(F(", micros="));
+				Serial.print(micros());
+				Serial.print(F(": "));
+				SerialStat(intr);
+			}
+#endif
+		} // if SCAN or CAD
+		
+		// else, S_RX of S_TX for example
+		else {
+			//yield();								// May take too much time for RX
+		} // else S_RX or S_TX, TXDONE
+		
+		yield();
+		
+	}// intr==0 && _hop
+
+	
+
+	// ================================================================
+	// This is the actual state machine of the gateway
+	// and its next actions are depending on the state we are in.
+	// For hop situations we do not get interrupts, so we have to
+	// simulate and generate events ourselves.
+	//
+	switch (_state) 
+	{
+	  // --------------------------------------------------------------
+	  // If the state is init, we are starting up.
+	  // The initLoraModem() function is already called in setup();
+	  //
+	  case S_INIT:
+#if DUSB>=1
+		if (( debug>=1 ) && ( pdebug & P_PRE )) { 
+			Serial.println(F("S_INIT")); 
+		}
+#endif
+		// new state, needed to startup the radio (to S_SCAN)
+		writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF );		// Clear ALL interrupts
+		writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 );		// Clear ALL interrupts
+		_event=0;
+	  break;
+
+	  
+	  // --------------------------------------------------------------
+	  // In S_SCAN we measure a high RSSI this means that there (probably) is a message
+	  // coming in at that freq. But not necessarily on the current SF.
+	  // If so find the right SF with CDDETD. 
+	  //
+	  case S_SCAN:
+	    //
+		// Intr==IRQ_LORA_CDDETD_MASK
+		// We detected a message on this frequency and SF when scanning
+		// We clear both CDDETD and swich to reading state to read the message
+		//
+		if (intr & IRQ_LORA_CDDETD_MASK) {
+
+			_state = S_RX;								// Set state to receiving
+
+			// Set RXDONE interrupt to dio0, RXTOUT to dio1
+			writeRegister(REG_DIO_MAPPING_1, (
+				MAP_DIO0_LORA_RXDONE | 
+				MAP_DIO1_LORA_RXTOUT | 
+				MAP_DIO2_LORA_NOP | 
+				MAP_DIO3_LORA_CRC));
+			
+			// Since new state is S_RX, accept no interrupts except RXDONE or RXTOUT
+			// HEADER and CRCERR
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
+				IRQ_LORA_RXDONE_MASK | 
+				IRQ_LORA_RXTOUT_MASK | 
+				IRQ_LORA_HEADER_MASK | 
+				IRQ_LORA_CRCERR_MASK));
+			
+			// Starting with version 5.0.1 the waittime is dependent on the SF
+			// So for SF12 we wait longer (2^7 == 128 uSec) and for SF7 4 uSec.
+			//delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) );
+			//if (_cad) 									// XXX 180520 make sure we start reading asap in hop
+			//	delayMicroseconds( RSSI_WAIT );				// Wait some microseconds less
+			
+			rssi = readRegister(REG_RSSI);				// Read the RSSI
+			_rssi = rssi;								// Read the RSSI in the state variable
+
+			_event = 0;									// Make 0, as soon as we have an interrupt
+			detTime = micros();							// mark time that preamble detected
+			
+#if DUSB>=1
+			if (( debug>=1 ) && ( pdebug & P_SCAN )) {
+				Serial.print(F("SCAN:: "));
+				SerialStat(intr);
+			}
+#endif
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF );		// reset all interrupt flags
+			opmode(OPMODE_RX_SINGLE);					// set reg 0x01 to 0x06 for receiving
+			
+		}//if
+
+		// CDDONE
+		// We received a CDDONE int telling us that we received a message on this
+		// frequency and possibly on one of its SF. Only when the incoming message
+		// matches the SF then also CDDETD is raised.
+		// If so, we switch to CAD state where we will wait for CDDETD event.
+		//
+		else if (intr & IRQ_LORA_CDDONE_MASK) {
+
+			opmode(OPMODE_CAD);
+			rssi = readRegister(REG_RSSI);				// Read the RSSI
+
+#if DUSB>=1
+			if (( debug>=2 ) && ( pdebug & P_SCAN )) {
+				Serial.print(F("SCAN:: CDDONE: "));
+				SerialStat(intr);
+			}
+#endif
+			// We choose the generic RSSI as a sorting mechanism for packages/messages
+			// The pRSSI (package RSSI) is calculated upon successful reception of message
+			// So we expect that this value makes little sense for the moment with CDDONE.
+			// Set the rssi as low as the noise floor. Lower values are not recognized then.
+			// Every cycle starts with ifreq==0 and sf=SF7 (or the set init SF)
+			//
+			//if ( rssi > RSSI_LIMIT )					// Is set to 35
+			if ( rssi > (RSSI_LIMIT - (_hop * 7)))		// Is set to 35, or 29 for HOP
+			{
+#if DUSB>=1
+				if (( debug>=2 ) && ( pdebug & P_SCAN )) {
+					Serial.print(F("SCAN:: -> CAD: "));
+					SerialStat(intr);
+				}
+#endif
+				_state = S_CAD;							// promote to next level
+				_event=0;
+			}
+			
+			// If the RSSI is not big enough we skip the CDDONE
+			// and go back to scanning
+			else {
+#if DUSB>=1
+				if (( debug>=2 ) && ( pdebug & P_SCAN )) {
+					Serial.print("SCAN:: rssi=");
+					Serial.print(rssi);
+					Serial.print(F(": "));
+					SerialStat(intr);
+				}
+#endif
+				_state = S_SCAN;
+				//_event=1;								// loop() scan until CDDONE
+			}
+
+			// Clear the CADDONE flag
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
+			doneTime = micros();						// We need CDDONE or other intr to reset timeout			
+
+		}//SCAN CDDONE 
+		
+		// So if we are here then we are in S_SCAN and the interrupt is not
+		// CDDECT or CDDONE. it is probably soft interrupt _event==1
+		// So if _hop we change the frequency and restart the
+		// interrupt in order to check for CDONE on other frequencies
+		// if _hop we start at the next frequency, hop () sets the sf to SF7.
+		// If we are at the end of all frequencies, reset frequencies and sf
+		// and go to S_SCAN state.
+		//
+		// Note:: We should make sure that all frequencies are scanned in a row
+		// and when we switch to ifreq==0 we should stop for a while
+		// to allow system processing.
+		// We should make sure that we enable webserver etc every once in a while.
+		// We do this by changing _event to 1 in loop() only for _hop and
+		// use _event=0 for non hop.
+		//
+		else if (intr == 0x00) 
+		{
+			_event=0;							// XXX 26/12/2017 !!! NEED
+		}
+		
+		// Unkown Interrupt, so we have an error
+		//
+		else {
+#if DUSB>=1
+			if (( debug>=0 ) && ( pdebug & P_SCAN )) {
+				Serial.print(F("SCAN unknown:: "));
+				SerialStat(intr);
+			}
+#endif
+			_state=S_SCAN;
+			//_event=1;								// XXX 06/03 loop until interrupt
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
+		}
+		
+	  break; // S_SCAN
+
+	  
+	  // --------------------------------------------------------------
+	  // S_CAD: In CAD mode we scan every SF for high RSSI until we have a DETECT.
+	  // Reason is the we received a CADDONE interrupt so we know a message is received
+	  // on the frequency but may be on another SF.
+	  //
+	  // If message is of the right frequency and SF, IRQ_LORA_CDDETD_MSAK interrupt
+	  // is raised, indicating that we can start beging reading the message from SPI.
+	  //
+	  // DIO0 interrupt IRQ_LORA_CDDONE_MASK in state S_CAD==2 means that we might have
+	  // a lock on the Freq but not the right SF. So we increase the SF
+	  //
+	  case S_CAD:
+
+		// Intr=IRQ_LORA_CDDETD_MASK
+		// We have to set the sf based on a strong RSSI for this channel
+		// Also we set the state to S_RX and start receiving the message
+		//
+		if (intr & IRQ_LORA_CDDETD_MASK) {
+
+			// Set RXDONE interrupt to dio0, RXTOUT to dio1
+			writeRegister(REG_DIO_MAPPING_1, (
+				MAP_DIO0_LORA_RXDONE | 
+				MAP_DIO1_LORA_RXTOUT | 
+				MAP_DIO2_LORA_NOP |
+				MAP_DIO3_LORA_CRC ));
+			
+			// Accept no interrupts except RXDONE or RXTOUT
+			_event=0;								
+			
+			// if CDECT, make state S_RX so we wait for RXDONE intr
+			
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) ~(
+				IRQ_LORA_RXDONE_MASK | 
+				IRQ_LORA_RXTOUT_MASK |
+				IRQ_LORA_HEADER_MASK |
+				IRQ_LORA_CRCERR_MASK ));
+				
+			// Reset all interrupts as soon as possible
+			// But listen ONLY to RXDONE and RXTOUT interrupts 
+			//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDETD_MASK | IRQ_LORA_RXDONE_MASK);
+			// If we want to reset CRC, HEADER and RXTOUT flags as well
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF );		// XXX 180326, reset all CAD Detect interrupt flags
+			
+			//_state = S_RX;								// XXX 180521 Set state to start receiving
+			opmode(OPMODE_RX_SINGLE);					// set reg 0x01 to 0x06, initiate READ
+			
+			delayMicroseconds( RSSI_WAIT );				// Wait some microseconds less
+			//delayMicroseconds( (0x01 << ((uint8_t)sf - 5 )) );
+			rssi = readRegister(REG_RSSI);				// Read the RSSI
+			_rssi = rssi;								// Read the RSSI in the state variable
+
+			detTime = micros();
+#if DUSB>=1
+			if (( debug>=1 ) && ( pdebug & P_CAD )) {
+				Serial.print(F("CAD:: "));
+				SerialStat(intr);
+			}
+#endif
+			_state = S_RX;								// Set state to start receiving
+			
+		}// CDDETD
+		
+		// Intr == CADDONE
+		// So we scan this SF and if not high enough ... next
+		//
+		else if (intr & IRQ_LORA_CDDONE_MASK) {
+			// If this is not SF12, increment the SF and try again
+			// We expect on other SF get CDDETD
+			//
+			if (((uint8_t)sf) < SF12) {
+			
+				sf = (sf_t)((uint8_t)sf+1);				// Increment sf
+				setRate(sf, 0x04);						// Set SF with CRC==on
+				
+				// reset interrupt flags for CAD Done
+				_event=0;								// XXX 180324, when increasing SF loop, ws 0x00
+				writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);	// Reset the interrupt mask
+				//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_CDDONE_MASK | IRQ_LORA_CDDETD_MASK);
+				writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF );	// This will prevent the CDDETD from being read
+
+				opmode(OPMODE_CAD);						// Scanning mode
+				
+				delayMicroseconds(RSSI_WAIT);
+				rssi = readRegister(REG_RSSI);			// Read the RSSI
+
+#if DUSB>=1
+				if (( debug>=2 ) && ( pdebug & P_CAD )) {
+					Serial.print(F("S_CAD:: CDONE, SF="));
+					Serial.println(sf);
+				}
+#endif
+			}
+
+			// If we reach SF12, we should go back to SCAN state
+			//
+			else {
+
+				// Reset Interrupts
+				_event=1;								// reset soft intr, to state machine again
+				writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);	// Reset the interrupt mask
+				writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF );	// or IRQ_LORA_CDDONE_MASK
+				
+				_state = S_SCAN;						// As soon as we reach SF12 do something
+				sf = SF7;
+				cadScanner();							// Which will reset SF to SF7
+
+#if DUSB>=1		
+				if (( debug>=2 ) && ( pdebug & P_CAD )) {
+					Serial.print(F("CAD->SCAN:: "));
+					SerialStat(intr);
+				}
+#endif
+			}
+			doneTime = micros();						// We need CDDONE or other intr to reset timeout
+			
+		} //CAD CDDONE
+
+		// if this interrupt is not CDECT or CDDONE then probably is 0x00
+		// This means _event was set but there was no real interrupt (yet).
+		// So we clear _event and wait for next (soft) interrupt.
+		// We stay in the CAD state because CDDONE means something is 
+		// coming on this frequency so we wait on CDECT.
+		//
+		else if (intr == 0x00) {
+#if DUSB>=0
+			if (( debug>=3 ) && ( pdebug & P_CAD )) {
+				Serial.println("Err CAD:: intr is 0x00");
+			}
+#endif
+			_event=1;											// Stay in CAD _state until real interrupt
+		}
+		
+		// else we do not recognize the interrupt. We print an error
+		// and restart scanning. If hop we even start at ifreq==1
+		//
+		else {
+#if DUSB>=1
+			if (( debug>=0) && ( pdebug & P_CAD )) { 
+				Serial.print(F("Err CAD: Unknown::")); 
+				SerialStat(intr);
+			}
+#endif
+			_state = S_SCAN;
+			sf = SF7;
+			cadScanner();										// Scan and set SF7
+			
+			// Reset Interrupts
+			_event=1;											// If unknown interrupt, restarts
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);	// Reset the interrupt mask
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);		// Reset all interrupts
+
+		}
+	  break; //S_CAD
+	  
+	  // --------------------------------------------------------------
+	  // If we receive an RXDONE interrupt on dio0 with state==S_RX
+	  // 	So we should handle the received message
+	  // Else if it is RXTOUT interrupt
+	  //	So we handle this, and get modem out of standby
+	  // Else
+	  //	Go back to SCAN
+	  //
+	  case S_RX:
+		
+		if (intr & IRQ_LORA_RXDONE_MASK) {
+		
+#if CRCCHECK==1
+			// We have to check for CRC error which will be visible AFTER RXDONE is set.
+			// CRC errors might indicate that the reception is not OK.
+			// Could be CRC error or message too large.
+			// CRC error checking requires DIO3
+			//
+			if (intr & IRQ_LORA_CRCERR_MASK) {
+#if DUSB>=1
+				if (( debug>=0 ) && ( pdebug & P_RX )) {
+					Serial.print(F("Rx CRC err: "));
+					SerialStat(intr);
+				}
+#endif
+				if (_cad) {
+					sf = SF7;
+					_state = S_SCAN;
+					cadScanner();
+				}
+				else {
+					_state = S_RX;
+					rxLoraModem();
+				}
+
+				// Reset interrupts
+				_event=0;											// CRC error
+				writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);	// Reset the interrupt mask
+				writeRegister(REG_IRQ_FLAGS, (uint8_t)(
+					IRQ_LORA_RXDONE_MASK | 
+					IRQ_LORA_RXTOUT_MASK | 
+					IRQ_LORA_HEADER_MASK | 
+					IRQ_LORA_CRCERR_MASK ));
+
+				break;
+			}// RX-CRC
+#endif // CRCCHECK
+			
+			// If we are here, no CRC error occurred, start timer
+#if DUSB>=1
+			unsigned long ffTime = micros();	
+#endif			
+			// There should not be an error in the message
+			LoraUp.payLoad[0]= 0x00;								// Empty the message
+
+			// If receive S_RX error, 
+			// - print Error message
+			// - Set _state to SCAN
+			// - Set _event=1 so that we loop until we have an interrupt
+			// - Reset the interrupts
+			// - break
+			if((LoraUp.payLength = receivePkt(LoraUp.payLoad)) <= 0) {
+#if DUSB>=1
+				if (( debug>=1 ) && ( pdebug & P_RX )) {
+					Serial.print(F("sMachine:: Error S-RX: "));
+					Serial.print(F("payLength="));
+					Serial.print(LoraUp.payLength);
+					Serial.println();
+				}
+#endif
+				_event=1;
+				writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);	// Reset the interrupt mask
+				//writeRegister(REG_IRQ_FLAGS, (uint8_t)(
+				//	IRQ_LORA_RXDONE_MASK | 
+				//	IRQ_LORA_RXTOUT_MASK | 
+				//	IRQ_LORA_HEADER_MASK | 
+				//	IRQ_LORA_CRCERR_MASK ));
+				writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
+				
+				_state = S_SCAN;
+				break;
+			}
+#if DUSB>=1
+			if (( debug>=1 ) && ( pdebug & P_RX )) {
+				Serial.print(F("RXDONE in dT="));
+				Serial.print(ffTime - detTime);
+				Serial.print(F(": "));
+				SerialStat(intr);
+			}
+#endif
+				
+			// Do all register processing in this section
+			uint8_t value = readRegister(REG_PKT_SNR_VALUE);	// 0x19; 
+			if ( value & 0x80 ) {								// The SNR sign bit is 1
+				
+				value = ( ( ~value + 1 ) & 0xFF ) >> 2;			// Invert and divide by 4
+				LoraUp.snr = -value;
+			}
+			else {
+				// Divide by 4
+				LoraUp.snr = ( value & 0xFF ) >> 2;
+			}
+
+			// Packet RSSI
+			LoraUp.prssi = readRegister(REG_PKT_RSSI);			// read register 0x1A, packet rssi
+    
+			// Correction of RSSI value based on chip used.	
+			if (sx1272) {										// Is it a sx1272 radio?
+				LoraUp.rssicorr = 139;
+			} else {											// Probably SX1276 or RFM95
+				LoraUp.rssicorr = 157;
+			}
+				
+			LoraUp.sf = readRegister(REG_MODEM_CONFIG2) >> 4;
+
+			// If read was successful, read the package from the LoRa bus
+			//
+			if (receivePacket() <= 0) {							// read is not successful
+#if DUSB>=1
+				if (( debug>=0 ) && ( pdebug & P_RX )) {
+					Serial.println(F("sMach:: Error receivePacket"));
+				}
+#endif
+			}
+			
+			// Set the modem to receiving BEFORE going back to user space.
+			// 
+			if ((_cad) || (_hop)) {
+				_state = S_SCAN;
+				sf = SF7;
+				cadScanner();
+			}
+			else {
+				_state = S_RX;
+				rxLoraModem();
+			}
+			
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);		// Reset the interrupt mask
+			eventTime=micros();				//There was an event for receive
+			_event=0;
+		}// RXDONE
+		
+		// RXOUT: 
+		// We did receive message receive timeout
+		// This is the most common event in hop mode, possibly due to the fact
+		// that receiving has started too late in the middle of a message
+		// (according to the documentation). So is there a way to start receiving
+		// immediately without delay.
+		//
+		else if (intr & IRQ_LORA_RXTOUT_MASK) {
+			
+			// Make sure we reset all interrupts
+			// and get back to scanning
+			_event=0;												// Is set by interrupt handlers
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 );
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);			// reset all interrupts
+			
+			// If RXTOUT we put the modem in cad state and reset to SF7
+			// If a timeout occurs here we reset the cadscanner
+			//
+			if ((_cad) || (_hop)) {
+				// Set the state to CAD scanning
+#if DUSB>=1
+				if (( debug>=2 ) && ( pdebug & P_RX )) {
+					Serial.print(F("RXTOUT:: "));
+					SerialStat(intr);
+				}
+#endif
+				sf = SF7;
+				cadScanner();								// Start the scanner after RXTOUT
+				_state = S_SCAN;							// New state is scan
+
+			}
+			
+			// If not in cad mode we are in single channel single sf mode.
+			//
+			else {
+				_state = S_RX;								// Receive when interrupted
+				rxLoraModem();
+			}
+			
+			eventTime=micros();								//There was an event for receive
+			doneTime = micros();							// We need CDDONE or other intr to reset timeout
+			
+		}// RXTOUT
+		
+		else if (intr & IRQ_LORA_HEADER_MASK) {
+			// This interrupt means we received an header successfully
+			// which is normall an indication of RXDONE
+			//writeRegister(REG_IRQ_FLAGS, IRQ_LORA_HEADER_MASK);
+#if DUSB>=1
+			if (( debug>=3 ) && ( pdebug & P_RX )) {
+				Serial.print(F("RX HEADER:: "));
+				SerialStat(intr);
+			}
+#endif
+			//_event=1;
+		}
+
+		// If we did not receive a hard interrupt
+		// Then probably do not do anything, because in the S_RX
+		// state there always comes a RXTOUT or RXDONE interrupt
+		//
+		else if (intr == 0x00) {
+#if DUSB>=1
+			if (( debug>=3) && ( pdebug & P_RX )) {
+				Serial.print(F("S_RX no INTR:: "));
+				SerialStat(intr);
+			}
+#endif
+		}
+		
+		// The interrupt received is not RXDONE, RXTOUT or HEADER
+		// therefore we wait. Make sure to clear the interrupt
+		// as HEADER interrupt comes just before RXDONE
+		else {							
+#if DUSB>=1
+			if (( debug>=0 ) && ( pdebug & P_RX )) {
+				Serial.print(F("S_RX:: no RXDONE, RXTOUT, HEADER:: "));
+				SerialStat(intr);
+			}
+#endif
+			//writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00 );
+			//writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);
+		}// int not RXDONE or RXTOUT
+		
+	  break; // S_RX
+
+	  
+	  // --------------------------------------------------------------  
+	  // Start te transmission of a message in state S-TX
+	  // This is not an interrupt state, we use this state to start transmission
+	  // the interrupt TX-DONE tells us that the transmission was successful.
+	  // It therefore is no use to set _event==1 as transmission might
+	  // not be finished in the next loop iteration
+	  //
+	  case S_TX:
+	  
+		// We need a timeout for this case. In case there does not come an interrupt,
+		// then there will nog be a TXDONE but probably another CDDONE/CDDETD before
+		// we have a timeout in the main program (Keep Alive)
+		if (intr == 0x00) {
+#if DUSB>=1
+			if (( debug>=2 ) && ( pdebug & P_TX )) {
+				Serial.println(F("TX:: 0x00"));
+			}
+#endif
+			_event=1;
+			_state=S_TXDONE;
+		}
+
+		// Set state to transmit
+		_state = S_TXDONE;
+		
+		// Clear interrupt flags and masks
+		writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
+		writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);					// reset interrupt flags
+		
+	  	// Initiate the transmission of the buffer (in Interrupt space)
+		// We react on ALL interrupts if we are in TX state.
+		txLoraModem(
+			LoraDown.payLoad,
+			LoraDown.payLength,
+			LoraDown.tmst,
+			LoraDown.sfTx,
+			LoraDown.powe,
+			LoraDown.fff,
+			LoraDown.crc,
+			LoraDown.iiq
+		);
+		// After filling the buffer we only react on TXDONE interrupt
+		
+#if DUSB>=1
+		if (( debug>=1 ) && ( pdebug & P_TX )) { 
+			Serial.print(F("T TX done:: ")); 
+			SerialStat(intr);
+		}
+#endif
+		// More or less start at the "case TXDONE:" below 
+		_state=S_TXDONE;
+		_event=1;													// Or remove the break below
+		
+	  break; // S_TX
+
+	  
+	  // ---------------------------------------------------
+	  // AFter the transmission is completed by the hardware, 
+	  // the interrupt TXDONE is raised telling us that the tranmission
+	  // was successful.
+	  // If we receive an interrupt on dio0 _state==S_TX it is a TxDone interrupt
+	  // Do nothing with the interrupt, it is just an indication.
+	  // send Packet switch back to scanner mode after transmission finished OK
+	  //
+	  case S_TXDONE:
+		if (intr & IRQ_LORA_TXDONE_MASK) {
+
+#if DUSB>=1
+			if (( debug>=0 ) && ( pdebug & P_TX )) {
+				Serial.print(F("T TXDONE:: rcvd="));
+				Serial.print(micros());
+				Serial.print(F(", diff="));
+				Serial.println(micros()-LoraDown.tmst);
+				if (debug>=2) Serial.flush();
+			}
+#endif
+			// After transmission reset to receiver
+			if ((_cad) || (_hop)) {									// XXX 26/02
+				// Set the state to CAD scanning
+				_state = S_SCAN;
+				sf = SF7;
+				cadScanner();										// Start the scanner after TX cycle
+			}
+			else {
+				_state = S_RX;
+				rxLoraModem();		
+			}
+			
+			_event=0;
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);			// reset interrupt flags
+#if DUSB>=1
+			if (( debug>=1 ) && ( pdebug & P_TX )) {
+				Serial.println(F("T TXDONE:: done OK"));
+			}
+#endif
+		}
+		
+		// If a soft _event==0 interrupt and no transmission finished:
+		else if ( intr != 0 ) {
+#if DUSB>=1
+			if (( debug>=0 ) && ( pdebug & P_TX )) {
+				Serial.print(F("T TXDONE:: unknown int:"));
+				SerialStat(intr);
+			}
+#endif
+			writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
+			writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);		// reset interrupt flags
+			_event=0;
+			_state=S_SCAN;
+		}
+		
+		// intr == 0
+		else {
+
+			// Increase timer. If timer exceeds certain value (7 seconds!), reset
+			// After sending a message with S_TX, we have to receive a TXDONE interrupt
+			// within 7 seconds according to spec, of here is a problem.
+			if ( sendTime > micros() ) sendTime = 0;					// This could be omitted for usigned ints
+			if (( _state == S_TXDONE ) && (( micros() - sendTime) > 7000000 )) {
+#if DUSB>=1
+				if (( debug>=1 ) && ( pdebug & P_TX )) {
+					Serial.println(F("T TXDONE:: reset TX"));
+					Serial.flush();
+				}
+#endif
+				startReceiver();
+			}
+#if DUSB>=1
+			if (( debug>=3 ) && ( pdebug & P_TX )) {
+				Serial.println(F("T TXDONE:: No Interrupt"));
+			}
+#endif
+		}
+	
+
+	  break; // S_TXDONE	  
+
+	  
+	  // --------------------------------------------------------------
+	  // If _STATE is in undefined state
+	  // If such a thing happens, we should re-init the interface and 
+	  // make sure that we pick up next interrupt
+	  default:
+#if DUSB>=1
+		if (( debug>=0) && ( pdebug & P_PRE )) { 
+			Serial.print("ERR state="); 
+			Serial.println(_state);	
+		}
+#endif
+		if ((_cad) || (_hop)) {
+#if DUSB>=1
+			if (debug>=0) {
+				Serial.println(F("default:: Unknown _state "));
+				SerialStat(intr);
+			}
+#endif
+			_state = S_SCAN;
+			sf = SF7;
+			cadScanner();								// Restart the state machine
+			_event=0;									
+		}
+		else											// Single channel AND single SF
+		{
+			_state = S_RX;
+			rxLoraModem();
+			_event=0;
+		}
+		writeRegister(REG_IRQ_FLAGS_MASK, (uint8_t) 0x00);
+		writeRegister(REG_IRQ_FLAGS, (uint8_t) 0xFF);	// Reset all interrupts
+		eventTime=micros();							// Reset event for unkonwn state
+		
+	  break;// default
+	}// switch(_state)
+	
+	return;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_txRx.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_txRx.ino
new file mode 100644
index 0000000000000000000000000000000000000000..75e6f46dfef1c0d0a347502f845dd2238a1cb668
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_txRx.ino
@@ -0,0 +1,713 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains the LoRa modem specific code enabling to receive
+// and transmit packages/messages.
+// ========================================================================================
+
+
+
+// ----------------------------------------------------------------------------
+// DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN DOWN 
+// Send DOWN a LoRa packet over the air to the node. This function does all the 
+// decoding of the server message and prepares a Payload buffer.
+// The payload is actually transmitted by the sendPkt() function.
+// This function is used for regular downstream messages and for JOIN_ACCEPT
+// messages.
+// NOTE: This is not an interrupt function, but is started by loop().
+// The _status is set an the end of the function to TX and in _stateMachine
+// function the actual transmission function is executed.
+// The LoraDown.tmst contains the timestamp that the tranmission should finish.
+// ----------------------------------------------------------------------------
+int sendPacket(uint8_t *buf, uint8_t length) 
+{
+	// Received package with Meta Data (for example):
+	// codr	: "4/5"
+	// data	: "Kuc5CSwJ7/a5JgPHrP29X9K6kf/Vs5kU6g=="	// for example
+	// freq	: 868.1 									// 868100000
+	// ipol	: true/false
+	// modu : "LORA"
+	// powe	: 14										// Set by default
+	// rfch : 0											// Set by default
+	// size	: 21
+	// tmst : 1800642 									// for example
+	// datr	: "SF7BW125"
+	
+	// 12-byte header;
+	//		HDR (1 byte)
+	//		
+	//
+	// Data Reply for JOIN_ACCEPT as sent by server:
+	//		AppNonce (3 byte)
+	//		NetID (3 byte)
+	//		DevAddr (4 byte) [ 31..25]:NwkID , [24..0]:NwkAddr
+ 	//		DLSettings (1 byte)
+	//		RxDelay (1 byte)
+	//		CFList (fill to 16 bytes)
+			
+	int i=0;
+	StaticJsonBuffer<312> jsonBuffer;
+	char * bufPtr = (char *) (buf);
+	buf[length] = 0;
+	
+#if DUSB>=1
+	if (debug>=2) {
+		Serial.println((char *)buf);
+		Serial.print(F("<"));
+		Serial.flush();
+	}
+#endif
+	// Use JSON to decode the string after the first 4 bytes.
+	// The data for the node is in the "data" field. This function destroys original buffer
+	JsonObject& root = jsonBuffer.parseObject(bufPtr);
+		
+	if (!root.success()) {
+#if DUSB>=1
+		if (( debug>=1) && (pdebug & P_TX)) {
+			Serial.print (F("T sendPacket:: ERROR Json Decode"));
+			if (debug>=2) {
+				Serial.print(':');
+				Serial.println(bufPtr);
+			}
+			Serial.flush();
+		}
+#endif
+		return(-1);
+	}
+	yield();
+	
+	// Meta Data sent by server (example)
+	// {"txpk":{"codr":"4/5","data":"YCkEAgIABQABGmIwYX/kSn4Y","freq":868.1,"ipol":true,"modu":"LORA","powe":14,"rfch":0,"size":18,"tmst":1890991792,"datr":"SF7BW125"}}
+
+	// Used in the protocol of Gateway:
+	const char * data	= root["txpk"]["data"];			// Downstream Payload
+	uint8_t psize		= root["txpk"]["size"];
+	bool ipol			= root["txpk"]["ipol"];
+	uint8_t powe		= root["txpk"]["powe"];			// e.g. 14 or 27
+	LoraDown.tmst		= (uint32_t) root["txpk"]["tmst"].as<unsigned long>();
+	const float ff		= root["txpk"]["freq"];			// eg 869.525
+	
+	// Not used in the protocol of Gateway TTN:
+	const char * datr	= root["txpk"]["datr"];			// eg "SF7BW125"
+	const char * modu	= root["txpk"]["modu"];			// =="LORA"
+	const char * codr	= root["txpk"]["codr"];			// e.g. "4/5"
+	//if (root["txpk"].containsKey("imme") ) {
+	//	const bool imme = root["txpk"]["imme"];			// Immediate Transmit (tmst don't care)
+	//}
+
+	if ( data != NULL ) {
+#if DUSB>=1
+		if (( debug>=2 ) && ( pdebug & P_TX )) { 
+			Serial.print(F("T data: ")); 
+			Serial.println((char *) data);
+			if (debug>=2) Serial.flush();
+		}
+#endif
+	}
+	else {												// There is data!
+#if DUSB>=1
+		if ((debug>0) && ( pdebug & P_TX )) {
+			Serial.println(F("T sendPacket:: ERROR: data is NULL"));
+			if (debug>=2) Serial.flush();
+		}
+#endif
+		return(-1);
+	}
+
+	LoraDown.sfTx = atoi(datr+2);						// Convert "SF9BW125" or what is received from gateway to number
+	LoraDown.iiq = (ipol? 0x40: 0x27);					// if ipol==true 0x40 else 0x27
+	LoraDown.crc = 0x00;								// switch CRC off for TX
+	LoraDown.payLength = base64_dec_len((char *) data, strlen(data));// Length of the Payload data	
+	base64_decode((char *) payLoad, (char *) data, strlen(data));	// Fill payload w decoded message
+
+	// Compute wait time in microseconds
+	uint32_t w = (uint32_t) (LoraDown.tmst - micros());	// Wait Time compute
+
+// _STRICT_1CH determines ho we will react on downstream messages.
+// If STRICT==0, we will receive messags from the TTN gateway presumably on SF12/869.5MHz
+// And since the Gateway is a single channel gateway, and its nodes are probably
+// single channle too. They will not listen to that frequency.
+// When STRICT==1, we will answer (in the RX1 timeslot) on the frequency we receive on.
+//
+#if _STRICT_1CH == 1
+	// If possible use RX1 timeslot as this is our frequency.
+	// Do not use RX2 or JOIN2 as they contain other frequencies
+	
+	if ((w>1000000) && (w<3000000)) { 
+		LoraDown.tmst-=1000000; 
+	}	// Is tmst correction necessary
+	else if ((w>6000000) && (w<7000000)) { 
+		LoraDown.tmst-=500000; 
+	}
+	LoraDown.powe = 14;										// On all freqs except 869.5MHz power is limited
+	//LoraDown.sfTx = sfi;									// Take care, TX sf not to be mixed with SCAN
+	LoraDown.fff = freq;									// Use the current frequency
+#else
+	LoraDown.powe = powe;
+
+	// convert double frequency (MHz) into uint32_t frequency in Hz.
+	LoraDown.fff = (uint32_t) ((uint32_t)((ff+0.000035)*1000)) * 1000;
+#endif
+	
+	LoraDown.payLoad = payLoad;				
+
+#if DUSB>=1
+	if (( debug>=1 ) && ( pdebug & P_TX)) {
+	
+		Serial.print(F("T LoraDown tmst="));
+		Serial.print(LoraDown.tmst);
+		//Serial.print(F(", w="));
+		//Serial.print(w);
+		
+		if ( debug>=2 ) {
+			Serial.print(F(" Request:: "));
+			Serial.print(F(" tmst=")); Serial.print(LoraDown.tmst); Serial.print(F(" wait=")); Serial.println(w);
+		
+			Serial.print(F(" strict=")); Serial.print(_STRICT_1CH);
+			Serial.print(F(" datr=")); Serial.println(datr);
+			Serial.print(F(" Rfreq=")); Serial.print(freq); Serial.print(F(", Request=")); Serial.print(freq); Serial.print(F(" ->")); Serial.println(LoraDown.fff);
+			Serial.print(F(" sf  =")); Serial.print(atoi(datr+2)); Serial.print(F(" ->")); Serial.println(LoraDown.sfTx);
+		
+			Serial.print(F(" modu=")); Serial.println(modu);
+			Serial.print(F(" powe=")); Serial.println(powe);
+			Serial.print(F(" codr=")); Serial.println(codr);
+
+			Serial.print(F(" ipol=")); Serial.println(ipol);
+		}
+		Serial.println();
+	}
+#endif
+
+	if (LoraDown.payLength != psize) {
+#if DUSB>=1
+		Serial.print(F("sendPacket:: WARNING payLength: "));
+		Serial.print(LoraDown.payLength);
+		Serial.print(F(", psize="));
+		Serial.println(psize);
+		if (debug>=2) Serial.flush();
+#endif
+	}
+#if DUSB>=1
+	else if (( debug >= 2 ) && ( pdebug & P_TX )) {
+		Serial.print(F("T Payload="));
+		for (i=0; i<LoraDown.payLength; i++) {
+			Serial.print(payLoad[i],HEX); 
+			Serial.print(':'); 
+		}
+		Serial.println();
+		if (debug>=2) Serial.flush();
+	}
+#endif
+	cp_up_pkt_fwd++;
+
+#if DUSB>=1
+	if (( debug>=2 ) && ( pdebug & P_TX )) {
+		Serial.println(F("T sendPacket:: fini OK"));
+	}
+#endif // DUSB
+
+	// All data is in Payload and parameters and need to be transmitted.
+	// The function is called in user-space
+	_state = S_TX;										// _state set to transmit
+	
+	return 1;
+}//sendPacket
+
+
+
+
+// ----------------------------------------------------------------------------
+// UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP
+// Based on the information read from the LoRa transceiver (or fake message)
+// build a gateway message to send upstream (to the user somewhere on the web).
+//
+// parameters:
+// 	tmst: Timestamp to include in the upstream message
+// 	buff_up: The buffer that is generated for upstream
+// 	message: The payload message to include in the the buff_up
+//	messageLength: The number of bytes received by the LoRa transceiver
+// 	internal: Boolean value to indicate whether the local sensor is processed
+//
+// returns:
+//	buff_index
+// ----------------------------------------------------------------------------
+int buildPacket(uint32_t tmst, uint8_t *buff_up, struct LoraUp LoraUp, bool internal) 
+{
+	long SNR;
+    int rssicorr;
+	int prssi;											// packet rssi
+	
+	char cfreq[12] = {0};								// Character array to hold freq in MHz
+	//lastTmst = tmst;									// Following/according to spec
+	int buff_index=0;
+	char b64[256];
+	
+	uint8_t *message = LoraUp.payLoad;
+	char messageLength = LoraUp.payLength;
+		
+#if _CHECK_MIC==1
+	unsigned char NwkSKey[16] = _NWKSKEY;
+	checkMic(message, messageLength, NwkSKey);
+#endif // _CHECK_MIC
+
+	// Read SNR and RSSI from the register. Note: Not for internal sensors!
+	// For internal sensor we fake these values as we cannot read a register
+	if (internal) {
+		SNR = 12;
+		prssi = 50;
+		rssicorr = 157;
+	}
+	else {
+		SNR = LoraUp.snr;
+		prssi = LoraUp.prssi;								// read register 0x1A, packet rssi
+		rssicorr = LoraUp.rssicorr;
+	}
+
+#if STATISTICS >= 1
+	// Receive statistics, move old statistics down 1 position
+	// and fill the new top line with the latest received sensor values.
+	// This works fine for the sensor, EXCEPT when we decode data for _LOCALSERVER
+	//
+	for (int m=( MAX_STAT -1); m>0; m--) statr[m]=statr[m-1];
+	
+	// From now on we can fill start[0] with sensor data
+#if _LOCALSERVER==1
+	statr[0].datal=0;
+	int index;
+	if ((index = inDecodes((char *)(LoraUp.payLoad+1))) >=0 ) {
+
+		uint16_t frameCount=LoraUp.payLoad[7]*256 + LoraUp.payLoad[6];
+		
+		for (int k=0; (k<LoraUp.payLength) && (k<23); k++) {
+			statr[0].data[k] = LoraUp.payLoad[k+9];
+		};
+		
+		// XXX Check that k<23 when leaving the for loop
+		// XXX or we can not display in statr
+		
+		uint8_t DevAddr[4]; 
+		DevAddr[0]= LoraUp.payLoad[4];
+		DevAddr[1]= LoraUp.payLoad[3];
+		DevAddr[2]= LoraUp.payLoad[2];
+		DevAddr[3]= LoraUp.payLoad[1];
+
+		statr[0].datal = encodePacket((uint8_t *)(statr[0].data), 
+								LoraUp.payLength-9-4, 
+								(uint16_t)frameCount, 
+								DevAddr, 
+								decodes[index].appKey, 
+								0);
+	}
+#endif //_LOCALSERVER
+	statr[0].tmst = now();
+	statr[0].ch= ifreq;
+	statr[0].prssi = prssi - rssicorr;
+#if RSSI==1
+	statr[0].rssi = _rssi - rssicorr;
+#endif // RSII
+	statr[0].sf = LoraUp.sf;
+#if DUSB>=2
+	if (debug>=0) {
+		if ((message[4] != 0x26) || (message[1]==0x99)) {
+			Serial.print(F("addr="));
+			for (int i=messageLength; i>0; i--) {
+				if (message[i]<0x10) Serial.print('0');
+				Serial.print(message[i],HEX);
+				Serial.print(' ');
+			}
+			Serial.println();
+		}
+	}
+#endif //DUSB
+	statr[0].node = ( message[1]<<24 | message[2]<<16 | message[3]<<8 | message[4] );
+
+#if STATISTICS >= 2
+	// Fill in the statistics that we will also need for the GUI.
+	// So 
+	switch (statr[0].sf) {
+		case SF7: statc.sf7++; break;
+		case SF8: statc.sf8++; break;
+		case SF9: statc.sf9++; break;
+		case SF10: statc.sf10++; break;
+		case SF11: statc.sf11++; break;
+		case SF12: statc.sf12++; break;
+	}
+#endif //STATISTICS >= 2
+
+#if STATISTICS >= 3
+	if (statr[0].ch == 0) switch (statr[0].sf) {
+		case SF7: statc.sf7_0++; break;
+		case SF8: statc.sf8_0++; break;
+		case SF9: statc.sf9_0++; break;
+		case SF10: statc.sf10_0++; break;
+		case SF11: statc.sf11_0++; break;
+		case SF12: statc.sf12_0++; break;
+	}
+	else 
+	if (statr[0].ch == 1) switch (statr[0].sf) {
+		case SF7: statc.sf7_1++; break;
+		case SF8: statc.sf8_1++; break;
+		case SF9: statc.sf9_1++; break;
+		case SF10: statc.sf10_1++; break;
+		case SF11: statc.sf11_1++; break;
+		case SF12: statc.sf12_1++; break;
+	}
+	else 
+	if (statr[0].ch == 2) switch (statr[0].sf) {
+		case SF7: statc.sf7_2++; break;
+		case SF8: statc.sf8_2++; break;
+		case SF9: statc.sf9_2++; break;
+		case SF10: statc.sf10_2++; break;
+		case SF11: statc.sf11_2++; break;
+		case SF12: statc.sf12_2++; break;
+	}
+#endif //STATISTICS >= 3
+
+#endif //STATISTICS >= 2
+
+#if DUSB>=1	
+	if (( debug>=2 ) && ( pdebug & P_RADIO )){
+		Serial.print(F("R buildPacket:: pRSSI="));
+		Serial.print(prssi-rssicorr);
+		Serial.print(F(" RSSI: "));
+		Serial.print(_rssi - rssicorr);
+		Serial.print(F(" SNR: "));
+		Serial.print(SNR);
+		Serial.print(F(" Length: "));
+		Serial.print((int)messageLength);
+		Serial.print(F(" -> "));
+		int i;
+		for (i=0; i< messageLength; i++) {
+					Serial.print(message[i],HEX);
+					Serial.print(' ');
+		}
+		Serial.println();
+		yield();
+	}
+#endif // DUSB
+
+// Show received message status on OLED display
+#if OLED>=1
+    char timBuff[20];
+    sprintf(timBuff, "%02i:%02i:%02i", hour(), minute(), second());
+	
+    display.clear();
+    display.setFont(ArialMT_Plain_16);
+    display.setTextAlignment(TEXT_ALIGN_LEFT);
+
+//	msg_oLED(timBuff, prssi-rssicorr, SNR, message)
+	
+    display.drawString(0, 0, "Time: " );
+    display.drawString(40, 0, timBuff);
+	
+    display.drawString(0, 16, "RSSI: " );
+    display.drawString(40, 16, String(prssi-rssicorr));
+	
+    display.drawString(70, 16, ",SNR: " );
+    display.drawString(110, 16, String(SNR) );
+	  
+	display.drawString(0, 32, "Addr: " );
+	  
+    if (message[4] < 0x10) display.drawString( 40, 32, "0"+String(message[4], HEX)); else display.drawString( 40, 32, String(message[4], HEX));
+	if (message[3] < 0x10) display.drawString( 61, 32, "0"+String(message[3], HEX)); else display.drawString( 61, 32, String(message[3], HEX));
+	if (message[2] < 0x10) display.drawString( 82, 32, "0"+String(message[2], HEX)); else display.drawString( 82, 32, String(message[2], HEX));
+	if (message[1] < 0x10) display.drawString(103, 32, "0"+String(message[1], HEX)); else display.drawString(103, 32, String(message[1], HEX));
+	  
+    display.drawString(0, 48, "LEN: " );
+    display.drawString(40, 48, String((int)messageLength) );
+    display.display();
+	//yield();
+
+#endif //OLED>=1
+			
+	int j;
+	
+	// XXX Base64 library is nopad. So we may have to add padding characters until
+	// 	message Length is multiple of 4!
+	// Encode message with messageLength into b64
+	int encodedLen = base64_enc_len(messageLength);		// max 341
+#if DUSB>=1
+	if ((debug>=1) && (encodedLen>255) && ( pdebug & P_RADIO )) {
+		Serial.print(F("R buildPacket:: b64 err, len="));
+		Serial.println(encodedLen);
+		if (debug>=2) Serial.flush();
+		return(-1);
+	}
+#endif // DUSB
+	base64_encode(b64, (char *) message, messageLength);// max 341
+	// start composing datagram with the header 
+	uint8_t token_h = (uint8_t)rand(); 					// random token
+	uint8_t token_l = (uint8_t)rand(); 					// random token
+	
+	// pre-fill the data buffer with fixed fields
+	buff_up[0] = PROTOCOL_VERSION;						// 0x01 still
+
+
+	buff_up[1] = token_h;
+	buff_up[2] = token_l;
+	
+	buff_up[3] = PKT_PUSH_DATA;							// 0x00
+	
+	// READ MAC ADDRESS OF ESP8266, and insert 0xFF 0xFF in the middle
+	buff_up[4]  = MAC_array[0];
+	buff_up[5]  = MAC_array[1];
+	buff_up[6]  = MAC_array[2];
+	buff_up[7]  = 0xFF;
+	buff_up[8]  = 0xFF;
+	buff_up[9]  = MAC_array[3];
+	buff_up[10] = MAC_array[4];
+	buff_up[11] = MAC_array[5];
+
+
+	buff_index = 12; 									// 12-byte binary (!) header
+
+	// start of JSON structure that will make payload
+	memcpy((void *)(buff_up + buff_index), (void *)"{\"rxpk\":[", 9);
+	buff_index += 9;
+	buff_up[buff_index] = '{';
+	++buff_index;
+	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, "\"tmst\":%u", tmst);
+#if DUSB>=1
+	if ((j<0) && ( debug>=1 ) && ( pdebug & P_RADIO )) {
+		Serial.println(F("buildPacket:: Error "));
+	}
+#endif
+	buff_index += j;
+	ftoa((double)freq/1000000,cfreq,6);					// XXX This can be done better
+	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"chan\":%1u,\"rfch\":%1u,\"freq\":%s", 0, 0, cfreq);
+	buff_index += j;
+	memcpy((void *)(buff_up + buff_index), (void *)",\"stat\":1", 9);
+	buff_index += 9;
+	memcpy((void *)(buff_up + buff_index), (void *)",\"modu\":\"LORA\"", 14);
+	buff_index += 14;
+	
+	/* Lora datarate & bandwidth, 16-19 useful chars */
+	switch (LoraUp.sf) {
+		case SF6:
+			memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF6", 12);
+			buff_index += 12;
+			break;
+		case SF7:
+			memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF7", 12);
+			buff_index += 12;
+			break;
+		case SF8:
+            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF8", 12);
+            buff_index += 12;
+            break;
+		case SF9:
+            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF9", 12);
+            buff_index += 12;
+            break;
+		case SF10:
+            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF10", 13);
+            buff_index += 13;
+            break;
+		case SF11:
+            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF11", 13);
+            buff_index += 13;
+            break;
+		case SF12:
+            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF12", 13);
+            buff_index += 13;
+            break;
+		default:
+            memcpy((void *)(buff_up + buff_index), (void *)",\"datr\":\"SF?", 12);
+            buff_index += 12;
+	}
+	memcpy((void *)(buff_up + buff_index), (void *)"BW125\"", 6);
+	buff_index += 6;
+	memcpy((void *)(buff_up + buff_index), (void *)",\"codr\":\"4/5\"", 13);
+	buff_index += 13;
+	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"lsnr\":%li", SNR);
+	buff_index += j;
+	j = snprintf((char *)(buff_up + buff_index), TX_BUFF_SIZE-buff_index, ",\"rssi\":%d,\"size\":%u", prssi-rssicorr, messageLength);
+	buff_index += j;
+	memcpy((void *)(buff_up + buff_index), (void *)",\"data\":\"", 9);
+	buff_index += 9;
+
+	// Use gBase64 library to fill in the data string
+	encodedLen = base64_enc_len(messageLength);			// max 341
+	j = base64_encode((char *)(buff_up + buff_index), (char *) message, messageLength);
+
+	buff_index += j;
+	buff_up[buff_index] = '"';
+	++buff_index;
+
+	// End of packet serialization
+	buff_up[buff_index] = '}';
+	++buff_index;
+	buff_up[buff_index] = ']';
+	++buff_index;
+	
+	// end of JSON datagram payload */
+	buff_up[buff_index] = '}';
+	++buff_index;
+	buff_up[buff_index] = 0; 							// add string terminator, for safety
+
+#if STAT_LOG == 1	
+	// Do statistics logging. In first version we might only
+	// write part of the record to files, later more
+
+	addLog( (unsigned char *)(buff_up), buff_index );
+#endif	
+	
+#if DUSB>=1
+	if (( debug>=2 ) && ( pdebug & P_RX )) {
+		Serial.print(F("R RXPK:: "));
+		Serial.println((char *)(buff_up + 12));			// debug: display JSON payload
+		Serial.print(F("R RXPK:: package length="));
+		Serial.println(buff_index);
+	}
+#endif
+	return(buff_index);
+}// buildPacket
+
+
+
+
+
+// ----------------------------------------------------------------------------
+// UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP UP 
+// Receive a LoRa package over the air, LoRa and deliver to server(s)
+//
+// Receive a LoRa message and fill the buff_up char buffer.
+// returns values:
+// - returns the length of string returned in buff_up
+// - returns -1 or -2 when no message arrived, depending connection.
+//
+// This is the "highlevel" function called by loop()
+// ----------------------------------------------------------------------------
+int receivePacket()
+{
+	uint8_t buff_up[TX_BUFF_SIZE]; 						// buffer to compose the upstream packet to backend server
+	long SNR;
+	uint8_t message[128] = { 0x00 };					// MSG size is 128 bytes for rx
+	uint8_t messageLength = 0;
+	
+	// Regular message received, see SX1276 spec table 18
+	// Next statement could also be a "while" to combine several messages received
+	// in one UDP message as the Semtech Gateway spec does allow this.
+	// XXX Not yet supported
+
+		// Take the timestamp as soon as possible, to have accurate reception timestamp
+		// TODO: tmst can jump if micros() overflow.
+		uint32_t tmst = (uint32_t) micros();			// Only microseconds, rollover in 5X minutes
+		//lastTmst = tmst;								// Following/according to spec
+		
+		// Handle the physical data read from LoraUp
+		if (LoraUp.payLength > 0) {
+
+			// externally received packet, so last parameter is false (==LoRa external)
+            int build_index = buildPacket(tmst, buff_up, LoraUp, false);
+
+			// REPEATER is a special function where we retransmit received 
+			// message on _ICHANN to _OCHANN.
+			// Note:: For the moment _OCHANN is not allowed to be same as _ICHANN
+#if REPEATER==1
+			if (!sendLora(LoraUp.payLoad, LoraUp.payLength)) {
+				return(-3);
+			}
+#endif
+			
+			// This is one of the potential problem areas.
+			// If possible, USB traffic should be left out of interrupt routines
+			// rxpk PUSH_DATA received from node is rxpk (*2, par. 3.2)
+#ifdef _TTNSERVER
+			if (!sendUdp(ttnServer, _TTNPORT, buff_up, build_index)) {
+				return(-1); 							// received a message
+			}
+			yield();
+#endif
+			// Use our own defined server or a second well kon server
+#ifdef _THINGSERVER
+			if (!sendUdp(thingServer, _THINGPORT, buff_up, build_index)) {
+				return(-2); 							// received a message
+			}
+#endif
+
+#if _LOCALSERVER==1
+			// Or special case, we do not use a local server to receive
+			// and decode the server. We use buildPacket() to call decode
+			// and use statr[0] information to store decoded message
+
+			//DecodePayload: para 4.3.1 of Lora 1.1 Spec
+			// MHDR
+			//	1 byte			Payload[0]
+			// FHDR
+			// 	4 byte Dev Addr Payload[1-4]
+			// 	1 byte FCtrl  	Payload[5]
+			// 	2 bytes FCnt	Payload[6-7]				
+			// 		= Optional 0 to 15 bytes Options
+			// FPort
+			//	1 bytes, 0x00	Payload[8]
+			// ------------
+			// +=9 BYTES HEADER
+			//
+			// FRMPayload
+			//	N bytes			(Payload )
+			//
+			// 4 bytes MIC trailer
+
+			int index=0;
+			if ((index = inDecodes((char *)(LoraUp.payLoad+1))) >=0 ) {
+
+				uint8_t DevAddr[4]; 
+				DevAddr[0]= LoraUp.payLoad[4];
+				DevAddr[1]= LoraUp.payLoad[3];
+				DevAddr[2]= LoraUp.payLoad[2];
+				DevAddr[3]= LoraUp.payLoad[1];
+				uint16_t frameCount=LoraUp.payLoad[7]*256 + LoraUp.payLoad[6];
+
+#if DUSB>=1
+				if (( debug>=1 ) && ( pdebug & P_RX )) {
+					Serial.print(F("R receivePacket:: Ind="));
+					Serial.print(index);
+					Serial.print(F(", Len="));
+					Serial.print(LoraUp.payLength);
+					Serial.print(F(", A="));
+					for (int i=0; i<4; i++) {
+						if (DevAddr[i]<0x0F) Serial.print('0');
+						Serial.print(DevAddr[i],HEX);
+						//Serial.print(' ');
+					}
+				
+					Serial.print(F(", Msg="));
+					for (int i=0; (i<statr[0].datal) && (i<23); i++) {
+						if (statr[0].data[i]<0x0F) Serial.print('0');
+						Serial.print(statr[0].data[i],HEX);
+						Serial.print(' ');
+					}
+					Serial.println();
+				}
+			}
+			else if (( debug>=2 ) && ( pdebug & P_RX )) {
+					Serial.println(F("receivePacket:: No Index"));
+			}
+#endif //DUSB
+#endif // _LOCALSERVER
+
+			// Reset the message area
+			LoraUp.payLength = 0;
+			LoraUp.payLoad[0] = 0x00;
+			return(build_index);
+        }
+		
+	return(0);											// failure no message read
+	
+}//receivePacket
+
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_utils.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_utils.ino
new file mode 100644
index 0000000000000000000000000000000000000000..37ea3046e59be643007a02fe45b8e5e11facb4ed
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_utils.ino
@@ -0,0 +1,217 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains the utilities for time and other functions
+// ========================================================================================
+
+// ----------------------------------------------------------------------------
+// Fill a HEXadecimal String  from a 4-byte char array
+//
+// ----------------------------------------------------------------------------
+static void printHEX(char * hexa, const char sep, String& response) 
+{
+	char m;
+	m = hexa[0]; if (m<016) response+='0'; response += String(m, HEX);  response+=sep;
+	m = hexa[1]; if (m<016) response+='0'; response += String(m, HEX);  response+=sep;
+	m = hexa[2]; if (m<016) response+='0'; response += String(m, HEX);  response+=sep;
+	m = hexa[3]; if (m<016) response+='0'; response += String(m, HEX);  response+=sep;
+}
+
+// ----------------------------------------------------------------------------
+// stringTime
+// Print the time t into the String reponse. t is of type time_t in seconds.
+// Only when RTC is present we print real time values
+// t contains number of seconds since system started that the event happened.
+// So a value of 100 would mean that the event took place 1 minute and 40 seconds ago
+// ----------------------------------------------------------------------------
+static void stringTime(time_t t, String& response) {
+
+	if (t==0) { response += "--"; return; }
+	
+	// now() gives seconds since 1970
+	// as millis() does rotate every 50 days
+	// So we need another timing parameter
+	time_t eTime = t;
+	
+	// Rest is standard
+	byte _hour   = hour(eTime);
+	byte _minute = minute(eTime);
+	byte _second = second(eTime);
+	
+	switch(weekday(eTime)) {
+		case 1: response += "Sunday "; break;
+		case 2: response += "Monday "; break;
+		case 3: response += "Tuesday "; break;
+		case 4: response += "Wednesday "; break;
+		case 5: response += "Thursday "; break;
+		case 6: response += "Friday "; break;
+		case 7: response += "Saturday "; break;
+	}
+	response += String() + day(eTime) + "-";
+	response += String() + month(eTime) + "-";
+	response += String() + year(eTime) + " ";
+	
+	if (_hour < 10) response += "0";
+	response += String() + _hour + ":";
+	if (_minute < 10) response += "0";
+	response += String() + _minute + ":";
+	if (_second < 10) response += "0";
+	response += String() + _second;
+}
+
+
+// ----------------------------------------------------------------------------
+// SerialTime
+// Print the current time on the Serial (USB), with leading 0.
+// ----------------------------------------------------------------------------
+void SerialTime() 
+{
+
+	uint32_t thrs = hour();
+	uint32_t tmin = minute();
+	uint32_t tsec = second();
+			
+	if (thrs<10) Serial.print('0'); Serial.print(thrs);
+	Serial.print(':');
+	if (tmin<10) Serial.print('0'); Serial.print(tmin);
+	Serial.print(':');
+	if (tsec<10) Serial.print('0'); Serial.print(tsec);
+			
+	if (debug>=2) Serial.flush();
+		
+	return;
+}
+
+// ----------------------------------------------------------------------------
+// SerialStat
+// Print the statistics on Serial (USB) port
+// ----------------------------------------------------------------------------
+
+void SerialStat(uint8_t intr) 
+{
+#if DUSB>=1
+	if (debug>=0) {
+		Serial.print(F("I="));
+
+		if (intr & IRQ_LORA_RXTOUT_MASK) Serial.print(F("RXTOUT "));		// 0x80
+		if (intr & IRQ_LORA_RXDONE_MASK) Serial.print(F("RXDONE "));		// 0x40
+		if (intr & IRQ_LORA_CRCERR_MASK) Serial.print(F("CRCERR "));		// 0x20
+		if (intr & IRQ_LORA_HEADER_MASK) Serial.print(F("HEADER "));		// 0x10
+		if (intr & IRQ_LORA_TXDONE_MASK) Serial.print(F("TXDONE "));		// 0x08
+		if (intr & IRQ_LORA_CDDONE_MASK) Serial.print(F("CDDONE "));		// 0x04
+		if (intr & IRQ_LORA_FHSSCH_MASK) Serial.print(F("FHSSCH "));		// 0x02
+		if (intr & IRQ_LORA_CDDETD_MASK) Serial.print(F("CDDETD "));		// 0x01
+
+		if (intr == 0x00) Serial.print(F("  --  "));
+			
+		Serial.print(F(", F="));
+		Serial.print(ifreq);
+		Serial.print(F(", SF="));
+		Serial.print(sf);
+		Serial.print(F(", E="));
+		Serial.print(_event);
+			
+		Serial.print(F(", S="));
+		//Serial.print(_state);
+		switch (_state) {
+			case S_INIT:
+				Serial.print(F("INIT "));
+			break;
+			case S_SCAN:
+				Serial.print(F("SCAN "));
+			break;
+			case S_CAD:
+				Serial.print(F("CAD  "));
+			break;
+			case S_RX:
+				Serial.print(F("RX   "));
+			break;
+			case S_TX:
+				Serial.print(F("TX   "));
+			break;
+			case S_TXDONE:
+				Serial.print(F("TXDONE"));
+			break;
+			default:
+				Serial.print(F(" -- "));
+		}
+		Serial.print(F(", eT="));
+		Serial.print( micros() - eventTime );
+		Serial.print(F(", dT="));
+		Serial.print( micros() - doneTime );
+		Serial.println();
+	}
+#endif
+}
+
+
+	
+// ----------------------------------------------------------------------------
+// SerialName(id, response)
+// Check whether for address a (4 bytes in Unsigned Long) there is a name
+// This function only works if _TRUSTED_NODES is set
+// ----------------------------------------------------------------------------
+
+int SerialName(char * a, String& response)
+{
+#if _TRUSTED_NODES>=1
+	uint32_t id = ((a[0]<<24) | (a[1]<<16) | (a[2]<<8) | a[3]);
+
+	int i;
+	for ( i=0; i< (sizeof(nodes)/sizeof(nodex)); i++) {
+
+		if (id == nodes[i].id) {
+#if DUSB >=1
+			if (( debug>=3 ) && ( pdebug & P_GUI )) {
+				Serial.print(F("G Name="));
+				Serial.print(nodes[i].nm);
+				Serial.print(F(" for node=0x"));
+				Serial.print(nodes[i].id,HEX);
+				Serial.println();
+			}
+#endif
+			response += nodes[i].nm;
+			return(i);
+		}
+	}
+
+#endif
+	return(-1);									// If no success OR is TRUSTED NODES not defined
+}
+
+#if _LOCALSERVER==1
+// ----------------------------------------------------------------------------
+// inDecodes(id)
+// Find the id in Decodes array, and return the index of the item
+// Parameters:
+//		id: The first field in the array (normally DevAddr id). Must be char[4]
+// Returns:
+//		The index of the ID in the Array. Returns -1 if not found
+// ----------------------------------------------------------------------------
+int inDecodes(char * id) {
+
+	uint32_t ident = ((id[3]<<24) | (id[2]<<16) | (id[1]<<8) | id[0]);
+
+	int i;
+	for ( i=0; i< (sizeof(decodes)/sizeof(codex)); i++) {
+		if (ident == decodes[i].id) {
+			return(i);
+		}
+	}
+	return(-1);
+}
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_wwwServer.ino b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_wwwServer.ino
new file mode 100644
index 0000000000000000000000000000000000000000..c89fb97fec7e26571cf66b7c56cf5b44c99ebeb9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/_wwwServer.ino
@@ -0,0 +1,1405 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// 	based on work done by many people and making use of several libraries.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains the webserver code for the ESP Single Channel Gateway.
+
+// Note:
+// The ESP Webserver works with Strings to display html content. 
+// Care must be taken that not all data is output to the webserver in one string
+// as this will use a LOT of memory and possibly kill the heap (cause system
+// crash or other unreliable behaviour.
+// Instead, output of the various tables on the webpage should be displayed in
+// chucks so that strings are limited in size.
+// Be aware that using no strings but only sendContent() calls has its own
+// disadvantage that these calls take a lot of time and cause the page to be
+// displayed like an old typewriter.
+// So, the trick is to make chucks that are sent to the website by using
+// a response String but not make those Strings too big.
+//
+// Also, selecting too many options for Statistics, display, Hopping channels
+// etc makes the gateway more sluggish and may impact the availabile memory and 
+// thus its performance and reliability. It's up to the uer to select wisely!
+//
+
+
+// ----------------------------------------------------------------------------
+// PRINT IP
+// Output the 4-byte IP address for easy printing.
+// As this function is also used by _otaServer.ino do not put in #define
+// ----------------------------------------------------------------------------
+static void printIP(IPAddress ipa, const char sep, String& response)
+{
+	response+=(IPAddress)ipa[0]; response+=sep;
+	response+=(IPAddress)ipa[1]; response+=sep;
+	response+=(IPAddress)ipa[2]; response+=sep;
+	response+=(IPAddress)ipa[3];
+}
+
+//
+// The remainder of the file ONLY works is A_SERVER=1 is set.
+//
+#if A_SERVER==1
+
+// ================================================================================
+// WEBSERVER DECLARATIONS 
+// ================================================================================
+
+// None at the moment
+
+// ================================================================================
+// WEBSERVER FUNCTIONS 
+// ================================================================================
+
+
+// ----------------------------------------------------------------------------
+// Used by all functions requiring user confirmation
+// Displays a menu by user and two buttons "OK" and "CANCEL"
+// The function returns true for OK and false for CANCEL
+// Usage: Call this function ONCE during startup to declare and init
+// the ynDialog JavaScript function, and call the function
+// from the program when needed.
+// Paramters of the JavaScript function:
+//	s: Th strig contining the question to be answered
+//	o: The OK tab for the webpage where to go to
+//	c: The Cancel string (optional)
+// ----------------------------------------------------------------------------
+boolean YesNo()
+{
+	boolean ret = false;
+	String response = "";
+	response += "<script>";
+	
+	response += "var ch = \"\"; ";								// Init ch oice
+	response += "function ynDialog(s,y) {";
+	response += "  try { adddlert(s); }";
+	response += "  catch(err) {";
+	response += "    ch  = \" \" + s + \".\\n\\n\"; ";
+	response += "    ch += \"Click OK to continue,\\n\"; ";
+	response += "    ch += \"or Cancel\\n\\n\"; ";
+	response += "    if(!confirm(ch)) { ";
+	response += "      javascript:window.location.reload(true);";
+	response += "    } ";
+	response += "    else { ";
+	response += "      document.location.href = '/'+y; ";
+	response += "    } ";
+	response += "  }";
+	response += "}";
+	response += "</script>";
+	server.sendContent(response);
+	
+// Put something like this in the ESP program
+//	response += "<input type=\"button\" value=\"YesNo\" onclick=\"ynDialog()\" />";
+	
+	return(ret);
+}
+
+
+// ----------------------------------------------------------------------------
+// WWWFILE
+// This function will open a pop-up in the browser and then 
+//	display the contents of a file in that window
+// Output is sent to server.sendContent()
+// Parameters:
+//		fn; String with filename
+// Returns:
+//		<none>
+// ----------------------------------------------------------------------------
+void wwwFile(String fn) {
+
+	if (!SPIFFS.exists(fn)) {
+#if DUSB>=1
+		Serial.print(F("wwwFile:: ERROR: file not found="));
+		Serial.println(fn);
+#endif
+		return;
+	}
+#if DUSB>=2
+	else {
+		Serial.print(F("wwwFile:: File existist= "));
+		Serial.println(fn);
+	}
+#endif
+
+#if DUSB>=1
+		File f = SPIFFS.open(fn, "r");					// Open the file for reading
+		
+		int j;
+		for (j=0; j<LOGFILEREC; j++) {
+			
+			String s=f.readStringUntil('\n');
+			if (s.length() == 0) {
+				Serial.print(F("wwwFile:: String length 0"));
+				break;
+			}
+			server.sendContent(s.substring(12));		// Skip the first 12 Gateway specific binary characters
+			server.sendContent("\n");
+			yield();
+		}
+#endif
+	
+}
+
+// ----------------------------------------------------------------------------
+// Button function Docu, display the documentation pages.
+// This is a button on the top of the GUI screen.
+// ----------------------------------------------------------------------------
+void buttonDocu() 
+{
+
+	String response = "";
+	response += "<script>";
+	
+	response += "var txt = \"\";";
+	response += "function showDocu() {";
+	response += "  try { adddlert(\"Welcome,\"); }";
+	response += "  catch(err) {";
+	response += "    txt  = \"Do you want the documentation page.\\n\\n\"; ";
+	response += "    txt += \"Click OK to continue viewing documentation,\\n\"; ";
+	response += "    txt += \"or Cancel to return to the home page.\\n\\n\"; ";
+	response += "    if(confirm(txt)) { ";
+	response += "      document.location.href = \"https://things4u.github.io/UserGuide/One%20Channel%20Gateway/Introduction%205.html\"; ";
+	response += "    }";
+	response += "  }";
+	response += "}";
+	
+	response += "</script>";
+	server.sendContent(response);
+}
+
+
+
+
+// ----------------------------------------------------------------------------
+// Button function Log displays  logfiles.
+// This is a button on the top of the GUI screen
+// ----------------------------------------------------------------------------
+void buttonLog() 
+{
+	
+	String response = "";
+	String fn = "";
+	int i = 0;
+	
+	while (i< LOGFILEMAX ) {
+		fn = "/log-" + String(gwayConfig.logFileNo - i);
+		wwwFile(fn);									// Display the file contents in the browser
+		i++;
+	}
+	
+	server.sendContent(response);
+}
+
+// ----------------------------------------------------------------------------
+// Navigate webpage by buttons. This method has some advantages:
+// - Less time/cpu usage
+// - Less memory usage		<a href=\"SPEED=160\">
+// ----------------------------------------------------------------------------
+static void wwwButtons()
+{
+	String response = "";
+	String mode = (gwayConfig.expert ? "Basic Mode" : "Expert Mode");
+
+	YesNo();												// Init the Yes/No function
+	buttonDocu();
+
+	response += "<input type=\"button\" value=\"Documentation\" onclick=\"showDocu()\" >";
+	
+	response += "<a href=\"EXPERT\" download><button type=\"button\">" + mode + "</button></a>";
+
+	response += "<a href=\"LOG\" download><button type=\"button\">Log Files</button></a>";
+
+	server.sendContent(response);							// Send to the screen
+}
+
+
+// ----------------------------------------------------------------------------
+// SET ESP8266 WEB SERVER VARIABLES
+//
+// This funtion implements the WiFI Webserver (very simple one). The purpose
+// of this server is to receive simple admin commands, and execute these
+// results are sent back to the web client.
+// Commands: DEBUG, ADDRESS, IP, CONFIG, GETTIME, SETTIME
+// The webpage is completely built response and then printed on screen.
+// Parameters:
+//		cmd: Contains a character array with the command to execute
+//		arg: Contains the parameter value of that command
+// Returns:
+//		<none>
+// ----------------------------------------------------------------------------
+static void setVariables(const char *cmd, const char *arg) {
+
+	// DEBUG settings; These can be used as a single argument
+	if (strcmp(cmd, "DEBUG")==0) {									// Set debug level 0-2
+		if (atoi(arg) == 1) {
+			debug = (debug+1)%4;
+		}	
+		else if (atoi(arg) == -1) {
+			debug = (debug+3)%4;
+		}
+		writeGwayCfg(CONFIGFILE);									// Save configuration to file
+	}
+	
+	if (strcmp(cmd, "CAD")==0) {									// Set -cad on=1 or off=0
+		_cad=(bool)atoi(arg);
+		writeGwayCfg(CONFIGFILE);									// Save configuration to file
+	}
+	
+	if (strcmp(cmd, "HOP")==0) {									// Set -hop on=1 or off=0
+		_hop=(bool)atoi(arg);
+		if (! _hop) { 
+			ifreq=0; 
+			freq=freqs[ifreq]; 
+			rxLoraModem();
+			sf = SF7;
+			cadScanner();
+		}
+		writeGwayCfg(CONFIGFILE);									// Save configuration to file
+	}
+	
+	if (strcmp(cmd, "DELAY")==0) {									// Set delay usecs
+		txDelay+=atoi(arg)*1000;
+	}
+	
+	// SF; Handle Spreading Factor Settings
+	if (strcmp(cmd, "SF")==0) {
+		uint8_t sfi = sf;
+		if (atoi(arg) == 1) {
+			if (sf>=SF12) sf=SF7; else sf= (sf_t)((int)sf+1);
+		}	
+		else if (atoi(arg) == -1) {
+			if (sf<=SF7) sf=SF12; else sf= (sf_t)((int)sf-1);
+		}
+		rxLoraModem();												// Reset the radion with the new spreading factor
+		writeGwayCfg(CONFIGFILE);									// Save configuration to file
+	}
+	
+	// FREQ; Handle Frequency  Settings
+	if (strcmp(cmd, "FREQ")==0) {
+		uint8_t nf = sizeof(freqs)/sizeof(int);						// Number of elements in array
+		
+		// Compute frequency index
+		if (atoi(arg) == 1) {
+			if (ifreq==(nf-1)) ifreq=0; else ifreq++;
+		}
+		else if (atoi(arg) == -1) {
+			Serial.println("down");
+			if (ifreq==0) ifreq=(nf-1); else ifreq--;
+		}
+
+		freq = freqs[ifreq];
+		rxLoraModem();												// Reset the radion with the new frequency
+		writeGwayCfg(CONFIGFILE);									// Save configuration to file
+	}
+
+	//if (strcmp(cmd, "GETTIME")==0) { Serial.println(F("gettime tbd")); }	// Get the local time
+	
+	//if (strcmp(cmd, "SETTIME")==0) { Serial.println(F("settime tbd")); }	// Set the local time
+	
+	if (strcmp(cmd, "HELP")==0)    { Serial.println(F("Display Help Topics")); }
+	
+#if GATEWAYNODE==1
+	if (strcmp(cmd, "NODE")==0) {									// Set node on=1 or off=0
+		gwayConfig.isNode =(bool)atoi(arg);
+		writeGwayCfg(CONFIGFILE);									// Save configuration to file
+	}
+	
+	if (strcmp(cmd, "FCNT")==0)   { 
+		frameCount=0; 
+		rxLoraModem();												// Reset the radion with the new frequency
+		writeGwayCfg(CONFIGFILE);
+	}
+#endif
+	
+#if WIFIMANAGER==1
+	if (strcmp(cmd, "NEWSSID")==0) { 
+		WiFiManager wifiManager;
+		strcpy(wpa[0].login,""); 
+		strcpy(wpa[0].passw,"");
+		WiFi.disconnect();
+		wifiManager.autoConnect(AP_NAME, AP_PASSWD );
+	}
+#endif
+
+#if A_OTA==1
+	if (strcmp(cmd, "UPDATE")==0) {
+		if (atoi(arg) == 1) {
+			updateOtaa();
+		}
+	}
+#endif
+
+#if A_REFRESH==1
+	if (strcmp(cmd, "REFR")==0) {									// Set refresh on=1 or off=0
+		gwayConfig.refresh =(bool)atoi(arg);
+		writeGwayCfg(CONFIGFILE);									// Save configuration to file
+	}
+#endif
+
+}
+
+
+// ----------------------------------------------------------------------------
+// OPEN WEB PAGE
+//	This is the init function for opening the webpage
+//
+// ----------------------------------------------------------------------------
+static void openWebPage()
+{
+	++gwayConfig.views;									// increment number of views
+#if A_REFRESH==1
+	//server.client().stop();							// Experimental, stop webserver in case something is still running!
+#endif
+	String response="";	
+
+	server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
+	server.sendHeader("Pragma", "no-cache");
+	server.sendHeader("Expires", "-1");
+	
+	// init webserver, fill the webpage
+	// NOTE: The page is renewed every _WWW_INTERVAL seconds, please adjust in ESP-sc-gway.h
+	//
+	server.setContentLength(CONTENT_LENGTH_UNKNOWN);
+	server.send(200, "text/html", "");
+#if A_REFRESH==1
+	if (gwayConfig.refresh) {
+		response += String() + "<!DOCTYPE HTML><HTML><HEAD><meta http-equiv='refresh' content='"+_WWW_INTERVAL+";http://";
+		printIP((IPAddress)WiFi.localIP(),'.',response);
+		response += "'><TITLE>ESP8266 1ch Gateway</TITLE>";
+	}
+	else {
+		response += String() + "<!DOCTYPE HTML><HTML><HEAD><TITLE>ESP8266 1ch Gateway</TITLE>";
+	}
+#else
+	response += String() + "<!DOCTYPE HTML><HTML><HEAD><TITLE>ESP8266 1ch Gateway</TITLE>";
+#endif
+	response += "<META HTTP-EQUIV='CONTENT-TYPE' CONTENT='text/html; charset=UTF-8'>";
+	response += "<META NAME='AUTHOR' CONTENT='M. Westenberg (mw1554@hotmail.com)'>";
+
+	response += "<style>.thead {background-color:green; color:white;} ";
+	response += ".cell {border: 1px solid black;}";
+	response += ".config_table {max_width:100%; min-width:400px; width:98%; border:1px solid black; border-collapse:collapse;}";
+	response += "</style></HEAD><BODY>";
+	
+	response +="<h1>ESP Gateway Config</h1>";
+
+	response +="<p style='font-size:10px;'>";
+	response +="Version: "; response+=VERSION;
+	response +="<br>ESP alive since "; 					// STARTED ON
+	stringTime(startTime, response);
+
+	response +=", Uptime: ";							// UPTIME
+	uint32_t secs = millis()/1000;
+	uint16_t days = secs / 86400;						// Determine number of days
+	uint8_t _hour   = hour(secs);
+	uint8_t _minute = minute(secs);
+	uint8_t _second = second(secs);
+	response += String() + days + "-";
+	if (_hour < 10) response += "0";
+	response += String() + _hour + ":";
+	if (_minute < 10) response += "0";
+	response += String() + _minute + ":";
+	if (_second < 10) response += "0";
+	response += String() + _second;
+	
+	response +="<br>Current time    "; 					// CURRENT TIME
+	stringTime(now(), response);
+	response +="<br>";
+	response +="</p>";
+	
+	server.sendContent(response);
+}
+
+
+// ----------------------------------------------------------------------------
+// CONFIG AND SETTINGS DATA
+//
+//
+// ----------------------------------------------------------------------------
+static void settingsData() 
+{
+	String response="";
+	String bg="";
+	
+	response +="<h2>Gateway Settings</h2>";
+	
+	response +="<table class=\"config_table\">";
+	response +="<tr>";
+	response +="<th class=\"thead\">Setting</th>";
+	response +="<th colspan=\"2\" style=\"background-color: green; color: white; width:120px;\">Value</th>";
+	response +="<th colspan=\"4\" style=\"background-color: green; color: white; width:100px;\">Set</th>";
+	response +="</tr>";
+	
+	bg = " background-color: ";
+	bg += ( _cad ? "LightGreen" : "orange" );
+	response +="<tr><td class=\"cell\">CAD</td>";
+	response +="<td colspan=\"2\" style=\"border: 1px solid black;"; response += bg; response += "\">";
+	response += ( _cad ? "ON" : "OFF" );
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"CAD=1\"><button>ON</button></a></td>";
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"CAD=0\"><button>OFF</button></a></td>";
+	response +="</tr>";
+	
+	bg = " background-color: ";
+	bg += ( _hop ? "LightGreen" : "orange" );
+	response +="<tr><td class=\"cell\">HOP</td>";
+	response +="<td colspan=\"2\" style=\"border: 1px solid black;"; response += bg; response += "\">";
+	response += ( _hop ? "ON" : "OFF" );
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"HOP=1\"><button>ON</button></a></td>";
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"HOP=0\"><button>OFF</button></a></td>";
+	response +="</tr>";
+	
+	response +="<tr><td class=\"cell\">SF Setting</td><td class=\"cell\" colspan=\"2\">";
+	if (_cad) {
+		response += "AUTO</td>";
+	}
+	else {
+		response += sf;
+		response +="<td class=\"cell\"><a href=\"SF=-1\"><button>-</button></a></td>";
+		response +="<td class=\"cell\"><a href=\"SF=1\"><button>+</button></a></td>";
+	}
+	response +="</tr>";
+	
+	// Channel
+	response +="<tr><td class=\"cell\">Channel</td>";
+	response +="<td class=\"cell\" colspan=\"2\">"; 
+	if (_hop) {
+		response += "AUTO</td>";
+	}
+	else {
+		response += String() + ifreq; 
+		response +="</td>";
+		response +="<td class=\"cell\"><a href=\"FREQ=-1\"><button>-</button></a></td>";
+		response +="<td class=\"cell\"><a href=\"FREQ=1\"><button>+</button></a></td>";
+	}
+	response +="</tr>";
+
+	// Debugging options, only when DUSB is set, otherwise no
+	// serial activity
+#if DUSB>=1	
+	response +="<tr><td class=\"cell\">Debug level</td><td class=\"cell\" colspan=\"2\">"; 
+	response +=debug; 
+	response +="</td>";
+	response +="<td class=\"cell\"><a href=\"DEBUG=-1\"><button>-</button></a></td>";
+	response +="<td class=\"cell\"><a href=\"DEBUG=1\"><button>+</button></a></td>";
+	response +="</tr>";
+	
+	response +="<tr><td class=\"cell\">Debug pattern</td>"; 
+	
+	bg = ( (pdebug & P_SCAN) ? "LightGreen" : "orange" ); 
+	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
+	response += bg;	response += "\">";
+	response +="<a href=\"PDEBUG=SCAN\">";
+	response +="<button>SCN</button></a></td>";
+	
+	bg = ( (pdebug & P_CAD) ? "LightGreen" : "orange" ); 
+	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
+	response += bg;	response += "\">";
+	response +="<a href=\"PDEBUG=CAD\">";
+	response +="<button>CAD</button></a></td>";
+
+	bg = ( (pdebug & P_RX) ? "LightGreen" : "orange" ); 
+	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
+	response += bg;	response += "\">";
+	response +="<a href=\"PDEBUG=RX\">";
+	response +="<button>RX</button></a></td>";
+
+	bg = ( (pdebug & P_TX) ? "LightGreen" : "orange" ); 
+	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
+	response += bg;	response += "\">";
+	response +="<a href=\"PDEBUG=TX\">";
+	response +="<button>TX</button></a></td>";
+	response += "</tr>";
+	
+	// Use a second Line
+	response +="<tr><td class=\"cell\"></td>";
+	bg = ( (pdebug & P_PRE) ? "LightGreen" : "orange" ); 
+	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
+	response += bg;	response += "\">";
+	response +="<a href=\"PDEBUG=PRE\">";
+	response +="<button>PRE</button></a></td>";
+
+	bg = ( (pdebug & P_MAIN) ? "LightGreen" : "orange" ); 
+	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
+	response += bg;	response += "\">";
+	response +="<a href=\"PDEBUG=MAIN\">";
+	response +="<button>MAI</button></a></td>";
+	
+	bg = ( (pdebug & P_GUI) ? "LightGreen" : "orange" ); 
+	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
+	response += bg;	response += "\">";
+	response +="<a href=\"PDEBUG=GUI\">";
+	response +="<button>GUI</button></a></td>";
+
+	bg = ( (pdebug & P_RADIO) ? "LightGreen" : "orange" ); 
+	response +="<td class=\"cell\" style=\"border: 1px solid black; width:20px; background-color: ";
+	response += bg;	response += "\">";
+	response +="<a href=\"PDEBUG=RADIO\">";
+	response +="<button>RDIO</button></a></td>";
+	response +="</tr>";
+#endif
+	// Serial Debugging
+	response +="<tr><td class=\"cell\">Usb Debug</td><td class=\"cell\" colspan=\"2\">"; 
+	response +=DUSB; 
+	response +="</td>";
+	//response +="<td class=\"cell\"> </td>";
+	//response +="<td class=\"cell\"> </td>";
+	response +="</tr>";
+	
+#if GATEWAYNODE==1
+	response +="<tr><td class=\"cell\">Framecounter Internal Sensor</td>";
+	response +="<td class=\"cell\" colspan=\"2\">";
+	response +=frameCount;
+	response +="</td><td colspan=\"2\" style=\"border: 1px solid black;\">";
+	response +="<button><a href=\"/FCNT\">RESET</a></button></td>";
+	response +="</tr>";
+	
+	bg = " background-color: ";
+	bg += ( (gwayConfig.isNode == 1) ? "LightGreen" : "orange" );
+	response +="<tr><td class=\"cell\">Gateway Node</td>";
+	response +="<td class=\"cell\" style=\"border: 1px solid black;" + bg + "\">";
+	response += ( (gwayConfig.isNode == true) ? "ON" : "OFF" );
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"NODE=1\"><button>ON</button></a></td>";
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"NODE=0\"><button>OFF</button></a></td>";
+	response +="</tr>";
+#endif
+
+#if A_REFRESH==1
+	bg = " background-color: ";
+	bg += ( (gwayConfig.refresh == 1) ? "LightGreen" : "orange" );
+	response +="<tr><td class=\"cell\">WWW Refresh</td>";
+	response +="<td class=\"cell\" colspan=\"2\" style=\"border: 1px solid black; " + bg + "\">";
+	response += ( (gwayConfig.refresh == 1) ? "ON" : "OFF" );
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=1\"><button>ON</button></a></td>";
+	response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"REFR=0\"><button>OFF</button></a></td>";
+	response +="</tr>";
+#endif
+
+	// Reset Accesspoint
+#if WIFIMANAGER==1
+	response +="<tr><td><tr><td>";
+	response +="Click <a href=\"/NEWSSID\">here</a> to reset accesspoint<br>";
+	response +="</td><td></td></tr>";
+#endif
+
+	// Update Firmware all statistics
+	response +="<tr><td class=\"cell\">Update Firmware</td><td colspan=\"2\"></td>";
+	response +="<td class=\"cell\" colspan=\"2\" class=\"cell\"><a href=\"/UPDATE=1\"><button>UPDATE</button></a></td></tr>";
+
+	// Format the Filesystem
+	response +="<tr><td class=\"cell\">Format SPIFFS</td>";
+	response +=String() + "<td class=\"cell\" colspan=\"2\" >"+""+"</td>";
+	response +="<td colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"FORMAT\" onclick=\"ynDialog(\'Do you really want to format?\',\'FORMAT\')\" /></td></tr>";
+
+	// Reset all statistics
+#if STATISTICS >= 1
+	response +="<tr><td class=\"cell\">Statistics</td>";
+	response +=String() + "<td class=\"cell\" colspan=\"2\" >"+statc.resets+"</td>";
+	response +="<td colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"RESET\" onclick=\"ynDialog(\'Do you really want to reset statistics?\',\'RESET\')\" /></td></tr>";
+
+	// Reset
+	response +="<tr><td class=\"cell\">Boots and Resets</td>";
+	response +=String() + "<td class=\"cell\" colspan=\"2\" >"+gwayConfig.boots+"</td>";
+	response +="<td colspan=\"2\" class=\"cell\"><input type=\"button\" value=\"RESET\" onclick=\"ynDialog(\'Do you want to reset boots?\',\'BOOT\')\" /></td></tr>";
+#endif
+	
+	response +="</table>";
+	
+	server.sendContent(response);
+}
+
+
+// ----------------------------------------------------------------------------
+// STATISTICS DATA
+//
+// ----------------------------------------------------------------------------
+static void statisticsData()
+{
+	String response="";
+
+	// Header
+	response +="<h2>Package Statistics</h2>";
+	response +="<table class=\"config_table\">";
+	response +="<tr><th class=\"thead\">Counter</th>";
+#if STATISTICS == 3
+	response +="<th class=\"thead\">C 0</th>";
+	response +="<th class=\"thead\">C 1</th>";
+	response +="<th class=\"thead\">C 2</th>";
+#endif
+	response +="<th class=\"thead\">Pkgs</th>";
+	response +="<th class=\"thead\">Pkgs/hr</th>";
+	response +="</tr>";
+
+	// Table rows
+	response +="<tr><td class=\"cell\">Packages Downlink</td>";
+#if STATISTICS == 3
+		response +="<td class=\"cell\"></td>";
+		response +="<td class=\"cell\"></td>";
+		response +="<td class=\"cell\"></td>"; 
+#endif
+	response += "<td class=\"cell\">" + String(cp_up_pkt_fwd) + "</td>";
+	response +="<td class=\"cell\"></td></tr>";
+		
+	response +="<tr><td class=\"cell\">Packages Uplink Total</td>";
+#if STATISTICS == 3
+		response +="<td class=\"cell\"></td>";
+		response +="<td class=\"cell\"></td>";
+		response +="<td class=\"cell\"></td>";
+#endif
+		response +="<td class=\"cell\">" + String(cp_nb_rx_rcv) + "</td>";
+		response +="<td class=\"cell\">" + String((cp_nb_rx_rcv*3600)/(now() - startTime)) + "</td></tr>";
+		
+	response +="<tr><td class=\"cell\">Packages Uplink OK </td>";
+#if STATISTICS == 3
+		response +="<td class=\"cell\"></td>";
+		response +="<td class=\"cell\"></td>";
+		response +="<td class=\"cell\"></td>";
+#endif
+	response +="<td class=\"cell\">" + String(cp_nb_rx_ok) + "</td>";
+	response +="<td class=\"cell\"></td></tr>";
+		
+
+	// Provide a table with all the SF data including percentage of messsages
+#if STATISTICS == 2
+	response +="<tr><td class=\"cell\">SF7 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf7; 
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf7/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+	response +="<tr><td class=\"cell\">SF8 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf8;
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf8/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+	response +="<tr><td class=\"cell\">SF9 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf9;
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf9/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+	response +="<tr><td class=\"cell\">SF10 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf10; 
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf10/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+	response +="<tr><td class=\"cell\">SF11 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf11; 
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf11/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+	response +="<tr><td class=\"cell\">SF12 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf12; 
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf12/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+#endif
+#if STATISTICS == 3
+	response +="<tr><td class=\"cell\">SF7 rcvd</td>";
+		response +="<td class=\"cell\">"; response +=statc.sf7_0; 
+		response +="<td class=\"cell\">"; response +=statc.sf7_1; 
+		response +="<td class=\"cell\">"; response +=statc.sf7_2; 
+		response +="<td class=\"cell\">"; response +=statc.sf7; 
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf7/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+		
+	response +="<tr><td class=\"cell\">SF8 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf8_0;
+		response +="<td class=\"cell\">"; response +=statc.sf8_1;
+		response +="<td class=\"cell\">"; response +=statc.sf8_2;
+		response +="<td class=\"cell\">"; response +=statc.sf8;
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf8/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+		
+	response +="<tr><td class=\"cell\">SF9 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf9_0;
+		response +="<td class=\"cell\">"; response +=statc.sf9_1;
+		response +="<td class=\"cell\">"; response +=statc.sf9_2;
+		response +="<td class=\"cell\">"; response +=statc.sf9;
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf9/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+		
+	response +="<tr><td class=\"cell\">SF10 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf10_0; 
+		response +="<td class=\"cell\">"; response +=statc.sf10_1; 
+		response +="<td class=\"cell\">"; response +=statc.sf10_2; 
+		response +="<td class=\"cell\">"; response +=statc.sf10; 
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf10/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+		
+	response +="<tr><td class=\"cell\">SF11 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf11_0;
+		response +="<td class=\"cell\">"; response +=statc.sf11_1; 
+		response +="<td class=\"cell\">"; response +=statc.sf11_2; 
+		response +="<td class=\"cell\">"; response +=statc.sf11; 
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf11/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+		
+	response +="<tr><td class=\"cell\">SF12 rcvd</td>"; 
+		response +="<td class=\"cell\">"; response +=statc.sf12_0;
+		response +="<td class=\"cell\">"; response +=statc.sf12_1;
+		response +="<td class=\"cell\">"; response +=statc.sf12_1;
+		response +="<td class=\"cell\">"; response +=statc.sf12;		
+		response +="<td class=\"cell\">"; response += String(cp_nb_rx_rcv>0 ? 100*statc.sf12/cp_nb_rx_rcv : 0)+" %"; 
+		response +="</td></tr>";
+#endif
+
+	response +="</table>";
+	server.sendContent(response);
+}
+
+
+
+
+// ----------------------------------------------------------------------------
+// SENSORDATA
+// If enabled, display the sensorHistory on the current webserver Page.
+// In this GUI section a number of statr[x] records are displayed such as:
+//
+// Time, The time the sensor message was received
+// Node, the DevAddr or even Node name for Trusted nodes,
+// Data (Localserver), when _LOCALSERVER is enabled contains decoded data
+// C, Channel frequency on which the sensor was received
+// Freq, The frequency of the channel
+// SF, Spreading Factor
+// pRSSI, Packet RSSI
+//
+// Parameters:
+//	- <none>
+// Returns:
+//	- <none>
+// ----------------------------------------------------------------------------
+static void sensorData() 
+{
+#if STATISTICS >= 1
+	String response="";
+	
+	response += "<h2>Message History</h2>";
+	response += "<table class=\"config_table\">";
+	response += "<tr>";
+	response += "<th class=\"thead\">Time</th>";
+	response += "<th class=\"thead\">Node</th>";
+#if _LOCALSERVER==1
+	response += "<th class=\"thead\">Data</th>";
+#endif
+	response += "<th class=\"thead\" style=\"width: 20px;\">C</th>";
+	response += "<th class=\"thead\">Freq</th>";
+	response += "<th class=\"thead\" style=\"width: 40px;\">SF</th>";
+	response += "<th class=\"thead\" style=\"width: 50px;\">pRSSI</th>";
+#if RSSI==1
+	if (debug > 1) {
+		response += "<th class=\"thead\" style=\"width: 50px;\">RSSI</th>";
+	}
+#endif
+	response += "</tr>";
+	server.sendContent(response);
+
+	for (int i=0; i<MAX_STAT; i++) {
+		if (statr[i].sf == 0) break;
+		
+		response = "";
+		
+		response += String() + "<tr><td class=\"cell\">";					// Tmst
+		stringTime((statr[i].tmst), response);			// XXX Change tmst not to be millis() dependent
+		response += "</td>";
+		
+		response += String() + "<td class=\"cell\">"; 						// Node
+		if (SerialName((char *)(& (statr[i].node)), response) < 0) {		// works with TRUSTED_NODES >= 1
+			printHEX((char *)(& (statr[i].node)),' ',response);				// else
+		}
+		response += "</td>";
+		
+#if _LOCALSERVER==1
+		response += String() + "<td class=\"cell\">";						// Data
+		for (int j=0; j<statr[i].datal; j++) {
+			if (statr[i].data[j] <0x10) response+= "0";
+			response += String(statr[i].data[j],HEX) + " ";
+		}
+		response += "</td>";
+#endif
+
+
+		response += String() + "<td class=\"cell\">" + statr[i].ch + "</td>";
+		response += String() + "<td class=\"cell\">" + freqs[statr[i].ch] + "</td>";
+		response += String() + "<td class=\"cell\">" + statr[i].sf + "</td>";
+
+		response += String() + "<td class=\"cell\">" + statr[i].prssi + "</td>";
+#if RSSI==1
+		if (debug >= 2) {
+			response += String() + "<td class=\"cell\">" + statr[i].rssi + "</td>";
+		}
+#endif
+		response += "</tr>";
+		server.sendContent(response);
+	}
+	
+	server.sendContent("</table>");
+	
+#endif
+}
+
+
+// ----------------------------------------------------------------------------
+// SEND WEB PAGE() 
+// Call the webserver and send the standard content and the content that is 
+// passed by the parameter. Each time a variable is changed, this function is 
+// called to display the webpage again/
+//
+// NOTE: This is the only place where yield() or delay() calls are used.
+//
+// ----------------------------------------------------------------------------
+void sendWebPage(const char *cmd, const char *arg)
+{
+	openWebPage(); yield();
+	
+	wwwButtons();
+	
+	setVariables(cmd,arg); yield();
+
+	statisticsData(); yield();		 			// Node statistics
+	sensorData(); yield();						// Display the sensor history, message statistics
+
+	settingsData(); yield();					// Display web configuration
+	wifiData(); yield();						// WiFI specific parameters
+	
+	systemData(); yield();						// System statistics such as heap etc.
+	interruptData(); yield();					// Display interrupts only when debug >= 2
+		
+	// Close the client connection to server
+	server.sendContent(String() + "<br><br /><p style='font-size:10px'>Click <a href=\"/HELP\">here</a> to explain Help and REST options</p><br>");
+
+	server.sendContent(String() + "</BODY></HTML>");
+	server.sendContent(""); yield();
+	
+	server.client().stop();
+}
+
+
+// ----------------------------------------------------------------------------
+// setupWWW is the main function for webserver functions/
+// SetupWWW function called by main setup() program to setup webserver
+// It does actually not much more than installing all the callback handlers
+// for messages sent to the webserver
+//
+// Implemented is an interface like:
+// http://<server>/<Variable>=<value>
+//
+// ----------------------------------------------------------------------------
+void setupWWW() 
+{
+	server.begin();									// Start the webserver
+	
+	// -----------------
+	// BUTTONS, define what should happen with the buttons we press on the homepage
+	
+	server.on("/", []() {
+		sendWebPage("","");							// Send the webPage string
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	
+	server.on("/HELP", []() {
+		sendWebPage("HELP","");					// Send the webPage string
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	// Format the filesystem
+	server.on("/FORMAT", []() {
+		Serial.print(F("FORMAT ..."));
+		
+		SPIFFS.format();								// Normally disabled. Enable only when SPIFFS corrupt
+		initConfig(&gwayConfig);
+		writeConfig( CONFIGFILE, &gwayConfig);
+#if DUSB>=1
+		Serial.println(F("DONE"));
+#endif
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	
+	
+	// Reset the statistics
+	server.on("/RESET", []() {
+		Serial.println(F("RESET"));
+		startTime= now() - 1;					// Reset all timers too	
+		cp_nb_rx_rcv = 0;						// Reset package statistics
+		cp_nb_rx_ok = 0;
+		cp_up_pkt_fwd = 0;
+#if STATISTICS >= 1
+		for (int i=0; i<MAX_STAT; i++) { statr[i].sf = 0; }
+#if STATISTICS >= 2
+		statc.sf7 = 0;
+		statc.sf8 = 0;
+		statc.sf9 = 0;
+		statc.sf10= 0;
+		statc.sf11= 0;
+		statc.sf12= 0;
+		
+		statc.resets= 0;
+		writeGwayCfg(CONFIGFILE);
+#if STATISTICS >= 3
+		statc.sf7_0 = 0; statc.sf7_1 = 0; statc.sf7_2 = 0;
+		statc.sf8_0 = 0; statc.sf8_1 = 0; statc.sf8_2 = 0;
+		statc.sf9_0 = 0; statc.sf9_1 = 0; statc.sf9_2 = 0;
+		statc.sf10_0= 0; statc.sf10_1= 0; statc.sf10_2= 0;
+		statc.sf11_0= 0; statc.sf11_1= 0; statc.sf11_2= 0;
+		statc.sf12_0= 0; statc.sf12_1= 0; statc.sf12_2= 0;
+#endif
+#endif
+#endif
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	// Reset the boot counter
+	server.on("/BOOT", []() {
+		Serial.println(F("BOOT"));
+#if STATISTICS >= 2
+		gwayConfig.boots = 0;
+		gwayConfig.wifis = 0;
+		gwayConfig.views = 0;
+		gwayConfig.ntpErr = 0;					// NTP errors
+		gwayConfig.ntpErrTime = 0;				// NTP last error time
+		gwayConfig.ntps = 0;					// Number of NTP calls
+#endif
+		gwayConfig.reents = 0;					// Re-entrance
+
+		writeGwayCfg(CONFIGFILE);
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	server.on("/NEWSSID", []() {
+		sendWebPage("NEWSSID","");				// Send the webPage string
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	// Set debug parameter
+	server.on("/DEBUG=-1", []() {				// Set debug level 0-2						
+		debug = (debug+3)%4;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/DEBUG=1", []() {
+		debug = (debug+1)%4;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	// Set PDEBUG parameter
+	//
+		server.on("/PDEBUG=SCAN", []() {		// Set debug level 0-2						
+		pdebug ^= P_SCAN;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/PDEBUG=CAD", []() {				// Set debug level 0-2						
+		pdebug ^= P_CAD;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/PDEBUG=RX", []() {				// Set debug level 0-2						
+		pdebug ^= P_RX;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/PDEBUG=TX", []() {				// Set debug level 0-2						
+		pdebug ^= P_TX;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/PDEBUG=PRE", []() {				// Set debug level 0-2						
+		pdebug ^= P_PRE;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/PDEBUG=MAIN", []() {				// Set debug level 0-2						
+		pdebug ^= P_MAIN;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/PDEBUG=GUI", []() {				// Set debug level 0-2						
+		pdebug ^= P_GUI;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/PDEBUG=RADIO", []() {				// Set debug level 0-2						
+		pdebug ^= P_RADIO;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	
+	// Set delay in microseconds
+	server.on("/DELAY=1", []() {
+		txDelay+=5000;
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/DELAY=-1", []() {
+		txDelay-=5000;
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+
+	// Spreading Factor setting
+	server.on("/SF=1", []() {
+		if (sf>=SF12) sf=SF7; else sf= (sf_t)((int)sf+1);
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/SF=-1", []() {
+		if (sf<=SF7) sf=SF12; else sf= (sf_t)((int)sf-1);
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	// Frequency of the GateWay node
+	server.on("/FREQ=1", []() {
+		uint8_t nf = sizeof(freqs)/sizeof(int);	// Number of elements in array
+		if (ifreq==(nf-1)) ifreq=0; else ifreq++;
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/FREQ=-1", []() {
+		uint8_t nf = sizeof(freqs)/sizeof(int);	// Number of elements in array
+		if (ifreq==0) ifreq=(nf-1); else ifreq--;
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	// Set CAD function off/on
+	server.on("/CAD=1", []() {
+		_cad=(bool)1;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/CAD=0", []() {
+		_cad=(bool)0;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	// GatewayNode
+	server.on("/NODE=1", []() {
+#if GATEWAYNODE==1
+		gwayConfig.isNode =(bool)1;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+#endif
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/NODE=0", []() {
+#if GATEWAYNODE==1
+		gwayConfig.isNode =(bool)0;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+#endif
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+#if GATEWAYNODE==1	
+	// Framecounter of the Gateway node
+	server.on("/FCNT", []() {
+
+		frameCount=0; 
+		rxLoraModem();							// Reset the radion with the new frequency
+		writeGwayCfg(CONFIGFILE);
+
+		//sendWebPage("","");						// Send the webPage string
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+#endif
+	// WWW Page refresh function
+	server.on("/REFR=1", []() {					// WWW page auto refresh ON
+#if A_REFRESH==1
+		gwayConfig.refresh =1;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+#endif		
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/REFR=0", []() {					// WWW page auto refresh OFF
+#if A_REFRESH==1
+		gwayConfig.refresh =0;
+		writeGwayCfg(CONFIGFILE);				// Save configuration to file
+#endif
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	
+	// Switch off/on the HOP functions
+	server.on("/HOP=1", []() {
+		_hop=true;
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/HOP=0", []() {
+		_hop=false;
+		ifreq=0; 
+		freq=freqs[0]; 
+		rxLoraModem();
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+#if !defined ESP32_ARCH
+	// Change speed to 160 MHz
+	server.on("/SPEED=80", []() {
+		system_update_cpu_freq(80);
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+	server.on("/SPEED=160", []() {
+		system_update_cpu_freq(160);
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+#endif
+	// Display Documentation pages
+	server.on("/DOCU", []() {
+
+		server.sendHeader("Location", String("/"), true);
+		buttonDocu();
+		server.send ( 302, "text/plain", "");
+	});
+	
+	server.on("/LOG", []() {
+		server.sendHeader("Location", String("/"), true);
+#if DUSB>=1
+		Serial.println(F("LOG button"));
+#endif
+		buttonLog();
+		server.send ( 302, "text/plain", "");
+	});
+	
+	// Display Expert mode or Simple mode
+	server.on("/EXPERT", []() {
+		server.sendHeader("Location", String("/"), true);
+		gwayConfig.expert = bool(1 - (int) gwayConfig.expert) ;
+		server.send ( 302, "text/plain", "");
+	});
+
+	
+	// Update the sketch. Not yet implemented
+	server.on("/UPDATE=1", []() {
+#if A_OTA==1
+		updateOtaa();
+#endif
+		server.sendHeader("Location", String("/"), true);
+		server.send ( 302, "text/plain", "");
+	});
+
+	// -----------
+	// This section from version 4.0.7 defines what PART of the
+	// webpage is shown based on the buttons pressed by the user
+	// Maybe not all information should be put on the screen since it
+	// may take too much time to serve all information before a next
+	// package interrupt arrives at the gateway
+	
+	Serial.print(F("WWW Server started on port "));
+	Serial.println(A_SERVERPORT);
+	return;
+} // setupWWW
+
+
+
+// ----------------------------------------------------------------------------
+// WIFI CONFIG
+// wifiData() displays the most important Wifi parameters gathered
+//
+// ----------------------------------------------------------------------------
+static void wifiData()
+{
+	if (gwayConfig.expert) {
+	String response="";
+	response +="<h2>WiFi Config</h2>";
+
+	response +="<table class=\"config_table\">";
+
+	response +="<tr><th class=\"thead\">Parameter</th><th class=\"thead\">Value</th></tr>";
+	
+	response +="<tr><td class=\"cell\">WiFi host</td><td class=\"cell\">"; 
+#if ESP32_ARCH==1
+	response +=WiFi.getHostname(); response+="</tr>";
+#else
+	response +=wifi_station_get_hostname(); response+="</tr>";
+#endif
+
+	response +="<tr><td class=\"cell\">WiFi SSID</td><td class=\"cell\">"; 
+	response +=WiFi.SSID(); response+="</tr>";
+	
+	response +="<tr><td class=\"cell\">IP Address</td><td class=\"cell\">"; 
+	printIP((IPAddress)WiFi.localIP(),'.',response); 
+	response +="</tr>";
+	response +="<tr><td class=\"cell\">IP Gateway</td><td class=\"cell\">"; 
+	printIP((IPAddress)WiFi.gatewayIP(),'.',response); 
+	response +="</tr>";
+	response +="<tr><td class=\"cell\">NTP Server</td><td class=\"cell\">"; response+=NTP_TIMESERVER; response+="</tr>";
+//	response +="<tr><td class=\"cell\">LoRa Router</td><td class=\"cell\">"; response+=_TTNSERVER; response+="</tr>";
+	response +="<tr><td class=\"cell\">LoRa Router IP</td><td class=\"cell\">"; 
+	printIP((IPAddress)ttnServer,'.',response); 
+	response +="</tr>";
+#ifdef _THINGSERVER
+	response +="<tr><td class=\"cell\">LoRa Router 2</td><td class=\"cell\">"; response+=_THINGSERVER; 
+	response += String() + ":" + _THINGPORT + "</tr>";
+	response +="<tr><td class=\"cell\">LoRa Router 2 IP</td><td class=\"cell\">"; 
+	printIP((IPAddress)thingServer,'.',response);
+	response +="</tr>";
+#endif
+	response +="</table>";
+
+	server.sendContent(response);
+	} // gwayConfig.expert
+} // wifiData
+
+
+// ----------------------------------------------------------------------------
+// SYSTEMDATA
+// This section contain a number of system specific data such as heap size etc.
+// ----------------------------------------------------------------------------
+static void systemData()
+{
+	if (gwayConfig.expert) {
+		String response="";
+		response +="<h2>System Status</h2>";
+	
+		response +="<table class=\"config_table\">";
+		response +="<tr>";
+		response +="<th class=\"thead\">Parameter</th>";
+		response +="<th class=\"thead\">Value</th>";
+		response +="<th colspan=\"2\" class=\"thead\">Set</th>";
+		response +="</tr>";
+	
+		response +="<tr><td style=\"border: 1px solid black; width:120px;\">Gateway ID</td>";
+		response +="<td class=\"cell\">";	
+		if (MAC_array[0]< 0x10) response +='0'; response +=String(MAC_array[0],HEX);	// The MAC array is always returned in lowercase
+		if (MAC_array[1]< 0x10) response +='0'; response +=String(MAC_array[1],HEX);
+		if (MAC_array[2]< 0x10) response +='0'; response +=String(MAC_array[2],HEX);
+		response +="FFFF"; 
+		if (MAC_array[3]< 0x10) response +='0'; response +=String(MAC_array[3],HEX);
+		if (MAC_array[4]< 0x10) response +='0'; response +=String(MAC_array[4],HEX);
+		if (MAC_array[5]< 0x10) response +='0'; response +=String(MAC_array[5],HEX);
+		response+="</tr>";
+	
+
+		response +="<tr><td class=\"cell\">Free heap</td><td class=\"cell\">"; response+=ESP.getFreeHeap(); response+="</tr>";
+// XXX We Shoudl find an ESP32 alternative
+#if !defined ESP32_ARCH
+		response +="<tr><td class=\"cell\">ESP speed</td><td class=\"cell\">"; response+=ESP.getCpuFreqMHz(); 
+		response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"SPEED=80\"><button>80</button></a></td>";
+		response +="<td style=\"border: 1px solid black; width:40px;\"><a href=\"SPEED=160\"><button>160</button></a></td>";
+		response+="</tr>";
+		response +="<tr><td class=\"cell\">ESP Chip ID</td><td class=\"cell\">"; response+=ESP.getChipId(); response+="</tr>";
+#endif
+		response +="<tr><td class=\"cell\">OLED</td><td class=\"cell\">"; response+=OLED; response+="</tr>";
+		
+#if STATISTICS>=1
+		response +="<tr><td class=\"cell\">WiFi Setups</td><td class=\"cell\">"; response+=gwayConfig.wifis; response+="</tr>";
+		response +="<tr><td class=\"cell\">WWW Views</td><td class=\"cell\">"; response+=gwayConfig.views; response+="</tr>";
+#endif
+
+		response +="</table>";
+		server.sendContent(response);
+	} // gwayConfig.expert
+} // systemData
+
+
+// ----------------------------------------------------------------------------
+// INTERRUPT DATA
+// Display interrupt data, but only for debug >= 2
+//
+// ----------------------------------------------------------------------------
+static void interruptData()
+{
+	if (gwayConfig.expert) {
+		uint8_t flags = readRegister(REG_IRQ_FLAGS);
+		uint8_t mask = readRegister(REG_IRQ_FLAGS_MASK);
+		String response="";
+		
+		response +="<h2>System State and Interrupt</h2>";
+		
+		response +="<table class=\"config_table\">";
+		response +="<tr>";
+		response +="<th class=\"thead\">Parameter</th>";
+		response +="<th class=\"thead\">Value</th>";
+		response +="<th colspan=\"2\"  class=\"thead\">Set</th>";
+		response +="</tr>";
+		
+		response +="<tr><td class=\"cell\">_state</td>";
+		response +="<td class=\"cell\">";
+		switch (_state) {							// See loraModem.h
+			case S_INIT: response +="INIT"; break;
+			case S_SCAN: response +="SCAN"; break;
+			case S_CAD: response +="CAD"; break;
+			case S_RX: response +="RX"; break;
+			case S_TX: response +="TX"; break;
+			default: response +="unknown"; break;
+		}
+		response +="</td></tr>";
+
+		response +="<tr><td class=\"cell\">flags (8 bits)</td>";
+		response +="<td class=\"cell\">0x";
+		if (flags <16) response += "0";
+		response +=String(flags,HEX); response+="</td></tr>";
+
+		
+		response +="<tr><td class=\"cell\">mask (8 bits)</td>";
+		response +="<td class=\"cell\">0x"; 
+		if (mask <16) response += "0";
+		response +=String(mask,HEX); response+="</td></tr>";
+		
+		response +="<tr><td class=\"cell\">Re-entrant cntr</td>";
+		response +="<td class=\"cell\">"; 
+		response += String() + gwayConfig.reents;
+		response +="</td></tr>";
+
+		response +="<tr><td class=\"cell\">ntp call cntr</td>";
+		response +="<td class=\"cell\">"; 
+		response += String() + gwayConfig.ntps;
+		response+="</td></tr>";
+		
+		response +="<tr><td class=\"cell\">ntpErr cntr</td>";
+		response +="<td class=\"cell\">"; 
+		response += String() + gwayConfig.ntpErr;
+		response +="</td>";
+		response +="<td colspan=\"2\" style=\"border: 1px solid black;\">";
+		stringTime(gwayConfig.ntpErrTime, response);
+		response +="</td>";
+		response +="</tr>";
+		
+		response +="<tr><td class=\"cell\">Time Correction (uSec)</td><td class=\"cell\">"; 
+		response += txDelay; 
+		response +="</td>";
+		response +="<td class=\"cell\"><a href=\"DELAY=-1\"><button>-</button></a></td>";
+		response +="<td class=\"cell\"><a href=\"DELAY=1\"><button>+</button></a></td>";
+		response +="</tr>";
+		
+		response +="</table>";
+		
+		server.sendContent(response);
+	}// if gwayConfig.expert
+} // interruptData
+
+#endif // A_SERVER==1
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/loraFiles.h b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/loraFiles.h
new file mode 100644
index 0000000000000000000000000000000000000000..98970412021a80fb05665ab64251100a453d543d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/loraFiles.h
@@ -0,0 +1,92 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains a number of compile-time settings that can be set on (=1) or off (=0)
+// The disadvantage of compile time is minor compared to the memory gain of not having
+// too much code compiled and loaded on your ESP8266.
+//
+// ----------------------------------------------------------------------------------------
+
+// At this moment there is only one record written to the ESP8266
+// filesystem. We can add more info, whcich makes the gateway even more usable,
+// however for large data we should only append to the existing file used.
+// This also means we'll have to check for available space so we won't run out of 
+// storage space to quickly.
+// One way would be to use let's say 10 files of each 10000 lines and when full
+// delete the first file and start sriting on a new one (for example)
+
+
+
+//
+// Define Pattern debug settings, this allows debugging per
+// module rather than per level. See also pdebug.
+//
+#define P_SCAN		0x01
+#define P_CAD		0x02
+#define P_RX		0x04
+#define P_TX		0x08
+#define P_PRE		0x10
+#define P_MAIN		0x20
+#define P_GUI		0x40
+#define P_RADIO		0x80
+
+// Definition of the configuration record that is read at startup and written
+// when settings are changed.
+
+struct espGwayConfig {
+	uint16_t fcnt;				// =0 as init value	XXX Could be 32 bit in size
+	uint16_t boots;				// Number of restarts made by the gateway after reset
+	uint16_t resets;			// Number of statistics resets
+	uint16_t views;				// Number of sendWebPage() calls
+	uint16_t wifis;				// Number of WiFi Setups
+	uint16_t reents;			// Number of re-entrant interrupt handler calls
+	uint16_t ntpErr;			// Number of UTP requests that failed
+	uint16_t ntps;
+
+	uint32_t ntpErrTime;		// Record the time of the last NTP error
+	uint8_t ch;					// index to freqs array, freqs[ifreq]=868100000 default
+	uint8_t sf;					// range from SF7 to SF12
+	uint8_t debug;				// range 0 to 4
+	uint8_t pdebug;				// pattern debug, 
+
+	uint16_t logFileRec;		// Logging File Record number
+	uint16_t logFileNo;			// Logging File Number
+	uint16_t logFileNum;		// Number of log files
+	
+	bool cad;					// is CAD enabled?
+	bool hop;					// Is HOP enabled (Note: default be disabled)
+	bool isNode;				// Is gateway node enabled
+	bool refresh;				// Is WWW browser refresh enabled
+	bool expert;
+	
+	String ssid;				// SSID of the last connected WiFi Network
+	String pass;				// Password of WiFi network
+} gwayConfig;
+
+// Define a log record to be written to the log file
+// Keep logfiles SHORT in name! to save memory
+#if STAT_LOG == 1
+
+// We do keep admin of logfiles by number
+// 
+//uint32_t logFileNo = 1;		// Included in struct espGwayConfig LogFile number
+//uint32_t logFileRec = 0;		// Number of records in a single logfile
+//uint32_t logFileNum = 1;		// Number of log files
+#define LOGFILEMAX 10
+#define LOGFILEREC 100
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/loraModem.h b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/loraModem.h
new file mode 100644
index 0000000000000000000000000000000000000000..1b93eb41c9d9f34da8785f797d7a1eed6f737d85
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/loraModem.h
@@ -0,0 +1,523 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many other contributors.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains a number of compile-time settings and declarations that are
+// specific to the LoRa rfm95, sx1276, sx1272 radio of the gateway.
+//
+//
+// ------------------------------------------------------------------------------------
+
+
+// ----------------------------------------
+// Used by REG_PAYLOAD_LENGTH to set receive payload length
+#define PAYLOAD_LENGTH              0x40		// 64 bytes
+#define MAX_PAYLOAD_LENGTH          0x80		// 128 bytes
+
+// In order to make the CAD behaviour dynamic we set a variable
+// when the CAD functions are defined. Value of 3 is minimum frequencies a
+// gateway should support to be fully LoRa compliant.
+// For performance reasons, 3 is the maximum as well!
+//
+#define NUM_HOPS 3
+
+// Do not change these setting for RSSI detection. They are used for CAD
+// Given the correction factor of 157, we can get to -122dB with this rating
+// 
+#define RSSI_LIMIT	35							// 
+
+// How long to wait in LoRa mode before using the RSSI value.
+// This period should be as short as possible, yet sufficient
+// 
+#define RSSI_WAIT	6							// was 25
+
+// How long will it take when hopping before a CDONE or CDETD value
+// is present and can be measured.
+//
+#define EVENT_WAIT 15000						// XXX 180520 was 25 milliseconds before CDDETD timeout
+#define DONE_WAIT 1950							// 2000 microseconds (1/500) sec between CDDONE events
+
+
+// Our code should correct the server Tramission delay settings
+long txDelay= 0x00;								// tx delay time on top of server TMST
+
+// SPI setting. 8MHz seems to be the max
+#define SPISPEED 8000000						// Set to 8 * 10E6
+
+// Frequencies
+// Set center frequency. If in doubt, choose the first one, comment all others
+// Each "real" gateway should support the first 3 frequencies according to LoRa spec.
+// NOTE: This means you have to specify at least 3 frequencies here for the single
+//	channel gateway to work.
+
+#if _LFREQ==868
+// This the the EU868 format as used in most of Europe
+// It is also the default for most of the single channel gateway work.
+int freqs [] = { 
+	868100000, 									// Channel 0, 868.1 MHz/125 primary
+	868300000, 									// Channel 1, 868.3 MHz mandatory
+	868500000, 									// Channel 2, 868.5 MHz mandatory
+	867100000, 									// Channel 3, 867.1 MHz Optional
+	867300000, 									// Channel 4, 867.3 MHz Optional
+	867500000,  								// Channel 5, 867.5 MHz Optional
+	867700000,  								// Channel 6, 867.7 MHz Optional 
+	867900000,  								// Channel 7, 867.9 MHz Optional 
+	868800000,   								// Channel 8, 868.9 MHz/125 Optional
+	869525000									// Channel 9, 869.5 MHz/125 for RX2 responses SF9(10%)
+	// TTN defines an additional channel at 869.525Mhz using SF9 for class B. Not used
+};
+#elif _LFREQ==433
+// The following 3 frequencies should be defined/used in an EU433 
+// environment.
+int freqs [] = {
+	433175000, 									// Channel 0, 433.175 MHz/125 primary
+	433375000, 									// Channel 1, 433.375 MHz primary
+	433575000, 									// Channel 2, 433.575 MHz primary
+	433775000, 									// Channel 3, 433.775 MHz primary
+	433975000, 									// Channel 4, 433.975 MHz primary
+	434175000, 									// Channel 5, 434.175 MHz primary
+	434375000, 									// Channel 6, 434.375 MHz primary
+	434575000, 									// Channel 7, 434.575 MHz primary
+	434775000 									// Channel 8, 434.775 MHz primary
+};
+#elif _LFREQ==915
+// US902=928
+// AU915-928
+int freqs [] = {
+	// Uplink
+	903900000, 									// Channel 0, SF7BW125 to SF10BW125 primary
+	904100000, 									// Ch 1, SF7BW125 to SF10BW125
+	904300000, 									// Ch 2, SF7BW125 to SF10BW125
+	904500000, 									// Ch 3, SF7BW125 to SF10BW125
+	904700000, 									// Ch 4, SF7BW125 to SF10BW125
+	904900000, 									// Ch 5, SF7BW125 to SF10BW125
+	905100000, 									// Ch 6, SF7BW125 to SF10BW125
+	905100000, 									// Ch 7, SF7BW125 to SF10BW125
+	904600000 									// Ch 8, SF8BW500 
+	// Downlink
+	// We should specify downlink frequencies here											
+												// SFxxxBW500
+};
+#else
+int freqs [] = {
+	// Print an Error, Not supported
+#error "Sorry, but your frequency plan is not supported"
+};
+#endif
+uint32_t  freq = freqs[0];
+uint8_t	 ifreq = 0;								// Channel Index
+
+
+
+// Set the structure for spreading factor
+enum sf_t { SF6=6, SF7, SF8, SF9, SF10, SF11, SF12 };
+
+// The state of the receiver. See Semtech Datasheet (rev 4, March 2015) page 43
+// The _state is of the enum type (and should be cast when used as a number)
+enum state_t { S_INIT=0, S_SCAN, S_CAD, S_RX, S_TX, S_TXDONE};
+
+volatile state_t _state=S_INIT;
+volatile uint8_t _event=0;
+
+// rssi is measured at specific moments and reported on others
+// so we need to store the current value we like to work with
+uint8_t _rssi;	
+
+bool _cad= (bool) _CAD;	// Set to true for Channel Activity Detection, only when dio 1 connected
+bool _hop= (bool) false;// experimental; frequency hopping. Only use when dio2 connected
+
+unsigned long nowTime=0;
+unsigned long msgTime=0;
+unsigned long hopTime=0;
+unsigned long detTime=0;
+
+#if _PIN_OUT==1
+// ----------------------------------------------------------------------------
+// Definition of the GPIO pins used by the Gateway for Hallard type boards
+//
+struct pins {
+	uint8_t dio0=15;	// GPIO15 / D8. For the Hallard board shared between DIO0/DIO1/DIO2
+	uint8_t dio1=15;	// GPIO15 / D8. Used for CAD, may or not be shared with DIO0
+	uint8_t dio2=15;	// GPIO15 / D8. Used for frequency hopping, don't care
+	uint8_t ss=16;		// GPIO16 / D0. Select pin connected to GPIO16 / D0
+	uint8_t rst=0;		// GPIO 0 / D3. Reset pin not used	
+	// MISO 12 / D6
+	// MOSI 13 / D7
+	// CLK  14 / D5
+} pins;
+
+#elif _PIN_OUT==2
+// ----------------------------------------------------------------------------
+// For ComResult gateway PCB use the following settings
+struct pins {
+	uint8_t dio0=5;		// GPIO5 / D1. Dio0 used for one frequency and one SF
+	uint8_t dio1=4;		// GPIO4 / D2. Used for CAD, may or not be shared with DIO0
+	uint8_t dio2=0;		// GPIO0 / D3. Used for frequency hopping, don't care
+	uint8_t ss=15;		// GPIO15 / D8. Select pin connected to GPIO15
+	uint8_t rst=0;		// GPIO0  / D3. Reset pin not used	
+} pins;
+
+
+#elif _PIN_OUT==3
+// ----------------------------------------------------------------------------
+// For ESP32/Wemos based board
+// SCK  == GPIO5/ PIN5
+// SS   == GPIO18/PIN18
+// MISO == GPIO19/ PIN19
+// MOSI == GPIO27/ PIN27
+// RST  == GPIO14/ PIN14
+struct pins {
+	uint8_t dio0=26;		// GPIO26 / Dio0 used for one frequency and one SF
+	uint8_t dio1=26;		// GPIO26 / Used for CAD, may or not be shared with DIO0
+	uint8_t dio2=26;		// GPI2O6 / Used for frequency hopping, don't care
+	uint8_t ss=18;			// GPIO18 / Dx. Select pin connected to GPIO18
+	uint8_t rst=14;			// GPIO0  / D3. Reset pin not used	
+} pins;
+
+
+#elif _PIN_OUT==4
+// ----------------------------------------------------------------------------
+// For ESP32/TTGO based board.
+// SCK  == GPIO5/ PIN5
+// SS   == GPIO18/PIN18 CS
+// MISO == GPIO19/ PIN19
+// MOSI == GPIO27/ PIN27
+// RST  == GPIO14/ PIN14
+struct pins {
+	uint8_t dio0=26;		// GPIO26 / Dio0 used for one frequency and one SF
+	uint8_t dio1=33;		// GPIO26 / Used for CAD, may or not be shared with DIO0
+	uint8_t dio2=32;		// GPIO26 / Used for frequency hopping, don't care
+	uint8_t ss=18;			// GPIO18 / Dx. Select pin connected to GPIO18
+	uint8_t rst=14;			// GPIO0  / D3. Reset pin not used	
+} pins;
+#define SCK 5
+#define MISO 19
+#define MOSI 27
+#define RST 14
+#define SS 18
+#define GPS_RX 15
+#define GPS_TX 12
+
+#elif _PIN_OUT==5
+// ----------------------------------------------------------------------------
+// For ESP32/TTGO based board for EU32 with 0.9" OLED
+// NOTE: This board shoudl be same as general type TTGO (nr 4)
+// but for the moment we include this as a separate item
+//
+// SCK  == GPIO5/ PIN5
+// SS   == GPIO18/PIN18 CS
+// MISO == GPIO19/ PIN19
+// MOSI == GPIO27/ PIN27
+// RST  == GPIO14/ PIN14
+struct pins {
+	uint8_t dio0=26;		// GPIO26 / Dio0 used for one frequency and one SF
+	uint8_t dio1=33;		// GPIO26 / Used for CAD, may or not be shared with DIO0
+	uint8_t dio2=32;		// GPIO26 / Used for frequency hopping, don't care
+	uint8_t ss=18;			// GPIO18 / Dx. Select pin connected to GPIO18
+	uint8_t rst=14;			// GPIO0 / D3. Reset pin not used	
+} pins;
+#define SCK 5				// Check
+#define MISO 19				// Check
+#define MOSI 27				// Check
+#define RST 14				// Check
+#define SS 18
+
+#else
+// ----------------------------------------------------------------------------
+// Use your own pin definitions, and comment #error line below
+// MISO 12 / D6
+// MOSI 13 / D7
+// CLK  14 / D5
+// SS   16 / D0
+
+struct pins {
+uint8_t dio0 = 26;
+uint8_t dio1 = 33;
+uint8_t dio2 = 32;
+uint8_t ss = 16;
+uint8_t rst = 27; // Reset not used
+} pins;
+#define SCK 14
+#define MISO 12
+#define MOSI 13
+#define SS 16
+#define DIO0 26
+#endif
+
+// STATR contains the statictis that are kept by message. 
+// Ech time a message is received or sent the statistics are updated.
+// In case STATISTICS==1 we define the last MAX_STAT messages as statistics
+struct stat_t {
+	unsigned long tmst;						// Time since 1970 in seconds		
+	unsigned long node;						// 4-byte DEVaddr (the only one known to gateway)
+	uint8_t ch;								// Channel index to freqs array
+	uint8_t sf;
+#if RSSI==1
+	int8_t		rssi;						// XXX Can be < -128
+#endif
+	int8_t		prssi;						// XXX Can be < -128
+#if _LOCALSERVER==1
+	uint8_t data[23];						// For memory purposes, only 23 chars
+	uint8_t datal;							// Length of decoded message 1 char
+#endif
+} stat_t;
+
+
+#if STATISTICS >= 1
+// STATC contains the statistic that are gateway related and not per
+// message. Example: Number of messages received on SF7 or number of (re) boots
+// So where statr contains the statistics gathered per packet the statc
+// contains general statics of the node
+#if STATISTICS >= 2							// Only if we explicitely set it higher
+struct stat_c {
+	unsigned long sf7;						// Spreading factor 7 statistics/Count
+	unsigned long sf8;						// Spreading factor 8
+	unsigned long sf9;						// Spreading factor 9
+	unsigned long sf10;						// Spreading factor 10
+	unsigned long sf11;						// Spreading factor 11
+	unsigned long sf12;						// Spreading factor 12
+
+	// If STATISTICS is 3, we add statistics about the channel 
+	// When only one changgel is used, we normally know the spread of
+	// statistics, but when HOP mode is selected we migth want to add this info
+#if STATISTICS >=3
+	unsigned long sf7_0, sf7_1, sf7_2;
+	unsigned long sf8_0, sf8_1, sf8_2;
+	unsigned long sf9_0, sf9_1, sf9_2;
+	unsigned long sf10_0, sf10_1, sf10_2;
+	unsigned long sf11_0, sf11_1, sf11_2;
+	unsigned long sf12_0, sf12_1, sf12_2;
+#endif
+	
+	uint16_t boots;							// Number of boots
+	uint16_t resets;
+} stat_c;
+struct stat_c statc;
+
+#endif
+
+// History of received uplink messages from nodes
+struct stat_t statr[MAX_STAT];
+
+
+
+
+#else // STATISTICS==0
+struct stat_t	statr[1];					// Always have at least one element to store in
+#endif
+
+// Define the payload structure used to separate interrupt ans SPI
+// processing from the loop() part
+uint8_t payLoad[128];						// Payload i
+struct LoraBuffer {
+	uint8_t	* 	payLoad;
+	uint8_t		payLength;
+	uint32_t	tmst;						// in millis()
+	uint8_t		sfTx;
+	uint8_t		powe;
+	uint32_t	fff;
+	uint8_t		crc;
+	uint8_t		iiq;
+} LoraDown;
+
+// Up buffer (from Lora sensor to UDP)
+//
+
+struct LoraUp {
+	uint8_t		payLoad[128];
+	uint8_t		payLength;
+	int			prssi; 
+	long		snr;
+	int			rssicorr;
+	uint8_t		sf;
+} LoraUp;
+
+
+
+
+// ============================================================================
+// Set all definitions for Gateway
+// ============================================================================	
+// Register definitions. These are the addresses of the TFM95, SX1276 that we 
+// need to set in the program.
+
+#define REG_FIFO                    0x00		// rw FIFO address
+#define REG_OPMODE                  0x01
+// Register 2 to 5 are unused for LoRa
+#define REG_FRF_MSB					0x06
+#define REG_FRF_MID					0x07
+#define REG_FRF_LSB					0x08
+#define REG_PAC                     0x09
+#define REG_PARAMP                  0x0A
+#define REG_LNA                     0x0C
+#define REG_FIFO_ADDR_PTR           0x0D		// rw SPI interface address pointer in FIFO data buffer
+#define REG_FIFO_TX_BASE_AD         0x0E		// rw write base address in FIFO data buffer for TX modulator
+#define REG_FIFO_RX_BASE_AD         0x0F		// rw read base address in FIFO data buffer for RX demodulator (0x00)
+
+#define REG_FIFO_RX_CURRENT_ADDR    0x10		// r  Address of last packet received
+#define REG_IRQ_FLAGS_MASK          0x11
+#define REG_IRQ_FLAGS               0x12
+#define REG_RX_NB_BYTES             0x13
+#define REG_PKT_SNR_VALUE			0x19
+#define REG_PKT_RSSI				0x1A		// latest package
+#define REG_RSSI					0x1B		// Current RSSI, section 6.4, or  5.5.5
+#define REG_HOP_CHANNEL				0x1C
+#define REG_MODEM_CONFIG1           0x1D
+#define REG_MODEM_CONFIG2           0x1E
+#define REG_SYMB_TIMEOUT_LSB  		0x1F
+
+#define REG_PAYLOAD_LENGTH          0x22
+#define REG_MAX_PAYLOAD_LENGTH 		0x23
+#define REG_HOP_PERIOD              0x24
+#define REG_MODEM_CONFIG3           0x26
+#define REG_RSSI_WIDEBAND			0x2C
+
+#define REG_INVERTIQ				0x33
+#define REG_DET_TRESH				0x37		// SF6
+#define REG_SYNC_WORD				0x39
+#define REG_TEMP					0x3C
+
+#define REG_DIO_MAPPING_1           0x40
+#define REG_DIO_MAPPING_2           0x41
+#define REG_VERSION	  				0x42
+
+#define REG_PADAC					0x5A
+#define REG_PADAC_SX1272			0x5A
+#define REG_PADAC_SX1276			0x4D
+
+
+// ----------------------------------------
+// opModes
+#define SX72_MODE_SLEEP             0x80
+#define SX72_MODE_STANDBY           0x81
+#define SX72_MODE_FSTX              0x82
+#define SX72_MODE_TX                0x83		// 0x80 | 0x03
+#define SX72_MODE_RX_CONTINUOS      0x85
+
+// ----------------------------------------
+// LMIC Constants for radio registers
+#define OPMODE_LORA      			0x80
+#define OPMODE_MASK      			0x07
+#define OPMODE_SLEEP     			0x00
+#define OPMODE_STANDBY   			0x01
+#define OPMODE_FSTX      			0x02
+#define OPMODE_TX        			0x03
+#define OPMODE_FSRX      			0x04
+#define OPMODE_RX        			0x05
+#define OPMODE_RX_SINGLE 			0x06
+#define OPMODE_CAD       			0x07
+
+
+
+// ----------------------------------------
+// LOW NOISE AMPLIFIER
+
+#define LNA_MAX_GAIN                0x23		// Max gain 0x20 | Boost 0x03
+#define LNA_OFF_GAIN                0x00
+#define LNA_LOW_GAIN		    	0x20
+
+// CONF REG
+#define REG1                        0x0A
+#define REG2                        0x84
+
+// ----------------------------------------
+// MC1 sx1276 RegModemConfig1
+#define SX1276_MC1_BW_125           0x70
+#define SX1276_MC1_BW_250           0x80
+#define SX1276_MC1_BW_500           0x90
+#define SX1276_MC1_CR_4_5           0x02
+#define SX1276_MC1_CR_4_6           0x04
+#define SX1276_MC1_CR_4_7           0x06
+#define SX1276_MC1_CR_4_8           0x08
+#define SX1276_MC1_IMPLICIT_HEADER_MODE_ON  0x01
+
+#define SX72_MC1_LOW_DATA_RATE_OPTIMIZE     0x01 	// mandated for SF11 and SF12
+
+// ----------------------------------------
+// MC2 definitions
+#define SX72_MC2_FSK                0x00
+#define SX72_MC2_SF7                0x70		// SF7 == 0x07, so (SF7<<4) == SX7_MC2_SF7
+#define SX72_MC2_SF8                0x80
+#define SX72_MC2_SF9                0x90
+#define SX72_MC2_SF10               0xA0
+#define SX72_MC2_SF11               0xB0
+#define SX72_MC2_SF12               0xC0
+
+// ----------------------------------------
+// MC3
+#define SX1276_MC3_LOW_DATA_RATE_OPTIMIZE  0x08
+#define SX1276_MC3_AGCAUTO                 0x04
+
+// ----------------------------------------
+// FRF
+#define FRF_MSB						0xD9		// 868.1 Mhz
+#define FRF_MID						0x06
+#define FRF_LSB						0x66
+
+// ----------------------------------------
+// DIO function mappings           		     D0D1D2D3
+#define MAP_DIO0_LORA_RXDONE   		0x00  // 00------ bit 7 and 6
+#define MAP_DIO0_LORA_TXDONE   		0x40  // 01------
+#define MAP_DIO0_LORA_CADDONE  		0x80  // 10------
+#define MAP_DIO0_LORA_NOP   		0xC0  // 11------
+
+#define MAP_DIO1_LORA_RXTOUT   		0x00  // --00---- bit 5 and 4
+#define MAP_DIO1_LORA_FCC			0x10  // --01----
+#define MAP_DIO1_LORA_CADDETECT		0x20  // --10----
+#define MAP_DIO1_LORA_NOP      		0x30  // --11----
+
+#define MAP_DIO2_LORA_FCC0      	0x00  // ----00-- bit 3 and 2
+#define MAP_DIO2_LORA_FCC1      	0x04  // ----01-- bit 3 and 2
+#define MAP_DIO2_LORA_FCC2      	0x08  // ----10-- bit 3 and 2
+#define MAP_DIO2_LORA_NOP      		0x0C  // ----11-- bit 3 and 2
+
+#define MAP_DIO3_LORA_CADDONE  		0x00  // ------00 bit 1 and 0
+#define MAP_DIO3_LORA_HEADER		0x01  // ------01
+#define MAP_DIO3_LORA_CRC			0x02  // ------10
+#define MAP_DIO3_LORA_NOP      		0x03  // ------11
+
+// FSK specific
+#define MAP_DIO0_FSK_READY     		0x00  // 00------ (packet sent / payload ready)
+#define MAP_DIO1_FSK_NOP       		0x30  // --11----
+#define MAP_DIO2_FSK_TXNOP     		0x04  // ----01--
+#define MAP_DIO2_FSK_TIMEOUT   		0x08  // ----10--
+
+// ----------------------------------------
+// Bits masking the corresponding IRQs from the radio
+#define IRQ_LORA_RXTOUT_MASK 		0x80	// RXTOUT
+#define IRQ_LORA_RXDONE_MASK 		0x40	// RXDONE after receiving the header and CRC, we receive payload part
+#define IRQ_LORA_CRCERR_MASK 		0x20	// CRC error detected. Note that RXDONE will also be set
+#define IRQ_LORA_HEADER_MASK 		0x10	// valid HEADER mask. This interrupt is first when receiving a message
+#define IRQ_LORA_TXDONE_MASK 		0x08	// End of TRansmission
+#define IRQ_LORA_CDDONE_MASK 		0x04	// CDDONE
+#define IRQ_LORA_FHSSCH_MASK 		0x02
+#define IRQ_LORA_CDDETD_MASK 		0x01	// Detect preamble channel
+
+
+// ----------------------------------------
+// Definitions for UDP message arriving from server
+#define PROTOCOL_VERSION			0x01
+#define PKT_PUSH_DATA				0x00
+#define PKT_PUSH_ACK				0x01
+#define PKT_PULL_DATA				0x02
+#define PKT_PULL_RESP				0x03
+#define PKT_PULL_ACK				0x04
+#define PKT_TX_ACK                  0x05
+
+#define MGT_RESET					0x15		// Not a LoRa Gateway Spec message
+#define MGT_SET_SF					0x16
+#define MGT_SET_FREQ				0x17
+
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/oLED.h b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/oLED.h
new file mode 100644
index 0000000000000000000000000000000000000000..fc537a99bff0bf45d54561c7e9ab7c5d11ea57c8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/oLED.h
@@ -0,0 +1,68 @@
+// 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-08-25
+//
+// based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+// and many others.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains a number of compile-time settings and definitions for OLED support.
+//
+// ----------------------------------------------------------------------------------------
+
+// OLEDs dupported by this program must be I2C.
+// This is because we do not want any diisturbance in the SPI area
+// which is also interfacing the LORA tranceiver.
+//
+// The following OLDs are supported:
+// 0. No OLED connected
+// 1. 0.9" OLED (cheap)
+// 2. 1.3" OLED with much better and larger display
+
+#if OLED>=1										// If OLED is used
+
+// --------------------------------------------------------	
+// Define the different PIN's used for SCL/SDA for each arch.
+//
+#if _PIN_OUT==1									// HALLARD
+#define OLED_SCL 5								// GPIO5 / D1
+#define OLED_SDA 4								// GPIO4 / D2
+
+#elif _PIN_OUT==2								// COMRESULT				
+#define OLED_SCL 0								// GPIO0 / D3
+#define OLED_SDA 2								// GPIO2 / D4
+
+#elif _PIN_OUT==4								// TTGO (onboard version used, also for DIY)
+#define OLED_SCL 15								// GPIO15 / 
+#define OLED_SDA 4								// GPIO4 / 
+#define OLED_RST 16								// Reset pin (Some OLED displays do not have it)
+
+#endif
+
+
+// --------------------------------------------------------	
+// Define the different OLED versions
+//
+#if OLED==1
+#include "SSD1306.h"
+#define OLED_ADDR 0x3C							// Default 0x3C for 0.9", for 1.3" it is 0x78
+SSD1306  display(OLED_ADDR, OLED_SDA, OLED_SCL);// i2c ADDR & SDA, SCL on wemos
+#endif
+
+// This is an 1.3" OLED display which is running on I2C
+#if OLED==2
+#include "SH1106.h"
+#define OLED_ADDR 0x3C							// Default 0x3C for 1.3" SH1106
+SH1106  display(OLED_ADDR, OLED_SDA, OLED_SCL);	// i2c ADDR & SDA, SCL on wemos
+#endif
+
+#endif//OLED>=1
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/sensor.h b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/sensor.h
new file mode 100644
index 0000000000000000000000000000000000000000..f4b57f9293d4d7460c37f2038e8f4f9464e7338a
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/ESP-sc-gway/sensor.h
@@ -0,0 +1,94 @@
+// sensor.h; 1-channel LoRa Gateway for ESP8266
+// Copyright (c) 2016, 2017, 2018 Maarten Westenberg version for ESP8266
+// Version 5.3.3
+// Date: 2018-05-25
+//
+// 	based on work done by Thomas Telkamp for Raspberry PI 1ch gateway
+//	and many other contributors.
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// NO WARRANTY OF ANY KIND IS PROVIDED
+//
+// Author: Maarten Westenberg (mw12554@hotmail.com)
+//
+// This file contains a number of compile-time settings and declarations that are
+// specific to the LoRa rfm95, sx1276, sx1272 radio of the gateway.
+//
+//
+// ------------------------------------------------------------------------------------
+
+#if _TRUSTED_NODES >= 1
+struct nodex {
+	uint32_t id;				// This is the LoRa ID (coded in 4 bytes uint32_t
+	char nm[32];				// Name of the node
+};
+
+// Add all your named and trusted nodes to this list
+nodex nodes[] = {
+	{ 0x260116BD , "lora-34 PIR node" },						// F=0
+	{ 0x26011152 , "lora-35 temp+humi node" },					// F=0
+	{ 0x2601148C , "lora-36 test node"  },						// F=0
+	{ 0x26011B90 , "lora-39 temp DS18B20" },					// F=1
+	{ 0x260119A6 , "lora-40 airquality" },						// F=0
+	{ 0x2601117D , "lora-41 temp+humi SR04T" },
+	{ 0x26011514 , "lora-43 ch1, no sensors" },					// F=1
+	{ 0x26011D77 , "lora-45 not sensor" },
+	{ 0x2601160F , "lora-46 HTU21 metal case" },				// F=0
+	{ 0x26011E71 , "lora-47 Dallas temperature" },				// F=0
+	{ 0x26011E52 , "lora-53 gas sensor" },						// F=ALL
+	{ 0x02020441 , "lora-65 Waterproof temp humi" },			// F=0
+	{ 0x26011b96 , "lora-50 Internal T-Beam gway" },			// F=0
+	{ 0x270005AB , "distance-42 sensor for trash"},				// F=0
+	{ 0x27000599 , "distance-44 sensor for trash"},				// F=0
+	{ 0x27000596 , "distance-45 sensor for trash"},				// F=0
+	{ 0x00000000 , "lora-00 well known sensor" }				// F=0
+};
+#endif //_TRUSTED_NODES
+
+
+
+#if _LOCALSERVER==1
+struct codex  {
+	uint32_t id;				// This is the device ID (coded in 4 bytes uint32_t
+	char nm[32];				// A name string which is free to choose
+	uint8_t nwkKey[16];			// The Network Session Key
+	uint8_t appKey[16];			// The Application Session Key
+};
+
+// Sometimes we want to decode the sensor completely as we do in the TTN server
+// This means that for all nodes we want to view the dara of, we need to provide
+// he AppsSKey and the NwkSKey
+
+// Definition of all nodes that we want to decode locally on the gateway.
+//
+codex decodes[] = {
+	{ 0x2601148C , "lora-36", 	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x24, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 } 
+	},
+	{ 0x26011b96 , "lora-50",	// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x32, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{ 0x270005AB , "distance-42",// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{ 0x27000599 , "distance-44",// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{ 0x27000596 , "distance-45",// F=0
+		{ 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 },
+		{ 0x02, 0x02, 0x04, 0x2D, 0x00, 0x00, 0x00, 0x00, 0x54, 0x68, 0x69, 0x6E, 0x67, 0x73, 0x34, 0x55 }
+	},
+	{ 0x00000000 , "lora-00",	// F=0
+		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },
+		{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }
+	}					
+};
+#endif //_LOCALSERVER
diff --git a/ESP-1ch-Gateway-v5.0-master/LICENSE b/ESP-1ch-Gateway-v5.0-master/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..4108bf0bb521077e5308bf6fbb0aae7fec0c4bb8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017, 2018 things4u
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/ESP-1ch-Gateway-v5.0-master/README.md b/ESP-1ch-Gateway-v5.0-master/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..3ca7414fbdd60002146ede87840814d6292239d4
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/README.md
@@ -0,0 +1,406 @@
+# Single Channel LoRaWAN Gateway
+
+Version 5.3.2, July 07, 2018  
+Author: M. Westenberg (mw12554@hotmail.com)  
+Copyright: M. Westenberg (mw12554@hotmail.com)  
+
+All rights reserved. This program and the accompanying materials are made available under the terms 
+of the MIT License which accompanies this distribution, and is available at
+https://opensource.org/licenses/mit-license.php  
+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.
+
+Maintained by Maarten Westenberg (mw12554@hotmail.com)
+
+
+# Description
+
+First of all: PLEASE READ THIS FILE AND HTTP://THINGS4U.GITHUB.IO it should contain most of the 
+information you need to get going.
+Unfortunately I do not have the time to follow up on all emails, and as most information including pin-outs 
+etc etc are contained on these pages I hope you have the time to read them before posting any questions.
+
+I do have more than 10 Wemos D1 mini boards running, some I built myself, 
+some 10+ on Hallard, 3 on ComResult and 4 ESP32 boards. They ALL work without problems
+on this code.
+I did find however that good soldering joints and wiring makes all the difference,
+so if you get resets you cannot explain, please have a second look at your wiring.
+
+This repository contains a proof-of-concept implementation of a single channel LoRaWAN gateway for the ESP8266. 
+Starting version 5.2 also the ESP32 of TTGO (and others) is supported.
+The software implements a standard LoRa gateway with the following exceptions and changes:
+
+-  This LoRa gateway is not a full gateway but it implements just a one-channel/one frequency gateway. 
+The minimum amount of frequencies supported by a full gateway is 3, most support 9 or more frequencies.
+This software started as a proof-of-concept to prove that a single low-cost RRFM95 chip which was present 
+in almost every LoRa node in Europe could be used as a cheap alternative to the far more expensive full 
+gateways that were making use of the SX1301 chip.
+
+- As the software of this gateway will often be used during the development phase of a project or in 
+demo situations, the software is flexible and can be easily configured according to environment or 
+customer requirements. There are two ways of interacting with the software: 
+1. Modifying the ESP-sc-gway.h file at compile time allows the administrator to set almost all parameters. 
+2. Using the webinterface (http://<gateway_IP>) will allow administrators to set and reset several of the 
+parameters at runtime.
+
+Full documentation of the Single Channel Gateway is found at things4u.github.io, please look at the Hardware Guide 
+under the Gateway chapter.
+
+
+## testing
+
+The single channel gateway has been tested on a gateway with the Wemos D1 Mini, using a HopeRF RFM95W transceiver.  
+The LoRa nodes tested againts this gateway are:
+
+- TeensyLC with HopeRF RFM95 radio
+- Arduino Pro-Mini (default Armega328 model, 8MHz 3.3V and 16MHz 3.3V)
+- ESP8266 based nodes with RFM95 transceivers.
+
+The code has been tested on at least 8 separate gateway boards both based on the Hallard and the Comresult boards. 
+I'm still working on the ESP32 pin-out and functions (expected soon).
+
+# Getting Started
+
+It is recommended to compile and start the single channel gateway with as little modificatons as possible. 
+This means that you should use the default settings in the configuration files as much as possible and 
+only change the SSID/Password for your WiFi setup and the pins of your ESP8266 that you use in loraModem.h.
+This section describes the minimum of configuration necessary to get a working gateway which than can be 
+configured further using the webpage.
+
+1. Unpack the source code including the libraries in a separate folder.
+2. Connect the gateway to a serial port of your computer, and configure that port in the IDE. 
+Switch on the Serial Monitor for the gateway. As the Wemos chip does not contain any code, you will probably 
+see nothing on the Serial Monitor.
+3. Modify the _loraModem.h file and change the "struct pins" area and configure either for a traditional
+(=Comresult) PCB or configure for a Hallard PCB where the dio0, dio1 and dio2 pins are shared. You HAVE to check 
+this section.
+4. Edit the ESP-sc-gway.h file and adapt the "wpas" structure. `Make sure that the first line of this structure 
+remains empty and put the SSID and Password of your router on the second line of the array.
+5. In the preferences part of the IDE, set the location of your sketch to the place where you put the 
+sketch on your computer. This will make sure that for example the required libraries that are shipped 
+with this sketch in the libraries folder can be found by the compiler
+6. If not yet done: Load the support for ESP8266 in your IDE. <Tools><Board><Board Manager...>
+7. Load the other necessary libraries that are not shipped with this sketch in your IDE. 
+Goto <Sketch><Include Library><Manage Libraries...> in the IDE to do so. 
+- ArduinoJson (version 5.13.1)
+- WifiManager (Version 0.12.0 by Tzapu)
+8. Compile the code and download the executable over USB to the gateway. If all is right, you should
+see the gateway starting up on the Serial Monitor.
+9. Note the IP address that the device receives from your router. Use that IP address in a browser on 
+your computer to connect to the gateway with the browser.
+
+Now your gateway should be running. Use the webpage to set "debug" to 1 and you should be able to see packages
+coming in on the Serial monitor.
+
+
+# Configuration
+
+There are two ways of changing the configuration of the single channel gateway:
+
+1. Changing the ESP-sc-gway.h file at compile-time
+2. Run the http://<gateway-IP> web interface to change settings at run time.
+
+
+## Editing the ESP-sc-gway.h file
+
+The ESP-sc-gway.h file contains all the user configurable settings. All have their definitions set through #define statements. 
+In general, setting a #define to 1 will enable the function and setting it to 0 will disable it. 
+
+Also, some settings cn be initialised by setting their value with a #define but can be changed at runtime in the web interface.
+For some settings, disabling the function with a #define will remove the function from the webserver as well.
+
+NOTE regarding memory usage: The ESP8266 has an enormous amount of memory available for program space and SPIFFS filesystem. 
+However the memory available for heap and variables is limited to about 80K bytes (For the ESP-32 this is higher). 
+The user is advised to turn off functions not used in order to save on memory usage. 
+If the heap drops below 18 KBytes some functions may not behave as expected (in extreme case the program may crash).
+
+
+### Setting USB
+
+The user can determine whether or not the USB console is used for output messages.
+When setting DUSB to 0 all output by Serial is disabled 
+(actually the Serial statements are not included in the code).
+
+ \#define DUSB 1
+
+
+### Debug
+
+The user can set the initial value of the DEBUG parameter. 
+Setting this parameter will also detemine some settings of the webserver.
+
+ \#define DEBUG 1
+
+ 
+### Selecting you standard pin-out
+
+We support two pin-out configurations out-of-the-box: HALLARD and COMPRESULT.
+If you use one of these two, just set the parameter to the right value.
+If your pin definitions are different, update the loraModem.h file to reflect these settings.
+	1: HALLARD
+	2: COMRESULT pin out
+	3: ESP32 pin out
+	4: Other, define your own in loraModem.h
+
+ \#define _PIN_OUT 1
+
+
+### Forcing a SPIFF format at startup
+
+The following parameter shoudl be set to 0 under normal circumstances.
+It does allow the system to foce formatting of the SPIFFS filesystem.
+
+ \#define SPIFF_FORMAT 0  
+ 
+### Setting Spreading Factor
+
+Set the _SPREADING factor to the desired SF7, SF8 - SF12 value. 
+Please note that this value is closely related to teh value used for _CAD. 
+If _CAD is enabled, the value of _SPREADING is not used by the gateway as it has all sreading factors enabled.
+
+ \#define _SPREADING SF9
+ 
+Please note that the default frequency used is 868.1 MHz which can be changed in the loraModem.h file. 
+The user is advised NOT to change this etting and only use the default 868.1 MHz frequency.
+
+
+### Channel Activity Detection
+
+Channel Activity Detection (CAD) is a function of the LoRa RFM95 chip to detect incoming messages (activity). 
+These incoming messages might arrive on any of the well know spreading factors SF7-SF12. 
+By enabling CAD, the gateway can receive messages of any of the spreading factors.
+
+Actually it is used in normal operation to tell the receiver that another signal is using the 
+channel already.
+
+The CAD functionality comes at a (little) price: The chip will not be able to receive very weak signals as 
+the CAD function will use the RSSI register setting of the chip to determine whether or not it received a 
+signal (or just noise). As a result, very weak signals are not received which means that the range of the 
+gateway will be reduced in CAD mode.
+
+ \#define _CAD 1
+
+ 
+### Over the Air Updates (OTA)
+
+As from version 4.0.6 the gateway allows over the air updating if the setting A_OTA is on. 
+The over the air software requires once setting of the 4.0.6 version over USB to the gateway,
+after which the software is (default) enabled for use.
+
+The first release only supports OTA function using the IDE which in practice means the IDE has to 
+be on the same network segment as the gateway.
+
+Note: You have to use Bonjour software (Apple) on your network somewhere. A version is available
+for most platforms (shipped with iTunes for windows for example). The Bonjour software enables the
+gateway to use mDNS to resolve the gateway ID set by OTA after which download ports show up in the IDE.
+
+Todo: The OTA software has not (yet) been tested in conjuction with the WiFiManager software.
+
+ \#define A_OTA 1  
+
+
+
+### Enable Webserver
+
+This setting enables the webserver. Although the webserver itself takes a lot of memory, it greatly helps 
+to configure the gatewayat run-time and inspects its behaviour. It also provides statistics of last messages received.
+The A_REFRESH parameter defines whether the webserver should renew every X seconds.
+
+ \#define A_SERVER 1				// Define local WebServer only if this define is set  
+ \#define A_REFRESH 0				// Will the webserver refresh or not?  
+ \#define A_SERVERPORT 80			// local webserver port  
+ \#define A_MAXBUFSIZE 192			// Must be larger than 128, but small enough to work  
+
+ The A_REFRESH parameter determines the refresh frequency of the webserver.  
+ 
+### Strict LoRa behaviour
+
+In order to have the gateway send downlink messages on the pre-set spreading factor and on the default frequency, 
+you have to set the _STRICT_1Ch parameter to 1. Note that when it is not set to 1, the gateway will respond to 
+downlink requests with the frequency and spreading factor set by the backend server. And at the moment TTN 
+responds to downlink messages for SF9-SF12 in the RX2 timeslot and with frequency 869.525MHz and on SF12 
+(according to the LoRa standard when sending in the RX2 timeslot). 
+
+ \#define _STRICT_1CH 0
+
+You are advised not to change the default setting of this parameter.
+
+### Enable OLED panel
+
+By setting the OLED you configure the system to work with OLED panels over I2C.
+Some panels work by both SPI and I2C where I2c is solwer. However, since SPI is use for RFM95 transceiver 
+communication you are stronly discouvared using one of these as they will not work with this software.
+Instead choose a OLED solution that works over I2C.
+
+ \#define OLED 1
+
+The following values are defined for OLED:
+1. 0.9 inch OLED screen for I2C bus
+2. 1.1 inch OLED screen for I2C bus
+
+### Define to gather statistics
+
+When this is defined (==1) we will gather the statistics of every message and output
+it to the SPIFFS filesystem. We make sure that we use a number of files with each a fixed number of records
+for statistics. The REC number tells us how many records are allowed in each statistics file.
+As soon as the REC number is higher than the number of records allowed, we open a new file.
+Once the number of files exceeds the NUM amount of statistics files, we delete the oldeest file and 
+open a new file.
+When selecting the "log" button on top of the GUI screen, all rthe log files are ouptu to the USB
+Serial device. This way, we can examine far more records than fitting the GUI screen or the Serial
+output.
+ 
+ \#define STAT_LOG 1
+ 
+
+ 
+Setting the I2C SDA/SCL pins is done in the ESP-sc-gway.h file right after the #define of OLED.
+Standard the ESP8266 uses pins D1 and D2 for the I2C bus SCL and SDA lines but these can be changed by the user.
+Normally thsi is not necessary. 
+The OLED functions are found in the _loraModem.ino file, and can be adapted to show other fields. 
+The functions are called when a message is received(!) and therefore potentionally this will add to the
+instability of the ESP as these functions may require more time than expected.
+If so, swithc off the OLED function or build in a function in the main loop() that displays in user time 
+(not interrupt).
+
+### Setting TTN server
+
+The gateway allows to connect to 2 servers at the same time (as most LoRa gateways do BTW). 
+You have to connect to at least one standard LoRa router, in case you use The Things Network (TTN) 
+than make sure that you set:
+
+ \#define _TTNSERVER "router.eu.thethings.network"  
+ \#define _TTNPORT 1700  
+  
+In case you setup your own server, you can specify as follows using your own router URL and your own port:
+
+ \#define _THINGSERVER "your_server.com"			// Server URL of the LoRa udp.js server program  
+ \#define _THINGPORT 1701							// Your UDP server should listen to this port  
+
+ 
+### Gateway Identity
+Set the identity parameters for your gateway:   
+
+\#define _DESCRIPTION "ESP-Gateway"  
+\#define _EMAIL "your.email@provider.com"  
+\#define _PLATFORM "ESP8266"  
+\#define _LAT 52.00  
+\#define _LON 5.00  
+\#define _ALT 0  
+
+
+### Using the gateway as a sensor node
+
+It is possible to use the gateway as a node. This way, local/internal sensor values are reported.
+This is a cpu and memory intensive function as making a sensor message involves EAS and CMAC functions.
+
+ \#define GATEWAYNODE 0  
+
+Further below in the configuration file, it is possible to set the address and other  LoRa information of the gateway node.
+
+ \#if GATEWAYNODE==1  
+ \#define _DEVADDR { 0x26, 0x01, 0x15, 0x3D }  
+ \#define _APPSKEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }  
+ \#define _NWKSKEY { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }  
+ \#define _SENSOR_INTERVAL 300  
+ \#endif  
+
+
+### Connect to WiFi with WiFiManager
+
+The easiest way to configure the Gateway on WiFi is by using the WiFimanager function. This function works out of the box. 
+WiFiManager will put the gateway in accesspoint mode so that you can connect to it as a WiFi accesspoint. 
+
+ \#define WIFIMANAGER 0  
+
+If Wifi Manager is enabled, make sure to define the name of the accesspoint if the gateway is in accesspoint 
+mode and the password.
+
+ \#define AP_NAME "ESP8266-Gway-Things4U"  
+ \#define AP_PASSWD "ttnAutoPw"  
+
+The standard access point name used by the gateway is "ESP8266 Gway" and its password is "ttnAutoPw". 
+After binding to the access point with your mobile phone or computer, go to htp://192.168.4.1 in a browser and tell the gateway to which WiFi network you want it to connect, and specify the password.
+
+The gateway will then reset and bind to the given network. If all goes well you are now set and the ESP8266 will remember the network that it must connect to. NOTE: As long as the accesspoint that the gateway is bound to is present, the gateway will not any longer work with the wpa list of known access points.
+If necessary, you can delete the current access point in the webserver and power cycle the gateway to force it to read the wpa array again.
+
+## Specify a name for known nodes
+- It is possible to substitue the address for known nodes with a chosen name. This will greatly enhance the readibility of the statistics overview 
+especially for your own nodes, Now you will find names for your own nodes in the webserver.
+- set the TRUSTED_NODES to either 0 (no names), 1 (specify names for known nodes) and 2 (Do not show or transfer to TTN other than known nodes)
+- Although this will work with OTAA nodes as well, please remind that OTAA nodes will change
+their LORA id with every reboot. So for these nodes this function does not add much value.
+
+### Other Settings
+
+- static char *wpa[WPASIZE][2] contains the array of known WiFi access points the Gateway will connect to.
+Make sure that the dimensions of the array are correctly defined in the WPASIZE settings. 
+Note: When the WiFiManager software is enabled (it is by default) there must at least be one entry in the wpa file, wpa[0] is used for storing WiFiManager information.
+- Only the sx1276 (and HopeRF 95) radio modules are supported at this time. The sx1272 code should be 
+working without much work, but as I do not have one of these modules available I cannot test this.
+
+
+## Webserver
+
+The built-in webserver can be used to display status and debugging information. 
+Also the webserver allows the user to change certain settings at run-time such as the debug level or switch on 
+and off the CAD function.
+It can be accessed with the following URL: http://<YourGatewayIP>:80 where <YourGatewayIP> is the IP given by 
+the router to the ESP8266 at startup. It is probably something like 192.168.1.XX
+The webserver shows various configuration settings as well as providing functions to set parameters.
+
+The following parameters can be set using the webServer. 
+- Debug Level (0-4)
+- CAD mode on or off (STD mode)
+- Switch frequency hopping on and off (Set to OFF)
+- When frequency Hopping is off: Select the frequency the gateway will work with. 
+NOTE: Frequency hopping is experimental and does not work correctly.
+- When CAD mode is off: Select the Spreading Factor (SF) the gateway will work with
+
+
+# Dependencies
+
+The software is dependent on several pieces of software, the Arduino IDE for ESP8266 
+being the most important. Several other libraries are also used by this program, 
+make sure you install those libraries with the IDE:
+
+- gBase64 library, The gBase library is actually a base64 library made 
+	by Adam Rudd (url=https://github.com/adamvr/arduino-base64). I changed the name because I had
+	another base64 library installed on my system and they did not coexist well.
+- Time library (http://playground.arduino.cc/code/time)
+- Arduino JSON; Needed to decode downstream messages
+- SimpleTimer; ot yet used, but reserved for interrupt and timing
+- WiFiManager
+- ESP8266 Web Server
+- Streaming library, used in the wwwServer part
+- AES library (taken from ideetron.nl) for downstream messages
+- Time
+
+For convenience, the libraries are also found in this github repository in the libraries directory. 
+Please note that they are NOT part of the ESP 1channel gateway and may have their own licensing.
+However, these libraries are not part of the single-channel Gateway software.
+
+
+# Pin Connections
+
+See http://things4u.github.io in the hardware section for building and connection instructions.
+
+
+# To-DO
+
+The following things are still on my wish list to make to the single channel gateway:  
+
+- Receive downstream message with commands from the server. These can be used to configure
+  the gateway through downlink messages (such as setting the SF)
+- Support for ESP32 and RFM95 on 433 MHz
+- Use the SPIFFS for storing .css files
+- Look at CLass B and C support
+
+
+
+# License
+
+The source files of the gateway sketch in this repository is made available under the MIT
+license. The libraries included in this repository are included for convenience only and all have their own license, 
+and are not part of the ESP 1ch gateway code.
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/ArduinoJson.h b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/ArduinoJson.h
new file mode 100644
index 0000000000000000000000000000000000000000..9f78b9f1853018a8615ff891747766eed910f6bd
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/ArduinoJson.h
@@ -0,0 +1,5 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include "src/ArduinoJson.h"
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/CHANGELOG.md b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/CHANGELOG.md
new file mode 100644
index 0000000000000000000000000000000000000000..1924bd24ec844c826bc22cedfd3908d090794f5f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/CHANGELOG.md
@@ -0,0 +1,463 @@
+ArduinoJson: change log
+=======================
+
+v5.13.1
+-------
+
+* Fixed `JsonVariant::operator|(int)` that returned the default value if the variant contained a double (issue #675)
+* Allowed non-quoted key to contain underscores (issue #665)
+
+v5.13.0
+-------
+
+* Changed the rules of string duplication (issue #658)
+* `RawJson()` accepts any kind of string and obeys to the same rules for duplication
+* Changed the return type of `strdup()` to `const char*` to prevent double duplication
+* Marked `strdup()` as deprecated
+
+> ### New rules for string duplication
+>
+> | type                       | duplication |
+> |:---------------------------|:------------|
+> | const char*                | no          |
+> | char*                      | ~~no~~ yes  |
+> | String                     | yes         |
+> | std::string                | yes         |
+> | const __FlashStringHelper* | yes         |
+>
+> These new rules make `JsonBuffer::strdup()` useless.
+
+v5.12.0
+-------
+
+* Added `JsonVariant::operator|` to return a default value (see below)
+* Added a clear error message when compiled as C instead of C++ (issue #629)
+* Added detection of MPLAB XC compiler (issue #629)
+* Added detection of Keil ARM Compiler (issue #629)
+* Added an example that shows how to save and load a configuration file
+* Reworked all other examples
+
+> ### How to use the new feature?
+>
+> If you have a block like this:
+>
+> ```c++
+> const char* ssid = root["ssid"];
+> if (!ssid)
+>   ssid = "default ssid";
+> ```
+>
+> You can simplify like that:
+>
+> ```c++
+> const char* ssid = root["ssid"] | "default ssid";
+> ```
+
+v5.11.2
+-------
+
+* Fixed `DynamicJsonBuffer::clear()` not resetting allocation size (issue #561)
+* Fixed incorrect rounding for float values (issue #588)
+
+v5.11.1
+-------
+
+* Removed dependency on `PGM_P` as Particle 0.6.2 doesn't define it (issue #546)
+* Fixed warning "dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]"
+* Fixed warning "floating constant exceeds range of 'float' [-Woverflow]" (issue #544)
+* Fixed warning "this statement may fall through" [-Wimplicit-fallthrough=] (issue #539)
+* Removed `ARDUINOJSON_DOUBLE_IS_64BITS` as it became useless.
+* Fixed too many decimals places in float serialization (issue #543)
+
+v5.11.0
+-------
+
+* Made `JsonBuffer` non-copyable (PR #524 by @luisrayas3)
+* Added `StaticJsonBuffer::clear()`
+* Added `DynamicJsonBuffer::clear()`
+
+v5.10.1
+-------
+
+* Fixed IntelliSense errors in Visual Micro (issue #483)
+* Fixed compilation in IAR Embedded Workbench (issue #515)
+* Fixed reading "true" as a float (issue #516)
+* Added `ARDUINOJSON_DOUBLE_IS_64BITS`
+* Added `ARDUINOJSON_EMBEDDED_MODE`
+
+v5.10.0
+-------
+
+* Removed configurable number of decimal places (issues #288, #427 and #506)
+* Changed exponentiation thresholds to `1e7` and `1e-5` (issues #288, #427 and #506)
+* `JsonVariant::is<double>()` now returns `true` for integers
+* Fixed error `IsBaseOf is not a member of ArduinoJson::TypeTraits` (issue #495)
+* Fixed error `forming reference to reference` (issue #495)
+
+> ### BREAKING CHANGES :warning:
+>
+> | Old syntax                      | New syntax          |
+> |:--------------------------------|:--------------------|
+> | `double_with_n_digits(3.14, 2)` | `3.14`              |
+> | `float_with_n_digits(3.14, 2)`  | `3.14f`             |
+> | `obj.set("key", 3.14, 2)`       | `obj["key"] = 3.14` |
+> | `arr.add(3.14, 2)`              | `arr.add(3.14)`     |
+>
+> | Input     | Old output | New output |
+> |:----------|:-----------|:-----------|
+> | `3.14159` | `3.14`     | `3.14159`  |
+> | `42.0`    | `42.00`    | `42`       |
+> | `0.0`     | `0.00`     | `0`        |
+>
+> | Expression                     | Old result | New result |
+> |:-------------------------------|:-----------|:-----------|
+> | `JsonVariant(42).is<int>()`    | `true`     | `true`     |
+> | `JsonVariant(42).is<float>()`  | `false`    | `true`     |
+> | `JsonVariant(42).is<double>()` | `false`    | `true`     |
+
+v5.9.0
+------
+
+* Added `JsonArray::remove(iterator)` (issue #479)
+* Added `JsonObject::remove(iterator)`
+* Renamed `JsonArray::removeAt(size_t)` into `remove(size_t)`
+* Renamed folder `include/` to `src/`
+* Fixed warnings `floating constant exceeds range of float`and `floating constant truncated to zero` (issue #483)
+* Removed `Print` class and converted `printTo()` to a template method (issue #276)
+* Removed example `IndentedPrintExample.ino`
+* Now compatible with Particle 0.6.1, thanks to Jacob Nite (issue #294 and PR #461 by @foodbag)
+
+v5.8.4
+------
+
+* Added custom implementation of `strtod()` (issue #453)
+* Added custom implementation of `strtol()` (issue #465)
+* `char` is now treated as an integral type (issue #337, #370)
+
+v5.8.3
+------
+
+* Fixed an access violation in `DynamicJsonBuffer` when memory allocation fails (issue #433)
+* Added operators `==` and `!=` for two `JsonVariant`s (issue #436)
+* Fixed `JsonVariant::operator[const FlashStringHelper*]` (issue #441)
+
+v5.8.2
+------
+
+* Fixed parsing of comments (issue #421)
+* Fixed ignored `Stream` timeout (issue #422)
+* Made sure we don't read more that necessary (issue #422)
+* Fixed error when the key of a `JsonObject` is a `char[]` (issue #423)
+* Reduced code size when using `const` references
+* Fixed error with string of type `unsigned char*` (issue #428)
+* Added `deprecated` attribute on `asArray()`, `asObject()` and `asString()` (issue #420)
+
+v5.8.1
+------
+
+* Fixed error when assigning a `volatile int` to a `JsonVariant` (issue #415)
+* Fixed errors with Variable Length Arrays (issue #416)
+* Fixed error when both `ARDUINOJSON_ENABLE_STD_STREAM` and `ARDUINOJSON_ENABLE_ARDUINO_STREAM` are set to `1`
+* Fixed error "Stream does not name a type" (issue #412)
+
+v5.8.0
+------
+
+* Added operator `==` to compare `JsonVariant` and strings (issue #402)
+* Added support for `Stream` (issue #300)
+* Reduced memory consumption by not duplicating spaces and comments
+
+> ### BREAKING CHANGES :warning:
+>
+> `JsonBuffer::parseObject()` and  `JsonBuffer::parseArray()` have been pulled down to the derived classes `DynamicJsonBuffer` and `StaticJsonBufferBase`.
+>
+> This means that if you have code like:
+>
+> ```c++
+> void myFunction(JsonBuffer& jsonBuffer);
+> ```
+>
+> you need to replace it with one of the following:
+>
+> ```c++
+> void myFunction(DynamicJsonBuffer& jsonBuffer);
+> void myFunction(StaticJsonBufferBase& jsonBuffer);
+> template<typename TJsonBuffer> void myFunction(TJsonBuffer& jsonBuffer);
+> ```
+
+v5.7.3
+------
+
+* Added an `printTo(char[N])` and `prettyPrintTo(char[N])` (issue #292)
+* Added ability to set a nested value like this: `root["A"]["B"] = "C"` (issue #352)
+* Renamed `*.ipp` to `*Impl.hpp` because they were ignored by Arduino IDE (issue #396)
+
+v5.7.2
+------
+
+* Made PROGMEM available on more platforms (issue #381)
+* Fixed PROGMEM causing an exception on ESP8266 (issue #383)
+
+v5.7.1
+------
+
+* Added support for PROGMEM (issue #76)
+* Fixed compilation error when index is not an `int` (issue #381)
+
+v5.7.0
+------
+
+* Templatized all functions using `String` or `std::string`
+* Removed `ArduinoJson::String`
+* Removed `JsonVariant::defaultValue<T>()`
+* Removed non-template `JsonObject::get()` and `JsonArray.get()`
+* Fixed support for `StringSumHelper` (issue #184)
+* Replaced `ARDUINOJSON_USE_ARDUINO_STRING` by `ARDUINOJSON_ENABLE_STD_STRING` and `ARDUINOJSON_ENABLE_ARDUINO_STRING` (issue #378)
+* Added example `StringExample.ino` to show where `String` can be used
+* Increased default nesting limit to 50 when compiled for a computer (issue #349)
+
+> ### BREAKING CHANGES :warning:
+>
+> The non-template functions `JsonObject::get()` and `JsonArray.get()` have been removed. This means that you need to explicitely tell the type you expect in return.
+>
+> Old code:
+>
+> ```c++
+> #define ARDUINOJSON_USE_ARDUINO_STRING 0
+> JsonVariant value1 = myObject.get("myKey");
+> JsonVariant value2 = myArray.get(0);
+> ```
+>
+> New code:
+>
+> ```c++
+> #define ARDUINOJSON_ENABLE_ARDUINO_STRING 0
+> #define ARDUINOJSON_ENABLE_STD_STRING 1
+> JsonVariant value1 = myObject.get<JsonVariant>("myKey");
+> JsonVariant value2 = myArray.get<JsonVariant>(0);
+> ```
+
+v5.6.7
+------
+
+* Fixed `array[idx].as<JsonVariant>()` and `object[key].as<JsonVariant>()`
+* Fixed return value of `JsonObject::set()` (issue #350)
+* Fixed undefined behavior in `Prettyfier` and `Print` (issue #354)
+* Fixed parser that incorrectly rejected floats containing a `+` (issue #349)
+
+v5.6.6
+------
+
+* Fixed `-Wparentheses` warning introduced in v5.6.5 (PR #335 by @nuket)
+* Added `.mbedignore` for ARM mbdeb (PR #334 by @nuket)
+* Fixed  `JsonVariant::success()` which didn't propagate `JsonArray::success()` nor `JsonObject::success()` (issue #342).
+
+v5.6.5
+------
+
+* `as<char*>()` now returns `true` when input is `null` (issue #330)
+
+v5.6.4
+------
+
+* Fixed error in float serialization (issue #324)
+
+v5.6.3
+------
+
+* Improved speed of float serialization (about twice faster)
+* Added `as<JsonArray>()` as a synonym for `as<JsonArray&>()`... (issue #291)
+* Fixed `call of overloaded isinf(double&) is ambiguous` (issue #284)
+
+v5.6.2
+------
+
+* Fixed build when another lib does `#undef isnan` (issue #284)
+
+v5.6.1
+------
+
+* Added missing `#pragma once` (issue #310)
+
+v5.6.0
+------
+
+* ArduinoJson is now a header-only library (issue #199)
+
+v5.5.1
+------
+
+* Fixed compilation error with Intel Galileo (issue #299)
+
+v5.5.0
+------
+
+* Added `JsonVariant::success()` (issue #279)
+* Renamed `JsonVariant::invalid<T>()` to `JsonVariant::defaultValue<T>()`
+
+v5.4.0
+------
+
+* Changed `::String` to `ArduinoJson::String` (issue #275)
+* Changed `::Print` to `ArduinoJson::Print` too
+
+v5.3.0
+------
+
+* Added custom implementation of `ftoa` (issues #266, #267, #269 and #270)
+* Added `JsonVariant JsonBuffer::parse()` (issue #265)
+* Fixed `unsigned long` printed as `signed long` (issue #170)
+
+v5.2.0
+------
+
+* Added `JsonVariant::as<char*>()` as a synonym for `JsonVariant::as<const char*>()` (issue #257)
+* Added example `JsonHttpClient` (issue #256)
+* Added `JsonArray::copyTo()` and `JsonArray::copyFrom()` (issue #254)
+* Added `RawJson()` to insert pregenerated JSON portions (issue #259)
+
+v5.1.1
+------
+
+* Removed `String` duplication when one replaces a value in a `JsonObject` (PR #232 by @ulion)
+
+v5.1.0
+------
+
+* Added support of `long long` (issue #171)
+* Moved all build settings to `ArduinoJson/Configuration.hpp`
+
+> ### BREAKING CHANGE :warning:
+>
+> If you defined `ARDUINOJSON_ENABLE_STD_STREAM`, you now need to define it to `1`.
+
+v5.0.8
+------
+
+* Made the library compatible with [PlatformIO](http://platformio.org/) (issue #181)
+* Fixed `JsonVariant::is<bool>()` that was incorrectly returning false (issue #214)
+
+v5.0.7
+------
+
+* Made library easier to use from a CMake project: simply `add_subdirectory(ArduinoJson/src)`
+* Changed `String` to be a `typedef` of `std::string` (issues #142 and #161)
+
+> ### BREAKING CHANGES :warning:
+>
+> - `JsonVariant(true).as<String>()` now returns `"true"` instead of `"1"`
+> - `JsonVariant(false).as<String>()` now returns `"false"` instead of `"0"`
+
+v5.0.6
+------
+
+* Added parameter to `DynamicJsonBuffer` constructor to set initial size (issue #152)
+* Fixed warning about library category in Arduino 1.6.6 (issue #147)
+* Examples: Added a loop to wait for serial port to be ready (issue #156)
+
+v5.0.5
+------
+
+* Added overload `JsonObjectSuscript::set(value, decimals)` (issue #143)
+* Use `float` instead of `double` to reduce the size of `JsonVariant` (issue #134)
+
+v5.0.4
+------
+
+* Fixed ambiguous overload with `JsonArraySubscript` and `JsonObjectSubscript` (issue #122)
+
+v5.0.3
+------
+
+* Fixed `printTo(String)` which wrote numbers instead of strings (issue #120)
+* Fixed return type of `JsonArray::is<T>()` and some others (issue #121)
+
+v5.0.2
+------
+
+* Fixed segmentation fault in `parseObject(String)` and `parseArray(String)`, when the
+  `StaticJsonBuffer` is too small to hold a copy of the string
+* Fixed Clang warning "register specifier is deprecated" (issue #102)
+* Fixed GCC warning "declaration shadows a member" (issue #103)
+* Fixed memory alignment, which made ESP8266 crash (issue #104)
+* Fixed compilation on Visual Studio 2010 and 2012 (issue #107)
+
+v5.0.1
+------
+
+* Fixed compilation with Arduino 1.0.6 (issue #99)
+
+v5.0.0
+------
+
+* Added support of `String` class (issues #55, #56, #70, #77)
+* Added `JsonBuffer::strdup()` to make a copy of a string (issues #10, #57)
+* Implicitly call `strdup()` for `String` but not for `char*` (issues #84, #87)
+* Added support of non standard JSON input (issue #44)
+* Added support of comments in JSON input (issue #88)
+* Added implicit cast between numerical types (issues #64, #69, #93)
+* Added ability to read number values as string (issue #90)
+* Redesigned `JsonVariant` to leverage converting constructors instead of assignment operators (issue #66)
+* Switched to new the library layout (requires Arduino 1.0.6 or above)
+
+> ### BREAKING CHANGES :warning:
+>
+> - `JsonObject::add()` was renamed to `set()`
+> - `JsonArray::at()` and `JsonObject::at()` were renamed to `get()`
+> - Number of digits of floating point value are now set with `double_with_n_digits()`
+
+**Personal note about the `String` class**:
+Support of the `String` class has been added to the library because many people use it in their programs.
+However, you should not see this as an invitation to use the `String` class.
+The `String` class is **bad** because it uses dynamic memory allocation.
+Compared to static allocation, it compiles to a bigger, slower program, and is less predictable.
+You certainly don't want that in an embedded environment!
+
+v4.6
+----
+
+* Fixed segmentation fault in `DynamicJsonBuffer` when memory allocation fails (issue #92)
+
+v4.5
+----
+
+* Fixed buffer overflow when input contains a backslash followed by a terminator (issue #81)
+
+**Upgrading is recommended** since previous versions contain a potential security risk.
+
+Special thanks to [Giancarlo Canales Barreto](https://github.com/gcanalesb) for finding this nasty bug.
+
+v4.4
+----
+
+* Added `JsonArray::measureLength()` and `JsonObject::measureLength()` (issue #75)
+
+v4.3
+----
+
+* Added `JsonArray::removeAt()` to remove an element of an array (issue #58)
+* Fixed stack-overflow in `DynamicJsonBuffer` when parsing huge JSON files (issue #65)
+* Fixed wrong return value of `parseArray()` and `parseObject()` when allocation fails (issue #68)
+
+v4.2
+----
+
+* Switched back to old library layout (issues #39, #43 and #45)
+* Removed global new operator overload (issue #40, #45 and #46)
+* Added an example with EthernetServer
+
+v4.1
+----
+
+* Added DynamicJsonBuffer (issue #19)
+
+v4.0
+----
+
+* Unified parser and generator API (issue #23)
+* Updated library layout, now requires Arduino 1.0.6 or newer
+
+> ### BREAKING CHANGES :warning:
+>
+> API changed significantly since v3, see [Migrating code to the new API](https://arduinojson.org/doc/migration/).
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..66c565b5b46f82c479c9b4281e3a8015e200c3f2
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/CMakeLists.txt
@@ -0,0 +1,16 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+cmake_minimum_required(VERSION 3.0)
+project(ArduinoJson)
+
+enable_testing()
+
+if(${COVERAGE})
+	set(CMAKE_CXX_FLAGS "-g -O0 -fprofile-arcs -ftest-coverage")
+endif()
+
+include_directories(${CMAKE_CURRENT_LIST_DIR}/src)
+add_subdirectory(third-party/catch)
+add_subdirectory(test)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/CONTRIBUTING.md b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/CONTRIBUTING.md
new file mode 100644
index 0000000000000000000000000000000000000000..5d4b96cf9c6770645024d2aa1f48b8592ddef822
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/CONTRIBUTING.md
@@ -0,0 +1,11 @@
+# Contribution to ArduinoJson
+
+First, thank you for taking the time to contribute to this project.
+
+You can submit changes via GitHub Pull Requests.
+
+Please:
+
+1. Unit test every change in behavior
+2. Use clang-format in "file" mode to format the code
+3. Consider using the Continuous Integration (Travis and AppVeyor)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/LICENSE.md b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/LICENSE.md
new file mode 100644
index 0000000000000000000000000000000000000000..f0c4b5ae785af3d73da05a8f1099618eb1bb14b3
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/LICENSE.md
@@ -0,0 +1,10 @@
+The MIT License (MIT)
+---------------------
+
+Copyright © 2014-2018 Benoit BLANCHON
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/README.md b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..8ddc698fc4fe67a148410e856424a97b4a42d9ad
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/README.md
@@ -0,0 +1,110 @@
+![ArduinoJson](banner.svg)
+
+---
+
+[![Build status](https://ci.appveyor.com/api/projects/status/m7s53wav1l0abssg/branch/master?svg=true)](https://ci.appveyor.com/project/bblanchon/arduinojson/branch/master) [![Build Status](https://travis-ci.org/bblanchon/ArduinoJson.svg?branch=master)](https://travis-ci.org/bblanchon/ArduinoJson) [![Coverage Status](https://img.shields.io/coveralls/bblanchon/ArduinoJson.svg)](https://coveralls.io/r/bblanchon/ArduinoJson?branch=master) [![Star this project](http://githubbadges.com/star.svg?user=bblanchon&repo=ArduinoJson&style=flat&color=fff&background=007ec6)](https://github.com/bblanchon/ArduinoJson)
+
+ArduinoJson is a C++ JSON library for Arduino and IoT (Internet Of Things).
+
+## Features
+
+* JSON decoding (comments are supported)
+* JSON encoding (with optional indentation)
+* Elegant API, easy to use
+* Fixed memory allocation (zero malloc)
+* No data duplication (zero copy)
+* Portable (written in C++98, can be used in any C++ project)
+* Self-contained (no external dependency)
+* Small footprint
+* Input and output streams
+* [100% code coverage](https://coveralls.io/github/bblanchon/ArduinoJson)
+* [Header-only library](https://en.wikipedia.org/wiki/Header-only)
+* [MIT License](https://en.wikipedia.org/wiki/MIT_License)
+* [Comprehensive documentation](https://arduinojson.org?utm_source=github&utm_medium=readme)
+
+## Compatibility
+
+ArduinoJson works on the following hardware:
+
+* <img src="https://www.arduino.cc/favicon.ico" height="16" width="16"> Arduino boards: [Uno](https://www.arduino.cc/en/Main/ArduinoBoardUno), [Due](https://www.arduino.cc/en/Main/ArduinoBoardDue), [Mini](https://www.arduino.cc/en/Main/ArduinoBoardMini), [Micro](https://www.arduino.cc/en/Main/ArduinoBoardMicro), [Yun](https://www.arduino.cc/en/Main/ArduinoBoardYun)...
+* <img src="http://espressif.com/sites/all/themes/espressif/favicon.ico" height="16" width="16"> Espressif chips: [ESP8266](https://en.wikipedia.org/wiki/ESP8266), [ESP32](https://en.wikipedia.org/wiki/ESP32)
+* <img src="https://www.wemos.cc/themes/martin-materialize-parallax/assets/favicon.ico" height="16" width="16"> WeMos boards: [D1](https://wiki.wemos.cc/products:d1:d1), [D1 mini](https://wiki.wemos.cc/products:d1:d1_mini),  ...
+* <img src="http://redbearlab.com/favicon.ico" height="16" width="16"> RedBearLab boards: [BLE Nano](http://redbearlab.com/blenano/), [BLE Mini](http://redbearlab.com/blemini/), [WiFi Micro](https://redbear.cc/product/wifi/wifi-micro.html), [LOLIN32](https://wiki.wemos.cc/products:lolin32:lolin32)...
+* <img src="https://www.pjrc.com/favicon.ico" height="16" width="16"> [Teensy](https://www.pjrc.com/teensy/) boards
+* <img src="https://software.intel.com/sites/all/themes/zero/favicon.ico" height="16" width="16"> Intel boards: Edison, Galileo...
+* <img src="https://www-assets.particle.io/images/favicon.png"  height="16" width="16"> Particle boards: [Photon](https://www.particle.io/products/hardware/photon-wifi-dev-kit), [Electron](https://www.particle.io/products/hardware/electron-cellular-dev-kit)...
+* <img src="http://www.ti.com/favicon.ico" height="16" width="16"> Texas Instruments boards: [MSP430](http://www.ti.com/microcontrollers/msp430-ultra-low-power-mcus/overview/overview.html)...
+
+ArduinoJson compiles with zero warning on the following compilers, IDEs, and platforms:
+
+* <img src="https://www.arduino.cc/favicon.ico" height="16" width="16"> [Arduino IDE](https://www.arduino.cc/en/Main/Software)
+* <img src="http://cdn.platformio.org/favicon.ico" height="16" width="16"> [PlatformIO](http://platformio.org/)
+* <img src="http://energia.nu/img/favicon.ico" height="16" width="16"> [Energia](http://energia.nu/)
+* <img src="http://www.visualmicro.com/pics/arduino-visual-studio-ld.png" height="16" width="16"> [Visual Micro](http://www.visualmicro.com/)
+* <img src="http://www.atmel.com/Images/favicon.ico" height="16" width="16"> [Atmel Studio](http://www.atmel.com/microsite/atmel-studio/)
+* <img src="https://www.iar.com/favicon.ico" height="16" width="16"> [IAR Embedded Workbench](https://www.iar.com/iar-embedded-workbench/)
+* <img src="http://www.st.com/etc/clientlibs/st-site/media/app/images/favicon.png" height="16" width="16"> [Atollic TrueSTUDIO](https://atollic.com/truestudio/)
+* <img src="http://www.keil.com/favicon.ico" height="16" width="16"> [Keil uVision](http://www.keil.com/)
+* <img src="http://www.microchip.com/favicon.ico" height="16" width="16"> [MPLAB X IDE](http://www.microchip.com/mplab/mplab-x-ide)
+* <img src="https://gcc.gnu.org/favicon.ico" height="16" width="16"> [GCC](https://gcc.gnu.org/)
+* <img src="https://clang.llvm.org/favicon.ico" height="16" width="16"> [Clang](https://clang.llvm.org/)
+* <img src="https://www.visualstudio.com/favicon.ico" height="16" width="16"> [Visual Studio](https://www.visualstudio.com/)
+
+## Quickstart
+
+### Deserialization
+
+Here is a program that parses a JSON document with ArduinoJson.
+
+```c++
+char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
+
+StaticJsonBuffer<200> jsonBuffer;
+
+JsonObject& root = jsonBuffer.parseObject(json);
+
+const char* sensor = root["sensor"];
+long time          = root["time"];
+double latitude    = root["data"][0];
+double longitude   = root["data"][1];
+```
+
+See the [tutorial on arduinojson.org](https://arduinojson.org/doc/decoding/?utm_source=github&utm_medium=readme)
+
+### Serialization
+
+Here is a program that generates a JSON document with ArduinoJson:
+
+```c++
+StaticJsonBuffer<200> jsonBuffer;
+
+JsonObject& root = jsonBuffer.createObject();
+root["sensor"] = "gps";
+root["time"] = 1351824120;
+
+JsonArray& data = root.createNestedArray("data");
+data.add(48.756080);
+data.add(2.302038);
+
+root.printTo(Serial);
+// This prints:
+// {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
+```
+
+See the [tutorial on arduinojson.org](https://arduinojson.org/doc/encoding/?utm_source=github&utm_medium=readme)
+
+## Documentation
+
+The documentation is available on [arduinojson.org](https://arduinojson.org/?utm_source=github&utm_medium=readme), here are some shortcuts:
+
+* The [Examples](https://arduinojson.org/example/?utm_source=github&utm_medium=readme) show how to use the library in various situations.
+* The [API Reference](https://arduinojson.org/api/?utm_source=github&utm_medium=readme) contains the description of each class and function.
+* The [FAQ](https://arduinojson.org/faq/?utm_source=github&utm_medium=readme) has the answer to virtually every question.
+* The [ArduinoJson Assistant](https://arduinojson.org/assistant/?utm_source=github&utm_medium=readme) writes programs for you!
+
+---
+
+Do you like this library? Please [star this project on GitHub](https://github.com/bblanchon/ArduinoJson/stargazers)!
+
+What? You don't like it but you *love* it?
+We don't take donations anymore, but [we sell a book](https://arduinojson.org/book/?utm_source=github&utm_medium=readme), so you can help and learn at the same time!
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/SUPPORT.md b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/SUPPORT.md
new file mode 100644
index 0000000000000000000000000000000000000000..c47e1b1ba86aa7c7ddd82b78d208492ae2e19e49
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/SUPPORT.md
@@ -0,0 +1,27 @@
+# ArduinoJson Support
+
+First off, thank you very much for using ArduinoJson.
+
+We'll be very happy to help you, but first please read the following.
+
+## Before asking for help
+
+1. Read the [FAQ](https://arduinojson.org/faq/?utm_source=github&utm_medium=support)
+2. Search in the [API Reference](https://arduinojson.org/api/?utm_source=github&utm_medium=support)
+
+If you did not find the answer, please create a [new issue on GitHub](https://github.com/bblanchon/ArduinoJson/issues/new).
+
+It is OK to add a comment to a currently opened issue, but please avoid adding comments to a closed issue.
+
+## Before hitting the Submit button
+
+Please provide all the relevant information:
+
+* Good title
+* Short description of the problem
+* Target platform
+* Compiler model and version
+* [MVCE](https://stackoverflow.com/help/mcve)
+* Compiler output
+
+Good questions get fast answers!
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/appveyor.yml b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/appveyor.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f5d79b18991cedc7a061f6e8b4d7f34bc9fe47e9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/appveyor.yml
@@ -0,0 +1,20 @@
+version: 5.13.1.{build}
+environment:
+  matrix:
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2017
+    CMAKE_GENERATOR: Visual Studio 15 2017
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2015
+    CMAKE_GENERATOR: Visual Studio 14 2015
+  - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2013
+    CMAKE_GENERATOR: Visual Studio 12 2013
+  - CMAKE_GENERATOR: Visual Studio 11 2012
+  - CMAKE_GENERATOR: Visual Studio 10 2010
+  - CMAKE_GENERATOR: MinGW Makefiles
+configuration: Debug
+before_build:
+- set PATH=C:\MinGW\bin;%PATH:C:\Program Files\Git\usr\bin;=% # Workaround for CMake not wanting sh.exe on PATH for MinGW
+- cmake -DCMAKE_BUILD_TYPE=%CONFIGURATION% -G "%CMAKE_GENERATOR%" .
+build_script:
+- cmake --build . --config %CONFIGURATION%
+test_script:
+- ctest --output-on-failure .
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/banner.svg b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/banner.svg
new file mode 100644
index 0000000000000000000000000000000000000000..5176096250f04cc7e50dc5e3160c5f2cb0ad957e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/banner.svg
@@ -0,0 +1,367 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="1001"
+   height="250"
+   viewBox="0 0 264.84791 66.145837"
+   version="1.1"
+   id="svg8"
+   inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
+   sodipodi:docname="logo.svg">
+  <defs
+     id="defs2">
+    <clipPath
+       id="clipPath831"
+       clipPathUnits="userSpaceOnUse">
+      <path
+         inkscape:connector-curvature="0"
+         id="path829"
+         d="M 0,638 H 1000 V 0 H 0 Z" />
+    </clipPath>
+    <clipPath
+       id="clipPath839"
+       clipPathUnits="userSpaceOnUse">
+      <path
+         inkscape:connector-curvature="0"
+         id="path837"
+         d="M 0,638 H 1000 V 0 H 0 Z" />
+    </clipPath>
+  </defs>
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.7"
+     inkscape:cx="399.91304"
+     inkscape:cy="242.52487"
+     inkscape:document-units="mm"
+     inkscape:current-layer="g823"
+     showgrid="false"
+     fit-margin-top="5"
+     fit-margin-bottom="5"
+     fit-margin-right="5"
+     fit-margin-left="5"
+     inkscape:window-width="1920"
+     inkscape:window-height="1017"
+     inkscape:window-x="-8"
+     inkscape:window-y="-8"
+     inkscape:window-maximized="1"
+     units="px" />
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-1.2883269,-86.047553)">
+    <g
+       transform="matrix(0.35277777,0,0,-0.35277777,-137.80575,256.55859)"
+       inkscape:label="logo (2)"
+       id="g823">
+      <g
+         id="g825"
+         transform="matrix(0.44331091,0,0,0.44331091,559.35527,248.127)">
+        <g
+           clip-path="url(#clipPath831)"
+           id="g827">
+          <g
+             transform="translate(246.9092,120.1406)"
+             id="g847">
+            <path
+               inkscape:connector-curvature="0"
+               id="path849"
+               style="fill:#00868e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 20.015,58.057 H 39.896 L 59.909,0 H 44.138 L 41.22,10.34 H 18.82 L 15.772,0 Z m 22.135,21.605 h 15.771 l -5.83,19.219 c -0.265,0.53 -0.53,1.457 -0.797,2.787 -0.398,1.323 -0.795,3.043 -1.192,5.034 -0.398,-1.461 -0.662,-2.784 -1.062,-4.11 -0.397,-1.322 -0.662,-2.519 -1.059,-3.711 z" />
+          </g>
+          <g
+             transform="translate(311.457,120.1406)"
+             id="g851">
+            <path
+               inkscape:connector-curvature="0"
+               id="path853"
+               style="fill:#00868e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 v 40.029 h 13.387 v -9.41 c 1.326,3.447 3.182,5.965 5.566,7.69 2.387,1.72 5.304,2.648 8.616,2.648 0.661,0 1.192,-0.133 1.723,-0.133 0.53,0 1.192,-0.135 1.722,-0.135 L 29.689,27.836 c -0.928,0.266 -1.723,0.533 -2.517,0.664 -0.795,0.133 -1.592,0.133 -2.386,0.133 -3.446,0 -6.098,-1.065 -7.953,-3.182 C 14.978,23.332 14.05,20.414 14.05,16.568 V 0 Z" />
+          </g>
+          <g
+             transform="translate(390.7183,181.6387)"
+             id="g855">
+            <path
+               inkscape:connector-curvature="0"
+               id="path857"
+               style="fill:#00868e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 v -61.498 h -12.989 v 6.494 c -1.724,-2.648 -3.844,-4.769 -5.966,-6.096 -2.253,-1.322 -4.771,-1.984 -7.554,-1.984 -5.567,0 -10.073,1.984 -13.387,5.83 -3.314,3.977 -5.036,9.147 -5.036,15.772 0,6.359 1.722,11.531 5.036,15.375 3.314,3.976 7.556,5.962 12.856,5.962 3.446,0 6.229,-0.664 8.35,-1.853 2.121,-1.191 3.977,-3.314 5.701,-6.098 -0.265,0.928 -0.265,1.989 -0.397,3.182 -0.135,1.193 -0.135,2.519 -0.135,3.975 V 0 Z m -12.459,-41.482 c 0,3.05 -0.795,5.566 -2.519,7.287 -1.591,1.859 -3.845,2.787 -6.76,2.787 -2.783,0 -5.036,-0.928 -6.761,-2.787 -1.588,-1.721 -2.385,-4.237 -2.385,-7.287 0,-3.182 0.797,-5.698 2.385,-7.426 1.725,-1.852 3.978,-2.647 6.761,-2.647 2.915,0 5.169,0.795 6.76,2.647 1.724,1.859 2.519,4.244 2.519,7.426" />
+          </g>
+          <g
+             transform="translate(442.2769,160.1699)"
+             id="g859">
+            <path
+               inkscape:connector-curvature="0"
+               id="path861"
+               style="fill:#00868e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 v -20.412 c 0,-4.506 -0.265,-7.953 -1.061,-10.072 -0.663,-2.254 -1.989,-4.11 -3.71,-5.83 -1.724,-1.727 -3.978,-3.051 -6.496,-3.979 -2.65,-0.793 -5.831,-1.322 -9.41,-1.322 -3.578,0 -6.626,0.529 -9.277,1.322 -2.651,0.928 -4.771,2.252 -6.628,3.979 -1.724,1.587 -3.048,3.576 -3.712,5.83 -0.663,2.119 -1.06,5.566 -1.06,10.072 V 0 h 13.519 v -21.336 c 0,-3.316 0.532,-5.836 1.591,-7.293 1.195,-1.457 2.916,-2.252 5.434,-2.252 2.518,0 4.375,0.795 5.435,2.252 1.06,1.457 1.59,3.842 1.59,7.293 V 0 Z" />
+          </g>
+          <path
+             inkscape:connector-curvature="0"
+             id="path863"
+             style="fill:#00868e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+             d="m 452.88,160.17 h 14.05 v -40.029 h -14.05 z m -0.795,12.461 c 0,2.119 0.795,3.974 2.386,5.435 1.457,1.588 3.313,2.25 5.434,2.25 2.254,0 4.108,-0.662 5.567,-2.119 1.458,-1.592 2.254,-3.316 2.254,-5.566 0,-2.123 -0.796,-3.977 -2.254,-5.57 -1.592,-1.457 -3.447,-2.25 -5.567,-2.25 -2.121,0 -3.977,0.793 -5.434,2.25 -1.591,1.593 -2.386,3.447 -2.386,5.57" />
+          <g
+             transform="translate(477.7983,120.1406)"
+             id="g865">
+            <path
+               inkscape:connector-curvature="0"
+               id="path867"
+               style="fill:#00868e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 v 40.029 h 12.856 v -8.088 c 1.988,3.186 4.109,5.567 6.496,7.163 2.517,1.455 5.434,2.25 8.881,2.25 2.648,0 4.904,-0.53 6.891,-1.325 1.99,-0.795 3.712,-1.984 4.903,-3.58 1.059,-1.322 1.854,-2.912 2.256,-4.902 0.529,-1.986 0.662,-4.774 0.662,-8.613 V 0 h -14.05 v 21.473 c 0,3.181 -0.53,5.433 -1.59,6.896 -1.062,1.455 -2.651,2.25 -5.035,2.25 -2.918,0 -5.04,-1.058 -6.365,-3.316 C 14.58,25.184 13.918,21.605 13.918,16.834 V 0 Z" />
+          </g>
+          <g
+             transform="translate(573.2275,140.0254)"
+             id="g869">
+            <path
+               inkscape:connector-curvature="0"
+               id="path871"
+               style="fill:#00868e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 c 0,-2.918 -0.529,-5.566 -1.721,-8.221 -1.059,-2.515 -2.653,-4.904 -4.905,-6.89 -2.118,-2.125 -4.507,-3.711 -7.287,-4.774 -2.653,-1.056 -5.439,-1.586 -8.484,-1.586 -3.05,0 -5.963,0.53 -8.617,1.586 -2.653,1.063 -5.037,2.649 -7.155,4.774 -2.124,1.986 -3.845,4.242 -4.904,6.89 -1.064,2.522 -1.594,5.303 -1.594,8.221 0,2.918 0.53,5.697 1.594,8.35 1.059,2.515 2.648,4.904 4.904,6.892 1.986,2.117 4.37,3.578 7.155,4.639 2.654,1.058 5.567,1.588 8.617,1.588 3.045,0 5.962,-0.53 8.612,-1.588 2.652,-1.061 5.041,-2.522 7.159,-4.639 2.252,-2.119 3.846,-4.377 4.905,-7.025 C -0.529,5.697 0,2.918 0,0 m -14.05,0 c 0,3.049 -0.795,5.299 -2.252,7.021 -1.457,1.727 -3.579,2.655 -6.095,2.655 -2.654,0 -4.639,-0.928 -6.096,-2.655 -1.461,-1.722 -2.256,-3.972 -2.256,-7.021 0,-2.918 0.795,-5.303 2.256,-7.027 1.457,-1.725 3.442,-2.518 6.096,-2.518 2.516,0 4.638,0.793 6.095,2.518 1.457,1.724 2.252,4.109 2.252,7.027" />
+          </g>
+          <g
+             transform="translate(593.7715,178.1973)"
+             id="g873">
+            <path
+               inkscape:connector-curvature="0"
+               id="path875"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 h 15.776 v -37.643 c 0,-4.109 -0.264,-7.287 -0.799,-9.545 -0.53,-2.117 -1.457,-4.109 -2.649,-5.832 -1.589,-2.115 -3.58,-3.843 -5.964,-4.902 -2.387,-1.191 -4.903,-1.721 -7.821,-1.721 -3.712,0 -7.022,0.793 -9.808,2.383 -2.914,1.588 -5.301,3.846 -7.289,6.889 l 9.676,8.75 c 0.133,-1.723 0.796,-3.045 1.589,-3.977 0.928,-0.927 1.987,-1.326 3.316,-1.326 1.456,0 2.516,0.797 3.178,2.524 0.529,1.586 0.795,5.83 0.795,12.455 z" />
+          </g>
+          <g
+             transform="translate(616.4375,123.457)"
+             id="g877">
+            <path
+               inkscape:connector-curvature="0"
+               id="path879"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 3.051,10.734 C 5.036,9.01 7.288,7.553 9.809,6.627 c 2.519,-0.928 4.903,-1.459 7.424,-1.459 1.854,0 3.178,0.268 4.105,0.928 0.933,0.531 1.329,1.326 1.329,2.384 0,1.86 -1.858,3.182 -5.571,3.979 -1.192,0.264 -2.251,0.529 -2.913,0.793 -4.242,1.191 -7.292,2.652 -9.279,4.639 -1.853,1.859 -2.786,4.244 -2.786,7.293 0,3.839 1.462,7.021 4.375,9.277 2.917,2.385 6.895,3.576 11.8,3.576 2.648,0 5.168,-0.396 7.816,-0.928 2.654,-0.66 5.434,-1.588 8.22,-2.785 l -2.918,-9.67 c -1.853,1.192 -3.841,2.25 -5.698,2.912 -1.986,0.664 -3.846,0.928 -5.831,0.928 -1.589,0 -2.786,-0.264 -3.713,-0.793 -0.794,-0.533 -1.192,-1.193 -1.192,-2.119 0,-1.463 1.855,-2.654 5.566,-3.582 0.928,-0.264 1.457,-0.396 1.859,-0.529 4.904,-1.455 8.348,-3.051 10.205,-4.903 1.987,-1.988 2.914,-4.507 2.914,-7.691 0,-4.104 -1.589,-7.42 -4.771,-9.939 -3.047,-2.518 -7.289,-3.84 -12.457,-3.84 -3.448,0 -6.763,0.396 -9.809,1.191 C 5.566,-2.918 2.653,-1.592 0,0" />
+          </g>
+          <g
+             transform="translate(701.9268,140.0254)"
+             id="g881">
+            <path
+               inkscape:connector-curvature="0"
+               id="path883"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 c 0,-2.918 -0.53,-5.566 -1.723,-8.221 -1.057,-2.515 -2.651,-4.904 -4.77,-6.89 -2.252,-2.125 -4.64,-3.711 -7.288,-4.774 -2.785,-1.056 -5.571,-1.586 -8.616,-1.586 -3.051,0 -5.964,0.53 -8.618,1.586 -2.647,1.063 -5.036,2.649 -7.155,4.774 -2.123,1.986 -3.712,4.242 -4.903,6.89 -1.065,2.522 -1.594,5.303 -1.594,8.221 0,2.918 0.529,5.697 1.594,8.35 1.191,2.515 2.78,4.904 4.903,6.892 2.119,2.117 4.508,3.578 7.155,4.639 2.654,1.058 5.567,1.588 8.618,1.588 3.177,0 5.962,-0.53 8.616,-1.588 C -11,18.82 -8.612,17.359 -6.493,15.242 -4.374,13.123 -2.78,10.865 -1.589,8.217 -0.53,5.697 0,2.918 0,0 m -14.05,0 c 0,3.049 -0.663,5.299 -2.252,7.021 -1.457,1.727 -3.447,2.655 -6.095,2.655 -2.521,0 -4.64,-0.928 -6.097,-2.655 -1.461,-1.722 -2.255,-3.972 -2.255,-7.021 0,-2.918 0.794,-5.303 2.255,-7.027 1.457,-1.725 3.576,-2.518 6.097,-2.518 2.648,0 4.638,0.793 6.095,2.518 1.589,1.724 2.252,4.109 2.252,7.027" />
+          </g>
+          <g
+             transform="translate(710.0127,120.1406)"
+             id="g885">
+            <path
+               inkscape:connector-curvature="0"
+               id="path887"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 v 40.029 h 12.992 v -8.088 c 1.854,3.186 3.973,5.567 6.492,7.163 2.516,1.455 5.434,2.25 8.882,2.25 2.516,0 4.904,-0.53 6.89,-1.325 1.986,-0.795 3.58,-1.984 4.904,-3.58 1.06,-1.322 1.854,-2.912 2.257,-4.902 0.396,-1.986 0.661,-4.774 0.661,-8.613 V 0 h -14.05 v 21.473 c 0,3.181 -0.529,5.433 -1.589,6.896 -1.064,1.455 -2.786,2.25 -5.042,2.25 -2.913,0 -5.036,-1.058 -6.492,-3.316 -1.324,-2.119 -1.987,-5.698 -1.987,-10.469 L 13.918,0 Z" />
+          </g>
+          <g
+             transform="translate(703.3916,417.1084)"
+             id="g889">
+            <path
+               inkscape:connector-curvature="0"
+               id="path891"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 -8.81,-3.271 c 5.363,-15.116 8.291,-31.384 8.291,-48.338 0,-4.616 -0.226,-9.177 -0.648,-13.683 l 9.375,-0.627 c 0.434,4.712 0.667,9.484 0.667,14.31 C 8.875,-33.505 5.739,-16.134 0,0" />
+          </g>
+          <g
+             transform="translate(558.3203,220.9473)"
+             id="g893">
+            <path
+               inkscape:connector-curvature="0"
+               id="path895"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 c -15.705,0 -30.819,2.516 -44.978,7.146 -3.751,-1.978 -7.583,-3.82 -11.51,-5.49 -0.144,-0.06 -0.29,-0.117 -0.434,-0.177 C -39.313,-5.533 -20.108,-9.395 0,-9.395 c 48.132,0 91.098,22.094 119.328,56.688 l -8.571,4.381 C 84.24,20.088 44.469,0 0,0" />
+          </g>
+          <g
+             transform="translate(441.6821,219.5352)"
+             id="g897">
+            <path
+               inkscape:connector-curvature="0"
+               id="path899"
+               style="fill:#e47128;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 C -21.463,0 -41.833,4.674 -60.154,13.053 L -64.849,5.168 C -45.139,-4.002 -23.17,-9.131 0,-9.131 c 21.177,0 41.352,4.283 59.716,12.022 C 55.719,4.482 51.807,6.24 47.983,8.148 32.965,2.875 16.819,0 0,0" />
+          </g>
+          <g
+             transform="translate(552.4727,271.1309)"
+             id="g901">
+            <path
+               inkscape:connector-curvature="0"
+               id="path903"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 C 0.009,0 0.017,-0.004 0.023,-0.004 0.017,-0.004 0.009,0 0,0" />
+          </g>
+          <g
+             transform="translate(554.5205,271.0293)"
+             id="g905">
+            <path
+               inkscape:connector-curvature="0"
+               id="path907"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 C 1.26,-0.057 2.524,-0.098 3.8,-0.098 5.079,-0.098 6.349,-0.057 7.614,0 6.347,-0.051 5.076,-0.096 3.796,-0.096 2.523,-0.096 1.262,-0.051 0,0" />
+          </g>
+          <g
+             transform="translate(554.3145,459.9658)"
+             id="g909">
+            <path
+               inkscape:connector-curvature="0"
+               id="path911"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 C 1.311,0.054 2.624,0.099 3.947,0.1 2.624,0.099 1.31,0.062 0,0" />
+          </g>
+          <g
+             transform="translate(550.6484,459.731)"
+             id="g913">
+            <path
+               inkscape:connector-curvature="0"
+               id="path915"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 V 0 C 0.003,0 0.004,0 0.006,0 0.004,0 0.003,0 0,0" />
+          </g>
+          <g
+             transform="translate(564.1133,271.125)"
+             id="g917">
+            <path
+               inkscape:connector-curvature="0"
+               id="path919"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 C 0.021,0.002 0.04,0.002 0.061,0.006 H 0.062 C 0.041,0.006 0.021,0.002 0,0" />
+          </g>
+          <g
+             transform="translate(562.3379,459.9663)"
+             id="g921">
+            <path
+               inkscape:connector-curvature="0"
+               id="path923"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 C -1.334,0.056 -2.67,0.101 -4.018,0.101 H -4.02 c 1.349,0 2.686,-0.046 4.021,-0.102 C 0,0 0,0 0,0" />
+          </g>
+          <g
+             transform="translate(595.6299,364.3545)"
+             id="g925">
+            <path
+               inkscape:connector-curvature="0"
+               id="path927"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 c 0,33.746 -10.866,64.951 -29.28,90.321 -2.649,0.236 -5.324,0.377 -8.033,0.377 -1.23,0 -2.45,-0.044 -3.667,-0.093 C -21.057,65.797 -9.131,34.293 -9.131,0 c 0,-33.273 -11.235,-63.909 -30.102,-88.358 0.64,-0.014 1.276,-0.049 1.92,-0.049 3.298,0 6.55,0.191 9.755,0.539 C -10.197,-62.95 0,-32.672 0,0" />
+          </g>
+          <g
+             transform="translate(672.6201,365.4995)"
+             id="g929">
+            <path
+               inkscape:connector-curvature="0"
+               id="path931"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 c 0,63.125 -51.175,114.299 -114.3,114.299 -4.692,0 -9.316,-0.289 -13.861,-0.839 -3.962,-0.48 -7.86,-1.156 -11.687,-2.03 3.089,-2.502 6.077,-5.122 8.947,-7.866 3.831,0.61 7.731,1.013 11.688,1.195 1.629,0.076 3.266,0.119 4.913,0.119 57.922,0 104.878,-46.956 104.878,-104.878 0,-3.787 -0.206,-7.525 -0.597,-11.208 l 9.292,-1.675 C -0.253,-8.653 0,-4.357 0,0" />
+          </g>
+          <g
+             transform="translate(383.3794,235.6973)"
+             id="g933">
+            <path
+               inkscape:connector-curvature="0"
+               id="path935"
+               style="fill:#62aeb2;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 2.881,4.838 c -47.274,21.193 -80.206,68.66 -80.206,123.819 0,8.489 0.321,14.111 1.814,22.167 l -18.228,2.079 c -1.249,-7.899 -1.907,-15.995 -1.907,-24.246 0,-61.852 36.484,-115.178 89.1,-139.651 l 4.695,7.885 z" />
+          </g>
+          <g
+             transform="translate(475.9927,495.5977)"
+             id="g937">
+            <path
+               inkscape:connector-curvature="0"
+               id="path939"
+               style="fill:#62aeb2;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 c 4.099,-1.069 8.124,-2.323 12.062,-3.756 2.162,1.206 4.357,2.358 6.585,3.453 1.45,0.713 2.909,1.406 4.384,2.071 3.797,1.711 7.685,3.255 11.648,4.638 14.921,5.208 30.952,8.047 47.649,8.047 50.216,0 94.445,-25.61 120.356,-64.478 l 7.703,5.385 C 182.777,-3.349 135.73,23.848 82.328,23.848 61.15,23.848 40.972,19.57 22.608,11.833 18.644,10.163 14.768,8.325 10.98,6.34 9.503,5.566 8.039,4.77 6.59,3.949 -6.357,7.861 -20.087,9.974 -34.311,9.974 c -3.094,0 -6.163,-0.111 -9.207,-0.307 l -0.323,-5.623 c 3.149,0.219 6.326,0.339 9.53,0.339 C -22.457,4.383 -10.959,2.857 0,0" />
+          </g>
+          <g
+             transform="translate(342.8081,347.3779)"
+             id="g941">
+            <path
+               inkscape:connector-curvature="0"
+               id="path943"
+               style="fill:#62aeb2;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 4.169,2.559 c -0.711,4.703 -1.083,9.516 -1.083,14.418 0,51.248 40.26,93.047 90.872,95.611 1.237,1.59 2.509,3.154 3.806,4.693 1.986,2.356 4.035,4.656 6.155,6.887 -1.673,0.08 -3.352,0.136 -5.045,0.136 -59.276,0 -107.328,-48.053 -107.328,-107.327 0,-57.863 45.79,-105.023 103.111,-107.238 l -5.896,7.424 C 43.744,-78.327 7.513,-44.065 0,0" />
+          </g>
+          <g
+             transform="translate(454.5591,264.8711)"
+             id="g945">
+            <path
+               inkscape:connector-curvature="0"
+               id="path947"
+               style="fill:#62aeb2;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 c -1.312,1.354 -2.598,2.729 -3.857,4.131 -5.416,6.037 -10.324,12.535 -14.674,19.422 l -10.18,-1.379 c 3.833,-6.455 8.123,-12.606 12.825,-18.408 1.262,-1.559 2.553,-3.09 3.872,-4.596 2.034,-2.32 4.137,-4.578 6.305,-6.772 2.299,-2.324 4.68,-4.566 7.121,-6.742 l 10.99,2.959 C 10.322,-9.689 8.29,-7.939 6.311,-6.131 4.144,-4.154 2.042,-2.105 0,0" />
+          </g>
+          <g
+             transform="translate(420.0469,407.7637)"
+             id="g949">
+            <path
+               inkscape:connector-curvature="0"
+               id="path951"
+               style="fill:#62aeb2;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 -9.694,0.332 c -3.889,-13.529 -5.979,-27.818 -5.979,-42.596 0,-20.788 4.126,-40.609 11.596,-58.699 H 6.143 C -1.84,-83.025 -6.279,-63.164 -6.279,-42.264 -6.279,-27.56 -4.082,-13.369 0,0" />
+          </g>
+          <g
+             transform="translate(458.4263,365.4995)"
+             id="g953">
+            <path
+               inkscape:connector-curvature="0"
+               id="path955"
+               style="fill:#62aeb2;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 c 0,-17.657 4.593,-34.231 12.636,-48.619 l 4.824,2.261 c -0.085,0.15 -0.164,0.305 -0.247,0.456 -0.309,0.553 -0.611,1.11 -0.909,1.671 -0.37,0.698 -0.728,1.404 -1.082,2.114 -0.377,0.757 -0.743,1.519 -1.101,2.286 -0.322,0.692 -0.638,1.387 -0.944,2.088 -0.239,0.55 -0.476,1.1 -0.706,1.654 -0.434,1.049 -0.854,2.106 -1.251,3.173 -0.178,0.479 -0.342,0.965 -0.512,1.449 -0.281,0.795 -0.552,1.595 -0.813,2.401 -0.16,0.498 -0.323,0.994 -0.476,1.496 -0.35,1.149 -0.68,2.307 -0.986,3.474 -0.119,0.448 -0.225,0.9 -0.337,1.351 -0.232,0.939 -0.45,1.884 -0.654,2.834 -0.087,0.408 -0.181,0.812 -0.263,1.222 -0.251,1.249 -0.473,2.509 -0.674,3.776 -0.067,0.418 -0.126,0.838 -0.186,1.258 -0.161,1.109 -0.303,2.223 -0.425,3.343 -0.031,0.282 -0.069,0.562 -0.097,0.844 -0.133,1.337 -0.23,2.684 -0.307,4.037 -0.023,0.4 -0.041,0.801 -0.059,1.202 -0.058,1.316 -0.097,2.637 -0.1,3.965 0,0.088 -0.007,0.175 -0.007,0.264 0,0.173 0.012,0.344 0.013,0.517 0.007,1.306 0.039,2.606 0.099,3.898 0.025,0.564 0.069,1.123 0.105,1.685 0.058,0.914 0.124,1.826 0.208,2.733 0.058,0.626 0.125,1.249 0.195,1.872 0.094,0.837 0.202,1.671 0.318,2.502 0.089,0.638 0.178,1.276 0.28,1.91 0.132,0.823 0.284,1.64 0.438,2.456 0.116,0.616 0.222,1.236 0.349,1.848 0.271,1.292 0.566,2.576 0.888,3.849 0.192,0.756 0.41,1.501 0.62,2.251 0.166,0.593 0.328,1.188 0.505,1.777 0.243,0.808 0.505,1.608 0.77,2.407 0.164,0.498 0.33,0.995 0.503,1.489 0.297,0.848 0.604,1.692 0.924,2.531 0.146,0.38 0.298,0.759 0.449,1.138 0.369,0.93 0.743,1.856 1.14,2.771 0.007,0.016 0.015,0.032 0.022,0.048 1.999,4.596 4.359,8.995 7.034,13.177 0.004,0.009 0.01,0.017 0.015,0.025 1.34,2.093 2.745,4.138 4.24,6.115 l -5.094,2.048 C 7.191,42.509 0,22.1 0,0" />
+          </g>
+          <g
+             transform="translate(581.1611,243.5664)"
+             id="g957">
+            <path
+               inkscape:connector-curvature="0"
+               id="path959"
+               style="fill:#00868e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 -2.615,9.418 -2.535,9.131 c 45.885,7.797 81.619,45.39 86.59,92.177 0.391,3.682 0.597,7.42 0.597,11.207 0,57.922 -46.956,104.878 -104.878,104.878 -1.647,0 -3.284,-0.043 -4.913,-0.119 -3.957,-0.182 -7.857,-0.585 -11.688,-1.194 -1.485,-0.237 -2.96,-0.499 -4.422,-0.797 -2.233,-0.454 -4.436,-0.984 -6.613,-1.578 -2.298,-0.625 -4.565,-1.328 -6.796,-2.103 -3.646,-1.267 -7.203,-2.727 -10.653,-4.372 -4.115,-1.962 -8.082,-4.187 -11.881,-6.648 -2.119,-1.375 -4.185,-2.822 -6.195,-4.34 -2.869,1.81 -5.835,3.481 -8.89,4.998 2.008,1.632 4.07,3.201 6.187,4.695 3.793,2.68 7.758,5.133 11.873,7.343 3.445,1.849 6.998,3.525 10.644,5.02 2.225,0.913 4.484,1.764 6.778,2.54 2.179,0.735 4.39,1.399 6.626,2.004 1.455,0.394 2.919,0.766 4.395,1.103 3.826,0.874 7.725,1.551 11.687,2.031 4.545,0.549 9.169,0.838 13.861,0.838 63.125,0 114.3,-51.173 114.3,-114.299 0,-4.357 -0.253,-8.653 -0.727,-12.883 l 9.607,-1.732 C 93.994,53.27 52.901,9.85 0,0 m -22.841,27.365 c -1.275,0 -2.54,0.041 -3.8,0.098 -0.676,0.025 -1.35,0.058 -2.024,0.098 -0.007,0 -0.015,0.003 -0.023,0.003 -19.058,1.165 -36.591,7.956 -50.95,18.756 -1.006,0.758 -1.994,1.537 -2.969,2.332 -0.391,0.323 -0.775,0.651 -1.161,0.975 -0.656,0.555 -1.306,1.115 -1.947,1.686 -0.425,0.38 -0.849,0.761 -1.269,1.148 -0.604,0.56 -1.197,1.131 -1.786,1.705 -0.4,0.389 -0.807,0.772 -1.2,1.168 -0.863,0.869 -1.708,1.756 -2.536,2.66 -0.474,0.516 -0.932,1.047 -1.394,1.574 -0.414,0.471 -0.827,0.942 -1.231,1.418 -0.487,0.58 -0.966,1.162 -1.439,1.752 -0.354,0.442 -0.701,0.885 -1.048,1.332 -0.48,0.621 -0.96,1.241 -1.424,1.869 -0.029,0.041 -0.06,0.079 -0.089,0.118 l -4.694,-2.575 -4.419,-2.419 c -12.258,17.162 -19.475,38.172 -19.475,60.87 0,25.55 9.142,48.961 24.325,67.153 l 4.675,-2.22 4.99,-2.369 c 7.854,8.892 17.353,16.287 28.026,21.725 0.266,0.137 0.532,0.277 0.803,0.411 0.77,0.384 1.547,0.755 2.33,1.118 0.419,0.194 0.841,0.382 1.263,0.571 9.537,4.253 19.904,6.968 30.794,7.843 0.003,0 0.004,0 0.006,0 1.216,0.097 2.434,0.183 3.66,0.234 1.31,0.062 2.624,0.099 3.948,0.101 0.018,0 0.036,0 0.054,0 h 0.002 0.002 c 1.348,0 2.684,-0.044 4.018,-0.1 0.008,-0.001 0.015,-0.001 0.024,-0.002 1.471,-0.062 2.93,-0.163 4.382,-0.292 0.116,-0.009 0.234,-0.015 0.352,-0.026 1.395,-0.128 2.778,-0.294 4.156,-0.483 0.171,-0.023 0.344,-0.041 0.514,-0.065 1.33,-0.189 2.645,-0.414 3.956,-0.657 0.213,-0.041 0.431,-0.075 0.646,-0.116 1.267,-0.245 2.521,-0.522 3.769,-0.817 0.252,-0.06 0.505,-0.113 0.756,-0.175 1.208,-0.295 2.404,-0.621 3.593,-0.963 0.283,-0.082 0.571,-0.158 0.853,-0.243 1.152,-0.343 2.293,-0.713 3.426,-1.099 0.31,-0.106 0.623,-0.207 0.933,-0.316 1.1,-0.387 2.186,-0.798 3.267,-1.225 0.332,-0.13 0.666,-0.257 0.997,-0.392 1.047,-0.427 2.082,-0.877 3.11,-1.339 0.354,-0.159 0.708,-0.315 1.059,-0.479 0.995,-0.462 1.978,-0.945 2.954,-1.441 0.371,-0.189 0.742,-0.376 1.109,-0.569 0.945,-0.495 1.876,-1.009 2.801,-1.535 0.387,-0.219 0.771,-0.439 1.154,-0.664 0.893,-0.524 1.775,-1.065 2.648,-1.617 0.399,-0.253 0.796,-0.506 1.192,-0.764 0.841,-0.55 1.672,-1.114 2.493,-1.69 0.411,-0.286 0.819,-0.577 1.224,-0.869 0.792,-0.573 1.573,-1.158 2.346,-1.754 0.417,-0.323 0.831,-0.648 1.242,-0.977 0.742,-0.593 1.473,-1.194 2.196,-1.808 0.424,-0.36 0.843,-0.725 1.261,-1.092 0.691,-0.608 1.375,-1.224 2.048,-1.852 0.426,-0.397 0.846,-0.802 1.267,-1.207 0.642,-0.622 1.279,-1.25 1.904,-1.89 0.426,-0.437 0.845,-0.881 1.263,-1.326 0.595,-0.633 1.184,-1.27 1.761,-1.918 0.425,-0.478 0.84,-0.964 1.255,-1.449 0.547,-0.641 1.091,-1.284 1.621,-1.939 0.419,-0.518 0.826,-1.045 1.235,-1.572 0.501,-0.647 1,-1.295 1.485,-1.954 0.41,-0.559 0.806,-1.128 1.205,-1.696 0.455,-0.649 0.91,-1.3 1.35,-1.962 0.4,-0.601 0.783,-1.212 1.168,-1.822 0.412,-0.652 0.825,-1.301 1.22,-1.963 0.384,-0.643 0.752,-1.297 1.121,-1.95 0.368,-0.65 0.738,-1.297 1.092,-1.956 0.367,-0.687 0.715,-1.386 1.065,-2.083 0.326,-0.644 0.655,-1.286 0.965,-1.939 0.348,-0.731 0.673,-1.474 1.002,-2.216 0.283,-0.637 0.573,-1.272 0.842,-1.917 0.324,-0.778 0.624,-1.567 0.929,-2.354 0.242,-0.627 0.493,-1.25 0.722,-1.883 0.299,-0.828 0.571,-1.668 0.848,-2.506 0.201,-0.61 0.413,-1.215 0.602,-1.83 0.272,-0.884 0.513,-1.781 0.759,-2.676 0.162,-0.585 0.335,-1.164 0.486,-1.753 0.242,-0.952 0.451,-1.916 0.665,-2.879 0.12,-0.546 0.256,-1.087 0.367,-1.638 0.211,-1.038 0.384,-2.089 0.56,-3.138 0.082,-0.488 0.18,-0.97 0.255,-1.461 0.178,-1.174 0.316,-2.361 0.451,-3.549 0.042,-0.376 0.1,-0.747 0.139,-1.124 0.148,-1.468 0.256,-2.947 0.337,-4.434 0.005,-0.103 0.018,-0.205 0.023,-0.307 0.082,-1.609 0.125,-3.227 0.125,-4.855 0,-1.619 -0.044,-3.227 -0.124,-4.826 -0.013,-0.244 -0.04,-0.484 -0.054,-0.728 -0.077,-1.336 -0.171,-2.667 -0.304,-3.987 -0.045,-0.459 -0.114,-0.911 -0.167,-1.368 -0.125,-1.096 -0.253,-2.193 -0.416,-3.278 -0.083,-0.549 -0.189,-1.089 -0.28,-1.634 -0.167,-0.983 -0.329,-1.966 -0.526,-2.937 -0.119,-0.594 -0.261,-1.179 -0.391,-1.769 -0.203,-0.911 -0.402,-1.823 -0.629,-2.724 -0.157,-0.62 -0.335,-1.231 -0.505,-1.847 -0.234,-0.855 -0.467,-1.711 -0.726,-2.557 -0.195,-0.639 -0.41,-1.267 -0.617,-1.901 -0.265,-0.806 -0.53,-1.613 -0.816,-2.411 -0.234,-0.65 -0.484,-1.291 -0.732,-1.933 -0.294,-0.764 -0.586,-1.527 -0.899,-2.28 -0.272,-0.658 -0.561,-1.307 -0.849,-1.957 -0.317,-0.721 -0.636,-1.441 -0.971,-2.152 -0.314,-0.663 -0.641,-1.317 -0.968,-1.972 -0.34,-0.679 -0.682,-1.356 -1.038,-2.026 -0.354,-0.666 -0.72,-1.324 -1.089,-1.98 -0.359,-0.639 -0.722,-1.273 -1.095,-1.902 -0.395,-0.668 -0.802,-1.328 -1.213,-1.985 -0.375,-0.595 -0.753,-1.189 -1.14,-1.779 -0.44,-0.664 -0.888,-1.324 -1.343,-1.979 -0.387,-0.556 -0.777,-1.107 -1.176,-1.656 -0.484,-0.664 -0.975,-1.322 -1.476,-1.972 -0.395,-0.514 -0.794,-1.022 -1.2,-1.526 -0.531,-0.664 -1.068,-1.32 -1.617,-1.969 -0.396,-0.468 -0.799,-0.929 -1.204,-1.39 -0.583,-0.66 -1.171,-1.317 -1.77,-1.963 -0.394,-0.422 -0.793,-0.836 -1.194,-1.25 -0.636,-0.66 -1.277,-1.315 -1.932,-1.955 -0.383,-0.375 -0.775,-0.74 -1.164,-1.111 -0.694,-0.655 -1.391,-1.307 -2.105,-1.942 -0.366,-0.324 -0.739,-0.641 -1.11,-0.961 -0.759,-0.654 -1.523,-1.305 -2.303,-1.937 -0.334,-0.27 -0.676,-0.53 -1.014,-0.797 -0.834,-0.654 -1.673,-1.307 -2.53,-1.934 -0.289,-0.213 -0.586,-0.416 -0.879,-0.625 -0.92,-0.66 -1.848,-1.314 -2.792,-1.941 C 29.148,42.947 28.922,42.809 28.7,42.664 27.67,41.994 26.633,41.334 25.576,40.703 25.463,40.635 25.347,40.572 25.234,40.506 12.77,33.131 -1.529,28.541 -16.819,27.578 c -0.056,-0.006 -0.113,-0.012 -0.168,-0.014 -0.021,-0.003 -0.04,-0.003 -0.061,-0.005 -0.657,-0.039 -1.316,-0.071 -1.978,-0.096 -1.266,-0.057 -2.536,-0.098 -3.815,-0.098 M -108.238,5.303 c -3.926,-1.059 -7.932,-1.924 -12.006,-2.582 -6.262,-1.014 -12.685,-1.549 -19.235,-1.549 -66.062,0 -119.615,53.557 -119.615,119.616 0,66.06 53.553,119.613 119.615,119.613 5.743,0 11.389,-0.413 16.916,-1.196 4.087,-0.579 8.108,-1.362 12.052,-2.343 3.042,-0.757 6.038,-1.628 8.984,-2.611 -4.136,-2.904 -8.102,-6.029 -11.885,-9.36 -2.126,-1.871 -4.197,-3.802 -6.202,-5.799 -1.277,-1.272 -2.524,-2.572 -3.749,-3.893 -22.715,-24.472 -36.612,-57.243 -36.612,-93.266 0,-36.992 14.653,-70.558 38.464,-95.226 1.261,-1.305 2.544,-2.59 3.856,-3.846 2.041,-1.955 4.148,-3.84 6.308,-5.668 3.836,-3.244 7.848,-6.289 12.028,-9.103 -2.922,-1.039 -5.897,-1.971 -8.919,-2.787 m 221.269,98.007 c 0.244,1.801 0.455,3.614 0.63,5.437 0.413,4.34 0.633,8.737 0.633,13.186 0,16.046 -2.766,31.444 -7.829,45.756 -0.55,1.554 -1.119,3.099 -1.724,4.626 l 6.893,2.746 8.729,3.476 c -3.067,7.752 -6.75,15.191 -10.98,22.267 l -7.722,-5.398 -6.098,-4.262 c -1.315,2.244 -2.692,4.45 -4.129,6.611 -24.569,36.953 -66.575,61.311 -114.275,61.311 -13.423,0 -26.389,-1.937 -38.65,-5.531 -3.959,-1.162 -7.849,-2.487 -11.651,-3.988 -1.476,-0.582 -2.935,-1.198 -4.386,-1.829 -2.231,-0.972 -4.43,-2 -6.597,-3.084 -2.298,-1.15 -4.563,-2.357 -6.785,-3.63 -2.941,1.19 -5.935,2.273 -8.978,3.248 -3.943,1.264 -7.968,2.342 -12.066,3.228 -8.871,1.919 -18.079,2.939 -27.525,2.939 -5.952,0 -11.807,-0.41 -17.545,-1.186 v 6.053 5.634 c -47.353,-5.868 -87.438,-35.177 -108.28,-75.958 -1.506,-2.943 0.317,1.31 -0.982,-1.749 l -12.972,2.059 c -4.076,-8.46 -7.828,-18.427 -10.767,-32.195 l 24.257,-2.727 c -0.699,-2.652 -0.383,-1.799 -0.915,-4.514 -1.588,-8.106 -2.428,-16.479 -2.428,-25.048 0,-71.595 58.038,-129.634 129.632,-129.634 10.329,0 20.368,1.233 30,3.516 4.071,0.965 8.063,2.127 11.975,3.465 0.891,0.304 1.789,0.594 2.672,0.916 2.115,0.775 4.194,1.625 6.256,2.506 2.248,-1.229 4.536,-2.399 6.859,-3.504 2.182,-1.039 4.398,-2.02 6.641,-2.946 1.471,-0.603 2.952,-1.189 4.447,-1.744 3.829,-1.424 7.742,-2.679 11.726,-3.763 11.492,-3.123 23.581,-4.801 36.062,-4.801 64.248,0 118.17,44.185 133.053,103.826 l 7.379,-1.056 C 112.708,67.543 103.657,49.154 91.403,33.371 l 8.534,-4.31 c 15.737,20.773 26.325,45.657 29.862,72.763 l -9.37,0.83 z" />
+          </g>
+          <g
+             transform="translate(554.6494,454.96)"
+             id="g961">
+            <path
+               inkscape:connector-curvature="0"
+               id="path963"
+               style="fill:#00868e;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="m 0,0 c -10.023,-0.405 -19.616,-2.459 -28.527,-5.896 21.724,-21.656 35.173,-51.611 35.173,-84.709 0,-13.873 -2.363,-27.191 -6.707,-39.58 l -12.005,2.938 c 4.153,11.435 6.425,23.771 6.425,36.642 0,31.303 -13.42,59.455 -34.8,79.071 -3.475,-1.972 -6.812,-4.159 -9.978,-6.562 20.346,-17.563 33.238,-43.523 33.238,-72.509 0,-28.309 -12.292,-53.734 -31.815,-71.269 14.298,-10.416 31.794,-16.689 50.743,-17.09 18.867,24.449 30.103,55.086 30.103,88.359 C 31.85,-56.312 19.924,-24.808 0,0" />
+          </g>
+          <g
+             transform="translate(393.1943,500.7769)"
+             id="g965">
+            <path
+               inkscape:connector-curvature="0"
+               id="path967"
+               style="fill:#e5ad23;fill-opacity:1;fill-rule:nonzero;stroke:none"
+               d="M 0,0 -2.466,8.942 C -39.654,-4.079 -70.318,-30.977 -88.254,-65.542 l 9.666,-1.535 C -61.654,-36.083 -33.706,-11.967 0,0" />
+          </g>
+        </g>
+      </g>
+    </g>
+  </g>
+</svg>
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino
new file mode 100644
index 0000000000000000000000000000000000000000..ce6dca3e6924bfa45a65e79b3019ec6a26edffb6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonConfigFile/JsonConfigFile.ino
@@ -0,0 +1,144 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+//
+// This example shows how to store your project configuration in a file.
+// It uses the SD library but can be easily modified for any other file-system.
+//
+// The file contains a JSON document with the following content:
+// {
+//   "hostname": "examples.com",
+//   "port": 2731
+// }
+
+#include <ArduinoJson.h>
+#include <SD.h>
+#include <SPI.h>
+
+// Configuration that we'll store on disk
+struct Config {
+  char hostname[64];
+  int port;
+};
+
+const char *filename = "/config.txt";  // <- SD library uses 8.3 filenames
+Config config;                         // <- global configuration object
+
+// Loads the configuration from a file
+void loadConfiguration(const char *filename, Config &config) {
+  // Open file for reading
+  File file = SD.open(filename);
+
+  // Allocate the memory pool on the stack.
+  // Don't forget to change the capacity to match your JSON document.
+  // Use arduinojson.org/assistant to compute the capacity.
+  StaticJsonBuffer<512> jsonBuffer;
+
+  // Parse the root object
+  JsonObject &root = jsonBuffer.parseObject(file);
+
+  if (!root.success())
+    Serial.println(F("Failed to read file, using default configuration"));
+
+  // Copy values from the JsonObject to the Config
+  config.port = root["port"] | 2731;
+  strlcpy(config.hostname,                   // <- destination
+          root["hostname"] | "example.com",  // <- source
+          sizeof(config.hostname));          // <- destination's capacity
+
+  // Close the file (File's destructor doesn't close the file)
+  file.close();
+}
+
+// Saves the configuration to a file
+void saveConfiguration(const char *filename, const Config &config) {
+  // Delete existing file, otherwise the configuration is appended to the file
+  SD.remove(filename);
+
+  // Open file for writing
+  File file = SD.open(filename, FILE_WRITE);
+  if (!file) {
+    Serial.println(F("Failed to create file"));
+    return;
+  }
+
+  // Allocate the memory pool on the stack
+  // Don't forget to change the capacity to match your JSON document.
+  // Use https://arduinojson.org/assistant/ to compute the capacity.
+  StaticJsonBuffer<256> jsonBuffer;
+
+  // Parse the root object
+  JsonObject &root = jsonBuffer.createObject();
+
+  // Set the values
+  root["hostname"] = config.hostname;
+  root["port"] = config.port;
+
+  // Serialize JSON to file
+  if (root.printTo(file) == 0) {
+    Serial.println(F("Failed to write to file"));
+  }
+
+  // Close the file (File's destructor doesn't close the file)
+  file.close();
+}
+
+// Prints the content of a file to the Serial
+void printFile(const char *filename) {
+  // Open file for reading
+  File file = SD.open(filename);
+  if (!file) {
+    Serial.println(F("Failed to read file"));
+    return;
+  }
+
+  // Extract each characters by one by one
+  while (file.available()) {
+    Serial.print((char)file.read());
+  }
+  Serial.println();
+
+  // Close the file (File's destructor doesn't close the file)
+  file.close();
+}
+
+void setup() {
+  // Initialize serial port
+  Serial.begin(9600);
+  while (!Serial) continue;
+
+  // Initialize SD library
+  while (!SD.begin()) {
+    Serial.println(F("Failed to initialize SD library"));
+    delay(1000);
+  }
+
+  // Should load default config if run for the first time
+  Serial.println(F("Loading configuration..."));
+  loadConfiguration(filename, config);
+
+  // Create configuration file
+  Serial.println(F("Saving configuration..."));
+  saveConfiguration(filename, config);
+
+  // Dump config file
+  Serial.println(F("Print config file..."));
+  printFile(filename);
+}
+
+void loop() {
+  // not used in this example
+}
+
+// See also
+// --------
+//
+// The website arduinojson.org contains the documentation for all the functions
+// used above. It also includes an FAQ that will help you solve any
+// serialization or deserialization problem.
+// Please check it out at: https://arduinojson.org/
+//
+// The book "Mastering ArduinoJson" contains a case study of a project that has
+// a complex configuration with nested members.
+// Contrary to this example, the project in the book uses the SPIFFS filesystem.
+// Please check it out at: https://arduinojson.org/book/
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonGeneratorExample/JsonGeneratorExample.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonGeneratorExample/JsonGeneratorExample.ino
new file mode 100644
index 0000000000000000000000000000000000000000..d9efaba1e2911f246247551a3baa4c1085e5fb5c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonGeneratorExample/JsonGeneratorExample.ino
@@ -0,0 +1,81 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+//
+// This example shows how to generate a JSON document with ArduinoJson.
+
+#include <ArduinoJson.h>
+
+void setup() {
+  // Initialize Serial port
+  Serial.begin(9600);
+  while (!Serial) continue;
+
+  // Memory pool for JSON object tree.
+  //
+  // Inside the brackets, 200 is the size of the pool in bytes.
+  // Don't forget to change this value to match your JSON document.
+  // Use arduinojson.org/assistant to compute the capacity.
+  StaticJsonBuffer<200> jsonBuffer;
+
+  // StaticJsonBuffer allocates memory on the stack, it can be
+  // replaced by DynamicJsonBuffer which allocates in the heap.
+  //
+  // DynamicJsonBuffer  jsonBuffer(200);
+
+  // Create the root of the object tree.
+  //
+  // It's a reference to the JsonObject, the actual bytes are inside the
+  // JsonBuffer with all the other nodes of the object tree.
+  // Memory is freed when jsonBuffer goes out of scope.
+  JsonObject& root = jsonBuffer.createObject();
+
+  // Add values in the object
+  //
+  // Most of the time, you can rely on the implicit casts.
+  // In other case, you can do root.set<long>("time", 1351824120);
+  root["sensor"] = "gps";
+  root["time"] = 1351824120;
+
+  // Add a nested array.
+  //
+  // It's also possible to create the array separately and add it to the
+  // JsonObject but it's less efficient.
+  JsonArray& data = root.createNestedArray("data");
+  data.add(48.756080);
+  data.add(2.302038);
+
+  root.printTo(Serial);
+  // This prints:
+  // {"sensor":"gps","time":1351824120,"data":[48.756080,2.302038]}
+
+  Serial.println();
+
+  root.prettyPrintTo(Serial);
+  // This prints:
+  // {
+  //   "sensor": "gps",
+  //   "time": 1351824120,
+  //   "data": [
+  //     48.756080,
+  //     2.302038
+  //   ]
+  // }
+}
+
+void loop() {
+  // not used in this example
+}
+
+// See also
+// --------
+//
+// The website arduinojson.org contains the documentation for all the functions
+// used above. It also includes an FAQ that will help you solve any
+// serialization problem.
+// Please check it out at: https://arduinojson.org/
+//
+// The book "Mastering ArduinoJson" contains a tutorial on serialization.
+// It begins with a simple example, like the one above, and then adds more
+// features like serializing directly to a file or an HTTP request.
+// Please check it out at: https://arduinojson.org/book/
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino
new file mode 100644
index 0000000000000000000000000000000000000000..6e5c05efc0c36262520639c861bdbef293a4aba5
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonHttpClient/JsonHttpClient.ino
@@ -0,0 +1,112 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+//
+// This example shows how to parse a JSON document in an HTTP response.
+// It uses the Ethernet library, but can be easily adapted for Wifi.
+//
+// It performs a GET resquest on arduinojson.org/example.json
+// Here is the expected response:
+// {
+//   "sensor": "gps",
+//   "time": 1351824120,
+//   "data": [
+//     48.756080,
+//     2.302038
+//   ]
+// }
+
+#include <ArduinoJson.h>
+#include <Ethernet.h>
+#include <SPI.h>
+
+void setup() {
+  // Initialize Serial port
+  Serial.begin(9600);
+  while (!Serial) continue;
+
+  // Initialize Ethernet library
+  byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
+  if (!Ethernet.begin(mac)) {
+    Serial.println(F("Failed to configure Ethernet"));
+    return;
+  }
+  delay(1000);
+
+  Serial.println(F("Connecting..."));
+
+  // Connect to HTTP server
+  EthernetClient client;
+  client.setTimeout(10000);
+  if (!client.connect("arduinojson.org", 80)) {
+    Serial.println(F("Connection failed"));
+    return;
+  }
+
+  Serial.println(F("Connected!"));
+
+  // Send HTTP request
+  client.println(F("GET /example.json HTTP/1.0"));
+  client.println(F("Host: arduinojson.org"));
+  client.println(F("Connection: close"));
+  if (client.println() == 0) {
+    Serial.println(F("Failed to send request"));
+    return;
+  }
+
+  // Check HTTP status
+  char status[32] = {0};
+  client.readBytesUntil('\r', status, sizeof(status));
+  if (strcmp(status, "HTTP/1.1 200 OK") != 0) {
+    Serial.print(F("Unexpected response: "));
+    Serial.println(status);
+    return;
+  }
+
+  // Skip HTTP headers
+  char endOfHeaders[] = "\r\n\r\n";
+  if (!client.find(endOfHeaders)) {
+    Serial.println(F("Invalid response"));
+    return;
+  }
+
+  // Allocate JsonBuffer
+  // Use arduinojson.org/assistant to compute the capacity.
+  const size_t capacity = JSON_OBJECT_SIZE(3) + JSON_ARRAY_SIZE(2) + 60;
+  DynamicJsonBuffer jsonBuffer(capacity);
+
+  // Parse JSON object
+  JsonObject& root = jsonBuffer.parseObject(client);
+  if (!root.success()) {
+    Serial.println(F("Parsing failed!"));
+    return;
+  }
+
+  // Extract values
+  Serial.println(F("Response:"));
+  Serial.println(root["sensor"].as<char*>());
+  Serial.println(root["time"].as<char*>());
+  Serial.println(root["data"][0].as<char*>());
+  Serial.println(root["data"][1].as<char*>());
+
+  // Disconnect
+  client.stop();
+}
+
+void loop() {
+  // not used in this example
+}
+
+// See also
+// --------
+//
+// The website arduinojson.org contains the documentation for all the functions
+// used above. It also includes an FAQ that will help you solve any
+// serialization  problem.
+// Please check it out at: https://arduinojson.org/
+//
+// The book "Mastering ArduinoJson" contains a tutorial on deserialization
+// showing how to parse the response from Yahoo Weather. In the last chapter,
+// it shows how to parse the huge documents from OpenWeatherMap
+// and Weather Underground.
+// Please check it out at: https://arduinojson.org/book/
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonParserExample/JsonParserExample.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonParserExample/JsonParserExample.ino
new file mode 100644
index 0000000000000000000000000000000000000000..0c771ab0227ce54d96907b1596b6ffab316d305e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonParserExample/JsonParserExample.ino
@@ -0,0 +1,78 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+//
+// This example shows how to deserialize a JSON document with ArduinoJson.
+
+#include <ArduinoJson.h>
+
+void setup() {
+  // Initialize serial port
+  Serial.begin(9600);
+  while (!Serial) continue;
+
+  // Memory pool for JSON object tree.
+  //
+  // Inside the brackets, 200 is the size of the pool in bytes.
+  // Don't forget to change this value to match your JSON document.
+  // Use arduinojson.org/assistant to compute the capacity.
+  StaticJsonBuffer<200> jsonBuffer;
+
+  // StaticJsonBuffer allocates memory on the stack, it can be
+  // replaced by DynamicJsonBuffer which allocates in the heap.
+  //
+  // DynamicJsonBuffer  jsonBuffer(200);
+
+  // JSON input string.
+  //
+  // It's better to use a char[] as shown here.
+  // If you use a const char* or a String, ArduinoJson will
+  // have to make a copy of the input in the JsonBuffer.
+  char json[] =
+      "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
+
+  // Root of the object tree.
+  //
+  // It's a reference to the JsonObject, the actual bytes are inside the
+  // JsonBuffer with all the other nodes of the object tree.
+  // Memory is freed when jsonBuffer goes out of scope.
+  JsonObject& root = jsonBuffer.parseObject(json);
+
+  // Test if parsing succeeds.
+  if (!root.success()) {
+    Serial.println("parseObject() failed");
+    return;
+  }
+
+  // Fetch values.
+  //
+  // Most of the time, you can rely on the implicit casts.
+  // In other case, you can do root["time"].as<long>();
+  const char* sensor = root["sensor"];
+  long time = root["time"];
+  double latitude = root["data"][0];
+  double longitude = root["data"][1];
+
+  // Print values.
+  Serial.println(sensor);
+  Serial.println(time);
+  Serial.println(latitude, 6);
+  Serial.println(longitude, 6);
+}
+
+void loop() {
+  // not used in this example
+}
+
+// See also
+// --------
+//
+// The website arduinojson.org contains the documentation for all the functions
+// used above. It also includes an FAQ that will help you solve any
+// deserialization problem.
+// Please check it out at: https://arduinojson.org/
+//
+// The book "Mastering ArduinoJson" contains a tutorial on deserialization.
+// It begins with a simple example, like the one above, and then adds more
+// features like deserializing directly from a file or an HTTP request.
+// Please check it out at: https://arduinojson.org/book/
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonServer/JsonServer.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonServer/JsonServer.ino
new file mode 100644
index 0000000000000000000000000000000000000000..229e289ac474d7806742bbaca24ecce9d651bb51
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonServer/JsonServer.ino
@@ -0,0 +1,109 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+//
+// This example shows how to implement an HTTP server that sends JSON document
+// in the responses.
+// It uses the Ethernet library but can be easily adapted for Wifi.
+//
+// It sends the value of the analog and digital pins.
+// The JSON document looks like the following:
+// {
+//   "analog": [ 0, 1, 2, 3, 4, 5 ],
+//   "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ]
+// }
+
+#include <ArduinoJson.h>
+#include <Ethernet.h>
+#include <SPI.h>
+
+byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
+EthernetServer server(80);
+
+void setup() {
+  // Initialize serial port
+  Serial.begin(9600);
+  while (!Serial) continue;
+
+  // Initialize Ethernet libary
+  if (!Ethernet.begin(mac)) {
+    Serial.println(F("Failed to initialize Ethernet library"));
+    return;
+  }
+
+  // Start to listen
+  server.begin();
+
+  Serial.println(F("Server is ready."));
+  Serial.print(F("Please connect to http://"));
+  Serial.println(Ethernet.localIP());
+}
+
+void loop() {
+  // Wait for an incomming connection
+  EthernetClient client = server.available();
+
+  // Do we have a client?
+  if (!client) return;
+
+  Serial.println(F("New client"));
+
+  // Read the request (we ignore the content in this example)
+  while (client.available()) client.read();
+
+  // Allocate JsonBuffer
+  // Use arduinojson.org/assistant to compute the capacity.
+  StaticJsonBuffer<500> jsonBuffer;
+
+  // Create the root object
+  JsonObject& root = jsonBuffer.createObject();
+
+  // Create the "analog" array
+  JsonArray& analogValues = root.createNestedArray("analog");
+  for (int pin = 0; pin < 6; pin++) {
+    // Read the analog input
+    int value = analogRead(pin);
+
+    // Add the value at the end of the array
+    analogValues.add(value);
+  }
+
+  // Create the "digital" array
+  JsonArray& digitalValues = root.createNestedArray("digital");
+  for (int pin = 0; pin < 14; pin++) {
+    // Read the digital input
+    int value = digitalRead(pin);
+
+    // Add the value at the end of the array
+    digitalValues.add(value);
+  }
+
+  Serial.print(F("Sending: "));
+  root.printTo(Serial);
+  Serial.println();
+
+  // Write response headers
+  client.println("HTTP/1.0 200 OK");
+  client.println("Content-Type: application/json");
+  client.println("Connection: close");
+  client.println();
+
+  // Write JSON document
+  root.prettyPrintTo(client);
+
+  // Disconnect
+  client.stop();
+}
+
+// See also
+// --------
+//
+// The website arduinojson.org contains the documentation for all the functions
+// used above. It also includes an FAQ that will help you solve any
+// serialization problem.
+// Please check it out at: https://arduinojson.org/
+//
+// The book "Mastering ArduinoJson" contains a tutorial on serialization.
+// It begins with a simple example, then adds more features like serializing
+// directly to a file or an HTTP client.
+// Please check it out at: https://arduinojson.org/book/
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino
new file mode 100644
index 0000000000000000000000000000000000000000..eb9f19a6637c3e43e86b93acfa0be2493989733e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/JsonUdpBeacon/JsonUdpBeacon.ino
@@ -0,0 +1,101 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+//
+// This example shows how to send a JSON document to a UDP socket.
+// At regular interval, it sends a UDP packet that contains the status of
+// analog and digital pins.
+// The JSON document looks like the following:
+// {
+//   "analog": [ 0, 1, 2, 3, 4, 5 ],
+//   "digital": [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 ]
+// }
+//
+// If you want to test this program, you need to be able to receive the UDP
+// packets.
+// For example, you can run netcat on your computer
+// $ ncat -ulp 8888
+// See https://nmap.org/ncat/
+
+#include <ArduinoJson.h>
+#include <Ethernet.h>
+#include <SPI.h>
+
+byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
+IPAddress remoteIp(192, 168, 0, 108);  // <- EDIT!!!!
+unsigned short remotePort = 8888;
+unsigned short localPort = 8888;
+EthernetUDP udp;
+
+void setup() {
+  // Initialize serial port
+  Serial.begin(9600);
+  while (!Serial) continue;
+
+  // Initialize Ethernet libary
+  if (!Ethernet.begin(mac)) {
+    Serial.println(F("Failed to initialize Ethernet library"));
+    return;
+  }
+
+  // Enable UDP
+  udp.begin(localPort);
+}
+
+void loop() {
+  // Allocate JsonBuffer
+  // Use arduinojson.org/assistant to compute the capacity.
+  StaticJsonBuffer<500> jsonBuffer;
+
+  // Create the root object
+  JsonObject& root = jsonBuffer.createObject();
+
+  // Create the "analog" array
+  JsonArray& analogValues = root.createNestedArray("analog");
+  for (int pin = 0; pin < 6; pin++) {
+    // Read the analog input
+    int value = analogRead(pin);
+
+    // Add the value at the end of the array
+    analogValues.add(value);
+  }
+
+  // Create the "digital" array
+  JsonArray& digitalValues = root.createNestedArray("digital");
+  for (int pin = 0; pin < 14; pin++) {
+    // Read the digital input
+    int value = digitalRead(pin);
+
+    // Add the value at the end of the array
+    digitalValues.add(value);
+  }
+
+  // Log
+  Serial.print(F("Sending to "));
+  Serial.print(remoteIp);
+  Serial.print(F(" on port "));
+  Serial.println(remotePort);
+  root.printTo(Serial);
+
+  // Send UDP packet
+  udp.beginPacket(remoteIp, remotePort);
+  root.printTo(udp);
+  udp.println();
+  udp.endPacket();
+
+  // Wait
+  delay(10000);
+}
+
+// See also
+// --------
+//
+// The website arduinojson.org contains the documentation for all the functions
+// used above. It also includes an FAQ that will help you solve any
+// serialization problem.
+// Please check it out at: https://arduinojson.org/
+//
+// The book "Mastering ArduinoJson" contains a tutorial on serialization.
+// It begins with a simple example, then adds more features like serializing
+// directly to a file or any stream.
+// Please check it out at: https://arduinojson.org/book/
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino
new file mode 100644
index 0000000000000000000000000000000000000000..15be8ed1cf99ee7ca0918a366210ccb263438463
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/ProgmemExample/ProgmemExample.ino
@@ -0,0 +1,70 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+//
+// This example shows the different ways you can use Flash strings with
+// ArduinoJson.
+//
+// Use Flash strings sparingly, because ArduinoJson duplicates them in the
+// JsonBuffer. Prefer plain old char*, as they are more efficient in term of
+// code size, speed, and memory usage.
+
+#include <ArduinoJson.h>
+
+void setup() {
+#ifdef PROGMEM  // <- check that Flash strings are supported
+
+  DynamicJsonBuffer jsonBuffer;
+
+  // You can use a Flash String as your JSON input.
+  // WARNING: the content of the Flash String will be duplicated in the
+  // JsonBuffer.
+  JsonObject& root =
+      jsonBuffer.parseObject(F("{\"sensor\":\"gps\",\"time\":1351824120,"
+                               "\"data\":[48.756080,2.302038]}"));
+
+  // You can use a Flash String to get an element of a JsonObject
+  // No duplication is done.
+  long time = root[F("time")];
+
+  // You can use a Flash String to set an element of a JsonObject
+  // WARNING: the content of the Flash String will be duplicated in the
+  // JsonBuffer.
+  root[F("time")] = time;
+
+  // You can set a Flash String to a JsonObject or JsonArray:
+  // WARNING: the content of the Flash String will be duplicated in the
+  // JsonBuffer.
+  root["sensor"] = F("gps");
+
+  // It works with RawJson too:
+  root["sensor"] = RawJson(F("\"gps\""));
+
+  // You can compare the content of a JsonVariant to a Flash String
+  if (root["sensor"] == F("gps")) {
+    // ...
+  }
+
+#else
+
+#warning PROGMEM is not supported on this platform
+
+#endif
+}
+
+void loop() {
+  // not used in this example
+}
+
+// See also
+// --------
+//
+// The website arduinojson.org contains the documentation for all the functions
+// used above. It also includes an FAQ that will help you solve any memory
+// problem.
+// Please check it out at: https://arduinojson.org/
+//
+// The book "Mastering ArduinoJson" contains a quick C++ course that explains
+// how your microcontroller stores strings in memory. It also tells why you
+// should not abuse Flash strings with ArduinoJson.
+// Please check it out at: https://arduinojson.org/book/
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/StringExample/StringExample.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/StringExample/StringExample.ino
new file mode 100644
index 0000000000000000000000000000000000000000..d5994ffb2bc074a79323f4228164ca6a270bed13
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/examples/StringExample/StringExample.ino
@@ -0,0 +1,74 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+//
+// This example shows the different ways you can use String with ArduinoJson.
+//
+// Use String objects sparingly, because ArduinoJson duplicates them in the
+// JsonBuffer. Prefer plain old char[], as they are more efficient in term of
+// code size, speed, and memory usage.
+
+#include <ArduinoJson.h>
+
+void setup() {
+  DynamicJsonBuffer jsonBuffer;
+
+  // You can use a String as your JSON input.
+  // WARNING: the content of the String will be duplicated in the JsonBuffer.
+  String input =
+      "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
+  JsonObject& root = jsonBuffer.parseObject(input);
+
+  // You can use a String to get an element of a JsonObject
+  // No duplication is done.
+  long time = root[String("time")];
+
+  // You can use a String to set an element of a JsonObject
+  // WARNING: the content of the String will be duplicated in the JsonBuffer.
+  root[String("time")] = time;
+
+  // You can get a String from a JsonObject or JsonArray:
+  // No duplication is done, at least not in the JsonBuffer.
+  String sensor = root["sensor"];
+
+  // Unfortunately, the following doesn't work (issue #118):
+  // sensor = root["sensor"]; // <-  error "ambiguous overload for 'operator='"
+  // As a workaround, you need to replace by:
+  sensor = root["sensor"].as<String>();
+
+  // You can set a String to a JsonObject or JsonArray:
+  // WARNING: the content of the String will be duplicated in the JsonBuffer.
+  root["sensor"] = sensor;
+
+  // It works with RawJson too:
+  root["sensor"] = RawJson(sensor);
+
+  // You can also concatenate strings
+  // WARNING: the content of the String will be duplicated in the JsonBuffer.
+  root[String("sen") + "sor"] = String("gp") + "s";
+
+  // You can compare the content of a JsonObject with a String
+  if (root["sensor"] == sensor) {
+    // ...
+  }
+
+  // Lastly, you can print the resulting JSON to a String
+  String output;
+  root.printTo(output);
+}
+
+void loop() {
+  // not used in this example
+}
+
+// See also
+// --------
+//
+// The website arduinojson.org contains the documentation for all the functions
+// used above. It also includes an FAQ that will help you solve any problem.
+// Please check it out at: https://arduinojson.org/
+//
+// The book "Mastering ArduinoJson" contains a quick C++ course that explains
+// how your microcontroller stores strings in memory. On several occasions, it
+// shows how you can avoid String in your program.
+// Please check it out at: https://arduinojson.org/book/
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/Makefile b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..f3ed397f54e7615f1281cc0fe092bca5219af158
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/Makefile
@@ -0,0 +1,19 @@
+# CAUTION: this file is invoked by https://github.com/google/oss-fuzz
+
+CXXFLAGS += -I../src
+
+all: \
+	$(OUT)/json_fuzzer \
+	$(OUT)/json_fuzzer_seed_corpus.zip \
+	$(OUT)/json_fuzzer.options
+
+$(OUT)/json_fuzzer: fuzzer.cpp $(shell find ../src -type f)
+	$(CXX) $(CXXFLAGS) $< -o$@ $(LIB_FUZZING_ENGINE)
+
+$(OUT)/json_fuzzer_seed_corpus.zip: seed_corpus/*
+	zip -j $@ $?
+
+$(OUT)/json_fuzzer.options:
+	@echo "[libfuzzer]" > $@
+	@echo "max_len = 256" >> $@
+	@echo "timeout = 10" >> $@
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/fuzz.sh b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/fuzz.sh
new file mode 100644
index 0000000000000000000000000000000000000000..e9f28c394876f99439da7801af99a2bb945151ff
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/fuzz.sh
@@ -0,0 +1,9 @@
+#!/bin/bash
+# This script mimics an invocation from https://github.com/google/oss-fuzz
+
+cd $(dirname $0)
+export CXX='clang++'
+export CXXFLAGS='-fsanitize-coverage=trace-pc-guard -fsanitize=address'
+export LIB_FUZZING_ENGINE=-lFuzzer
+make OUT=.
+./json_fuzzer my_corpus seed_corpus -max_len=1024 -timeout=10
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/fuzzer.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/fuzzer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..24b2f19117d23a8becdd4c87c94cb2786c755c61
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/fuzzer.cpp
@@ -0,0 +1,26 @@
+#include <ArduinoJson.h>
+
+class memstream : public std::istream {
+  struct membuf : std::streambuf {
+    membuf(const uint8_t *p, size_t l) {
+      setg((char *)p, (char *)p, (char *)p + l);
+    }
+  };
+  membuf _buffer;
+
+ public:
+  memstream(const uint8_t *p, size_t l)
+      : std::istream(&_buffer), _buffer(p, l) {
+    rdbuf(&_buffer);
+  }
+};
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+  DynamicJsonBuffer jsonBuffer;
+  memstream json(data, size);
+  JsonVariant variant = jsonBuffer.parse(json);
+  if (variant.success()) {
+    variant.as<std::string>();  // <- serialize to JSON
+  }
+  return 0;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/my_corpus/.gitignore b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/my_corpus/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d6b7ef32c8478a48c3994dcadc86837f4371184d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/my_corpus/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/Comments.json b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/Comments.json
new file mode 100644
index 0000000000000000000000000000000000000000..bcc4cece6cef14e5fc32c3319282067d7278bfcf
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/Comments.json
@@ -0,0 +1,10 @@
+//comment
+/*comment*/
+[ //comment
+/*comment*/"comment"/*comment*/,//comment
+/*comment*/{//comment
+/* comment*/"key"//comment
+: //comment
+"value"//comment
+}/*comment*/
+]//comment
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/EmptyArray.json b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/EmptyArray.json
new file mode 100644
index 0000000000000000000000000000000000000000..0637a088a01e8ddab3bf3fa98dbe804cbde1a0dc
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/EmptyArray.json
@@ -0,0 +1 @@
+[]
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/EmptyObject.json b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/EmptyObject.json
new file mode 100644
index 0000000000000000000000000000000000000000..9e26dfeeb6e641a33dae4961196235bdb965b21b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/EmptyObject.json
@@ -0,0 +1 @@
+{}
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/ExcessiveNesting.json b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/ExcessiveNesting.json
new file mode 100644
index 0000000000000000000000000000000000000000..9285019af41efb3c135ce7d20cf038552c54caf6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/ExcessiveNesting.json
@@ -0,0 +1 @@
+[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,[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,[52,[53,[54,[55,[56,[57,[58,[59,[60,[61,[62,[63,[64,[65,[66,[67,[68,[69,[70,[71,[72,[73,[74,[75,[76,[77,[78,[79,[80,[81,[82,[83,[84,[85,[86,[87,[88,[89,[90,[91,[92,[93,[94,[95,[96,[97,[98,[99,[100,[101,[102,[103,[104,[105,[106,[107,[108,[109,[110,[111,[112,[113,[114,[115,[116,[117,[118,[119,[120]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/Numbers.json b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/Numbers.json
new file mode 100644
index 0000000000000000000000000000000000000000..f6b9419422fad9851a2a8141d42093dc2a0a93e6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/Numbers.json
@@ -0,0 +1,24 @@
+[
+  123,
+  -123,
+  123.456,
+  -123.456,
+  12e34,
+  12e-34,
+  12e+34,
+  12E34,
+  12E-34,
+  12E+34,
+  12.34e56,
+  12.34e-56,
+  12.34e+56,
+  12.34E56,
+  12.34E-56,
+  12.34E+56,
+  NaN,
+  -NaN,
+  +NaN,
+  Infinity,
+  +Infinity,
+  -Infinity
+]
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/OpenWeatherMap.json b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/OpenWeatherMap.json
new file mode 100644
index 0000000000000000000000000000000000000000..27d6bafd469bf506166a03dccf0cef7710a66a96
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/OpenWeatherMap.json
@@ -0,0 +1,53 @@
+{
+  "coord": {
+    "lon": -0.13,
+    "lat": 51.51
+  },
+  "weather": [
+    {
+      "id": 301,
+      "main": "Drizzle",
+      "description": "drizzle",
+      "icon": "09n"
+    },
+    {
+      "id": 701,
+      "main": "Mist",
+      "description": "mist",
+      "icon": "50n"
+    },
+    {
+      "id": 741,
+      "main": "Fog",
+      "description": "fog",
+      "icon": "50n"
+    }
+  ],
+  "base": "stations",
+  "main": {
+    "temp": 281.87,
+    "pressure": 1032,
+    "humidity": 100,
+    "temp_min": 281.15,
+    "temp_max": 283.15
+  },
+  "visibility": 2900,
+  "wind": {
+    "speed": 1.5
+  },
+  "clouds": {
+    "all": 90
+  },
+  "dt": 1483820400,
+  "sys": {
+    "type": 1,
+    "id": 5091,
+    "message": 0.0226,
+    "country": "GB",
+    "sunrise": 1483776245,
+    "sunset": 1483805443
+  },
+  "id": 2643743,
+  "name": "London",
+  "cod": 200
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/Strings.json b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/Strings.json
new file mode 100644
index 0000000000000000000000000000000000000000..3ffa235e643a0b50cadf84d61d04d9ef5c6e5033
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/Strings.json
@@ -0,0 +1,8 @@
+[
+  "hello",
+  'hello',
+  hello,
+  {"hello":"world"},
+  {'hello':'world'},
+  {hello:world}
+]
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/WeatherUnderground.json b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/WeatherUnderground.json
new file mode 100644
index 0000000000000000000000000000000000000000..d53ce0064a99025a915694f3581ea1689ddfbfe7
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/fuzzing/seed_corpus/WeatherUnderground.json
@@ -0,0 +1,90 @@
+{
+  "response": {
+    "version": "0.1",
+    "termsofService": "http://www.wunderground.com/weather/api/d/terms.html",
+    "features": {
+      "conditions": 1
+    }
+  },
+  "current_observation": {
+    "image": {
+      "url": "http://icons-ak.wxug.com/graphics/wu2/logo_130x80.png",
+      "title": "Weather Underground",
+      "link": "http://www.wunderground.com"
+    },
+    "display_location": {
+      "full": "San Francisco, CA",
+      "city": "San Francisco",
+      "state": "CA",
+      "state_name": "California",
+      "country": "US",
+      "country_iso3166": "US",
+      "zip": "94101",
+      "latitude": "37.77500916",
+      "longitude": "-122.41825867",
+      "elevation": "47.00000000"
+    },
+    "observation_location": {
+      "full": "SOMA - Near Van Ness, San Francisco, California",
+      "city": "SOMA - Near Van Ness, San Francisco",
+      "state": "California",
+      "country": "US",
+      "country_iso3166": "US",
+      "latitude": "37.773285",
+      "longitude": "-122.417725",
+      "elevation": "49 ft"
+    },
+    "estimated": {},
+    "station_id": "KCASANFR58",
+    "observation_time": "Last Updated on June 27, 5:27 PM PDT",
+    "observation_time_rfc822": "Wed, 27 Jun 2012 17:27:13 -0700",
+    "observation_epoch": "1340843233",
+    "local_time_rfc822": "Wed, 27 Jun 2012 17:27:14 -0700",
+    "local_epoch": "1340843234",
+    "local_tz_short": "PDT",
+    "local_tz_long": "America/Los_Angeles",
+    "local_tz_offset": "-0700",
+    "weather": "Partly Cloudy",
+    "temperature_string": "66.3 F (19.1 C)",
+    "temp_f": 66.3,
+    "temp_c": 19.1,
+    "relative_humidity": "65%",
+    "wind_string": "From the NNW at 22.0 MPH Gusting to 28.0 MPH",
+    "wind_dir": "NNW",
+    "wind_degrees": 346,
+    "wind_mph": 22,
+    "wind_gust_mph": "28.0",
+    "wind_kph": 35.4,
+    "wind_gust_kph": "45.1",
+    "pressure_mb": "1013",
+    "pressure_in": "29.93",
+    "pressure_trend": "+",
+    "dewpoint_string": "54 F (12 C)",
+    "dewpoint_f": 54,
+    "dewpoint_c": 12,
+    "heat_index_string": "NA",
+    "heat_index_f": "NA",
+    "heat_index_c": "NA",
+    "windchill_string": "NA",
+    "windchill_f": "NA",
+    "windchill_c": "NA",
+    "feelslike_string": "66.3 F (19.1 C)",
+    "feelslike_f": "66.3",
+    "feelslike_c": "19.1",
+    "visibility_mi": "10.0",
+    "visibility_km": "16.1",
+    "solarradiation": "",
+    "UV": "5",
+    "precip_1hr_string": "0.00 in ( 0 mm)",
+    "precip_1hr_in": "0.00",
+    "precip_1hr_metric": " 0",
+    "precip_today_string": "0.00 in (0 mm)",
+    "precip_today_in": "0.00",
+    "precip_today_metric": "0",
+    "icon": "partlycloudy",
+    "icon_url": "http://icons-ak.wxug.com/i/c/k/partlycloudy.gif",
+    "forecast_url": "http://www.wunderground.com/US/CA/San_Francisco.html",
+    "history_url": "http://www.wunderground.com/history/airport/KCASANFR58/2012/6/27/DailyHistory.html",
+    "ob_url": "http://www.wunderground.com/cgi-bin/findweather/getForecast?query=37.773285,-122.417725"
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/keywords.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/keywords.txt
new file mode 100644
index 0000000000000000000000000000000000000000..833cddb7379684b841987a3a3f42b24e4a695041
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/keywords.txt
@@ -0,0 +1,15 @@
+JsonArray	KEYWORD1
+JsonObject	KEYWORD1
+JsonVariant	KEYWORD1
+StaticJsonBuffer	KEYWORD1
+DynamicJsonBuffer	KEYWORD1
+add	KEYWORD2
+createArray	KEYWORD2
+createNestedArray	KEYWORD2
+createNestedObject	KEYWORD2
+createObject	KEYWORD2
+parseArray	KEYWORD2
+parseObject	KEYWORD2
+prettyPrintTo	KEYWORD2
+printTo	KEYWORD2
+success	KEYWORD2
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/library.json b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..eb790b92891f470129d6d6d8d67446572e1dc6ea
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/library.json
@@ -0,0 +1,23 @@
+{
+  "name": "ArduinoJson",
+  "keywords": "json, rest, http, web",
+  "description": "An elegant and efficient JSON library for embedded systems",
+  "homepage": "https://arduinojson.org/?utm_source=meta&utm_medium=library.json",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/bblanchon/ArduinoJson.git"
+  },
+  "version": "5.13.1",
+  "authors": {
+    "name": "Benoit Blanchon",
+    "url": "https://blog.benoitblanchon.fr"
+  },
+  "exclude": [
+    "fuzzing",
+    "scripts",
+    "test",
+    "third-party"
+  ],
+  "frameworks": "arduino",
+  "platforms": "*"
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/library.properties b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/library.properties
new file mode 100644
index 0000000000000000000000000000000000000000..97e2f03a72986598f3f0b51a3126d69440ed2e97
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/library.properties
@@ -0,0 +1,11 @@
+name=ArduinoJson
+version=5.13.1
+author=Benoit Blanchon <blog.benoitblanchon.fr>
+maintainer=Benoit Blanchon <blog.benoitblanchon.fr>
+sentence=An efficient and elegant JSON library for Arduino.
+paragraph=ArduinoJson supports ✔ serialization, ✔ deserialization, ✔ fixed allocation, ✔ zero-copy, ✔ streams, and more. It is the most popular Arduino library on GitHub ❤❤❤❤❤. Check out arduinojson.org for a comprehensive documentation.
+category=Data Processing
+url=https://arduinojson.org/?utm_source=meta&utm_medium=library.properties
+architectures=*
+repository=https://github.com/bblanchon/ArduinoJson.git
+license=MIT
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/build-arduino-package.sh b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/build-arduino-package.sh
new file mode 100644
index 0000000000000000000000000000000000000000..6976bcd311a160efe06e403a70e3062020b144a4
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/build-arduino-package.sh
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+TAG=$(git describe)
+OUTPUT="ArduinoJson-$TAG.zip"
+
+cd $(dirname $0)/../..
+
+# remove existing file
+rm -f $OUTPUT
+
+# create zip
+7z a $OUTPUT \
+	ArduinoJson/CHANGELOG.md \
+	ArduinoJson/examples \
+	ArduinoJson/src \
+	ArduinoJson/keywords.txt \
+	ArduinoJson/library.properties \
+	ArduinoJson/LICENSE.md \
+	ArduinoJson/README.md \
+	ArduinoJson/ArduinoJson.h
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/build-single-header.sh b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/build-single-header.sh
new file mode 100644
index 0000000000000000000000000000000000000000..b09e91f604842fbdf4492e8e3a7e5ce904dfb6f6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/build-single-header.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+TAG=$(git describe)
+RE_INCLUDE='^#include[[:space:]]*["<](.*)[">]'
+RE_EMPTY='^(#pragma[[:space:]]+once)?[[:space:]]*(//.*)?$'
+
+declare -A INCLUDED
+
+process()
+{
+	local PARENT=$1
+	local FOLDER=$(dirname $1)
+	local SHOW_COMMENT=$2
+	while IFS= read -r LINE; do
+		if [[ $LINE =~ $RE_INCLUDE ]]; then
+			local CHILD=${BASH_REMATCH[1]}
+			pushd "$FOLDER" > /dev/null
+			if [[ -e $CHILD ]]; then
+				local CHILD_PATH=$(realpath $CHILD)
+				if [[ ! ${INCLUDED[$CHILD_PATH]} ]]; then
+					#echo "// $PARENT -> $CHILD"
+					INCLUDED[$CHILD_PATH]=true
+					process "$CHILD" false
+				fi
+			else
+				if [[ ! ${INCLUDED[$CHILD]} ]]; then
+					echo "$LINE"
+					INCLUDED[$CHILD]=true
+				fi
+			fi
+			popd > /dev/null
+		elif [[ "${SHOW_COMMENT}" = "true" ]] ; then
+			echo "$LINE"
+		elif [[ ! $LINE =~ $RE_EMPTY ]]; then
+			echo "$LINE"
+		fi
+	done < $PARENT
+}
+
+cd $(dirname $0)/../
+INCLUDED=()
+process src/ArduinoJson.h true > ../ArduinoJson-$TAG.h
+g++ -x c++ -c -o ../smoketest.o - <<END
+#include "../ArduinoJson-$TAG.h"
+int main() {}
+END
+
+INCLUDED=()
+process src/ArduinoJson.hpp true > ../ArduinoJson-$TAG.hpp
+g++ -x c++ -c -o ../smoketest.o - <<END
+#include "../ArduinoJson-$TAG.hpp"
+int main() {}
+END
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/create-build-envs.sh b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/create-build-envs.sh
new file mode 100644
index 0000000000000000000000000000000000000000..d5750a4045e2d6f8a87b03b984e03749f13afdd1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/create-build-envs.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+export PATH="$PATH:/Applications/CMake.app/Contents/bin/"
+
+cd $(dirname $0)/..
+ROOT=$(pwd)
+
+mkdir "build"
+cd build
+BUILD=$(pwd)
+
+build-env()
+{
+	cd $BUILD
+	mkdir "$1"
+	cd "$1"
+	cmake "$ROOT" -G "$2"
+}
+
+if [[ $(uname) == MINGW* ]]
+then
+	build-env "Make" "MinGW Makefiles"
+	build-env "SublimeText" "Sublime Text 2 - Ninja"
+	build-env "VisualStudio" "Visual Studio 14 2015"
+else
+	build-env "SublimeText" "Sublime Text 2 - Ninja"
+	build-env "Make" "Unix Makefiles"
+	build-env "Xcode" "Xcode"
+fi
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/create-size-graph.sh b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/create-size-graph.sh
new file mode 100644
index 0000000000000000000000000000000000000000..ed37e863aa1fdb749fd23187504de1304b97a5a9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/create-size-graph.sh
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+set -eu
+
+OUTPUT="$(pwd)/sizes.csv"
+
+echo "Tag;Date;Parser;Generator" > $OUTPUT
+
+cd $(dirname $(dirname $0))
+
+git tag | while read TAG
+do 
+
+	git checkout -q tags/$TAG
+
+	DATE=$(git log -1 --date=short --pretty=format:%cd)
+	PARSER_SIZE=$(arduino --verify examples/JsonParserExample/JsonParserExample.ino 2>/dev/null  | grep -e 'Sketch uses' | sed 's/.*uses \([0-9]*\).\([0-9]\+\).*/\1\2/')
+	
+	if [ -e 'examples/JsonGeneratorExample/JsonGeneratorExample.ino' ]; then
+		GENERATOR_SIZE=$(arduino --verify examples/JsonGeneratorExample/JsonGeneratorExample.ino 2>/dev/null  | grep -e 'Sketch uses' | sed 's/.*uses \([0-9]*\).\([0-9]\+\).*/\1\2/')
+	else
+		GENERATOR_SIZE=""
+	fi
+
+	echo $TAG 
+	if [ ! -z "$PARSER_SIZE" ]
+	then
+		echo "JsonParserExample = $PARSER_SIZE bytes"
+	else
+		echo "JsonParserExample compilation failed."
+	fi
+
+	if [ ! -z "$GENERATOR_SIZE" ]
+	then
+		echo "JsonGeneratorExample = $GENERATOR_SIZE bytes"
+	else
+		echo "JsonGeneratorExample compilation failed."
+	fi
+
+	echo "$TAG;$DATE;$PARSER_SIZE;$GENERATOR_SIZE" >> $OUTPUT
+
+done
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/oss-fuzz/.gitignore b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/oss-fuzz/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..d50ee7720d5015f01e9700dcf5bcdfffccdf7489
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/oss-fuzz/.gitignore
@@ -0,0 +1,2 @@
+/.vagrant/
+*.log
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/oss-fuzz/Vagrantfile b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/oss-fuzz/Vagrantfile
new file mode 100644
index 0000000000000000000000000000000000000000..252c191c1b1fba578b573841c88006b9b12288d1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/oss-fuzz/Vagrantfile
@@ -0,0 +1,33 @@
+# A virtual machine to run https://github.com/google/oss-fuzz
+Vagrant.configure(2) do |config|
+  config.vm.box = "ubuntu/xenial64"
+
+  config.vm.synced_folder "E:\\Git\\Arduino\\libraries\\ArduinoJson", "/host/ArduinoJson"
+  config.vm.synced_folder "E:\\Git\\oss-fuzz", "/host/oss-fuzz"
+
+  config.vm.network "forwarded_port", guest: 8001, host: 8001
+
+  config.vm.provision "shell", privileged: false, inline: <<-SHELL
+    set -x
+
+    sudo apt-get update
+    sudo apt-get install -y make git docker.io zip
+    sudo groupadd docker
+    sudo usermod -aG docker $USER
+
+    git clone https://github.com/google/fuzzer-test-suite.git FTS
+    ./FTS/tutorial/install-deps.sh  # Get deps
+    ./FTS/tutorial/install-clang.sh # Get fresh clang binaries
+    # Get libFuzzer sources and build it
+    svn co http://llvm.org/svn/llvm-project/llvm/trunk/lib/Fuzzer
+    Fuzzer/build.sh
+    sudo mv libFuzzer.a /usr/local/lib/
+
+    echo "export PROJECT_NAME='arduinojson'" >> $HOME/.profile
+    echo "export CC='clang'" >> $HOME/.profile
+    echo "export CXX='clang++'" >> $HOME/.profile
+    echo "export LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/" >> $HOME/.profile
+
+    echo "Run /host/ArduinoJson/fuzzing/fuzz.sh" | sudo tee /etc/motd
+  SHELL
+end
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/arduino.sh b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/arduino.sh
new file mode 100644
index 0000000000000000000000000000000000000000..4de24c154a15dd7e3eaa7a1cb50e9f79eadc7e54
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/arduino.sh
@@ -0,0 +1,16 @@
+#!/bin/sh -eux
+
+/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
+sleep 3
+export DISPLAY=:1.0
+
+mkdir -p /tmp/arduino
+curl -sS http://downloads.arduino.cc/arduino-$VERSION-linux64.tar.xz | tar xJ -C /tmp/arduino --strip 1 ||
+curl -sS http://downloads.arduino.cc/arduino-$VERSION-linux64.tgz | tar xz -C /tmp/arduino --strip 1 
+export PATH=$PATH:/tmp/arduino/
+  
+ln -s $PWD /tmp/arduino/libraries/ArduinoJson
+
+for EXAMPLE in $PWD/examples/*/*.ino; do
+	arduino --verify --board $BOARD $EXAMPLE
+done
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/cmake.sh b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/cmake.sh
new file mode 100644
index 0000000000000000000000000000000000000000..56691d9b810f1aebfda91a5ad2ed1a4dea564ab3
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/cmake.sh
@@ -0,0 +1,30 @@
+#!/bin/sh -ex
+
+if [ $(uname) = 'Darwin' ]; then
+	URL=https://cmake.org/files/v3.4/cmake-3.4.3-Darwin-x86_64.tar.gz
+	CMAKE=/tmp/CMake.app/Contents/bin/cmake
+	CTEST=/tmp/CMake.app/Contents/bin/ctest
+else
+	URL=https://cmake.org/files/v3.4/cmake-3.4.3-Linux-x86_64.tar.gz
+	CMAKE=/tmp/bin/cmake
+	CTEST=/tmp/bin/ctest
+fi
+curl -sS $URL | tar xz -C /tmp --strip 1
+
+if [ -n "$GCC" ]; then
+	export CC="gcc-$GCC"
+	export CXX="g++-$GCC"
+fi
+
+if [ -n "$CLANG" ]; then
+	export CC="clang-$CLANG"
+	export CXX="clang++-$CLANG"
+fi
+
+if [ -n "$SANITIZE" ]; then
+	export CXXFLAGS="-fsanitize=$SANITIZE"
+fi
+
+$CMAKE .
+$CMAKE --build .
+$CTEST --output-on-failure .
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/coverage.sh b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/coverage.sh
new file mode 100644
index 0000000000000000000000000000000000000000..20de1e6a6722c3ff638aa0005f9e64e4188842e3
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/coverage.sh
@@ -0,0 +1,10 @@
+#!/bin/sh -eux
+
+curl https://cmake.org/files/v3.4/cmake-3.4.0-Linux-x86_64.tar.gz | tar xz -C /tmp --strip 1
+
+/tmp/bin/cmake -DCOVERAGE=true .
+make 
+make test
+
+pip install --user cpp-coveralls 'requests[security]'
+coveralls --exclude third-party --gcov-options '\-lp'; fi
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/platformio.sh b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/platformio.sh
new file mode 100644
index 0000000000000000000000000000000000000000..4bb7a4c96b62a208022849bee757bee4f02dbff3
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/scripts/travis/platformio.sh
@@ -0,0 +1,10 @@
+#!/bin/sh -eux
+
+pip install --user platformio
+
+rm -r test
+
+for EXAMPLE in $PWD/examples/*/*.ino; 
+do
+	platformio ci $EXAMPLE -l '.' -b $BOARD
+done
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson.h b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson.h
new file mode 100644
index 0000000000000000000000000000000000000000..3782aeabcf1b90e45347c63c26a2e0dc5891a1c8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson.h
@@ -0,0 +1,17 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#ifdef __cplusplus
+
+#include "ArduinoJson.hpp"
+
+using namespace ArduinoJson;
+
+#else
+
+#error ArduinoJson requires a C++ compiler, please change file extension to .cc or .cpp
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..445a2c8a696d00e1fda4ec62f14db59d0e3aa090
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson.hpp
@@ -0,0 +1,17 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "ArduinoJson/DynamicJsonBuffer.hpp"
+#include "ArduinoJson/JsonArray.hpp"
+#include "ArduinoJson/JsonObject.hpp"
+#include "ArduinoJson/StaticJsonBuffer.hpp"
+
+#include "ArduinoJson/Deserialization/JsonParserImpl.hpp"
+#include "ArduinoJson/JsonArrayImpl.hpp"
+#include "ArduinoJson/JsonBufferImpl.hpp"
+#include "ArduinoJson/JsonObjectImpl.hpp"
+#include "ArduinoJson/JsonVariantImpl.hpp"
+#include "ArduinoJson/Serialization/JsonSerializerImpl.hpp"
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Configuration.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Configuration.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..82483adfa408a14a97fb7a09413d7860667c8414
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Configuration.hpp
@@ -0,0 +1,151 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+// Small or big machine?
+#ifndef ARDUINOJSON_EMBEDDED_MODE
+#if defined(ARDUINO) || defined(__IAR_SYSTEMS_ICC__) || defined(__XC) || \
+    defined(__ARMCC_VERSION)
+#define ARDUINOJSON_EMBEDDED_MODE 1
+#else
+#define ARDUINOJSON_EMBEDDED_MODE 0
+#endif
+#endif
+
+#if ARDUINOJSON_EMBEDDED_MODE
+
+// Store floats by default to reduce the memory usage (issue #134)
+#ifndef ARDUINOJSON_USE_DOUBLE
+#define ARDUINOJSON_USE_DOUBLE 0
+#endif
+
+// Store longs by default, because they usually match the size of a float.
+#ifndef ARDUINOJSON_USE_LONG_LONG
+#define ARDUINOJSON_USE_LONG_LONG 0
+#endif
+#ifndef ARDUINOJSON_USE_INT64
+#define ARDUINOJSON_USE_INT64 0
+#endif
+
+// Embedded systems usually don't have std::string
+#ifndef ARDUINOJSON_ENABLE_STD_STRING
+#define ARDUINOJSON_ENABLE_STD_STRING 0
+#endif
+
+// Embedded systems usually don't have std::stream
+#ifndef ARDUINOJSON_ENABLE_STD_STREAM
+#define ARDUINOJSON_ENABLE_STD_STREAM 0
+#endif
+
+// Limit nesting as the stack is likely to be small
+#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT
+#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 10
+#endif
+
+#else  // ARDUINOJSON_EMBEDDED_MODE
+
+// On a computer we have plenty of memory so we can use doubles
+#ifndef ARDUINOJSON_USE_DOUBLE
+#define ARDUINOJSON_USE_DOUBLE 1
+#endif
+
+// Use long long when available
+#ifndef ARDUINOJSON_USE_LONG_LONG
+#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1800)
+#define ARDUINOJSON_USE_LONG_LONG 1
+#else
+#define ARDUINOJSON_USE_LONG_LONG 0
+#endif
+#endif
+
+// Use _int64 on old versions of Visual Studio
+#ifndef ARDUINOJSON_USE_INT64
+#if defined(_MSC_VER) && _MSC_VER <= 1700
+#define ARDUINOJSON_USE_INT64 1
+#else
+#define ARDUINOJSON_USE_INT64 0
+#endif
+#endif
+
+// On a computer, we can use std::string
+#ifndef ARDUINOJSON_ENABLE_STD_STRING
+#define ARDUINOJSON_ENABLE_STD_STRING 1
+#endif
+
+// On a computer, we can assume std::stream
+#ifndef ARDUINOJSON_ENABLE_STD_STREAM
+#define ARDUINOJSON_ENABLE_STD_STREAM 1
+#endif
+
+// On a computer, the stack is large so we can increase nesting limit
+#ifndef ARDUINOJSON_DEFAULT_NESTING_LIMIT
+#define ARDUINOJSON_DEFAULT_NESTING_LIMIT 50
+#endif
+
+#endif  // ARDUINOJSON_EMBEDDED_MODE
+
+#ifdef ARDUINO
+
+// Enable support for Arduino String
+#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING
+#define ARDUINOJSON_ENABLE_ARDUINO_STRING 1
+#endif
+
+// Enable support for Arduino Stream
+#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM
+#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 1
+#endif
+
+#else  // ARDUINO
+
+// Disable support for Arduino String
+#ifndef ARDUINOJSON_ENABLE_ARDUINO_STRING
+#define ARDUINOJSON_ENABLE_ARDUINO_STRING 0
+#endif
+
+// Disable support for Arduino Stream
+#ifndef ARDUINOJSON_ENABLE_ARDUINO_STREAM
+#define ARDUINOJSON_ENABLE_ARDUINO_STREAM 0
+#endif
+
+#endif  // ARDUINO
+
+#ifndef ARDUINOJSON_ENABLE_PROGMEM
+#ifdef PROGMEM
+#define ARDUINOJSON_ENABLE_PROGMEM 1
+#else
+#define ARDUINOJSON_ENABLE_PROGMEM 0
+#endif
+#endif
+
+#ifndef ARDUINOJSON_ENABLE_ALIGNMENT
+#ifdef ARDUINO_ARCH_AVR
+// alignment isn't needed for 8-bit AVR
+#define ARDUINOJSON_ENABLE_ALIGNMENT 0
+#else
+// but most processors need pointers to be align on word size
+#define ARDUINOJSON_ENABLE_ALIGNMENT 1
+#endif
+#endif
+
+// Enable deprecated functions by default
+#ifndef ARDUINOJSON_ENABLE_DEPRECATED
+#define ARDUINOJSON_ENABLE_DEPRECATED 1
+#endif
+
+// Control the exponentiation threshold for big numbers
+// CAUTION: cannot be more that 1e9 !!!!
+#ifndef ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD
+#define ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD 1e7
+#endif
+
+// Control the exponentiation threshold for small numbers
+#ifndef ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD
+#define ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD 1e-5
+#endif
+
+#if ARDUINOJSON_USE_LONG_LONG && ARDUINOJSON_USE_INT64
+#error ARDUINOJSON_USE_LONG_LONG and ARDUINOJSON_USE_INT64 cannot be set together
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/Encoding.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/Encoding.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a0efa2c74994db367b9f3632bd71e85fff446106
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/Encoding.hpp
@@ -0,0 +1,37 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+class Encoding {
+ public:
+  // Optimized for code size on a 8-bit AVR
+  static char escapeChar(char c) {
+    const char *p = escapeTable(false);
+    while (p[0] && p[1] != c) {
+      p += 2;
+    }
+    return p[0];
+  }
+
+  // Optimized for code size on a 8-bit AVR
+  static char unescapeChar(char c) {
+    const char *p = escapeTable(true);
+    for (;;) {
+      if (p[0] == '\0') return c;
+      if (p[0] == c) return p[1];
+      p += 2;
+    }
+  }
+
+ private:
+  static const char *escapeTable(bool excludeIdenticals) {
+    return &"\"\"\\\\b\bf\fn\nr\rt\t"[excludeIdenticals ? 4 : 0];
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonBufferAllocated.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonBufferAllocated.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..443aae4df5423f0c24d82ea00903c0c4339a346e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonBufferAllocated.hpp
@@ -0,0 +1,22 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../JsonBuffer.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+class JsonBufferAllocated {
+ public:
+  void *operator new(size_t n, JsonBuffer *jsonBuffer) throw() {
+    if (!jsonBuffer) return NULL;
+    return jsonBuffer->alloc(n);
+  }
+
+  void operator delete(void *, JsonBuffer *)throw();
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonFloat.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonFloat.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0ed42140f58d90a2651b364fae1e1398ed75dcac
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonFloat.hpp
@@ -0,0 +1,18 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../Configuration.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+#if ARDUINOJSON_USE_DOUBLE
+typedef double JsonFloat;
+#else
+typedef float JsonFloat;
+#endif
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonInteger.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonInteger.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c8ddd00b48b041d4d4c492f9911df04dacf9473e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonInteger.hpp
@@ -0,0 +1,23 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../Configuration.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+#if ARDUINOJSON_USE_LONG_LONG
+typedef long long JsonInteger;
+typedef unsigned long long JsonUInt;
+#elif ARDUINOJSON_USE_INT64
+typedef __int64 JsonInteger;
+typedef unsigned _int64 JsonUInt;
+#else
+typedef long JsonInteger;
+typedef unsigned long JsonUInt;
+#endif
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantAs.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantAs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8f202c5eb0dddf4440151dd566bea77a471a58e9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantAs.hpp
@@ -0,0 +1,42 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A metafunction that returns the type of the value returned by
+// JsonVariant::as<T>()
+template <typename T>
+struct JsonVariantAs {
+  typedef T type;
+};
+
+template <>
+struct JsonVariantAs<char*> {
+  typedef const char* type;
+};
+
+template <>
+struct JsonVariantAs<JsonArray> {
+  typedef JsonArray& type;
+};
+
+template <>
+struct JsonVariantAs<const JsonArray> {
+  typedef const JsonArray& type;
+};
+
+template <>
+struct JsonVariantAs<JsonObject> {
+  typedef JsonObject& type;
+};
+
+template <>
+struct JsonVariantAs<const JsonObject> {
+  typedef const JsonObject& type;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantContent.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantContent.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c525a60602211735b22c73c01a4efc8b7fcd5701
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantContent.hpp
@@ -0,0 +1,27 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "JsonFloat.hpp"
+#include "JsonInteger.hpp"
+
+namespace ArduinoJson {
+
+// Forward declarations
+class JsonArray;
+class JsonObject;
+
+namespace Internals {
+// A union that defines the actual content of a JsonVariant.
+// The enum JsonVariantType determines which member is in use.
+union JsonVariantContent {
+  JsonFloat asFloat;     // used for double and float
+  JsonUInt asInteger;    // used for bool, char, short, int and longs
+  const char* asString;  // asString can be null
+  JsonArray* asArray;    // asArray cannot be null
+  JsonObject* asObject;  // asObject cannot be null
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantDefault.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantDefault.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..57ecc83ee0e67868da663e7135c9666cf9226df5
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantDefault.hpp
@@ -0,0 +1,23 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename T>
+struct JsonVariantDefault {
+  static T get() {
+    return T();
+  }
+};
+
+template <typename T>
+struct JsonVariantDefault<const T> : JsonVariantDefault<T> {};
+
+template <typename T>
+struct JsonVariantDefault<T&> : JsonVariantDefault<T> {};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantType.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantType.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..21f890e522ade74f5f553484de7e6da3c6124896
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/JsonVariantType.hpp
@@ -0,0 +1,27 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+class JsonArray;
+class JsonObject;
+
+namespace Internals {
+
+// Enumerated type to know the current type of a JsonVariant.
+// The value determines which member of JsonVariantContent is used.
+enum JsonVariantType {
+  JSON_UNDEFINED,         // JsonVariant has not been initialized
+  JSON_UNPARSED,          // JsonVariant contains an unparsed string
+  JSON_STRING,            // JsonVariant stores a const char*
+  JSON_BOOLEAN,           // JsonVariant stores a bool
+  JSON_POSITIVE_INTEGER,  // JsonVariant stores an JsonUInt
+  JSON_NEGATIVE_INTEGER,  // JsonVariant stores an JsonUInt that must be negated
+  JSON_ARRAY,             // JsonVariant stores a pointer to a JsonArray
+  JSON_OBJECT,            // JsonVariant stores a pointer to a JsonObject
+  JSON_FLOAT              // JsonVariant stores a JsonFloat
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/List.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/List.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..506308cc3dc71f0da781383ee0c6dcda878d6746
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/List.hpp
@@ -0,0 +1,94 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../JsonBuffer.hpp"
+#include "ListConstIterator.hpp"
+#include "ListIterator.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A singly linked list of T.
+// The linked list is composed of ListNode<T>.
+// It is derived by JsonArray and JsonObject
+template <typename T>
+class List {
+ public:
+  typedef T value_type;
+  typedef ListNode<T> node_type;
+  typedef ListIterator<T> iterator;
+  typedef ListConstIterator<T> const_iterator;
+
+  // Creates an empty List<T> attached to a JsonBuffer.
+  // The JsonBuffer allows to allocate new nodes.
+  // When buffer is NULL, the List is not able to grow and success() returns
+  // false. This is used to identify bad memory allocations and parsing
+  // failures.
+  explicit List(JsonBuffer *buffer) : _buffer(buffer), _firstNode(NULL) {}
+
+  // Returns true if the object is valid
+  // Would return false in the following situation:
+  // - the memory allocation failed (StaticJsonBuffer was too small)
+  // - the JSON parsing failed
+  bool success() const {
+    return _buffer != NULL;
+  }
+
+  // Returns the numbers of elements in the list.
+  // For a JsonObject, it would return the number of key-value pairs
+  size_t size() const {
+    size_t nodeCount = 0;
+    for (node_type *node = _firstNode; node; node = node->next) nodeCount++;
+    return nodeCount;
+  }
+
+  iterator add() {
+    node_type *newNode = new (_buffer) node_type();
+
+    if (_firstNode) {
+      node_type *lastNode = _firstNode;
+      while (lastNode->next) lastNode = lastNode->next;
+      lastNode->next = newNode;
+    } else {
+      _firstNode = newNode;
+    }
+
+    return iterator(newNode);
+  }
+
+  iterator begin() {
+    return iterator(_firstNode);
+  }
+  iterator end() {
+    return iterator(NULL);
+  }
+
+  const_iterator begin() const {
+    return const_iterator(_firstNode);
+  }
+  const_iterator end() const {
+    return const_iterator(NULL);
+  }
+
+  void remove(iterator it) {
+    node_type *nodeToRemove = it._node;
+    if (!nodeToRemove) return;
+    if (nodeToRemove == _firstNode) {
+      _firstNode = nodeToRemove->next;
+    } else {
+      for (node_type *node = _firstNode; node; node = node->next)
+        if (node->next == nodeToRemove) node->next = nodeToRemove->next;
+    }
+  }
+
+ protected:
+  JsonBuffer *_buffer;
+
+ private:
+  node_type *_firstNode;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ListConstIterator.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ListConstIterator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a6af685e5ad05a02e935e6afd3663509f6cc9ebc
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ListConstIterator.hpp
@@ -0,0 +1,50 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "ListNode.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A read-only forward itertor for List<T>
+template <typename T>
+class ListConstIterator {
+ public:
+  explicit ListConstIterator(const ListNode<T> *node = NULL) : _node(node) {}
+
+  const T &operator*() const {
+    return _node->content;
+  }
+  const T *operator->() {
+    return &_node->content;
+  }
+
+  bool operator==(const ListConstIterator<T> &other) const {
+    return _node == other._node;
+  }
+
+  bool operator!=(const ListConstIterator<T> &other) const {
+    return _node != other._node;
+  }
+
+  ListConstIterator<T> &operator++() {
+    if (_node) _node = _node->next;
+    return *this;
+  }
+
+  ListConstIterator<T> &operator+=(size_t distance) {
+    while (_node && distance) {
+      _node = _node->next;
+      --distance;
+    }
+    return *this;
+  }
+
+ private:
+  const ListNode<T> *_node;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ListIterator.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ListIterator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..01fa287f7df5fbb3012402350baecf148af262ca
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ListIterator.hpp
@@ -0,0 +1,60 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "ListConstIterator.hpp"
+#include "ListNode.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename T>
+class List;
+
+// A read-write forward iterator for List<T>
+template <typename T>
+class ListIterator {
+  friend class List<T>;
+
+ public:
+  explicit ListIterator(ListNode<T> *node = NULL) : _node(node) {}
+
+  T &operator*() const {
+    return _node->content;
+  }
+  T *operator->() {
+    return &_node->content;
+  }
+
+  bool operator==(const ListIterator<T> &other) const {
+    return _node == other._node;
+  }
+
+  bool operator!=(const ListIterator<T> &other) const {
+    return _node != other._node;
+  }
+
+  ListIterator<T> &operator++() {
+    if (_node) _node = _node->next;
+    return *this;
+  }
+
+  ListIterator<T> &operator+=(size_t distance) {
+    while (_node && distance) {
+      _node = _node->next;
+      --distance;
+    }
+    return *this;
+  }
+
+  operator ListConstIterator<T>() const {
+    return ListConstIterator<T>(_node);
+  }
+
+ private:
+  ListNode<T> *_node;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ListNode.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ListNode.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c0907120e079fd367597488651643bbaefaa208b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ListNode.hpp
@@ -0,0 +1,24 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include <stddef.h>  // for NULL
+
+#include "JsonBufferAllocated.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A node for a singly-linked list.
+// Used by List<T> and its iterators.
+template <typename T>
+struct ListNode : public Internals::JsonBufferAllocated {
+  ListNode() throw() : next(NULL) {}
+
+  ListNode<T> *next;
+  T content;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/NonCopyable.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/NonCopyable.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..73f3d8edb0ed78f1905724c2e5f84f29aa84edb3
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/NonCopyable.hpp
@@ -0,0 +1,23 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A type that cannot be copied
+class NonCopyable {
+ protected:
+  NonCopyable() {}
+
+ private:
+  // copy constructor is private
+  NonCopyable(const NonCopyable&);
+
+  // copy operator is private
+  NonCopyable& operator=(const NonCopyable&);
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ReferenceType.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ReferenceType.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e491172f5df95de96adc900c09570b6fd040d3a
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ReferenceType.hpp
@@ -0,0 +1,24 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A type that is meant to be used by reference only (JsonArray and JsonObject)
+class ReferenceType {
+ public:
+  bool operator==(const ReferenceType& other) const {
+    // two JsonArray are equal if they are the same instance
+    // (we don't compare the content)
+    return this == &other;
+  }
+
+  bool operator!=(const ReferenceType& other) const {
+    return this != &other;
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ValueSaver.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ValueSaver.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9750f1ac52905486eda8b30d1538ea0f9e629fac
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Data/ValueSaver.hpp
@@ -0,0 +1,52 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../JsonBuffer.hpp"
+#include "../JsonVariant.hpp"
+#include "../StringTraits/StringTraits.hpp"
+#include "../TypeTraits/EnableIf.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename Source, typename Enable = void>
+struct ValueSaver {
+  template <typename Destination>
+  static bool save(JsonBuffer*, Destination& destination, Source source) {
+    destination = source;
+    return true;
+  }
+};
+
+template <typename Source>
+struct ValueSaver<
+    Source, typename EnableIf<StringTraits<Source>::should_duplicate>::type> {
+  template <typename Destination>
+  static bool save(JsonBuffer* buffer, Destination& dest, Source source) {
+    if (!StringTraits<Source>::is_null(source)) {
+      typename StringTraits<Source>::duplicate_t dup =
+          StringTraits<Source>::duplicate(source, buffer);
+      if (!dup) return false;
+      dest = dup;
+    } else {
+      dest = reinterpret_cast<const char*>(0);
+    }
+    return true;
+  }
+};
+
+// const char*, const signed char*, const unsigned char*
+template <typename Char>
+struct ValueSaver<
+    Char*, typename EnableIf<!StringTraits<Char*>::should_duplicate>::type> {
+  template <typename Destination>
+  static bool save(JsonBuffer*, Destination& dest, Char* source) {
+    dest = reinterpret_cast<const char*>(source);
+    return true;
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Comments.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Comments.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c2c48ebcc82753ba01586aabf8bf488cb9ea9f6b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/Comments.hpp
@@ -0,0 +1,61 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+template <typename TInput>
+void skipSpacesAndComments(TInput& input) {
+  for (;;) {
+    switch (input.current()) {
+      // spaces
+      case ' ':
+      case '\t':
+      case '\r':
+      case '\n':
+        input.move();
+        continue;
+
+      // comments
+      case '/':
+        switch (input.next()) {
+          // C-style block comment
+          case '*':
+            input.move();  // skip '/'
+            // no need to skip '*'
+            for (;;) {
+              input.move();
+              if (input.current() == '\0') return;
+              if (input.current() == '*' && input.next() == '/') {
+                input.move();  // skip '*'
+                input.move();  // skip '/'
+                break;
+              }
+            }
+            break;
+
+          // C++-style line comment
+          case '/':
+            // not need to skip "//"
+            for (;;) {
+              input.move();
+              if (input.current() == '\0') return;
+              if (input.current() == '\n') break;
+            }
+            break;
+
+          // not a comment, just a '/'
+          default:
+            return;
+        }
+        break;
+
+      default:
+        return;
+    }
+  }
+}
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/JsonParser.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/JsonParser.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c348e759af1746e9fd9688bac78e0f69fe7488b8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/JsonParser.hpp
@@ -0,0 +1,103 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../JsonBuffer.hpp"
+#include "../JsonVariant.hpp"
+#include "../TypeTraits/IsConst.hpp"
+#include "StringWriter.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// Parse JSON string to create JsonArrays and JsonObjects
+// This internal class is not indended to be used directly.
+// Instead, use JsonBuffer.parseArray() or .parseObject()
+template <typename TReader, typename TWriter>
+class JsonParser {
+ public:
+  JsonParser(JsonBuffer *buffer, TReader reader, TWriter writer,
+             uint8_t nestingLimit)
+      : _buffer(buffer),
+        _reader(reader),
+        _writer(writer),
+        _nestingLimit(nestingLimit) {}
+
+  JsonArray &parseArray();
+  JsonObject &parseObject();
+
+  JsonVariant parseVariant() {
+    JsonVariant result;
+    parseAnythingTo(&result);
+    return result;
+  }
+
+ private:
+  JsonParser &operator=(const JsonParser &);  // non-copiable
+
+  static bool eat(TReader &, char charToSkip);
+  FORCE_INLINE bool eat(char charToSkip) {
+    return eat(_reader, charToSkip);
+  }
+
+  const char *parseString();
+  bool parseAnythingTo(JsonVariant *destination);
+  FORCE_INLINE bool parseAnythingToUnsafe(JsonVariant *destination);
+
+  inline bool parseArrayTo(JsonVariant *destination);
+  inline bool parseObjectTo(JsonVariant *destination);
+  inline bool parseStringTo(JsonVariant *destination);
+
+  static inline bool isBetween(char c, char min, char max) {
+    return min <= c && c <= max;
+  }
+
+  static inline bool canBeInNonQuotedString(char c) {
+    return isBetween(c, '0', '9') || isBetween(c, '_', 'z') ||
+           isBetween(c, 'A', 'Z') || c == '+' || c == '-' || c == '.';
+  }
+
+  static inline bool isQuote(char c) {
+    return c == '\'' || c == '\"';
+  }
+
+  JsonBuffer *_buffer;
+  TReader _reader;
+  TWriter _writer;
+  uint8_t _nestingLimit;
+};
+
+template <typename TJsonBuffer, typename TString, typename Enable = void>
+struct JsonParserBuilder {
+  typedef typename StringTraits<TString>::Reader InputReader;
+  typedef JsonParser<InputReader, TJsonBuffer &> TParser;
+
+  static TParser makeParser(TJsonBuffer *buffer, TString &json,
+                            uint8_t nestingLimit) {
+    return TParser(buffer, InputReader(json), *buffer, nestingLimit);
+  }
+};
+
+template <typename TJsonBuffer, typename TChar>
+struct JsonParserBuilder<TJsonBuffer, TChar *,
+                         typename EnableIf<!IsConst<TChar>::value>::type> {
+  typedef typename StringTraits<TChar *>::Reader TReader;
+  typedef StringWriter<TChar> TWriter;
+  typedef JsonParser<TReader, TWriter> TParser;
+
+  static TParser makeParser(TJsonBuffer *buffer, TChar *json,
+                            uint8_t nestingLimit) {
+    return TParser(buffer, TReader(json), TWriter(json), nestingLimit);
+  }
+};
+
+template <typename TJsonBuffer, typename TString>
+inline typename JsonParserBuilder<TJsonBuffer, TString>::TParser makeParser(
+    TJsonBuffer *buffer, TString &json, uint8_t nestingLimit) {
+  return JsonParserBuilder<TJsonBuffer, TString>::makeParser(buffer, json,
+                                                             nestingLimit);
+}
+}  // namespace Internals
+}  // namespace ArduinoJson
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/JsonParserImpl.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/JsonParserImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..33ad42e9efb3c5c63792e4dec1e8f4520443dd14
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/JsonParserImpl.hpp
@@ -0,0 +1,192 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Comments.hpp"
+#include "JsonParser.hpp"
+
+template <typename TReader, typename TWriter>
+inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::eat(
+    TReader &reader, char charToSkip) {
+  skipSpacesAndComments(reader);
+  if (reader.current() != charToSkip) return false;
+  reader.move();
+  return true;
+}
+
+template <typename TReader, typename TWriter>
+inline bool
+ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingTo(
+    JsonVariant *destination) {
+  if (_nestingLimit == 0) return false;
+  _nestingLimit--;
+  bool success = parseAnythingToUnsafe(destination);
+  _nestingLimit++;
+  return success;
+}
+
+template <typename TReader, typename TWriter>
+inline bool
+ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseAnythingToUnsafe(
+    JsonVariant *destination) {
+  skipSpacesAndComments(_reader);
+
+  switch (_reader.current()) {
+    case '[':
+      return parseArrayTo(destination);
+
+    case '{':
+      return parseObjectTo(destination);
+
+    default:
+      return parseStringTo(destination);
+  }
+}
+
+template <typename TReader, typename TWriter>
+inline ArduinoJson::JsonArray &
+ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArray() {
+  // Create an empty array
+  JsonArray &array = _buffer->createArray();
+
+  // Check opening braket
+  if (!eat('[')) goto ERROR_MISSING_BRACKET;
+  if (eat(']')) goto SUCCESS_EMPTY_ARRAY;
+
+  // Read each value
+  for (;;) {
+    // 1 - Parse value
+    JsonVariant value;
+    if (!parseAnythingTo(&value)) goto ERROR_INVALID_VALUE;
+    if (!array.add(value)) goto ERROR_NO_MEMORY;
+
+    // 2 - More values?
+    if (eat(']')) goto SUCCES_NON_EMPTY_ARRAY;
+    if (!eat(',')) goto ERROR_MISSING_COMMA;
+  }
+
+SUCCESS_EMPTY_ARRAY:
+SUCCES_NON_EMPTY_ARRAY:
+  return array;
+
+ERROR_INVALID_VALUE:
+ERROR_MISSING_BRACKET:
+ERROR_MISSING_COMMA:
+ERROR_NO_MEMORY:
+  return JsonArray::invalid();
+}
+
+template <typename TReader, typename TWriter>
+inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseArrayTo(
+    JsonVariant *destination) {
+  JsonArray &array = parseArray();
+  if (!array.success()) return false;
+
+  *destination = array;
+  return true;
+}
+
+template <typename TReader, typename TWriter>
+inline ArduinoJson::JsonObject &
+ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObject() {
+  // Create an empty object
+  JsonObject &object = _buffer->createObject();
+
+  // Check opening brace
+  if (!eat('{')) goto ERROR_MISSING_BRACE;
+  if (eat('}')) goto SUCCESS_EMPTY_OBJECT;
+
+  // Read each key value pair
+  for (;;) {
+    // 1 - Parse key
+    const char *key = parseString();
+    if (!key) goto ERROR_INVALID_KEY;
+    if (!eat(':')) goto ERROR_MISSING_COLON;
+
+    // 2 - Parse value
+    JsonVariant value;
+    if (!parseAnythingTo(&value)) goto ERROR_INVALID_VALUE;
+    if (!object.set(key, value)) goto ERROR_NO_MEMORY;
+
+    // 3 - More keys/values?
+    if (eat('}')) goto SUCCESS_NON_EMPTY_OBJECT;
+    if (!eat(',')) goto ERROR_MISSING_COMMA;
+  }
+
+SUCCESS_EMPTY_OBJECT:
+SUCCESS_NON_EMPTY_OBJECT:
+  return object;
+
+ERROR_INVALID_KEY:
+ERROR_INVALID_VALUE:
+ERROR_MISSING_BRACE:
+ERROR_MISSING_COLON:
+ERROR_MISSING_COMMA:
+ERROR_NO_MEMORY:
+  return JsonObject::invalid();
+}
+
+template <typename TReader, typename TWriter>
+inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseObjectTo(
+    JsonVariant *destination) {
+  JsonObject &object = parseObject();
+  if (!object.success()) return false;
+
+  *destination = object;
+  return true;
+}
+
+template <typename TReader, typename TWriter>
+inline const char *
+ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseString() {
+  typename RemoveReference<TWriter>::type::String str = _writer.startString();
+
+  skipSpacesAndComments(_reader);
+  char c = _reader.current();
+
+  if (isQuote(c)) {  // quotes
+    _reader.move();
+    char stopChar = c;
+    for (;;) {
+      c = _reader.current();
+      if (c == '\0') break;
+      _reader.move();
+
+      if (c == stopChar) break;
+
+      if (c == '\\') {
+        // replace char
+        c = Encoding::unescapeChar(_reader.current());
+        if (c == '\0') break;
+        _reader.move();
+      }
+
+      str.append(c);
+    }
+  } else {  // no quotes
+    for (;;) {
+      if (!canBeInNonQuotedString(c)) break;
+      _reader.move();
+      str.append(c);
+      c = _reader.current();
+    }
+  }
+
+  return str.c_str();
+}
+
+template <typename TReader, typename TWriter>
+inline bool ArduinoJson::Internals::JsonParser<TReader, TWriter>::parseStringTo(
+    JsonVariant *destination) {
+  bool hasQuotes = isQuote(_reader.current());
+  const char *value = parseString();
+  if (value == NULL) return false;
+  if (hasQuotes) {
+    *destination = value;
+  } else {
+    *destination = RawJson(value);
+  }
+  return true;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/StringWriter.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/StringWriter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fd5507ea5f1edeea153cd77fa2037c9e61a7a978
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Deserialization/StringWriter.hpp
@@ -0,0 +1,41 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TChar>
+class StringWriter {
+ public:
+  class String {
+   public:
+    String(TChar** ptr) : _writePtr(ptr), _startPtr(*ptr) {}
+
+    void append(char c) {
+      *(*_writePtr)++ = TChar(c);
+    }
+
+    const char* c_str() const {
+      *(*_writePtr)++ = 0;
+      return reinterpret_cast<const char*>(_startPtr);
+    }
+
+   private:
+    TChar** _writePtr;
+    TChar* _startPtr;
+  };
+
+  StringWriter(TChar* buffer) : _ptr(buffer) {}
+
+  String startString() {
+    return String(&_ptr);
+  }
+
+ private:
+  TChar* _ptr;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/DynamicJsonBuffer.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/DynamicJsonBuffer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bdbd5dd90121b91627d2aca1eb99afbaad05c201
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/DynamicJsonBuffer.hpp
@@ -0,0 +1,170 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "JsonBufferBase.hpp"
+
+#include <stdlib.h>
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#elif defined(__GNUC__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic push
+#endif
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+namespace ArduinoJson {
+namespace Internals {
+class DefaultAllocator {
+ public:
+  void* allocate(size_t size) {
+    return malloc(size);
+  }
+  void deallocate(void* pointer) {
+    free(pointer);
+  }
+};
+
+template <typename TAllocator>
+class DynamicJsonBufferBase
+    : public JsonBufferBase<DynamicJsonBufferBase<TAllocator> > {
+  struct Block;
+  struct EmptyBlock {
+    Block* next;
+    size_t capacity;
+    size_t size;
+  };
+  struct Block : EmptyBlock {
+    uint8_t data[1];
+  };
+
+ public:
+  enum { EmptyBlockSize = sizeof(EmptyBlock) };
+
+  DynamicJsonBufferBase(size_t initialSize = 256)
+      : _head(NULL), _nextBlockCapacity(initialSize) {}
+
+  ~DynamicJsonBufferBase() {
+    clear();
+  }
+
+  // Gets the number of bytes occupied in the buffer
+  size_t size() const {
+    size_t total = 0;
+    for (const Block* b = _head; b; b = b->next) total += b->size;
+    return total;
+  }
+
+  // Allocates the specified amount of bytes in the buffer
+  virtual void* alloc(size_t bytes) {
+    alignNextAlloc();
+    return canAllocInHead(bytes) ? allocInHead(bytes) : allocInNewBlock(bytes);
+  }
+
+  // Resets the buffer.
+  // USE WITH CAUTION: this invalidates all previously allocated data
+  void clear() {
+    Block* currentBlock = _head;
+    while (currentBlock != NULL) {
+      _nextBlockCapacity = currentBlock->capacity;
+      Block* nextBlock = currentBlock->next;
+      _allocator.deallocate(currentBlock);
+      currentBlock = nextBlock;
+    }
+    _head = 0;
+  }
+
+  class String {
+   public:
+    String(DynamicJsonBufferBase* parent)
+        : _parent(parent), _start(NULL), _length(0) {}
+
+    void append(char c) {
+      if (_parent->canAllocInHead(1)) {
+        char* end = static_cast<char*>(_parent->allocInHead(1));
+        *end = c;
+        if (_length == 0) _start = end;
+      } else {
+        char* newStart =
+            static_cast<char*>(_parent->allocInNewBlock(_length + 1));
+        if (_start && newStart) memcpy(newStart, _start, _length);
+        if (newStart) newStart[_length] = c;
+        _start = newStart;
+      }
+      _length++;
+    }
+
+    const char* c_str() {
+      append(0);
+      return _start;
+    }
+
+   private:
+    DynamicJsonBufferBase* _parent;
+    char* _start;
+    size_t _length;
+  };
+
+  String startString() {
+    return String(this);
+  }
+
+ private:
+  void alignNextAlloc() {
+    if (_head) _head->size = this->round_size_up(_head->size);
+  }
+
+  bool canAllocInHead(size_t bytes) const {
+    return _head != NULL && _head->size + bytes <= _head->capacity;
+  }
+
+  void* allocInHead(size_t bytes) {
+    void* p = _head->data + _head->size;
+    _head->size += bytes;
+    return p;
+  }
+
+  void* allocInNewBlock(size_t bytes) {
+    size_t capacity = _nextBlockCapacity;
+    if (bytes > capacity) capacity = bytes;
+    if (!addNewBlock(capacity)) return NULL;
+    _nextBlockCapacity *= 2;
+    return allocInHead(bytes);
+  }
+
+  bool addNewBlock(size_t capacity) {
+    size_t bytes = EmptyBlockSize + capacity;
+    Block* block = static_cast<Block*>(_allocator.allocate(bytes));
+    if (block == NULL) return false;
+    block->capacity = capacity;
+    block->size = 0;
+    block->next = _head;
+    _head = block;
+    return true;
+  }
+
+  TAllocator _allocator;
+  Block* _head;
+  size_t _nextBlockCapacity;
+};
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(__GNUC__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+// Implements a JsonBuffer with dynamic memory allocation.
+// You are strongly encouraged to consider using StaticJsonBuffer which is much
+// more suitable for embedded systems.
+typedef Internals::DynamicJsonBufferBase<Internals::DefaultAllocator>
+    DynamicJsonBuffer;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonArray.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonArray.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2acd2a1a5034ced42eedabdf141da51ab6043821
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonArray.hpp
@@ -0,0 +1,227 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Data/JsonBufferAllocated.hpp"
+#include "Data/List.hpp"
+#include "Data/ReferenceType.hpp"
+#include "Data/ValueSaver.hpp"
+#include "JsonVariant.hpp"
+#include "Serialization/JsonPrintable.hpp"
+#include "StringTraits/StringTraits.hpp"
+#include "TypeTraits/EnableIf.hpp"
+#include "TypeTraits/IsArray.hpp"
+#include "TypeTraits/IsFloatingPoint.hpp"
+#include "TypeTraits/IsSame.hpp"
+
+// Returns the size (in bytes) of an array with n elements.
+// Can be very handy to determine the size of a StaticJsonBuffer.
+#define JSON_ARRAY_SIZE(NUMBER_OF_ELEMENTS) \
+  (sizeof(JsonArray) + (NUMBER_OF_ELEMENTS) * sizeof(JsonArray::node_type))
+
+namespace ArduinoJson {
+
+// Forward declarations
+class JsonObject;
+class JsonBuffer;
+namespace Internals {
+class JsonArraySubscript;
+}
+
+// An array of JsonVariant.
+//
+// The constructor is private, instances must be created via
+// JsonBuffer::createArray() or JsonBuffer::parseArray().
+// A JsonArray can be serialized to a JSON string via JsonArray::printTo().
+// It can also be deserialized from a JSON string via JsonBuffer::parseArray().
+class JsonArray : public Internals::JsonPrintable<JsonArray>,
+                  public Internals::ReferenceType,
+                  public Internals::NonCopyable,
+                  public Internals::List<JsonVariant>,
+                  public Internals::JsonBufferAllocated {
+ public:
+  // Create an empty JsonArray attached to the specified JsonBuffer.
+  // You should not call this constructor directly.
+  // Instead, use JsonBuffer::createArray() or JsonBuffer::parseArray().
+  explicit JsonArray(JsonBuffer *buffer) throw()
+      : Internals::List<JsonVariant>(buffer) {}
+
+  // Gets the value at the specified index
+  const Internals::JsonArraySubscript operator[](size_t index) const;
+
+  // Gets or sets the value at specified index
+  Internals::JsonArraySubscript operator[](size_t index);
+
+  // Adds the specified value at the end of the array.
+  //
+  // bool add(TValue);
+  // TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename T>
+  bool add(const T &value) {
+    return add_impl<const T &>(value);
+  }
+  //
+  // bool add(TValue);
+  // TValue = char*, const char*, const FlashStringHelper*
+  template <typename T>
+  bool add(T *value) {
+    return add_impl<T *>(value);
+  }
+  //
+  // bool add(TValue value, uint8_t decimals);
+  // TValue = float, double
+  template <typename T>
+  DEPRECATED("Second argument is not supported anymore")
+  bool add(T value, uint8_t) {
+    return add_impl<const JsonVariant &>(JsonVariant(value));
+  }
+
+  // Sets the value at specified index.
+  //
+  // bool add(size_t index, const TValue&);
+  // TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename T>
+  bool set(size_t index, const T &value) {
+    return set_impl<const T &>(index, value);
+  }
+  //
+  // bool add(size_t index, TValue);
+  // TValue = char*, const char*, const FlashStringHelper*
+  template <typename T>
+  bool set(size_t index, T *value) {
+    return set_impl<T *>(index, value);
+  }
+  //
+  // bool set(size_t index, TValue value, uint8_t decimals);
+  // TValue = float, double
+  template <typename T>
+  typename Internals::EnableIf<Internals::IsFloatingPoint<T>::value, bool>::type
+  set(size_t index, T value, uint8_t decimals) {
+    return set_impl<const JsonVariant &>(index, JsonVariant(value, decimals));
+  }
+
+  // Gets the value at the specified index.
+  template <typename T>
+  typename Internals::JsonVariantAs<T>::type get(size_t index) const {
+    const_iterator it = begin() += index;
+    return it != end() ? it->as<T>() : Internals::JsonVariantDefault<T>::get();
+  }
+
+  // Check the type of the value at specified index.
+  template <typename T>
+  bool is(size_t index) const {
+    const_iterator it = begin() += index;
+    return it != end() ? it->is<T>() : false;
+  }
+
+  // Creates a JsonArray and adds a reference at the end of the array.
+  // It's a shortcut for JsonBuffer::createArray() and JsonArray::add()
+  JsonArray &createNestedArray();
+
+  // Creates a JsonObject and adds a reference at the end of the array.
+  // It's a shortcut for JsonBuffer::createObject() and JsonArray::add()
+  JsonObject &createNestedObject();
+
+  // Removes element at specified index.
+  void remove(size_t index) {
+    remove(begin() += index);
+  }
+  using Internals::List<JsonVariant>::remove;
+
+  // Returns a reference an invalid JsonArray.
+  // This object is meant to replace a NULL pointer.
+  // This is used when memory allocation or JSON parsing fail.
+  static JsonArray &invalid() {
+    static JsonArray instance(NULL);
+    return instance;
+  }
+
+  // Imports a 1D array
+  template <typename T, size_t N>
+  bool copyFrom(T (&array)[N]) {
+    return copyFrom(array, N);
+  }
+
+  // Imports a 1D array
+  template <typename T>
+  bool copyFrom(T *array, size_t len) {
+    bool ok = true;
+    for (size_t i = 0; i < len; i++) {
+      ok &= add(array[i]);
+    }
+    return ok;
+  }
+
+  // Imports a 2D array
+  template <typename T, size_t N1, size_t N2>
+  bool copyFrom(T (&array)[N1][N2]) {
+    bool ok = true;
+    for (size_t i = 0; i < N1; i++) {
+      JsonArray &nestedArray = createNestedArray();
+      for (size_t j = 0; j < N2; j++) {
+        ok &= nestedArray.add(array[i][j]);
+      }
+    }
+    return ok;
+  }
+
+  // Exports a 1D array
+  template <typename T, size_t N>
+  size_t copyTo(T (&array)[N]) const {
+    return copyTo(array, N);
+  }
+
+  // Exports a 1D array
+  template <typename T>
+  size_t copyTo(T *array, size_t len) const {
+    size_t i = 0;
+    for (const_iterator it = begin(); it != end() && i < len; ++it)
+      array[i++] = *it;
+    return i;
+  }
+
+  // Exports a 2D array
+  template <typename T, size_t N1, size_t N2>
+  void copyTo(T (&array)[N1][N2]) const {
+    size_t i = 0;
+    for (const_iterator it = begin(); it != end() && i < N1; ++it) {
+      it->as<JsonArray>().copyTo(array[i++]);
+    }
+  }
+
+#if ARDUINOJSON_ENABLE_DEPRECATED
+  DEPRECATED("use remove() instead")
+  FORCE_INLINE void removeAt(size_t index) {
+    return remove(index);
+  }
+#endif
+
+ private:
+  template <typename TValueRef>
+  bool set_impl(size_t index, TValueRef value) {
+    iterator it = begin() += index;
+    if (it == end()) return false;
+    return Internals::ValueSaver<TValueRef>::save(_buffer, *it, value);
+  }
+
+  template <typename TValueRef>
+  bool add_impl(TValueRef value) {
+    iterator it = Internals::List<JsonVariant>::add();
+    if (it == end()) return false;
+    return Internals::ValueSaver<TValueRef>::save(_buffer, *it, value);
+  }
+};
+
+namespace Internals {
+template <>
+struct JsonVariantDefault<JsonArray> {
+  static JsonArray &get() {
+    return JsonArray::invalid();
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonArrayImpl.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonArrayImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..924b7ea7a3c9af3c8a5f366494eed3d3faf26dc8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonArrayImpl.hpp
@@ -0,0 +1,26 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "JsonArray.hpp"
+#include "JsonArraySubscript.hpp"
+#include "JsonObject.hpp"
+
+namespace ArduinoJson {
+
+inline JsonArray &JsonArray::createNestedArray() {
+  if (!_buffer) return JsonArray::invalid();
+  JsonArray &array = _buffer->createArray();
+  add(array);
+  return array;
+}
+
+inline JsonObject &JsonArray::createNestedObject() {
+  if (!_buffer) return JsonObject::invalid();
+  JsonObject &object = _buffer->createObject();
+  add(object);
+  return object;
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonArraySubscript.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonArraySubscript.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..afb4dc1ece587a7321ec1ed6410df441f9e068a5
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonArraySubscript.hpp
@@ -0,0 +1,122 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Configuration.hpp"
+#include "JsonVariantBase.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4522)
+#endif
+
+namespace ArduinoJson {
+namespace Internals {
+class JsonArraySubscript : public JsonVariantBase<JsonArraySubscript> {
+ public:
+  FORCE_INLINE JsonArraySubscript(JsonArray& array, size_t index)
+      : _array(array), _index(index) {}
+
+  FORCE_INLINE JsonArraySubscript& operator=(const JsonArraySubscript& src) {
+    _array.set(_index, src);
+    return *this;
+  }
+
+  // Replaces the value
+  //
+  // operator=(const TValue&)
+  // TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename T>
+  FORCE_INLINE JsonArraySubscript& operator=(const T& src) {
+    _array.set(_index, src);
+    return *this;
+  }
+  //
+  // operator=(TValue)
+  // TValue = char*, const char*, const FlashStringHelper*
+  template <typename T>
+  FORCE_INLINE JsonArraySubscript& operator=(T* src) {
+    _array.set(_index, src);
+    return *this;
+  }
+
+  FORCE_INLINE bool success() const {
+    return _index < _array.size();
+  }
+
+  template <typename T>
+  FORCE_INLINE typename JsonVariantAs<T>::type as() const {
+    return _array.get<T>(_index);
+  }
+
+  template <typename T>
+  FORCE_INLINE bool is() const {
+    return _array.is<T>(_index);
+  }
+
+  // Replaces the value
+  //
+  // bool set(const TValue&)
+  // TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename TValue>
+  FORCE_INLINE bool set(const TValue& value) {
+    return _array.set(_index, value);
+  }
+  //
+  // bool set(TValue)
+  // TValue = char*, const char*, const FlashStringHelper*
+  template <typename TValue>
+  FORCE_INLINE bool set(TValue* value) {
+    return _array.set(_index, value);
+  }
+  //
+  // bool set(TValue, uint8_t decimals);
+  // TValue = float, double
+  template <typename TValue>
+  DEPRECATED("Second argument is not supported anymore")
+  FORCE_INLINE bool set(const TValue& value, uint8_t) {
+    return _array.set(_index, value);
+  }
+
+ private:
+  JsonArray& _array;
+  const size_t _index;
+};
+
+template <typename TImpl>
+inline JsonArraySubscript JsonVariantSubscripts<TImpl>::operator[](
+    size_t index) {
+  return impl()->template as<JsonArray>()[index];
+}
+
+template <typename TImpl>
+inline const JsonArraySubscript JsonVariantSubscripts<TImpl>::operator[](
+    size_t index) const {
+  return impl()->template as<JsonArray>()[index];
+}
+
+#if ARDUINOJSON_ENABLE_STD_STREAM
+inline std::ostream& operator<<(std::ostream& os,
+                                const JsonArraySubscript& source) {
+  return source.printTo(os);
+}
+#endif
+}
+
+inline Internals::JsonArraySubscript JsonArray::operator[](size_t index) {
+  return Internals::JsonArraySubscript(*this, index);
+}
+
+inline const Internals::JsonArraySubscript JsonArray::operator[](
+    size_t index) const {
+  return Internals::JsonArraySubscript(*const_cast<JsonArray*>(this), index);
+}
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonBuffer.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonBuffer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..26101e086809a35e99cb790935ac9b650ff67f4d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonBuffer.hpp
@@ -0,0 +1,78 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include <stddef.h>  // for size_t
+#include <stdint.h>  // for uint8_t
+#include <string.h>
+
+#include "Data/NonCopyable.hpp"
+#include "JsonVariant.hpp"
+#include "TypeTraits/EnableIf.hpp"
+#include "TypeTraits/IsArray.hpp"
+
+namespace ArduinoJson {
+class JsonArray;
+class JsonObject;
+
+// Entry point for using the library.
+//
+// Handle the memory management (done in derived classes) and calls the parser.
+// This abstract class is implemented by StaticJsonBuffer which implements a
+// fixed memory allocation.
+class JsonBuffer : Internals::NonCopyable {
+ public:
+  // Allocates an empty JsonArray.
+  //
+  // Returns a reference to the new JsonArray or JsonArray::invalid() if the
+  // allocation fails.
+  JsonArray &createArray();
+
+  // Allocates an empty JsonObject.
+  //
+  // Returns a reference to the new JsonObject or JsonObject::invalid() if the
+  // allocation fails.
+  JsonObject &createObject();
+
+  // Duplicates a string
+  //
+  // const char* strdup(TValue);
+  // TValue = const std::string&, const String&,
+  template <typename TString>
+  DEPRECATED("char* are duplicated, you don't need strdup() anymore")
+  typename Internals::EnableIf<!Internals::IsArray<TString>::value,
+                               const char *>::type strdup(const TString &src) {
+    return Internals::StringTraits<TString>::duplicate(src, this);
+  }
+  //
+  // const char* strdup(TValue);
+  // TValue = char*, const char*, const FlashStringHelper*
+  template <typename TString>
+  DEPRECATED("char* are duplicated, you don't need strdup() anymore")
+  const char *strdup(TString *src) {
+    return Internals::StringTraits<TString *>::duplicate(src, this);
+  }
+
+  // Allocates n bytes in the JsonBuffer.
+  // Return a pointer to the allocated memory or NULL if allocation fails.
+  virtual void *alloc(size_t size) = 0;
+
+ protected:
+  // CAUTION: NO VIRTUAL DESTRUCTOR!
+  // If we add a virtual constructor the Arduino compiler will add malloc()
+  // and free() to the binary, adding 706 useless bytes.
+  ~JsonBuffer() {}
+
+  // Preserve aligment if necessary
+  static FORCE_INLINE size_t round_size_up(size_t bytes) {
+#if ARDUINOJSON_ENABLE_ALIGNMENT
+    const size_t x = sizeof(void *) - 1;
+    return (bytes + x) & ~x;
+#else
+    return bytes;
+#endif
+  }
+};
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonBufferBase.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonBufferBase.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e771bfdbc290ef35f76ac6db124ae2c1930603a
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonBufferBase.hpp
@@ -0,0 +1,127 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Deserialization/JsonParser.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+template <typename TDerived>
+class JsonBufferBase : public JsonBuffer {
+ public:
+  // Allocates and populate a JsonArray from a JSON string.
+  //
+  // The First argument is a pointer to the JSON string, the memory must be
+  // writable
+  // because the parser will insert null-terminators and replace escaped chars.
+  //
+  // The second argument set the nesting limit
+  //
+  // Returns a reference to the new JsonObject or JsonObject::invalid() if the
+  // allocation fails.
+  // With this overload, the JsonBuffer will make a copy of the string
+  //
+  // JsonArray& parseArray(TString);
+  // TString = const std::string&, const String&
+  template <typename TString>
+  typename Internals::EnableIf<!Internals::IsArray<TString>::value,
+                               JsonArray &>::type
+  parseArray(const TString &json,
+             uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
+    return Internals::makeParser(that(), json, nestingLimit).parseArray();
+  }
+  //
+  // JsonArray& parseArray(TString);
+  // TString = const char*, const char[N], const FlashStringHelper*
+  template <typename TString>
+  JsonArray &parseArray(
+      TString *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
+    return Internals::makeParser(that(), json, nestingLimit).parseArray();
+  }
+  //
+  // JsonArray& parseArray(TString);
+  // TString = std::istream&, Stream&
+  template <typename TString>
+  JsonArray &parseArray(
+      TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
+    return Internals::makeParser(that(), json, nestingLimit).parseArray();
+  }
+
+  // Allocates and populate a JsonObject from a JSON string.
+  //
+  // The First argument is a pointer to the JSON string, the memory must be
+  // writable
+  // because the parser will insert null-terminators and replace escaped chars.
+  //
+  // The second argument set the nesting limit
+  //
+  // Returns a reference to the new JsonObject or JsonObject::invalid() if the
+  // allocation fails.
+  //
+  // JsonObject& parseObject(TString);
+  // TString = const std::string&, const String&
+  template <typename TString>
+  typename Internals::EnableIf<!Internals::IsArray<TString>::value,
+                               JsonObject &>::type
+  parseObject(const TString &json,
+              uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
+    return Internals::makeParser(that(), json, nestingLimit).parseObject();
+  }
+  //
+  // JsonObject& parseObject(TString);
+  // TString = const char*, const char[N], const FlashStringHelper*
+  template <typename TString>
+  JsonObject &parseObject(
+      TString *json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
+    return Internals::makeParser(that(), json, nestingLimit).parseObject();
+  }
+  //
+  // JsonObject& parseObject(TString);
+  // TString = std::istream&, Stream&
+  template <typename TString>
+  JsonObject &parseObject(
+      TString &json, uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
+    return Internals::makeParser(that(), json, nestingLimit).parseObject();
+  }
+
+  // Generalized version of parseArray() and parseObject(), also works for
+  // integral types.
+  //
+  // JsonVariant parse(TString);
+  // TString = const std::string&, const String&
+  template <typename TString>
+  typename Internals::EnableIf<!Internals::IsArray<TString>::value,
+                               JsonVariant>::type
+  parse(const TString &json,
+        uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
+    return Internals::makeParser(that(), json, nestingLimit).parseVariant();
+  }
+  //
+  // JsonVariant parse(TString);
+  // TString = const char*, const char[N], const FlashStringHelper*
+  template <typename TString>
+  JsonVariant parse(TString *json,
+                    uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
+    return Internals::makeParser(that(), json, nestingLimit).parseVariant();
+  }
+  //
+  // JsonVariant parse(TString);
+  // TString = std::istream&, Stream&
+  template <typename TString>
+  JsonVariant parse(TString &json,
+                    uint8_t nestingLimit = ARDUINOJSON_DEFAULT_NESTING_LIMIT) {
+    return Internals::makeParser(that(), json, nestingLimit).parseVariant();
+  }
+
+ protected:
+  ~JsonBufferBase() {}
+
+ private:
+  TDerived *that() {
+    return static_cast<TDerived *>(this);
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonBufferImpl.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonBufferImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cdea374bbe81ceac640e94ded45fd3a3288f7cbc
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonBufferImpl.hpp
@@ -0,0 +1,17 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Deserialization/JsonParser.hpp"
+
+inline ArduinoJson::JsonArray &ArduinoJson::JsonBuffer::createArray() {
+  JsonArray *ptr = new (this) JsonArray(this);
+  return ptr ? *ptr : JsonArray::invalid();
+}
+
+inline ArduinoJson::JsonObject &ArduinoJson::JsonBuffer::createObject() {
+  JsonObject *ptr = new (this) JsonObject(this);
+  return ptr ? *ptr : JsonObject::invalid();
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonObject.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonObject.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c03bfa51d5a036dae498c0a1b682853c86b6784a
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonObject.hpp
@@ -0,0 +1,322 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Data/JsonBufferAllocated.hpp"
+#include "Data/List.hpp"
+#include "Data/ReferenceType.hpp"
+#include "Data/ValueSaver.hpp"
+#include "JsonPair.hpp"
+#include "Serialization/JsonPrintable.hpp"
+#include "StringTraits/StringTraits.hpp"
+#include "TypeTraits/EnableIf.hpp"
+#include "TypeTraits/IsArray.hpp"
+#include "TypeTraits/IsFloatingPoint.hpp"
+#include "TypeTraits/IsSame.hpp"
+
+// Returns the size (in bytes) of an object with n elements.
+// Can be very handy to determine the size of a StaticJsonBuffer.
+#define JSON_OBJECT_SIZE(NUMBER_OF_ELEMENTS) \
+  (sizeof(JsonObject) + (NUMBER_OF_ELEMENTS) * sizeof(JsonObject::node_type))
+
+namespace ArduinoJson {
+
+// Forward declarations
+class JsonArray;
+class JsonBuffer;
+namespace Internals {
+template <typename>
+class JsonObjectSubscript;
+}
+
+// A dictionary of JsonVariant indexed by string (char*)
+//
+// The constructor is private, instances must be created via
+// JsonBuffer::createObject() or JsonBuffer::parseObject().
+// A JsonObject can be serialized to a JSON string via JsonObject::printTo().
+// It can also be deserialized from a JSON string via JsonBuffer::parseObject().
+class JsonObject : public Internals::JsonPrintable<JsonObject>,
+                   public Internals::ReferenceType,
+                   public Internals::NonCopyable,
+                   public Internals::List<JsonPair>,
+                   public Internals::JsonBufferAllocated {
+ public:
+  // Create an empty JsonArray attached to the specified JsonBuffer.
+  // You should not use this constructor directly.
+  // Instead, use JsonBuffer::createObject() or JsonBuffer.parseObject().
+  explicit JsonObject(JsonBuffer* buffer) throw()
+      : Internals::List<JsonPair>(buffer) {}
+
+  // Gets or sets the value associated with the specified key.
+  //
+  // JsonObjectSubscript operator[](TKey)
+  // TKey = const std::string&, const String&
+  template <typename TString>
+  Internals::JsonObjectSubscript<const TString&> operator[](
+      const TString& key) {
+    return Internals::JsonObjectSubscript<const TString&>(*this, key);
+  }
+  //
+  // JsonObjectSubscript operator[](TKey)
+  // TKey = char*, const char*, char[], const char[N], const FlashStringHelper*
+  template <typename TString>
+  Internals::JsonObjectSubscript<TString*> operator[](TString* key) {
+    return Internals::JsonObjectSubscript<TString*>(*this, key);
+  }
+
+  // Gets the value associated with the specified key.
+  //
+  // const JsonObjectSubscript operator[](TKey) const;
+  // TKey = const std::string&, const String&
+  template <typename TString>
+  const Internals::JsonObjectSubscript<const TString&> operator[](
+      const TString& key) const {
+    return Internals::JsonObjectSubscript<const TString&>(
+        *const_cast<JsonObject*>(this), key);
+  }
+  //
+  // const JsonObjectSubscript operator[](TKey) const;
+  // TKey = const char*, const char[N], const FlashStringHelper*
+  template <typename TString>
+  const Internals::JsonObjectSubscript<TString*> operator[](
+      TString* key) const {
+    return Internals::JsonObjectSubscript<TString*>(
+        *const_cast<JsonObject*>(this), key);
+  }
+
+  // Sets the specified key with the specified value.
+  //
+  // bool set(TKey, TValue);
+  // TKey = const std::string&, const String&
+  // TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename TValue, typename TString>
+  bool set(const TString& key, const TValue& value) {
+    return set_impl<const TString&, const TValue&>(key, value);
+  }
+  //
+  // bool set(TKey, TValue);
+  // TKey = const std::string&, const String&
+  // TValue = char*, const char*, const FlashStringHelper*
+  template <typename TValue, typename TString>
+  bool set(const TString& key, TValue* value) {
+    return set_impl<const TString&, TValue*>(key, value);
+  }
+  //
+  // bool set(TKey, const TValue&);
+  // TKey = char*, const char*, const FlashStringHelper*
+  // TValue = bool, long, int, short, float, double, RawJson, JsonVariant,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename TValue, typename TString>
+  bool set(TString* key, const TValue& value) {
+    return set_impl<TString*, const TValue&>(key, value);
+  }
+  //
+  // bool set(TKey, TValue);
+  // TKey = char*, const char*, const FlashStringHelper*
+  // TValue = char*, const char*, const FlashStringHelper*
+  template <typename TValue, typename TString>
+  bool set(TString* key, TValue* value) {
+    return set_impl<TString*, TValue*>(key, value);
+  }
+  //
+  // bool set(TKey, TValue, uint8_t decimals);
+  // TKey = const std::string&, const String&
+  // TValue = float, double
+  template <typename TValue, typename TString>
+  DEPRECATED("Second argument is not supported anymore")
+  typename Internals::EnableIf<Internals::IsFloatingPoint<TValue>::value,
+                               bool>::type
+      set(const TString& key, TValue value, uint8_t) {
+    return set_impl<const TString&, const JsonVariant&>(key,
+                                                        JsonVariant(value));
+  }
+  //
+  // bool set(TKey, TValue, uint8_t decimals);
+  // TKey = char*, const char*, const FlashStringHelper*
+  // TValue = float, double
+  template <typename TValue, typename TString>
+  DEPRECATED("Second argument is not supported anymore")
+  typename Internals::EnableIf<Internals::IsFloatingPoint<TValue>::value,
+                               bool>::type
+      set(TString* key, TValue value, uint8_t) {
+    return set_impl<TString*, const JsonVariant&>(key, JsonVariant(value));
+  }
+
+  // Gets the value associated with the specified key.
+  //
+  // TValue get<TValue>(TKey) const;
+  // TKey = const std::string&, const String&
+  // TValue = bool, char, long, int, short, float, double,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename TValue, typename TString>
+  typename Internals::JsonVariantAs<TValue>::type get(
+      const TString& key) const {
+    return get_impl<const TString&, TValue>(key);
+  }
+  //
+  // TValue get<TValue>(TKey) const;
+  // TKey = char*, const char*, const FlashStringHelper*
+  // TValue = bool, char, long, int, short, float, double,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename TValue, typename TString>
+  typename Internals::JsonVariantAs<TValue>::type get(TString* key) const {
+    return get_impl<TString*, TValue>(key);
+  }
+
+  // Checks the type of the value associated with the specified key.
+  //
+  //
+  // bool is<TValue>(TKey) const;
+  // TKey = const std::string&, const String&
+  // TValue = bool, char, long, int, short, float, double,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename TValue, typename TString>
+  bool is(const TString& key) const {
+    return is_impl<const TString&, TValue>(key);
+  }
+  //
+  // bool is<TValue>(TKey) const;
+  // TKey = char*, const char*, const FlashStringHelper*
+  // TValue = bool, char, long, int, short, float, double,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename TValue, typename TString>
+  bool is(TString* key) const {
+    return is_impl<TString*, TValue>(key);
+  }
+
+  // Creates and adds a JsonArray.
+  //
+  // JsonArray& createNestedArray(TKey);
+  // TKey = const std::string&, const String&
+  template <typename TString>
+  JsonArray& createNestedArray(const TString& key) {
+    return createNestedArray_impl<const TString&>(key);
+  }
+  // JsonArray& createNestedArray(TKey);
+  // TKey = char*, const char*, char[], const char[], const FlashStringHelper*
+  template <typename TString>
+  JsonArray& createNestedArray(TString* key) {
+    return createNestedArray_impl<TString*>(key);
+  }
+
+  // Creates and adds a JsonObject.
+  //
+  // JsonObject& createNestedObject(TKey);
+  // TKey = const std::string&, const String&
+  template <typename TString>
+  JsonObject& createNestedObject(const TString& key) {
+    return createNestedObject_impl<const TString&>(key);
+  }
+  //
+  // JsonObject& createNestedObject(TKey);
+  // TKey = char*, const char*, char[], const char[], const FlashStringHelper*
+  template <typename TString>
+  JsonObject& createNestedObject(TString* key) {
+    return createNestedObject_impl<TString*>(key);
+  }
+
+  // Tells weither the specified key is present and associated with a value.
+  //
+  // bool containsKey(TKey);
+  // TKey = const std::string&, const String&
+  template <typename TString>
+  bool containsKey(const TString& key) const {
+    return findKey<const TString&>(key) != end();
+  }
+  //
+  // bool containsKey(TKey);
+  // TKey = char*, const char*, char[], const char[], const FlashStringHelper*
+  template <typename TString>
+  bool containsKey(TString* key) const {
+    return findKey<TString*>(key) != end();
+  }
+
+  // Removes the specified key and the associated value.
+  //
+  // void remove(TKey);
+  // TKey = const std::string&, const String&
+  template <typename TString>
+  void remove(const TString& key) {
+    remove(findKey<const TString&>(key));
+  }
+  //
+  // void remove(TKey);
+  // TKey = char*, const char*, char[], const char[], const FlashStringHelper*
+  template <typename TString>
+  void remove(TString* key) {
+    remove(findKey<TString*>(key));
+  }
+  //
+  // void remove(iterator)
+  using Internals::List<JsonPair>::remove;
+
+  // Returns a reference an invalid JsonObject.
+  // This object is meant to replace a NULL pointer.
+  // This is used when memory allocation or JSON parsing fail.
+  static JsonObject& invalid() {
+    static JsonObject instance(NULL);
+    return instance;
+  }
+
+ private:
+  // Returns the list node that matches the specified key.
+  template <typename TStringRef>
+  iterator findKey(TStringRef key) {
+    iterator it;
+    for (it = begin(); it != end(); ++it) {
+      if (Internals::StringTraits<TStringRef>::equals(key, it->key)) break;
+    }
+    return it;
+  }
+  template <typename TStringRef>
+  const_iterator findKey(TStringRef key) const {
+    return const_cast<JsonObject*>(this)->findKey<TStringRef>(key);
+  }
+
+  template <typename TStringRef, typename TValue>
+  typename Internals::JsonVariantAs<TValue>::type get_impl(
+      TStringRef key) const {
+    const_iterator it = findKey<TStringRef>(key);
+    return it != end() ? it->value.as<TValue>()
+                       : Internals::JsonVariantDefault<TValue>::get();
+  }
+
+  template <typename TStringRef, typename TValueRef>
+  bool set_impl(TStringRef key, TValueRef value) {
+    iterator it = findKey<TStringRef>(key);
+    if (it == end()) {
+      it = Internals::List<JsonPair>::add();
+      if (it == end()) return false;
+
+      bool key_ok =
+          Internals::ValueSaver<TStringRef>::save(_buffer, it->key, key);
+      if (!key_ok) return false;
+    }
+    return Internals::ValueSaver<TValueRef>::save(_buffer, it->value, value);
+  }
+
+  template <typename TStringRef, typename TValue>
+  bool is_impl(TStringRef key) const {
+    const_iterator it = findKey<TStringRef>(key);
+    return it != end() ? it->value.is<TValue>() : false;
+  }
+
+  template <typename TStringRef>
+  JsonArray& createNestedArray_impl(TStringRef key);
+
+  template <typename TStringRef>
+  JsonObject& createNestedObject_impl(TStringRef key);
+};
+
+namespace Internals {
+template <>
+struct JsonVariantDefault<JsonObject> {
+  static JsonObject& get() {
+    return JsonObject::invalid();
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonObjectImpl.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonObjectImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e7689b507de832c40c41f0be937ad8d273e764d2
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonObjectImpl.hpp
@@ -0,0 +1,28 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "JsonArray.hpp"
+#include "JsonObject.hpp"
+#include "JsonObjectSubscript.hpp"
+
+namespace ArduinoJson {
+
+template <typename TStringRef>
+inline JsonArray &JsonObject::createNestedArray_impl(TStringRef key) {
+  if (!_buffer) return JsonArray::invalid();
+  JsonArray &array = _buffer->createArray();
+  set(key, array);
+  return array;
+}
+
+template <typename TStringRef>
+inline JsonObject &JsonObject::createNestedObject_impl(TStringRef key) {
+  if (!_buffer) return JsonObject::invalid();
+  JsonObject &object = _buffer->createObject();
+  set(key, object);
+  return object;
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonObjectSubscript.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonObjectSubscript.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ac476370c92aa1672d7b869bfd4b2802db24693
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonObjectSubscript.hpp
@@ -0,0 +1,110 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Configuration.hpp"
+#include "JsonVariantBase.hpp"
+#include "TypeTraits/EnableIf.hpp"
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4522)
+#endif
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TStringRef>
+class JsonObjectSubscript
+    : public JsonVariantBase<JsonObjectSubscript<TStringRef> > {
+  typedef JsonObjectSubscript<TStringRef> this_type;
+
+ public:
+  FORCE_INLINE JsonObjectSubscript(JsonObject& object, TStringRef key)
+      : _object(object), _key(key) {}
+
+  FORCE_INLINE this_type& operator=(const this_type& src) {
+    _object.set(_key, src);
+    return *this;
+  }
+
+  // Set the specified value
+  //
+  // operator=(const TValue&);
+  // TValue = bool, char, long, int, short, float, double,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename TValue>
+  FORCE_INLINE typename EnableIf<!IsArray<TValue>::value, this_type&>::type
+  operator=(const TValue& src) {
+    _object.set(_key, src);
+    return *this;
+  }
+  //
+  // operator=(TValue);
+  // TValue = char*, const char*, const FlashStringHelper*
+  template <typename TValue>
+  FORCE_INLINE this_type& operator=(TValue* src) {
+    _object.set(_key, src);
+    return *this;
+  }
+
+  FORCE_INLINE bool success() const {
+    return _object.containsKey(_key);
+  }
+
+  template <typename TValue>
+  FORCE_INLINE typename JsonVariantAs<TValue>::type as() const {
+    return _object.get<TValue>(_key);
+  }
+
+  template <typename TValue>
+  FORCE_INLINE bool is() const {
+    return _object.is<TValue>(_key);
+  }
+
+  // Sets the specified value.
+  //
+  // bool set(const TValue&);
+  // TValue = bool, char, long, int, short, float, double, RawJson, JsonVariant,
+  //          std::string, String, JsonArray, JsonObject
+  template <typename TValue>
+  FORCE_INLINE typename EnableIf<!IsArray<TValue>::value, bool>::type set(
+      const TValue& value) {
+    return _object.set(_key, value);
+  }
+  //
+  // bool set(TValue);
+  // TValue = char*, const char, const FlashStringHelper*
+  template <typename TValue>
+  FORCE_INLINE bool set(const TValue* value) {
+    return _object.set(_key, value);
+  }
+  //
+  // bool set(TValue, uint8_t decimals);
+  // TValue = float, double
+  template <typename TValue>
+  DEPRECATED("Second argument is not supported anymore")
+  FORCE_INLINE bool set(const TValue& value, uint8_t) {
+    return _object.set(_key, value);
+  }
+
+ private:
+  JsonObject& _object;
+  TStringRef _key;
+};
+
+#if ARDUINOJSON_ENABLE_STD_STREAM
+template <typename TStringRef>
+inline std::ostream& operator<<(std::ostream& os,
+                                const JsonObjectSubscript<TStringRef>& source) {
+  return source.printTo(os);
+}
+#endif
+}
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonPair.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonPair.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4172430455615a6a60c12e06f24faf0782258e24
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonPair.hpp
@@ -0,0 +1,16 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "JsonVariant.hpp"
+
+namespace ArduinoJson {
+
+// A key value pair for JsonObject.
+struct JsonPair {
+  const char* key;
+  JsonVariant value;
+};
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariant.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariant.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8326cbe86d3f3019695ea8c9de88cef119173cc8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariant.hpp
@@ -0,0 +1,355 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include <stddef.h>
+#include <stdint.h>  // for uint8_t
+
+#include "Data/JsonVariantContent.hpp"
+#include "Data/JsonVariantDefault.hpp"
+#include "Data/JsonVariantType.hpp"
+#include "JsonVariantBase.hpp"
+#include "RawJson.hpp"
+#include "Serialization/JsonPrintable.hpp"
+#include "TypeTraits/EnableIf.hpp"
+#include "TypeTraits/IsChar.hpp"
+#include "TypeTraits/IsFloatingPoint.hpp"
+#include "TypeTraits/IsIntegral.hpp"
+#include "TypeTraits/IsSame.hpp"
+#include "TypeTraits/IsSignedIntegral.hpp"
+#include "TypeTraits/IsUnsignedIntegral.hpp"
+#include "TypeTraits/RemoveConst.hpp"
+#include "TypeTraits/RemoveReference.hpp"
+
+namespace ArduinoJson {
+
+// Forward declarations.
+class JsonArray;
+class JsonObject;
+
+// A variant that can be a any value serializable to a JSON value.
+//
+// It can be set to:
+// - a boolean
+// - a char, short, int or a long (signed or unsigned)
+// - a string (const char*)
+// - a reference to a JsonArray or JsonObject
+class JsonVariant : public Internals::JsonVariantBase<JsonVariant> {
+  template <typename Print>
+  friend class Internals::JsonSerializer;
+
+ public:
+  // Creates an uninitialized JsonVariant
+  JsonVariant() : _type(Internals::JSON_UNDEFINED) {}
+
+  // Create a JsonVariant containing a boolean value.
+  // It will be serialized as "true" or "false" in JSON.
+  JsonVariant(bool value) {
+    using namespace Internals;
+    _type = JSON_BOOLEAN;
+    _content.asInteger = static_cast<JsonUInt>(value);
+  }
+
+  // Create a JsonVariant containing a floating point value.
+  // JsonVariant(double value);
+  // JsonVariant(float value);
+  template <typename T>
+  JsonVariant(T value, typename Internals::EnableIf<
+                           Internals::IsFloatingPoint<T>::value>::type * = 0) {
+    using namespace Internals;
+    _type = JSON_FLOAT;
+    _content.asFloat = static_cast<JsonFloat>(value);
+  }
+  template <typename T>
+  DEPRECATED("Second argument is not supported anymore")
+  JsonVariant(T value, uint8_t,
+              typename Internals::EnableIf<
+                  Internals::IsFloatingPoint<T>::value>::type * = 0) {
+    using namespace Internals;
+    _type = JSON_FLOAT;
+    _content.asFloat = static_cast<JsonFloat>(value);
+  }
+
+  // Create a JsonVariant containing an integer value.
+  // JsonVariant(char)
+  // JsonVariant(signed short)
+  // JsonVariant(signed int)
+  // JsonVariant(signed long)
+  // JsonVariant(signed char)
+  template <typename T>
+  JsonVariant(
+      T value,
+      typename Internals::EnableIf<Internals::IsSignedIntegral<T>::value ||
+                                   Internals::IsSame<T, char>::value>::type * =
+          0) {
+    using namespace Internals;
+    if (value >= 0) {
+      _type = JSON_POSITIVE_INTEGER;
+      _content.asInteger = static_cast<JsonUInt>(value);
+    } else {
+      _type = JSON_NEGATIVE_INTEGER;
+      _content.asInteger = static_cast<JsonUInt>(-value);
+    }
+  }
+  // JsonVariant(unsigned short)
+  // JsonVariant(unsigned int)
+  // JsonVariant(unsigned long)
+  template <typename T>
+  JsonVariant(T value,
+              typename Internals::EnableIf<
+                  Internals::IsUnsignedIntegral<T>::value>::type * = 0) {
+    using namespace Internals;
+    _type = JSON_POSITIVE_INTEGER;
+    _content.asInteger = static_cast<JsonUInt>(value);
+  }
+
+  // Create a JsonVariant containing a string.
+  // JsonVariant(const char*);
+  // JsonVariant(const signed char*);
+  // JsonVariant(const unsigned char*);
+  template <typename TChar>
+  JsonVariant(
+      const TChar *value,
+      typename Internals::EnableIf<Internals::IsChar<TChar>::value>::type * =
+          0) {
+    _type = Internals::JSON_STRING;
+    _content.asString = reinterpret_cast<const char *>(value);
+  }
+
+  // Create a JsonVariant containing an unparsed string
+  JsonVariant(Internals::RawJsonString<const char *> value) {
+    _type = Internals::JSON_UNPARSED;
+    _content.asString = value;
+  }
+
+  // Create a JsonVariant containing a reference to an array.
+  // CAUTION: we are lying about constness, because the array can be modified if
+  // the variant is converted back to a JsonArray&
+  JsonVariant(const JsonArray &array);
+
+  // Create a JsonVariant containing a reference to an object.
+  // CAUTION: we are lying about constness, because the object can be modified
+  // if the variant is converted back to a JsonObject&
+  JsonVariant(const JsonObject &object);
+
+  // Get the variant as the specified type.
+  //
+  // char as<char>() const;
+  // signed char as<signed char>() const;
+  // signed short as<signed short>() const;
+  // signed int as<signed int>() const;
+  // signed long as<signed long>() const;
+  // unsigned char as<unsigned char>() const;
+  // unsigned short as<unsigned short>() const;
+  // unsigned int as<unsigned int>() const;
+  // unsigned long as<unsigned long>() const;
+  template <typename T>
+  const typename Internals::EnableIf<Internals::IsIntegral<T>::value, T>::type
+  as() const {
+    return variantAsInteger<T>();
+  }
+  // bool as<bool>() const
+  template <typename T>
+  const typename Internals::EnableIf<Internals::IsSame<T, bool>::value, T>::type
+  as() const {
+    return variantAsInteger<int>() != 0;
+  }
+  //
+  // double as<double>() const;
+  // float as<float>() const;
+  template <typename T>
+  const typename Internals::EnableIf<Internals::IsFloatingPoint<T>::value,
+                                     T>::type
+  as() const {
+    return variantAsFloat<T>();
+  }
+  //
+  // const char* as<const char*>() const;
+  // const char* as<char*>() const;
+  template <typename T>
+  typename Internals::EnableIf<Internals::IsSame<T, const char *>::value ||
+                                   Internals::IsSame<T, char *>::value,
+                               const char *>::type
+  as() const {
+    return variantAsString();
+  }
+  //
+  // std::string as<std::string>() const;
+  // String as<String>() const;
+  template <typename T>
+  typename Internals::EnableIf<Internals::StringTraits<T>::has_append, T>::type
+  as() const {
+    const char *cstr = variantAsString();
+    if (cstr) return T(cstr);
+    T s;
+    printTo(s);
+    return s;
+  }
+  //
+  // JsonArray& as<JsonArray> const;
+  // JsonArray& as<JsonArray&> const;
+  template <typename T>
+  typename Internals::EnableIf<
+      Internals::IsSame<typename Internals::RemoveReference<T>::type,
+                        JsonArray>::value,
+      JsonArray &>::type
+  as() const {
+    return variantAsArray();
+  }
+  //
+  // const JsonArray& as<const JsonArray&> const;
+  template <typename T>
+  typename Internals::EnableIf<
+      Internals::IsSame<typename Internals::RemoveReference<T>::type,
+                        const JsonArray>::value,
+      const JsonArray &>::type
+  as() const {
+    return variantAsArray();
+  }
+  //
+  // JsonObject& as<JsonObject> const;
+  // JsonObject& as<JsonObject&> const;
+  template <typename T>
+  typename Internals::EnableIf<
+      Internals::IsSame<typename Internals::RemoveReference<T>::type,
+                        JsonObject>::value,
+      JsonObject &>::type
+  as() const {
+    return variantAsObject();
+  }
+  //
+  // JsonObject& as<const JsonObject> const;
+  // JsonObject& as<const JsonObject&> const;
+  template <typename T>
+  typename Internals::EnableIf<
+      Internals::IsSame<typename Internals::RemoveReference<T>::type,
+                        const JsonObject>::value,
+      const JsonObject &>::type
+  as() const {
+    return variantAsObject();
+  }
+  //
+  // JsonVariant as<JsonVariant> const;
+  template <typename T>
+  typename Internals::EnableIf<Internals::IsSame<T, JsonVariant>::value,
+                               T>::type
+  as() const {
+    return *this;
+  }
+
+  // Tells weither the variant has the specified type.
+  // Returns true if the variant has type type T, false otherwise.
+  //
+  // bool is<char>() const;
+  // bool is<signed char>() const;
+  // bool is<signed short>() const;
+  // bool is<signed int>() const;
+  // bool is<signed long>() const;
+  // bool is<unsigned char>() const;
+  // bool is<unsigned short>() const;
+  // bool is<unsigned int>() const;
+  // bool is<unsigned long>() const;
+  template <typename T>
+  typename Internals::EnableIf<Internals::IsIntegral<T>::value, bool>::type is()
+      const {
+    return variantIsInteger();
+  }
+  //
+  // bool is<double>() const;
+  // bool is<float>() const;
+  template <typename T>
+  typename Internals::EnableIf<Internals::IsFloatingPoint<T>::value, bool>::type
+  is() const {
+    return variantIsFloat();
+  }
+  //
+  // bool is<bool>() const
+  template <typename T>
+  typename Internals::EnableIf<Internals::IsSame<T, bool>::value, bool>::type
+  is() const {
+    return variantIsBoolean();
+  }
+  //
+  // bool is<const char*>() const;
+  // bool is<char*>() const;
+  template <typename T>
+  typename Internals::EnableIf<Internals::IsSame<T, const char *>::value ||
+                                   Internals::IsSame<T, char *>::value,
+                               bool>::type
+  is() const {
+    return variantIsString();
+  }
+  //
+  // bool is<JsonArray> const;
+  // bool is<JsonArray&> const;
+  // bool is<const JsonArray&> const;
+  template <typename T>
+  typename Internals::EnableIf<
+      Internals::IsSame<typename Internals::RemoveConst<
+                            typename Internals::RemoveReference<T>::type>::type,
+                        JsonArray>::value,
+      bool>::type
+  is() const {
+    return variantIsArray();
+  }
+  //
+  // bool is<JsonObject> const;
+  // bool is<JsonObject&> const;
+  // bool is<const JsonObject&> const;
+  template <typename T>
+  typename Internals::EnableIf<
+      Internals::IsSame<typename Internals::RemoveConst<
+                            typename Internals::RemoveReference<T>::type>::type,
+                        JsonObject>::value,
+      bool>::type
+  is() const {
+    return variantIsObject();
+  }
+
+  // Returns true if the variant has a value
+  bool success() const {
+    return _type != Internals::JSON_UNDEFINED;
+  }
+
+ private:
+  JsonArray &variantAsArray() const;
+  JsonObject &variantAsObject() const;
+  const char *variantAsString() const;
+  template <typename T>
+  T variantAsFloat() const;
+  template <typename T>
+  T variantAsInteger() const;
+  bool variantIsBoolean() const;
+  bool variantIsFloat() const;
+  bool variantIsInteger() const;
+  bool variantIsArray() const {
+    return _type == Internals::JSON_ARRAY;
+  }
+  bool variantIsObject() const {
+    return _type == Internals::JSON_OBJECT;
+  }
+  bool variantIsString() const {
+    return _type == Internals::JSON_STRING ||
+           (_type == Internals::JSON_UNPARSED && _content.asString &&
+            !strcmp("null", _content.asString));
+  }
+
+  // The current type of the variant
+  Internals::JsonVariantType _type;
+
+  // The various alternatives for the value of the variant.
+  Internals::JsonVariantContent _content;
+};
+
+DEPRECATED("Decimal places are ignored, use the float value instead")
+inline JsonVariant float_with_n_digits(float value, uint8_t) {
+  return JsonVariant(value);
+}
+
+DEPRECATED("Decimal places are ignored, use the double value instead")
+inline JsonVariant double_with_n_digits(double value, uint8_t) {
+  return JsonVariant(value);
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantBase.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantBase.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..44acf2e142c92eb0b7ddca648fc88021212fa675
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantBase.hpp
@@ -0,0 +1,24 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "JsonVariantCasts.hpp"
+#include "JsonVariantComparisons.hpp"
+#include "JsonVariantOr.hpp"
+#include "JsonVariantSubscripts.hpp"
+#include "Serialization/JsonPrintable.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TImpl>
+class JsonVariantBase : public JsonPrintable<TImpl>,
+                        public JsonVariantCasts<TImpl>,
+                        public JsonVariantComparisons<TImpl>,
+                        public JsonVariantOr<TImpl>,
+                        public JsonVariantSubscripts<TImpl>,
+                        public JsonVariantTag {};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantCasts.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantCasts.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..68f5bd7dd915fc80f3c409a628d45c842b17fa5d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantCasts.hpp
@@ -0,0 +1,59 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Data/JsonVariantAs.hpp"
+#include "Polyfills/attributes.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TImpl>
+class JsonVariantCasts {
+ public:
+#if ARDUINOJSON_ENABLE_DEPRECATED
+  DEPRECATED("use as<JsonArray>() instead")
+  FORCE_INLINE JsonArray &asArray() const {
+    return impl()->template as<JsonArray>();
+  }
+
+  DEPRECATED("use as<JsonObject>() instead")
+  FORCE_INLINE JsonObject &asObject() const {
+    return impl()->template as<JsonObject>();
+  }
+
+  DEPRECATED("use as<char*>() instead")
+  FORCE_INLINE const char *asString() const {
+    return impl()->template as<const char *>();
+  }
+#endif
+
+  // Gets the variant as an array.
+  // Returns a reference to the JsonArray or JsonArray::invalid() if the
+  // variant
+  // is not an array.
+  FORCE_INLINE operator JsonArray &() const {
+    return impl()->template as<JsonArray &>();
+  }
+
+  // Gets the variant as an object.
+  // Returns a reference to the JsonObject or JsonObject::invalid() if the
+  // variant is not an object.
+  FORCE_INLINE operator JsonObject &() const {
+    return impl()->template as<JsonObject &>();
+  }
+
+  template <typename T>
+  FORCE_INLINE operator T() const {
+    return impl()->template as<T>();
+  }
+
+ private:
+  const TImpl *impl() const {
+    return static_cast<const TImpl *>(this);
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantComparisons.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantComparisons.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cae53372976962c0dba801ea49c76e5974f0d75c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantComparisons.hpp
@@ -0,0 +1,138 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "StringTraits/StringTraits.hpp"
+#include "TypeTraits/EnableIf.hpp"
+#include "TypeTraits/IsVariant.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TImpl>
+class JsonVariantComparisons {
+ public:
+  template <typename TComparand>
+  friend bool operator==(const JsonVariantComparisons &variant,
+                         TComparand comparand) {
+    return variant.equals(comparand);
+  }
+
+  template <typename TComparand>
+  friend typename EnableIf<!IsVariant<TComparand>::value, bool>::type
+  operator==(TComparand comparand, const JsonVariantComparisons &variant) {
+    return variant.equals(comparand);
+  }
+
+  template <typename TComparand>
+  friend bool operator!=(const JsonVariantComparisons &variant,
+                         TComparand comparand) {
+    return !variant.equals(comparand);
+  }
+
+  template <typename TComparand>
+  friend typename EnableIf<!IsVariant<TComparand>::value, bool>::type
+  operator!=(TComparand comparand, const JsonVariantComparisons &variant) {
+    return !variant.equals(comparand);
+  }
+
+  template <typename TComparand>
+  friend bool operator<=(const JsonVariantComparisons &left, TComparand right) {
+    return left.as<TComparand>() <= right;
+  }
+
+  template <typename TComparand>
+  friend bool operator<=(TComparand comparand,
+                         const JsonVariantComparisons &variant) {
+    return comparand <= variant.as<TComparand>();
+  }
+
+  template <typename TComparand>
+  friend bool operator>=(const JsonVariantComparisons &variant,
+                         TComparand comparand) {
+    return variant.as<TComparand>() >= comparand;
+  }
+
+  template <typename TComparand>
+  friend bool operator>=(TComparand comparand,
+                         const JsonVariantComparisons &variant) {
+    return comparand >= variant.as<TComparand>();
+  }
+
+  template <typename TComparand>
+  friend bool operator<(const JsonVariantComparisons &varian,
+                        TComparand comparand) {
+    return varian.as<TComparand>() < comparand;
+  }
+
+  template <typename TComparand>
+  friend bool operator<(TComparand comparand,
+                        const JsonVariantComparisons &variant) {
+    return comparand < variant.as<TComparand>();
+  }
+
+  template <typename TComparand>
+  friend bool operator>(const JsonVariantComparisons &variant,
+                        TComparand comparand) {
+    return variant.as<TComparand>() > comparand;
+  }
+
+  template <typename TComparand>
+  friend bool operator>(TComparand comparand,
+                        const JsonVariantComparisons &variant) {
+    return comparand > variant.as<TComparand>();
+  }
+
+ private:
+  const TImpl *impl() const {
+    return static_cast<const TImpl *>(this);
+  }
+
+  template <typename T>
+  const typename JsonVariantAs<T>::type as() const {
+    return impl()->template as<T>();
+  }
+
+  template <typename T>
+  bool is() const {
+    return impl()->template is<T>();
+  }
+
+  template <typename TString>
+  typename EnableIf<StringTraits<TString>::has_equals, bool>::type equals(
+      const TString &comparand) const {
+    const char *value = as<const char *>();
+    return StringTraits<TString>::equals(comparand, value);
+  }
+
+  template <typename TComparand>
+  typename EnableIf<!IsVariant<TComparand>::value &&
+                        !StringTraits<TComparand>::has_equals,
+                    bool>::type
+  equals(const TComparand &comparand) const {
+    return as<TComparand>() == comparand;
+  }
+
+  template <typename TVariant2>
+  bool equals(const JsonVariantComparisons<TVariant2> &right) const {
+    using namespace Internals;
+    if (is<bool>() && right.template is<bool>())
+      return as<bool>() == right.template as<bool>();
+    if (is<JsonInteger>() && right.template is<JsonInteger>())
+      return as<JsonInteger>() == right.template as<JsonInteger>();
+    if (is<JsonFloat>() && right.template is<JsonFloat>())
+      return as<JsonFloat>() == right.template as<JsonFloat>();
+    if (is<JsonArray>() && right.template is<JsonArray>())
+      return as<JsonArray>() == right.template as<JsonArray>();
+    if (is<JsonObject>() && right.template is<JsonObject>())
+      return as<JsonObject>() == right.template as<JsonObject>();
+    if (is<char *>() && right.template is<char *>())
+      return strcmp(as<char *>(), right.template as<char *>()) == 0;
+
+    return false;
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantImpl.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..31f96ce1a8020691f0e9a5cb1d4871d005da6d8e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantImpl.hpp
@@ -0,0 +1,126 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Configuration.hpp"
+#include "JsonArray.hpp"
+#include "JsonObject.hpp"
+#include "JsonVariant.hpp"
+#include "Polyfills/isFloat.hpp"
+#include "Polyfills/isInteger.hpp"
+#include "Polyfills/parseFloat.hpp"
+#include "Polyfills/parseInteger.hpp"
+
+#include <string.h>  // for strcmp
+
+namespace ArduinoJson {
+
+inline JsonVariant::JsonVariant(const JsonArray &array) {
+  if (array.success()) {
+    _type = Internals::JSON_ARRAY;
+    _content.asArray = const_cast<JsonArray *>(&array);
+  } else {
+    _type = Internals::JSON_UNDEFINED;
+  }
+}
+
+inline JsonVariant::JsonVariant(const JsonObject &object) {
+  if (object.success()) {
+    _type = Internals::JSON_OBJECT;
+    _content.asObject = const_cast<JsonObject *>(&object);
+  } else {
+    _type = Internals::JSON_UNDEFINED;
+  }
+}
+
+inline JsonArray &JsonVariant::variantAsArray() const {
+  if (_type == Internals::JSON_ARRAY) return *_content.asArray;
+  return JsonArray::invalid();
+}
+
+inline JsonObject &JsonVariant::variantAsObject() const {
+  if (_type == Internals::JSON_OBJECT) return *_content.asObject;
+  return JsonObject::invalid();
+}
+
+template <typename T>
+inline T JsonVariant::variantAsInteger() const {
+  using namespace Internals;
+  switch (_type) {
+    case JSON_UNDEFINED:
+      return 0;
+    case JSON_POSITIVE_INTEGER:
+    case JSON_BOOLEAN:
+      return T(_content.asInteger);
+    case JSON_NEGATIVE_INTEGER:
+      return T(~_content.asInteger + 1);
+    case JSON_STRING:
+    case JSON_UNPARSED:
+      return parseInteger<T>(_content.asString);
+    default:
+      return T(_content.asFloat);
+  }
+}
+
+inline const char *JsonVariant::variantAsString() const {
+  using namespace Internals;
+  if (_type == JSON_UNPARSED && _content.asString &&
+      !strcmp("null", _content.asString))
+    return NULL;
+  if (_type == JSON_STRING || _type == JSON_UNPARSED) return _content.asString;
+  return NULL;
+}
+
+template <typename T>
+inline T JsonVariant::variantAsFloat() const {
+  using namespace Internals;
+  switch (_type) {
+    case JSON_UNDEFINED:
+      return 0;
+    case JSON_POSITIVE_INTEGER:
+    case JSON_BOOLEAN:
+      return static_cast<T>(_content.asInteger);
+    case JSON_NEGATIVE_INTEGER:
+      return -static_cast<T>(_content.asInteger);
+    case JSON_STRING:
+    case JSON_UNPARSED:
+      return parseFloat<T>(_content.asString);
+    default:
+      return static_cast<T>(_content.asFloat);
+  }
+}
+
+inline bool JsonVariant::variantIsBoolean() const {
+  using namespace Internals;
+  if (_type == JSON_BOOLEAN) return true;
+
+  if (_type != JSON_UNPARSED || _content.asString == NULL) return false;
+
+  return !strcmp(_content.asString, "true") ||
+         !strcmp(_content.asString, "false");
+}
+
+inline bool JsonVariant::variantIsInteger() const {
+  using namespace Internals;
+
+  return _type == JSON_POSITIVE_INTEGER || _type == JSON_NEGATIVE_INTEGER ||
+         (_type == JSON_UNPARSED && isInteger(_content.asString));
+}
+
+inline bool JsonVariant::variantIsFloat() const {
+  using namespace Internals;
+
+  return _type == JSON_FLOAT || _type == JSON_POSITIVE_INTEGER ||
+         _type == JSON_NEGATIVE_INTEGER ||
+         (_type == JSON_UNPARSED && isFloat(_content.asString));
+}
+
+#if ARDUINOJSON_ENABLE_STD_STREAM
+inline std::ostream &operator<<(std::ostream &os, const JsonVariant &source) {
+  return source.printTo(os);
+}
+#endif
+
+}  // namespace ArduinoJson
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantOr.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantOr.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d8022fcb2003c0745e67c717b5da46d7ddb339f0
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantOr.hpp
@@ -0,0 +1,52 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Data/JsonVariantAs.hpp"
+#include "Polyfills/attributes.hpp"
+#include "TypeTraits/EnableIf.hpp"
+#include "TypeTraits/IsIntegral.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TImpl>
+class JsonVariantOr {
+ public:
+  // Returns the default value if the JsonVariant is undefined of incompatible
+  template <typename T>
+  typename EnableIf<!IsIntegral<T>::value, T>::type operator|(
+      const T &defaultValue) const {
+    if (impl()->template is<T>())
+      return impl()->template as<T>();
+    else
+      return defaultValue;
+  }
+
+  // Returns the default value if the JsonVariant is undefined of incompatible
+  // Special case for string: null is treated as undefined
+  const char *operator|(const char *defaultValue) const {
+    const char *value = impl()->template as<const char *>();
+    return value ? value : defaultValue;
+  }
+
+  // Returns the default value if the JsonVariant is undefined of incompatible
+  // Special case for integers: we also accept double
+  template <typename Integer>
+  typename EnableIf<IsIntegral<Integer>::value, Integer>::type operator|(
+      const Integer &defaultValue) const {
+    if (impl()->template is<double>())
+      return impl()->template as<Integer>();
+    else
+      return defaultValue;
+  }
+
+ private:
+  const TImpl *impl() const {
+    return static_cast<const TImpl *>(this);
+  }
+};
+}  // namespace Internals
+}  // namespace ArduinoJson
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantSubscripts.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantSubscripts.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..279ee019fb58dfbd246e91c5fbf52c98dc11a254
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/JsonVariantSubscripts.hpp
@@ -0,0 +1,86 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "Data/JsonVariantAs.hpp"
+#include "Polyfills/attributes.hpp"
+#include "StringTraits/StringTraits.hpp"
+#include "TypeTraits/EnableIf.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// Forward declarations.
+class JsonArraySubscript;
+template <typename TKey>
+class JsonObjectSubscript;
+
+template <typename TImpl>
+class JsonVariantSubscripts {
+ public:
+  // Mimics an array or an object.
+  // Returns the size of the array or object if the variant has that type.
+  // Returns 0 if the variant is neither an array nor an object
+  size_t size() const {
+    return impl()->template as<JsonArray>().size() +
+           impl()->template as<JsonObject>().size();
+  }
+
+  // Mimics an array.
+  // Returns the element at specified index if the variant is an array.
+  // Returns JsonVariant::invalid() if the variant is not an array.
+  FORCE_INLINE const JsonArraySubscript operator[](size_t index) const;
+  FORCE_INLINE JsonArraySubscript operator[](size_t index);
+
+  // Mimics an object.
+  // Returns the value associated with the specified key if the variant is
+  // an object.
+  // Return JsonVariant::invalid() if the variant is not an object.
+  //
+  // const JsonObjectSubscript operator[](TKey) const;
+  // TKey = const std::string&, const String&
+  template <typename TString>
+  FORCE_INLINE
+      typename EnableIf<StringTraits<TString>::has_equals,
+                        const JsonObjectSubscript<const TString &> >::type
+      operator[](const TString &key) const {
+    return impl()->template as<JsonObject>()[key];
+  }
+  //
+  // const JsonObjectSubscript operator[](TKey) const;
+  // TKey = const std::string&, const String&
+  template <typename TString>
+  FORCE_INLINE typename EnableIf<StringTraits<TString>::has_equals,
+                                 JsonObjectSubscript<const TString &> >::type
+  operator[](const TString &key) {
+    return impl()->template as<JsonObject>()[key];
+  }
+  //
+  // JsonObjectSubscript operator[](TKey);
+  // TKey = const char*, const char[N], const FlashStringHelper*
+  template <typename TString>
+  FORCE_INLINE typename EnableIf<StringTraits<const TString *>::has_equals,
+                                 JsonObjectSubscript<const TString *> >::type
+  operator[](const TString *key) {
+    return impl()->template as<JsonObject>()[key];
+  }
+  //
+  // JsonObjectSubscript operator[](TKey);
+  // TKey = const char*, const char[N], const FlashStringHelper*
+  template <typename TString>
+  FORCE_INLINE
+      typename EnableIf<StringTraits<TString *>::has_equals,
+                        const JsonObjectSubscript<const TString *> >::type
+      operator[](const TString *key) const {
+    return impl()->template as<JsonObject>()[key];
+  }
+
+ private:
+  const TImpl *impl() const {
+    return static_cast<const TImpl *>(this);
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b49091ddc5dc2aa91ef10bd34a1980699268315d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/attributes.hpp
@@ -0,0 +1,29 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#ifdef _MSC_VER  // Visual Studio
+
+#define FORCE_INLINE  // __forceinline causes C4714 when returning std::string
+#define NO_INLINE __declspec(noinline)
+#define DEPRECATED(msg) __declspec(deprecated(msg))
+
+#elif defined(__GNUC__)  // GCC or Clang
+
+#define FORCE_INLINE __attribute__((always_inline))
+#define NO_INLINE __attribute__((noinline))
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)
+#define DEPRECATED(msg) __attribute__((deprecated(msg)))
+#else
+#define DEPRECATED(msg) __attribute__((deprecated))
+#endif
+
+#else  // Other compilers
+
+#define FORCE_INLINE
+#define NO_INLINE
+#define DEPRECATED(msg)
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/ctype.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/ctype.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2d52703cd84ec1ca77fe2301761b8eedc3d653c8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/ctype.hpp
@@ -0,0 +1,18 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+inline bool isdigit(char c) {
+  return '0' <= c && c <= '9';
+}
+
+inline bool issign(char c) {
+  return '-' == c || c == '+';
+}
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/isFloat.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/isFloat.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..973b89fe956b9e387167894f2d2449a59017a44b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/isFloat.hpp
@@ -0,0 +1,38 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include <string.h>  // for strcmp
+#include "./ctype.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+inline bool isFloat(const char* s) {
+  if (!s) return false;
+
+  if (!strcmp(s, "NaN")) return true;
+  if (issign(*s)) s++;
+  if (!strcmp(s, "Infinity")) return true;
+  if (*s == '\0') return false;
+
+  while (isdigit(*s)) s++;
+
+  if (*s == '.') {
+    s++;
+    while (isdigit(*s)) s++;
+  }
+
+  if (*s == 'e' || *s == 'E') {
+    s++;
+    if (issign(*s)) s++;
+    if (!isdigit(*s)) return false;
+    while (isdigit(*s)) s++;
+  }
+
+  return *s == '\0';
+}
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/isInteger.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/isInteger.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..21f166897dde25e20a9fe5f30a3d2170f7dc98eb
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/isInteger.hpp
@@ -0,0 +1,19 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "./ctype.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+inline bool isInteger(const char* s) {
+  if (!s) return false;
+  if (issign(*s)) s++;
+  while (isdigit(*s)) s++;
+  return *s == '\0';
+}
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/math.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/math.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..48773edd2fe432fea0ca02172463b00ff7901781
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/math.hpp
@@ -0,0 +1,19 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+template <typename T>
+bool isNaN(T x) {
+  return x != x;
+}
+
+template <typename T>
+bool isInfinity(T x) {
+  return x != 0.0 && x * 2 == x;
+}
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/parseFloat.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/parseFloat.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..49b0f6fcd1d353bc456c339ebaa84c4e69de5234
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/parseFloat.hpp
@@ -0,0 +1,90 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../TypeTraits/FloatTraits.hpp"
+#include "./ctype.hpp"
+#include "./math.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename T>
+inline T parseFloat(const char* s) {
+  typedef FloatTraits<T> traits;
+  typedef typename traits::mantissa_type mantissa_t;
+  typedef typename traits::exponent_type exponent_t;
+
+  if (!s) return 0;  // NULL
+
+  bool negative_result = false;
+  switch (*s) {
+    case '-':
+      negative_result = true;
+      s++;
+      break;
+    case '+':
+      s++;
+      break;
+  }
+
+  if (*s == 't') return 1;  // true
+  if (*s == 'n' || *s == 'N') return traits::nan();
+  if (*s == 'i' || *s == 'I')
+    return negative_result ? -traits::inf() : traits::inf();
+
+  mantissa_t mantissa = 0;
+  exponent_t exponent_offset = 0;
+
+  while (isdigit(*s)) {
+    if (mantissa < traits::mantissa_max / 10)
+      mantissa = mantissa * 10 + (*s - '0');
+    else
+      exponent_offset++;
+    s++;
+  }
+
+  if (*s == '.') {
+    s++;
+    while (isdigit(*s)) {
+      if (mantissa < traits::mantissa_max / 10) {
+        mantissa = mantissa * 10 + (*s - '0');
+        exponent_offset--;
+      }
+      s++;
+    }
+  }
+
+  int exponent = 0;
+  if (*s == 'e' || *s == 'E') {
+    s++;
+    bool negative_exponent = false;
+    if (*s == '-') {
+      negative_exponent = true;
+      s++;
+    } else if (*s == '+') {
+      s++;
+    }
+
+    while (isdigit(*s)) {
+      exponent = exponent * 10 + (*s - '0');
+      if (exponent + exponent_offset > traits::exponent_max) {
+        if (negative_exponent)
+          return negative_result ? -0.0f : 0.0f;
+        else
+          return negative_result ? -traits::inf() : traits::inf();
+      }
+      s++;
+    }
+    if (negative_exponent) exponent = -exponent;
+  }
+  exponent += exponent_offset;
+
+  T result = traits::make_float(static_cast<T>(mantissa), exponent);
+
+  return negative_result ? -result : result;
+}
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/parseInteger.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/parseInteger.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e8f1974940032a232f78c25b195542df5ee45b19
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Polyfills/parseInteger.hpp
@@ -0,0 +1,41 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include <stdlib.h>
+
+#include "../Configuration.hpp"
+#include "./ctype.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+template <typename T>
+T parseInteger(const char *s) {
+  if (!s) return 0;  // NULL
+
+  if (*s == 't') return 1;  // "true"
+
+  T result = 0;
+  bool negative_result = false;
+
+  switch (*s) {
+    case '-':
+      negative_result = true;
+      s++;
+      break;
+    case '+':
+      s++;
+      break;
+  }
+
+  while (isdigit(*s)) {
+    result = T(result * 10 + T(*s - '0'));
+    s++;
+  }
+
+  return negative_result ? T(~result + 1) : result;
+}
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/RawJson.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/RawJson.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4beb980ee6fe8d11095f5e59912f17d175a111ad
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/RawJson.hpp
@@ -0,0 +1,46 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+
+namespace Internals {
+// A special type of data that can be used to insert pregenerated JSON portions.
+template <typename T>
+class RawJsonString {
+ public:
+  explicit RawJsonString(T str) : _str(str) {}
+  operator T() const {
+    return _str;
+  }
+
+ private:
+  T _str;
+};
+
+template <typename String>
+struct StringTraits<RawJsonString<String>, void> {
+  static bool is_null(RawJsonString<String> source) {
+    return StringTraits<String>::is_null(static_cast<String>(source));
+  }
+
+  typedef RawJsonString<const char*> duplicate_t;
+
+  template <typename Buffer>
+  static duplicate_t duplicate(RawJsonString<String> source, Buffer* buffer) {
+    return duplicate_t(StringTraits<String>::duplicate(source, buffer));
+  }
+
+  static const bool has_append = false;
+  static const bool has_equals = false;
+  static const bool should_duplicate = StringTraits<String>::should_duplicate;
+};
+}
+
+template <typename T>
+inline Internals::RawJsonString<T> RawJson(T str) {
+  return Internals::RawJsonString<T>(str);
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/DummyPrint.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/DummyPrint.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9fdf2d6a0bfb90a7b0ac108fa1bd9b6d5aff627f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/DummyPrint.hpp
@@ -0,0 +1,22 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A dummy Print implementation used in JsonPrintable::measureLength()
+class DummyPrint {
+ public:
+  size_t print(char) {
+    return 1;
+  }
+
+  size_t print(const char* s) {
+    return strlen(s);
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..41be6392c7fcdf052fdfff82a0922de79fde94af
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/DynamicStringBuilder.hpp
@@ -0,0 +1,35 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../StringTraits/StringTraits.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A Print implementation that allows to write in a String
+template <typename TString>
+class DynamicStringBuilder {
+ public:
+  DynamicStringBuilder(TString &str) : _str(str) {}
+
+  size_t print(char c) {
+    StringTraits<TString>::append(_str, c);
+    return 1;
+  }
+
+  size_t print(const char *s) {
+    size_t initialLen = _str.length();
+    StringTraits<TString>::append(_str, s);
+    return _str.length() - initialLen;
+  }
+
+ private:
+  DynamicStringBuilder &operator=(const DynamicStringBuilder &);
+
+  TString &_str;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/FloatParts.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/FloatParts.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c14e3b553f3292da8380c23e41276923597673da
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/FloatParts.hpp
@@ -0,0 +1,89 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../Configuration.hpp"
+#include "../Polyfills/math.hpp"
+#include "../TypeTraits/FloatTraits.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TFloat>
+struct FloatParts {
+  uint32_t integral;
+  uint32_t decimal;
+  int16_t exponent;
+  int8_t decimalPlaces;
+
+  FloatParts(TFloat value) {
+    uint32_t maxDecimalPart = sizeof(TFloat) >= 8 ? 1000000000 : 1000000;
+    decimalPlaces = sizeof(TFloat) >= 8 ? 9 : 6;
+
+    exponent = normalize(value);
+
+    integral = uint32_t(value);
+    // reduce number of decimal places by the number of integral places
+    for (uint32_t tmp = integral; tmp >= 10; tmp /= 10) {
+      maxDecimalPart /= 10;
+      decimalPlaces--;
+    }
+
+    TFloat remainder = (value - TFloat(integral)) * TFloat(maxDecimalPart);
+
+    decimal = uint32_t(remainder);
+    remainder = remainder - TFloat(decimal);
+
+    // rounding:
+    // increment by 1 if remainder >= 0.5
+    decimal += uint32_t(remainder * 2);
+    if (decimal >= maxDecimalPart) {
+      decimal = 0;
+      integral++;
+      if (exponent && integral >= 10) {
+        exponent++;
+        integral = 1;
+      }
+    }
+
+    // remove trailing zeros
+    while (decimal % 10 == 0 && decimalPlaces > 0) {
+      decimal /= 10;
+      decimalPlaces--;
+    }
+  }
+
+  static int16_t normalize(TFloat& value) {
+    typedef FloatTraits<TFloat> traits;
+    int16_t powersOf10 = 0;
+
+    int8_t index = sizeof(TFloat) == 8 ? 8 : 5;
+    int bit = 1 << index;
+
+    if (value >= ARDUINOJSON_POSITIVE_EXPONENTIATION_THRESHOLD) {
+      for (; index >= 0; index--) {
+        if (value >= traits::positiveBinaryPowerOfTen(index)) {
+          value *= traits::negativeBinaryPowerOfTen(index);
+          powersOf10 = int16_t(powersOf10 + bit);
+        }
+        bit >>= 1;
+      }
+    }
+
+    if (value > 0 && value <= ARDUINOJSON_NEGATIVE_EXPONENTIATION_THRESHOLD) {
+      for (; index >= 0; index--) {
+        if (value < traits::negativeBinaryPowerOfTenPlusOne(index)) {
+          value *= traits::positiveBinaryPowerOfTen(index);
+          powersOf10 = int16_t(powersOf10 - bit);
+        }
+        bit >>= 1;
+      }
+    }
+
+    return powersOf10;
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/IndentedPrint.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/IndentedPrint.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..864f9aaa4fe50efbdc6a58cfb26ff8ab9b6c8e3e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/IndentedPrint.hpp
@@ -0,0 +1,68 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// Decorator on top of Print to allow indented output.
+// This class is used by JsonPrintable::prettyPrintTo() but can also be used
+// for your own purpose, like logging.
+template <typename Print>
+class IndentedPrint {
+ public:
+  explicit IndentedPrint(Print &p) : sink(&p) {
+    level = 0;
+    tabSize = 2;
+    isNewLine = true;
+  }
+
+  size_t print(char c) {
+    size_t n = 0;
+    if (isNewLine) n += writeTabs();
+    n += sink->print(c);
+    isNewLine = c == '\n';
+    return n;
+  }
+
+  size_t print(const char *s) {
+    // TODO: optimize
+    size_t n = 0;
+    while (*s) n += print(*s++);
+    return n;
+  }
+
+  // Adds one level of indentation
+  void indent() {
+    if (level < MAX_LEVEL) level++;
+  }
+
+  // Removes one level of indentation
+  void unindent() {
+    if (level > 0) level--;
+  }
+
+  // Set the number of space printed for each level of indentation
+  void setTabSize(uint8_t n) {
+    if (n < MAX_TAB_SIZE) tabSize = n & MAX_TAB_SIZE;
+  }
+
+ private:
+  Print *sink;
+  uint8_t level : 4;
+  uint8_t tabSize : 3;
+  bool isNewLine : 1;
+
+  size_t writeTabs() {
+    size_t n = 0;
+    for (int i = 0; i < level * tabSize; i++) n += sink->print(' ');
+    return n;
+  }
+
+  static const int MAX_LEVEL = 15;    // because it's only 4 bits
+  static const int MAX_TAB_SIZE = 7;  // because it's only 3 bits
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonPrintable.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonPrintable.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..43d413a857a5a8ab935688b1fcb924a5ee2b4753
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonPrintable.hpp
@@ -0,0 +1,117 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../Configuration.hpp"
+#include "../TypeTraits/EnableIf.hpp"
+#include "DummyPrint.hpp"
+#include "DynamicStringBuilder.hpp"
+#include "IndentedPrint.hpp"
+#include "JsonSerializer.hpp"
+#include "JsonWriter.hpp"
+#include "Prettyfier.hpp"
+#include "StaticStringBuilder.hpp"
+
+#if ARDUINOJSON_ENABLE_STD_STREAM
+#include "StreamPrintAdapter.hpp"
+#endif
+
+namespace ArduinoJson {
+namespace Internals {
+
+// Implements all the overloads of printTo() and prettyPrintTo()
+// Caution: this class use a template parameter to avoid virtual methods.
+// This is a bit curious but allows to reduce the size of JsonVariant, JsonArray
+// and JsonObject.
+template <typename T>
+class JsonPrintable {
+ public:
+  template <typename Print>
+  typename EnableIf<!StringTraits<Print>::has_append, size_t>::type printTo(
+      Print &print) const {
+    JsonWriter<Print> writer(print);
+    JsonSerializer<JsonWriter<Print> >::serialize(downcast(), writer);
+    return writer.bytesWritten();
+  }
+
+#if ARDUINOJSON_ENABLE_STD_STREAM
+  std::ostream &printTo(std::ostream &os) const {
+    StreamPrintAdapter adapter(os);
+    printTo(adapter);
+    return os;
+  }
+#endif
+
+  size_t printTo(char *buffer, size_t bufferSize) const {
+    StaticStringBuilder sb(buffer, bufferSize);
+    return printTo(sb);
+  }
+
+  template <size_t N>
+  size_t printTo(char (&buffer)[N]) const {
+    return printTo(buffer, N);
+  }
+
+  template <typename TString>
+  typename EnableIf<StringTraits<TString>::has_append, size_t>::type printTo(
+      TString &str) const {
+    DynamicStringBuilder<TString> sb(str);
+    return printTo(sb);
+  }
+
+  template <typename Print>
+  size_t prettyPrintTo(IndentedPrint<Print> &print) const {
+    Prettyfier<Print> p(print);
+    return printTo(p);
+  }
+
+  size_t prettyPrintTo(char *buffer, size_t bufferSize) const {
+    StaticStringBuilder sb(buffer, bufferSize);
+    return prettyPrintTo(sb);
+  }
+
+  template <size_t N>
+  size_t prettyPrintTo(char (&buffer)[N]) const {
+    return prettyPrintTo(buffer, N);
+  }
+
+  template <typename Print>
+  typename EnableIf<!StringTraits<Print>::has_append, size_t>::type
+  prettyPrintTo(Print &print) const {
+    IndentedPrint<Print> indentedPrint(print);
+    return prettyPrintTo(indentedPrint);
+  }
+
+  template <typename TString>
+  typename EnableIf<StringTraits<TString>::has_append, size_t>::type
+  prettyPrintTo(TString &str) const {
+    DynamicStringBuilder<TString> sb(str);
+    return prettyPrintTo(sb);
+  }
+
+  size_t measureLength() const {
+    DummyPrint dp;
+    return printTo(dp);
+  }
+
+  size_t measurePrettyLength() const {
+    DummyPrint dp;
+    return prettyPrintTo(dp);
+  }
+
+ private:
+  const T &downcast() const {
+    return *static_cast<const T *>(this);
+  }
+};
+
+#if ARDUINOJSON_ENABLE_STD_STREAM
+template <typename T>
+inline std::ostream &operator<<(std::ostream &os, const JsonPrintable<T> &v) {
+  return v.printTo(os);
+}
+#endif
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonSerializer.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonSerializer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0cb537f7d8dd773ae3d512d2759877320b229b2e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonSerializer.hpp
@@ -0,0 +1,32 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "JsonWriter.hpp"
+
+namespace ArduinoJson {
+
+class JsonArray;
+class JsonObject;
+class JsonVariant;
+
+namespace Internals {
+
+class JsonArraySubscript;
+template <typename TKey>
+class JsonObjectSubscript;
+
+template <typename Writer>
+class JsonSerializer {
+ public:
+  static void serialize(const JsonArray &, Writer &);
+  static void serialize(const JsonArraySubscript &, Writer &);
+  static void serialize(const JsonObject &, Writer &);
+  template <typename TKey>
+  static void serialize(const JsonObjectSubscript<TKey> &, Writer &);
+  static void serialize(const JsonVariant &, Writer &);
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0faae2769ecda1c973091fb6993e3dcc41d275a9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonSerializerImpl.hpp
@@ -0,0 +1,103 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../JsonArray.hpp"
+#include "../JsonArraySubscript.hpp"
+#include "../JsonObject.hpp"
+#include "../JsonObjectSubscript.hpp"
+#include "../JsonVariant.hpp"
+#include "JsonSerializer.hpp"
+
+template <typename Writer>
+inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
+    const JsonArray& array, Writer& writer) {
+  writer.beginArray();
+
+  JsonArray::const_iterator it = array.begin();
+  while (it != array.end()) {
+    serialize(*it, writer);
+
+    ++it;
+    if (it == array.end()) break;
+
+    writer.writeComma();
+  }
+
+  writer.endArray();
+}
+
+template <typename Writer>
+inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
+    const JsonArraySubscript& arraySubscript, Writer& writer) {
+  serialize(arraySubscript.as<JsonVariant>(), writer);
+}
+
+template <typename Writer>
+inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
+    const JsonObject& object, Writer& writer) {
+  writer.beginObject();
+
+  JsonObject::const_iterator it = object.begin();
+  while (it != object.end()) {
+    writer.writeString(it->key);
+    writer.writeColon();
+    serialize(it->value, writer);
+
+    ++it;
+    if (it == object.end()) break;
+
+    writer.writeComma();
+  }
+
+  writer.endObject();
+}
+
+template <typename Writer>
+template <typename TKey>
+inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
+    const JsonObjectSubscript<TKey>& objectSubscript, Writer& writer) {
+  serialize(objectSubscript.template as<JsonVariant>(), writer);
+}
+
+template <typename Writer>
+inline void ArduinoJson::Internals::JsonSerializer<Writer>::serialize(
+    const JsonVariant& variant, Writer& writer) {
+  switch (variant._type) {
+    case JSON_FLOAT:
+      writer.writeFloat(variant._content.asFloat);
+      return;
+
+    case JSON_ARRAY:
+      serialize(*variant._content.asArray, writer);
+      return;
+
+    case JSON_OBJECT:
+      serialize(*variant._content.asObject, writer);
+      return;
+
+    case JSON_STRING:
+      writer.writeString(variant._content.asString);
+      return;
+
+    case JSON_UNPARSED:
+      writer.writeRaw(variant._content.asString);
+      return;
+
+    case JSON_NEGATIVE_INTEGER:
+      writer.writeRaw('-');  // Falls through.
+
+    case JSON_POSITIVE_INTEGER:
+      writer.writeInteger(variant._content.asInteger);
+      return;
+
+    case JSON_BOOLEAN:
+      writer.writeBoolean(variant._content.asInteger != 0);
+      return;
+
+    default:  // JSON_UNDEFINED
+      return;
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonWriter.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonWriter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..146d51dcb95a9fb26eb329e3b43854cb83565116
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/JsonWriter.hpp
@@ -0,0 +1,155 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include <stdint.h>
+#include "../Data/Encoding.hpp"
+#include "../Data/JsonInteger.hpp"
+#include "../Polyfills/attributes.hpp"
+#include "../Serialization/FloatParts.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// Writes the JSON tokens to a Print implementation
+// This class is used by:
+// - JsonArray::writeTo()
+// - JsonObject::writeTo()
+// - JsonVariant::writeTo()
+// Its derived by PrettyJsonWriter that overrides some members to add
+// indentation.
+template <typename Print>
+class JsonWriter {
+ public:
+  explicit JsonWriter(Print &sink) : _sink(sink), _length(0) {}
+
+  // Returns the number of bytes sent to the Print implementation.
+  // This is very handy for implementations of printTo() that must return the
+  // number of bytes written.
+  size_t bytesWritten() const {
+    return _length;
+  }
+
+  void beginArray() {
+    writeRaw('[');
+  }
+  void endArray() {
+    writeRaw(']');
+  }
+
+  void beginObject() {
+    writeRaw('{');
+  }
+  void endObject() {
+    writeRaw('}');
+  }
+
+  void writeColon() {
+    writeRaw(':');
+  }
+  void writeComma() {
+    writeRaw(',');
+  }
+
+  void writeBoolean(bool value) {
+    writeRaw(value ? "true" : "false");
+  }
+
+  void writeString(const char *value) {
+    if (!value) {
+      writeRaw("null");
+    } else {
+      writeRaw('\"');
+      while (*value) writeChar(*value++);
+      writeRaw('\"');
+    }
+  }
+
+  void writeChar(char c) {
+    char specialChar = Encoding::escapeChar(c);
+    if (specialChar) {
+      writeRaw('\\');
+      writeRaw(specialChar);
+    } else {
+      writeRaw(c);
+    }
+  }
+
+  template <typename TFloat>
+  void writeFloat(TFloat value) {
+    if (isNaN(value)) return writeRaw("NaN");
+
+    if (value < 0.0) {
+      writeRaw('-');
+      value = -value;
+    }
+
+    if (isInfinity(value)) return writeRaw("Infinity");
+
+    FloatParts<TFloat> parts(value);
+
+    writeInteger(parts.integral);
+    if (parts.decimalPlaces) writeDecimals(parts.decimal, parts.decimalPlaces);
+
+    if (parts.exponent < 0) {
+      writeRaw("e-");
+      writeInteger(-parts.exponent);
+    }
+
+    if (parts.exponent > 0) {
+      writeRaw('e');
+      writeInteger(parts.exponent);
+    }
+  }
+
+  template <typename UInt>
+  void writeInteger(UInt value) {
+    char buffer[22];
+    char *end = buffer + sizeof(buffer) - 1;
+    char *ptr = end;
+
+    *ptr = 0;
+    do {
+      *--ptr = char(value % 10 + '0');
+      value = UInt(value / 10);
+    } while (value);
+
+    writeRaw(ptr);
+  }
+
+  void writeDecimals(uint32_t value, int8_t width) {
+    // buffer should be big enough for all digits, the dot and the null
+    // terminator
+    char buffer[16];
+    char *ptr = buffer + sizeof(buffer) - 1;
+
+    // write the string in reverse order
+    *ptr = 0;
+    while (width--) {
+      *--ptr = char(value % 10 + '0');
+      value /= 10;
+    }
+    *--ptr = '.';
+
+    // and dump it in the right order
+    writeRaw(ptr);
+  }
+
+  void writeRaw(const char *s) {
+    _length += _sink.print(s);
+  }
+  void writeRaw(char c) {
+    _length += _sink.print(c);
+  }
+
+ protected:
+  Print &_sink;
+  size_t _length;
+
+ private:
+  JsonWriter &operator=(const JsonWriter &);  // cannot be assigned
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/Prettyfier.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/Prettyfier.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8b4f0d2eb5fac7bc7e2eb726d77033c7068c49d6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/Prettyfier.hpp
@@ -0,0 +1,133 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "IndentedPrint.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// Converts a compact JSON string into an indented one.
+template <typename Print>
+class Prettyfier {
+ public:
+  explicit Prettyfier(IndentedPrint<Print>& p) : _sink(p) {
+    _previousChar = 0;
+    _inString = false;
+  }
+
+  size_t print(char c) {
+    size_t n = _inString ? handleStringChar(c) : handleMarkupChar(c);
+    _previousChar = c;
+    return n;
+  }
+
+  size_t print(const char* s) {
+    // TODO: optimize
+    size_t n = 0;
+    while (*s) n += print(*s++);
+    return n;
+  }
+
+ private:
+  Prettyfier& operator=(const Prettyfier&);  // cannot be assigned
+
+  bool inEmptyBlock() {
+    return _previousChar == '{' || _previousChar == '[';
+  }
+
+  size_t handleStringChar(char c) {
+    bool isQuote = c == '"' && _previousChar != '\\';
+
+    if (isQuote) _inString = false;
+
+    return _sink.print(c);
+  }
+
+  size_t handleMarkupChar(char c) {
+    switch (c) {
+      case '{':
+      case '[':
+        return writeBlockOpen(c);
+
+      case '}':
+      case ']':
+        return writeBlockClose(c);
+
+      case ':':
+        return writeColon();
+
+      case ',':
+        return writeComma();
+
+      case '"':
+        return writeQuoteOpen();
+
+      default:
+        return writeNormalChar(c);
+    }
+  }
+
+  size_t writeBlockClose(char c) {
+    size_t n = 0;
+    n += unindentIfNeeded();
+    n += _sink.print(c);
+    return n;
+  }
+
+  size_t writeBlockOpen(char c) {
+    size_t n = 0;
+    n += indentIfNeeded();
+    n += _sink.print(c);
+    return n;
+  }
+
+  size_t writeColon() {
+    size_t n = 0;
+    n += _sink.print(": ");
+    return n;
+  }
+
+  size_t writeComma() {
+    size_t n = 0;
+    n += _sink.print(",\r\n");
+    return n;
+  }
+
+  size_t writeQuoteOpen() {
+    _inString = true;
+    size_t n = 0;
+    n += indentIfNeeded();
+    n += _sink.print('"');
+    return n;
+  }
+
+  size_t writeNormalChar(char c) {
+    size_t n = 0;
+    n += indentIfNeeded();
+    n += _sink.print(c);
+    return n;
+  }
+
+  size_t indentIfNeeded() {
+    if (!inEmptyBlock()) return 0;
+
+    _sink.indent();
+    return _sink.print("\r\n");
+  }
+
+  size_t unindentIfNeeded() {
+    if (inEmptyBlock()) return 0;
+
+    _sink.unindent();
+    return _sink.print("\r\n");
+  }
+
+  char _previousChar;
+  IndentedPrint<Print>& _sink;
+  bool _inString;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/StaticStringBuilder.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/StaticStringBuilder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9617bbd977e87cb38468974a0277b5344105048d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/StaticStringBuilder.hpp
@@ -0,0 +1,36 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A Print implementation that allows to write in a char[]
+class StaticStringBuilder {
+ public:
+  StaticStringBuilder(char *buf, size_t size) : end(buf + size - 1), p(buf) {
+    *p = '\0';
+  }
+
+  size_t print(char c) {
+    if (p >= end) return 0;
+    *p++ = c;
+    *p = '\0';
+    return 1;
+  }
+
+  size_t print(const char *s) {
+    char *begin = p;
+    while (p < end && *s) *p++ = *s++;
+    *p = '\0';
+    return size_t(p - begin);
+  }
+
+ private:
+  char *end;
+  char *p;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..60f0af4a39cc03b8a06c010124c1a85f92685630
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/Serialization/StreamPrintAdapter.hpp
@@ -0,0 +1,39 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../Configuration.hpp"
+
+#if ARDUINOJSON_ENABLE_STD_STREAM
+
+#include <ostream>
+
+namespace ArduinoJson {
+namespace Internals {
+
+class StreamPrintAdapter {
+ public:
+  explicit StreamPrintAdapter(std::ostream& os) : _os(os) {}
+
+  size_t print(char c) {
+    _os << c;
+    return 1;
+  }
+
+  size_t print(const char* s) {
+    _os << s;
+    return strlen(s);
+  }
+
+ private:
+  // cannot be assigned
+  StreamPrintAdapter& operator=(const StreamPrintAdapter&);
+
+  std::ostream& _os;
+};
+}
+}
+
+#endif  // ARDUINOJSON_ENABLE_STD_STREAM
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StaticJsonBuffer.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StaticJsonBuffer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..267d9d01889a4e4884c8cde07730461fb2f6922d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StaticJsonBuffer.hpp
@@ -0,0 +1,126 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "JsonBufferBase.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+class StaticJsonBufferBase : public JsonBufferBase<StaticJsonBufferBase> {
+ public:
+  class String {
+   public:
+    String(StaticJsonBufferBase* parent) : _parent(parent) {
+      _start = parent->_buffer + parent->_size;
+    }
+
+    void append(char c) {
+      if (_parent->canAlloc(1)) {
+        char* last = static_cast<char*>(_parent->doAlloc(1));
+        *last = c;
+      }
+    }
+
+    const char* c_str() const {
+      if (_parent->canAlloc(1)) {
+        char* last = static_cast<char*>(_parent->doAlloc(1));
+        *last = '\0';
+        return _start;
+      } else {
+        return NULL;
+      }
+    }
+
+   private:
+    StaticJsonBufferBase* _parent;
+    char* _start;
+  };
+
+  StaticJsonBufferBase(char* buffer, size_t capa)
+      : _buffer(buffer), _capacity(capa), _size(0) {}
+
+  // Gets the capacity of the buffer in bytes
+  size_t capacity() const {
+    return _capacity;
+  }
+
+  // Gets the current usage of the buffer in bytes
+  size_t size() const {
+    return _size;
+  }
+
+  // Allocates the specified amount of bytes in the buffer
+  virtual void* alloc(size_t bytes) {
+    alignNextAlloc();
+    if (!canAlloc(bytes)) return NULL;
+    return doAlloc(bytes);
+  }
+
+  // Resets the buffer.
+  // USE WITH CAUTION: this invalidates all previously allocated data
+  void clear() {
+    _size = 0;
+  }
+
+  String startString() {
+    return String(this);
+  }
+
+ protected:
+  ~StaticJsonBufferBase() {}
+
+ private:
+  void alignNextAlloc() {
+    _size = round_size_up(_size);
+  }
+
+  bool canAlloc(size_t bytes) const {
+    return _size + bytes <= _capacity;
+  }
+
+  void* doAlloc(size_t bytes) {
+    void* p = &_buffer[_size];
+    _size += bytes;
+    return p;
+  }
+
+  char* _buffer;
+  size_t _capacity;
+  size_t _size;
+};
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnon-virtual-dtor"
+#elif defined(__GNUC__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic push
+#endif
+#pragma GCC diagnostic ignored "-Wnon-virtual-dtor"
+#endif
+
+// Implements a JsonBuffer with fixed memory allocation.
+// The template paramenter CAPACITY specifies the capacity of the buffer in
+// bytes.
+template <size_t CAPACITY>
+class StaticJsonBuffer : public Internals::StaticJsonBufferBase {
+ public:
+  explicit StaticJsonBuffer()
+      : Internals::StaticJsonBufferBase(_buffer, CAPACITY) {}
+
+ private:
+  char _buffer[CAPACITY];
+};
+}
+
+#if defined(__clang__)
+#pragma clang diagnostic pop
+#elif defined(__GNUC__)
+#if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)
+#pragma GCC diagnostic pop
+#endif
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/ArduinoStream.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/ArduinoStream.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5db0852b8996c6b0c39eac6992598bee1acef74c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/ArduinoStream.hpp
@@ -0,0 +1,61 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#if ARDUINOJSON_ENABLE_ARDUINO_STREAM
+
+#include <Stream.h>
+
+namespace ArduinoJson {
+namespace Internals {
+
+struct ArduinoStreamTraits {
+  class Reader {
+    Stream& _stream;
+    char _current, _next;
+
+   public:
+    Reader(Stream& stream) : _stream(stream), _current(0), _next(0) {}
+
+    void move() {
+      _current = _next;
+      _next = 0;
+    }
+
+    char current() {
+      if (!_current) _current = read();
+      return _current;
+    }
+
+    char next() {
+      // assumes that current() has been called
+      if (!_next) _next = read();
+      return _next;
+    }
+
+   private:
+    char read() {
+      // don't use _stream.read() as it ignores the timeout
+      char c = 0;
+      _stream.readBytes(&c, 1);
+      return c;
+    }
+  };
+
+  static const bool has_append = false;
+  static const bool has_equals = false;
+};
+
+template <typename TStream>
+struct StringTraits<
+    TStream,
+    // match any type that is derived from Stream:
+    typename EnableIf<
+        IsBaseOf<Stream, typename RemoveReference<TStream>::type>::value>::type>
+    : ArduinoStreamTraits {};
+}
+}
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/CharPointer.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/CharPointer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a9f30f786b6c231ff48eff7d716d0402f9599cae
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/CharPointer.hpp
@@ -0,0 +1,62 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TChar>
+struct CharPointerTraits {
+  class Reader {
+    const TChar* _ptr;
+
+   public:
+    Reader(const TChar* ptr)
+        : _ptr(ptr ? ptr : reinterpret_cast<const TChar*>("")) {}
+
+    void move() {
+      ++_ptr;
+    }
+
+    char current() const {
+      return char(_ptr[0]);
+    }
+
+    char next() const {
+      return char(_ptr[1]);
+    }
+  };
+
+  static bool equals(const TChar* str, const char* expected) {
+    return strcmp(reinterpret_cast<const char*>(str), expected) == 0;
+  }
+
+  static bool is_null(const TChar* str) {
+    return !str;
+  }
+
+  typedef const char* duplicate_t;
+
+  template <typename Buffer>
+  static duplicate_t duplicate(const TChar* str, Buffer* buffer) {
+    if (!str) return NULL;
+    size_t size = strlen(reinterpret_cast<const char*>(str)) + 1;
+    void* dup = buffer->alloc(size);
+    if (dup != NULL) memcpy(dup, str, size);
+    return static_cast<duplicate_t>(dup);
+  }
+
+  static const bool has_append = false;
+  static const bool has_equals = true;
+  static const bool should_duplicate = !IsConst<TChar>::value;
+};
+
+// char*, unsigned char*, signed char*
+// const char*, const unsigned char*, const signed char*
+template <typename TChar>
+struct StringTraits<TChar*, typename EnableIf<IsChar<TChar>::value>::type>
+    : CharPointerTraits<TChar> {};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/FlashString.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/FlashString.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..95f555d20a1f2b137b8c65ac0103dbc7117f4285
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/FlashString.hpp
@@ -0,0 +1,59 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#if ARDUINOJSON_ENABLE_PROGMEM
+
+namespace ArduinoJson {
+namespace Internals {
+template <>
+struct StringTraits<const __FlashStringHelper*, void> {
+  class Reader {
+    const char* _ptr;
+
+   public:
+    Reader(const __FlashStringHelper* ptr)
+        : _ptr(reinterpret_cast<const char*>(ptr)) {}
+
+    void move() {
+      _ptr++;
+    }
+
+    char current() const {
+      return pgm_read_byte_near(_ptr);
+    }
+
+    char next() const {
+      return pgm_read_byte_near(_ptr + 1);
+    }
+  };
+
+  static bool equals(const __FlashStringHelper* str, const char* expected) {
+    return strcmp_P(expected, (const char*)str) == 0;
+  }
+
+  static bool is_null(const __FlashStringHelper* str) {
+    return !str;
+  }
+
+  typedef const char* duplicate_t;
+
+  template <typename Buffer>
+  static duplicate_t duplicate(const __FlashStringHelper* str, Buffer* buffer) {
+    if (!str) return NULL;
+    size_t size = strlen_P((const char*)str) + 1;
+    void* dup = buffer->alloc(size);
+    if (dup != NULL) memcpy_P(dup, (const char*)str, size);
+    return static_cast<duplicate_t>(dup);
+  }
+
+  static const bool has_append = false;
+  static const bool has_equals = true;
+  static const bool should_duplicate = true;
+};
+}
+}
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/StdStream.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/StdStream.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..227c74406a200daabfc820bae08c601532b88a96
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/StdStream.hpp
@@ -0,0 +1,60 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#if ARDUINOJSON_ENABLE_STD_STREAM
+
+#include <istream>
+
+namespace ArduinoJson {
+namespace Internals {
+
+struct StdStreamTraits {
+  class Reader {
+    std::istream& _stream;
+    char _current, _next;
+
+   public:
+    Reader(std::istream& stream) : _stream(stream), _current(0), _next(0) {}
+
+    void move() {
+      _current = _next;
+      _next = 0;
+    }
+
+    char current() {
+      if (!_current) _current = read();
+      return _current;
+    }
+
+    char next() {
+      // assumes that current() has been called
+      if (!_next) _next = read();
+      return _next;
+    }
+
+   private:
+    Reader& operator=(const Reader&);  // Visual Studio C4512
+
+    char read() {
+      return _stream.eof() ? '\0' : static_cast<char>(_stream.get());
+    }
+  };
+
+  static const bool has_append = false;
+  static const bool has_equals = false;
+};
+
+template <typename TStream>
+struct StringTraits<
+    TStream,
+    // match any type that is derived from std::istream:
+    typename EnableIf<IsBaseOf<
+        std::istream, typename RemoveReference<TStream>::type>::value>::type>
+    : StdStreamTraits {};
+}
+}
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/StdString.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/StdString.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..39124dac898a735267f5ae04479132e799fa8995
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/StdString.hpp
@@ -0,0 +1,74 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#if ARDUINOJSON_ENABLE_STD_STRING || ARDUINOJSON_ENABLE_ARDUINO_STRING
+
+#if ARDUINOJSON_ENABLE_ARDUINO_STRING
+#include <WString.h>
+#endif
+
+#if ARDUINOJSON_ENABLE_STD_STRING
+#include <string>
+#endif
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TString>
+struct StdStringTraits {
+  typedef const char* duplicate_t;
+
+  template <typename Buffer>
+  static duplicate_t duplicate(const TString& str, Buffer* buffer) {
+    if (!str.c_str()) return NULL;  // <- Arduino string can return NULL
+    size_t size = str.length() + 1;
+    void* dup = buffer->alloc(size);
+    if (dup != NULL) memcpy(dup, str.c_str(), size);
+    return static_cast<duplicate_t>(dup);
+  }
+
+  static bool is_null(const TString& str) {
+    // Arduino's String::c_str() can return NULL
+    return !str.c_str();
+  }
+
+  struct Reader : CharPointerTraits<char>::Reader {
+    Reader(const TString& str) : CharPointerTraits<char>::Reader(str.c_str()) {}
+  };
+
+  static bool equals(const TString& str, const char* expected) {
+    return 0 == strcmp(str.c_str(), expected);
+  }
+
+  static void append(TString& str, char c) {
+    str += c;
+  }
+
+  static void append(TString& str, const char* s) {
+    str += s;
+  }
+
+  static const bool has_append = true;
+  static const bool has_equals = true;
+  static const bool should_duplicate = true;
+};
+
+#if ARDUINOJSON_ENABLE_ARDUINO_STRING
+template <>
+struct StringTraits<String, void> : StdStringTraits<String> {};
+template <>
+struct StringTraits<StringSumHelper, void> : StdStringTraits<StringSumHelper> {
+};
+#endif
+
+#if ARDUINOJSON_ENABLE_STD_STRING
+template <>
+struct StringTraits<std::string, void> : StdStringTraits<std::string> {};
+#endif
+}
+}
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/StringTraits.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/StringTraits.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dd5694b2eb44c4d47848bda6fffdf8cb3d372a0b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/StringTraits/StringTraits.hpp
@@ -0,0 +1,36 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include <string.h>
+#include "../Configuration.hpp"
+#include "../TypeTraits/EnableIf.hpp"
+#include "../TypeTraits/IsBaseOf.hpp"
+#include "../TypeTraits/IsChar.hpp"
+#include "../TypeTraits/IsConst.hpp"
+#include "../TypeTraits/RemoveReference.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename TString, typename Enable = void>
+struct StringTraits {
+  static const bool has_append = false;
+  static const bool has_equals = false;
+};
+
+template <typename TString>
+struct StringTraits<const TString, void> : StringTraits<TString> {};
+
+template <typename TString>
+struct StringTraits<TString&, void> : StringTraits<TString> {};
+}
+}
+
+#include "ArduinoStream.hpp"
+#include "CharPointer.hpp"
+#include "FlashString.hpp"
+#include "StdStream.hpp"
+#include "StdString.hpp"
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/EnableIf.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/EnableIf.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..83fc5e07fae9e3db8094a15e036fa9fd46d2e8e7
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/EnableIf.hpp
@@ -0,0 +1,19 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that return the type T if Condition is true.
+template <bool Condition, typename T = void>
+struct EnableIf {};
+
+template <typename T>
+struct EnableIf<true, T> {
+  typedef T type;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/FloatTraits.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/FloatTraits.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5044807a60b894b223fd45aebe92104cbb3ef5de
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/FloatTraits.hpp
@@ -0,0 +1,150 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include <stdint.h>
+#include <stdlib.h>  // for size_t
+#include "../Configuration.hpp"
+#include "../Polyfills/math.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+template <typename T, size_t = sizeof(T)>
+struct FloatTraits {};
+
+template <typename T>
+struct FloatTraits<T, 8 /*64bits*/> {
+  typedef int64_t mantissa_type;
+  static const short mantissa_bits = 52;
+  static const mantissa_type mantissa_max =
+      (static_cast<mantissa_type>(1) << mantissa_bits) - 1;
+
+  typedef int16_t exponent_type;
+  static const exponent_type exponent_max = 308;
+
+  template <typename TExponent>
+  static T make_float(T m, TExponent e) {
+    if (e > 0) {
+      for (uint8_t index = 0; e != 0; index++) {
+        if (e & 1) m *= positiveBinaryPowerOfTen(index);
+        e >>= 1;
+      }
+    } else {
+      e = TExponent(-e);
+      for (uint8_t index = 0; e != 0; index++) {
+        if (e & 1) m *= negativeBinaryPowerOfTen(index);
+        e >>= 1;
+      }
+    }
+    return m;
+  }
+
+  static T positiveBinaryPowerOfTen(int index) {
+    static T factors[] = {
+        1e1, 1e2, 1e4, 1e8, 1e16, 1e32,
+        // workaround to support platforms with single precision literals
+        forge(0x4D384F03, 0xE93FF9F5), forge(0x5A827748, 0xF9301D32),
+        forge(0x75154FDD, 0x7F73BF3C)};
+    return factors[index];
+  }
+
+  static T negativeBinaryPowerOfTen(int index) {
+    static T factors[] = {
+        1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32,
+        // workaround to support platforms with single precision literals
+        forge(0x32A50FFD, 0x44F4A73D), forge(0x255BBA08, 0xCF8C979D),
+        forge(0x0AC80628, 0x64AC6F43)};
+    return factors[index];
+  }
+
+  static T negativeBinaryPowerOfTenPlusOne(int index) {
+    static T factors[] = {
+        1e0, 1e-1, 1e-3, 1e-7, 1e-15, 1e-31,
+        // workaround to support platforms with single precision literals
+        forge(0x32DA53FC, 0x9631D10D), forge(0x25915445, 0x81B7DEC2),
+        forge(0x0AFE07B2, 0x7DD78B14)};
+    return factors[index];
+  }
+
+  static T nan() {
+    return forge(0x7ff80000, 0x00000000);
+  }
+
+  static T inf() {
+    return forge(0x7ff00000, 0x00000000);
+  }
+
+  static T forge(uint32_t msb, uint32_t lsb) {
+    union {
+      uint64_t integerBits;
+      T floatBits;
+    };
+    integerBits = (uint64_t(msb) << 32) | lsb;
+    return floatBits;
+  }
+};
+
+template <typename T>
+struct FloatTraits<T, 4 /*32bits*/> {
+  typedef int32_t mantissa_type;
+  static const short mantissa_bits = 23;
+  static const mantissa_type mantissa_max =
+      (static_cast<mantissa_type>(1) << mantissa_bits) - 1;
+
+  typedef int8_t exponent_type;
+  static const exponent_type exponent_max = 38;
+
+  template <typename TExponent>
+  static T make_float(T m, TExponent e) {
+    if (e > 0) {
+      for (uint8_t index = 0; e != 0; index++) {
+        if (e & 1) m *= positiveBinaryPowerOfTen(index);
+        e >>= 1;
+      }
+    } else {
+      e = -e;
+      for (uint8_t index = 0; e != 0; index++) {
+        if (e & 1) m *= negativeBinaryPowerOfTen(index);
+        e >>= 1;
+      }
+    }
+    return m;
+  }
+
+  static T positiveBinaryPowerOfTen(int index) {
+    static T factors[] = {1e1f, 1e2f, 1e4f, 1e8f, 1e16f, 1e32f};
+    return factors[index];
+  }
+
+  static T negativeBinaryPowerOfTen(int index) {
+    static T factors[] = {1e-1f, 1e-2f, 1e-4f, 1e-8f, 1e-16f, 1e-32f};
+    return factors[index];
+  }
+
+  static T negativeBinaryPowerOfTenPlusOne(int index) {
+    static T factors[] = {1e0f, 1e-1f, 1e-3f, 1e-7f, 1e-15f, 1e-31f};
+    return factors[index];
+  }
+
+  static T forge(uint32_t bits) {
+    union {
+      uint32_t integerBits;
+      T floatBits;
+    };
+    integerBits = bits;
+    return floatBits;
+  }
+
+  static T nan() {
+    return forge(0x7fc00000);
+  }
+
+  static T inf() {
+    return forge(0x7f800000);
+  }
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsArray.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsArray.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2599231158cef5cbb875ddd6442f633a5437e584
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsArray.hpp
@@ -0,0 +1,24 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that return the type T without the const modifier
+template <typename T>
+struct IsArray {
+  static const bool value = false;
+};
+template <typename T>
+struct IsArray<T[]> {
+  static const bool value = true;
+};
+template <typename T, size_t N>
+struct IsArray<T[N]> {
+  static const bool value = true;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsBaseOf.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsBaseOf.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bf24e965e1b51ff31faa2fe121edf3b8ef434f62
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsBaseOf.hpp
@@ -0,0 +1,27 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that returns true if Derived inherits from TBase is an
+// integral type.
+template <typename TBase, typename TDerived>
+class IsBaseOf {
+ protected:  // <- to avoid GCC's "all member functions in class are private"
+  typedef char Yes[1];
+  typedef char No[2];
+
+  static Yes &probe(const TBase *);
+  static No &probe(...);
+
+ public:
+  enum {
+    value = sizeof(probe(reinterpret_cast<TDerived *>(0))) == sizeof(Yes)
+  };
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsChar.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsChar.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d97cec2131c511eabb46abbd032a7504b869fcc6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsChar.hpp
@@ -0,0 +1,23 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "IsSame.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that returns true if T is a charater
+template <typename T>
+struct IsChar {
+  static const bool value = IsSame<T, char>::value ||
+                            IsSame<T, signed char>::value ||
+                            IsSame<T, unsigned char>::value;
+};
+
+template <typename T>
+struct IsChar<const T> : IsChar<T> {};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsConst.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsConst.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..512ee5ca065a84fe55db25077b8635cea2f28d4d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsConst.hpp
@@ -0,0 +1,21 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that return the type T without the const modifier
+template <typename T>
+struct IsConst {
+  static const bool value = false;
+};
+
+template <typename T>
+struct IsConst<const T> {
+  static const bool value = true;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e41a6824cddfd90c01bae1ebe7dc53b934bb4b78
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsFloatingPoint.hpp
@@ -0,0 +1,18 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "IsSame.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that returns true if T is a floating point type
+template <typename T>
+struct IsFloatingPoint {
+  static const bool value = IsSame<T, float>::value || IsSame<T, double>::value;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsIntegral.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsIntegral.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..17ae5f284ff57e1ba87c164e1c1d06df907a5fd1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsIntegral.hpp
@@ -0,0 +1,26 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "IsSame.hpp"
+#include "IsSignedIntegral.hpp"
+#include "IsUnsignedIntegral.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that returns true if T is an integral type.
+template <typename T>
+struct IsIntegral {
+  static const bool value = IsSignedIntegral<T>::value ||
+                            IsUnsignedIntegral<T>::value ||
+                            IsSame<T, char>::value;
+  // CAUTION: differs from std::is_integral as it doesn't include bool
+};
+
+template <typename T>
+struct IsIntegral<const T> : IsIntegral<T> {};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsSame.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsSame.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..06567c93b501de29fdcb8e138aaeb708bc7f2ccf
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsSame.hpp
@@ -0,0 +1,21 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that returns true if types T and U are the same.
+template <typename T, typename U>
+struct IsSame {
+  static const bool value = false;
+};
+
+template <typename T>
+struct IsSame<T, T> {
+  static const bool value = true;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7334eb9c73eb9c98a6b0d4c298fb80b398abc3ae
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsSignedIntegral.hpp
@@ -0,0 +1,28 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../Configuration.hpp"
+#include "IsSame.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that returns true if T is an integral type.
+template <typename T>
+struct IsSignedIntegral {
+  static const bool value =
+      IsSame<T, signed char>::value || IsSame<T, signed short>::value ||
+      IsSame<T, signed int>::value || IsSame<T, signed long>::value ||
+#if ARDUINOJSON_USE_LONG_LONG
+      IsSame<T, signed long long>::value ||
+#endif
+#if ARDUINOJSON_USE_INT64
+      IsSame<T, signed __int64>::value ||
+#endif
+      false;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..938423f5cd67de634a3572f439efc0caa9fe18e6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsUnsignedIntegral.hpp
@@ -0,0 +1,28 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "../Configuration.hpp"
+#include "IsSame.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that returns true if T is an integral type.
+template <typename T>
+struct IsUnsignedIntegral {
+  static const bool value =
+      IsSame<T, unsigned char>::value || IsSame<T, unsigned short>::value ||
+      IsSame<T, unsigned int>::value || IsSame<T, unsigned long>::value ||
+#if ARDUINOJSON_USE_LONG_LONG
+      IsSame<T, unsigned long long>::value ||
+#endif
+#if ARDUINOJSON_USE_INT64
+      IsSame<T, unsigned __int64>::value ||
+#endif
+      false;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsVariant.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsVariant.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8b299f7a0c1afbba3f8f8acfcbe52a30fd4cf50
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/IsVariant.hpp
@@ -0,0 +1,17 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+#include "IsBaseOf.hpp"
+
+namespace ArduinoJson {
+namespace Internals {
+
+class JsonVariantTag {};
+
+template <typename T>
+struct IsVariant : IsBaseOf<JsonVariantTag, T> {};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/RemoveConst.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/RemoveConst.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..39d4cb5a52b31aa7fa7e03ce2a2a2f098eb397fe
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/RemoveConst.hpp
@@ -0,0 +1,20 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that return the type T without the const modifier
+template <typename T>
+struct RemoveConst {
+  typedef T type;
+};
+template <typename T>
+struct RemoveConst<const T> {
+  typedef T type;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/RemoveReference.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/RemoveReference.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..395a12889d94a9d3535cca3c2c3c06a85d7e1f06
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/src/ArduinoJson/TypeTraits/RemoveReference.hpp
@@ -0,0 +1,20 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#pragma once
+
+namespace ArduinoJson {
+namespace Internals {
+
+// A meta-function that return the type T without the reference modifier.
+template <typename T>
+struct RemoveReference {
+  typedef T type;
+};
+template <typename T>
+struct RemoveReference<T&> {
+  typedef T type;
+};
+}
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4fee0ea1cad5cb2b05d75b7d5c7b4c22cf3b8def
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/CMakeLists.txt
@@ -0,0 +1,76 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)")
+	add_compile_options(
+		-pedantic
+		-Wall
+		-Wcast-align
+		-Wcast-qual
+		-Wconversion
+		-Wctor-dtor-privacy
+		-Wdisabled-optimization
+		-Werror
+		-Wextra
+		-Wformat=2
+		-Winit-self
+		-Wmissing-include-dirs
+		-Wnon-virtual-dtor
+		-Wold-style-cast
+		-Woverloaded-virtual
+		-Wparentheses
+		-Wredundant-decls
+		-Wshadow
+		-Wsign-promo
+		-Wstrict-aliasing
+		-Wstrict-overflow=5
+		-Wundef
+	)
+
+	if(NOT MINGW)
+		add_compile_options(
+			-std=c++98
+		)
+	endif()
+endif()
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+	add_compile_options(
+		-Wstrict-null-sentinel
+	)
+
+	if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.5)
+		add_compile_options(-Wlogical-op) # the flag exists in 4.4 but is buggy
+	endif()
+
+	if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.6)
+		add_compile_options(-Wnoexcept)
+	endif()
+endif()
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+	add_compile_options(
+		-Wc++11-compat
+		-Wdeprecated-register
+	)
+endif()
+
+if(MSVC)
+	add_definitions(-D_CRT_SECURE_NO_WARNINGS)
+	add_compile_options(
+		/W4 # Set warning level
+		/WX # Treats all compiler warnings as errors.
+	)
+endif()
+
+add_subdirectory(DynamicJsonBuffer)
+add_subdirectory(IntegrationTests)
+add_subdirectory(JsonArray)
+add_subdirectory(JsonBuffer)
+add_subdirectory(JsonObject)
+add_subdirectory(JsonVariant)
+add_subdirectory(JsonWriter)
+add_subdirectory(Misc)
+add_subdirectory(Polyfills)
+add_subdirectory(StaticJsonBuffer)
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9030f376032c9df3f63d443e70d6d710e7d95d96
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/CMakeLists.txt
@@ -0,0 +1,15 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(DynamicJsonBufferTests 
+	alloc.cpp
+	createArray.cpp
+	createObject.cpp
+	no_memory.cpp
+	size.cpp
+	startString.cpp
+)
+
+target_link_libraries(DynamicJsonBufferTests catch)
+add_test(DynamicJsonBuffer DynamicJsonBufferTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/alloc.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/alloc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c45e6866a41fa35d56e9578abad6ec819d6d6ea1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/alloc.cpp
@@ -0,0 +1,77 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <sstream>
+
+using namespace ArduinoJson::Internals;
+
+static bool isAligned(void* ptr) {
+  const size_t mask = sizeof(void*) - 1;
+  size_t addr = reinterpret_cast<size_t>(ptr);
+  return (addr & mask) == 0;
+}
+
+std::stringstream allocatorLog;
+
+struct SpyingAllocator : DefaultAllocator {
+  void* allocate(size_t n) {
+    allocatorLog << "A" << (n - DynamicJsonBuffer::EmptyBlockSize);
+    return DefaultAllocator::allocate(n);
+  }
+  void deallocate(void* p) {
+    allocatorLog << "F";
+    return DefaultAllocator::deallocate(p);
+  }
+};
+
+TEST_CASE("DynamicJsonBuffer::alloc()") {
+  SECTION("Returns different pointers") {
+    DynamicJsonBuffer buffer;
+    void* p1 = buffer.alloc(1);
+    void* p2 = buffer.alloc(2);
+    REQUIRE(p1 != p2);
+  }
+
+  SECTION("Doubles allocation size when full") {
+    allocatorLog.str("");
+    {
+      DynamicJsonBufferBase<SpyingAllocator> buffer(1);
+      buffer.alloc(1);
+      buffer.alloc(1);
+    }
+    REQUIRE(allocatorLog.str() == "A1A2FF");
+  }
+
+  SECTION("Resets allocation size after clear()") {
+    allocatorLog.str("");
+    {
+      DynamicJsonBufferBase<SpyingAllocator> buffer(1);
+      buffer.alloc(1);
+      buffer.alloc(1);
+      buffer.clear();
+      buffer.alloc(1);
+    }
+    REQUIRE(allocatorLog.str() == "A1A2FFA1F");
+  }
+
+  SECTION("Makes a big allocation when needed") {
+    allocatorLog.str("");
+    {
+      DynamicJsonBufferBase<SpyingAllocator> buffer(1);
+      buffer.alloc(42);
+    }
+    REQUIRE(allocatorLog.str() == "A42F");
+  }
+
+  SECTION("Alignment") {
+    // make room for two but not three
+    DynamicJsonBuffer tinyBuf(2 * sizeof(void*) + 1);
+
+    REQUIRE(isAligned(tinyBuf.alloc(1)));  // this on is aligned by design
+    REQUIRE(isAligned(tinyBuf.alloc(1)));  // this one fits in the first block
+    REQUIRE(isAligned(tinyBuf.alloc(1)));  // this one requires a new block
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/createArray.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/createArray.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..34ab07e97b35187f5eaaacf9d8002ea83c888d53
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/createArray.cpp
@@ -0,0 +1,28 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("DynamicJsonBuffer::createArray()") {
+  DynamicJsonBuffer jsonBuffer;
+  JsonArray &array = jsonBuffer.createArray();
+
+  SECTION("GrowsWithArray") {
+    REQUIRE(JSON_ARRAY_SIZE(0) == jsonBuffer.size());
+
+    array.add("hello");
+    REQUIRE(JSON_ARRAY_SIZE(1) == jsonBuffer.size());
+
+    array.add("world");
+    REQUIRE(JSON_ARRAY_SIZE(2) == jsonBuffer.size());
+  }
+
+  SECTION("CanAdd1000Values") {
+    for (size_t i = 1; i <= 1000; i++) {
+      array.add("hello");
+      REQUIRE(array.size() == i);
+    }
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/createObject.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/createObject.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2193a659bd591ed6ea4a4700e1753ac53563c6d3
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/createObject.cpp
@@ -0,0 +1,22 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("DynamicJsonBuffer::createObject()") {
+  DynamicJsonBuffer json;
+
+  JsonObject &obj = json.createObject();
+  REQUIRE(JSON_OBJECT_SIZE(0) == json.size());
+
+  obj["hello"] = 1;
+  REQUIRE(JSON_OBJECT_SIZE(1) == json.size());
+
+  obj["world"] = 2;
+  REQUIRE(JSON_OBJECT_SIZE(2) == json.size());
+
+  obj["world"] = 3;  // <- same key, should not grow
+  REQUIRE(JSON_OBJECT_SIZE(2) == json.size());
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/no_memory.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/no_memory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..824d968c3564eaffdb9252553a9fd6b0092af422
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/no_memory.cpp
@@ -0,0 +1,49 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+struct NoMemoryAllocator {
+  void* allocate(size_t) {
+    return NULL;
+  }
+  void deallocate(void*) {}
+};
+
+TEST_CASE("DynamicJsonBuffer no memory") {
+  DynamicJsonBufferBase<NoMemoryAllocator> _jsonBuffer;
+
+  SECTION("FixCodeCoverage") {
+    // call this function to fix code coverage
+    NoMemoryAllocator().deallocate(NULL);
+  }
+
+  SECTION("createArray()") {
+    REQUIRE_FALSE(_jsonBuffer.createArray().success());
+  }
+
+  SECTION("createObject()") {
+    REQUIRE_FALSE(_jsonBuffer.createObject().success());
+  }
+
+  SECTION("parseArray()") {
+    char json[] = "[]";
+    REQUIRE_FALSE(_jsonBuffer.parseArray(json).success());
+  }
+
+  SECTION("parseObject()") {
+    char json[] = "{}";
+    REQUIRE_FALSE(_jsonBuffer.parseObject(json).success());
+  }
+
+  SECTION("startString()") {
+    DynamicJsonBufferBase<NoMemoryAllocator>::String str =
+        _jsonBuffer.startString();
+    str.append('!');
+    REQUIRE(0 == str.c_str());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/size.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/size.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8ebba424c28cdb246ef0f3ad9e2ef97f56e77125
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/size.cpp
@@ -0,0 +1,27 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("DynamicJsonBuffer::size()") {
+  DynamicJsonBuffer buffer;
+
+  SECTION("Initial size is 0") {
+    REQUIRE(0 == buffer.size());
+  }
+
+  SECTION("Increases after alloc()") {
+    buffer.alloc(1);
+    REQUIRE(1U <= buffer.size());
+    buffer.alloc(1);
+    REQUIRE(2U <= buffer.size());
+  }
+
+  SECTION("Goes back to 0 after clear()") {
+    buffer.alloc(1);
+    buffer.clear();
+    REQUIRE(0 == buffer.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/startString.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/startString.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2e5bef7fed03870a1b8398bdf19340ffdbfcc464
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/DynamicJsonBuffer/startString.cpp
@@ -0,0 +1,47 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("DynamicJsonBuffer::startString()") {
+  SECTION("WorksWhenBufferIsBigEnough") {
+    DynamicJsonBuffer jsonBuffer(6);
+
+    DynamicJsonBuffer::String str = jsonBuffer.startString();
+    str.append('h');
+    str.append('e');
+    str.append('l');
+    str.append('l');
+    str.append('o');
+
+    REQUIRE(std::string("hello") == str.c_str());
+  }
+
+  SECTION("GrowsWhenBufferIsTooSmall") {
+    DynamicJsonBuffer jsonBuffer(5);
+
+    DynamicJsonBuffer::String str = jsonBuffer.startString();
+    str.append('h');
+    str.append('e');
+    str.append('l');
+    str.append('l');
+    str.append('o');
+
+    REQUIRE(std::string("hello") == str.c_str());
+  }
+
+  SECTION("SizeIncreases") {
+    DynamicJsonBuffer jsonBuffer(5);
+
+    DynamicJsonBuffer::String str = jsonBuffer.startString();
+    REQUIRE(0 == jsonBuffer.size());
+
+    str.append('h');
+    REQUIRE(1 == jsonBuffer.size());
+
+    str.c_str();
+    REQUIRE(2 == jsonBuffer.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/IntegrationTests/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/IntegrationTests/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..9294bbbf9848f99764ee764e4374a598a1a9ef70
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/IntegrationTests/CMakeLists.txt
@@ -0,0 +1,18 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(IntegrationTests 
+	gbathree.cpp
+	round_trip.cpp
+)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES "GNU")
+	target_compile_options(IntegrationTests
+		PUBLIC
+		-fsingle-precision-constant # issue 544
+	)
+endif()
+
+target_link_libraries(IntegrationTests catch)
+add_test(IntegrationTests IntegrationTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/IntegrationTests/gbathree.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/IntegrationTests/gbathree.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9a51442510a0fe5e8c108322b5bddeca5aefa202
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/IntegrationTests/gbathree.cpp
@@ -0,0 +1,208 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("Gbathree") {
+  DynamicJsonBuffer _buffer;
+
+  const JsonObject& _object = _buffer.parseObject(
+      "{\"protocol_name\":\"fluorescence\",\"repeats\":1,\"wait\":0,"
+      "\"averages\":1,\"measurements\":3,\"meas2_light\":15,\"meas1_"
+      "baseline\":0,\"act_light\":20,\"pulsesize\":25,\"pulsedistance\":"
+      "10000,\"actintensity1\":50,\"actintensity2\":255,\"measintensity\":"
+      "255,\"calintensity\":255,\"pulses\":[50,50,50],\"act\":[2,1,2,2],"
+      "\"red\":[2,2,2,2],\"detectors\":[[34,34,34,34],[34,34,34,34],[34,"
+      "34,34,34],[34,34,34,34]],\"alta\":[2,2,2,2],\"altb\":[2,2,2,2],"
+      "\"measlights\":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,"
+      "15,15]],\"measlights2\":[[15,15,15,15],[15,15,15,15],[15,15,15,15],"
+      "[15,15,15,15]],\"altc\":[2,2,2,2],\"altd\":[2,2,2,2]}");
+
+  SECTION("Success") {
+    REQUIRE(_object.success());
+  }
+
+  SECTION("ProtocolName") {
+    REQUIRE("fluorescence" == _object["protocol_name"]);
+  }
+
+  SECTION("Repeats") {
+    REQUIRE(1 == _object["repeats"]);
+  }
+
+  SECTION("Wait") {
+    REQUIRE(0 == _object["wait"]);
+  }
+
+  SECTION("Measurements") {
+    REQUIRE(3 == _object["measurements"]);
+  }
+
+  SECTION("Meas2_Light") {
+    REQUIRE(15 == _object["meas2_light"]);
+  }
+
+  SECTION("Meas1_Baseline") {
+    REQUIRE(0 == _object["meas1_baseline"]);
+  }
+
+  SECTION("Act_Light") {
+    REQUIRE(20 == _object["act_light"]);
+  }
+
+  SECTION("Pulsesize") {
+    REQUIRE(25 == _object["pulsesize"]);
+  }
+
+  SECTION("Pulsedistance") {
+    REQUIRE(10000 == _object["pulsedistance"]);
+  }
+
+  SECTION("Actintensity1") {
+    REQUIRE(50 == _object["actintensity1"]);
+  }
+
+  SECTION("Actintensity2") {
+    REQUIRE(255 == _object["actintensity2"]);
+  }
+
+  SECTION("Measintensity") {
+    REQUIRE(255 == _object["measintensity"]);
+  }
+
+  SECTION("Calintensity") {
+    REQUIRE(255 == _object["calintensity"]);
+  }
+
+  SECTION("Pulses") {
+    // "pulses":[50,50,50]
+
+    JsonArray& array = _object["pulses"];
+    REQUIRE(array.success());
+
+    REQUIRE(3 == array.size());
+
+    for (size_t i = 0; i < 3; i++) {
+      REQUIRE(50 == array[i]);
+    }
+  }
+
+  SECTION("Act") {
+    // "act":[2,1,2,2]
+
+    JsonArray& array = _object["act"];
+    REQUIRE(array.success());
+
+    REQUIRE(4 == array.size());
+    REQUIRE(2 == array[0]);
+    REQUIRE(1 == array[1]);
+    REQUIRE(2 == array[2]);
+    REQUIRE(2 == array[3]);
+  }
+
+  SECTION("Detectors") {
+    // "detectors":[[34,34,34,34],[34,34,34,34],[34,34,34,34],[34,34,34,34]]
+
+    JsonArray& array = _object["detectors"];
+    REQUIRE(array.success());
+    REQUIRE(4 == array.size());
+
+    for (size_t i = 0; i < 4; i++) {
+      JsonArray& nestedArray = array[i];
+      REQUIRE(4 == nestedArray.size());
+
+      for (size_t j = 0; j < 4; j++) {
+        REQUIRE(34 == nestedArray[j]);
+      }
+    }
+  }
+
+  SECTION("Alta") {
+    // alta:[2,2,2,2]
+
+    JsonArray& array = _object["alta"];
+    REQUIRE(array.success());
+
+    REQUIRE(4 == array.size());
+
+    for (size_t i = 0; i < 4; i++) {
+      REQUIRE(2 == array[i]);
+    }
+  }
+
+  SECTION("Altb") {
+    // altb:[2,2,2,2]
+
+    JsonArray& array = _object["altb"];
+    REQUIRE(array.success());
+
+    REQUIRE(4 == array.size());
+
+    for (size_t i = 0; i < 4; i++) {
+      REQUIRE(2 == array[i]);
+    }
+  }
+
+  SECTION("Measlights") {
+    // "measlights":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,15,15]]
+
+    JsonArray& array = _object["measlights"];
+    REQUIRE(array.success());
+    REQUIRE(4 == array.size());
+
+    for (size_t i = 0; i < 4; i++) {
+      JsonArray& nestedArray = array[i];
+
+      REQUIRE(4 == nestedArray.size());
+
+      for (size_t j = 0; j < 4; j++) {
+        REQUIRE(15 == nestedArray[j]);
+      }
+    }
+  }
+
+  SECTION("Measlights2") {
+    // "measlights2":[[15,15,15,15],[15,15,15,15],[15,15,15,15],[15,15,15,15]]
+
+    JsonArray& array = _object["measlights2"];
+    REQUIRE(array.success());
+    REQUIRE(4 == array.size());
+
+    for (size_t i = 0; i < 4; i++) {
+      JsonArray& nestedArray = array[i];
+      REQUIRE(4 == nestedArray.size());
+
+      for (size_t j = 0; j < 4; j++) {
+        REQUIRE(15 == nestedArray[j]);
+      }
+    }
+  }
+
+  SECTION("Altc") {
+    // altc:[2,2,2,2]
+
+    JsonArray& array = _object["altc"];
+    REQUIRE(array.success());
+
+    REQUIRE(4 == array.size());
+
+    for (size_t i = 0; i < 4; i++) {
+      REQUIRE(2 == array[i]);
+    }
+  }
+
+  SECTION("Altd") {
+    // altd:[2,2,2,2]
+
+    JsonArray& array = _object["altd"];
+    REQUIRE(array.success());
+
+    REQUIRE(4 == array.size());
+
+    for (size_t i = 0; i < 4; i++) {
+      REQUIRE(2 == array[i]);
+    }
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/IntegrationTests/round_trip.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/IntegrationTests/round_trip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ec8c0b0cb9efba898e5557f5d2ec2f7eb19aabcf
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/IntegrationTests/round_trip.cpp
@@ -0,0 +1,80 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+void check(std::string originalJson) {
+  DynamicJsonBuffer jb;
+
+  std::string prettyJson;
+  jb.parseObject(originalJson).prettyPrintTo(prettyJson);
+
+  std::string finalJson;
+  jb.parseObject(prettyJson).printTo(finalJson);
+
+  REQUIRE(originalJson == finalJson);
+}
+
+TEST_CASE("Round Trip: parse -> prettyPrint -> parse -> print") {
+  SECTION("OpenWeatherMap") {
+    check(
+        "{\"coord\":{\"lon\":145.77,\"lat\":-16.92},\"sys\":{\"type\":1,\"id\":"
+        "8166,\"message\":0.1222,\"country\":\"AU\",\"sunrise\":1414784325,"
+        "\"sunset\":1414830137},\"weather\":[{\"id\":801,\"main\":\"Clouds\","
+        "\"description\":\"few clouds\",\"icon\":\"02n\"}],\"base\":\"cmc "
+        "stations\",\"main\":{\"temp\":296.15,\"pressure\":1014,\"humidity\":"
+        "83,\"temp_min\":296.15,\"temp_max\":296.15},\"wind\":{\"speed\":2.22,"
+        "\"deg\":114.501},\"clouds\":{\"all\":20},\"dt\":1414846800,\"id\":"
+        "2172797,\"name\":\"Cairns\",\"cod\":200}");
+  }
+
+  SECTION("YahooQueryLanguage") {
+    check(
+        "{\"query\":{\"count\":40,\"created\":\"2014-11-01T14:16:49Z\","
+        "\"lang\":\"fr-FR\",\"results\":{\"item\":[{\"title\":\"Burkina army "
+        "backs Zida as interim leader\"},{\"title\":\"British jets intercept "
+        "Russian bombers\"},{\"title\":\"Doubts chip away at nation's most "
+        "trusted agencies\"},{\"title\":\"Cruise ship stuck off Norway, no "
+        "damage\"},{\"title\":\"U.S. military launches 10 air strikes in "
+        "Syria, Iraq\"},{\"title\":\"Blackout hits Bangladesh as line from "
+        "India fails\"},{\"title\":\"Burkina Faso president in Ivory Coast "
+        "after ouster\"},{\"title\":\"Kurds in Turkey rally to back city "
+        "besieged by IS\"},{\"title\":\"A majority of Scots would vote for "
+        "independence now:poll\"},{\"title\":\"Tunisia elections possible "
+        "model for region\"},{\"title\":\"Islamic State kills 85 more members "
+        "of Iraqi tribe\"},{\"title\":\"Iraqi officials:IS extremists line "
+        "up, kill 50\"},{\"title\":\"Burkina Faso army backs presidential "
+        "guard official to lead transition\"},{\"title\":\"Kurdish peshmerga "
+        "arrive with weapons in Syria's Kobani\"},{\"title\":\"Driver sought "
+        "in crash that killed 3 on Halloween\"},{\"title\":\"Ex-Marine arrives "
+        "in US after release from Mexico jail\"},{\"title\":\"UN panel "
+        "scrambling to finish climate report\"},{\"title\":\"Investigators, "
+        "Branson go to spacecraft crash site\"},{\"title\":\"Soldiers vie for "
+        "power after Burkina Faso president quits\"},{\"title\":\"For a man "
+        "without a party, turnout is big test\"},{\"title\":\"'We just had a "
+        "hunch':US marshals nab Eric Frein\"},{\"title\":\"Boko Haram leader "
+        "threatens to kill German hostage\"},{\"title\":\"Nurse free to move "
+        "about as restrictions eased\"},{\"title\":\"Former Burkina president "
+        "Compaore arrives in Ivory Coast:sources\"},{\"title\":\"Libyan port "
+        "rebel leader refuses to hand over oil ports to rival "
+        "group\"},{\"title\":\"Iraqi peshmerga fighters prepare for Syria "
+        "battle\"},{\"title\":\"1 Dem Senate candidate welcoming Obama's "
+        "help\"},{\"title\":\"Bikers cancel party after police recover "
+        "bar\"},{\"title\":\"New question in Texas:Can Davis survive "
+        "defeat?\"},{\"title\":\"Ukraine rebels to hold election, despite "
+        "criticism\"},{\"title\":\"Iraqi officials say Islamic State group "
+        "lines up, kills 50 tribesmen, women in Anbar "
+        "province\"},{\"title\":\"James rebounds, leads Cavaliers past "
+        "Bulls\"},{\"title\":\"UK warns travelers they could be terror "
+        "targets\"},{\"title\":\"Hello Kitty celebrates 40th "
+        "birthday\"},{\"title\":\"A look at people killed during space "
+        "missions\"},{\"title\":\"Nigeria's purported Boko Haram leader says "
+        "has 'married off' girls:AFP\"},{\"title\":\"Mexico orders immediate "
+        "release of Marine veteran\"},{\"title\":\"As election closes in, "
+        "Obama on center stage\"},{\"title\":\"Body of Zambian president "
+        "arrives home\"},{\"title\":\"South Africa arrests 2 Vietnamese for "
+        "poaching\"}]}}}");
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ec0e5206b508626e6a5e132c40368b24044f31fd
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/CMakeLists.txt
@@ -0,0 +1,21 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(JsonArrayTests 
+	add.cpp
+	basics.cpp
+	copyFrom.cpp
+	copyTo.cpp
+	invalid.cpp
+	iterator.cpp
+	prettyPrintTo.cpp
+	printTo.cpp
+	remove.cpp
+	set.cpp
+	size.cpp
+	subscript.cpp
+)
+
+target_link_libraries(JsonArrayTests catch)
+add_test(JsonArray JsonArrayTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/add.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/add.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2e10754eea236c5791b1761f4c6437541a6dd270
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/add.cpp
@@ -0,0 +1,110 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonArray::add()") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonArray& _array = _jsonBuffer.createArray();
+
+  SECTION("int") {
+    _array.add(123);
+    REQUIRE(123 == _array[0].as<int>());
+    REQUIRE(_array[0].is<int>());
+    REQUIRE(_array[0].is<double>());
+  }
+
+  SECTION("double") {
+    _array.add(123.45);
+    REQUIRE(123.45 == _array[0].as<double>());
+    REQUIRE(_array[0].is<double>());
+    REQUIRE_FALSE(_array[0].is<bool>());
+  }
+
+  SECTION("bool") {
+    _array.add(true);
+    REQUIRE(true == _array[0].as<bool>());
+    REQUIRE(_array[0].is<bool>());
+    REQUIRE_FALSE(_array[0].is<int>());
+  }
+
+  SECTION("const char*") {
+    const char* str = "hello";
+    _array.add(str);
+    REQUIRE(str == _array[0].as<std::string>());
+    REQUIRE(_array[0].is<const char*>());
+    REQUIRE_FALSE(_array[0].is<int>());
+  }
+
+  SECTION("nested array") {
+    JsonArray& arr = _jsonBuffer.createArray();
+
+    _array.add(arr);
+
+    REQUIRE(&arr == &_array[0].as<JsonArray&>());
+    REQUIRE(_array[0].is<JsonArray&>());
+    REQUIRE_FALSE(_array[0].is<int>());
+  }
+
+  SECTION("nested object") {
+    JsonObject& obj = _jsonBuffer.createObject();
+
+    _array.add(obj);
+
+    REQUIRE(&obj == &_array[0].as<JsonObject&>());
+    REQUIRE(_array[0].is<JsonObject&>());
+    REQUIRE_FALSE(_array[0].is<int>());
+  }
+
+  SECTION("array subscript") {
+    const char* str = "hello";
+    JsonArray& arr = _jsonBuffer.createArray();
+    arr.add(str);
+
+    _array.add(arr[0]);
+
+    REQUIRE(str == _array[0]);
+  }
+
+  SECTION("object subscript") {
+    const char* str = "hello";
+    JsonObject& obj = _jsonBuffer.createObject();
+    obj["x"] = str;
+
+    _array.add(obj["x"]);
+
+    REQUIRE(str == _array[0]);
+  }
+
+  SECTION("should not duplicate const char*") {
+    _array.add("world");
+    const size_t expectedSize = JSON_ARRAY_SIZE(1);
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate char*") {
+    _array.add(const_cast<char*>("world"));
+    const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate std::string") {
+    _array.add(std::string("world"));
+    const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should not duplicate RawJson(const char*)") {
+    _array.add(RawJson("{}"));
+    const size_t expectedSize = JSON_ARRAY_SIZE(1);
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate RawJson(char*)") {
+    _array.add(RawJson(const_cast<char*>("{}")));
+    const size_t expectedSize = JSON_ARRAY_SIZE(1) + 3;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/basics.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/basics.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eada17e25e83956258b5e01a5eadbf7e04f88512
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/basics.cpp
@@ -0,0 +1,29 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonArray basics") {
+  DynamicJsonBuffer jb;
+  JsonArray& array = jb.createArray();
+
+  SECTION("SuccessIsTrue") {
+    REQUIRE(array.success());
+  }
+
+  SECTION("InitialSizeIsZero") {
+    REQUIRE(0U == array.size());
+  }
+
+  SECTION("CreateNestedArray") {
+    JsonArray& arr = array.createNestedArray();
+    REQUIRE(&arr == &array[0].as<JsonArray&>());
+  }
+
+  SECTION("CreateNestedObject") {
+    JsonObject& obj = array.createNestedObject();
+    REQUIRE(&obj == &array[0].as<JsonObject&>());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/copyFrom.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/copyFrom.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..850301a7e8e22b8f494f104a6a00fa6cdd5dac8f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/copyFrom.cpp
@@ -0,0 +1,63 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonArray::copyFrom()") {
+  SECTION("OneDimension") {
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& array = jsonBuffer.createArray();
+    char json[32];
+    int source[] = {1, 2, 3};
+
+    bool ok = array.copyFrom(source);
+    REQUIRE(ok);
+
+    array.printTo(json, sizeof(json));
+    REQUIRE(std::string("[1,2,3]") == json);
+  }
+
+  SECTION("OneDimension_JsonBufferTooSmall") {
+    const size_t SIZE = JSON_ARRAY_SIZE(2);
+    StaticJsonBuffer<SIZE> jsonBuffer;
+    JsonArray& array = jsonBuffer.createArray();
+    char json[32];
+    int source[] = {1, 2, 3};
+
+    bool ok = array.copyFrom(source);
+    REQUIRE_FALSE(ok);
+
+    array.printTo(json, sizeof(json));
+    REQUIRE(std::string("[1,2]") == json);
+  }
+
+  SECTION("TwoDimensions") {
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& array = jsonBuffer.createArray();
+    char json[32];
+    int source[][3] = {{1, 2, 3}, {4, 5, 6}};
+
+    bool ok = array.copyFrom(source);
+    REQUIRE(ok);
+
+    array.printTo(json, sizeof(json));
+    REQUIRE(std::string("[[1,2,3],[4,5,6]]") == json);
+  }
+
+  SECTION("TwoDimensions_JsonBufferTooSmall") {
+    const size_t SIZE =
+        JSON_ARRAY_SIZE(2) + JSON_ARRAY_SIZE(3) + JSON_ARRAY_SIZE(2);
+    StaticJsonBuffer<SIZE> jsonBuffer;
+    JsonArray& array = jsonBuffer.createArray();
+    char json[32];
+    int source[][3] = {{1, 2, 3}, {4, 5, 6}};
+
+    bool ok = array.copyFrom(source);
+    REQUIRE_FALSE(ok);
+
+    array.printTo(json, sizeof(json));
+    REQUIRE(std::string("[[1,2,3],[4,5]]") == json);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/copyTo.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/copyTo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..90150590205423ee302df6e9cb35b3e83d853970
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/copyTo.cpp
@@ -0,0 +1,52 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonArray::copyTo()") {
+  DynamicJsonBuffer jsonBuffer;
+
+  SECTION("BiggerOneDimensionIntegerArray") {
+    char json[] = "[1,2,3]";
+    JsonArray& array = jsonBuffer.parseArray(json);
+
+    int destination[4] = {0};
+    size_t result = array.copyTo(destination);
+
+    REQUIRE(3 == result);
+    REQUIRE(1 == destination[0]);
+    REQUIRE(2 == destination[1]);
+    REQUIRE(3 == destination[2]);
+    REQUIRE(0 == destination[3]);
+  }
+
+  SECTION("SmallerOneDimensionIntegerArray") {
+    char json[] = "[1,2,3]";
+    JsonArray& array = jsonBuffer.parseArray(json);
+
+    int destination[2] = {0};
+    size_t result = array.copyTo(destination);
+
+    REQUIRE(2 == result);
+    REQUIRE(1 == destination[0]);
+    REQUIRE(2 == destination[1]);
+  }
+
+  SECTION("TwoOneDimensionIntegerArray") {
+    char json[] = "[[1,2],[3],[4]]";
+
+    JsonArray& array = jsonBuffer.parseArray(json);
+
+    int destination[3][2] = {{0}};
+    array.copyTo(destination);
+
+    REQUIRE(1 == destination[0][0]);
+    REQUIRE(2 == destination[0][1]);
+    REQUIRE(3 == destination[1][0]);
+    REQUIRE(0 == destination[1][1]);
+    REQUIRE(4 == destination[2][0]);
+    REQUIRE(0 == destination[2][1]);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/invalid.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/invalid.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..045517babeb378fb860faa5910fba64af9403fec
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/invalid.cpp
@@ -0,0 +1,34 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace Catch::Matchers;
+
+TEST_CASE("JsonArray::invalid()") {
+  SECTION("SubscriptFails") {
+    REQUIRE_FALSE(JsonArray::invalid()[0].success());
+  }
+
+  SECTION("AddFails") {
+    JsonArray& array = JsonArray::invalid();
+    array.add(1);
+    REQUIRE(0 == array.size());
+  }
+
+  SECTION("CreateNestedArrayFails") {
+    REQUIRE_FALSE(JsonArray::invalid().createNestedArray().success());
+  }
+
+  SECTION("CreateNestedObjectFails") {
+    REQUIRE_FALSE(JsonArray::invalid().createNestedObject().success());
+  }
+
+  SECTION("PrintToWritesBrackets") {
+    char buffer[32];
+    JsonArray::invalid().printTo(buffer, sizeof(buffer));
+    REQUIRE_THAT(buffer, Equals("[]"));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/iterator.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/iterator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fff8196450cb2abe11393029d7c488f11e2a56bf
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/iterator.cpp
@@ -0,0 +1,38 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+template <typename TIterator>
+static void run_iterator_test() {
+  StaticJsonBuffer<JSON_ARRAY_SIZE(2)> jsonBuffer;
+
+  JsonArray &array = jsonBuffer.createArray();
+  array.add(12);
+  array.add(34);
+
+  TIterator it = array.begin();
+  TIterator end = array.end();
+
+  REQUIRE(end != it);
+  REQUIRE(12 == it->template as<int>());
+  REQUIRE(12 == static_cast<int>(*it));
+  ++it;
+  REQUIRE(end != it);
+  REQUIRE(34 == it->template as<int>());
+  REQUIRE(34 == static_cast<int>(*it));
+  ++it;
+  REQUIRE(end == it);
+}
+
+TEST_CASE("JsonArray::begin()/end()") {
+  SECTION("Mutable") {
+    run_iterator_test<JsonArray::iterator>();
+  }
+
+  SECTION("Const") {
+    run_iterator_test<JsonArray::const_iterator>();
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/prettyPrintTo.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/prettyPrintTo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc5c526da7dcdaa9fb05f00751aa4a2d71e2fe6d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/prettyPrintTo.cpp
@@ -0,0 +1,75 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+static void check(JsonArray& array, std::string expected) {
+  std::string actual;
+  size_t actualLen = array.prettyPrintTo(actual);
+  size_t measuredLen = array.measurePrettyLength();
+  CHECK(actualLen == expected.size());
+  CHECK(measuredLen == expected.size());
+  REQUIRE(expected == actual);
+}
+
+TEST_CASE("JsonArray::prettyPrintTo()") {
+  DynamicJsonBuffer jb;
+  JsonArray& array = jb.createArray();
+
+  SECTION("Empty") {
+    check(array, "[]");
+  }
+
+  SECTION("OneElement") {
+    array.add(1);
+
+    check(array,
+          "[\r\n"
+          "  1\r\n"
+          "]");
+  }
+
+  SECTION("TwoElements") {
+    array.add(1);
+    array.add(2);
+
+    check(array,
+          "[\r\n"
+          "  1,\r\n"
+          "  2\r\n"
+          "]");
+  }
+
+  SECTION("EmptyNestedArrays") {
+    array.createNestedArray();
+    array.createNestedArray();
+
+    check(array,
+          "[\r\n"
+          "  [],\r\n"
+          "  []\r\n"
+          "]");
+  }
+
+  SECTION("NestedArrays") {
+    JsonArray& nested1 = array.createNestedArray();
+    nested1.add(1);
+    nested1.add(2);
+
+    JsonObject& nested2 = array.createNestedObject();
+    nested2["key"] = 3;
+
+    check(array,
+          "[\r\n"
+          "  [\r\n"
+          "    1,\r\n"
+          "    2\r\n"
+          "  ],\r\n"
+          "  {\r\n"
+          "    \"key\": 3\r\n"
+          "  }\r\n"
+          "]");
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/printTo.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/printTo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6b7f003c67358710ae7894316f2f98ec62fb6656
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/printTo.cpp
@@ -0,0 +1,132 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+static void check(JsonArray &array, std::string expected) {
+  std::string actual;
+  size_t actualLen = array.printTo(actual);
+  REQUIRE(expected == actual);
+  REQUIRE(actualLen == expected.size());
+  size_t measuredLen = array.measureLength();
+  REQUIRE(measuredLen == expected.size());
+}
+
+TEST_CASE("JsonArray::printTo()") {
+  StaticJsonBuffer<JSON_ARRAY_SIZE(2)> jb;
+  JsonArray &array = jb.createArray();
+
+  SECTION("Empty") {
+    check(array, "[]");
+  }
+
+  SECTION("Null") {
+    array.add(static_cast<char *>(0));
+
+    check(array, "[null]");
+  }
+
+  SECTION("OneString") {
+    array.add("hello");
+
+    check(array, "[\"hello\"]");
+  }
+
+  SECTION("TwoStrings") {
+    array.add("hello");
+    array.add("world");
+
+    check(array, "[\"hello\",\"world\"]");
+  }
+
+  SECTION("OneStringOverCapacity") {
+    array.add("hello");
+    array.add("world");
+    array.add("lost");
+
+    check(array, "[\"hello\",\"world\"]");
+  }
+
+  SECTION("One double") {
+    array.add(3.1415927);
+    check(array, "[3.1415927]");
+  }
+
+  SECTION("OneInteger") {
+    array.add(1);
+
+    check(array, "[1]");
+  }
+
+  SECTION("TwoIntegers") {
+    array.add(1);
+    array.add(2);
+
+    check(array, "[1,2]");
+  }
+
+  SECTION("RawJson(const char*)") {
+    array.add(RawJson("{\"key\":\"value\"}"));
+
+    check(array, "[{\"key\":\"value\"}]");
+  }
+
+  SECTION("RawJson(char*)") {
+    DynamicJsonBuffer jb2;
+    JsonArray &arr = jb2.createArray();
+
+    char tmp[] = "{\"key\":\"value\"}";
+    arr.add(RawJson(tmp));
+
+    check(arr, "[{\"key\":\"value\"}]");
+  }
+
+  SECTION("OneIntegerOverCapacity") {
+    array.add(1);
+    array.add(2);
+    array.add(3);
+
+    check(array, "[1,2]");
+  }
+
+  SECTION("OneTrue") {
+    array.add(true);
+
+    check(array, "[true]");
+  }
+
+  SECTION("OneFalse") {
+    array.add(false);
+
+    check(array, "[false]");
+  }
+
+  SECTION("TwoBooleans") {
+    array.add(false);
+    array.add(true);
+
+    check(array, "[false,true]");
+  }
+
+  SECTION("OneBooleanOverCapacity") {
+    array.add(false);
+    array.add(true);
+    array.add(false);
+
+    check(array, "[false,true]");
+  }
+
+  SECTION("OneEmptyNestedArray") {
+    array.createNestedArray();
+
+    check(array, "[[]]");
+  }
+
+  SECTION("OneEmptyNestedHash") {
+    array.createNestedObject();
+
+    check(array, "[{}]");
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/remove.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/remove.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..089358183f2a925422d1837c5cfbd0e6d7923bdc
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/remove.cpp
@@ -0,0 +1,68 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonArray::remove()") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonArray& _array = _jsonBuffer.createArray();
+  _array.add(1);
+  _array.add(2);
+  _array.add(3);
+
+  SECTION("RemoveFirstByIndex") {
+    _array.remove(0);
+
+    REQUIRE(2 == _array.size());
+    REQUIRE(_array[0] == 2);
+    REQUIRE(_array[1] == 3);
+  }
+
+  SECTION("RemoveMiddleByIndex") {
+    _array.remove(1);
+
+    REQUIRE(2 == _array.size());
+    REQUIRE(_array[0] == 1);
+    REQUIRE(_array[1] == 3);
+  }
+
+  SECTION("RemoveLastByIndex") {
+    _array.remove(2);
+
+    REQUIRE(2 == _array.size());
+    REQUIRE(_array[0] == 1);
+    REQUIRE(_array[1] == 2);
+  }
+
+  SECTION("RemoveFirstByIterator") {
+    JsonArray::iterator it = _array.begin();
+    _array.remove(it);
+
+    REQUIRE(2 == _array.size());
+    REQUIRE(_array[0] == 2);
+    REQUIRE(_array[1] == 3);
+  }
+
+  SECTION("RemoveMiddleByIterator") {
+    JsonArray::iterator it = _array.begin();
+    ++it;
+    _array.remove(it);
+
+    REQUIRE(2 == _array.size());
+    REQUIRE(_array[0] == 1);
+    REQUIRE(_array[1] == 3);
+  }
+
+  SECTION("RemoveLastByIterator") {
+    JsonArray::iterator it = _array.begin();
+    ++it;
+    ++it;
+    _array.remove(it);
+
+    REQUIRE(2 == _array.size());
+    REQUIRE(_array[0] == 1);
+    REQUIRE(_array[1] == 2);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/set.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/set.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..901f9054a3edff63cab5944770860b0d0e730e4f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/set.cpp
@@ -0,0 +1,98 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace Catch::Matchers;
+
+TEST_CASE("JsonArray::set()") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonArray& _array = _jsonBuffer.createArray();
+  _array.add(0);
+
+  SECTION("int") {
+    _array.set(0, 123);
+    REQUIRE(123 == _array[0].as<int>());
+    REQUIRE(_array[0].is<int>());
+    REQUIRE_FALSE(_array[0].is<bool>());
+  }
+
+  SECTION("double") {
+    _array.set(0, 123.45);
+    REQUIRE(123.45 == _array[0].as<double>());
+    REQUIRE(_array[0].is<double>());
+    REQUIRE_FALSE(_array[0].is<int>());
+  }
+
+  SECTION("bool") {
+    _array.set(0, true);
+    REQUIRE(true == _array[0].as<bool>());
+    REQUIRE(_array[0].is<bool>());
+    REQUIRE_FALSE(_array[0].is<int>());
+  }
+
+  SECTION("const char*") {
+    _array.set(0, "hello");
+    REQUIRE_THAT(_array[0].as<const char*>(), Equals("hello"));
+    REQUIRE(_array[0].is<const char*>());
+    REQUIRE_FALSE(_array[0].is<int>());
+  }
+
+  SECTION("nested array") {
+    JsonArray& arr = _jsonBuffer.createArray();
+
+    _array.set(0, arr);
+
+    REQUIRE(&arr == &_array[0].as<JsonArray&>());
+    REQUIRE(_array[0].is<JsonArray&>());
+    REQUIRE_FALSE(_array[0].is<int>());
+  }
+
+  SECTION("nested object") {
+    JsonObject& obj = _jsonBuffer.createObject();
+
+    _array.set(0, obj);
+
+    REQUIRE(&obj == &_array[0].as<JsonObject&>());
+    REQUIRE(_array[0].is<JsonObject&>());
+    REQUIRE_FALSE(_array[0].is<int>());
+  }
+
+  SECTION("array subscript") {
+    JsonArray& arr = _jsonBuffer.createArray();
+    arr.add("hello");
+
+    _array.set(0, arr[0]);
+
+    REQUIRE_THAT(_array[0].as<char*>(), Equals("hello"));
+  }
+
+  SECTION("object subscript") {
+    JsonObject& obj = _jsonBuffer.createObject();
+    obj["x"] = "hello";
+
+    _array.set(0, obj["x"]);
+
+    REQUIRE_THAT(_array[0].as<char*>(), Equals("hello"));
+  }
+
+  SECTION("should not duplicate const char*") {
+    _array.set(0, "world");
+    const size_t expectedSize = JSON_ARRAY_SIZE(1);
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate char*") {
+    _array.set(0, const_cast<char*>("world"));
+    const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate std::string") {
+    _array.set(0, std::string("world"));
+    const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/size.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/size.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c45fe30ea09745584892626e2ea514f843892d69
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/size.cpp
@@ -0,0 +1,35 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonArray::size()") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonArray& _array = _jsonBuffer.createArray();
+
+  SECTION("increases after add()") {
+    _array.add("hello");
+    REQUIRE(1U == _array.size());
+
+    _array.add("world");
+    REQUIRE(2U == _array.size());
+  }
+
+  SECTION("remains the same after set()") {
+    _array.add("hello");
+    REQUIRE(1U == _array.size());
+
+    _array.set(0, "hello");
+    REQUIRE(1U == _array.size());
+  }
+
+  SECTION("remains the same after assigment") {
+    _array.add("hello");
+    REQUIRE(1U == _array.size());
+
+    _array[0] = "hello";
+    REQUIRE(1U == _array.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/subscript.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/subscript.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..69893c53c2bafdd58996999e97ad6d9da7b9a750
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonArray/subscript.cpp
@@ -0,0 +1,119 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <stdint.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonArray::operator[]") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonArray& _array = _jsonBuffer.createArray();
+  _array.add(0);
+
+  SECTION("int") {
+    _array[0] = 123;
+    REQUIRE(123 == _array[0].as<int>());
+    REQUIRE(true == _array[0].is<int>());
+    REQUIRE(false == _array[0].is<bool>());
+  }
+
+#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
+  SECTION("long long") {
+    _array[0] = 9223372036854775807;
+    REQUIRE(9223372036854775807 == _array[0].as<long long>());
+    REQUIRE(true == _array[0].is<int>());
+    REQUIRE(false == _array[0].is<bool>());
+  }
+#endif
+
+  SECTION("double") {
+    _array[0] = 123.45;
+    REQUIRE(123.45 == _array[0].as<double>());
+    REQUIRE(true == _array[0].is<double>());
+    REQUIRE(false == _array[0].is<int>());
+  }
+
+  SECTION("bool") {
+    _array[0] = true;
+    REQUIRE(true == _array[0].as<bool>());
+    REQUIRE(true == _array[0].is<bool>());
+    REQUIRE(false == _array[0].is<int>());
+  }
+
+  SECTION("const char*") {
+    const char* str = "hello";
+
+    _array[0] = str;
+    REQUIRE(str == _array[0].as<const char*>());
+    REQUIRE(str == _array[0].as<char*>());  // <- short hand
+    REQUIRE(true == _array[0].is<const char*>());
+    REQUIRE(false == _array[0].is<int>());
+  }
+
+  SECTION("nested array") {
+    JsonArray& arr = _jsonBuffer.createArray();
+
+    _array[0] = arr;
+
+    REQUIRE(&arr == &_array[0].as<JsonArray&>());
+    REQUIRE(&arr == &_array[0].as<JsonArray>());  // <- short hand
+    REQUIRE(&arr == &_array[0].as<const JsonArray&>());
+    REQUIRE(&arr == &_array[0].as<const JsonArray>());  // <- short hand
+    REQUIRE(true == _array[0].is<JsonArray&>());
+    REQUIRE(false == _array[0].is<int>());
+  }
+
+  SECTION("nested object") {
+    JsonObject& obj = _jsonBuffer.createObject();
+
+    _array[0] = obj;
+
+    REQUIRE(&obj == &_array[0].as<JsonObject&>());
+    REQUIRE(&obj == &_array[0].as<JsonObject>());  // <- short hand
+    REQUIRE(&obj == &_array[0].as<const JsonObject&>());
+    REQUIRE(&obj == &_array[0].as<const JsonObject>());  // <- short hand
+    REQUIRE(true == _array[0].is<JsonObject&>());
+    REQUIRE(false == _array[0].is<int>());
+  }
+
+  SECTION("array subscript") {
+    JsonArray& arr = _jsonBuffer.createArray();
+    const char* str = "hello";
+
+    arr.add(str);
+
+    _array[0] = arr[0];
+
+    REQUIRE(str == _array[0]);
+  }
+
+  SECTION("object subscript") {
+    JsonObject& obj = _jsonBuffer.createObject();
+    const char* str = "hello";
+
+    obj["x"] = str;
+
+    _array[0] = obj["x"];
+
+    REQUIRE(str == _array[0]);
+  }
+
+  SECTION("should not duplicate const char*") {
+    _array[0] = "world";
+    const size_t expectedSize = JSON_ARRAY_SIZE(1);
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate char*") {
+    _array[0] = const_cast<char*>("world");
+    const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate std::string") {
+    _array[0] = std::string("world");
+    const size_t expectedSize = JSON_ARRAY_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..34b3e735a817ad6548420ac460322fd114caa473
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/CMakeLists.txt
@@ -0,0 +1,14 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(JsonBufferTests
+	nested.cpp
+	nestingLimit.cpp
+	parse.cpp
+	parseArray.cpp
+	parseObject.cpp
+)
+
+target_link_libraries(JsonBufferTests catch)
+add_test(JsonBuffer JsonBufferTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/nested.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/nested.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..263e40e640045d76b2a15aa833536949b9297266
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/nested.cpp
@@ -0,0 +1,63 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonBuffer nested objects") {
+  SECTION("ArrayNestedInObject") {
+    DynamicJsonBuffer jsonBuffer;
+    char jsonString[] = " { \"ab\" : [ 1 , 2 ] , \"cd\" : [ 3 , 4 ] } ";
+
+    JsonObject &object = jsonBuffer.parseObject(jsonString);
+    JsonArray &array1 = object["ab"];
+    const JsonArray &array2 = object["cd"];
+    JsonArray &array3 = object["ef"];
+
+    REQUIRE(true == object.success());
+
+    REQUIRE(true == array1.success());
+    REQUIRE(true == array2.success());
+    REQUIRE(false == array3.success());
+
+    REQUIRE(2 == array1.size());
+    REQUIRE(2 == array2.size());
+    REQUIRE(0 == array3.size());
+
+    REQUIRE(1 == array1[0].as<int>());
+    REQUIRE(2 == array1[1].as<int>());
+
+    REQUIRE(3 == array2[0].as<int>());
+    REQUIRE(4 == array2[1].as<int>());
+
+    REQUIRE(0 == array3[0].as<int>());
+  }
+
+  SECTION("ObjectNestedInArray") {
+    DynamicJsonBuffer jsonBuffer;
+    char jsonString[] =
+        " [ { \"a\" : 1 , \"b\" : 2 } , { \"c\" : 3 , \"d\" : 4 } ] ";
+
+    JsonArray &array = jsonBuffer.parseArray(jsonString);
+    JsonObject &object1 = array[0];
+    const JsonObject &object2 = array[1];
+    JsonObject &object3 = array[2];
+
+    REQUIRE(true == array.success());
+
+    REQUIRE(true == object1.success());
+    REQUIRE(true == object2.success());
+    REQUIRE(false == object3.success());
+
+    REQUIRE(2 == object1.size());
+    REQUIRE(2 == object2.size());
+    REQUIRE(0 == object3.size());
+
+    REQUIRE(1 == object1["a"].as<int>());
+    REQUIRE(2 == object1["b"].as<int>());
+    REQUIRE(3 == object2["c"].as<int>());
+    REQUIRE(4 == object2["d"].as<int>());
+    REQUIRE(0 == object3["e"].as<int>());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/nestingLimit.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/nestingLimit.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0d7be11513bdb64f7f6ae71ca36c3798357d80ee
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/nestingLimit.cpp
@@ -0,0 +1,48 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+bool tryParseArray(const char *json, uint8_t nestingLimit) {
+  DynamicJsonBuffer buffer;
+  return buffer.parseArray(json, nestingLimit).success();
+}
+
+bool tryParseObject(const char *json, uint8_t nestingLimit) {
+  DynamicJsonBuffer buffer;
+  return buffer.parseObject(json, nestingLimit).success();
+}
+
+TEST_CASE("JsonParser nestingLimit") {
+  SECTION("ParseArrayWithNestingLimit0") {
+    REQUIRE(true == tryParseArray("[]", 0));
+    REQUIRE(false == tryParseArray("[[]]", 0));
+  }
+
+  SECTION("ParseArrayWithNestingLimit1") {
+    REQUIRE(true == tryParseArray("[[]]", 1));
+    REQUIRE(false == tryParseArray("[[[]]]", 1));
+  }
+
+  SECTION("ParseArrayWithNestingLimit2") {
+    REQUIRE(true == tryParseArray("[[[]]]", 2));
+    REQUIRE(false == tryParseArray("[[[[]]]]", 2));
+  }
+
+  SECTION("ParseObjectWithNestingLimit0") {
+    REQUIRE(true == tryParseObject("{}", 0));
+    REQUIRE(false == tryParseObject("{\"key\":{}}", 0));
+  }
+
+  SECTION("ParseObjectWithNestingLimit1") {
+    REQUIRE(true == tryParseObject("{\"key\":{}}", 1));
+    REQUIRE(false == tryParseObject("{\"key\":{\"key\":{}}}", 1));
+  }
+
+  SECTION("ParseObjectWithNestingLimit2") {
+    REQUIRE(true == tryParseObject("{\"key\":{\"key\":{}}}", 2));
+    REQUIRE(false == tryParseObject("{\"key\":{\"key\":{\"key\":{}}}}", 2));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/parse.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/parse.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..70e57918c41cff8df18b236933ad1ed6416e4462
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/parse.cpp
@@ -0,0 +1,80 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace Catch::Matchers;
+
+TEST_CASE("JsonBuffer::parse()") {
+  DynamicJsonBuffer jb;
+
+  SECTION("EmptyObject") {
+    JsonVariant variant = jb.parse("{}");
+    REQUIRE(variant.success());
+    REQUIRE(variant.is<JsonObject>());
+  }
+
+  SECTION("EmptyArray") {
+    JsonVariant variant = jb.parse("[]");
+    REQUIRE(variant.success());
+    REQUIRE(variant.is<JsonArray>());
+  }
+
+  SECTION("Integer") {
+    JsonVariant variant = jb.parse("-42");
+    REQUIRE(variant.success());
+    REQUIRE(variant.is<int>());
+    REQUIRE_FALSE(variant.is<bool>());
+    REQUIRE(variant == -42);
+  }
+
+  SECTION("Double") {
+    JsonVariant variant = jb.parse("-1.23e+4");
+    REQUIRE(variant.success());
+    REQUIRE_FALSE(variant.is<int>());
+    REQUIRE(variant.is<double>());
+    REQUIRE(variant.as<double>() == Approx(-1.23e+4));
+  }
+
+  SECTION("Double quoted string") {
+    JsonVariant variant = jb.parse("\"hello world\"");
+    REQUIRE(variant.success());
+    REQUIRE(variant.is<char*>());
+    REQUIRE_THAT(variant.as<char*>(), Equals("hello world"));
+  }
+
+  SECTION("Single quoted string") {
+    JsonVariant variant = jb.parse("\'hello world\'");
+    REQUIRE(variant.success());
+    REQUIRE(variant.is<char*>());
+    REQUIRE_THAT(variant.as<char*>(), Equals("hello world"));
+  }
+
+  SECTION("True") {
+    JsonVariant variant = jb.parse("true");
+    REQUIRE(variant.success());
+    REQUIRE(variant.is<bool>());
+    REQUIRE(variant == true);
+  }
+
+  SECTION("False") {
+    JsonVariant variant = jb.parse("false");
+    REQUIRE(variant.success());
+    REQUIRE(variant.is<bool>());
+    REQUIRE(variant == false);
+  }
+
+  SECTION("OpenBrace") {
+    JsonVariant variant = jb.parse("{");
+    REQUIRE_FALSE(variant.success());
+  }
+
+  SECTION("Incomplete string") {
+    JsonVariant variant = jb.parse("\"hello");
+    REQUIRE(variant.success());
+    REQUIRE(variant.is<char*>());
+    REQUIRE_THAT(variant.as<char*>(), Equals("hello"));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/parseArray.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/parseArray.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2e843f8cfc6478eac8e0cb45a71e556790ed5a14
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/parseArray.cpp
@@ -0,0 +1,318 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonBuffer::parseArray()") {
+  DynamicJsonBuffer jb;
+
+  SECTION("EmptyArray") {
+    JsonArray& arr = jb.parseArray("[]");
+
+    REQUIRE(arr.success());
+    REQUIRE(0 == arr.size());
+  }
+
+  SECTION("MissingOpeningBracket") {
+    JsonArray& arr = jb.parseArray("]");
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("ArrayWithNoEnd") {
+    JsonArray& arr = jb.parseArray("[");
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("EmptyArrayWithLeadingSpaces") {
+    JsonArray& arr = jb.parseArray("  []");
+
+    REQUIRE(arr.success());
+    REQUIRE(0 == arr.size());
+  }
+
+  SECTION("Garbage") {
+    JsonArray& arr = jb.parseArray("%*$£¤");
+
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("OneInteger") {
+    JsonArray& arr = jb.parseArray("[42]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == 42);
+  }
+
+  SECTION("OneIntegerWithSpacesBefore") {
+    JsonArray& arr = jb.parseArray("[ \t\r\n42]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == 42);
+  }
+
+  SECTION("OneIntegerWithSpaceAfter") {
+    JsonArray& arr = jb.parseArray("[42 \t\r\n]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == 42);
+  }
+
+  SECTION("TwoIntegers") {
+    JsonArray& arr = jb.parseArray("[42,84]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == 42);
+    REQUIRE(arr[1] == 84);
+  }
+
+  SECTION("TwoDoubles") {
+    JsonArray& arr = jb.parseArray("[4.2,1e2]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == 4.2);
+    REQUIRE(arr[1] == 1e2);
+  }
+
+  SECTION("UnsignedLong") {
+    JsonArray& arr = jb.parseArray("[4294967295]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == 4294967295UL);
+  }
+
+  SECTION("TwoBooleans") {
+    JsonArray& arr = jb.parseArray("[true,false]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == true);
+    REQUIRE(arr[1] == false);
+  }
+
+  SECTION("TwoNulls") {
+    JsonArray& arr = jb.parseArray("[null,null]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0].as<char*>() == 0);
+    REQUIRE(arr[1].as<char*>() == 0);
+  }
+
+  SECTION("TwoStringsDoubleQuotes") {
+    JsonArray& arr = jb.parseArray("[ \"hello\" , \"world\" ]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "hello");
+    REQUIRE(arr[1] == "world");
+  }
+
+  SECTION("TwoStringsSingleQuotes") {
+    JsonArray& arr = jb.parseArray("[ 'hello' , 'world' ]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "hello");
+    REQUIRE(arr[1] == "world");
+  }
+
+  SECTION("TwoStringsNoQuotes") {
+    JsonArray& arr = jb.parseArray("[ hello , world ]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "hello");
+    REQUIRE(arr[1] == "world");
+  }
+
+  SECTION("EmptyStringsDoubleQuotes") {
+    JsonArray& arr = jb.parseArray("[\"\",\"\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "");
+    REQUIRE(arr[1] == "");
+  }
+
+  SECTION("EmptyStringSingleQuotes") {
+    JsonArray& arr = jb.parseArray("[\'\',\'\']");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "");
+    REQUIRE(arr[1] == "");
+  }
+
+  SECTION("EmptyStringNoQuotes") {
+    JsonArray& arr = jb.parseArray("[,]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "");
+    REQUIRE(arr[1] == "");
+  }
+
+  SECTION("ClosingDoubleQuoteMissing") {
+    JsonArray& arr = jb.parseArray("[\"]");
+
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("ClosingSignleQuoteMissing") {
+    JsonArray& arr = jb.parseArray("[\']");
+
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("StringWithEscapedChars") {
+    JsonArray& arr = jb.parseArray("[\"1\\\"2\\\\3\\/4\\b5\\f6\\n7\\r8\\t9\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == "1\"2\\3/4\b5\f6\n7\r8\t9");
+  }
+
+  SECTION("StringWithUnterminatedEscapeSequence") {
+    JsonArray& arr = jb.parseArray("\"\\\0\"", 4);
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("CCommentBeforeOpeningBracket") {
+    JsonArray& arr = jb.parseArray("/*COMMENT*/  [\"hello\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == "hello");
+  }
+
+  SECTION("CCommentAfterOpeningBracket") {
+    JsonArray& arr = jb.parseArray("[/*COMMENT*/ \"hello\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == "hello");
+  }
+
+  SECTION("CCommentBeforeClosingBracket") {
+    JsonArray& arr = jb.parseArray("[\"hello\"/*COMMENT*/]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == "hello");
+  }
+
+  SECTION("CCommentAfterClosingBracket") {
+    JsonArray& arr = jb.parseArray("[\"hello\"]/*COMMENT*/");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == "hello");
+  }
+
+  SECTION("CCommentBeforeComma") {
+    JsonArray& arr = jb.parseArray("[\"hello\"/*COMMENT*/,\"world\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "hello");
+    REQUIRE(arr[1] == "world");
+  }
+
+  SECTION("CCommentAfterComma") {
+    JsonArray& arr = jb.parseArray("[\"hello\",/*COMMENT*/ \"world\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "hello");
+    REQUIRE(arr[1] == "world");
+  }
+
+  SECTION("CppCommentBeforeOpeningBracket") {
+    JsonArray& arr = jb.parseArray("//COMMENT\n\t[\"hello\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == "hello");
+  }
+
+  SECTION("CppCommentAfterOpeningBracket") {
+    JsonArray& arr = jb.parseArray("[//COMMENT\n\"hello\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == "hello");
+  }
+
+  SECTION("CppCommentBeforeClosingBracket") {
+    JsonArray& arr = jb.parseArray("[\"hello\"//COMMENT\r\n]");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == "hello");
+  }
+
+  SECTION("CppCommentAfterClosingBracket") {
+    JsonArray& arr = jb.parseArray("[\"hello\"]//COMMENT\n");
+
+    REQUIRE(arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(arr[0] == "hello");
+  }
+
+  SECTION("CppCommentBeforeComma") {
+    JsonArray& arr = jb.parseArray("[\"hello\"//COMMENT\n,\"world\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "hello");
+    REQUIRE(arr[1] == "world");
+  }
+
+  SECTION("CppCommentAfterComma") {
+    JsonArray& arr = jb.parseArray("[\"hello\",//COMMENT\n\"world\"]");
+
+    REQUIRE(arr.success());
+    REQUIRE(2 == arr.size());
+    REQUIRE(arr[0] == "hello");
+    REQUIRE(arr[1] == "world");
+  }
+
+  SECTION("InvalidCppComment") {
+    JsonArray& arr = jb.parseArray("[/COMMENT\n]");
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("InvalidComment") {
+    JsonArray& arr = jb.parseArray("[/*/\n]");
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("UnfinishedCComment") {
+    JsonArray& arr = jb.parseArray("[/*COMMENT]");
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("EndsInCppComment") {
+    JsonArray& arr = jb.parseArray("[//COMMENT");
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("AfterClosingStar") {
+    JsonArray& arr = jb.parseArray("[/*COMMENT*");
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("DeeplyNested") {
+    JsonArray& arr =
+        jb.parseArray("[[[[[[[[[[[[[[[[[[[\"Not too deep\"]]]]]]]]]]]]]]]]]]]");
+    REQUIRE(arr.success());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/parseObject.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/parseObject.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3a4067c56206f5f37e6fbedc3c5e6ad40b15fe42
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonBuffer/parseObject.cpp
@@ -0,0 +1,170 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonBuffer::parseObject()") {
+  DynamicJsonBuffer jb;
+
+  SECTION("An empty object") {
+    JsonObject& obj = jb.parseObject("{}");
+    REQUIRE(obj.success());
+    REQUIRE(obj.size() == 0);
+  }
+
+  SECTION("Quotes") {
+    SECTION("Double quotes") {
+      JsonObject& obj = jb.parseObject("{\"key\":\"value\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("Single quotes") {
+      JsonObject& obj = jb.parseObject("{'key':'value'}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("No quotes") {
+      JsonObject& obj = jb.parseObject("{key:value}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("No quotes, allow underscore in key") {
+      JsonObject& obj = jb.parseObject("{_k_e_y_:42}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["_k_e_y_"] == 42);
+    }
+  }
+
+  SECTION("Spaces") {
+    SECTION("Before the key") {
+      JsonObject& obj = jb.parseObject("{ \"key\":\"value\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("After the key") {
+      JsonObject& obj = jb.parseObject("{\"key\" :\"value\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("Before the value") {
+      JsonObject& obj = jb.parseObject("{\"key\": \"value\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("After the value") {
+      JsonObject& obj = jb.parseObject("{\"key\":\"value\" }");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 1);
+      REQUIRE(obj["key"] == "value");
+    }
+
+    SECTION("Before the colon") {
+      JsonObject& obj =
+          jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == "value1");
+      REQUIRE(obj["key2"] == "value2");
+    }
+
+    SECTION("After the colon") {
+      JsonObject& obj =
+          jb.parseObject("{\"key1\":\"value1\" ,\"key2\":\"value2\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == "value1");
+      REQUIRE(obj["key2"] == "value2");
+    }
+  }
+
+  SECTION("Values types") {
+    SECTION("String") {
+      JsonObject& obj =
+          jb.parseObject("{\"key1\":\"value1\",\"key2\":\"value2\"}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == "value1");
+      REQUIRE(obj["key2"] == "value2");
+    }
+
+    SECTION("Integer") {
+      JsonObject& obj = jb.parseObject("{\"key1\":42,\"key2\":-42}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == 42);
+      REQUIRE(obj["key2"] == -42);
+    }
+
+    SECTION("Double") {
+      JsonObject& obj = jb.parseObject("{\"key1\":12.345,\"key2\":-7E89}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == 12.345);
+      REQUIRE(obj["key2"] == -7E89);
+    }
+
+    SECTION("Booleans") {
+      JsonObject& obj = jb.parseObject("{\"key1\":true,\"key2\":false}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"] == true);
+      REQUIRE(obj["key2"] == false);
+    }
+
+    SECTION("Null") {
+      JsonObject& obj = jb.parseObject("{\"key1\":null,\"key2\":null}");
+      REQUIRE(obj.success());
+      REQUIRE(obj.size() == 2);
+      REQUIRE(obj["key1"].as<char*>() == 0);
+      REQUIRE(obj["key2"].as<char*>() == 0);
+    }
+  }
+
+  SECTION("Misc") {
+    SECTION("The opening brace is missing") {
+      JsonObject& obj = jb.parseObject("}");
+      REQUIRE_FALSE(obj.success());
+    }
+
+    SECTION("The closing brace is missing") {
+      JsonObject& obj = jb.parseObject("{");
+      REQUIRE_FALSE(obj.success());
+    }
+
+    SECTION("A quoted key without value") {
+      JsonObject& obj = jb.parseObject("{\"key\"}");
+      REQUIRE_FALSE(obj.success());
+    }
+
+    SECTION("A non-quoted key without value") {
+      JsonObject& obj = jb.parseObject("{key}");
+      REQUIRE_FALSE(obj.success());
+    }
+
+    SECTION("A dangling comma") {
+      JsonObject& obj = jb.parseObject("{\"key1\":\"value1\",}");
+      REQUIRE_FALSE(obj.success());
+      REQUIRE(obj.size() == 0);
+    }
+
+    SECTION("null as a key") {
+      JsonObject& obj = jb.parseObject("null:\"value\"}");
+      REQUIRE_FALSE(obj.success());
+    }
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..93e8f0ded8cc2c7c83ab214a3b0d270999c77470
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/CMakeLists.txt
@@ -0,0 +1,20 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(JsonObjectTests 
+	basics.cpp
+	containsKey.cpp
+	get.cpp
+	invalid.cpp
+	iterator.cpp
+	prettyPrintTo.cpp
+	printTo.cpp
+	remove.cpp
+	set.cpp
+	size.cpp
+	subscript.cpp
+)
+
+target_link_libraries(JsonObjectTests catch)
+add_test(JsonObject JsonObjectTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/basics.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/basics.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dd9817029364e07bb18fe539d5b8c7a4150f9d46
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/basics.cpp
@@ -0,0 +1,19 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonObject basics") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonObject& _object = _jsonBuffer.createObject();
+
+  SECTION("InitialSizeIsZero") {
+    REQUIRE(0 == _object.size());
+  }
+
+  SECTION("SuccessIsTrue") {
+    REQUIRE(_object.success());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/containsKey.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/containsKey.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8d0a15206a81854741cfbc8d227dcdd642fa041
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/containsKey.cpp
@@ -0,0 +1,30 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonObject::containsKey()") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonObject& _object = _jsonBuffer.createObject();
+
+  SECTION("ContainsKeyReturnsFalseForNonExistingKey") {
+    _object.set("hello", 42);
+
+    REQUIRE(false == _object.containsKey("world"));
+  }
+
+  SECTION("ContainsKeyReturnsTrueForDefinedValue") {
+    _object.set("hello", 42);
+
+    REQUIRE(true == _object.containsKey("hello"));
+  }
+
+  SECTION("ContainsKeyReturnsFalseAfterRemove") {
+    _object.set("hello", 42);
+    _object.remove("hello");
+
+    REQUIRE(false == _object.containsKey("hello"));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/get.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/get.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b3635956a4aaa7b8a2630deb7825701a0b5f0a2d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/get.cpp
@@ -0,0 +1,19 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace Catch::Matchers;
+
+TEST_CASE("JsonObject::get()") {
+  DynamicJsonBuffer jb;
+  JsonObject& obj = jb.createObject();
+
+  SECTION("GetConstCharPointer_GivenStringLiteral") {
+    obj.set("hello", "world");
+    const char* value = obj.get<const char*>("hello");
+    REQUIRE_THAT(value, Equals("world"));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/invalid.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/invalid.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..019ced10ca18dd35d95d34a7cecf1ae6470d5e98
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/invalid.cpp
@@ -0,0 +1,35 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace Catch::Matchers;
+
+TEST_CASE("JsonObject::invalid()") {
+  JsonObject& obj = JsonObject::invalid();
+
+  SECTION("SubscriptFails") {
+    REQUIRE_FALSE(obj["key"].success());
+  }
+
+  SECTION("AddFails") {
+    obj.set("hello", "world");
+    REQUIRE(0 == obj.size());
+  }
+
+  SECTION("CreateNestedArrayFails") {
+    REQUIRE_FALSE(obj.createNestedArray("hello").success());
+  }
+
+  SECTION("CreateNestedObjectFails") {
+    REQUIRE_FALSE(obj.createNestedObject("world").success());
+  }
+
+  SECTION("PrintToWritesBraces") {
+    char buffer[32];
+    obj.printTo(buffer, sizeof(buffer));
+    REQUIRE_THAT(buffer, Equals("{}"));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/iterator.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/iterator.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e765d5c71ba6596ad3d0204dc01c049d0a07031e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/iterator.cpp
@@ -0,0 +1,51 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace Catch::Matchers;
+
+TEST_CASE("JsonObject::begin()/end()") {
+  StaticJsonBuffer<JSON_OBJECT_SIZE(2)> jb;
+  JsonObject& obj = jb.createObject();
+  obj["ab"] = 12;
+  obj["cd"] = 34;
+
+  SECTION("NonConstIterator") {
+    JsonObject::iterator it = obj.begin();
+    REQUIRE(obj.end() != it);
+    REQUIRE_THAT(it->key, Equals("ab"));
+    REQUIRE(12 == it->value);
+    it->key = "a.b";
+    it->value = 1.2;
+    ++it;
+    REQUIRE(obj.end() != it);
+    REQUIRE_THAT(it->key, Equals("cd"));
+    REQUIRE(34 == it->value);
+    it->key = "c.d";
+    it->value = 3.4;
+    ++it;
+    REQUIRE(obj.end() == it);
+
+    REQUIRE(2 == obj.size());
+    REQUIRE(1.2 == obj["a.b"]);
+    REQUIRE(3.4 == obj["c.d"]);
+  }
+
+  SECTION("ConstIterator") {
+    const JsonObject& const_object = obj;
+    JsonObject::const_iterator it = const_object.begin();
+
+    REQUIRE(const_object.end() != it);
+    REQUIRE_THAT(it->key, Equals("ab"));
+    REQUIRE(12 == it->value);
+    ++it;
+    REQUIRE(const_object.end() != it);
+    REQUIRE_THAT(it->key, Equals("cd"));
+    REQUIRE(34 == it->value);
+    ++it;
+    REQUIRE(const_object.end() == it);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/prettyPrintTo.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/prettyPrintTo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3045924693f0aae606f4ac972c6742ba581598a1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/prettyPrintTo.cpp
@@ -0,0 +1,76 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <string>
+
+void check(const JsonObject &obj, const std::string expected) {
+  char json[256];
+
+  size_t actualLen = obj.prettyPrintTo(json);
+  size_t measuredLen = obj.measurePrettyLength();
+
+  REQUIRE(json == expected);
+  REQUIRE(expected.size() == actualLen);
+  REQUIRE(expected.size() == measuredLen);
+}
+
+TEST_CASE("JsonObject::prettyPrintTo()") {
+  DynamicJsonBuffer jb;
+  JsonObject &obj = jb.createObject();
+
+  SECTION("EmptyObject") {
+    check(obj, "{}");
+  }
+
+  SECTION("OneMember") {
+    obj["key"] = "value";
+
+    check(obj,
+          "{\r\n"
+          "  \"key\": \"value\"\r\n"
+          "}");
+  }
+
+  SECTION("TwoMembers") {
+    obj["key1"] = "value1";
+    obj["key2"] = "value2";
+
+    check(obj,
+          "{\r\n"
+          "  \"key1\": \"value1\",\r\n"
+          "  \"key2\": \"value2\"\r\n"
+          "}");
+  }
+
+  SECTION("EmptyNestedContainers") {
+    obj.createNestedObject("key1");
+    obj.createNestedArray("key2");
+
+    check(obj,
+          "{\r\n"
+          "  \"key1\": {},\r\n"
+          "  \"key2\": []\r\n"
+          "}");
+  }
+
+  SECTION("NestedContainers") {
+    JsonObject &nested1 = obj.createNestedObject("key1");
+    nested1["a"] = 1;
+
+    JsonArray &nested2 = obj.createNestedArray("key2");
+    nested2.add(2);
+
+    check(obj,
+          "{\r\n"
+          "  \"key1\": {\r\n"
+          "    \"a\": 1\r\n"
+          "  },\r\n"
+          "  \"key2\": [\r\n"
+          "    2\r\n"
+          "  ]\r\n"
+          "}");
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/printTo.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/printTo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d0bcc9ad5d441a1aff61981f6e48ab0d231a374
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/printTo.cpp
@@ -0,0 +1,109 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <string>
+
+void check(const JsonObject &obj, const std::string &expected) {
+  char actual[256];
+  size_t actualLen = obj.printTo(actual);
+  size_t measuredLen = obj.measureLength();
+
+  REQUIRE(expected == actual);
+  REQUIRE(expected.size() == actualLen);
+  REQUIRE(expected.size() == measuredLen);
+}
+TEST_CASE("JsonObject::printTo()") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonObject &obj = _jsonBuffer.createObject();
+
+  SECTION("EmptyObject") {
+    check(obj, "{}");
+  }
+
+  SECTION("TwoStrings") {
+    obj["key1"] = "value1";
+    obj.set("key2", "value2");
+
+    check(obj, "{\"key1\":\"value1\",\"key2\":\"value2\"}");
+  }
+
+  SECTION("RemoveFirst") {
+    obj["key1"] = "value1";
+    obj["key2"] = "value2";
+    obj.remove("key1");
+
+    check(obj, "{\"key2\":\"value2\"}");
+  }
+
+  SECTION("RemoveLast") {
+    obj["key1"] = "value1";
+    obj["key2"] = "value2";
+    obj.remove("key2");
+
+    check(obj, "{\"key1\":\"value1\"}");
+  }
+
+  SECTION("RemoveUnexistingKey") {
+    obj["key1"] = "value1";
+    obj["key2"] = "value2";
+    obj.remove("key3");
+
+    check(obj, "{\"key1\":\"value1\",\"key2\":\"value2\"}");
+  }
+
+  SECTION("ReplaceExistingKey") {
+    obj["key"] = "value1";
+    obj["key"] = "value2";
+
+    check(obj, "{\"key\":\"value2\"}");
+  }
+
+  SECTION("TwoIntegers") {
+    obj["a"] = 1;
+    obj.set("b", 2);
+    check(obj, "{\"a\":1,\"b\":2}");
+  }
+
+  SECTION("RawJson") {
+    obj["a"] = RawJson("[1,2]");
+    obj.set("b", RawJson("[4,5]"));
+    check(obj, "{\"a\":[1,2],\"b\":[4,5]}");
+  }
+
+  SECTION("Two doubles") {
+    obj["a"] = 12.34;
+    obj.set("b", 56.78);
+    check(obj, "{\"a\":12.34,\"b\":56.78}");
+  }
+
+  SECTION("TwoNull") {
+    obj["a"] = static_cast<char *>(0);
+    obj.set("b", static_cast<char *>(0));
+    check(obj, "{\"a\":null,\"b\":null}");
+  }
+
+  SECTION("TwoBooleans") {
+    obj["a"] = true;
+    obj.set("b", false);
+    check(obj, "{\"a\":true,\"b\":false}");
+  }
+
+  SECTION("ThreeNestedArrays") {
+    obj.createNestedArray("a");
+    obj["b"] = _jsonBuffer.createArray();
+    obj.set("c", _jsonBuffer.createArray());
+
+    check(obj, "{\"a\":[],\"b\":[],\"c\":[]}");
+  }
+
+  SECTION("ThreeNestedObjects") {
+    obj.createNestedObject("a");
+    obj["b"] = _jsonBuffer.createObject();
+    obj.set("c", _jsonBuffer.createObject());
+
+    check(obj, "{\"a\":{},\"b\":{},\"c\":{}}");
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/remove.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/remove.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..0e68eec0529288d2275f364984a0554ced506977
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/remove.cpp
@@ -0,0 +1,41 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <string>
+
+TEST_CASE("JsonObject::remove()") {
+  DynamicJsonBuffer jb;
+
+  SECTION("SizeDecreased_WhenValuesAreRemoved") {
+    JsonObject& obj = jb.createObject();
+    obj["hello"] = 1;
+
+    obj.remove("hello");
+
+    REQUIRE(0 == obj.size());
+  }
+
+  SECTION("SizeUntouched_WhenRemoveIsCalledWithAWrongKey") {
+    JsonObject& obj = jb.createObject();
+    obj["hello"] = 1;
+
+    obj.remove("world");
+
+    REQUIRE(1 == obj.size());
+  }
+
+  SECTION("RemoveByIterator") {
+    JsonObject& obj = jb.parseObject("{\"a\":0,\"b\":1,\"c\":2}");
+
+    for (JsonObject::iterator it = obj.begin(); it != obj.end(); ++it) {
+      if (it->value == 1) obj.remove(it);
+    }
+
+    std::string result;
+    obj.printTo(result);
+    REQUIRE("{\"a\":0,\"c\":2}" == result);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/set.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/set.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d5ae48b21d23e90ae261f3197d901e399f683b93
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/set.cpp
@@ -0,0 +1,138 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <string>
+
+TEST_CASE("JsonObject::set()") {
+  DynamicJsonBuffer jb;
+  JsonObject& _object = jb.createObject();
+
+  SECTION("int") {
+    _object.set("hello", 123);
+
+    REQUIRE(123 == _object["hello"].as<int>());
+    REQUIRE(_object["hello"].is<int>());
+    REQUIRE_FALSE(_object["hello"].is<bool>());
+  }
+
+  SECTION("double") {
+    _object.set("hello", 123.45);
+
+    REQUIRE(123.45 == _object["hello"].as<double>());
+    REQUIRE(_object["hello"].is<double>());
+    REQUIRE_FALSE(_object["hello"].is<bool>());
+  }
+
+  SECTION("bool") {
+    _object.set("hello", true);
+
+    REQUIRE(_object["hello"].as<bool>());
+    REQUIRE(_object["hello"].is<bool>());
+    REQUIRE_FALSE(_object["hello"].is<long>());
+  }
+
+  SECTION("const char*") {
+    _object.set("hello", "h3110");
+
+    REQUIRE(std::string("h3110") == _object["hello"].as<const char*>());
+    REQUIRE(_object["hello"].is<const char*>());
+    REQUIRE_FALSE(_object["hello"].is<long>());
+  }
+
+  SECTION("nested array") {
+    JsonArray& arr = jb.createArray();
+
+    _object.set("hello", arr);
+
+    REQUIRE(&arr == &_object["hello"].as<JsonArray>());
+    REQUIRE(_object["hello"].is<JsonArray&>());
+    REQUIRE_FALSE(_object["hello"].is<JsonObject&>());
+  }
+
+  SECTION("nested object") {
+    JsonObject& obj = jb.createObject();
+
+    _object.set("hello", obj);
+
+    REQUIRE(&obj == &_object["hello"].as<JsonObject>());
+    REQUIRE(_object["hello"].is<JsonObject&>());
+    REQUIRE_FALSE(_object["hello"].is<JsonArray&>());
+  }
+
+  SECTION("array subscript") {
+    JsonArray& arr = jb.createArray();
+    arr.add(42);
+
+    _object.set("a", arr[0]);
+
+    REQUIRE(42 == _object["a"]);
+  }
+
+  SECTION("object subscript") {
+    JsonObject& obj = jb.createObject();
+    obj.set("x", 42);
+
+    _object.set("a", obj["x"]);
+
+    REQUIRE(42 == _object["a"]);
+  }
+
+  SECTION("returns true when allocation succeeds") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(1) + 15> jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+
+    REQUIRE(true == obj.set(std::string("hello"), std::string("world")));
+  }
+
+  SECTION("returns false when allocation fails") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(1) + 10> jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+
+    REQUIRE(false == obj.set(std::string("hello"), std::string("world")));
+  }
+
+  SECTION("should not duplicate const char*") {
+    _object.set("hello", "world");
+    const size_t expectedSize = JSON_OBJECT_SIZE(1);
+    REQUIRE(expectedSize == jb.size());
+  }
+
+  SECTION("should duplicate char* value") {
+    _object.set("hello", const_cast<char*>("world"));
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
+    REQUIRE(expectedSize == jb.size());
+  }
+
+  SECTION("should duplicate char* key") {
+    _object.set(const_cast<char*>("hello"), "world");
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
+    REQUIRE(expectedSize == jb.size());
+  }
+
+  SECTION("should duplicate char* key&value") {
+    _object.set(const_cast<char*>("hello"), const_cast<char*>("world"));
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
+    REQUIRE(expectedSize <= jb.size());
+  }
+
+  SECTION("should duplicate std::string value") {
+    _object.set("hello", std::string("world"));
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
+    REQUIRE(expectedSize == jb.size());
+  }
+
+  SECTION("should duplicate std::string key") {
+    _object.set(std::string("hello"), "world");
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
+    REQUIRE(expectedSize == jb.size());
+  }
+
+  SECTION("should duplicate std::string key&value") {
+    _object.set(std::string("hello"), std::string("world"));
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
+    REQUIRE(expectedSize <= jb.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/size.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/size.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eae888342bcbba64744bba528ce440a9f65a920c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/size.cpp
@@ -0,0 +1,23 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <string>
+
+TEST_CASE("JsonObject::size()") {
+  DynamicJsonBuffer jb;
+  JsonObject& _object = jb.createObject();
+
+  SECTION("increases when values are added") {
+    _object.set("hello", 42);
+    REQUIRE(1 == _object.size());
+  }
+
+  SECTION("doesn't increase when the smae key is added twice") {
+    _object["hello"] = 1;
+    _object["hello"] = 2;
+    REQUIRE(1 == _object.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/subscript.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/subscript.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d08e8c77d2d0760d39e6f3adadee35d9966b0948
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonObject/subscript.cpp
@@ -0,0 +1,152 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonObject::operator[]") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonObject& _object = _jsonBuffer.createObject();
+
+  SECTION("int") {
+    _object["hello"] = 123;
+
+    REQUIRE(123 == _object["hello"].as<int>());
+    REQUIRE(true == _object["hello"].is<int>());
+    REQUIRE(false == _object["hello"].is<bool>());
+  }
+
+  SECTION("volatile int") {  // issue #415
+    volatile int i = 123;
+    _object["hello"] = i;
+
+    REQUIRE(123 == _object["hello"].as<int>());
+    REQUIRE(true == _object["hello"].is<int>());
+    REQUIRE(false == _object["hello"].is<bool>());
+  }
+
+  SECTION("double") {
+    _object["hello"] = 123.45;
+
+    REQUIRE(true == _object["hello"].is<double>());
+    REQUIRE(false == _object["hello"].is<long>());
+    REQUIRE(123.45 == _object["hello"].as<double>());
+  }
+
+  SECTION("bool") {
+    _object["hello"] = true;
+
+    REQUIRE(true == _object["hello"].is<bool>());
+    REQUIRE(false == _object["hello"].is<long>());
+    REQUIRE(true == _object["hello"].as<bool>());
+  }
+
+  SECTION("const char*") {
+    _object["hello"] = "h3110";
+
+    REQUIRE(true == _object["hello"].is<const char*>());
+    REQUIRE(false == _object["hello"].is<long>());
+    REQUIRE(std::string("h3110") == _object["hello"].as<const char*>());
+    REQUIRE(std::string("h3110") ==
+            _object["hello"].as<char*>());  // <- short hand
+  }
+
+  SECTION("array") {
+    JsonArray& arr = _jsonBuffer.createArray();
+
+    _object["hello"] = arr;
+
+    REQUIRE(&arr == &_object["hello"].as<JsonArray&>());
+    REQUIRE(&arr == &_object["hello"].as<JsonArray>());  // <- short hand
+    REQUIRE(&arr == &_object["hello"].as<const JsonArray&>());
+    REQUIRE(&arr == &_object["hello"].as<const JsonArray>());  // <- short hand
+    REQUIRE(true == _object["hello"].is<JsonArray&>());
+    REQUIRE(true == _object["hello"].is<JsonArray>());
+    REQUIRE(true == _object["hello"].is<const JsonArray&>());
+    REQUIRE(true == _object["hello"].is<const JsonArray>());
+    REQUIRE(false == _object["hello"].is<JsonObject&>());
+  }
+
+  SECTION("object") {
+    JsonObject& obj = _jsonBuffer.createObject();
+
+    _object["hello"] = obj;
+
+    REQUIRE(&obj == &_object["hello"].as<JsonObject&>());
+    REQUIRE(&obj == &_object["hello"].as<JsonObject>());  // <- short hand
+    REQUIRE(&obj == &_object["hello"].as<const JsonObject&>());
+    REQUIRE(&obj == &_object["hello"].as<const JsonObject>());  // <- short hand
+    REQUIRE(true == _object["hello"].is<JsonObject&>());
+    REQUIRE(true == _object["hello"].is<JsonObject>());
+    REQUIRE(true == _object["hello"].is<const JsonObject&>());
+    REQUIRE(true == _object["hello"].is<const JsonObject>());
+    REQUIRE(false == _object["hello"].is<JsonArray&>());
+  }
+
+  SECTION("array subscript") {
+    JsonArray& arr = _jsonBuffer.createArray();
+    arr.add(42);
+
+    _object["a"] = arr[0];
+
+    REQUIRE(42 == _object["a"]);
+  }
+
+  SECTION("object subscript") {
+    JsonObject& obj = _jsonBuffer.createObject();
+    obj.set("x", 42);
+
+    _object["a"] = obj["x"];
+
+    REQUIRE(42 == _object["a"]);
+  }
+
+  SECTION("char key[]") {  // issue #423
+    char key[] = "hello";
+    _object[key] = 42;
+    REQUIRE(42 == _object[key]);
+  }
+
+  SECTION("should not duplicate const char*") {
+    _object["hello"] = "world";
+    const size_t expectedSize = JSON_OBJECT_SIZE(1);
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate char* value") {
+    _object["hello"] = const_cast<char*>("world");
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate char* key") {
+    _object[const_cast<char*>("hello")] = "world";
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate char* key&value") {
+    _object[const_cast<char*>("hello")] = const_cast<char*>("world");
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
+    REQUIRE(expectedSize <= _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate std::string value") {
+    _object["hello"] = std::string("world");
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate std::string key") {
+    _object[std::string("hello")] = "world";
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 6;
+    REQUIRE(expectedSize == _jsonBuffer.size());
+  }
+
+  SECTION("should duplicate std::string key&value") {
+    _object[std::string("hello")] = std::string("world");
+    const size_t expectedSize = JSON_OBJECT_SIZE(1) + 12;
+    REQUIRE(expectedSize <= _jsonBuffer.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0a00a15a1d4dfd737b9c3492088b617baae7147f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/CMakeLists.txt
@@ -0,0 +1,19 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(JsonVariantTests 
+	as.cpp
+	compare.cpp
+	copy.cpp
+	is.cpp
+	or.cpp
+	printTo.cpp
+	set_get.cpp
+	subscript.cpp
+	success.cpp
+	undefined.cpp
+)
+
+target_link_libraries(JsonVariantTests catch)
+add_test(JsonVariant JsonVariantTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/as.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/as.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d3b11cc239f3f76f2366401bea9f6575164866f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/as.cpp
@@ -0,0 +1,232 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <stdint.h>
+#include <catch.hpp>
+
+static const char* null = 0;
+
+TEST_CASE("JsonVariant::as()") {
+  SECTION("DoubleAsBool") {
+    JsonVariant variant = 4.2;
+    REQUIRE(variant.as<bool>());
+  }
+
+  SECTION("DoubleAsCstr") {
+    JsonVariant variant = 4.2;
+    REQUIRE_FALSE(variant.as<const char*>());
+  }
+
+  SECTION("DoubleAsString") {
+    JsonVariant variant = 4.2;
+    REQUIRE(std::string("4.2") == variant.as<std::string>());
+  }
+
+  SECTION("DoubleAsLong") {
+    JsonVariant variant = 4.2;
+    REQUIRE(4L == variant.as<long>());
+  }
+
+  SECTION("DoubleAsUnsigned") {
+    JsonVariant variant = 4.2;
+    REQUIRE(4U == variant.as<unsigned>());
+  }
+
+  SECTION("DoubleZeroAsBool") {
+    JsonVariant variant = 0.0;
+    REQUIRE_FALSE(variant.as<bool>());
+  }
+
+  SECTION("DoubleZeroAsLong") {
+    JsonVariant variant = 0.0;
+    REQUIRE(0L == variant.as<long>());
+  }
+
+  SECTION("FalseAsBool") {
+    JsonVariant variant = false;
+    REQUIRE_FALSE(variant.as<bool>());
+  }
+
+  SECTION("FalseAsDouble") {
+    JsonVariant variant = false;
+    REQUIRE(0.0 == variant.as<double>());
+  }
+
+  SECTION("FalseAsLong") {
+    JsonVariant variant = false;
+    REQUIRE(0L == variant.as<long>());
+  }
+
+  SECTION("FalseAsString") {
+    JsonVariant variant = false;
+    REQUIRE(std::string("false") == variant.as<std::string>());
+  }
+
+  SECTION("TrueAsBool") {
+    JsonVariant variant = true;
+    REQUIRE(variant.as<bool>());
+  }
+
+  SECTION("TrueAsDouble") {
+    JsonVariant variant = true;
+    REQUIRE(1.0 == variant.as<double>());
+  }
+
+  SECTION("TrueAsLong") {
+    JsonVariant variant = true;
+    REQUIRE(1L == variant.as<long>());
+  }
+
+  SECTION("TrueAsString") {
+    JsonVariant variant = true;
+    REQUIRE(std::string("true") == variant.as<std::string>());
+  }
+
+  SECTION("LongAsBool") {
+    JsonVariant variant = 42L;
+    REQUIRE(variant.as<bool>());
+  }
+
+  SECTION("LongZeroAsBool") {
+    JsonVariant variant = 0L;
+    REQUIRE_FALSE(variant.as<bool>());
+  }
+
+  SECTION("PositiveLongAsDouble") {
+    JsonVariant variant = 42L;
+    REQUIRE(42.0 == variant.as<double>());
+  }
+
+  SECTION("NegativeLongAsDouble") {
+    JsonVariant variant = -42L;
+    REQUIRE(-42.0 == variant.as<double>());
+  }
+
+  SECTION("LongAsString") {
+    JsonVariant variant = 42L;
+    REQUIRE(std::string("42") == variant.as<std::string>());
+  }
+
+  SECTION("LongZeroAsDouble") {
+    JsonVariant variant = 0L;
+    REQUIRE(0.0 == variant.as<double>());
+  }
+
+  SECTION("NullAsBool") {
+    JsonVariant variant = null;
+    REQUIRE_FALSE(variant.as<bool>());
+  }
+
+  SECTION("NullAsDouble") {
+    JsonVariant variant = null;
+    REQUIRE(0.0 == variant.as<double>());
+  }
+
+  SECTION("NullAsLong") {
+    JsonVariant variant = null;
+    REQUIRE(0L == variant.as<long>());
+  }
+
+  SECTION("NullAsString") {
+    JsonVariant variant = null;
+    REQUIRE(std::string("null") == variant.as<std::string>());
+  }
+
+  SECTION("NumberStringAsBool") {
+    JsonVariant variant = "42";
+    REQUIRE(variant.as<bool>());
+  }
+
+  SECTION("NumberStringAsLong") {
+    JsonVariant variant = "42";
+    REQUIRE(42L == variant.as<long>());
+  }
+
+#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
+  SECTION("NumberStringAsInt64Negative") {
+    JsonVariant variant = "-9223372036854775808";
+    REQUIRE(-9223372036854775807 - 1 == variant.as<long long>());
+  }
+
+  SECTION("NumberStringAsInt64Positive") {
+    JsonVariant variant = "9223372036854775807";
+    REQUIRE(9223372036854775807 == variant.as<long long>());
+  }
+#endif
+
+  SECTION("RandomStringAsBool") {
+    JsonVariant variant = "hello";
+    REQUIRE_FALSE(variant.as<bool>());
+  }
+
+  SECTION("RandomStringAsLong") {
+    JsonVariant variant = "hello";
+    REQUIRE(0L == variant.as<long>());
+  }
+
+  SECTION("RandomStringAsConstCharPtr") {
+    JsonVariant variant = "hello";
+    REQUIRE(std::string("hello") == variant.as<const char*>());
+  }
+
+  SECTION("RandomStringAsCharPtr") {
+    JsonVariant variant = "hello";
+    REQUIRE(std::string("hello") == variant.as<char*>());
+  }
+
+  SECTION("RandomStringAsString") {
+    JsonVariant variant = "hello";
+    REQUIRE(std::string("hello") == variant.as<std::string>());
+  }
+
+  SECTION("TrueStringAsBool") {
+    JsonVariant variant = "true";
+    REQUIRE(variant.as<bool>());
+  }
+
+  SECTION("TrueStringAsLong") {
+    JsonVariant variant = "true";
+    REQUIRE(1L == variant.as<long>());
+  }
+
+  SECTION("ObjectAsString") {
+    DynamicJsonBuffer buffer;
+
+    JsonObject& obj = buffer.createObject();
+    obj["key"] = "value";
+
+    JsonVariant variant = obj;
+    REQUIRE(std::string("{\"key\":\"value\"}") == variant.as<std::string>());
+  }
+
+  SECTION("ArrayAsString") {
+    DynamicJsonBuffer buffer;
+
+    JsonArray& arr = buffer.createArray();
+    arr.add(4);
+    arr.add(2);
+
+    JsonVariant variant = arr;
+    REQUIRE(std::string("[4,2]") == variant.as<std::string>());
+  }
+
+  SECTION("ArrayAsJsonArray") {
+    DynamicJsonBuffer buffer;
+    JsonArray& arr = buffer.createArray();
+
+    JsonVariant variant = arr;
+    REQUIRE(&arr == &variant.as<JsonArray&>());
+    REQUIRE(&arr == &variant.as<JsonArray>());  // <- shorthand
+  }
+
+  SECTION("ObjectAsJsonObject") {
+    DynamicJsonBuffer buffer;
+    JsonObject& arr = buffer.createObject();
+
+    JsonVariant variant = arr;
+    REQUIRE(&arr == &variant.as<JsonObject&>());
+    REQUIRE(&arr == &variant.as<JsonObject>());  // <- shorthand
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/compare.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/compare.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..965ec8efcf170a08a068b9b2728e477790932487
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/compare.cpp
@@ -0,0 +1,232 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+template <typename T>
+void checkEquals(JsonVariant a, T b) {
+  REQUIRE(b == a);
+  REQUIRE(a == b);
+  REQUIRE(b <= a);
+  REQUIRE(a <= b);
+  REQUIRE(b >= a);
+  REQUIRE(a >= b);
+
+  REQUIRE_FALSE(b != a);
+  REQUIRE_FALSE(a != b);
+  REQUIRE_FALSE(b > a);
+  REQUIRE_FALSE(a > b);
+  REQUIRE_FALSE(b < a);
+  REQUIRE_FALSE(a < b);
+}
+
+template <typename T>
+void checkGreater(JsonVariant a, T b) {
+  REQUIRE(a > b);
+  REQUIRE(b < a);
+  REQUIRE(a != b);
+  REQUIRE(b != a);
+
+  REQUIRE_FALSE(a < b);
+  REQUIRE_FALSE(b > a);
+  REQUIRE_FALSE(a == b);
+  REQUIRE_FALSE(b == a);
+}
+
+template <typename T>
+void checkLower(JsonVariant a, T b) {
+  REQUIRE(a < b);
+  REQUIRE(b > a);
+  REQUIRE(a != b);
+  REQUIRE(b != a);
+
+  REQUIRE_FALSE(a > b);
+  REQUIRE_FALSE(b < a);
+  REQUIRE_FALSE(a == b);
+  REQUIRE_FALSE(b == a);
+}
+
+template <typename T>
+void checkComparisons(T low, T mid, T high) {
+  checkEquals(mid, mid);
+  checkGreater(mid, low);
+  checkLower(mid, high);
+}
+
+TEST_CASE("JsonVariant comparisons") {
+  SECTION("Double") {
+    checkComparisons<double>(123.44, 123.45, 123.46);
+  }
+
+  SECTION("Float") {
+    checkComparisons<float>(123.44f, 123.45f, 123.46f);
+  }
+
+  SECTION("SChar") {
+    checkComparisons<signed char>(122, 123, 124);
+  }
+
+  SECTION("SInt") {
+    checkComparisons<signed int>(122, 123, 124);
+  }
+
+  SECTION("SLong") {
+    checkComparisons<signed long>(122L, 123L, 124L);
+  }
+
+  SECTION("SShort") {
+    checkComparisons<signed short>(122, 123, 124);
+  }
+
+  SECTION("UChar") {
+    checkComparisons<unsigned char>(122, 123, 124);
+  }
+
+  SECTION("UInt") {
+    checkComparisons<unsigned int>(122, 123, 124);
+  }
+
+  SECTION("ULong") {
+    checkComparisons<unsigned long>(122L, 123L, 124L);
+  }
+
+  SECTION("UShort") {
+    checkComparisons<unsigned short>(122, 123, 124);
+  }
+
+  SECTION("StringLiteral") {
+    DynamicJsonBuffer jsonBuffer;
+    JsonVariant variant = jsonBuffer.parse("\"hello\"");
+
+    REQUIRE(variant == "hello");
+    REQUIRE_FALSE(variant != "hello");
+
+    REQUIRE(variant != "world");
+    REQUIRE_FALSE(variant == "world");
+
+    REQUIRE("hello" == variant);
+    REQUIRE_FALSE("hello" != variant);
+
+    REQUIRE("world" != variant);
+    REQUIRE_FALSE("world" == variant);
+  }
+
+  SECTION("String") {
+    DynamicJsonBuffer jsonBuffer;
+    JsonVariant variant = jsonBuffer.parse("\"hello\"");
+
+    REQUIRE(variant == std::string("hello"));
+    REQUIRE_FALSE(variant != std::string("hello"));
+
+    REQUIRE(variant != std::string("world"));
+    REQUIRE_FALSE(variant == std::string("world"));
+
+    REQUIRE(std::string("hello") == variant);
+    REQUIRE_FALSE(std::string("hello") != variant);
+
+    REQUIRE(std::string("world") != variant);
+    REQUIRE_FALSE(std::string("world") == variant);
+  }
+
+  SECTION("IntegerInVariant") {
+    JsonVariant variant1 = 42;
+    JsonVariant variant2 = 42;
+    JsonVariant variant3 = 666;
+
+    REQUIRE(variant1 == variant2);
+    REQUIRE_FALSE(variant1 != variant2);
+
+    REQUIRE(variant1 != variant3);
+    REQUIRE_FALSE(variant1 == variant3);
+  }
+
+  SECTION("StringInVariant") {
+    JsonVariant variant1 = "0hello" + 1;  // make sure they have
+    JsonVariant variant2 = "1hello" + 1;  // different addresses
+    JsonVariant variant3 = "world";
+
+    REQUIRE(variant1 == variant2);
+    REQUIRE_FALSE(variant1 != variant2);
+
+    REQUIRE(variant1 != variant3);
+    REQUIRE_FALSE(variant1 == variant3);
+  }
+
+  SECTION("DoubleInVariant") {
+    JsonVariant variant1 = 42.0;
+    JsonVariant variant2 = 42.0;
+    JsonVariant variant3 = 666.0;
+
+    REQUIRE(variant1 == variant2);
+    REQUIRE_FALSE(variant1 != variant2);
+
+    REQUIRE(variant1 != variant3);
+    REQUIRE_FALSE(variant1 == variant3);
+  }
+
+  SECTION("BoolInVariant") {
+    JsonVariant variant1 = true;
+    JsonVariant variant2 = true;
+    JsonVariant variant3 = false;
+
+    REQUIRE(variant1 == variant2);
+    REQUIRE_FALSE(variant1 != variant2);
+
+    REQUIRE(variant1 != variant3);
+    REQUIRE_FALSE(variant1 == variant3);
+  }
+
+  SECTION("ArrayInVariant") {
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& array1 = jsonBuffer.createArray();
+    JsonArray& array2 = jsonBuffer.createArray();
+
+    JsonVariant variant1 = array1;
+    JsonVariant variant2 = array1;
+    JsonVariant variant3 = array2;
+
+    REQUIRE(variant1 == variant2);
+    REQUIRE_FALSE(variant1 != variant2);
+
+    REQUIRE(variant1 != variant3);
+    REQUIRE_FALSE(variant1 == variant3);
+  }
+
+  SECTION("ObjectInVariant") {
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj1 = jsonBuffer.createObject();
+    JsonObject& obj2 = jsonBuffer.createObject();
+
+    JsonVariant variant1 = obj1;
+    JsonVariant variant2 = obj1;
+    JsonVariant variant3 = obj2;
+
+    REQUIRE(variant1 == variant2);
+    REQUIRE_FALSE(variant1 != variant2);
+
+    REQUIRE(variant1 != variant3);
+    REQUIRE_FALSE(variant1 == variant3);
+  }
+
+  SECTION("VariantsOfDifferentTypes") {
+    DynamicJsonBuffer jsonBuffer;
+    JsonVariant variants[] = {
+        true,
+        42,
+        666.667,
+        "hello",
+        jsonBuffer.createArray(),
+        jsonBuffer.createObject(),
+    };
+    size_t n = sizeof(variants) / sizeof(variants[0]);
+
+    for (size_t i = 0; i < n; i++) {
+      for (size_t j = i + 1; j < n; j++) {
+        REQUIRE(variants[i] != variants[j]);
+        REQUIRE_FALSE(variants[i] == variants[j]);
+      }
+    }
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/copy.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/copy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..50e0a3bf11f0834ad3d637dfddacaba8d16b07f0
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/copy.cpp
@@ -0,0 +1,64 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonVariant copy") {
+  DynamicJsonBuffer _jsonBuffer;
+  JsonVariant _variant1;
+  JsonVariant _variant2;
+
+  SECTION("IntegersAreCopiedByValue") {
+    _variant1 = 123;
+    _variant2 = _variant1;
+    _variant1 = 456;
+
+    REQUIRE(123 == _variant2.as<int>());
+  }
+
+  SECTION("DoublesAreCopiedByValue") {
+    _variant1 = 123.45;
+    _variant2 = _variant1;
+    _variant1 = 456.78;
+
+    REQUIRE(123.45 == _variant2.as<double>());
+  }
+
+  SECTION("BooleansAreCopiedByValue") {
+    _variant1 = true;
+    _variant2 = _variant1;
+    _variant1 = false;
+
+    REQUIRE(_variant2.as<bool>());
+  }
+
+  SECTION("StringsAreCopiedByValue") {
+    _variant1 = "hello";
+    _variant2 = _variant1;
+    _variant1 = "world";
+
+    REQUIRE(std::string("hello") == _variant2.as<const char *>());
+  }
+
+  SECTION("ObjectsAreCopiedByReference") {
+    JsonObject &object = _jsonBuffer.createObject();
+
+    _variant1 = object;
+
+    object["hello"] = "world";
+
+    REQUIRE(1 == _variant1.as<JsonObject>().size());
+  }
+
+  SECTION("ArraysAreCopiedByReference") {
+    JsonArray &array = _jsonBuffer.createArray();
+
+    _variant1 = array;
+
+    array.add("world");
+
+    REQUIRE(1 == _variant1.as<JsonArray>().size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/is.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/is.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1bb03cbbce7f6cdfbfc7ea32fa62c704c1030a5b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/is.cpp
@@ -0,0 +1,115 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+void checkIsArray(JsonVariant var) {
+  REQUIRE(var.is<JsonArray>());
+  REQUIRE(var.is<JsonArray&>());
+  REQUIRE(var.is<const JsonArray>());
+  REQUIRE(var.is<const JsonArray&>());
+
+  REQUIRE_FALSE(var.is<bool>());
+  REQUIRE_FALSE(var.is<double>());
+  REQUIRE_FALSE(var.is<float>());
+  REQUIRE_FALSE(var.is<int>());
+  REQUIRE_FALSE(var.is<long>());
+  REQUIRE_FALSE(var.is<const char*>());
+  REQUIRE_FALSE(var.is<JsonObject>());
+}
+
+void checkIsBool(JsonVariant var) {
+  REQUIRE(var.is<bool>());
+
+  REQUIRE_FALSE(var.is<double>());
+  REQUIRE_FALSE(var.is<float>());
+  REQUIRE_FALSE(var.is<int>());
+  REQUIRE_FALSE(var.is<long>());
+  REQUIRE_FALSE(var.is<const char*>());
+  REQUIRE_FALSE(var.is<JsonArray>());
+  REQUIRE_FALSE(var.is<JsonObject>());
+}
+
+void checkIsFloat(JsonVariant var) {
+  REQUIRE(var.is<double>());
+  REQUIRE(var.is<float>());
+
+  REQUIRE_FALSE(var.is<bool>());
+  REQUIRE_FALSE(var.is<int>());
+  REQUIRE_FALSE(var.is<long>());
+  REQUIRE_FALSE(var.is<const char*>());
+  REQUIRE_FALSE(var.is<JsonArray>());
+  REQUIRE_FALSE(var.is<JsonObject>());
+}
+
+void checkIsInteger(JsonVariant var) {
+  REQUIRE(var.is<long>());
+  REQUIRE(var.is<int>());
+  REQUIRE(var.is<float>());
+  REQUIRE(var.is<double>());
+
+  REQUIRE_FALSE(var.is<bool>());
+  REQUIRE_FALSE(var.is<const char*>());
+  REQUIRE_FALSE(var.is<JsonArray>());
+  REQUIRE_FALSE(var.is<JsonObject>());
+}
+
+void checkIsString(JsonVariant var) {
+  REQUIRE(var.is<const char*>());
+
+  REQUIRE_FALSE(var.is<bool>());
+  REQUIRE_FALSE(var.is<int>());
+  REQUIRE_FALSE(var.is<double>());
+  REQUIRE_FALSE(var.is<float>());
+  REQUIRE_FALSE(var.is<long>());
+  REQUIRE_FALSE(var.is<JsonArray>());
+  REQUIRE_FALSE(var.is<JsonObject>());
+}
+
+TEST_CASE("JsonVariant::is()") {
+  DynamicJsonBuffer jsonBuffer;
+
+  SECTION("JsonArray") {
+    checkIsArray(jsonBuffer.createArray());
+  }
+
+  SECTION("bool") {
+    checkIsBool(true);
+    checkIsBool(false);
+  }
+
+  SECTION("double") {
+    checkIsFloat(4.2);
+  }
+
+  SECTION("int") {
+    checkIsInteger(42);
+  }
+
+  SECTION("long") {
+    checkIsInteger(42L);
+  }
+
+  SECTION("string") {
+    checkIsString("42");
+  }
+
+  SECTION("unparsed bool") {
+    checkIsBool(RawJson("true"));
+    checkIsBool(RawJson("false"));
+  }
+
+  SECTION("unparsed int") {
+    checkIsInteger(RawJson("42"));
+  }
+
+  SECTION("unparsed float") {
+    checkIsFloat(RawJson("4.2e-10"));
+  }
+
+  SECTION("unparsed null") {
+    checkIsString(RawJson("null"));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/or.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/or.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..84349463daa73a96b71f3af26da7553d3a1605a4
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/or.cpp
@@ -0,0 +1,83 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+static const JsonVariant undefined;
+static const JsonVariant null = static_cast<const char*>(0);
+
+TEST_CASE("JsonVariant::operator|()") {
+  SECTION("undefined | const char*") {
+    std::string result = undefined | "default";
+    REQUIRE(result == "default");
+  }
+
+  SECTION("undefined | int") {
+    int result = undefined | 42;
+    REQUIRE(result == 42);
+  }
+
+  SECTION("undefined | bool") {
+    bool result = undefined | true;
+    REQUIRE(result == true);
+  }
+
+  SECTION("null | const char*") {
+    std::string result = null | "default";
+    REQUIRE(result == "default");
+  }
+
+  SECTION("null | int") {
+    int result = null | 42;
+    REQUIRE(result == 42);
+  }
+
+  SECTION("null | bool") {
+    bool result = null | true;
+    REQUIRE(result == true);
+  }
+
+  SECTION("int | const char*") {
+    JsonVariant variant = 42;
+    std::string result = variant | "default";
+    REQUIRE(result == "default");
+  }
+
+  SECTION("int | int") {
+    JsonVariant variant = 0;
+    int result = variant | 666;
+    REQUIRE(result == 0);
+  }
+
+  SECTION("double | int") {
+    JsonVariant variant = 42.0;
+    int result = variant | 666;
+    REQUIRE(result == 42);
+  }
+
+  SECTION("bool | bool") {
+    JsonVariant variant = false;
+    bool result = variant | true;
+    REQUIRE(result == false);
+  }
+
+  SECTION("int | bool") {
+    JsonVariant variant = 0;
+    bool result = variant | true;
+    REQUIRE(result == true);
+  }
+
+  SECTION("const char* | const char*") {
+    JsonVariant variant = "not default";
+    std::string result = variant | "default";
+    REQUIRE(result == "not default");
+  }
+
+  SECTION("const char* | int") {
+    JsonVariant variant = "not default";
+    int result = variant | 42;
+    REQUIRE(result == 42);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/printTo.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/printTo.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3431eaeee10f49bd4a9559a87f93bd222cd153b5
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/printTo.cpp
@@ -0,0 +1,66 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <limits>
+
+void check(JsonVariant variant, const std::string &expected) {
+  char buffer[256] = "";
+  size_t returnValue = variant.printTo(buffer, sizeof(buffer));
+  REQUIRE(expected == buffer);
+  REQUIRE(expected.size() == returnValue);
+}
+
+TEST_CASE("JsonVariant::printTo()") {
+  SECTION("Empty") {
+    check(JsonVariant(), "");
+  }
+
+  SECTION("Null") {
+    check(static_cast<char *>(0), "null");
+  }
+
+  SECTION("String") {
+    check("hello", "\"hello\"");
+  }
+
+  SECTION("Double") {
+    check(3.1415927, "3.1415927");
+  }
+
+  SECTION("Integer") {
+    check(42, "42");
+  }
+
+  SECTION("NegativeLong") {
+    check(-42, "-42");
+  }
+
+  SECTION("UnsignedLong") {
+    check(4294967295UL, "4294967295");
+  }
+
+  SECTION("True") {
+    check(true, "true");
+  }
+
+  SECTION("OneFalse") {
+    check(false, "false");
+  }
+
+#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
+  SECTION("NegativeInt64") {
+    check(-9223372036854775807 - 1, "-9223372036854775808");
+  }
+
+  SECTION("PositiveInt64") {
+    check(9223372036854775807, "9223372036854775807");
+  }
+
+  SECTION("UInt64") {
+    check(18446744073709551615U, "18446744073709551615");
+  }
+#endif
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/set_get.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/set_get.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..af68c8b0584c62cd18b25e2fe806c185f6a6d93b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/set_get.cpp
@@ -0,0 +1,130 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <stdint.h>
+#include <catch.hpp>
+#include <limits>
+
+template <typename T>
+void checkValue(T expected) {
+  JsonVariant variant = expected;
+  REQUIRE(expected == variant.as<T>());
+}
+
+template <typename T>
+void checkReference(T &expected) {
+  JsonVariant variant = expected;
+  REQUIRE(expected == variant.as<T &>());
+}
+
+template <typename T>
+void checkNumericType() {
+  T min = std::numeric_limits<T>::min();
+  T max = std::numeric_limits<T>::max();
+
+  JsonVariant variantMin(min);
+  JsonVariant variantMax(max);
+
+  REQUIRE(min == variantMin.as<T>());
+  REQUIRE(max == variantMax.as<T>());
+}
+
+TEST_CASE("JsonVariant set()/get()") {
+#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
+  SECTION("SizeOfJsonInteger") {
+    REQUIRE(8 == sizeof(Internals::JsonInteger));
+  }
+#endif
+
+  SECTION("Null") {
+    checkValue<const char *>(NULL);
+  }
+  SECTION("String") {
+    checkValue<const char *>("hello");
+  }
+
+  SECTION("False") {
+    checkValue<bool>(false);
+  }
+  SECTION("True") {
+    checkValue<bool>(true);
+  }
+
+  SECTION("Double") {
+    checkNumericType<double>();
+  }
+  SECTION("Float") {
+    checkNumericType<float>();
+  }
+  SECTION("Char") {
+    checkNumericType<char>();
+  }
+  SECTION("SChar") {
+    checkNumericType<signed char>();
+  }
+  SECTION("SInt") {
+    checkNumericType<signed int>();
+  }
+  SECTION("SLong") {
+    checkNumericType<signed long>();
+  }
+  SECTION("SShort") {
+    checkNumericType<signed short>();
+  }
+  SECTION("UChar") {
+    checkNumericType<unsigned char>();
+  }
+  SECTION("UInt") {
+    checkNumericType<unsigned int>();
+  }
+  SECTION("ULong") {
+    checkNumericType<unsigned long>();
+  }
+  SECTION("UShort") {
+    checkNumericType<unsigned short>();
+  }
+#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
+  SECTION("LongLong") {
+    checkNumericType<unsigned long long>();
+  }
+  SECTION("ULongLong") {
+    checkNumericType<unsigned long long>();
+  }
+#endif
+
+  SECTION("Int8") {
+    checkNumericType<int8_t>();
+  }
+  SECTION("Uint8") {
+    checkNumericType<uint8_t>();
+  }
+  SECTION("Int16") {
+    checkNumericType<int16_t>();
+  }
+  SECTION("Uint16") {
+    checkNumericType<uint16_t>();
+  }
+  SECTION("Int32") {
+    checkNumericType<int32_t>();
+  }
+  SECTION("Uint32") {
+    checkNumericType<uint32_t>();
+  }
+#if ARDUINOJSON_USE_LONG_LONG || ARDUINOJSON_USE_INT64
+  SECTION("Int64") {
+    checkNumericType<int64_t>();
+  }
+  SECTION("Uint64") {
+    checkNumericType<uint64_t>();
+  }
+#endif
+
+  SECTION("CanStoreObject") {
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject &object = jsonBuffer.createObject();
+
+    checkReference(object);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/subscript.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/subscript.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5ce13e6f6a05a6daa305c9125f79de59a955409e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/subscript.cpp
@@ -0,0 +1,77 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonVariant::operator[]") {
+  DynamicJsonBuffer _jsonBuffer;
+
+  SECTION("Array") {
+    JsonArray &array = _jsonBuffer.createArray();
+    array.add("element at index 0");
+    array.add("element at index 1");
+
+    JsonVariant var = array;
+
+    REQUIRE(2 == var.size());
+    REQUIRE(std::string("element at index 0") == var[0]);
+    REQUIRE(std::string("element at index 1") == var[1]);
+    REQUIRE(std::string("element at index 0") ==
+            var[static_cast<unsigned char>(0)]);  // issue #381
+    REQUIRE_FALSE(var[666].success());
+    REQUIRE_FALSE(var[3].success());
+    REQUIRE_FALSE(var["0"].success());
+  }
+
+  SECTION("Object") {
+    JsonObject &object = _jsonBuffer.createObject();
+    object["a"] = "element at key \"a\"";
+    object["b"] = "element at key \"b\"";
+
+    JsonVariant var = object;
+
+    REQUIRE(2 == var.size());
+    REQUIRE(std::string("element at key \"a\"") == var["a"]);
+    REQUIRE(std::string("element at key \"b\"") == var["b"]);
+    REQUIRE_FALSE(var["c"].success());
+    REQUIRE_FALSE(var[0].success());
+  }
+
+  SECTION("Undefined") {
+    JsonVariant var = JsonVariant();
+    REQUIRE(0 == var.size());
+    REQUIRE_FALSE(var["0"].success());
+    REQUIRE_FALSE(var[0].success());
+  }
+
+  SECTION("String") {
+    JsonVariant var = "hello world";
+    REQUIRE(0 == var.size());
+    REQUIRE_FALSE(var["0"].success());
+    REQUIRE_FALSE(var[0].success());
+  }
+
+  SECTION("ObjectSetValue") {
+    JsonVariant var = _jsonBuffer.createObject();
+    var["hello"] = "world";
+    REQUIRE(1 == var.size());
+    REQUIRE(std::string("world") == var["hello"]);
+  }
+
+  SECTION("ArraySetValue") {
+    JsonVariant var = _jsonBuffer.parseArray("[\"hello\"]");
+    var[0] = "world";
+    REQUIRE(1 == var.size());
+    REQUIRE(std::string("world") == var[0]);
+  }
+
+  SECTION("NestedObjectSetValue") {
+    JsonVariant var = _jsonBuffer.parseArray("[{}]");
+    var[0]["hello"] = "world";
+    REQUIRE(1 == var.size());
+    REQUIRE(1 == var[0].size());
+    REQUIRE(std::string("world") == var[0]["hello"]);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/success.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/success.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..af8aad94089e1949852761710ac50d8fa6e4bfca
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/success.cpp
@@ -0,0 +1,42 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonVariant::success()") {
+  SECTION("ReturnsFalse_WhenUndefined") {
+    JsonVariant variant;
+    REQUIRE(false == variant.success());
+  }
+
+  SECTION("ReturnsTrue_WhenInteger") {
+    JsonVariant variant = 0;
+    REQUIRE(true == variant.success());
+  }
+
+  SECTION("ReturnsTrue_WhenEmptyArray") {
+    DynamicJsonBuffer jsonBuffer;
+
+    JsonVariant variant = jsonBuffer.createArray();
+    REQUIRE(true == variant.success());
+  }
+
+  SECTION("ReturnsTrue_WhenEmptyObject") {
+    DynamicJsonBuffer jsonBuffer;
+
+    JsonVariant variant = jsonBuffer.createObject();
+    REQUIRE(true == variant.success());
+  }
+
+  SECTION("ReturnsFalse_WhenInvalidArray") {
+    JsonVariant variant = JsonArray::invalid();
+    REQUIRE(false == variant.success());
+  }
+
+  SECTION("ReturnsFalse_WhenInvalidObject") {
+    JsonVariant variant = JsonObject::invalid();
+    REQUIRE(false == variant.success());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/undefined.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/undefined.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9615f0f4c39a23a5be40d4ae27f54a307aafb1f0
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonVariant/undefined.cpp
@@ -0,0 +1,54 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("JsonVariant undefined") {
+  JsonVariant variant;
+
+  SECTION("AsLongReturns0") {
+    REQUIRE(0 == variant.as<long>());
+  }
+
+  SECTION("AsUnsignedReturns0") {
+    REQUIRE(0 == variant.as<unsigned>());
+  }
+
+  SECTION("AsStringReturnsNull") {
+    REQUIRE(0 == variant.as<char*>());
+  }
+
+  SECTION("AsDoubleReturns0") {
+    REQUIRE(0 == variant.as<double>());
+  }
+
+  SECTION("AsBoolReturnsFalse") {
+    REQUIRE(false == variant.as<bool>());
+  }
+
+  SECTION("AsArrayReturnInvalid") {
+    REQUIRE(JsonArray::invalid() == variant.as<JsonArray&>());
+  }
+
+  SECTION("AsConstArrayReturnInvalid") {
+    REQUIRE(JsonArray::invalid() == variant.as<const JsonArray&>());
+  }
+
+  SECTION("AsObjectReturnInvalid") {
+    REQUIRE(JsonObject::invalid() == variant.as<JsonObject&>());
+  }
+
+  SECTION("AsConstObjectReturnInvalid") {
+    REQUIRE(JsonObject::invalid() == variant.as<const JsonObject&>());
+  }
+
+  SECTION("AsArrayWrapperReturnInvalid") {
+    REQUIRE(JsonArray::invalid() == variant.as<JsonArray>());
+  }
+
+  SECTION("AsObjectWrapperReturnInvalid") {
+    REQUIRE(JsonObject::invalid() == variant.as<JsonObject>());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonWriter/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonWriter/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..12a12bfdd50ba481245901d45bfabbcb14a8725f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonWriter/CMakeLists.txt
@@ -0,0 +1,11 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(JsonWriterTests 
+	writeFloat.cpp
+	writeString.cpp
+)
+
+target_link_libraries(JsonWriterTests catch)
+add_test(JsonWriter JsonWriterTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonWriter/writeFloat.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonWriter/writeFloat.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..532de5dc63109287e7ebe2a40e40441041d59b1f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonWriter/writeFloat.cpp
@@ -0,0 +1,117 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <catch.hpp>
+#include <limits>
+#include <string>
+
+#include <ArduinoJson/Serialization/DynamicStringBuilder.hpp>
+#include <ArduinoJson/Serialization/JsonWriter.hpp>
+
+using namespace ArduinoJson::Internals;
+
+template <typename TFloat>
+void check(TFloat input, const std::string& expected) {
+  std::string output;
+  DynamicStringBuilder<std::string> sb(output);
+  JsonWriter<DynamicStringBuilder<std::string> > writer(sb);
+  writer.writeFloat(input);
+  REQUIRE(writer.bytesWritten() == output.size());
+  CHECK(expected == output);
+}
+
+TEST_CASE("JsonWriter::writeFloat(double)") {
+  SECTION("Pi") {
+    check<double>(3.14159265359, "3.141592654");
+  }
+
+  SECTION("Signaling NaN") {
+    double nan = std::numeric_limits<double>::signaling_NaN();
+    check<double>(nan, "NaN");
+  }
+
+  SECTION("Quiet NaN") {
+    double nan = std::numeric_limits<double>::quiet_NaN();
+    check<double>(nan, "NaN");
+  }
+
+  SECTION("Infinity") {
+    double inf = std::numeric_limits<double>::infinity();
+    check<double>(inf, "Infinity");
+    check<double>(-inf, "-Infinity");
+  }
+
+  SECTION("Zero") {
+    check<double>(0.0, "0");
+    check<double>(-0.0, "0");
+  }
+
+  SECTION("Espilon") {
+    check<double>(2.2250738585072014E-308, "2.225073859e-308");
+    check<double>(-2.2250738585072014E-308, "-2.225073859e-308");
+  }
+
+  SECTION("Max double") {
+    check<double>(1.7976931348623157E+308, "1.797693135e308");
+    check<double>(-1.7976931348623157E+308, "-1.797693135e308");
+  }
+
+  SECTION("Big exponent") {
+    // this test increases coverage of normalize()
+    check<double>(1e255, "1e255");
+    check<double>(1e-255, "1e-255");
+  }
+
+  SECTION("Exponentation when <= 1e-5") {
+    check<double>(1e-4, "0.0001");
+    check<double>(1e-5, "1e-5");
+
+    check<double>(-1e-4, "-0.0001");
+    check<double>(-1e-5, "-1e-5");
+  }
+
+  SECTION("Exponentation when >= 1e7") {
+    check<double>(9999999.999, "9999999.999");
+    check<double>(10000000.0, "1e7");
+
+    check<double>(-9999999.999, "-9999999.999");
+    check<double>(-10000000.0, "-1e7");
+  }
+
+  SECTION("Rounding when too many decimals") {
+    check<double>(0.000099999999999, "0.0001");
+    check<double>(0.0000099999999999, "1e-5");
+    check<double>(0.9999999996, "1");
+  }
+
+  SECTION("9 decimal places") {
+    check<double>(0.100000001, "0.100000001");
+    check<double>(0.999999999, "0.999999999");
+
+    check<double>(9.000000001, "9.000000001");
+    check<double>(9.999999999, "9.999999999");
+  }
+
+  SECTION("10 decimal places") {
+    check<double>(0.1000000001, "0.1");
+    check<double>(0.9999999999, "1");
+
+    check<double>(9.0000000001, "9");
+    check<double>(9.9999999999, "10");
+  }
+}
+
+TEST_CASE("JsonWriter::writeFloat(float)") {
+  SECTION("Pi") {
+    check<float>(3.14159265359f, "3.141593");
+  }
+
+  SECTION("999.9") {  // issue #543
+    check<float>(999.9f, "999.9");
+  }
+
+  SECTION("24.3") {  // # issue #588
+    check<float>(24.3f, "24.3");
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonWriter/writeString.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonWriter/writeString.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e583fbcf5b79e9d521c0586a972b81b06dd19977
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/JsonWriter/writeString.cpp
@@ -0,0 +1,61 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <catch.hpp>
+
+#include <ArduinoJson/Serialization/JsonWriter.hpp>
+#include <ArduinoJson/Serialization/StaticStringBuilder.hpp>
+
+using namespace ArduinoJson::Internals;
+
+void check(const char* input, std::string expected) {
+  char output[1024];
+  StaticStringBuilder sb(output, sizeof(output));
+  JsonWriter<StaticStringBuilder> writer(sb);
+  writer.writeString(input);
+  REQUIRE(expected == output);
+  REQUIRE(writer.bytesWritten() == expected.size());
+}
+
+TEST_CASE("JsonWriter::writeString()") {
+  SECTION("Null") {
+    check(0, "null");
+  }
+
+  SECTION("EmptyString") {
+    check("", "\"\"");
+  }
+
+  SECTION("QuotationMark") {
+    check("\"", "\"\\\"\"");
+  }
+
+  SECTION("ReverseSolidus") {
+    check("\\", "\"\\\\\"");
+  }
+
+  SECTION("Solidus") {
+    check("/", "\"/\"");  // but the JSON format allows \/
+  }
+
+  SECTION("Backspace") {
+    check("\b", "\"\\b\"");
+  }
+
+  SECTION("Formfeed") {
+    check("\f", "\"\\f\"");
+  }
+
+  SECTION("Newline") {
+    check("\n", "\"\\n\"");
+  }
+
+  SECTION("CarriageReturn") {
+    check("\r", "\"\\r\"");
+  }
+
+  SECTION("HorizontalTab") {
+    check("\t", "\"\\t\"");
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..0b54bd7bd44f69112a8c9e7fe7683581fc225f8c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/CMakeLists.txt
@@ -0,0 +1,18 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(MiscTests 
+	deprecated.cpp
+	FloatParts.cpp
+	std_stream.cpp
+	std_string.cpp
+	StringBuilder.cpp
+	StringTraits.cpp
+	TypeTraits.cpp
+	unsigned_char.cpp
+	vla.cpp
+)
+
+target_link_libraries(MiscTests catch)
+add_test(Misc MiscTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/FloatParts.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/FloatParts.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aeed52e7525973205801def15335304f61de8c22
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/FloatParts.cpp
@@ -0,0 +1,44 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson/Serialization/FloatParts.hpp>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+TEST_CASE("FloatParts<double>") {
+  SECTION("1.7976931348623157E+308") {
+    FloatParts<double> parts(1.7976931348623157E+308);
+    REQUIRE(parts.integral == 1);
+    REQUIRE(parts.decimal == 797693135);
+    REQUIRE(parts.decimalPlaces == 9);
+    REQUIRE(parts.exponent == 308);
+  }
+
+  SECTION("4.94065645841247e-324") {
+    FloatParts<double> parts(4.94065645841247e-324);
+    REQUIRE(parts.integral == 4);
+    REQUIRE(parts.decimal == 940656458);
+    REQUIRE(parts.decimalPlaces == 9);
+    REQUIRE(parts.exponent == -324);
+  }
+}
+
+TEST_CASE("FloatParts<float>") {
+  SECTION("3.4E+38") {
+    FloatParts<float> parts(3.4E+38f);
+    REQUIRE(parts.integral == 3);
+    REQUIRE(parts.decimal == 4);
+    REQUIRE(parts.decimalPlaces == 1);
+    REQUIRE(parts.exponent == 38);
+  }
+
+  SECTION("1.17549435e−38") {
+    FloatParts<float> parts(1.17549435e-38f);
+    REQUIRE(parts.integral == 1);
+    REQUIRE(parts.decimal == 175494);
+    REQUIRE(parts.decimalPlaces == 6);
+    REQUIRE(parts.exponent == -38);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/StringBuilder.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/StringBuilder.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a7132dbd92fa275d14928785165da9a47e2482b9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/StringBuilder.cpp
@@ -0,0 +1,50 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+template <typename StringBuilder, typename String>
+void common_tests(StringBuilder& sb, const String& output) {
+  SECTION("InitialState") {
+    REQUIRE(std::string("") == output);
+  }
+
+  SECTION("EmptyString") {
+    REQUIRE(0 == sb.print(""));
+    REQUIRE(std::string("") == output);
+  }
+
+  SECTION("OneString") {
+    REQUIRE(4 == sb.print("ABCD"));
+    REQUIRE(std::string("ABCD") == output);
+  }
+
+  SECTION("TwoStrings") {
+    REQUIRE(4 == sb.print("ABCD"));
+    REQUIRE(4 == sb.print("EFGH"));
+    REQUIRE(std::string("ABCDEFGH") == output);
+  }
+}
+
+TEST_CASE("StaticStringBuilder") {
+  char output[20];
+  StaticStringBuilder sb(output, sizeof(output));
+
+  common_tests(sb, static_cast<const char*>(output));
+
+  SECTION("OverCapacity") {
+    REQUIRE(19 == sb.print("ABCDEFGHIJKLMNOPQRSTUVWXYZ"));
+    REQUIRE(0 == sb.print("ABC"));
+    REQUIRE(std::string("ABCDEFGHIJKLMNOPQRS") == output);
+  }
+}
+
+TEST_CASE("DynamicStringBuilder") {
+  std::string output;
+  DynamicStringBuilder<std::string> sb(output);
+  common_tests(sb, output);
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/StringTraits.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/StringTraits.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..db18648caf517ba6bf81c2c8db7de23963d6c2ae
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/StringTraits.cpp
@@ -0,0 +1,22 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+template <typename String>
+bool should_duplicate() {
+  return StringTraits<String>::should_duplicate;
+}
+
+TEST_CASE("StringTraits") {
+  SECTION("should_duplicate") {
+    REQUIRE(false == should_duplicate<const char*>());
+    REQUIRE(true == should_duplicate<char*>());
+    REQUIRE(true == should_duplicate<RawJsonString<char*> >());
+    REQUIRE(false == should_duplicate<RawJsonString<const char*> >());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/TypeTraits.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/TypeTraits.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d730b21961d22f5d5d8e553ff2be4d076fa9e56
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/TypeTraits.cpp
@@ -0,0 +1,37 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+TEST_CASE("TypeTraits") {
+  SECTION("IsBaseOf") {
+    REQUIRE_FALSE(
+        static_cast<bool>(IsBaseOf<std::istream, std::ostringstream>::value));
+    REQUIRE(
+        static_cast<bool>(IsBaseOf<std::istream, std::istringstream>::value));
+    REQUIRE(static_cast<bool>(
+        IsBaseOf<JsonVariantBase<JsonObjectSubscript<const char*> >,
+                 JsonObjectSubscript<const char*> >::value));
+  }
+
+  SECTION("IsArray") {
+    REQUIRE_FALSE((IsArray<const char*>::value));
+    REQUIRE((IsArray<const char[]>::value));
+    REQUIRE((IsArray<const char[10]>::value));
+  }
+
+  SECTION("IsVariant") {
+    REQUIRE(
+        static_cast<bool>(IsVariant<JsonObjectSubscript<const char*> >::value));
+    REQUIRE(static_cast<bool>(IsVariant<JsonVariant>::value));
+  }
+
+  SECTION("IsConst") {
+    REQUIRE_FALSE((IsConst<char>::value));
+    REQUIRE((IsConst<const char>::value));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/deprecated.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/deprecated.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8fa4fe28df14fcad40c3a2b21c2e86536f5dda29
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/deprecated.cpp
@@ -0,0 +1,140 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#define ARDUINOJSON_ENABLE_DEPRECATED 1
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#elif defined(_MSC_VER)
+#pragma warning(disable : 4996)
+#endif
+
+TEST_CASE("Deprecated functions") {
+  DynamicJsonBuffer jsonBuffer;
+
+  SECTION("JsonVariant::asArray()") {
+    JsonVariant variant = jsonBuffer.createArray();
+    REQUIRE(variant.asArray().success());
+  }
+
+  SECTION("JsonVariant::asObject()") {
+    JsonVariant variant = jsonBuffer.createObject();
+    REQUIRE(variant.asObject().success());
+  }
+
+  SECTION("JsonVariant::asString()") {
+    JsonVariant variant = "hello";
+    REQUIRE(std::string("hello") == variant.asString());
+  }
+
+  SECTION("JsonArray::removeAt()") {
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.removeAt(0);
+  }
+
+  SECTION("JsonVariant::JsonVariant(float, uint8_t)") {
+    JsonVariant variant(3.14f, 2);
+    REQUIRE(variant == 3.14f);
+  }
+
+  SECTION("JsonVariant::JsonVariant(double, uint8_t)") {
+    JsonVariant variant(3.14, 2);
+    REQUIRE(variant == 3.14);
+  }
+
+  SECTION("float_with_n_digits()") {
+    JsonVariant variant = float_with_n_digits(3.14f, 4);
+    REQUIRE(variant == 3.14f);
+  }
+
+  SECTION("double_with_n_digits()") {
+    JsonVariant variant = double_with_n_digits(3.14f, 4);
+    REQUIRE(variant == 3.14f);
+  }
+
+  SECTION("JsonArraySubscript::set(double, uint8_t)") {
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add(666);
+    arr[0].set(123.45, 2);
+    REQUIRE(123.45 == arr[0].as<double>());
+    REQUIRE(true == arr[0].is<double>());
+    REQUIRE(false == arr[0].is<int>());
+  }
+
+  SECTION("JsonArray::add(double, uint8_t)") {
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add(3.14159265358979323846, 4);
+  }
+
+  SECTION("JsonArray::add(float, uint8_t)") {
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add(3.14159265358979323846f, 4);
+  }
+
+  SECTION("JsonObject::set(unsigned char[], double, uint8_t)") {
+    unsigned char key[] = "hello";
+
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set(key, 3.14, 2);
+
+    REQUIRE(3.14 == obj["hello"]);
+  }
+
+  SECTION("JsonObject::set(const char*, double, uint8_t)") {
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set("hello", 123.45, 2);
+
+    REQUIRE(123.45 == obj["hello"].as<double>());
+    REQUIRE(obj["hello"].is<double>());
+    REQUIRE_FALSE(obj["hello"].is<long>());
+  }
+
+  SECTION("JsonObjectSubscript::set(double, uint8_t)") {
+    JsonObject& obj = jsonBuffer.createObject();
+    obj["hello"].set(123.45, 2);
+
+    REQUIRE(true == obj["hello"].is<double>());
+    REQUIRE(false == obj["hello"].is<long>());
+    REQUIRE(123.45 == obj["hello"].as<double>());
+  }
+}
+
+TEST_CASE("DynamicJsonBuffer::strdup()") {
+  DynamicJsonBuffer buffer;
+
+  SECTION("char*") {
+    char original[] = "hello";
+    const char* copy = buffer.strdup(original);
+    strcpy(original, "world");
+    REQUIRE(std::string("hello") == copy);
+  }
+
+  SECTION("unsigned char*") {
+    unsigned char value[] = "world";
+
+    DynamicJsonBuffer jsonBuffer;
+    const char* dup = jsonBuffer.strdup(value);
+
+    REQUIRE(static_cast<const void*>(value) != static_cast<const void*>(dup));
+    REQUIRE(std::string("world") == dup);
+  }
+
+  SECTION("std::string") {
+    std::string original("hello");
+    const char* copy = buffer.strdup(original);
+    original[0] = 'w';
+    REQUIRE(std::string("hello") == copy);
+  }
+
+  SECTION("NULL") {
+    const char* original = NULL;
+    const char* copy = buffer.strdup(original);
+    REQUIRE(0 == copy);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/std_stream.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/std_stream.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..170c9418a19789b2fc6f93f6014fa8dc81119a75
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/std_stream.cpp
@@ -0,0 +1,84 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+#include <sstream>
+
+TEST_CASE("std::stream") {
+  SECTION("JsonVariantFalse") {
+    std::ostringstream os;
+    JsonVariant variant = false;
+    os << variant;
+    REQUIRE("false" == os.str());
+  }
+
+  SECTION("JsonVariantString") {
+    std::ostringstream os;
+    JsonVariant variant = "coucou";
+    os << variant;
+    REQUIRE("\"coucou\"" == os.str());
+  }
+
+  SECTION("JsonObject") {
+    std::ostringstream os;
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& object = jsonBuffer.createObject();
+    object["key"] = "value";
+    os << object;
+    REQUIRE("{\"key\":\"value\"}" == os.str());
+  }
+
+  SECTION("JsonObjectSubscript") {
+    std::ostringstream os;
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& object = jsonBuffer.createObject();
+    object["key"] = "value";
+    os << object["key"];
+    REQUIRE("\"value\"" == os.str());
+  }
+
+  SECTION("JsonArray") {
+    std::ostringstream os;
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& array = jsonBuffer.createArray();
+    array.add("value");
+    os << array;
+    REQUIRE("[\"value\"]" == os.str());
+  }
+
+  SECTION("JsonArraySubscript") {
+    std::ostringstream os;
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& array = jsonBuffer.createArray();
+    array.add("value");
+    os << array[0];
+    REQUIRE("\"value\"" == os.str());
+  }
+
+  SECTION("ParseArray") {
+    std::istringstream json(" [ 42 /* comment */ ] ");
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& arr = jsonBuffer.parseArray(json);
+    REQUIRE(true == arr.success());
+    REQUIRE(1 == arr.size());
+    REQUIRE(42 == arr[0]);
+  }
+
+  SECTION("ParseObject") {
+    std::istringstream json(" { hello : world // comment\n }");
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.parseObject(json);
+    REQUIRE(true == obj.success());
+    REQUIRE(1 == obj.size());
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("ShouldNotReadPastTheEnd") {
+    std::istringstream json("{}123");
+    DynamicJsonBuffer jsonBuffer;
+    jsonBuffer.parseObject(json);
+    REQUIRE('1' == json.get());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/std_string.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/std_string.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..aeb188ddf413bbaa92bc3a064e38938c1b77e173
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/std_string.cpp
@@ -0,0 +1,243 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+static void eraseString(std::string &str) {
+  char *p = const_cast<char *>(str.c_str());
+  while (*p) *p++ = '*';
+}
+
+TEST_CASE("std::string") {
+  DynamicJsonBuffer jb;
+
+  SECTION("JsonBuffer_ParseArray") {
+    std::string json("[\"hello\"]");
+    JsonArray &array = jb.parseArray(json);
+    eraseString(json);
+    REQUIRE(true == array.success());
+    REQUIRE(std::string("hello") == array[0]);
+  }
+
+  SECTION("JsonBuffer_ParseObject") {
+    std::string json("{\"hello\":\"world\"}");
+    JsonObject &object = jb.parseObject(json);
+    eraseString(json);
+    REQUIRE(true == object.success());
+    REQUIRE(std::string("world") == object["hello"]);
+  }
+
+  SECTION("JsonObject_Subscript") {
+    char json[] = "{\"key\":\"value\"}";
+    JsonObject &object = jb.parseObject(json);
+    REQUIRE(std::string("value") == object[std::string("key")]);
+  }
+
+  SECTION("JsonObject_ConstSubscript") {
+    char json[] = "{\"key\":\"value\"}";
+    const JsonObject &object = jb.parseObject(json);
+    REQUIRE(std::string("value") == object[std::string("key")]);
+  }
+
+  SECTION("JsonObject_SetKey") {
+    JsonObject &object = jb.createObject();
+    std::string key("hello");
+    object.set(key, "world");
+    eraseString(key);
+    REQUIRE(std::string("world") == object["hello"]);
+  }
+
+  SECTION("JsonObject_SetValue") {
+    JsonObject &object = jb.createObject();
+    std::string value("world");
+    object.set("hello", value);
+    eraseString(value);
+    REQUIRE(std::string("world") == object["hello"]);
+  }
+
+  SECTION("JsonObject_SetKeyValue") {
+    JsonObject &object = jb.createObject();
+    std::string key("hello");
+    std::string value("world");
+    object.set(key, value);
+    eraseString(key);
+    eraseString(value);
+    REQUIRE(std::string("world") == object["hello"]);
+  }
+
+  SECTION("JsonObject_SetToArraySubscript") {
+    JsonArray &arr = jb.createArray();
+    arr.add("world");
+
+    JsonObject &object = jb.createObject();
+    object.set(std::string("hello"), arr[0]);
+
+    REQUIRE(std::string("world") == object["hello"]);
+  }
+
+  SECTION("JsonObject_SetToObjectSubscript") {
+    JsonObject &arr = jb.createObject();
+    arr.set("x", "world");
+
+    JsonObject &object = jb.createObject();
+    object.set(std::string("hello"), arr["x"]);
+
+    REQUIRE(std::string("world") == object["hello"]);
+  }
+
+  SECTION("JsonObject_Get") {
+    char json[] = "{\"key\":\"value\"}";
+    const JsonObject &object = jb.parseObject(json);
+    REQUIRE(std::string("value") ==
+            object.get<const char *>(std::string("key")));
+  }
+
+  SECTION("JsonObject_GetT") {
+    char json[] = "{\"key\":\"value\"}";
+    const JsonObject &object = jb.parseObject(json);
+    REQUIRE(std::string("value") ==
+            object.get<const char *>(std::string("key")));
+  }
+
+  SECTION("JsonObject_IsT") {
+    char json[] = "{\"key\":\"value\"}";
+    const JsonObject &object = jb.parseObject(json);
+    REQUIRE(true == object.is<const char *>(std::string("key")));
+  }
+
+  SECTION("JsonObject_CreateNestedObject") {
+    std::string key = "key";
+    char json[64];
+    JsonObject &object = jb.createObject();
+    object.createNestedObject(key);
+    eraseString(key);
+    object.printTo(json, sizeof(json));
+    REQUIRE(std::string("{\"key\":{}}") == json);
+  }
+
+  SECTION("JsonObject_CreateNestedArray") {
+    std::string key = "key";
+    char json[64];
+    JsonObject &object = jb.createObject();
+    object.createNestedArray(key);
+    eraseString(key);
+    object.printTo(json, sizeof(json));
+    REQUIRE(std::string("{\"key\":[]}") == json);
+  }
+
+  SECTION("JsonObject_ContainsKey") {
+    char json[] = "{\"key\":\"value\"}";
+    const JsonObject &object = jb.parseObject(json);
+    REQUIRE(true == object.containsKey(std::string("key")));
+  }
+
+  SECTION("JsonObject_Remove") {
+    char json[] = "{\"key\":\"value\"}";
+    JsonObject &object = jb.parseObject(json);
+    REQUIRE(1 == object.size());
+    object.remove(std::string("key"));
+    REQUIRE(0 == object.size());
+  }
+
+  SECTION("JsonObjectSubscript_SetKey") {
+    JsonObject &object = jb.createObject();
+    std::string key("hello");
+    object[key] = "world";
+    eraseString(key);
+    REQUIRE(std::string("world") == object["hello"]);
+  }
+
+  SECTION("JsonObjectSubscript_SetValue") {
+    JsonObject &object = jb.createObject();
+    std::string value("world");
+    object["hello"] = value;
+    eraseString(value);
+    REQUIRE(std::string("world") == object["hello"]);
+  }
+
+  SECTION("JsonArray_Add") {
+    JsonArray &array = jb.createArray();
+    std::string value("hello");
+    array.add(value);
+    eraseString(value);
+    REQUIRE(std::string("hello") == array[0]);
+  }
+
+  SECTION("JsonArray_Set") {
+    JsonArray &array = jb.createArray();
+    std::string value("world");
+    array.add("hello");
+    array.set(0, value);
+    eraseString(value);
+    REQUIRE(std::string("world") == array[0]);
+  }
+
+  SECTION("JsonArraySubscript") {
+    JsonArray &array = jb.createArray();
+    std::string value("world");
+    array.add("hello");
+    array[0] = value;
+    eraseString(value);
+    REQUIRE(std::string("world") == array[0]);
+  }
+
+  SECTION("JsonArray_PrintTo") {
+    JsonArray &array = jb.createArray();
+    array.add(4);
+    array.add(2);
+    std::string json;
+    array.printTo(json);
+    REQUIRE(std::string("[4,2]") == json);
+  }
+
+  SECTION("JsonArray_PrettyPrintTo") {
+    JsonArray &array = jb.createArray();
+    array.add(4);
+    array.add(2);
+    std::string json;
+    array.prettyPrintTo(json);
+    REQUIRE(std::string("[\r\n  4,\r\n  2\r\n]") == json);
+  }
+
+  SECTION("JsonObject_PrintTo") {
+    JsonObject &object = jb.createObject();
+    object["key"] = "value";
+    std::string json;
+    object.printTo(json);
+    REQUIRE(std::string("{\"key\":\"value\"}") == json);
+  }
+
+  SECTION("JsonObject_PrettyPrintTo") {
+    JsonObject &object = jb.createObject();
+    object["key"] = "value";
+    std::string json;
+    object.prettyPrintTo(json);
+    REQUIRE(std::string("{\r\n  \"key\": \"value\"\r\n}") == json);
+  }
+
+  SECTION("JsonBuffer_GrowWhenAddingNewKey") {
+    JsonObject &object = jb.createObject();
+    std::string key1("hello"), key2("world");
+
+    object[key1] = 1;
+    size_t sizeBefore = jb.size();
+    object[key2] = 2;
+    size_t sizeAfter = jb.size();
+
+    REQUIRE(sizeAfter - sizeBefore >= key2.size());
+  }
+
+  SECTION("JsonBuffer_DontGrowWhenReusingKey") {
+    JsonObject &object = jb.createObject();
+    std::string key("hello");
+
+    object[key] = 1;
+    size_t sizeBefore = jb.size();
+    object[key] = 2;
+    size_t sizeAfter = jb.size();
+
+    REQUIRE(sizeBefore == sizeAfter);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/unsigned_char.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/unsigned_char.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ce1c474afcb48c06ac3b5bd4ea92a50ab0c27330
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/unsigned_char.cpp
@@ -0,0 +1,262 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+#if defined(__clang__)
+#define CONFLICTS_WITH_BUILTIN_OPERATOR
+#endif
+
+TEST_CASE("unsigned char string") {
+  SECTION("JsonBuffer::parseArray") {
+    unsigned char json[] = "[42]";
+
+    StaticJsonBuffer<JSON_ARRAY_SIZE(1)> jsonBuffer;
+    JsonArray& arr = jsonBuffer.parseArray(json);
+
+    REQUIRE(true == arr.success());
+  }
+
+  SECTION("JsonBuffer::parseObject") {
+    unsigned char json[] = "{\"a\":42}";
+
+    StaticJsonBuffer<JSON_OBJECT_SIZE(1)> jsonBuffer;
+    JsonObject& obj = jsonBuffer.parseObject(json);
+
+    REQUIRE(true == obj.success());
+  }
+
+  SECTION("JsonVariant constructor") {
+    unsigned char value[] = "42";
+
+    JsonVariant variant(value);
+
+    REQUIRE(42 == variant.as<int>());
+  }
+
+  SECTION("JsonVariant assignment operator") {
+    unsigned char value[] = "42";
+
+    JsonVariant variant(666);
+    variant = value;
+
+    REQUIRE(42 == variant.as<int>());
+  }
+
+#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
+  SECTION("JsonVariant::operator[]") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonVariant variant = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(std::string("world") == variant[key]);
+  }
+#endif
+
+#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
+  SECTION("JsonVariant::operator[] const") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonVariant variant = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(std::string("world") == variant[key]);
+  }
+#endif
+
+  SECTION("JsonVariant::operator==") {
+    unsigned char comparand[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonVariant variant = "hello";
+
+    REQUIRE(comparand == variant);
+    REQUIRE(variant == comparand);
+    REQUIRE_FALSE(comparand != variant);
+    REQUIRE_FALSE(variant != comparand);
+  }
+
+  SECTION("JsonVariant::operator!=") {
+    unsigned char comparand[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonVariant variant = "world";
+
+    REQUIRE(comparand != variant);
+    REQUIRE(variant != comparand);
+    REQUIRE_FALSE(comparand == variant);
+    REQUIRE_FALSE(variant == comparand);
+  }
+
+#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
+  SECTION("JsonObject::operator[]") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj[key] = "world";
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+#endif
+
+  SECTION("JsonObjectSubscript::operator=") {  // issue #416
+    unsigned char value[] = "world";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj["hello"] = value;
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("JsonObjectSubscript::set()") {
+    unsigned char value[] = "world";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj["hello"].set(value);
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
+  SECTION("JsonObject::operator[] const") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(std::string("world") == obj[key]);
+  }
+#endif
+
+  SECTION("JsonObject::get()") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(std::string("world") == obj.get<char*>(key));
+  }
+
+  SECTION("JsonObject::set() key") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set(key, "world");
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("JsonObject::set() value") {
+    unsigned char value[] = "world";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set("hello", value);
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("JsonObject::set key&value") {
+    unsigned char key[] = "world";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set(key, key);
+
+    REQUIRE(std::string("world") == obj["world"]);
+  }
+
+  SECTION("JsonObject::containsKey()") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(true == obj.containsKey(key));
+  }
+
+  SECTION("JsonObject::remove()") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+    obj.remove(key);
+
+    REQUIRE(0 == obj.size());
+  }
+
+  SECTION("JsonObject::is()") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.parseObject("{\"hello\":42}");
+
+    REQUIRE(true == obj.is<int>(key));
+  }
+
+  SECTION("JsonObject::createNestedArray()") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.createNestedArray(key);
+  }
+
+  SECTION("JsonObject::createNestedObject()") {
+    unsigned char key[] = "hello";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.createNestedObject(key);
+  }
+
+  SECTION("JsonArray::add()") {
+    unsigned char value[] = "world";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add(value);
+
+    REQUIRE(std::string("world") == arr[0]);
+  }
+
+  SECTION("JsonArray::set()") {
+    unsigned char value[] = "world";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add("hello");
+    arr.set(0, value);
+
+    REQUIRE(std::string("world") == arr[0]);
+  }
+
+  SECTION("JsonArraySubscript::set()") {
+    unsigned char value[] = "world";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add("hello");
+    arr[0].set(value);
+
+    REQUIRE(std::string("world") == arr[0]);
+  }
+
+  SECTION("JsonArraySubscript::operator=") {
+    unsigned char value[] = "world";
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add("hello");
+    arr[0] = value;
+
+    REQUIRE(std::string("world") == arr[0]);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/vla.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/vla.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e9d1cb676476a913bcecb87046887c046a402869
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Misc/vla.cpp
@@ -0,0 +1,331 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+#if defined(__clang__)
+#pragma clang diagnostic ignored "-Wvla-extension"
+#define CONFLICTS_WITH_BUILTIN_OPERATOR
+#elif defined(__GNUC__)
+#pragma GCC diagnostic ignored "-Wvla"
+#else
+#define VLA_NOT_SUPPORTED
+#endif
+
+#ifndef VLA_NOT_SUPPORTED
+
+TEST_CASE("Variable Length Array") {
+  SECTION("ParseArray") {
+    int i = 8;
+    char vla[i];
+    strcpy(vla, "[42]");
+
+    StaticJsonBuffer<JSON_ARRAY_SIZE(1)> jsonBuffer;
+    JsonArray& arr = jsonBuffer.parseArray(vla);
+
+    REQUIRE(true == arr.success());
+  }
+
+  SECTION("ParseObject") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "{\"a\":42}");
+
+    StaticJsonBuffer<JSON_OBJECT_SIZE(1)> jsonBuffer;
+    JsonObject& obj = jsonBuffer.parseObject(vla);
+
+    REQUIRE(true == obj.success());
+  }
+
+  SECTION("Parse") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "42");
+
+    StaticJsonBuffer<1> jsonBuffer;
+    JsonVariant variant = jsonBuffer.parse(vla);
+
+    REQUIRE(42 == variant.as<int>());
+  }
+
+  SECTION("JsonVariant_Constructor") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "42");
+
+    JsonVariant variant(vla);
+
+    REQUIRE(42 == variant.as<int>());
+  }
+
+  SECTION("JsonVariant_Assign") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "42");
+
+    JsonVariant variant(666);
+    variant = vla;
+
+    REQUIRE(42 == variant.as<int>());
+  }
+
+#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
+  SECTION("JsonVariant_Subscript") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonVariant variant = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(std::string("world") == variant[vla]);
+  }
+#endif
+
+#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
+  SECTION("JsonVariant_Subscript_Const") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonVariant variant = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(std::string("world") == variant[vla]);
+  }
+#endif
+
+  SECTION("JsonVariant_Equals") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonVariant variant = "hello";
+
+    REQUIRE((vla == variant));
+    REQUIRE((variant == vla));
+    REQUIRE_FALSE((vla != variant));
+    REQUIRE_FALSE((variant != vla));
+  }
+
+  SECTION("JsonVariant_Differs") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonVariant variant = "world";
+
+    REQUIRE((vla != variant));
+    REQUIRE((variant != vla));
+    REQUIRE_FALSE((vla == variant));
+    REQUIRE_FALSE((variant == vla));
+  }
+
+#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
+  SECTION("JsonObject_Subscript") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj[vla] = "world";
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+#endif
+
+  SECTION("JsonObject_Subscript_Assign") {  // issue #416
+    int i = 32;
+    char vla[i];
+    strcpy(vla, "world");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj["hello"] = vla;
+
+    REQUIRE(std::string("world") == obj["hello"].as<char*>());
+  }
+
+  SECTION("JsonObject_Subscript_Set") {
+    int i = 32;
+    char vla[i];
+    strcpy(vla, "world");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj["hello"].set(vla);
+
+    REQUIRE(std::string("world") == obj["hello"].as<char*>());
+  }
+
+#ifndef CONFLICTS_WITH_BUILTIN_OPERATOR
+  SECTION("JsonObject_Subscript_Const") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(std::string("world") == obj[vla]);
+  }
+#endif
+
+  SECTION("JsonObject_Get") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(std::string("world") == obj.get<char*>(vla));
+  }
+
+  SECTION("JsonObject_Set_Key") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set(vla, "world");
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("JsonObject_Set_Value") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "world");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set("hello", vla);
+
+    REQUIRE(std::string("world") == obj["hello"]);
+  }
+
+  SECTION("JsonObject_Set_KeyAndValue") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "world");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.set(vla, vla);
+
+    REQUIRE(std::string("world") == obj["world"]);
+  }
+
+  SECTION("JsonObject_ContainsKey") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    const JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+
+    REQUIRE(true == obj.containsKey(vla));
+  }
+
+  SECTION("JsonObject_Remove") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.parseObject("{\"hello\":\"world\"}");
+    obj.remove(vla);
+
+    REQUIRE(0 == obj.size());
+  }
+
+  SECTION("JsonObject_Is") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.parseObject("{\"hello\":42}");
+
+    REQUIRE(true == obj.is<int>(vla));
+  }
+
+  SECTION("JsonObject_CreateNestedArray") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.createNestedArray(vla);
+  }
+
+  SECTION("JsonObject_CreateNestedObject") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "hello");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonObject& obj = jsonBuffer.createObject();
+    obj.createNestedObject(vla);
+  }
+
+  SECTION("JsonArray_Add") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "world");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add(vla);
+
+    REQUIRE(std::string("world") == arr[0]);
+  }
+
+  SECTION("JsonArray_Set") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "world");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add("hello");
+    arr.set(0, vla);
+
+    REQUIRE(std::string("world") == arr[0]);
+  }
+
+  SECTION("JsonArraySubscript_Set") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "world");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add("hello");
+    arr[0].set(vla);
+
+    REQUIRE(std::string("world") == arr[0]);
+  }
+
+  SECTION("JsonArraySubscript_Assign") {
+    int i = 16;
+    char vla[i];
+    strcpy(vla, "world");
+
+    DynamicJsonBuffer jsonBuffer;
+    JsonArray& arr = jsonBuffer.createArray();
+    arr.add("hello");
+    arr[0] = vla;
+
+    REQUIRE(std::string("world") == arr[0]);
+  }
+}
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d96ce719de8004d9cc93ca5a4a92617e74092f98
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/CMakeLists.txt
@@ -0,0 +1,13 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(PolyfillsTests 
+	isFloat.cpp
+	isInteger.cpp
+	parseFloat.cpp
+	parseInteger.cpp
+)
+
+target_link_libraries(PolyfillsTests catch)
+add_test(Polyfills PolyfillsTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/isFloat.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/isFloat.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..225d52214e9c2acaafe11cc54f412ffe25b93e4f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/isFloat.cpp
@@ -0,0 +1,76 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson/Polyfills/isFloat.hpp>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+TEST_CASE("isFloat()") {
+  SECTION("Input is NULL") {
+    REQUIRE(isFloat(NULL) == false);
+  }
+
+  SECTION("NoExponent") {
+    REQUIRE(isFloat("3.14"));
+    REQUIRE(isFloat("-3.14"));
+    REQUIRE(isFloat("+3.14"));
+  }
+
+  SECTION("IntegralPartMissing") {
+    REQUIRE(isFloat(".14"));
+    REQUIRE(isFloat("-.14"));
+    REQUIRE(isFloat("+.14"));
+  }
+
+  SECTION("FractionalPartMissing") {
+    REQUIRE(isFloat("3."));
+    REQUIRE(isFloat("-3.e14"));
+    REQUIRE(isFloat("+3.e-14"));
+  }
+
+  SECTION("NoDot") {
+    REQUIRE(isFloat("3e14"));
+    REQUIRE(isFloat("3e-14"));
+    REQUIRE(isFloat("3e+14"));
+  }
+
+  SECTION("Integer") {
+    REQUIRE(isFloat("14"));
+    REQUIRE(isFloat("-14"));
+    REQUIRE(isFloat("+14"));
+  }
+
+  SECTION("ExponentMissing") {
+    REQUIRE_FALSE(isFloat("3.14e"));
+    REQUIRE_FALSE(isFloat("3.14e-"));
+    REQUIRE_FALSE(isFloat("3.14e+"));
+  }
+
+  SECTION("JustASign") {
+    REQUIRE_FALSE(isFloat("-"));
+    REQUIRE_FALSE(isFloat("+"));
+  }
+
+  SECTION("Empty") {
+    REQUIRE_FALSE(isFloat(""));
+  }
+
+  SECTION("NaN") {
+    REQUIRE(isFloat("NaN"));
+    REQUIRE_FALSE(isFloat("n"));
+    REQUIRE_FALSE(isFloat("N"));
+    REQUIRE_FALSE(isFloat("nan"));
+    REQUIRE_FALSE(isFloat("-NaN"));
+    REQUIRE_FALSE(isFloat("+NaN"));
+  }
+
+  SECTION("Infinity") {
+    REQUIRE(isFloat("Infinity"));
+    REQUIRE(isFloat("+Infinity"));
+    REQUIRE(isFloat("-Infinity"));
+    REQUIRE_FALSE(isFloat("infinity"));
+    REQUIRE_FALSE(isFloat("Inf"));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/isInteger.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/isInteger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f0bec4ae2643b1fbab436cdd9e1fd50670ac2c11
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/isInteger.cpp
@@ -0,0 +1,36 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson/Polyfills/isInteger.hpp>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+TEST_CASE("isInteger()") {
+  SECTION("Null") {
+    REQUIRE_FALSE(isInteger(NULL));
+  }
+
+  SECTION("FloatNotInteger") {
+    REQUIRE_FALSE(isInteger("3.14"));
+    REQUIRE_FALSE(isInteger("-3.14"));
+    REQUIRE_FALSE(isInteger("+3.14"));
+  }
+
+  SECTION("Spaces") {
+    REQUIRE_FALSE(isInteger("42 "));
+    REQUIRE_FALSE(isInteger(" 42"));
+  }
+
+  SECTION("Valid") {
+    REQUIRE(isInteger("42"));
+    REQUIRE(isInteger("-42"));
+    REQUIRE(isInteger("+42"));
+  }
+
+  SECTION("ExtraSign") {
+    REQUIRE_FALSE(isInteger("--42"));
+    REQUIRE_FALSE(isInteger("++42"));
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/parseFloat.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/parseFloat.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..362e46d6f8abe8420c7a699a4fce35b82cf2d929
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/parseFloat.cpp
@@ -0,0 +1,177 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson/Polyfills/parseFloat.hpp>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+template <typename T>
+void check(const char* input, T expected) {
+  CAPTURE(input);
+  REQUIRE(parseFloat<T>(input) == Approx(expected));
+}
+
+template <typename T>
+void checkNaN(const char* input) {
+  CAPTURE(input);
+  T result = parseFloat<T>(input);
+  REQUIRE(result != result);
+}
+
+template <typename T>
+void checkInf(const char* input, bool negative) {
+  CAPTURE(input);
+  T x = parseFloat<T>(input);
+  if (negative)
+    REQUIRE(x < 0);
+  else
+    REQUIRE(x > 0);
+  REQUIRE(x == x);      // not a NaN
+  REQUIRE(x * 2 == x);  // a property of infinity
+}
+
+TEST_CASE("parseFloat<float>()") {
+  SECTION("Null") {
+    check<float>(NULL, 0);
+  }
+
+  SECTION("Float_Short_NoExponent") {
+    check<float>("3.14", 3.14f);
+    check<float>("-3.14", -3.14f);
+    check<float>("+3.14", +3.14f);
+  }
+
+  SECTION("Short_NoDot") {
+    check<float>("1E+38", 1E+38f);
+    check<float>("-1E+38", -1E+38f);
+    check<float>("+1E-38", +1E-38f);
+    check<float>("+1e+38", +1e+38f);
+    check<float>("-1e-38", -1e-38f);
+  }
+
+  SECTION("Max") {
+    check<float>("340.2823e+36", 3.402823e+38f);
+    check<float>("34.02823e+37", 3.402823e+38f);
+    check<float>("3.402823e+38", 3.402823e+38f);
+    check<float>("0.3402823e+39", 3.402823e+38f);
+    check<float>("0.03402823e+40", 3.402823e+38f);
+    check<float>("0.003402823e+41", 3.402823e+38f);
+  }
+
+  SECTION("VeryLong") {
+    check<float>("0.00000000000000000000000000000001", 1e-32f);
+    check<float>("100000000000000000000000000000000.0", 1e+32f);
+    check<float>(
+        "100000000000000000000000000000000.00000000000000000000000000000",
+        1e+32f);
+  }
+
+  SECTION("MantissaTooLongToFit") {
+    check<float>("0.340282346638528861111111111111", 0.34028234663852886f);
+    check<float>("34028234663852886.11111111111111", 34028234663852886.0f);
+    check<float>("34028234.66385288611111111111111", 34028234.663852886f);
+
+    check<float>("-0.340282346638528861111111111111", -0.34028234663852886f);
+    check<float>("-34028234663852886.11111111111111", -34028234663852886.0f);
+    check<float>("-34028234.66385288611111111111111", -34028234.663852886f);
+  }
+
+  SECTION("ExponentTooBig") {
+    checkInf<float>("1e39", false);
+    checkInf<float>("-1e39", true);
+    checkInf<float>("1e255", false);
+    check<float>("1e-255", 0.0f);
+  }
+
+  SECTION("NaN") {
+    checkNaN<float>("NaN");
+    checkNaN<float>("nan");
+  }
+
+  SECTION("Infinity") {
+    checkInf<float>("Infinity", false);
+    checkInf<float>("+Infinity", false);
+    checkInf<float>("-Infinity", true);
+    checkInf<float>("inf", false);
+    checkInf<float>("+inf", false);
+    checkInf<float>("-inf", true);
+  }
+
+  SECTION("Boolean") {
+    check<float>("false", 0.0f);
+    check<float>("true", 1.0f);
+  }
+}
+
+TEST_CASE("parseFloat<double>()") {
+  SECTION("Null") {
+    check<double>(NULL, 0);
+  }
+
+  SECTION("Short_NoExponent") {
+    check<double>("3.14", 3.14);
+    check<double>("-3.14", -3.14);
+    check<double>("+3.14", +3.14);
+  }
+
+  SECTION("Short_NoDot") {
+    check<double>("1E+308", 1E+308);
+    check<double>("-1E+308", -1E+308);
+    check<double>("+1E-308", +1E-308);
+    check<double>("+1e+308", +1e+308);
+    check<double>("-1e-308", -1e-308);
+  }
+
+  SECTION("Max") {
+    check<double>(".017976931348623147e+310", 1.7976931348623147e+308);
+    check<double>(".17976931348623147e+309", 1.7976931348623147e+308);
+    check<double>("1.7976931348623147e+308", 1.7976931348623147e+308);
+    check<double>("17.976931348623147e+307", 1.7976931348623147e+308);
+    check<double>("179.76931348623147e+306", 1.7976931348623147e+308);
+  }
+
+  SECTION("Min") {
+    check<double>(".022250738585072014e-306", 2.2250738585072014e-308);
+    check<double>(".22250738585072014e-307", 2.2250738585072014e-308);
+    check<double>("2.2250738585072014e-308", 2.2250738585072014e-308);
+    check<double>("22.250738585072014e-309", 2.2250738585072014e-308);
+    check<double>("222.50738585072014e-310", 2.2250738585072014e-308);
+  }
+
+  SECTION("VeryLong") {
+    check<double>("0.00000000000000000000000000000001", 1e-32);
+    check<double>("100000000000000000000000000000000.0", 1e+32);
+    check<double>(
+        "100000000000000000000000000000000.00000000000000000000000000000",
+        1e+32);
+  }
+
+  SECTION("MantissaTooLongToFit") {
+    check<double>("0.179769313486231571111111111111", 0.17976931348623157);
+    check<double>("17976931348623157.11111111111111", 17976931348623157.0);
+    check<double>("1797693.134862315711111111111111", 1797693.1348623157);
+
+    check<double>("-0.179769313486231571111111111111", -0.17976931348623157);
+    check<double>("-17976931348623157.11111111111111", -17976931348623157.0);
+    check<double>("-1797693.134862315711111111111111", -1797693.1348623157);
+  }
+
+  SECTION("ExponentTooBig") {
+    checkInf<double>("1e309", false);
+    checkInf<double>("-1e309", true);
+    checkInf<double>("1e65535", false);
+    check<double>("1e-65535", 0.0);
+  }
+
+  SECTION("NaN") {
+    checkNaN<double>("NaN");
+    checkNaN<double>("nan");
+  }
+
+  SECTION("Boolean") {
+    check<double>("false", 0.0);
+    check<double>("true", 1.0);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/parseInteger.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/parseInteger.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d25bdb2543212dc10e9c8d5e1f5f61cfc5bb600
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/Polyfills/parseInteger.cpp
@@ -0,0 +1,79 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <stdint.h>
+#include <ArduinoJson/Polyfills/parseInteger.hpp>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+template <typename T>
+void check(const char* input, T expected) {
+  CAPTURE(input);
+  T actual = parseInteger<T>(input);
+  REQUIRE(expected == actual);
+}
+
+TEST_CASE("parseInteger<int8_t>()") {
+  check<int8_t>("-128", -128);
+  check<int8_t>("127", 127);
+  check<int8_t>("+127", 127);
+  check<int8_t>("3.14", 3);
+  check<int8_t>("x42", 0);
+  check<int8_t>("128", -128);
+  check<int8_t>("-129", 127);
+  check<int8_t>(NULL, 0);
+  check<int8_t>("true", 1);
+  check<int8_t>("false", 0);
+}
+
+TEST_CASE("parseInteger<int16_t>()") {
+  check<int16_t>("-32768", -32768);
+  check<int16_t>("32767", 32767);
+  check<int16_t>("+32767", 32767);
+  check<int16_t>("3.14", 3);
+  check<int16_t>("x42", 0);
+  check<int16_t>("-32769", 32767);
+  check<int16_t>("32768", -32768);
+  check<int16_t>(NULL, 0);
+  check<int16_t>("true", 1);
+  check<int16_t>("false", 0);
+}
+
+TEST_CASE("parseInteger<int32_t>()") {
+  check<int32_t>("-2147483648", (-2147483647 - 1));
+  check<int32_t>("2147483647", 2147483647);
+  check<int32_t>("+2147483647", 2147483647);
+  check<int32_t>("3.14", 3);
+  check<int32_t>("x42", 0);
+  check<int32_t>("-2147483649", 2147483647);
+  check<int32_t>("2147483648", (-2147483647 - 1));
+  check<int32_t>("true", 1);
+  check<int32_t>("false", 0);
+}
+
+TEST_CASE("parseInteger<uint8_t>()") {
+  check<uint8_t>("0", 0);
+  check<uint8_t>("255", 255);
+  check<uint8_t>("+255", 255);
+  check<uint8_t>("3.14", 3);
+  check<uint8_t>("x42", 0);
+  check<uint8_t>("-1", 255);
+  check<uint8_t>("256", 0);
+  check<uint8_t>("true", 1);
+  check<uint8_t>("false", 0);
+}
+
+TEST_CASE("parseInteger<uint16_t>()") {
+  check<uint16_t>("0", 0);
+  check<uint16_t>("65535", 65535);
+  check<uint16_t>("+65535", 65535);
+  check<uint16_t>("3.14", 3);
+  // check<uint16_t>(" 42", 0);
+  check<uint16_t>("x42", 0);
+  check<uint16_t>("-1", 65535);
+  check<uint16_t>("65536", 0);
+  check<uint16_t>("true", 1);
+  check<uint16_t>("false", 0);
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..00c78dfa5986024650b84651250fc9f443e624c0
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/CMakeLists.txt
@@ -0,0 +1,16 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_executable(StaticJsonBufferTests 
+	alloc.cpp
+	createArray.cpp
+	createObject.cpp
+	parseArray.cpp
+	parseObject.cpp
+	size.cpp
+	startString.cpp
+)
+
+target_link_libraries(StaticJsonBufferTests catch)
+add_test(StaticJsonBuffer StaticJsonBufferTests)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/alloc.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/alloc.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9141def8282cf902bfca875ebb4cceb4275c7f6e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/alloc.cpp
@@ -0,0 +1,52 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+static bool isAligned(void *ptr) {
+  const size_t mask = sizeof(void *) - 1;
+  size_t addr = reinterpret_cast<size_t>(ptr);
+  return (addr & mask) == 0;
+}
+
+TEST_CASE("StaticJsonBuffer::alloc()") {
+  StaticJsonBuffer<64> buffer;
+
+  SECTION("Returns different addresses") {
+    void *p1 = buffer.alloc(1);
+    void *p2 = buffer.alloc(1);
+    REQUIRE(p1 != p2);
+  }
+
+  SECTION("Returns non-NULL when using full capacity") {
+    void *p = buffer.alloc(64);
+    REQUIRE(0 != p);
+  }
+
+  SECTION("Returns NULL when full") {
+    buffer.alloc(64);
+    void *p = buffer.alloc(1);
+    REQUIRE(0 == p);
+  }
+
+  SECTION("Returns NULL when buffer is too small") {
+    void *p = buffer.alloc(65);
+    REQUIRE(0 == p);
+  }
+
+  SECTION("Returns aligned pointers") {
+    for (size_t size = 1; size <= sizeof(void *); size++) {
+      void *p = buffer.alloc(1);
+      REQUIRE(isAligned(p));
+    }
+  }
+
+  SECTION("Returns same address after clear()") {
+    void *p1 = buffer.alloc(1);
+    buffer.clear();
+    void *p2 = buffer.alloc(1);
+    REQUIRE(p1 == p2);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/createArray.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/createArray.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9f0d3bd5f73f34d693d5173484f03674d25d36c5
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/createArray.cpp
@@ -0,0 +1,45 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("StaticJsonBuffer::createArray()") {
+  SECTION("GrowsWithArray") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(2)> json;
+
+    JsonArray &array = json.createArray();
+    REQUIRE(JSON_ARRAY_SIZE(0) == json.size());
+
+    array.add("hello");
+    REQUIRE(JSON_ARRAY_SIZE(1) == json.size());
+
+    array.add("world");
+    REQUIRE(JSON_ARRAY_SIZE(2) == json.size());
+  }
+
+  SECTION("SucceedWhenBigEnough") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(0)> json;
+
+    JsonArray &array = json.createArray();
+    REQUIRE(array.success());
+  }
+
+  SECTION("FailsWhenTooSmall") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(0) - 1> json;
+
+    JsonArray &array = json.createArray();
+    REQUIRE_FALSE(array.success());
+  }
+
+  SECTION("ArrayDoesntGrowWhenFull") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(1)> json;
+
+    JsonArray &array = json.createArray();
+    array.add("hello");
+    array.add("world");
+
+    REQUIRE(1 == array.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/createObject.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/createObject.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c51e9bb959361b75bab3650f15a82c2643a9c9fc
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/createObject.cpp
@@ -0,0 +1,56 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("StaticJsonBuffer::createObject()") {
+  SECTION("GrowsWithObject") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(3)> buffer;
+
+    JsonObject &obj = buffer.createObject();
+    REQUIRE(JSON_OBJECT_SIZE(0) == buffer.size());
+
+    obj["hello"];
+    REQUIRE(JSON_OBJECT_SIZE(0) == buffer.size());
+
+    obj["hello"] = 1;
+    REQUIRE(JSON_OBJECT_SIZE(1) == buffer.size());
+
+    obj["world"] = 2;
+    REQUIRE(JSON_OBJECT_SIZE(2) == buffer.size());
+
+    obj["world"] = 3;  // <- same key, should not grow
+    REQUIRE(JSON_OBJECT_SIZE(2) == buffer.size());
+  }
+
+  SECTION("SucceedWhenBigEnough") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(0)> buffer;
+
+    JsonObject &object = buffer.createObject();
+    REQUIRE(object.success());
+  }
+
+  SECTION("FailsWhenTooSmall") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(0) - 1> buffer;
+
+    JsonObject &object = buffer.createObject();
+    REQUIRE_FALSE(object.success());
+  }
+
+  SECTION("ObjectDoesntGrowWhenFull") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(1)> buffer;
+
+    JsonObject &obj = buffer.createObject();
+    obj["hello"] = 1;
+    obj["world"] = 2;
+
+    REQUIRE(JSON_OBJECT_SIZE(1) == buffer.size());
+    REQUIRE(1 == obj.size());
+
+    char json[64];
+    obj.printTo(json, sizeof(json));
+    REQUIRE(std::string("{\"hello\":1}") == json);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/parseArray.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/parseArray.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f2feda995bef292fbec04b48d7676c1f40a67263
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/parseArray.cpp
@@ -0,0 +1,71 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("StaticJsonBuffer::parseArray()") {
+  SECTION("TooSmallBufferForEmptyArray") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(0) - 1> bufferTooSmall;
+    char input[] = "[]";
+    JsonArray& arr = bufferTooSmall.parseArray(input);
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("BufferOfTheRightSizeForEmptyArray") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(0)> bufferOfRightSize;
+    char input[] = "[]";
+    JsonArray& arr = bufferOfRightSize.parseArray(input);
+    REQUIRE(arr.success());
+  }
+
+  SECTION("TooSmallBufferForArrayWithOneValue") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(1) - 1> bufferTooSmall;
+    char input[] = "[1]";
+    JsonArray& arr = bufferTooSmall.parseArray(input);
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("BufferOfTheRightSizeForArrayWithOneValue") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(1)> bufferOfRightSize;
+    char input[] = "[1]";
+    JsonArray& arr = bufferOfRightSize.parseArray(input);
+    REQUIRE(arr.success());
+  }
+
+  SECTION("TooSmallBufferForArrayWithNestedObject") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(0) - 1>
+        bufferTooSmall;
+    char input[] = "[{}]";
+    JsonArray& arr = bufferTooSmall.parseArray(input);
+    REQUIRE_FALSE(arr.success());
+  }
+
+  SECTION("BufferOfTheRightSizeForArrayWithNestedObject") {
+    StaticJsonBuffer<JSON_ARRAY_SIZE(1) + JSON_OBJECT_SIZE(0)>
+        bufferOfRightSize;
+    char input[] = "[{}]";
+    JsonArray& arr = bufferOfRightSize.parseArray(input);
+    REQUIRE(arr.success());
+  }
+
+  SECTION("CharPtrNull") {
+    REQUIRE_FALSE(
+        StaticJsonBuffer<100>().parseArray(static_cast<char*>(0)).success());
+  }
+
+  SECTION("ConstCharPtrNull") {
+    REQUIRE_FALSE(StaticJsonBuffer<100>()
+                      .parseArray(static_cast<const char*>(0))
+                      .success());
+  }
+
+  SECTION("CopyStringNotSpaces") {
+    StaticJsonBuffer<100> jsonBuffer;
+    jsonBuffer.parseArray("  [ \"1234567\" ] ");
+    REQUIRE(JSON_ARRAY_SIZE(1) + sizeof("1234567") == jsonBuffer.size());
+    // note we use a string of 8 bytes to be sure that the StaticJsonBuffer
+    // will not insert bytes to enforce alignement
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/parseObject.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/parseObject.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..83601d6726d629c58f2904a72cf57c9d5bef200e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/parseObject.cpp
@@ -0,0 +1,62 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+TEST_CASE("StaticJsonBuffer::parseObject()") {
+  SECTION("TooSmallBufferForEmptyObject") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(0) - 1> bufferTooSmall;
+    char input[] = "{}";
+    JsonObject& obj = bufferTooSmall.parseObject(input);
+    REQUIRE_FALSE(obj.success());
+  }
+
+  SECTION("BufferOfTheRightSizeForEmptyObject") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(0)> bufferOfRightSize;
+    char input[] = "{}";
+    JsonObject& obj = bufferOfRightSize.parseObject(input);
+    REQUIRE(obj.success());
+  }
+
+  SECTION("TooSmallBufferForObjectWithOneValue") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(1) - 1> bufferTooSmall;
+    char input[] = "{\"a\":1}";
+    JsonObject& obj = bufferTooSmall.parseObject(input);
+    REQUIRE_FALSE(obj.success());
+  }
+
+  SECTION("BufferOfTheRightSizeForObjectWithOneValue") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(1)> bufferOfRightSize;
+    char input[] = "{\"a\":1}";
+    JsonObject& obj = bufferOfRightSize.parseObject(input);
+    REQUIRE(obj.success());
+  }
+
+  SECTION("TooSmallBufferForObjectWithNestedObject") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(0) - 1>
+        bufferTooSmall;
+    char input[] = "{\"a\":[]}";
+    JsonObject& obj = bufferTooSmall.parseObject(input);
+    REQUIRE_FALSE(obj.success());
+  }
+
+  SECTION("BufferOfTheRightSizeForObjectWithNestedObject") {
+    StaticJsonBuffer<JSON_OBJECT_SIZE(1) + JSON_ARRAY_SIZE(0)>
+        bufferOfRightSize;
+    char input[] = "{\"a\":[]}";
+    JsonObject& obj = bufferOfRightSize.parseObject(input);
+    REQUIRE(obj.success());
+  }
+
+  SECTION("CharPtrNull") {
+    REQUIRE_FALSE(
+        StaticJsonBuffer<100>().parseObject(static_cast<char*>(0)).success());
+  }
+
+  SECTION("ConstCharPtrNull") {
+    REQUIRE_FALSE(StaticJsonBuffer<100>()
+                      .parseObject(static_cast<const char*>(0))
+                      .success());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/size.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/size.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5ef76bf4f15c0238d2c0e05f62a5c4e15197054c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/size.cpp
@@ -0,0 +1,42 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+TEST_CASE("StaticJsonBuffer::size()") {
+  StaticJsonBuffer<64> buffer;
+
+  SECTION("Capacity equals template parameter") {
+    REQUIRE(64 == buffer.capacity());
+  }
+
+  SECTION("Initial size is 0") {
+    REQUIRE(0 == buffer.size());
+  }
+
+  SECTION("Increases after alloc()") {
+    buffer.alloc(1);
+    REQUIRE(1U <= buffer.size());
+    buffer.alloc(1);
+    REQUIRE(2U <= buffer.size());
+  }
+
+  SECTION("Doesn't grow when buffer is full") {
+    buffer.alloc(64);
+    buffer.alloc(1);
+    REQUIRE(64 == buffer.size());
+  }
+
+  SECTION("Does't grow when buffer is too small for alloc") {
+    buffer.alloc(65);
+    REQUIRE(0 == buffer.size());
+  }
+
+  SECTION("Goes back to zero after clear()") {
+    buffer.alloc(1);
+    buffer.clear();
+    REQUIRE(0 == buffer.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/startString.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/startString.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..282823b7070ed1d111e978b749c0ef8d868bf13f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/test/StaticJsonBuffer/startString.cpp
@@ -0,0 +1,49 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#include <ArduinoJson.h>
+#include <catch.hpp>
+
+using namespace ArduinoJson::Internals;
+
+TEST_CASE("StaticJsonBuffer::startString()") {
+  SECTION("WorksWhenBufferIsBigEnough") {
+    StaticJsonBuffer<6> jsonBuffer;
+
+    StaticJsonBufferBase::String str = jsonBuffer.startString();
+    str.append('h');
+    str.append('e');
+    str.append('l');
+    str.append('l');
+    str.append('o');
+
+    REQUIRE(std::string("hello") == str.c_str());
+  }
+
+  SECTION("ReturnsNullWhenTooSmall") {
+    StaticJsonBuffer<5> jsonBuffer;
+
+    StaticJsonBufferBase::String str = jsonBuffer.startString();
+    str.append('h');
+    str.append('e');
+    str.append('l');
+    str.append('l');
+    str.append('o');
+
+    REQUIRE(0 == str.c_str());
+  }
+
+  SECTION("SizeIncreases") {
+    StaticJsonBuffer<5> jsonBuffer;
+
+    StaticJsonBufferBase::String str = jsonBuffer.startString();
+    REQUIRE(0 == jsonBuffer.size());
+
+    str.append('h');
+    REQUIRE(1 == jsonBuffer.size());
+
+    str.c_str();
+    REQUIRE(2 == jsonBuffer.size());
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/third-party/catch/CMakeLists.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/third-party/catch/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..59515f54132a9eceb69535b8991a8c9021d8e92c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/third-party/catch/CMakeLists.txt
@@ -0,0 +1,13 @@
+# ArduinoJson - arduinojson.org
+# Copyright Benoit Blanchon 2014-2018
+# MIT License
+
+add_library(catch
+	catch.hpp
+	catch.cpp
+)
+
+target_include_directories(catch
+	PUBLIC
+	${CMAKE_CURRENT_SOURCE_DIR}
+)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/third-party/catch/catch.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/third-party/catch/catch.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2eb9b09eca459860edeee06d173bc55afe1650e6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/third-party/catch/catch.cpp
@@ -0,0 +1,6 @@
+// ArduinoJson - arduinojson.org
+// Copyright Benoit Blanchon 2014-2018
+// MIT License
+
+#define CATCH_CONFIG_MAIN
+#include "catch.hpp"
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/third-party/catch/catch.hpp b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/third-party/catch/catch.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7c351e93181f7488bc2cba2b473d70a4d8e3de9b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ArduinoJson/third-party/catch/catch.hpp
@@ -0,0 +1,11618 @@
+/*
+ *  Catch v1.9.7
+ *  Generated: 2017-08-10 23:49:15.233907
+ *  ----------------------------------------------------------
+ *  This file has been merged from multiple headers. Please don't edit it directly
+ *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
+ *
+ *  Distributed under the Boost Software License, Version 1.0. (See accompanying
+ *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
+#define TWOBLUECUBES_CATCH_HPP_INCLUDED
+
+#ifdef __clang__
+#    pragma clang system_header
+#elif defined __GNUC__
+#    pragma GCC system_header
+#endif
+
+// #included from: internal/catch_suppress_warnings.h
+
+#ifdef __clang__
+#   ifdef __ICC // icpc defines the __clang__ macro
+#       pragma warning(push)
+#       pragma warning(disable: 161 1682)
+#   else // __ICC
+#       pragma clang diagnostic ignored "-Wglobal-constructors"
+#       pragma clang diagnostic ignored "-Wvariadic-macros"
+#       pragma clang diagnostic ignored "-Wc99-extensions"
+#       pragma clang diagnostic ignored "-Wunused-variable"
+#       pragma clang diagnostic push
+#       pragma clang diagnostic ignored "-Wpadded"
+#       pragma clang diagnostic ignored "-Wc++98-compat"
+#       pragma clang diagnostic ignored "-Wc++98-compat-pedantic"
+#       pragma clang diagnostic ignored "-Wswitch-enum"
+#       pragma clang diagnostic ignored "-Wcovered-switch-default"
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic ignored "-Wvariadic-macros"
+#    pragma GCC diagnostic ignored "-Wunused-variable"
+#    pragma GCC diagnostic ignored "-Wparentheses"
+
+#    pragma GCC diagnostic push
+#    pragma GCC diagnostic ignored "-Wpadded"
+#endif
+#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
+#  define CATCH_IMPL
+#endif
+
+#ifdef CATCH_IMPL
+#  ifndef CLARA_CONFIG_MAIN
+#    define CLARA_CONFIG_MAIN_NOT_DEFINED
+#    define CLARA_CONFIG_MAIN
+#  endif
+#endif
+
+// #included from: internal/catch_notimplemented_exception.h
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_H_INCLUDED
+
+// #included from: catch_common.h
+#define TWOBLUECUBES_CATCH_COMMON_H_INCLUDED
+
+// #included from: catch_compiler_capabilities.h
+#define TWOBLUECUBES_CATCH_COMPILER_CAPABILITIES_HPP_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CATCH_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CATCH_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CATCH_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CATCH_CONFIG_CPP11_IS_ENUM : std::is_enum is supported?
+// CATCH_CONFIG_CPP11_TUPLE : std::tuple is supported
+// CATCH_CONFIG_CPP11_LONG_LONG : is long long supported?
+// CATCH_CONFIG_CPP11_OVERRIDE : is override supported?
+// CATCH_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+// CATCH_CONFIG_CPP11_SHUFFLE : is std::shuffle supported?
+// CATCH_CONFIG_CPP11_TYPE_TRAITS : are type_traits and enable_if supported?
+
+// CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
+// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
+// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
+// ****************
+// Note to maintainers: if new toggles are added please document them
+// in configuration.md, too
+// ****************
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CATCH_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
+
+#ifdef __cplusplus
+
+#  if __cplusplus >= 201103L
+#    define CATCH_CPP11_OR_GREATER
+#  endif
+
+#  if __cplusplus >= 201402L
+#    define CATCH_CPP14_OR_GREATER
+#  endif
+
+#endif
+
+#ifdef __clang__
+
+#  if __has_feature(cxx_nullptr)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  if __has_feature(cxx_noexcept)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#   if defined(CATCH_CPP11_OR_GREATER)
+#       define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+            _Pragma( "clang diagnostic push" ) \
+            _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" )
+#       define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+            _Pragma( "clang diagnostic pop" )
+
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "clang diagnostic push" ) \
+            _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+#       define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+            _Pragma( "clang diagnostic pop" )
+#   endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// We know some environments not to support full POSIX signals
+#if defined(__CYGWIN__) || defined(__QNX__)
+
+#   if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#   endif
+
+#endif
+
+#ifdef __OS400__
+#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
+#       define CATCH_CONFIG_COLOUR_NONE
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// Cygwin
+#ifdef __CYGWIN__
+
+// Required for some versions of Cygwin to declare gettimeofday
+// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
+#   define _BSD_SOURCE
+
+#endif // __CYGWIN__
+
+////////////////////////////////////////////////////////////////////////////////
+// Borland
+#ifdef __BORLANDC__
+
+#endif // __BORLANDC__
+
+////////////////////////////////////////////////////////////////////////////////
+// EDG
+#ifdef __EDG_VERSION__
+
+#endif // __EDG_VERSION__
+
+////////////////////////////////////////////////////////////////////////////////
+// Digital Mars
+#ifdef __DMC__
+
+#endif // __DMC__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#   if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#       define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
+
+#if (_MSC_VER >= 1600)
+#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Use variadic macros if the compiler supports them
+#if ( defined _MSC_VER && _MSC_VER > 1400 && !defined __EDGE__) || \
+    ( defined __WAVE__ && __WAVE_HAS_VARIADICS ) || \
+    ( defined __GNUC__ && __GNUC__ >= 3 ) || \
+    ( !defined __cplusplus && __STDC_VERSION__ >= 199901L || __cplusplus >= 201103L )
+
+#define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+
+#endif
+
+// Use __COUNTER__ if the compiler supports it
+#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
+    ( defined __GNUC__  && ( __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3 )) ) || \
+    ( defined __clang__ && __clang_major__ >= 3 )
+
+#define CATCH_INTERNAL_CONFIG_COUNTER
+
+#endif
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(CATCH_CPP11_OR_GREATER)
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#    define CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#    define CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#    define CATCH_INTERNAL_CONFIG_CPP11_TUPLE
+#  endif
+
+#  ifndef CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#    define CATCH_INTERNAL_CONFIG_VARIADIC_MACROS
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG)
+#    define CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG
+#  endif
+
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#    define CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE
+#  endif
+#  if !defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#    define CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#  endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE)
+#   define CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE
+#  endif
+# if !defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS)
+#  define CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS
+# endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NO_NULLPTR) && !defined(CATCH_CONFIG_CPP11_NULLPTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CATCH_CONFIG_CPP11_GENERATED_METHODS) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_NO_IS_ENUM) && !defined(CATCH_CONFIG_CPP11_IS_ENUM) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_IS_ENUM
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_CPP11_NO_TUPLE) && !defined(CATCH_CONFIG_CPP11_TUPLE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_TUPLE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_VARIADIC_MACROS) && !defined(CATCH_CONFIG_NO_VARIADIC_MACROS) && !defined(CATCH_CONFIG_VARIADIC_MACROS)
+#   define CATCH_CONFIG_VARIADIC_MACROS
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_NO_LONG_LONG) && !defined(CATCH_CONFIG_CPP11_LONG_LONG) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_LONG_LONG
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_NO_OVERRIDE) && !defined(CATCH_CONFIG_CPP11_OVERRIDE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_UNIQUE_PTR
+#endif
+// Use of __COUNTER__ is suppressed if __JETBRAINS_IDE__ is #defined (meaning we're being parsed by a JetBrains IDE for
+// analytics) because, at time of writing, __COUNTER__ is not properly handled by it.
+// This does not affect compilation
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) && !defined(__JETBRAINS_IDE__)
+#   define CATCH_CONFIG_COUNTER
+#endif
+#if defined(CATCH_INTERNAL_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_NO_SHUFFLE) && !defined(CATCH_CONFIG_CPP11_SHUFFLE) && !defined(CATCH_CONFIG_NO_CPP11)
+#   define CATCH_CONFIG_CPP11_SHUFFLE
+#endif
+# if defined(CATCH_INTERNAL_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_NO_TYPE_TRAITS) && !defined(CATCH_CONFIG_CPP11_TYPE_TRAITS) && !defined(CATCH_CONFIG_NO_CPP11)
+#  define CATCH_CONFIG_CPP11_TYPE_TRAITS
+# endif
+#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH)
+#   define CATCH_CONFIG_WINDOWS_SEH
+#endif
+// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
+#if !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
+#   define CATCH_CONFIG_POSIX_SIGNALS
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#   define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
+#endif
+#if !defined(CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+#   define CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+#endif
+
+// noexcept support:
+#if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
+#  define CATCH_NOEXCEPT noexcept
+#  define CATCH_NOEXCEPT_IS(x) noexcept(x)
+#else
+#  define CATCH_NOEXCEPT throw()
+#  define CATCH_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+#   define CATCH_NULL nullptr
+#else
+#   define CATCH_NULL NULL
+#endif
+
+// override support
+#ifdef CATCH_CONFIG_CPP11_OVERRIDE
+#   define CATCH_OVERRIDE override
+#else
+#   define CATCH_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CATCH_CONFIG_CPP11_UNIQUE_PTR
+#   define CATCH_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+#   define CATCH_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
+#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
+#ifdef CATCH_CONFIG_COUNTER
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
+
+#define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
+#define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
+
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+    struct IConfig;
+
+    struct CaseSensitive { enum Choice {
+        Yes,
+        No
+    }; };
+
+    class NonCopyable {
+#ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        NonCopyable( NonCopyable const& )              = delete;
+        NonCopyable( NonCopyable && )                  = delete;
+        NonCopyable& operator = ( NonCopyable const& ) = delete;
+        NonCopyable& operator = ( NonCopyable && )     = delete;
+#else
+        NonCopyable( NonCopyable const& info );
+        NonCopyable& operator = ( NonCopyable const& );
+#endif
+
+    protected:
+        NonCopyable() {}
+        virtual ~NonCopyable();
+    };
+
+    class SafeBool {
+    public:
+        typedef void (SafeBool::*type)() const;
+
+        static type makeSafe( bool value ) {
+            return value ? &SafeBool::trueValue : 0;
+        }
+    private:
+        void trueValue() const {}
+    };
+
+    template<typename ContainerT>
+    void deleteAll( ContainerT& container ) {
+        typename ContainerT::const_iterator it = container.begin();
+        typename ContainerT::const_iterator itEnd = container.end();
+        for(; it != itEnd; ++it )
+            delete *it;
+    }
+    template<typename AssociativeContainerT>
+    void deleteAllValues( AssociativeContainerT& container ) {
+        typename AssociativeContainerT::const_iterator it = container.begin();
+        typename AssociativeContainerT::const_iterator itEnd = container.end();
+        for(; it != itEnd; ++it )
+            delete it->second;
+    }
+
+    bool startsWith( std::string const& s, std::string const& prefix );
+    bool startsWith( std::string const& s, char prefix );
+    bool endsWith( std::string const& s, std::string const& suffix );
+    bool endsWith( std::string const& s, char suffix );
+    bool contains( std::string const& s, std::string const& infix );
+    void toLowerInPlace( std::string& s );
+    std::string toLower( std::string const& s );
+    std::string trim( std::string const& str );
+    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
+
+    struct pluralise {
+        pluralise( std::size_t count, std::string const& label );
+
+        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
+
+        std::size_t m_count;
+        std::string m_label;
+    };
+
+    struct SourceLineInfo {
+
+        SourceLineInfo();
+        SourceLineInfo( char const* _file, std::size_t _line );
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        SourceLineInfo(SourceLineInfo const& other)          = default;
+        SourceLineInfo( SourceLineInfo && )                  = default;
+        SourceLineInfo& operator = ( SourceLineInfo const& ) = default;
+        SourceLineInfo& operator = ( SourceLineInfo && )     = default;
+#  endif
+        bool empty() const;
+        bool operator == ( SourceLineInfo const& other ) const;
+        bool operator < ( SourceLineInfo const& other ) const;
+
+        char const* file;
+        std::size_t line;
+    };
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
+
+    // This is just here to avoid compiler warnings with macro constants and boolean literals
+    inline bool isTrue( bool value ){ return value; }
+    inline bool alwaysTrue() { return true; }
+    inline bool alwaysFalse() { return false; }
+
+    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo );
+
+    void seedRng( IConfig const& config );
+    unsigned int rngSeed();
+
+    // Use this in variadic streaming macros to allow
+    //    >> +StreamEndStop
+    // as well as
+    //    >> stuff +StreamEndStop
+    struct StreamEndStop {
+        std::string operator+() {
+            return std::string();
+        }
+    };
+    template<typename T>
+    T const& operator + ( T const& value, StreamEndStop ) {
+        return value;
+    }
+}
+
+#define CATCH_INTERNAL_LINEINFO ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
+#define CATCH_INTERNAL_ERROR( msg ) ::Catch::throwLogicError( msg, CATCH_INTERNAL_LINEINFO );
+
+namespace Catch {
+
+    class NotImplementedException : public std::exception
+    {
+    public:
+        NotImplementedException( SourceLineInfo const& lineInfo );
+
+        virtual ~NotImplementedException() CATCH_NOEXCEPT {}
+
+        virtual const char* what() const CATCH_NOEXCEPT;
+
+    private:
+        std::string m_what;
+        SourceLineInfo m_lineInfo;
+    };
+
+} // end namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define CATCH_NOT_IMPLEMENTED throw Catch::NotImplementedException( CATCH_INTERNAL_LINEINFO )
+
+// #included from: internal/catch_context.h
+#define TWOBLUECUBES_CATCH_CONTEXT_H_INCLUDED
+
+// #included from: catch_interfaces_generators.h
+#define TWOBLUECUBES_CATCH_INTERFACES_GENERATORS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct IGeneratorInfo {
+        virtual ~IGeneratorInfo();
+        virtual bool moveNext() = 0;
+        virtual std::size_t getCurrentIndex() const = 0;
+    };
+
+    struct IGeneratorsForTest {
+        virtual ~IGeneratorsForTest();
+
+        virtual IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) = 0;
+        virtual bool moveNext() = 0;
+    };
+
+    IGeneratorsForTest* createGeneratorsForTest();
+
+} // end namespace Catch
+
+// #included from: catch_ptr.hpp
+#define TWOBLUECUBES_CATCH_PTR_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    // An intrusive reference counting smart pointer.
+    // T must implement addRef() and release() methods
+    // typically implementing the IShared interface
+    template<typename T>
+    class Ptr {
+    public:
+        Ptr() : m_p( CATCH_NULL ){}
+        Ptr( T* p ) : m_p( p ){
+            if( m_p )
+                m_p->addRef();
+        }
+        Ptr( Ptr const& other ) : m_p( other.m_p ){
+            if( m_p )
+                m_p->addRef();
+        }
+        ~Ptr(){
+            if( m_p )
+                m_p->release();
+        }
+        void reset() {
+            if( m_p )
+                m_p->release();
+            m_p = CATCH_NULL;
+        }
+        Ptr& operator = ( T* p ){
+            Ptr temp( p );
+            swap( temp );
+            return *this;
+        }
+        Ptr& operator = ( Ptr const& other ){
+            Ptr temp( other );
+            swap( temp );
+            return *this;
+        }
+        void swap( Ptr& other ) { std::swap( m_p, other.m_p ); }
+        T* get() const{ return m_p; }
+        T& operator*() const { return *m_p; }
+        T* operator->() const { return m_p; }
+        bool operator !() const { return m_p == CATCH_NULL; }
+        operator SafeBool::type() const { return SafeBool::makeSafe( m_p != CATCH_NULL ); }
+
+    private:
+        T* m_p;
+    };
+
+    struct IShared : NonCopyable {
+        virtual ~IShared();
+        virtual void addRef() const = 0;
+        virtual void release() const = 0;
+    };
+
+    template<typename T = IShared>
+    struct SharedImpl : T {
+
+        SharedImpl() : m_rc( 0 ){}
+
+        virtual void addRef() const {
+            ++m_rc;
+        }
+        virtual void release() const {
+            if( --m_rc == 0 )
+                delete this;
+        }
+
+        mutable unsigned int m_rc;
+    };
+
+} // end namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+    class TestCase;
+    class Stream;
+    struct IResultCapture;
+    struct IRunner;
+    struct IGeneratorsForTest;
+    struct IConfig;
+
+    struct IContext
+    {
+        virtual ~IContext();
+
+        virtual IResultCapture* getResultCapture() = 0;
+        virtual IRunner* getRunner() = 0;
+        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) = 0;
+        virtual bool advanceGeneratorsForCurrentTest() = 0;
+        virtual Ptr<IConfig const> getConfig() const = 0;
+    };
+
+    struct IMutableContext : IContext
+    {
+        virtual ~IMutableContext();
+        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
+        virtual void setRunner( IRunner* runner ) = 0;
+        virtual void setConfig( Ptr<IConfig const> const& config ) = 0;
+    };
+
+    IContext& getCurrentContext();
+    IMutableContext& getCurrentMutableContext();
+    void cleanUpContext();
+    Stream createStream( std::string const& streamName );
+
+}
+
+// #included from: internal/catch_test_registry.hpp
+#define TWOBLUECUBES_CATCH_TEST_REGISTRY_HPP_INCLUDED
+
+// #included from: catch_interfaces_testcase.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TESTCASE_H_INCLUDED
+
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec;
+
+    struct ITestCase : IShared {
+        virtual void invoke () const = 0;
+    protected:
+        virtual ~ITestCase();
+    };
+
+    class TestCase;
+    struct IConfig;
+
+    struct ITestCaseRegistry {
+        virtual ~ITestCaseRegistry();
+        virtual std::vector<TestCase> const& getAllTests() const = 0;
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
+    };
+
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
+
+}
+
+namespace Catch {
+
+template<typename C>
+class MethodTestCase : public SharedImpl<ITestCase> {
+
+public:
+    MethodTestCase( void (C::*method)() ) : m_method( method ) {}
+
+    virtual void invoke() const {
+        C obj;
+        (obj.*m_method)();
+    }
+
+private:
+    virtual ~MethodTestCase() {}
+
+    void (C::*m_method)();
+};
+
+typedef void(*TestFunction)();
+
+struct NameAndDesc {
+    NameAndDesc( const char* _name = "", const char* _description= "" )
+    : name( _name ), description( _description )
+    {}
+
+    const char* name;
+    const char* description;
+};
+
+void registerTestCase
+    (   ITestCase* testCase,
+        char const* className,
+        NameAndDesc const& nameAndDesc,
+        SourceLineInfo const& lineInfo );
+
+struct AutoReg {
+
+    AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc );
+
+    template<typename C>
+    AutoReg
+        (   void (C::*method)(),
+            char const* className,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
+
+        registerTestCase
+            (   new MethodTestCase<C>( method ),
+                className,
+                nameAndDesc,
+                lineInfo );
+    }
+
+    ~AutoReg();
+
+private:
+    AutoReg( AutoReg const& );
+    void operator= ( AutoReg const& );
+};
+
+void registerTestCaseFunction
+    (   TestFunction function,
+        SourceLineInfo const& lineInfo,
+        NameAndDesc const& nameAndDesc );
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+        static void TestName(); \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); } /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE( ... ) \
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ \
+            struct TestName : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \
+        } \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+        void TestName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#else
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
+        static void TestName(); \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); } /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+        static void TestName()
+    #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); } /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        namespace{ \
+            struct TestCaseName : ClassName{ \
+                void test(); \
+            }; \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); /* NOLINT */ \
+        } \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS \
+        void TestCaseName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
+
+    ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
+        CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS \
+        Catch::AutoReg( Function, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); /* NOLINT */ \
+        CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+#endif
+
+// #included from: internal/catch_capture.hpp
+#define TWOBLUECUBES_CATCH_CAPTURE_HPP_INCLUDED
+
+// #included from: catch_result_builder.h
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_H_INCLUDED
+
+// #included from: catch_result_type.h
+#define TWOBLUECUBES_CATCH_RESULT_TYPE_H_INCLUDED
+
+namespace Catch {
+
+    // ResultWas::OfType enum
+    struct ResultWas { enum OfType {
+        Unknown = -1,
+        Ok = 0,
+        Info = 1,
+        Warning = 2,
+
+        FailureBit = 0x10,
+
+        ExpressionFailed = FailureBit | 1,
+        ExplicitFailure = FailureBit | 2,
+
+        Exception = 0x100 | FailureBit,
+
+        ThrewException = Exception | 1,
+        DidntThrowException = Exception | 2,
+
+        FatalErrorCondition = 0x200 | FailureBit
+
+    }; };
+
+    inline bool isOk( ResultWas::OfType resultType ) {
+        return ( resultType & ResultWas::FailureBit ) == 0;
+    }
+    inline bool isJustInfo( int flags ) {
+        return flags == ResultWas::Info;
+    }
+
+    // ResultDisposition::Flags enum
+    struct ResultDisposition { enum Flags {
+        Normal = 0x01,
+
+        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
+        FalseTest = 0x04,           // Prefix expression with !
+        SuppressFail = 0x08         // Failures are reported but do not fail the test
+    }; };
+
+    inline ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
+        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
+    }
+
+    inline bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
+    inline bool isFalseTest( int flags )                { return ( flags & ResultDisposition::FalseTest ) != 0; }
+    inline bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.h
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison;
+
+    struct DecomposedExpression
+    {
+        virtual ~DecomposedExpression() {}
+        virtual bool isBinaryExpression() const {
+            return false;
+        }
+        virtual void reconstructExpression( std::string& dest ) const = 0;
+
+        // Only simple binary comparisons can be decomposed.
+        // If more complex check is required then wrap sub-expressions in parentheses.
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator + ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator - ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator * ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator / ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator % ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator && ( T const& );
+        template<typename T> STATIC_ASSERT_Expression_Too_Complex_Please_Rewrite_As_Binary_Comparison& operator || ( T const& );
+
+    private:
+        DecomposedExpression& operator = (DecomposedExpression const&);
+    };
+
+    struct AssertionInfo
+    {
+        AssertionInfo();
+        AssertionInfo(  char const * _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        char const * _capturedExpression,
+                        ResultDisposition::Flags _resultDisposition,
+                        char const * _secondArg = "");
+
+        char const * macroName;
+        SourceLineInfo lineInfo;
+        char const * capturedExpression;
+        ResultDisposition::Flags resultDisposition;
+        char const * secondArg;
+    };
+
+    struct AssertionResultData
+    {
+        AssertionResultData() : decomposedExpression( CATCH_NULL )
+                              , resultType( ResultWas::Unknown )
+                              , negated( false )
+                              , parenthesized( false ) {}
+
+        void negate( bool parenthesize ) {
+            negated = !negated;
+            parenthesized = parenthesize;
+            if( resultType == ResultWas::Ok )
+                resultType = ResultWas::ExpressionFailed;
+            else if( resultType == ResultWas::ExpressionFailed )
+                resultType = ResultWas::Ok;
+        }
+
+        std::string const& reconstructExpression() const {
+            if( decomposedExpression != CATCH_NULL ) {
+                decomposedExpression->reconstructExpression( reconstructedExpression );
+                if( parenthesized ) {
+                    reconstructedExpression.insert( 0, 1, '(' );
+                    reconstructedExpression.append( 1, ')' );
+                }
+                if( negated ) {
+                    reconstructedExpression.insert( 0, 1, '!' );
+                }
+                decomposedExpression = CATCH_NULL;
+            }
+            return reconstructedExpression;
+        }
+
+        mutable DecomposedExpression const* decomposedExpression;
+        mutable std::string reconstructedExpression;
+        std::string message;
+        ResultWas::OfType resultType;
+        bool negated;
+        bool parenthesized;
+    };
+
+    class AssertionResult {
+    public:
+        AssertionResult();
+        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
+        ~AssertionResult();
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+         AssertionResult( AssertionResult const& )              = default;
+         AssertionResult( AssertionResult && )                  = default;
+         AssertionResult& operator = ( AssertionResult const& ) = default;
+         AssertionResult& operator = ( AssertionResult && )     = default;
+#  endif
+
+        bool isOk() const;
+        bool succeeded() const;
+        ResultWas::OfType getResultType() const;
+        bool hasExpression() const;
+        bool hasMessage() const;
+        std::string getExpression() const;
+        std::string getExpressionInMacro() const;
+        bool hasExpandedExpression() const;
+        std::string getExpandedExpression() const;
+        std::string getMessage() const;
+        SourceLineInfo getSourceInfo() const;
+        std::string getTestMacroName() const;
+        void discardDecomposedExpression() const;
+        void expandDecomposedExpression() const;
+
+    protected:
+        AssertionInfo m_info;
+        AssertionResultData m_resultData;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_matchers.hpp
+#define TWOBLUECUBES_CATCH_MATCHERS_HPP_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+    namespace Impl {
+
+        template<typename ArgT> struct MatchAllOf;
+        template<typename ArgT> struct MatchAnyOf;
+        template<typename ArgT> struct MatchNotOf;
+
+        class MatcherUntypedBase {
+        public:
+            std::string toString() const {
+                if( m_cachedToString.empty() )
+                    m_cachedToString = describe();
+                return m_cachedToString;
+            }
+
+        protected:
+            virtual ~MatcherUntypedBase();
+            virtual std::string describe() const = 0;
+            mutable std::string m_cachedToString;
+        private:
+            MatcherUntypedBase& operator = ( MatcherUntypedBase const& );
+        };
+
+        template<typename ObjectT>
+        struct MatcherMethod {
+            virtual bool match( ObjectT const& arg ) const = 0;
+        };
+        template<typename PtrT>
+        struct MatcherMethod<PtrT*> {
+            virtual bool match( PtrT* arg ) const = 0;
+        };
+
+        template<typename ObjectT, typename ComparatorT = ObjectT>
+        struct MatcherBase : MatcherUntypedBase, MatcherMethod<ObjectT> {
+
+            MatchAllOf<ComparatorT> operator && ( MatcherBase const& other ) const;
+            MatchAnyOf<ComparatorT> operator || ( MatcherBase const& other ) const;
+            MatchNotOf<ComparatorT> operator ! () const;
+        };
+
+        template<typename ArgT>
+        struct MatchAllOf : MatcherBase<ArgT> {
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if (!m_matchers[i]->match(arg))
+                        return false;
+                }
+                return true;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        description += " and ";
+                    description += m_matchers[i]->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
+                m_matchers.push_back( &other );
+                return *this;
+            }
+
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+        template<typename ArgT>
+        struct MatchAnyOf : MatcherBase<ArgT> {
+
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if (m_matchers[i]->match(arg))
+                        return true;
+                }
+                return false;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                std::string description;
+                description.reserve( 4 + m_matchers.size()*32 );
+                description += "( ";
+                for( std::size_t i = 0; i < m_matchers.size(); ++i ) {
+                    if( i != 0 )
+                        description += " or ";
+                    description += m_matchers[i]->toString();
+                }
+                description += " )";
+                return description;
+            }
+
+            MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
+                m_matchers.push_back( &other );
+                return *this;
+            }
+
+            std::vector<MatcherBase<ArgT> const*> m_matchers;
+        };
+
+        template<typename ArgT>
+        struct MatchNotOf : MatcherBase<ArgT> {
+
+            MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
+
+            virtual bool match( ArgT const& arg ) const CATCH_OVERRIDE {
+                return !m_underlyingMatcher.match( arg );
+            }
+
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "not " + m_underlyingMatcher.toString();
+            }
+            MatcherBase<ArgT> const& m_underlyingMatcher;
+        };
+
+        template<typename ObjectT, typename ComparatorT>
+        MatchAllOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator && ( MatcherBase const& other ) const {
+            return MatchAllOf<ComparatorT>() && *this && other;
+        }
+        template<typename ObjectT, typename ComparatorT>
+        MatchAnyOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator || ( MatcherBase const& other ) const {
+            return MatchAnyOf<ComparatorT>() || *this || other;
+        }
+        template<typename ObjectT, typename ComparatorT>
+        MatchNotOf<ComparatorT> MatcherBase<ObjectT, ComparatorT>::operator ! () const {
+            return MatchNotOf<ComparatorT>( *this );
+        }
+
+    } // namespace Impl
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+    // - deprecated: prefer ||, && and !
+    template<typename T>
+    Impl::MatchNotOf<T> Not( Impl::MatcherBase<T> const& underlyingMatcher ) {
+        return Impl::MatchNotOf<T>( underlyingMatcher );
+    }
+    template<typename T>
+    Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+        return Impl::MatchAllOf<T>() && m1 && m2;
+    }
+    template<typename T>
+    Impl::MatchAllOf<T> AllOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+        return Impl::MatchAllOf<T>() && m1 && m2 && m3;
+    }
+    template<typename T>
+    Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2 ) {
+        return Impl::MatchAnyOf<T>() || m1 || m2;
+    }
+    template<typename T>
+    Impl::MatchAnyOf<T> AnyOf( Impl::MatcherBase<T> const& m1, Impl::MatcherBase<T> const& m2, Impl::MatcherBase<T> const& m3 ) {
+        return Impl::MatchAnyOf<T>() || m1 || m2 || m3;
+    }
+
+} // namespace Matchers
+
+using namespace Matchers;
+using Matchers::Impl::MatcherBase;
+
+} // namespace Catch
+
+namespace Catch {
+
+    struct TestFailureException{};
+
+    template<typename T> class ExpressionLhs;
+
+    struct CopyableStream {
+        CopyableStream() {}
+        CopyableStream( CopyableStream const& other ) {
+            oss << other.oss.str();
+        }
+        CopyableStream& operator=( CopyableStream const& other ) {
+            oss.str(std::string());
+            oss << other.oss.str();
+            return *this;
+        }
+        std::ostringstream oss;
+    };
+
+    class ResultBuilder : public DecomposedExpression {
+    public:
+        ResultBuilder(  char const* macroName,
+                        SourceLineInfo const& lineInfo,
+                        char const* capturedExpression,
+                        ResultDisposition::Flags resultDisposition,
+                        char const* secondArg = "" );
+        ~ResultBuilder();
+
+        template<typename T>
+        ExpressionLhs<T const&> operator <= ( T const& operand );
+        ExpressionLhs<bool> operator <= ( bool value );
+
+        template<typename T>
+        ResultBuilder& operator << ( T const& value ) {
+            stream().oss << value;
+            return *this;
+        }
+
+        ResultBuilder& setResultType( ResultWas::OfType result );
+        ResultBuilder& setResultType( bool result );
+
+        void endExpression( DecomposedExpression const& expr );
+
+        virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE;
+
+        AssertionResult build() const;
+        AssertionResult build( DecomposedExpression const& expr ) const;
+
+        void useActiveException( ResultDisposition::Flags resultDisposition = ResultDisposition::Normal );
+        void captureResult( ResultWas::OfType resultType );
+        void captureExpression();
+        void captureExpectedException( std::string const& expectedMessage );
+        void captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher );
+        void handleResult( AssertionResult const& result );
+        void react();
+        bool shouldDebugBreak() const;
+        bool allowThrows() const;
+
+        template<typename ArgT, typename MatcherT>
+        void captureMatch( ArgT const& arg, MatcherT const& matcher, char const* matcherString );
+
+        void setExceptionGuard();
+        void unsetExceptionGuard();
+
+    private:
+        AssertionInfo m_assertionInfo;
+        AssertionResultData m_data;
+
+        CopyableStream &stream()
+        {
+            if(!m_usedStream)
+            {
+                m_usedStream = true;
+                m_stream().oss.str("");
+            }
+            return m_stream();
+        }
+
+        static CopyableStream &m_stream()
+        {
+            static CopyableStream s;
+            return s;
+        }
+
+        bool m_shouldDebugBreak;
+        bool m_shouldThrow;
+        bool m_guardException;
+        bool m_usedStream;
+    };
+
+} // namespace Catch
+
+// Include after due to circular dependency:
+// #included from: catch_expression_lhs.hpp
+#define TWOBLUECUBES_CATCH_EXPRESSION_LHS_HPP_INCLUDED
+
+// #included from: catch_evaluate.hpp
+#define TWOBLUECUBES_CATCH_EVALUATE_HPP_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
+#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
+#endif
+
+#include <cstddef>
+
+namespace Catch {
+namespace Internal {
+
+    enum Operator {
+        IsEqualTo,
+        IsNotEqualTo,
+        IsLessThan,
+        IsGreaterThan,
+        IsLessThanOrEqualTo,
+        IsGreaterThanOrEqualTo
+    };
+
+    template<Operator Op> struct OperatorTraits             { static const char* getName(){ return "*error*"; } };
+    template<> struct OperatorTraits<IsEqualTo>             { static const char* getName(){ return "=="; } };
+    template<> struct OperatorTraits<IsNotEqualTo>          { static const char* getName(){ return "!="; } };
+    template<> struct OperatorTraits<IsLessThan>            { static const char* getName(){ return "<"; } };
+    template<> struct OperatorTraits<IsGreaterThan>         { static const char* getName(){ return ">"; } };
+    template<> struct OperatorTraits<IsLessThanOrEqualTo>   { static const char* getName(){ return "<="; } };
+    template<> struct OperatorTraits<IsGreaterThanOrEqualTo>{ static const char* getName(){ return ">="; } };
+
+    template<typename T>
+    T& opCast(T const& t) { return const_cast<T&>(t); }
+
+// nullptr_t support based on pull request #154 from Konstantin Baumann
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    inline std::nullptr_t opCast(std::nullptr_t) { return nullptr; }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+    // So the compare overloads can be operator agnostic we convey the operator as a template
+    // enum, which is used to specialise an Evaluator for doing the comparison.
+    template<typename T1, typename T2, Operator Op>
+    struct Evaluator{};
+
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs) {
+            return bool( opCast( lhs ) ==  opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsNotEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) != opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) < opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThan> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) > opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) >= opCast( rhs ) );
+        }
+    };
+    template<typename T1, typename T2>
+    struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
+        static bool evaluate( T1 const& lhs, T2 const& rhs ) {
+            return bool( opCast( lhs ) <= opCast( rhs ) );
+        }
+    };
+
+    template<Operator Op, typename T1, typename T2>
+    bool applyEvaluator( T1 const& lhs, T2 const& rhs ) {
+        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+    }
+
+    // This level of indirection allows us to specialise for integer types
+    // to avoid signed/ unsigned warnings
+
+    // "base" overload
+    template<Operator Op, typename T1, typename T2>
+    bool compare( T1 const& lhs, T2 const& rhs ) {
+        return Evaluator<T1, T2, Op>::evaluate( lhs, rhs );
+    }
+
+    // unsigned X to int
+    template<Operator Op> bool compare( unsigned int lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned long lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned char lhs, int rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned int>( rhs ) );
+    }
+
+    // unsigned X to long
+    template<Operator Op> bool compare( unsigned int lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned long lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+    template<Operator Op> bool compare( unsigned char lhs, long rhs ) {
+        return applyEvaluator<Op>( lhs, static_cast<unsigned long>( rhs ) );
+    }
+
+    // int to unsigned X
+    template<Operator Op> bool compare( int lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( int lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( int lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned int>( lhs ), rhs );
+    }
+
+    // long to unsigned X
+    template<Operator Op> bool compare( long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // pointer to long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+
+    // pointer to int (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( int lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, int rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+    // long long to unsigned X
+    template<Operator Op> bool compare( long long lhs, unsigned int rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned long long rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( long long lhs, unsigned char rhs ) {
+        return applyEvaluator<Op>( static_cast<unsigned long>( lhs ), rhs );
+    }
+
+    // unsigned long long to X
+    template<Operator Op> bool compare( unsigned long long lhs, int rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, long long rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+    template<Operator Op> bool compare( unsigned long long lhs, char rhs ) {
+        return applyEvaluator<Op>( static_cast<long>( lhs ), rhs );
+    }
+
+    // pointer to long long (when comparing against NULL)
+    template<Operator Op, typename T> bool compare( long long lhs, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( reinterpret_cast<T*>( lhs ), rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, long long rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, reinterpret_cast<T*>( rhs ) );
+    }
+#endif // CATCH_CONFIG_CPP11_LONG_LONG
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+    // pointer to nullptr_t (when comparing against nullptr)
+    template<Operator Op, typename T> bool compare( std::nullptr_t, T* rhs ) {
+        return Evaluator<T*, T*, Op>::evaluate( nullptr, rhs );
+    }
+    template<Operator Op, typename T> bool compare( T* lhs, std::nullptr_t ) {
+        return Evaluator<T*, T*, Op>::evaluate( lhs, nullptr );
+    }
+#endif // CATCH_CONFIG_CPP11_NULLPTR
+
+} // end of namespace Internal
+} // end of namespace Catch
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+// #included from: catch_tostring.h
+#define TWOBLUECUBES_CATCH_TOSTRING_H_INCLUDED
+
+#include <sstream>
+#include <iomanip>
+#include <limits>
+#include <vector>
+#include <cstddef>
+
+#ifdef __OBJC__
+// #included from: catch_objc_arc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_ARC_HPP_INCLUDED
+
+#import <Foundation/Foundation.h>
+
+#ifdef __has_feature
+#define CATCH_ARC_ENABLED __has_feature(objc_arc)
+#else
+#define CATCH_ARC_ENABLED 0
+#endif
+
+void arcSafeRelease( NSObject* obj );
+id performOptionalSelector( id obj, SEL sel );
+
+#if !CATCH_ARC_ENABLED
+inline void arcSafeRelease( NSObject* obj ) {
+    [obj release];
+}
+inline id performOptionalSelector( id obj, SEL sel ) {
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED
+#define CATCH_ARC_STRONG
+#else
+inline void arcSafeRelease( NSObject* ){}
+inline id performOptionalSelector( id obj, SEL sel ) {
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
+#endif
+    if( [obj respondsToSelector: sel] )
+        return [obj performSelector: sel];
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+    return nil;
+}
+#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
+#define CATCH_ARC_STRONG __strong
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+#include <tuple>
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_IS_ENUM
+#include <type_traits>
+#endif
+
+namespace Catch {
+
+// Why we're here.
+template<typename T>
+std::string toString( T const& value );
+
+// Built in overloads
+
+std::string toString( std::string const& value );
+std::string toString( std::wstring const& value );
+std::string toString( const char* const value );
+std::string toString( char* const value );
+std::string toString( const wchar_t* const value );
+std::string toString( wchar_t* const value );
+std::string toString( int value );
+std::string toString( unsigned long value );
+std::string toString( unsigned int value );
+std::string toString( const double value );
+std::string toString( const float value );
+std::string toString( bool value );
+std::string toString( char value );
+std::string toString( signed char value );
+std::string toString( unsigned char value );
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value );
+std::string toString( unsigned long long value );
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t );
+#endif
+
+#ifdef __OBJC__
+    std::string toString( NSString const * const& nsstring );
+    std::string toString( NSString * CATCH_ARC_STRONG & nsstring );
+    std::string toString( NSObject* const& nsObject );
+#endif
+
+namespace Detail {
+
+    extern const std::string unprintableString;
+
+ #if !defined(CATCH_CONFIG_CPP11_STREAM_INSERTABLE_CHECK)
+    struct BorgType {
+        template<typename T> BorgType( T const& );
+    };
+
+    struct TrueType { char sizer[1]; };
+    struct FalseType { char sizer[2]; };
+
+    TrueType& testStreamable( std::ostream& );
+    FalseType testStreamable( FalseType );
+
+    FalseType operator<<( std::ostream const&, BorgType const& );
+
+    template<typename T>
+    struct IsStreamInsertable {
+        static std::ostream &s;
+        static T  const&t;
+        enum { value = sizeof( testStreamable(s << t) ) == sizeof( TrueType ) };
+    };
+#else
+    template<typename T>
+    class IsStreamInsertable {
+        template<typename SS, typename TT>
+        static auto test(int)
+        -> decltype( std::declval<SS&>() << std::declval<TT>(), std::true_type() );
+
+        template<typename, typename>
+        static auto test(...) -> std::false_type;
+
+    public:
+        static const bool value = decltype(test<std::ostream,const T&>(0))::value;
+    };
+#endif
+
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+    template<typename T,
+             bool IsEnum = std::is_enum<T>::value
+             >
+    struct EnumStringMaker
+    {
+        static std::string convert( T const& ) { return unprintableString; }
+    };
+
+    template<typename T>
+    struct EnumStringMaker<T,true>
+    {
+        static std::string convert( T const& v )
+        {
+            return ::Catch::toString(
+                static_cast<typename std::underlying_type<T>::type>(v)
+                );
+        }
+    };
+#endif
+    template<bool C>
+    struct StringMakerBase {
+#if defined(CATCH_CONFIG_CPP11_IS_ENUM)
+        template<typename T>
+        static std::string convert( T const& v )
+        {
+            return EnumStringMaker<T>::convert( v );
+        }
+#else
+        template<typename T>
+        static std::string convert( T const& ) { return unprintableString; }
+#endif
+    };
+
+    template<>
+    struct StringMakerBase<true> {
+        template<typename T>
+        static std::string convert( T const& _value ) {
+            std::ostringstream oss;
+            oss << _value;
+            return oss.str();
+        }
+    };
+
+    std::string rawMemoryToString( const void *object, std::size_t size );
+
+    template<typename T>
+    std::string rawMemoryToString( const T& object ) {
+      return rawMemoryToString( &object, sizeof(object) );
+    }
+
+} // end namespace Detail
+
+template<typename T>
+struct StringMaker :
+    Detail::StringMakerBase<Detail::IsStreamInsertable<T>::value> {};
+
+template<typename T>
+struct StringMaker<T*> {
+    template<typename U>
+    static std::string convert( U* p ) {
+        if( !p )
+            return "NULL";
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+template<typename R, typename C>
+struct StringMaker<R C::*> {
+    static std::string convert( R C::* p ) {
+        if( !p )
+            return "NULL";
+        else
+            return Detail::rawMemoryToString( p );
+    }
+};
+
+namespace Detail {
+    template<typename InputIterator>
+    std::string rangeToString( InputIterator first, InputIterator last );
+}
+
+//template<typename T, typename Allocator>
+//struct StringMaker<std::vector<T, Allocator> > {
+//    static std::string convert( std::vector<T,Allocator> const& v ) {
+//        return Detail::rangeToString( v.begin(), v.end() );
+//    }
+//};
+
+template<typename T, typename Allocator>
+std::string toString( std::vector<T,Allocator> const& v ) {
+    return Detail::rangeToString( v.begin(), v.end() );
+}
+
+#ifdef CATCH_CONFIG_CPP11_TUPLE
+
+// toString for tuples
+namespace TupleDetail {
+  template<
+      typename Tuple,
+      std::size_t N = 0,
+      bool = (N < std::tuple_size<Tuple>::value)
+      >
+  struct ElementPrinter {
+      static void print( const Tuple& tuple, std::ostream& os )
+      {
+          os << ( N ? ", " : " " )
+             << Catch::toString(std::get<N>(tuple));
+          ElementPrinter<Tuple,N+1>::print(tuple,os);
+      }
+  };
+
+  template<
+      typename Tuple,
+      std::size_t N
+      >
+  struct ElementPrinter<Tuple,N,false> {
+      static void print( const Tuple&, std::ostream& ) {}
+  };
+
+}
+
+template<typename ...Types>
+struct StringMaker<std::tuple<Types...>> {
+
+    static std::string convert( const std::tuple<Types...>& tuple )
+    {
+        std::ostringstream os;
+        os << '{';
+        TupleDetail::ElementPrinter<std::tuple<Types...>>::print( tuple, os );
+        os << " }";
+        return os.str();
+    }
+};
+#endif // CATCH_CONFIG_CPP11_TUPLE
+
+namespace Detail {
+    template<typename T>
+    std::string makeString( T const& value ) {
+        return StringMaker<T>::convert( value );
+    }
+} // end namespace Detail
+
+/// \brief converts any type to a string
+///
+/// The default template forwards on to ostringstream - except when an
+/// ostringstream overload does not exist - in which case it attempts to detect
+/// that and writes {?}.
+/// Overload (not specialise) this template for custom typs that you don't want
+/// to provide an ostream overload for.
+template<typename T>
+std::string toString( T const& value ) {
+    return StringMaker<T>::convert( value );
+}
+
+    namespace Detail {
+    template<typename InputIterator>
+    std::string rangeToString( InputIterator first, InputIterator last ) {
+        std::ostringstream oss;
+        oss << "{ ";
+        if( first != last ) {
+            oss << Catch::toString( *first );
+            for( ++first ; first != last ; ++first )
+                oss << ", " << Catch::toString( *first );
+        }
+        oss << " }";
+        return oss.str();
+    }
+}
+
+} // end namespace Catch
+
+namespace Catch {
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression;
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression;
+
+// Wraps the LHS of an expression and overloads comparison operators
+// for also capturing those and RHS (if any)
+template<typename T>
+class ExpressionLhs : public DecomposedExpression {
+public:
+    ExpressionLhs( ResultBuilder& rb, T lhs ) : m_rb( rb ), m_lhs( lhs ), m_truthy(false) {}
+
+    ExpressionLhs& operator = ( const ExpressionLhs& );
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsEqualTo, RhsT const&>
+    operator == ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsNotEqualTo, RhsT const&>
+    operator != ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsLessThan, RhsT const&>
+    operator < ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThan>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsGreaterThan, RhsT const&>
+    operator > ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThan>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsLessThanOrEqualTo, RhsT const&>
+    operator <= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsLessThanOrEqualTo>( rhs );
+    }
+
+    template<typename RhsT>
+    BinaryExpression<T, Internal::IsGreaterThanOrEqualTo, RhsT const&>
+    operator >= ( RhsT const& rhs ) {
+        return captureExpression<Internal::IsGreaterThanOrEqualTo>( rhs );
+    }
+
+    BinaryExpression<T, Internal::IsEqualTo, bool> operator == ( bool rhs ) {
+        return captureExpression<Internal::IsEqualTo>( rhs );
+    }
+
+    BinaryExpression<T, Internal::IsNotEqualTo, bool> operator != ( bool rhs ) {
+        return captureExpression<Internal::IsNotEqualTo>( rhs );
+    }
+
+    void endExpression() {
+        m_truthy = m_lhs ? true : false;
+        m_rb
+            .setResultType( m_truthy )
+            .endExpression( *this );
+    }
+
+    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+        dest = Catch::toString( m_lhs );
+    }
+
+private:
+    template<Internal::Operator Op, typename RhsT>
+    BinaryExpression<T, Op, RhsT&> captureExpression( RhsT& rhs ) const {
+        return BinaryExpression<T, Op, RhsT&>( m_rb, m_lhs, rhs );
+    }
+
+    template<Internal::Operator Op>
+    BinaryExpression<T, Op, bool> captureExpression( bool rhs ) const {
+        return BinaryExpression<T, Op, bool>( m_rb, m_lhs, rhs );
+    }
+
+private:
+    ResultBuilder& m_rb;
+    T m_lhs;
+    bool m_truthy;
+};
+
+template<typename LhsT, Internal::Operator Op, typename RhsT>
+class BinaryExpression : public DecomposedExpression {
+public:
+    BinaryExpression( ResultBuilder& rb, LhsT lhs, RhsT rhs )
+        : m_rb( rb ), m_lhs( lhs ), m_rhs( rhs ) {}
+
+    BinaryExpression& operator = ( BinaryExpression& );
+
+    void endExpression() const {
+        m_rb
+            .setResultType( Internal::compare<Op>( m_lhs, m_rhs ) )
+            .endExpression( *this );
+    }
+
+    virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+        return true;
+    }
+
+    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+        std::string lhs = Catch::toString( m_lhs );
+        std::string rhs = Catch::toString( m_rhs );
+        char delim = lhs.size() + rhs.size() < 40 &&
+                     lhs.find('\n') == std::string::npos &&
+                     rhs.find('\n') == std::string::npos ? ' ' : '\n';
+        dest.reserve( 7 + lhs.size() + rhs.size() );
+                   // 2 for spaces around operator
+                   // 2 for operator
+                   // 2 for parentheses (conditionally added later)
+                   // 1 for negation (conditionally added later)
+        dest = lhs;
+        dest += delim;
+        dest += Internal::OperatorTraits<Op>::getName();
+        dest += delim;
+        dest += rhs;
+    }
+
+private:
+    ResultBuilder& m_rb;
+    LhsT m_lhs;
+    RhsT m_rhs;
+};
+
+template<typename ArgT, typename MatcherT>
+class MatchExpression : public DecomposedExpression {
+public:
+    MatchExpression( ArgT arg, MatcherT matcher, char const* matcherString )
+        : m_arg( arg ), m_matcher( matcher ), m_matcherString( matcherString ) {}
+
+    virtual bool isBinaryExpression() const CATCH_OVERRIDE {
+        return true;
+    }
+
+    virtual void reconstructExpression( std::string& dest ) const CATCH_OVERRIDE {
+        std::string matcherAsString = m_matcher.toString();
+        dest = Catch::toString( m_arg );
+        dest += ' ';
+        if( matcherAsString == Detail::unprintableString )
+            dest += m_matcherString;
+        else
+            dest += matcherAsString;
+    }
+
+private:
+    ArgT m_arg;
+    MatcherT m_matcher;
+    char const* m_matcherString;
+};
+
+} // end namespace Catch
+
+
+namespace Catch {
+
+    template<typename T>
+    ExpressionLhs<T const&> ResultBuilder::operator <= ( T const& operand ) {
+        return ExpressionLhs<T const&>( *this, operand );
+    }
+
+    inline ExpressionLhs<bool> ResultBuilder::operator <= ( bool value ) {
+        return ExpressionLhs<bool>( *this, value );
+    }
+
+    template<typename ArgT, typename MatcherT>
+    void ResultBuilder::captureMatch( ArgT const& arg, MatcherT const& matcher,
+                                             char const* matcherString ) {
+        MatchExpression<ArgT const&, MatcherT const&> expr( arg, matcher, matcherString );
+        setResultType( matcher.match( arg ) );
+        endExpression( expr );
+    }
+
+} // namespace Catch
+
+// #included from: catch_message.h
+#define TWOBLUECUBES_CATCH_MESSAGE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct MessageInfo {
+        MessageInfo(    std::string const& _macroName,
+                        SourceLineInfo const& _lineInfo,
+                        ResultWas::OfType _type );
+
+        std::string macroName;
+        SourceLineInfo lineInfo;
+        ResultWas::OfType type;
+        std::string message;
+        unsigned int sequence;
+
+        bool operator == ( MessageInfo const& other ) const {
+            return sequence == other.sequence;
+        }
+        bool operator < ( MessageInfo const& other ) const {
+            return sequence < other.sequence;
+        }
+    private:
+        static unsigned int globalCount;
+    };
+
+    struct MessageBuilder {
+        MessageBuilder( std::string const& macroName,
+                        SourceLineInfo const& lineInfo,
+                        ResultWas::OfType type )
+        : m_info( macroName, lineInfo, type )
+        {}
+
+        template<typename T>
+        MessageBuilder& operator << ( T const& value ) {
+            m_stream << value;
+            return *this;
+        }
+
+        MessageInfo m_info;
+        std::ostringstream m_stream;
+    };
+
+    class ScopedMessage {
+    public:
+        ScopedMessage( MessageBuilder const& builder );
+        ScopedMessage( ScopedMessage const& other );
+        ~ScopedMessage();
+
+        MessageInfo m_info;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_capture.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CAPTURE_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class TestCase;
+    class AssertionResult;
+    struct AssertionInfo;
+    struct SectionInfo;
+    struct SectionEndInfo;
+    struct MessageInfo;
+    class ScopedMessageBuilder;
+    struct Counts;
+
+    struct IResultCapture {
+
+        virtual ~IResultCapture();
+
+        virtual void assertionEnded( AssertionResult const& result ) = 0;
+        virtual bool sectionStarted(    SectionInfo const& sectionInfo,
+                                        Counts& assertions ) = 0;
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
+        virtual void pushScopedMessage( MessageInfo const& message ) = 0;
+        virtual void popScopedMessage( MessageInfo const& message ) = 0;
+
+        virtual std::string getCurrentTestName() const = 0;
+        virtual const AssertionResult* getLastResult() const = 0;
+
+        virtual void exceptionEarlyReported() = 0;
+
+        virtual void handleFatalErrorCondition( std::string const& message ) = 0;
+
+        virtual bool lastAssertionPassed() = 0;
+        virtual void assertionPassed() = 0;
+        virtual void assertionRun() = 0;
+    };
+
+    IResultCapture& getResultCapture();
+}
+
+// #included from: catch_debugger.h
+#define TWOBLUECUBES_CATCH_DEBUGGER_H_INCLUDED
+
+// #included from: catch_platform.h
+#define TWOBLUECUBES_CATCH_PLATFORM_H_INCLUDED
+
+#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED)
+#  define CATCH_PLATFORM_MAC
+#elif  defined(__IPHONE_OS_VERSION_MIN_REQUIRED)
+#  define CATCH_PLATFORM_IPHONE
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+#  define CATCH_PLATFORM_LINUX
+#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#  define CATCH_PLATFORM_WINDOWS
+#  if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
+#    define CATCH_DEFINES_NOMINMAX
+#  endif
+#  if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
+#    define CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+#  endif
+#endif
+
+#include <string>
+
+namespace Catch{
+
+    bool isDebuggerActive();
+    void writeToDebugConsole( std::string const& text );
+}
+
+#ifdef CATCH_PLATFORM_MAC
+
+    // The following code snippet based on:
+    // http://cocoawithlove.com/2008/03/break-into-debugger.html
+    #if defined(__ppc64__) || defined(__ppc__)
+        #define CATCH_TRAP() \
+                __asm__("li r0, 20\nsc\nnop\nli r0, 37\nli r4, 2\nsc\nnop\n" \
+                : : : "memory","r0","r3","r4" ) /* NOLINT */
+    #else
+        #define CATCH_TRAP() __asm__("int $3\n" : : /* NOLINT */ )
+    #endif
+
+#elif defined(CATCH_PLATFORM_LINUX)
+    // If we can use inline assembler, do it because this allows us to break
+    // directly at the location of the failing check instead of breaking inside
+    // raise() called from it, i.e. one stack frame below.
+    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
+        #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */
+    #else // Fall back to the generic way.
+        #include <signal.h>
+
+        #define CATCH_TRAP() raise(SIGTRAP)
+    #endif
+#elif defined(_MSC_VER)
+    #define CATCH_TRAP() __debugbreak()
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
+    #define CATCH_TRAP() DebugBreak()
+#endif
+
+#ifdef CATCH_TRAP
+    #define CATCH_BREAK_INTO_DEBUGGER() if( Catch::isDebuggerActive() ) { CATCH_TRAP(); }
+#else
+    #define CATCH_BREAK_INTO_DEBUGGER() Catch::alwaysTrue();
+#endif
+
+// #included from: catch_interfaces_runner.h
+#define TWOBLUECUBES_CATCH_INTERFACES_RUNNER_H_INCLUDED
+
+namespace Catch {
+    class TestCase;
+
+    struct IRunner {
+        virtual ~IRunner();
+        virtual bool aborting() const = 0;
+    };
+}
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+///////////////////////////////////////////////////////////////////////////////
+// We can speedup compilation significantly by breaking into debugger lower in
+// the callstack, because then we don't have to expand CATCH_BREAK_INTO_DEBUGGER
+// macro in each assertion
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+    resultBuilder.react();
+
+///////////////////////////////////////////////////////////////////////////////
+// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
+// macros.
+// This can potentially cause false negative, if the test code catches
+// the exception before it propagates back up to the runner.
+#define INTERNAL_CATCH_TEST_NO_TRY( macroName, resultDisposition, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        __catchResult.setExceptionGuard(); \
+        CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+        ( __catchResult <= expr ).endExpression(); \
+        CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+        __catchResult.unsetExceptionGuard(); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+// The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+#define INTERNAL_CHECK_THAT_NO_TRY( macroName, matcher, resultDisposition, arg ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
+        __catchResult.setExceptionGuard(); \
+        __catchResult.captureMatch( arg, matcher, #matcher ); \
+        __catchResult.unsetExceptionGuard(); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+#else
+///////////////////////////////////////////////////////////////////////////////
+// In the event of a failure works out if the debugger needs to be invoked
+// and/or an exception thrown and takes appropriate action.
+// This needs to be done as a macro so the debugger will stop in the user
+// source code rather than in Catch library code
+#define INTERNAL_CATCH_REACT( resultBuilder ) \
+    if( resultBuilder.shouldDebugBreak() ) CATCH_BREAK_INTO_DEBUGGER(); \
+    resultBuilder.react();
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        try { \
+            CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
+            ( __catchResult <= expr ).endExpression(); \
+            CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::isTrue( false && static_cast<bool>( !!(expr) ) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+    // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_IF( macroName, resultDisposition, expr ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+    if( Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, expr ) \
+    INTERNAL_CATCH_TEST( macroName, resultDisposition, expr ); \
+    if( !Catch::getResultCapture().lastAssertionPassed() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
+        try { \
+            static_cast<void>(expr); \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        } \
+        catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, matcher, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition, #matcher ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                static_cast<void>(expr); \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( ... ) { \
+                __catchResult.captureExpectedException( matcher ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr ", " #exceptionType, resultDisposition ); \
+        if( __catchResult.allowThrows() ) \
+            try { \
+                static_cast<void>(expr); \
+                __catchResult.captureResult( Catch::ResultWas::DidntThrowException ); \
+            } \
+            catch( exceptionType ) { \
+                __catchResult.captureResult( Catch::ResultWas::Ok ); \
+            } \
+            catch( ... ) { \
+                __catchResult.useActiveException( resultDisposition ); \
+            } \
+        else \
+            __catchResult.captureResult( Catch::ResultWas::Ok ); \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+///////////////////////////////////////////////////////////////////////////////
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
+        do { \
+            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+            __catchResult << __VA_ARGS__ + ::Catch::StreamEndStop(); \
+            __catchResult.captureResult( messageType ); \
+            INTERNAL_CATCH_REACT( __catchResult ) \
+        } while( Catch::alwaysFalse() )
+#else
+    #define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, log ) \
+        do { \
+            Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, "", resultDisposition ); \
+            __catchResult << log + ::Catch::StreamEndStop(); \
+            __catchResult.captureResult( messageType ); \
+            INTERNAL_CATCH_REACT( __catchResult ) \
+        } while( Catch::alwaysFalse() )
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_INFO( macroName, log ) \
+    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage ) = Catch::MessageBuilder( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log;
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
+    do { \
+        Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #arg ", " #matcher, resultDisposition ); \
+        try { \
+            __catchResult.captureMatch( arg, matcher, #matcher ); \
+        } catch( ... ) { \
+            __catchResult.useActiveException( resultDisposition | Catch::ResultDisposition::ContinueOnFailure ); \
+        } \
+        INTERNAL_CATCH_REACT( __catchResult ) \
+    } while( Catch::alwaysFalse() )
+
+// #included from: internal/catch_section.h
+#define TWOBLUECUBES_CATCH_SECTION_H_INCLUDED
+
+// #included from: catch_section_info.h
+#define TWOBLUECUBES_CATCH_SECTION_INFO_H_INCLUDED
+
+// #included from: catch_totals.hpp
+#define TWOBLUECUBES_CATCH_TOTALS_HPP_INCLUDED
+
+#include <cstddef>
+
+namespace Catch {
+
+    struct Counts {
+        Counts() : passed( 0 ), failed( 0 ), failedButOk( 0 ) {}
+
+        Counts operator - ( Counts const& other ) const {
+            Counts diff;
+            diff.passed = passed - other.passed;
+            diff.failed = failed - other.failed;
+            diff.failedButOk = failedButOk - other.failedButOk;
+            return diff;
+        }
+        Counts& operator += ( Counts const& other ) {
+            passed += other.passed;
+            failed += other.failed;
+            failedButOk += other.failedButOk;
+            return *this;
+        }
+
+        std::size_t total() const {
+            return passed + failed + failedButOk;
+        }
+        bool allPassed() const {
+            return failed == 0 && failedButOk == 0;
+        }
+        bool allOk() const {
+            return failed == 0;
+        }
+
+        std::size_t passed;
+        std::size_t failed;
+        std::size_t failedButOk;
+    };
+
+    struct Totals {
+
+        Totals operator - ( Totals const& other ) const {
+            Totals diff;
+            diff.assertions = assertions - other.assertions;
+            diff.testCases = testCases - other.testCases;
+            return diff;
+        }
+
+        Totals delta( Totals const& prevTotals ) const {
+            Totals diff = *this - prevTotals;
+            if( diff.assertions.failed > 0 )
+                ++diff.testCases.failed;
+            else if( diff.assertions.failedButOk > 0 )
+                ++diff.testCases.failedButOk;
+            else
+                ++diff.testCases.passed;
+            return diff;
+        }
+
+        Totals& operator += ( Totals const& other ) {
+            assertions += other.assertions;
+            testCases += other.testCases;
+            return *this;
+        }
+
+        Counts assertions;
+        Counts testCases;
+    };
+}
+
+#include <string>
+
+namespace Catch {
+
+    struct SectionInfo {
+        SectionInfo
+            (   SourceLineInfo const& _lineInfo,
+                std::string const& _name,
+                std::string const& _description = std::string() );
+
+        std::string name;
+        std::string description;
+        SourceLineInfo lineInfo;
+    };
+
+    struct SectionEndInfo {
+        SectionEndInfo( SectionInfo const& _sectionInfo, Counts const& _prevAssertions, double _durationInSeconds )
+        : sectionInfo( _sectionInfo ), prevAssertions( _prevAssertions ), durationInSeconds( _durationInSeconds )
+        {}
+
+        SectionInfo sectionInfo;
+        Counts prevAssertions;
+        double durationInSeconds;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_timer.h
+#define TWOBLUECUBES_CATCH_TIMER_H_INCLUDED
+
+#ifdef _MSC_VER
+
+namespace Catch {
+    typedef unsigned long long UInt64;
+}
+#else
+#include <stdint.h>
+namespace Catch {
+    typedef uint64_t UInt64;
+}
+#endif
+
+namespace Catch {
+    class Timer {
+    public:
+        Timer() : m_ticks( 0 ) {}
+        void start();
+        unsigned int getElapsedMicroseconds() const;
+        unsigned int getElapsedMilliseconds() const;
+        double getElapsedSeconds() const;
+
+    private:
+        UInt64 m_ticks;
+    };
+
+} // namespace Catch
+
+#include <string>
+
+namespace Catch {
+
+    class Section : NonCopyable {
+    public:
+        Section( SectionInfo const& info );
+        ~Section();
+
+        // This indicates whether the section should be executed or not
+        operator bool() const;
+
+    private:
+        SectionInfo m_info;
+
+        std::string m_name;
+        Counts m_assertions;
+        bool m_sectionIncluded;
+        Timer m_timer;
+    };
+
+} // end namespace Catch
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define INTERNAL_CATCH_SECTION( ... ) \
+        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) )
+#else
+    #define INTERNAL_CATCH_SECTION( name, desc ) \
+        if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, name, desc ) )
+#endif
+
+// #included from: internal/catch_generators.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <stdlib.h>
+
+namespace Catch {
+
+template<typename T>
+struct IGenerator {
+    virtual ~IGenerator() {}
+    virtual T getValue( std::size_t index ) const = 0;
+    virtual std::size_t size () const = 0;
+};
+
+template<typename T>
+class BetweenGenerator : public IGenerator<T> {
+public:
+    BetweenGenerator( T from, T to ) : m_from( from ), m_to( to ){}
+
+    virtual T getValue( std::size_t index ) const {
+        return m_from+static_cast<int>( index );
+    }
+
+    virtual std::size_t size() const {
+        return static_cast<std::size_t>( 1+m_to-m_from );
+    }
+
+private:
+
+    T m_from;
+    T m_to;
+};
+
+template<typename T>
+class ValuesGenerator : public IGenerator<T> {
+public:
+    ValuesGenerator(){}
+
+    void add( T value ) {
+        m_values.push_back( value );
+    }
+
+    virtual T getValue( std::size_t index ) const {
+        return m_values[index];
+    }
+
+    virtual std::size_t size() const {
+        return m_values.size();
+    }
+
+private:
+    std::vector<T> m_values;
+};
+
+template<typename T>
+class CompositeGenerator {
+public:
+    CompositeGenerator() : m_totalSize( 0 ) {}
+
+    // *** Move semantics, similar to auto_ptr ***
+    CompositeGenerator( CompositeGenerator& other )
+    :   m_fileInfo( other.m_fileInfo ),
+        m_totalSize( 0 )
+    {
+        move( other );
+    }
+
+    CompositeGenerator& setFileInfo( const char* fileInfo ) {
+        m_fileInfo = fileInfo;
+        return *this;
+    }
+
+    ~CompositeGenerator() {
+        deleteAll( m_composed );
+    }
+
+    operator T () const {
+        size_t overallIndex = getCurrentContext().getGeneratorIndex( m_fileInfo, m_totalSize );
+
+        typename std::vector<const IGenerator<T>*>::const_iterator it = m_composed.begin();
+        typename std::vector<const IGenerator<T>*>::const_iterator itEnd = m_composed.end();
+        for( size_t index = 0; it != itEnd; ++it )
+        {
+            const IGenerator<T>* generator = *it;
+            if( overallIndex >= index && overallIndex < index + generator->size() )
+            {
+                return generator->getValue( overallIndex-index );
+            }
+            index += generator->size();
+        }
+        CATCH_INTERNAL_ERROR( "Indexed past end of generated range" );
+        return T(); // Suppress spurious "not all control paths return a value" warning in Visual Studio - if you know how to fix this please do so
+    }
+
+    void add( const IGenerator<T>* generator ) {
+        m_totalSize += generator->size();
+        m_composed.push_back( generator );
+    }
+
+    CompositeGenerator& then( CompositeGenerator& other ) {
+        move( other );
+        return *this;
+    }
+
+    CompositeGenerator& then( T value ) {
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( value );
+        add( valuesGen );
+        return *this;
+    }
+
+private:
+
+    void move( CompositeGenerator& other ) {
+        m_composed.insert( m_composed.end(), other.m_composed.begin(), other.m_composed.end() );
+        m_totalSize += other.m_totalSize;
+        other.m_composed.clear();
+    }
+
+    std::vector<const IGenerator<T>*> m_composed;
+    std::string m_fileInfo;
+    size_t m_totalSize;
+};
+
+namespace Generators
+{
+    template<typename T>
+    CompositeGenerator<T> between( T from, T to ) {
+        CompositeGenerator<T> generators;
+        generators.add( new BetweenGenerator<T>( from, to ) );
+        return generators;
+    }
+
+    template<typename T>
+    CompositeGenerator<T> values( T val1, T val2 ) {
+        CompositeGenerator<T> generators;
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( val1 );
+        valuesGen->add( val2 );
+        generators.add( valuesGen );
+        return generators;
+    }
+
+    template<typename T>
+    CompositeGenerator<T> values( T val1, T val2, T val3 ){
+        CompositeGenerator<T> generators;
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( val1 );
+        valuesGen->add( val2 );
+        valuesGen->add( val3 );
+        generators.add( valuesGen );
+        return generators;
+    }
+
+    template<typename T>
+    CompositeGenerator<T> values( T val1, T val2, T val3, T val4 ) {
+        CompositeGenerator<T> generators;
+        ValuesGenerator<T>* valuesGen = new ValuesGenerator<T>();
+        valuesGen->add( val1 );
+        valuesGen->add( val2 );
+        valuesGen->add( val3 );
+        valuesGen->add( val4 );
+        generators.add( valuesGen );
+        return generators;
+    }
+
+} // end namespace Generators
+
+using namespace Generators;
+
+} // end namespace Catch
+
+#define INTERNAL_CATCH_LINESTR2( line ) #line
+#define INTERNAL_CATCH_LINESTR( line ) INTERNAL_CATCH_LINESTR2( line )
+
+#define INTERNAL_CATCH_GENERATE( expr ) expr.setFileInfo( __FILE__ "(" INTERNAL_CATCH_LINESTR( __LINE__ ) ")" )
+
+// #included from: internal/catch_interfaces_exception.h
+#define TWOBLUECUBES_CATCH_INTERFACES_EXCEPTION_H_INCLUDED
+
+#include <string>
+#include <vector>
+
+// #included from: catch_interfaces_registry_hub.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REGISTRY_HUB_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    class TestCase;
+    struct ITestCaseRegistry;
+    struct IExceptionTranslatorRegistry;
+    struct IExceptionTranslator;
+    struct IReporterRegistry;
+    struct IReporterFactory;
+    struct ITagAliasRegistry;
+
+    struct IRegistryHub {
+        virtual ~IRegistryHub();
+
+        virtual IReporterRegistry const& getReporterRegistry() const = 0;
+        virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
+        virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
+
+        virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() = 0;
+    };
+
+    struct IMutableRegistryHub {
+        virtual ~IMutableRegistryHub();
+        virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) = 0;
+        virtual void registerListener( Ptr<IReporterFactory> const& factory ) = 0;
+        virtual void registerTest( TestCase const& testInfo ) = 0;
+        virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
+        virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
+    };
+
+    IRegistryHub& getRegistryHub();
+    IMutableRegistryHub& getMutableRegistryHub();
+    void cleanUp();
+    std::string translateActiveException();
+
+}
+
+namespace Catch {
+
+    typedef std::string(*exceptionTranslateFunction)();
+
+    struct IExceptionTranslator;
+    typedef std::vector<const IExceptionTranslator*> ExceptionTranslators;
+
+    struct IExceptionTranslator {
+        virtual ~IExceptionTranslator();
+        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
+    };
+
+    struct IExceptionTranslatorRegistry {
+        virtual ~IExceptionTranslatorRegistry();
+
+        virtual std::string translateActiveException() const = 0;
+    };
+
+    class ExceptionTranslatorRegistrar {
+        template<typename T>
+        class ExceptionTranslator : public IExceptionTranslator {
+        public:
+
+            ExceptionTranslator( std::string(*translateFunction)( T& ) )
+            : m_translateFunction( translateFunction )
+            {}
+
+            virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const CATCH_OVERRIDE {
+                try {
+                    if( it == itEnd )
+                        throw;
+                    else
+                        return (*it)->translate( it+1, itEnd );
+                }
+                catch( T& ex ) {
+                    return m_translateFunction( ex );
+                }
+            }
+
+        protected:
+            std::string(*m_translateFunction)( T& );
+        };
+
+    public:
+        template<typename T>
+        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
+            getMutableRegistryHub().registerTranslator
+                ( new ExceptionTranslator<T>( translateFunction ) );
+        }
+    };
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+    static std::string translatorName( signature ); \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
+    static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
+
+// #included from: internal/catch_approx.hpp
+#define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
+
+#include <cmath>
+#include <limits>
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+#include <type_traits>
+#endif
+
+namespace Catch {
+namespace Detail {
+
+    class Approx {
+    public:
+        explicit Approx ( double value )
+        :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
+            m_margin( 0.0 ),
+            m_scale( 1.0 ),
+            m_value( value )
+        {}
+
+        static Approx custom() {
+            return Approx( 0 );
+        }
+
+#if defined(CATCH_CONFIG_CPP11_TYPE_TRAITS)
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        Approx operator()( T value ) {
+            Approx approx( static_cast<double>(value) );
+            approx.epsilon( m_epsilon );
+            approx.margin( m_margin );
+            approx.scale( m_scale );
+            return approx;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        explicit Approx( T value ): Approx(static_cast<double>(value))
+        {}
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator == ( const T& lhs, Approx const& rhs ) {
+            // Thanks to Richard Harris for his help refining this formula
+            auto lhs_v = double(lhs);
+            bool relativeOK = std::fabs(lhs_v - rhs.m_value) < rhs.m_epsilon * (rhs.m_scale + (std::max)(std::fabs(lhs_v), std::fabs(rhs.m_value)));
+            if (relativeOK) {
+                return true;
+            }
+            return std::fabs(lhs_v - rhs.m_value) < rhs.m_margin;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator == ( Approx const& lhs, const T& rhs ) {
+            return operator==( rhs, lhs );
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator != ( T lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator != ( Approx const& lhs, T rhs ) {
+            return !operator==( rhs, lhs );
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator <= ( T lhs, Approx const& rhs ) {
+            return double(lhs) < rhs.m_value || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator <= ( Approx const& lhs, T rhs ) {
+            return lhs.m_value < double(rhs) || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator >= ( T lhs, Approx const& rhs ) {
+            return double(lhs) > rhs.m_value || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        friend bool operator >= ( Approx const& lhs, T rhs ) {
+            return lhs.m_value > double(rhs) || lhs == rhs;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        Approx& epsilon( T newEpsilon ) {
+            m_epsilon = double(newEpsilon);
+            return *this;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        Approx& margin( T newMargin ) {
+            m_margin = double(newMargin);
+            return *this;
+        }
+
+        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
+        Approx& scale( T newScale ) {
+            m_scale = double(newScale);
+            return *this;
+        }
+
+#else
+
+        Approx operator()( double value ) {
+            Approx approx( value );
+            approx.epsilon( m_epsilon );
+            approx.margin( m_margin );
+            approx.scale( m_scale );
+            return approx;
+        }
+
+        friend bool operator == ( double lhs, Approx const& rhs ) {
+            // Thanks to Richard Harris for his help refining this formula
+            bool relativeOK = std::fabs( lhs - rhs.m_value ) < rhs.m_epsilon * (rhs.m_scale + (std::max)( std::fabs(lhs), std::fabs(rhs.m_value) ) );
+            if (relativeOK) {
+                return true;
+            }
+            return std::fabs(lhs - rhs.m_value) < rhs.m_margin;
+        }
+
+        friend bool operator == ( Approx const& lhs, double rhs ) {
+            return operator==( rhs, lhs );
+        }
+
+        friend bool operator != ( double lhs, Approx const& rhs ) {
+            return !operator==( lhs, rhs );
+        }
+
+        friend bool operator != ( Approx const& lhs, double rhs ) {
+            return !operator==( rhs, lhs );
+        }
+
+        friend bool operator <= ( double lhs, Approx const& rhs ) {
+            return lhs < rhs.m_value || lhs == rhs;
+        }
+
+        friend bool operator <= ( Approx const& lhs, double rhs ) {
+            return lhs.m_value < rhs || lhs == rhs;
+        }
+
+        friend bool operator >= ( double lhs, Approx const& rhs ) {
+            return lhs > rhs.m_value || lhs == rhs;
+        }
+
+        friend bool operator >= ( Approx const& lhs, double rhs ) {
+            return lhs.m_value > rhs || lhs == rhs;
+        }
+
+        Approx& epsilon( double newEpsilon ) {
+            m_epsilon = newEpsilon;
+            return *this;
+        }
+
+        Approx& margin( double newMargin ) {
+            m_margin = newMargin;
+            return *this;
+        }
+
+        Approx& scale( double newScale ) {
+            m_scale = newScale;
+            return *this;
+        }
+#endif
+
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << "Approx( " << Catch::toString( m_value ) << " )";
+            return oss.str();
+        }
+
+    private:
+        double m_epsilon;
+        double m_margin;
+        double m_scale;
+        double m_value;
+    };
+}
+
+template<>
+inline std::string toString<Detail::Approx>( Detail::Approx const& value ) {
+    return value.toString();
+}
+
+} // end namespace Catch
+
+// #included from: internal/catch_matchers_string.h
+#define TWOBLUECUBES_CATCH_MATCHERS_STRING_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+    namespace StdString {
+
+        struct CasedString
+        {
+            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
+            std::string adjustString( std::string const& str ) const;
+            std::string caseSensitivitySuffix() const;
+
+            CaseSensitive::Choice m_caseSensitivity;
+            std::string m_str;
+        };
+
+        struct StringMatcherBase : MatcherBase<std::string> {
+            StringMatcherBase( std::string const& operation, CasedString const& comparator );
+            virtual std::string describe() const CATCH_OVERRIDE;
+
+            CasedString m_comparator;
+            std::string m_operation;
+        };
+
+        struct EqualsMatcher : StringMatcherBase {
+            EqualsMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+        struct ContainsMatcher : StringMatcherBase {
+            ContainsMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+        struct StartsWithMatcher : StringMatcherBase {
+            StartsWithMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+        struct EndsWithMatcher : StringMatcherBase {
+            EndsWithMatcher( CasedString const& comparator );
+            virtual bool match( std::string const& source ) const CATCH_OVERRIDE;
+        };
+
+    } // namespace StdString
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_matchers_vector.h
+#define TWOBLUECUBES_CATCH_MATCHERS_VECTOR_H_INCLUDED
+
+namespace Catch {
+namespace Matchers {
+
+    namespace Vector {
+
+        template<typename T>
+        struct ContainsElementMatcher : MatcherBase<std::vector<T>, T> {
+
+            ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
+
+            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+                return std::find(v.begin(), v.end(), m_comparator) != v.end();
+            }
+
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "Contains: " + Catch::toString( m_comparator );
+            }
+
+            T const& m_comparator;
+        };
+
+        template<typename T>
+        struct ContainsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+            ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+                // !TBD: see note in EqualsMatcher
+                if (m_comparator.size() > v.size())
+                    return false;
+                for (size_t i = 0; i < m_comparator.size(); ++i)
+                    if (std::find(v.begin(), v.end(), m_comparator[i]) == v.end())
+                        return false;
+                return true;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "Contains: " + Catch::toString( m_comparator );
+            }
+
+            std::vector<T> const& m_comparator;
+        };
+
+        template<typename T>
+        struct EqualsMatcher : MatcherBase<std::vector<T>, std::vector<T> > {
+
+            EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
+
+            bool match(std::vector<T> const &v) const CATCH_OVERRIDE {
+                // !TBD: This currently works if all elements can be compared using !=
+                // - a more general approach would be via a compare template that defaults
+                // to using !=. but could be specialised for, e.g. std::vector<T> etc
+                // - then just call that directly
+                if (m_comparator.size() != v.size())
+                    return false;
+                for (size_t i = 0; i < v.size(); ++i)
+                    if (m_comparator[i] != v[i])
+                        return false;
+                return true;
+            }
+            virtual std::string describe() const CATCH_OVERRIDE {
+                return "Equals: " + Catch::toString( m_comparator );
+            }
+            std::vector<T> const& m_comparator;
+        };
+
+    } // namespace Vector
+
+    // The following functions create the actual matcher objects.
+    // This allows the types to be inferred
+
+    template<typename T>
+    Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
+        return Vector::ContainsMatcher<T>( comparator );
+    }
+
+    template<typename T>
+    Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
+        return Vector::ContainsElementMatcher<T>( comparator );
+    }
+
+    template<typename T>
+    Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
+        return Vector::EqualsMatcher<T>( comparator );
+    }
+
+} // namespace Matchers
+} // namespace Catch
+
+// #included from: internal/catch_interfaces_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_INTERFACES_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+// #included from: catch_tag_alias.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_H_INCLUDED
+
+#include <string>
+
+namespace Catch {
+
+    struct TagAlias {
+        TagAlias( std::string const& _tag, SourceLineInfo _lineInfo ) : tag( _tag ), lineInfo( _lineInfo ) {}
+
+        std::string tag;
+        SourceLineInfo lineInfo;
+    };
+
+    struct RegistrarForTagAliases {
+        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
+    };
+
+} // end namespace Catch
+
+#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); }
+// #included from: catch_option.hpp
+#define TWOBLUECUBES_CATCH_OPTION_HPP_INCLUDED
+
+namespace Catch {
+
+    // An optional type
+    template<typename T>
+    class Option {
+    public:
+        Option() : nullableValue( CATCH_NULL ) {}
+        Option( T const& _value )
+        : nullableValue( new( storage ) T( _value ) )
+        {}
+        Option( Option const& _other )
+        : nullableValue( _other ? new( storage ) T( *_other ) : CATCH_NULL )
+        {}
+
+        ~Option() {
+            reset();
+        }
+
+        Option& operator= ( Option const& _other ) {
+            if( &_other != this ) {
+                reset();
+                if( _other )
+                    nullableValue = new( storage ) T( *_other );
+            }
+            return *this;
+        }
+        Option& operator = ( T const& _value ) {
+            reset();
+            nullableValue = new( storage ) T( _value );
+            return *this;
+        }
+
+        void reset() {
+            if( nullableValue )
+                nullableValue->~T();
+            nullableValue = CATCH_NULL;
+        }
+
+        T& operator*() { return *nullableValue; }
+        T const& operator*() const { return *nullableValue; }
+        T* operator->() { return nullableValue; }
+        const T* operator->() const { return nullableValue; }
+
+        T valueOr( T const& defaultValue ) const {
+            return nullableValue ? *nullableValue : defaultValue;
+        }
+
+        bool some() const { return nullableValue != CATCH_NULL; }
+        bool none() const { return nullableValue == CATCH_NULL; }
+
+        bool operator !() const { return nullableValue == CATCH_NULL; }
+        operator SafeBool::type() const {
+            return SafeBool::makeSafe( some() );
+        }
+
+    private:
+        T *nullableValue;
+        union {
+            char storage[sizeof(T)];
+
+            // These are here to force alignment for the storage
+            long double dummy1;
+            void (*dummy2)();
+            long double dummy3;
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+            long long dummy4;
+#endif
+        };
+    };
+
+} // end namespace Catch
+
+namespace Catch {
+
+    struct ITagAliasRegistry {
+        virtual ~ITagAliasRegistry();
+        virtual Option<TagAlias> find( std::string const& alias ) const = 0;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
+
+        static ITagAliasRegistry const& get();
+    };
+
+} // end namespace Catch
+
+// These files are included here so the single_include script doesn't put them
+// in the conditionally compiled sections
+// #included from: internal/catch_test_case_info.h
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_H_INCLUDED
+
+#include <string>
+#include <set>
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+namespace Catch {
+
+    struct ITestCase;
+
+    struct TestCaseInfo {
+        enum SpecialProperties{
+            None = 0,
+            IsHidden = 1 << 1,
+            ShouldFail = 1 << 2,
+            MayFail = 1 << 3,
+            Throws = 1 << 4,
+            NonPortable = 1 << 5
+        };
+
+        TestCaseInfo(   std::string const& _name,
+                        std::string const& _className,
+                        std::string const& _description,
+                        std::set<std::string> const& _tags,
+                        SourceLineInfo const& _lineInfo );
+
+        TestCaseInfo( TestCaseInfo const& other );
+
+        friend void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags );
+
+        bool isHidden() const;
+        bool throws() const;
+        bool okToFail() const;
+        bool expectedToFail() const;
+
+        std::string name;
+        std::string className;
+        std::string description;
+        std::set<std::string> tags;
+        std::set<std::string> lcaseTags;
+        std::string tagsAsString;
+        SourceLineInfo lineInfo;
+        SpecialProperties properties;
+    };
+
+    class TestCase : public TestCaseInfo {
+    public:
+
+        TestCase( ITestCase* testCase, TestCaseInfo const& info );
+        TestCase( TestCase const& other );
+
+        TestCase withName( std::string const& _newName ) const;
+
+        void invoke() const;
+
+        TestCaseInfo const& getTestCaseInfo() const;
+
+        void swap( TestCase& other );
+        bool operator == ( TestCase const& other ) const;
+        bool operator < ( TestCase const& other ) const;
+        TestCase& operator = ( TestCase const& other );
+
+    private:
+        Ptr<ITestCase> test;
+    };
+
+    TestCase makeTestCase(  ITestCase* testCase,
+                            std::string const& className,
+                            std::string const& name,
+                            std::string const& description,
+                            SourceLineInfo const& lineInfo );
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+
+#ifdef __OBJC__
+// #included from: internal/catch_objc.hpp
+#define TWOBLUECUBES_CATCH_OBJC_HPP_INCLUDED
+
+#import <objc/runtime.h>
+
+#include <string>
+
+// NB. Any general catch headers included here must be included
+// in catch.hpp first to make sure they are included by the single
+// header for non obj-usage
+
+///////////////////////////////////////////////////////////////////////////////
+// This protocol is really only here for (self) documenting purposes, since
+// all its methods are optional.
+@protocol OcFixture
+
+@optional
+
+-(void) setUp;
+-(void) tearDown;
+
+@end
+
+namespace Catch {
+
+    class OcMethod : public SharedImpl<ITestCase> {
+
+    public:
+        OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
+
+        virtual void invoke() const {
+            id obj = [[m_cls alloc] init];
+
+            performOptionalSelector( obj, @selector(setUp)  );
+            performOptionalSelector( obj, m_sel );
+            performOptionalSelector( obj, @selector(tearDown)  );
+
+            arcSafeRelease( obj );
+        }
+    private:
+        virtual ~OcMethod() {}
+
+        Class m_cls;
+        SEL m_sel;
+    };
+
+    namespace Detail{
+
+        inline std::string getAnnotation(   Class cls,
+                                            std::string const& annotationName,
+                                            std::string const& testCaseName ) {
+            NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
+            SEL sel = NSSelectorFromString( selStr );
+            arcSafeRelease( selStr );
+            id value = performOptionalSelector( cls, sel );
+            if( value )
+                return [(NSString*)value UTF8String];
+            return "";
+        }
+    }
+
+    inline size_t registerTestMethods() {
+        size_t noTestMethods = 0;
+        int noClasses = objc_getClassList( CATCH_NULL, 0 );
+
+        Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
+        objc_getClassList( classes, noClasses );
+
+        for( int c = 0; c < noClasses; c++ ) {
+            Class cls = classes[c];
+            {
+                u_int count;
+                Method* methods = class_copyMethodList( cls, &count );
+                for( u_int m = 0; m < count ; m++ ) {
+                    SEL selector = method_getName(methods[m]);
+                    std::string methodName = sel_getName(selector);
+                    if( startsWith( methodName, "Catch_TestCase_" ) ) {
+                        std::string testCaseName = methodName.substr( 15 );
+                        std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
+                        std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
+                        const char* className = class_getName( cls );
+
+                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, name.c_str(), desc.c_str(), SourceLineInfo() ) );
+                        noTestMethods++;
+                    }
+                }
+                free(methods);
+            }
+        }
+        return noTestMethods;
+    }
+
+    namespace Matchers {
+        namespace Impl {
+        namespace NSStringMatchers {
+
+            struct StringHolder : MatcherBase<NSString*>{
+                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
+                StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
+                StringHolder() {
+                    arcSafeRelease( m_substr );
+                }
+
+                virtual bool match( NSString* arg ) const CATCH_OVERRIDE {
+                    return false;
+                }
+
+                NSString* m_substr;
+            };
+
+            struct Equals : StringHolder {
+                Equals( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( NSString* str ) const CATCH_OVERRIDE {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str isEqualToString:m_substr];
+                }
+
+                virtual std::string describe() const CATCH_OVERRIDE {
+                    return "equals string: " + Catch::toString( m_substr );
+                }
+            };
+
+            struct Contains : StringHolder {
+                Contains( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( NSString* str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location != NSNotFound;
+                }
+
+                virtual std::string describe() const CATCH_OVERRIDE {
+                    return "contains string: " + Catch::toString( m_substr );
+                }
+            };
+
+            struct StartsWith : StringHolder {
+                StartsWith( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( NSString* str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == 0;
+                }
+
+                virtual std::string describe() const CATCH_OVERRIDE {
+                    return "starts with: " + Catch::toString( m_substr );
+                }
+            };
+            struct EndsWith : StringHolder {
+                EndsWith( NSString* substr ) : StringHolder( substr ){}
+
+                virtual bool match( NSString* str ) const {
+                    return  (str != nil || m_substr == nil ) &&
+                            [str rangeOfString:m_substr].location == [str length] - [m_substr length];
+                }
+
+                virtual std::string describe() const CATCH_OVERRIDE {
+                    return "ends with: " + Catch::toString( m_substr );
+                }
+            };
+
+        } // namespace NSStringMatchers
+        } // namespace Impl
+
+        inline Impl::NSStringMatchers::Equals
+            Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
+
+        inline Impl::NSStringMatchers::Contains
+            Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
+
+        inline Impl::NSStringMatchers::StartsWith
+            StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
+
+        inline Impl::NSStringMatchers::EndsWith
+            EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
+
+    } // namespace Matchers
+
+    using namespace Matchers;
+
+} // namespace Catch
+
+///////////////////////////////////////////////////////////////////////////////
+#define OC_TEST_CASE( name, desc )\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Name_test ) \
+{\
+return @ name; \
+}\
++(NSString*) INTERNAL_CATCH_UNIQUE_NAME( Catch_Description_test ) \
+{ \
+return @ desc; \
+} \
+-(void) INTERNAL_CATCH_UNIQUE_NAME( Catch_TestCase_test )
+
+#endif
+
+#ifdef CATCH_IMPL
+
+// !TBD: Move the leak detector code into a separate header
+#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
+#include <crtdbg.h>
+class LeakDetector {
+public:
+    LeakDetector() {
+        int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
+        flag |= _CRTDBG_LEAK_CHECK_DF;
+        flag |= _CRTDBG_ALLOC_MEM_DF;
+        _CrtSetDbgFlag(flag);
+        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
+        _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
+        // Change this to leaking allocation's number to break there
+        _CrtSetBreakAlloc(-1);
+    }
+};
+#else
+class LeakDetector {};
+#endif
+
+LeakDetector leakDetector;
+
+// #included from: internal/catch_impl.hpp
+#define TWOBLUECUBES_CATCH_IMPL_HPP_INCLUDED
+
+// Collect all the implementation files together here
+// These are the equivalent of what would usually be cpp files
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wweak-vtables"
+#endif
+
+// #included from: ../catch_session.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_HPP_INCLUDED
+
+// #included from: internal/catch_commandline.hpp
+#define TWOBLUECUBES_CATCH_COMMANDLINE_HPP_INCLUDED
+
+// #included from: catch_config.hpp
+#define TWOBLUECUBES_CATCH_CONFIG_HPP_INCLUDED
+
+// #included from: catch_test_spec_parser.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_PARSER_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_test_spec.hpp
+#define TWOBLUECUBES_CATCH_TEST_SPEC_HPP_INCLUDED
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wpadded"
+#endif
+
+// #included from: catch_wildcard_pattern.hpp
+#define TWOBLUECUBES_CATCH_WILDCARD_PATTERN_HPP_INCLUDED
+
+#include <stdexcept>
+
+namespace Catch
+{
+    class WildcardPattern {
+        enum WildcardPosition {
+            NoWildcard = 0,
+            WildcardAtStart = 1,
+            WildcardAtEnd = 2,
+            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
+        };
+
+    public:
+
+        WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_wildcard( NoWildcard ),
+            m_pattern( adjustCase( pattern ) )
+        {
+            if( startsWith( m_pattern, '*' ) ) {
+                m_pattern = m_pattern.substr( 1 );
+                m_wildcard = WildcardAtStart;
+            }
+            if( endsWith( m_pattern, '*' ) ) {
+                m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
+                m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
+            }
+        }
+        virtual ~WildcardPattern();
+        virtual bool matches( std::string const& str ) const {
+            switch( m_wildcard ) {
+                case NoWildcard:
+                    return m_pattern == adjustCase( str );
+                case WildcardAtStart:
+                    return endsWith( adjustCase( str ), m_pattern );
+                case WildcardAtEnd:
+                    return startsWith( adjustCase( str ), m_pattern );
+                case WildcardAtBothEnds:
+                    return contains( adjustCase( str ), m_pattern );
+            }
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunreachable-code"
+#endif
+            throw std::logic_error( "Unknown enum" );
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+        }
+    private:
+        std::string adjustCase( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
+        }
+        CaseSensitive::Choice m_caseSensitivity;
+        WildcardPosition m_wildcard;
+        std::string m_pattern;
+    };
+}
+
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    class TestSpec {
+        struct Pattern : SharedImpl<> {
+            virtual ~Pattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const = 0;
+        };
+        class NamePattern : public Pattern {
+        public:
+            NamePattern( std::string const& name )
+            : m_wildcardPattern( toLower( name ), CaseSensitive::No )
+            {}
+            virtual ~NamePattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const {
+                return m_wildcardPattern.matches( toLower( testCase.name ) );
+            }
+        private:
+            WildcardPattern m_wildcardPattern;
+        };
+
+        class TagPattern : public Pattern {
+        public:
+            TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
+            virtual ~TagPattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const {
+                return testCase.lcaseTags.find( m_tag ) != testCase.lcaseTags.end();
+            }
+        private:
+            std::string m_tag;
+        };
+
+        class ExcludedPattern : public Pattern {
+        public:
+            ExcludedPattern( Ptr<Pattern> const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
+            virtual ~ExcludedPattern();
+            virtual bool matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
+        private:
+            Ptr<Pattern> m_underlyingPattern;
+        };
+
+        struct Filter {
+            std::vector<Ptr<Pattern> > m_patterns;
+
+            bool matches( TestCaseInfo const& testCase ) const {
+                // All patterns in a filter must match for the filter to be a match
+                for( std::vector<Ptr<Pattern> >::const_iterator it = m_patterns.begin(), itEnd = m_patterns.end(); it != itEnd; ++it ) {
+                    if( !(*it)->matches( testCase ) )
+                        return false;
+                }
+                return true;
+            }
+        };
+
+    public:
+        bool hasFilters() const {
+            return !m_filters.empty();
+        }
+        bool matches( TestCaseInfo const& testCase ) const {
+            // A TestSpec matches if any filter matches
+            for( std::vector<Filter>::const_iterator it = m_filters.begin(), itEnd = m_filters.end(); it != itEnd; ++it )
+                if( it->matches( testCase ) )
+                    return true;
+            return false;
+        }
+
+    private:
+        std::vector<Filter> m_filters;
+
+        friend class TestSpecParser;
+    };
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+namespace Catch {
+
+    class TestSpecParser {
+        enum Mode{ None, Name, QuotedName, Tag, EscapedName };
+        Mode m_mode;
+        bool m_exclusion;
+        std::size_t m_start, m_pos;
+        std::string m_arg;
+        std::vector<std::size_t> m_escapeChars;
+        TestSpec::Filter m_currentFilter;
+        TestSpec m_testSpec;
+        ITagAliasRegistry const* m_tagAliases;
+
+    public:
+        TestSpecParser( ITagAliasRegistry const& tagAliases ) :m_mode(None), m_exclusion(false), m_start(0), m_pos(0), m_tagAliases( &tagAliases ) {}
+
+        TestSpecParser& parse( std::string const& arg ) {
+            m_mode = None;
+            m_exclusion = false;
+            m_start = std::string::npos;
+            m_arg = m_tagAliases->expandAliases( arg );
+            m_escapeChars.clear();
+            for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
+                visitChar( m_arg[m_pos] );
+            if( m_mode == Name )
+                addPattern<TestSpec::NamePattern>();
+            return *this;
+        }
+        TestSpec testSpec() {
+            addFilter();
+            return m_testSpec;
+        }
+    private:
+        void visitChar( char c ) {
+            if( m_mode == None ) {
+                switch( c ) {
+                case ' ': return;
+                case '~': m_exclusion = true; return;
+                case '[': return startNewMode( Tag, ++m_pos );
+                case '"': return startNewMode( QuotedName, ++m_pos );
+                case '\\': return escape();
+                default: startNewMode( Name, m_pos ); break;
+                }
+            }
+            if( m_mode == Name ) {
+                if( c == ',' ) {
+                    addPattern<TestSpec::NamePattern>();
+                    addFilter();
+                }
+                else if( c == '[' ) {
+                    if( subString() == "exclude:" )
+                        m_exclusion = true;
+                    else
+                        addPattern<TestSpec::NamePattern>();
+                    startNewMode( Tag, ++m_pos );
+                }
+                else if( c == '\\' )
+                    escape();
+            }
+            else if( m_mode == EscapedName )
+                m_mode = Name;
+            else if( m_mode == QuotedName && c == '"' )
+                addPattern<TestSpec::NamePattern>();
+            else if( m_mode == Tag && c == ']' )
+                addPattern<TestSpec::TagPattern>();
+        }
+        void startNewMode( Mode mode, std::size_t start ) {
+            m_mode = mode;
+            m_start = start;
+        }
+        void escape() {
+            if( m_mode == None )
+                m_start = m_pos;
+            m_mode = EscapedName;
+            m_escapeChars.push_back( m_pos );
+        }
+        std::string subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
+        template<typename T>
+        void addPattern() {
+            std::string token = subString();
+            for( size_t i = 0; i < m_escapeChars.size(); ++i )
+                token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
+            m_escapeChars.clear();
+            if( startsWith( token, "exclude:" ) ) {
+                m_exclusion = true;
+                token = token.substr( 8 );
+            }
+            if( !token.empty() ) {
+                Ptr<TestSpec::Pattern> pattern = new T( token );
+                if( m_exclusion )
+                    pattern = new TestSpec::ExcludedPattern( pattern );
+                m_currentFilter.m_patterns.push_back( pattern );
+            }
+            m_exclusion = false;
+            m_mode = None;
+        }
+        void addFilter() {
+            if( !m_currentFilter.m_patterns.empty() ) {
+                m_testSpec.m_filters.push_back( m_currentFilter );
+                m_currentFilter = TestSpec::Filter();
+            }
+        }
+    };
+    inline TestSpec parseTestSpec( std::string const& arg ) {
+        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
+    }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+// #included from: catch_interfaces_config.h
+#define TWOBLUECUBES_CATCH_INTERFACES_CONFIG_H_INCLUDED
+
+#include <iosfwd>
+#include <string>
+#include <vector>
+
+namespace Catch {
+
+    struct Verbosity { enum Level {
+        NoOutput = 0,
+        Quiet,
+        Normal
+    }; };
+
+    struct WarnAbout { enum What {
+        Nothing = 0x00,
+        NoAssertions = 0x01
+    }; };
+
+    struct ShowDurations { enum OrNot {
+        DefaultForReporter,
+        Always,
+        Never
+    }; };
+    struct RunTests { enum InWhatOrder {
+        InDeclarationOrder,
+        InLexicographicalOrder,
+        InRandomOrder
+    }; };
+    struct UseColour { enum YesOrNo {
+        Auto,
+        Yes,
+        No
+    }; };
+
+    class TestSpec;
+
+    struct IConfig : IShared {
+
+        virtual ~IConfig();
+
+        virtual bool allowThrows() const = 0;
+        virtual std::ostream& stream() const = 0;
+        virtual std::string name() const = 0;
+        virtual bool includeSuccessfulResults() const = 0;
+        virtual bool shouldDebugBreak() const = 0;
+        virtual bool warnAboutMissingAssertions() const = 0;
+        virtual int abortAfter() const = 0;
+        virtual bool showInvisibles() const = 0;
+        virtual ShowDurations::OrNot showDurations() const = 0;
+        virtual TestSpec const& testSpec() const = 0;
+        virtual RunTests::InWhatOrder runOrder() const = 0;
+        virtual unsigned int rngSeed() const = 0;
+        virtual UseColour::YesOrNo useColour() const = 0;
+        virtual std::vector<std::string> const& getSectionsToRun() const = 0;
+
+    };
+}
+
+// #included from: catch_stream.h
+#define TWOBLUECUBES_CATCH_STREAM_H_INCLUDED
+
+// #included from: catch_streambuf.h
+#define TWOBLUECUBES_CATCH_STREAMBUF_H_INCLUDED
+
+#include <streambuf>
+
+namespace Catch {
+
+    class StreamBufBase : public std::streambuf {
+    public:
+        virtual ~StreamBufBase() CATCH_NOEXCEPT;
+    };
+}
+
+#include <streambuf>
+#include <ostream>
+#include <fstream>
+#include <memory>
+
+namespace Catch {
+
+    std::ostream& cout();
+    std::ostream& cerr();
+    std::ostream& clog();
+
+    struct IStream {
+        virtual ~IStream() CATCH_NOEXCEPT;
+        virtual std::ostream& stream() const = 0;
+    };
+
+    class FileStream : public IStream {
+        mutable std::ofstream m_ofs;
+    public:
+        FileStream( std::string const& filename );
+        virtual ~FileStream() CATCH_NOEXCEPT;
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class CoutStream : public IStream {
+        mutable std::ostream m_os;
+    public:
+        CoutStream();
+        virtual ~CoutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+
+    class DebugOutStream : public IStream {
+        CATCH_AUTO_PTR( StreamBufBase ) m_streamBuf;
+        mutable std::ostream m_os;
+    public:
+        DebugOutStream();
+        virtual ~DebugOutStream() CATCH_NOEXCEPT;
+
+    public: // IStream
+        virtual std::ostream& stream() const CATCH_OVERRIDE;
+    };
+}
+
+#include <memory>
+#include <vector>
+#include <string>
+#include <stdexcept>
+
+#ifndef CATCH_CONFIG_CONSOLE_WIDTH
+#define CATCH_CONFIG_CONSOLE_WIDTH 80
+#endif
+
+namespace Catch {
+
+    struct ConfigData {
+
+        ConfigData()
+        :   listTests( false ),
+            listTags( false ),
+            listReporters( false ),
+            listTestNamesOnly( false ),
+            listExtraInfo( false ),
+            showSuccessfulTests( false ),
+            shouldDebugBreak( false ),
+            noThrow( false ),
+            showHelp( false ),
+            showInvisibles( false ),
+            filenamesAsTags( false ),
+            abortAfter( -1 ),
+            rngSeed( 0 ),
+            verbosity( Verbosity::Normal ),
+            warnings( WarnAbout::Nothing ),
+            showDurations( ShowDurations::DefaultForReporter ),
+            runOrder( RunTests::InDeclarationOrder ),
+            useColour( UseColour::Auto )
+        {}
+
+        bool listTests;
+        bool listTags;
+        bool listReporters;
+        bool listTestNamesOnly;
+        bool listExtraInfo;
+
+        bool showSuccessfulTests;
+        bool shouldDebugBreak;
+        bool noThrow;
+        bool showHelp;
+        bool showInvisibles;
+        bool filenamesAsTags;
+
+        int abortAfter;
+        unsigned int rngSeed;
+
+        Verbosity::Level verbosity;
+        WarnAbout::What warnings;
+        ShowDurations::OrNot showDurations;
+        RunTests::InWhatOrder runOrder;
+        UseColour::YesOrNo useColour;
+
+        std::string outputFilename;
+        std::string name;
+        std::string processName;
+
+        std::vector<std::string> reporterNames;
+        std::vector<std::string> testsOrTags;
+        std::vector<std::string> sectionsToRun;
+    };
+
+    class Config : public SharedImpl<IConfig> {
+    private:
+        Config( Config const& other );
+        Config& operator = ( Config const& other );
+        virtual void dummy();
+    public:
+
+        Config()
+        {}
+
+        Config( ConfigData const& data )
+        :   m_data( data ),
+            m_stream( openStream() )
+        {
+            if( !data.testsOrTags.empty() ) {
+                TestSpecParser parser( ITagAliasRegistry::get() );
+                for( std::size_t i = 0; i < data.testsOrTags.size(); ++i )
+                    parser.parse( data.testsOrTags[i] );
+                m_testSpec = parser.testSpec();
+            }
+        }
+
+        virtual ~Config() {}
+
+        std::string const& getFilename() const {
+            return m_data.outputFilename ;
+        }
+
+        bool listTests() const { return m_data.listTests; }
+        bool listTestNamesOnly() const { return m_data.listTestNamesOnly; }
+        bool listTags() const { return m_data.listTags; }
+        bool listReporters() const { return m_data.listReporters; }
+        bool listExtraInfo() const { return m_data.listExtraInfo; }
+
+        std::string getProcessName() const { return m_data.processName; }
+
+        std::vector<std::string> const& getReporterNames() const { return m_data.reporterNames; }
+        std::vector<std::string> const& getSectionsToRun() const CATCH_OVERRIDE { return m_data.sectionsToRun; }
+
+        virtual TestSpec const& testSpec() const CATCH_OVERRIDE { return m_testSpec; }
+
+        bool showHelp() const { return m_data.showHelp; }
+
+        // IConfig interface
+        virtual bool allowThrows() const CATCH_OVERRIDE                 { return !m_data.noThrow; }
+        virtual std::ostream& stream() const CATCH_OVERRIDE             { return m_stream->stream(); }
+        virtual std::string name() const CATCH_OVERRIDE                 { return m_data.name.empty() ? m_data.processName : m_data.name; }
+        virtual bool includeSuccessfulResults() const CATCH_OVERRIDE    { return m_data.showSuccessfulTests; }
+        virtual bool warnAboutMissingAssertions() const CATCH_OVERRIDE  { return m_data.warnings & WarnAbout::NoAssertions; }
+        virtual ShowDurations::OrNot showDurations() const CATCH_OVERRIDE { return m_data.showDurations; }
+        virtual RunTests::InWhatOrder runOrder() const CATCH_OVERRIDE   { return m_data.runOrder; }
+        virtual unsigned int rngSeed() const CATCH_OVERRIDE             { return m_data.rngSeed; }
+        virtual UseColour::YesOrNo useColour() const CATCH_OVERRIDE     { return m_data.useColour; }
+        virtual bool shouldDebugBreak() const CATCH_OVERRIDE { return m_data.shouldDebugBreak; }
+        virtual int abortAfter() const CATCH_OVERRIDE { return m_data.abortAfter; }
+        virtual bool showInvisibles() const CATCH_OVERRIDE { return m_data.showInvisibles; }
+
+    private:
+
+        IStream const* openStream() {
+            if( m_data.outputFilename.empty() )
+                return new CoutStream();
+            else if( m_data.outputFilename[0] == '%' ) {
+                if( m_data.outputFilename == "%debug" )
+                    return new DebugOutStream();
+                else
+                    throw std::domain_error( "Unrecognised stream: " + m_data.outputFilename );
+            }
+            else
+                return new FileStream( m_data.outputFilename );
+        }
+        ConfigData m_data;
+
+        CATCH_AUTO_PTR( IStream const ) m_stream;
+        TestSpec m_testSpec;
+    };
+
+} // end namespace Catch
+
+// #included from: catch_clara.h
+#define TWOBLUECUBES_CATCH_CLARA_H_INCLUDED
+
+// Use Catch's value for console width (store Clara's off to the side, if present)
+#ifdef CLARA_CONFIG_CONSOLE_WIDTH
+#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH
+#undef CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+// Declare Clara inside the Catch namespace
+#define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
+// #included from: ../external/clara.h
+
+// Version 0.0.2.4
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
+
+#ifndef STITCH_CLARA_OPEN_NAMESPACE
+#define TWOBLUECUBES_CLARA_H_INCLUDED
+#define STITCH_CLARA_OPEN_NAMESPACE
+#define STITCH_CLARA_CLOSE_NAMESPACE
+#else
+#define STITCH_CLARA_CLOSE_NAMESPACE }
+#endif
+
+#define STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE STITCH_CLARA_OPEN_NAMESPACE
+
+// ----------- #included from tbc_text_format.h -----------
+
+// Only use header guard if we are not using an outer namespace
+#if !defined(TBC_TEXT_FORMAT_H_INCLUDED) || defined(STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE)
+#ifndef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+#define TBC_TEXT_FORMAT_H_INCLUDED
+#endif
+
+#include <string>
+#include <vector>
+#include <sstream>
+#include <algorithm>
+#include <cctype>
+
+// Use optional outer namespace
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+    struct TextAttributes {
+        TextAttributes()
+        :   initialIndent( std::string::npos ),
+            indent( 0 ),
+            width( consoleWidth-1 ),
+            tabChar( '\t' )
+        {}
+
+        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
+        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
+        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
+        TextAttributes& setTabChar( char _value )               { tabChar = _value; return *this; }
+
+        std::size_t initialIndent;  // indent of first line, or npos
+        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
+        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
+        char tabChar;               // If this char is seen the indent is changed to current pos
+    };
+
+    class Text {
+    public:
+        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+        : attr( _attr )
+        {
+            std::string wrappableChars = " [({.,/|\\-";
+            std::size_t indent = _attr.initialIndent != std::string::npos
+                ? _attr.initialIndent
+                : _attr.indent;
+            std::string remainder = _str;
+
+            while( !remainder.empty() ) {
+                if( lines.size() >= 1000 ) {
+                    lines.push_back( "... message truncated due to excessive size" );
+                    return;
+                }
+                std::size_t tabPos = std::string::npos;
+                std::size_t width = (std::min)( remainder.size(), _attr.width - indent );
+                std::size_t pos = remainder.find_first_of( '\n' );
+                if( pos <= width ) {
+                    width = pos;
+                }
+                pos = remainder.find_last_of( _attr.tabChar, width );
+                if( pos != std::string::npos ) {
+                    tabPos = pos;
+                    if( remainder[width] == '\n' )
+                        width--;
+                    remainder = remainder.substr( 0, tabPos ) + remainder.substr( tabPos+1 );
+                }
+
+                if( width == remainder.size() ) {
+                    spliceLine( indent, remainder, width );
+                }
+                else if( remainder[width] == '\n' ) {
+                    spliceLine( indent, remainder, width );
+                    if( width <= 1 || remainder.size() != 1 )
+                        remainder = remainder.substr( 1 );
+                    indent = _attr.indent;
+                }
+                else {
+                    pos = remainder.find_last_of( wrappableChars, width );
+                    if( pos != std::string::npos && pos > 0 ) {
+                        spliceLine( indent, remainder, pos );
+                        if( remainder[0] == ' ' )
+                            remainder = remainder.substr( 1 );
+                    }
+                    else {
+                        spliceLine( indent, remainder, width-1 );
+                        lines.back() += "-";
+                    }
+                    if( lines.size() == 1 )
+                        indent = _attr.indent;
+                    if( tabPos != std::string::npos )
+                        indent += tabPos;
+                }
+            }
+        }
+
+        void spliceLine( std::size_t _indent, std::string& _remainder, std::size_t _pos ) {
+            lines.push_back( std::string( _indent, ' ' ) + _remainder.substr( 0, _pos ) );
+            _remainder = _remainder.substr( _pos );
+        }
+
+        typedef std::vector<std::string>::const_iterator const_iterator;
+
+        const_iterator begin() const { return lines.begin(); }
+        const_iterator end() const { return lines.end(); }
+        std::string const& last() const { return lines.back(); }
+        std::size_t size() const { return lines.size(); }
+        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << *this;
+            return oss.str();
+        }
+
+        friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+                it != itEnd; ++it ) {
+                if( it != _text.begin() )
+                    _stream << "\n";
+                _stream << *it;
+            }
+            return _stream;
+        }
+
+    private:
+        std::string str;
+        TextAttributes attr;
+        std::vector<std::string> lines;
+    };
+
+} // end namespace Tbc
+
+#ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TBC_TEXT_FORMAT_H_INCLUDED
+
+// ----------- end of #include from tbc_text_format.h -----------
+// ........... back in clara.h
+
+#undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
+
+// ----------- #included from clara_compilers.h -----------
+
+#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CLARA_CONFIG_CPP11_OVERRIDE : is override supported?
+// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+
+// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11
+
+#ifdef __clang__
+
+#if __has_feature(cxx_nullptr)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#if __has_feature(cxx_noexcept)
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1600)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(__cplusplus) && __cplusplus >= 201103L
+
+#define CLARA_CPP11_OR_GREATER
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE
+#endif
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+// noexcept support:
+#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT)
+#define CLARA_NOEXCEPT noexcept
+#  define CLARA_NOEXCEPT_IS(x) noexcept(x)
+#else
+#define CLARA_NOEXCEPT throw()
+#  define CLARA_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CLARA_CONFIG_CPP11_NULLPTR
+#define CLARA_NULL nullptr
+#else
+#define CLARA_NULL NULL
+#endif
+
+// override support
+#ifdef CLARA_CONFIG_CPP11_OVERRIDE
+#define CLARA_OVERRIDE override
+#else
+#define CLARA_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR
+#   define CLARA_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+#   define CLARA_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// ----------- end of #include from clara_compilers.h -----------
+// ........... back in clara.h
+
+#include <map>
+#include <stdexcept>
+#include <memory>
+
+#if defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER)
+#define CLARA_PLATFORM_WINDOWS
+#endif
+
+// Use optional outer namespace
+#ifdef STITCH_CLARA_OPEN_NAMESPACE
+STITCH_CLARA_OPEN_NAMESPACE
+#endif
+
+namespace Clara {
+
+    struct UnpositionalTag {};
+
+    extern UnpositionalTag _;
+
+#ifdef CLARA_CONFIG_MAIN
+    UnpositionalTag _;
+#endif
+
+    namespace Detail {
+
+#ifdef CLARA_CONSOLE_WIDTH
+    const unsigned int consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+        using namespace Tbc;
+
+        inline bool startsWith( std::string const& str, std::string const& prefix ) {
+            return str.size() >= prefix.size() && str.substr( 0, prefix.size() ) == prefix;
+        }
+
+        template<typename T> struct RemoveConstRef{ typedef T type; };
+        template<typename T> struct RemoveConstRef<T&>{ typedef T type; };
+        template<typename T> struct RemoveConstRef<T const&>{ typedef T type; };
+        template<typename T> struct RemoveConstRef<T const>{ typedef T type; };
+
+        template<typename T>    struct IsBool       { static const bool value = false; };
+        template<>              struct IsBool<bool> { static const bool value = true; };
+
+        template<typename T>
+        void convertInto( std::string const& _source, T& _dest ) {
+            std::stringstream ss;
+            ss << _source;
+            ss >> _dest;
+            if( ss.fail() )
+                throw std::runtime_error( "Unable to convert " + _source + " to destination type" );
+        }
+        inline void convertInto( std::string const& _source, std::string& _dest ) {
+            _dest = _source;
+        }
+        char toLowerCh(char c) {
+            return static_cast<char>( std::tolower( c ) );
+        }
+        inline void convertInto( std::string const& _source, bool& _dest ) {
+            std::string sourceLC = _source;
+            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh );
+            if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
+                _dest = true;
+            else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
+                _dest = false;
+            else
+                throw std::runtime_error( "Expected a boolean value but did not recognise:\n  '" + _source + "'" );
+        }
+
+        template<typename ConfigT>
+        struct IArgFunction {
+            virtual ~IArgFunction() {}
+#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS
+            IArgFunction()                      = default;
+            IArgFunction( IArgFunction const& ) = default;
+#endif
+            virtual void set( ConfigT& config, std::string const& value ) const = 0;
+            virtual bool takesArg() const = 0;
+            virtual IArgFunction* clone() const = 0;
+        };
+
+        template<typename ConfigT>
+        class BoundArgFunction {
+        public:
+            BoundArgFunction() : functionObj( CLARA_NULL ) {}
+            BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
+            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {}
+            BoundArgFunction& operator = ( BoundArgFunction const& other ) {
+                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL;
+                delete functionObj;
+                functionObj = newFunctionObj;
+                return *this;
+            }
+            ~BoundArgFunction() { delete functionObj; }
+
+            void set( ConfigT& config, std::string const& value ) const {
+                functionObj->set( config, value );
+            }
+            bool takesArg() const { return functionObj->takesArg(); }
+
+            bool isSet() const {
+                return functionObj != CLARA_NULL;
+            }
+        private:
+            IArgFunction<ConfigT>* functionObj;
+        };
+
+        template<typename C>
+        struct NullBinder : IArgFunction<C>{
+            virtual void set( C&, std::string const& ) const {}
+            virtual bool takesArg() const { return true; }
+            virtual IArgFunction<C>* clone() const { return new NullBinder( *this ); }
+        };
+
+        template<typename C, typename M>
+        struct BoundDataMember : IArgFunction<C>{
+            BoundDataMember( M C::* _member ) : member( _member ) {}
+            virtual void set( C& p, std::string const& stringValue ) const {
+                convertInto( stringValue, p.*member );
+            }
+            virtual bool takesArg() const { return !IsBool<M>::value; }
+            virtual IArgFunction<C>* clone() const { return new BoundDataMember( *this ); }
+            M C::* member;
+        };
+        template<typename C, typename M>
+        struct BoundUnaryMethod : IArgFunction<C>{
+            BoundUnaryMethod( void (C::*_member)( M ) ) : member( _member ) {}
+            virtual void set( C& p, std::string const& stringValue ) const {
+                typename RemoveConstRef<M>::type value;
+                convertInto( stringValue, value );
+                (p.*member)( value );
+            }
+            virtual bool takesArg() const { return !IsBool<M>::value; }
+            virtual IArgFunction<C>* clone() const { return new BoundUnaryMethod( *this ); }
+            void (C::*member)( M );
+        };
+        template<typename C>
+        struct BoundNullaryMethod : IArgFunction<C>{
+            BoundNullaryMethod( void (C::*_member)() ) : member( _member ) {}
+            virtual void set( C& p, std::string const& stringValue ) const {
+                bool value;
+                convertInto( stringValue, value );
+                if( value )
+                    (p.*member)();
+            }
+            virtual bool takesArg() const { return false; }
+            virtual IArgFunction<C>* clone() const { return new BoundNullaryMethod( *this ); }
+            void (C::*member)();
+        };
+
+        template<typename C>
+        struct BoundUnaryFunction : IArgFunction<C>{
+            BoundUnaryFunction( void (*_function)( C& ) ) : function( _function ) {}
+            virtual void set( C& obj, std::string const& stringValue ) const {
+                bool value;
+                convertInto( stringValue, value );
+                if( value )
+                    function( obj );
+            }
+            virtual bool takesArg() const { return false; }
+            virtual IArgFunction<C>* clone() const { return new BoundUnaryFunction( *this ); }
+            void (*function)( C& );
+        };
+
+        template<typename C, typename T>
+        struct BoundBinaryFunction : IArgFunction<C>{
+            BoundBinaryFunction( void (*_function)( C&, T ) ) : function( _function ) {}
+            virtual void set( C& obj, std::string const& stringValue ) const {
+                typename RemoveConstRef<T>::type value;
+                convertInto( stringValue, value );
+                function( obj, value );
+            }
+            virtual bool takesArg() const { return !IsBool<T>::value; }
+            virtual IArgFunction<C>* clone() const { return new BoundBinaryFunction( *this ); }
+            void (*function)( C&, T );
+        };
+
+    } // namespace Detail
+
+    inline std::vector<std::string> argsToVector( int argc, char const* const* const argv ) {
+        std::vector<std::string> args( static_cast<std::size_t>( argc ) );
+        for( std::size_t i = 0; i < static_cast<std::size_t>( argc ); ++i )
+            args[i] = argv[i];
+
+        return args;
+    }
+
+    class Parser {
+        enum Mode { None, MaybeShortOpt, SlashOpt, ShortOpt, LongOpt, Positional };
+        Mode mode;
+        std::size_t from;
+        bool inQuotes;
+    public:
+
+        struct Token {
+            enum Type { Positional, ShortOpt, LongOpt };
+            Token( Type _type, std::string const& _data ) : type( _type ), data( _data ) {}
+            Type type;
+            std::string data;
+        };
+
+        Parser() : mode( None ), from( 0 ), inQuotes( false ){}
+
+        void parseIntoTokens( std::vector<std::string> const& args, std::vector<Token>& tokens ) {
+            const std::string doubleDash = "--";
+            for( std::size_t i = 1; i < args.size() && args[i] != doubleDash; ++i )
+                parseIntoTokens( args[i], tokens);
+        }
+
+        void parseIntoTokens( std::string const& arg, std::vector<Token>& tokens ) {
+            for( std::size_t i = 0; i < arg.size(); ++i ) {
+                char c = arg[i];
+                if( c == '"' )
+                    inQuotes = !inQuotes;
+                mode = handleMode( i, c, arg, tokens );
+            }
+            mode = handleMode( arg.size(), '\0', arg, tokens );
+        }
+        Mode handleMode( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+            switch( mode ) {
+                case None: return handleNone( i, c );
+                case MaybeShortOpt: return handleMaybeShortOpt( i, c );
+                case ShortOpt:
+                case LongOpt:
+                case SlashOpt: return handleOpt( i, c, arg, tokens );
+                case Positional: return handlePositional( i, c, arg, tokens );
+                default: throw std::logic_error( "Unknown mode" );
+            }
+        }
+
+        Mode handleNone( std::size_t i, char c ) {
+            if( inQuotes ) {
+                from = i;
+                return Positional;
+            }
+            switch( c ) {
+                case '-': return MaybeShortOpt;
+#ifdef CLARA_PLATFORM_WINDOWS
+                case '/': from = i+1; return SlashOpt;
+#endif
+                default: from = i; return Positional;
+            }
+        }
+        Mode handleMaybeShortOpt( std::size_t i, char c ) {
+            switch( c ) {
+                case '-': from = i+1; return LongOpt;
+                default: from = i; return ShortOpt;
+            }
+        }
+
+        Mode handleOpt( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+            if( std::string( ":=\0", 3 ).find( c ) == std::string::npos )
+                return mode;
+
+            std::string optName = arg.substr( from, i-from );
+            if( mode == ShortOpt )
+                for( std::size_t j = 0; j < optName.size(); ++j )
+                    tokens.push_back( Token( Token::ShortOpt, optName.substr( j, 1 ) ) );
+            else if( mode == SlashOpt && optName.size() == 1 )
+                tokens.push_back( Token( Token::ShortOpt, optName ) );
+            else
+                tokens.push_back( Token( Token::LongOpt, optName ) );
+            return None;
+        }
+        Mode handlePositional( std::size_t i, char c, std::string const& arg, std::vector<Token>& tokens ) {
+            if( inQuotes || std::string( "\0", 1 ).find( c ) == std::string::npos )
+                return mode;
+
+            std::string data = arg.substr( from, i-from );
+            tokens.push_back( Token( Token::Positional, data ) );
+            return None;
+        }
+    };
+
+    template<typename ConfigT>
+    struct CommonArgProperties {
+        CommonArgProperties() {}
+        CommonArgProperties( Detail::BoundArgFunction<ConfigT> const& _boundField ) : boundField( _boundField ) {}
+
+        Detail::BoundArgFunction<ConfigT> boundField;
+        std::string description;
+        std::string detail;
+        std::string placeholder; // Only value if boundField takes an arg
+
+        bool takesArg() const {
+            return !placeholder.empty();
+        }
+        void validate() const {
+            if( !boundField.isSet() )
+                throw std::logic_error( "option not bound" );
+        }
+    };
+    struct OptionArgProperties {
+        std::vector<std::string> shortNames;
+        std::string longName;
+
+        bool hasShortName( std::string const& shortName ) const {
+            return std::find( shortNames.begin(), shortNames.end(), shortName ) != shortNames.end();
+        }
+        bool hasLongName( std::string const& _longName ) const {
+            return _longName == longName;
+        }
+    };
+    struct PositionalArgProperties {
+        PositionalArgProperties() : position( -1 ) {}
+        int position; // -1 means non-positional (floating)
+
+        bool isFixedPositional() const {
+            return position != -1;
+        }
+    };
+
+    template<typename ConfigT>
+    class CommandLine {
+
+        struct Arg : CommonArgProperties<ConfigT>, OptionArgProperties, PositionalArgProperties {
+            Arg() {}
+            Arg( Detail::BoundArgFunction<ConfigT> const& _boundField ) : CommonArgProperties<ConfigT>( _boundField ) {}
+
+            using CommonArgProperties<ConfigT>::placeholder; // !TBD
+
+            std::string dbgName() const {
+                if( !longName.empty() )
+                    return "--" + longName;
+                if( !shortNames.empty() )
+                    return "-" + shortNames[0];
+                return "positional args";
+            }
+            std::string commands() const {
+                std::ostringstream oss;
+                bool first = true;
+                std::vector<std::string>::const_iterator it = shortNames.begin(), itEnd = shortNames.end();
+                for(; it != itEnd; ++it ) {
+                    if( first )
+                        first = false;
+                    else
+                        oss << ", ";
+                    oss << "-" << *it;
+                }
+                if( !longName.empty() ) {
+                    if( !first )
+                        oss << ", ";
+                    oss << "--" << longName;
+                }
+                if( !placeholder.empty() )
+                    oss << " <" << placeholder << ">";
+                return oss.str();
+            }
+        };
+
+        typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr;
+
+        friend void addOptName( Arg& arg, std::string const& optName )
+        {
+            if( optName.empty() )
+                return;
+            if( Detail::startsWith( optName, "--" ) ) {
+                if( !arg.longName.empty() )
+                    throw std::logic_error( "Only one long opt may be specified. '"
+                        + arg.longName
+                        + "' already specified, now attempting to add '"
+                        + optName + "'" );
+                arg.longName = optName.substr( 2 );
+            }
+            else if( Detail::startsWith( optName, "-" ) )
+                arg.shortNames.push_back( optName.substr( 1 ) );
+            else
+                throw std::logic_error( "option must begin with - or --. Option was: '" + optName + "'" );
+        }
+        friend void setPositionalArg( Arg& arg, int position )
+        {
+            arg.position = position;
+        }
+
+        class ArgBuilder {
+        public:
+            ArgBuilder( Arg* arg ) : m_arg( arg ) {}
+
+            // Bind a non-boolean data member (requires placeholder string)
+            template<typename C, typename M>
+            void bind( M C::* field, std::string const& placeholder ) {
+                m_arg->boundField = new Detail::BoundDataMember<C,M>( field );
+                m_arg->placeholder = placeholder;
+            }
+            // Bind a boolean data member (no placeholder required)
+            template<typename C>
+            void bind( bool C::* field ) {
+                m_arg->boundField = new Detail::BoundDataMember<C,bool>( field );
+            }
+
+            // Bind a method taking a single, non-boolean argument (requires a placeholder string)
+            template<typename C, typename M>
+            void bind( void (C::* unaryMethod)( M ), std::string const& placeholder ) {
+                m_arg->boundField = new Detail::BoundUnaryMethod<C,M>( unaryMethod );
+                m_arg->placeholder = placeholder;
+            }
+
+            // Bind a method taking a single, boolean argument (no placeholder string required)
+            template<typename C>
+            void bind( void (C::* unaryMethod)( bool ) ) {
+                m_arg->boundField = new Detail::BoundUnaryMethod<C,bool>( unaryMethod );
+            }
+
+            // Bind a method that takes no arguments (will be called if opt is present)
+            template<typename C>
+            void bind( void (C::* nullaryMethod)() ) {
+                m_arg->boundField = new Detail::BoundNullaryMethod<C>( nullaryMethod );
+            }
+
+            // Bind a free function taking a single argument - the object to operate on (no placeholder string required)
+            template<typename C>
+            void bind( void (* unaryFunction)( C& ) ) {
+                m_arg->boundField = new Detail::BoundUnaryFunction<C>( unaryFunction );
+            }
+
+            // Bind a free function taking a single argument - the object to operate on (requires a placeholder string)
+            template<typename C, typename T>
+            void bind( void (* binaryFunction)( C&, T ), std::string const& placeholder ) {
+                m_arg->boundField = new Detail::BoundBinaryFunction<C, T>( binaryFunction );
+                m_arg->placeholder = placeholder;
+            }
+
+            ArgBuilder& describe( std::string const& description ) {
+                m_arg->description = description;
+                return *this;
+            }
+            ArgBuilder& detail( std::string const& detail ) {
+                m_arg->detail = detail;
+                return *this;
+            }
+
+        protected:
+            Arg* m_arg;
+        };
+
+        class OptBuilder : public ArgBuilder {
+        public:
+            OptBuilder( Arg* arg ) : ArgBuilder( arg ) {}
+            OptBuilder( OptBuilder& other ) : ArgBuilder( other ) {}
+
+            OptBuilder& operator[]( std::string const& optName ) {
+                addOptName( *ArgBuilder::m_arg, optName );
+                return *this;
+            }
+        };
+
+    public:
+
+        CommandLine()
+        :   m_boundProcessName( new Detail::NullBinder<ConfigT>() ),
+            m_highestSpecifiedArgPosition( 0 ),
+            m_throwOnUnrecognisedTokens( false )
+        {}
+        CommandLine( CommandLine const& other )
+        :   m_boundProcessName( other.m_boundProcessName ),
+            m_options ( other.m_options ),
+            m_positionalArgs( other.m_positionalArgs ),
+            m_highestSpecifiedArgPosition( other.m_highestSpecifiedArgPosition ),
+            m_throwOnUnrecognisedTokens( other.m_throwOnUnrecognisedTokens )
+        {
+            if( other.m_floatingArg.get() )
+                m_floatingArg.reset( new Arg( *other.m_floatingArg ) );
+        }
+
+        CommandLine& setThrowOnUnrecognisedTokens( bool shouldThrow = true ) {
+            m_throwOnUnrecognisedTokens = shouldThrow;
+            return *this;
+        }
+
+        OptBuilder operator[]( std::string const& optName ) {
+            m_options.push_back( Arg() );
+            addOptName( m_options.back(), optName );
+            OptBuilder builder( &m_options.back() );
+            return builder;
+        }
+
+        ArgBuilder operator[]( int position ) {
+            m_positionalArgs.insert( std::make_pair( position, Arg() ) );
+            if( position > m_highestSpecifiedArgPosition )
+                m_highestSpecifiedArgPosition = position;
+            setPositionalArg( m_positionalArgs[position], position );
+            ArgBuilder builder( &m_positionalArgs[position] );
+            return builder;
+        }
+
+        // Invoke this with the _ instance
+        ArgBuilder operator[]( UnpositionalTag ) {
+            if( m_floatingArg.get() )
+                throw std::logic_error( "Only one unpositional argument can be added" );
+            m_floatingArg.reset( new Arg() );
+            ArgBuilder builder( m_floatingArg.get() );
+            return builder;
+        }
+
+        template<typename C, typename M>
+        void bindProcessName( M C::* field ) {
+            m_boundProcessName = new Detail::BoundDataMember<C,M>( field );
+        }
+        template<typename C, typename M>
+        void bindProcessName( void (C::*_unaryMethod)( M ) ) {
+            m_boundProcessName = new Detail::BoundUnaryMethod<C,M>( _unaryMethod );
+        }
+
+        void optUsage( std::ostream& os, std::size_t indent = 0, std::size_t width = Detail::consoleWidth ) const {
+            typename std::vector<Arg>::const_iterator itBegin = m_options.begin(), itEnd = m_options.end(), it;
+            std::size_t maxWidth = 0;
+            for( it = itBegin; it != itEnd; ++it )
+                maxWidth = (std::max)( maxWidth, it->commands().size() );
+
+            for( it = itBegin; it != itEnd; ++it ) {
+                Detail::Text usage( it->commands(), Detail::TextAttributes()
+                                                        .setWidth( maxWidth+indent )
+                                                        .setIndent( indent ) );
+                Detail::Text desc( it->description, Detail::TextAttributes()
+                                                        .setWidth( width - maxWidth - 3 ) );
+
+                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+                    std::string usageCol = i < usage.size() ? usage[i] : "";
+                    os << usageCol;
+
+                    if( i < desc.size() && !desc[i].empty() )
+                        os  << std::string( indent + 2 + maxWidth - usageCol.size(), ' ' )
+                            << desc[i];
+                    os << "\n";
+                }
+            }
+        }
+        std::string optUsage() const {
+            std::ostringstream oss;
+            optUsage( oss );
+            return oss.str();
+        }
+
+        void argSynopsis( std::ostream& os ) const {
+            for( int i = 1; i <= m_highestSpecifiedArgPosition; ++i ) {
+                if( i > 1 )
+                    os << " ";
+                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( i );
+                if( it != m_positionalArgs.end() )
+                    os << "<" << it->second.placeholder << ">";
+                else if( m_floatingArg.get() )
+                    os << "<" << m_floatingArg->placeholder << ">";
+                else
+                    throw std::logic_error( "non consecutive positional arguments with no floating args" );
+            }
+            // !TBD No indication of mandatory args
+            if( m_floatingArg.get() ) {
+                if( m_highestSpecifiedArgPosition > 1 )
+                    os << " ";
+                os << "[<" << m_floatingArg->placeholder << "> ...]";
+            }
+        }
+        std::string argSynopsis() const {
+            std::ostringstream oss;
+            argSynopsis( oss );
+            return oss.str();
+        }
+
+        void usage( std::ostream& os, std::string const& procName ) const {
+            validate();
+            os << "usage:\n  " << procName << " ";
+            argSynopsis( os );
+            if( !m_options.empty() ) {
+                os << " [options]\n\nwhere options are: \n";
+                optUsage( os, 2 );
+            }
+            os << "\n";
+        }
+        std::string usage( std::string const& procName ) const {
+            std::ostringstream oss;
+            usage( oss, procName );
+            return oss.str();
+        }
+
+        ConfigT parse( std::vector<std::string> const& args ) const {
+            ConfigT config;
+            parseInto( args, config );
+            return config;
+        }
+
+        std::vector<Parser::Token> parseInto( std::vector<std::string> const& args, ConfigT& config ) const {
+            std::string processName = args.empty() ? std::string() : args[0];
+            std::size_t lastSlash = processName.find_last_of( "/\\" );
+            if( lastSlash != std::string::npos )
+                processName = processName.substr( lastSlash+1 );
+            m_boundProcessName.set( config, processName );
+            std::vector<Parser::Token> tokens;
+            Parser parser;
+            parser.parseIntoTokens( args, tokens );
+            return populate( tokens, config );
+        }
+
+        std::vector<Parser::Token> populate( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            validate();
+            std::vector<Parser::Token> unusedTokens = populateOptions( tokens, config );
+            unusedTokens = populateFixedArgs( unusedTokens, config );
+            unusedTokens = populateFloatingArgs( unusedTokens, config );
+            return unusedTokens;
+        }
+
+        std::vector<Parser::Token> populateOptions( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            std::vector<Parser::Token> unusedTokens;
+            std::vector<std::string> errors;
+            for( std::size_t i = 0; i < tokens.size(); ++i ) {
+                Parser::Token const& token = tokens[i];
+                typename std::vector<Arg>::const_iterator it = m_options.begin(), itEnd = m_options.end();
+                for(; it != itEnd; ++it ) {
+                    Arg const& arg = *it;
+
+                    try {
+                        if( ( token.type == Parser::Token::ShortOpt && arg.hasShortName( token.data ) ) ||
+                            ( token.type == Parser::Token::LongOpt && arg.hasLongName( token.data ) ) ) {
+                            if( arg.takesArg() ) {
+                                if( i == tokens.size()-1 || tokens[i+1].type != Parser::Token::Positional )
+                                    errors.push_back( "Expected argument to option: " + token.data );
+                                else
+                                    arg.boundField.set( config, tokens[++i].data );
+                            }
+                            else {
+                                arg.boundField.set( config, "true" );
+                            }
+                            break;
+                        }
+                    }
+                    catch( std::exception& ex ) {
+                        errors.push_back( std::string( ex.what() ) + "\n- while parsing: (" + arg.commands() + ")" );
+                    }
+                }
+                if( it == itEnd ) {
+                    if( token.type == Parser::Token::Positional || !m_throwOnUnrecognisedTokens )
+                        unusedTokens.push_back( token );
+                    else if( errors.empty() && m_throwOnUnrecognisedTokens )
+                        errors.push_back( "unrecognised option: " + token.data );
+                }
+            }
+            if( !errors.empty() ) {
+                std::ostringstream oss;
+                for( std::vector<std::string>::const_iterator it = errors.begin(), itEnd = errors.end();
+                        it != itEnd;
+                        ++it ) {
+                    if( it != errors.begin() )
+                        oss << "\n";
+                    oss << *it;
+                }
+                throw std::runtime_error( oss.str() );
+            }
+            return unusedTokens;
+        }
+        std::vector<Parser::Token> populateFixedArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            std::vector<Parser::Token> unusedTokens;
+            int position = 1;
+            for( std::size_t i = 0; i < tokens.size(); ++i ) {
+                Parser::Token const& token = tokens[i];
+                typename std::map<int, Arg>::const_iterator it = m_positionalArgs.find( position );
+                if( it != m_positionalArgs.end() )
+                    it->second.boundField.set( config, token.data );
+                else
+                    unusedTokens.push_back( token );
+                if( token.type == Parser::Token::Positional )
+                    position++;
+            }
+            return unusedTokens;
+        }
+        std::vector<Parser::Token> populateFloatingArgs( std::vector<Parser::Token> const& tokens, ConfigT& config ) const {
+            if( !m_floatingArg.get() )
+                return tokens;
+            std::vector<Parser::Token> unusedTokens;
+            for( std::size_t i = 0; i < tokens.size(); ++i ) {
+                Parser::Token const& token = tokens[i];
+                if( token.type == Parser::Token::Positional )
+                    m_floatingArg->boundField.set( config, token.data );
+                else
+                    unusedTokens.push_back( token );
+            }
+            return unusedTokens;
+        }
+
+        void validate() const
+        {
+            if( m_options.empty() && m_positionalArgs.empty() && !m_floatingArg.get() )
+                throw std::logic_error( "No options or arguments specified" );
+
+            for( typename std::vector<Arg>::const_iterator  it = m_options.begin(),
+                                                            itEnd = m_options.end();
+                    it != itEnd; ++it )
+                it->validate();
+        }
+
+    private:
+        Detail::BoundArgFunction<ConfigT> m_boundProcessName;
+        std::vector<Arg> m_options;
+        std::map<int, Arg> m_positionalArgs;
+        ArgAutoPtr m_floatingArg;
+        int m_highestSpecifiedArgPosition;
+        bool m_throwOnUnrecognisedTokens;
+    };
+
+} // end namespace Clara
+
+STITCH_CLARA_CLOSE_NAMESPACE
+#undef STITCH_CLARA_OPEN_NAMESPACE
+#undef STITCH_CLARA_CLOSE_NAMESPACE
+
+#endif // TWOBLUECUBES_CLARA_H_INCLUDED
+#undef STITCH_CLARA_OPEN_NAMESPACE
+
+// Restore Clara's value for console width, if present
+#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#define CLARA_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
+#endif
+
+#include <fstream>
+#include <ctime>
+
+namespace Catch {
+
+    inline void abortAfterFirst( ConfigData& config ) { config.abortAfter = 1; }
+    inline void abortAfterX( ConfigData& config, int x ) {
+        if( x < 1 )
+            throw std::runtime_error( "Value after -x or --abortAfter must be greater than zero" );
+        config.abortAfter = x;
+    }
+    inline void addTestOrTags( ConfigData& config, std::string const& _testSpec ) { config.testsOrTags.push_back( _testSpec ); }
+    inline void addSectionToRun( ConfigData& config, std::string const& sectionName ) { config.sectionsToRun.push_back( sectionName ); }
+    inline void addReporterName( ConfigData& config, std::string const& _reporterName ) { config.reporterNames.push_back( _reporterName ); }
+
+    inline void addWarning( ConfigData& config, std::string const& _warning ) {
+        if( _warning == "NoAssertions" )
+            config.warnings = static_cast<WarnAbout::What>( config.warnings | WarnAbout::NoAssertions );
+        else
+            throw std::runtime_error( "Unrecognised warning: '" + _warning + '\'' );
+    }
+    inline void setOrder( ConfigData& config, std::string const& order ) {
+        if( startsWith( "declared", order ) )
+            config.runOrder = RunTests::InDeclarationOrder;
+        else if( startsWith( "lexical", order ) )
+            config.runOrder = RunTests::InLexicographicalOrder;
+        else if( startsWith( "random", order ) )
+            config.runOrder = RunTests::InRandomOrder;
+        else
+            throw std::runtime_error( "Unrecognised ordering: '" + order + '\'' );
+    }
+    inline void setRngSeed( ConfigData& config, std::string const& seed ) {
+        if( seed == "time" ) {
+            config.rngSeed = static_cast<unsigned int>( std::time(0) );
+        }
+        else {
+            std::stringstream ss;
+            ss << seed;
+            ss >> config.rngSeed;
+            if( ss.fail() )
+                throw std::runtime_error( "Argument to --rng-seed should be the word 'time' or a number" );
+        }
+    }
+    inline void setVerbosity( ConfigData& config, int level ) {
+        // !TBD: accept strings?
+        config.verbosity = static_cast<Verbosity::Level>( level );
+    }
+    inline void setShowDurations( ConfigData& config, bool _showDurations ) {
+        config.showDurations = _showDurations
+            ? ShowDurations::Always
+            : ShowDurations::Never;
+    }
+    inline void setUseColour( ConfigData& config, std::string const& value ) {
+        std::string mode = toLower( value );
+
+        if( mode == "yes" )
+            config.useColour = UseColour::Yes;
+        else if( mode == "no" )
+            config.useColour = UseColour::No;
+        else if( mode == "auto" )
+            config.useColour = UseColour::Auto;
+        else
+            throw std::runtime_error( "colour mode must be one of: auto, yes or no" );
+    }
+    inline void forceColour( ConfigData& config ) {
+        config.useColour = UseColour::Yes;
+    }
+    inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
+        std::ifstream f( _filename.c_str() );
+        if( !f.is_open() )
+            throw std::domain_error( "Unable to load input file: " + _filename );
+
+        std::string line;
+        while( std::getline( f, line ) ) {
+            line = trim(line);
+            if( !line.empty() && !startsWith( line, '#' ) ) {
+                if( !startsWith( line, '"' ) )
+                    line = '"' + line + '"';
+                addTestOrTags( config, line + ',' );
+            }
+        }
+    }
+
+    inline Clara::CommandLine<ConfigData> makeCommandLineParser() {
+
+        using namespace Clara;
+        CommandLine<ConfigData> cli;
+
+        cli.bindProcessName( &ConfigData::processName );
+
+        cli["-?"]["-h"]["--help"]
+            .describe( "display usage information" )
+            .bind( &ConfigData::showHelp );
+
+        cli["-l"]["--list-tests"]
+            .describe( "list all/matching test cases" )
+            .bind( &ConfigData::listTests );
+
+        cli["-t"]["--list-tags"]
+            .describe( "list all/matching tags" )
+            .bind( &ConfigData::listTags );
+
+        cli["-s"]["--success"]
+            .describe( "include successful tests in output" )
+            .bind( &ConfigData::showSuccessfulTests );
+
+        cli["-b"]["--break"]
+            .describe( "break into debugger on failure" )
+            .bind( &ConfigData::shouldDebugBreak );
+
+        cli["-e"]["--nothrow"]
+            .describe( "skip exception tests" )
+            .bind( &ConfigData::noThrow );
+
+        cli["-i"]["--invisibles"]
+            .describe( "show invisibles (tabs, newlines)" )
+            .bind( &ConfigData::showInvisibles );
+
+        cli["-o"]["--out"]
+            .describe( "output filename" )
+            .bind( &ConfigData::outputFilename, "filename" );
+
+        cli["-r"]["--reporter"]
+//            .placeholder( "name[:filename]" )
+            .describe( "reporter to use (defaults to console)" )
+            .bind( &addReporterName, "name" );
+
+        cli["-n"]["--name"]
+            .describe( "suite name" )
+            .bind( &ConfigData::name, "name" );
+
+        cli["-a"]["--abort"]
+            .describe( "abort at first failure" )
+            .bind( &abortAfterFirst );
+
+        cli["-x"]["--abortx"]
+            .describe( "abort after x failures" )
+            .bind( &abortAfterX, "no. failures" );
+
+        cli["-w"]["--warn"]
+            .describe( "enable warnings" )
+            .bind( &addWarning, "warning name" );
+
+// - needs updating if reinstated
+//        cli.into( &setVerbosity )
+//            .describe( "level of verbosity (0=no output)" )
+//            .shortOpt( "v")
+//            .longOpt( "verbosity" )
+//            .placeholder( "level" );
+
+        cli[_]
+            .describe( "which test or tests to use" )
+            .bind( &addTestOrTags, "test name, pattern or tags" );
+
+        cli["-d"]["--durations"]
+            .describe( "show test durations" )
+            .bind( &setShowDurations, "yes|no" );
+
+        cli["-f"]["--input-file"]
+            .describe( "load test names to run from a file" )
+            .bind( &loadTestNamesFromFile, "filename" );
+
+        cli["-#"]["--filenames-as-tags"]
+            .describe( "adds a tag for the filename" )
+            .bind( &ConfigData::filenamesAsTags );
+
+        cli["-c"]["--section"]
+                .describe( "specify section to run" )
+                .bind( &addSectionToRun, "section name" );
+
+        // Less common commands which don't have a short form
+        cli["--list-test-names-only"]
+            .describe( "list all/matching test cases names only" )
+            .bind( &ConfigData::listTestNamesOnly );
+
+        cli["--list-extra-info"]
+            .describe( "list all/matching test cases with more info" )
+            .bind( &ConfigData::listExtraInfo );
+
+        cli["--list-reporters"]
+            .describe( "list all reporters" )
+            .bind( &ConfigData::listReporters );
+
+        cli["--order"]
+            .describe( "test case order (defaults to decl)" )
+            .bind( &setOrder, "decl|lex|rand" );
+
+        cli["--rng-seed"]
+            .describe( "set a specific seed for random numbers" )
+            .bind( &setRngSeed, "'time'|number" );
+
+        cli["--force-colour"]
+            .describe( "force colourised output (deprecated)" )
+            .bind( &forceColour );
+
+        cli["--use-colour"]
+            .describe( "should output be colourised" )
+            .bind( &setUseColour, "yes|no" );
+
+        return cli;
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_list.hpp
+#define TWOBLUECUBES_CATCH_LIST_HPP_INCLUDED
+
+// #included from: catch_text.h
+#define TWOBLUECUBES_CATCH_TEXT_H_INCLUDED
+
+#define TBC_TEXT_FORMAT_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH
+
+#define CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE Catch
+// #included from: ../external/tbc_text_format.h
+// Only use header guard if we are not using an outer namespace
+#ifndef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+# ifdef TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+#  ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#   define TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#  endif
+# else
+#  define TWOBLUECUBES_TEXT_FORMAT_H_INCLUDED
+# endif
+#endif
+#ifndef TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#include <string>
+#include <vector>
+#include <sstream>
+
+// Use optional outer namespace
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+namespace CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE {
+#endif
+
+namespace Tbc {
+
+#ifdef TBC_TEXT_FORMAT_CONSOLE_WIDTH
+    const unsigned int consoleWidth = TBC_TEXT_FORMAT_CONSOLE_WIDTH;
+#else
+    const unsigned int consoleWidth = 80;
+#endif
+
+    struct TextAttributes {
+        TextAttributes()
+        :   initialIndent( std::string::npos ),
+            indent( 0 ),
+            width( consoleWidth-1 )
+        {}
+
+        TextAttributes& setInitialIndent( std::size_t _value )  { initialIndent = _value; return *this; }
+        TextAttributes& setIndent( std::size_t _value )         { indent = _value; return *this; }
+        TextAttributes& setWidth( std::size_t _value )          { width = _value; return *this; }
+
+        std::size_t initialIndent;  // indent of first line, or npos
+        std::size_t indent;         // indent of subsequent lines, or all if initialIndent is npos
+        std::size_t width;          // maximum width of text, including indent. Longer text will wrap
+    };
+
+    class Text {
+    public:
+        Text( std::string const& _str, TextAttributes const& _attr = TextAttributes() )
+        : attr( _attr )
+        {
+            const std::string wrappableBeforeChars = "[({<\t";
+            const std::string wrappableAfterChars = "])}>-,./|\\";
+            const std::string wrappableInsteadOfChars = " \n\r";
+            std::string indent = _attr.initialIndent != std::string::npos
+                ? std::string( _attr.initialIndent, ' ' )
+                : std::string( _attr.indent, ' ' );
+
+            typedef std::string::const_iterator iterator;
+            iterator it = _str.begin();
+            const iterator strEnd = _str.end();
+
+            while( it != strEnd ) {
+
+                if( lines.size() >= 1000 ) {
+                    lines.push_back( "... message truncated due to excessive size" );
+                    return;
+                }
+
+                std::string suffix;
+                std::size_t width = (std::min)( static_cast<size_t>( strEnd-it ), _attr.width-static_cast<size_t>( indent.size() ) );
+                iterator itEnd = it+width;
+                iterator itNext = _str.end();
+
+                iterator itNewLine = std::find( it, itEnd, '\n' );
+                if( itNewLine != itEnd )
+                    itEnd = itNewLine;
+
+                if( itEnd != strEnd  ) {
+                    bool foundWrapPoint = false;
+                    iterator findIt = itEnd;
+                    do {
+                        if( wrappableAfterChars.find( *findIt ) != std::string::npos && findIt != itEnd ) {
+                            itEnd = findIt+1;
+                            itNext = findIt+1;
+                            foundWrapPoint = true;
+                        }
+                        else if( findIt > it && wrappableBeforeChars.find( *findIt ) != std::string::npos ) {
+                            itEnd = findIt;
+                            itNext = findIt;
+                            foundWrapPoint = true;
+                        }
+                        else if( wrappableInsteadOfChars.find( *findIt ) != std::string::npos ) {
+                            itNext = findIt+1;
+                            itEnd = findIt;
+                            foundWrapPoint = true;
+                        }
+                        if( findIt == it )
+                            break;
+                        else
+                            --findIt;
+                    }
+                    while( !foundWrapPoint );
+
+                    if( !foundWrapPoint ) {
+                        // No good wrap char, so we'll break mid word and add a hyphen
+                        --itEnd;
+                        itNext = itEnd;
+                        suffix = "-";
+                    }
+                    else {
+                        while( itEnd > it && wrappableInsteadOfChars.find( *(itEnd-1) ) != std::string::npos )
+                            --itEnd;
+                    }
+                }
+                lines.push_back( indent + std::string( it, itEnd ) + suffix );
+
+                if( indent.size() != _attr.indent )
+                    indent = std::string( _attr.indent, ' ' );
+                it = itNext;
+            }
+        }
+
+        typedef std::vector<std::string>::const_iterator const_iterator;
+
+        const_iterator begin() const { return lines.begin(); }
+        const_iterator end() const { return lines.end(); }
+        std::string const& last() const { return lines.back(); }
+        std::size_t size() const { return lines.size(); }
+        std::string const& operator[]( std::size_t _index ) const { return lines[_index]; }
+        std::string toString() const {
+            std::ostringstream oss;
+            oss << *this;
+            return oss.str();
+        }
+
+        inline friend std::ostream& operator << ( std::ostream& _stream, Text const& _text ) {
+            for( Text::const_iterator it = _text.begin(), itEnd = _text.end();
+                it != itEnd; ++it ) {
+                if( it != _text.begin() )
+                    _stream << "\n";
+                _stream << *it;
+            }
+            return _stream;
+        }
+
+    private:
+        std::string str;
+        TextAttributes attr;
+        std::vector<std::string> lines;
+    };
+
+} // end namespace Tbc
+
+#ifdef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+} // end outer namespace
+#endif
+
+#endif // TWOBLUECUBES_TEXT_FORMAT_H_ALREADY_INCLUDED
+#undef CLICHE_TBC_TEXT_FORMAT_OUTER_NAMESPACE
+
+namespace Catch {
+    using Tbc::Text;
+    using Tbc::TextAttributes;
+}
+
+// #included from: catch_console_colour.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_HPP_INCLUDED
+
+namespace Catch {
+
+    struct Colour {
+        enum Code {
+            None = 0,
+
+            White,
+            Red,
+            Green,
+            Blue,
+            Cyan,
+            Yellow,
+            Grey,
+
+            Bright = 0x10,
+
+            BrightRed = Bright | Red,
+            BrightGreen = Bright | Green,
+            LightGrey = Bright | Grey,
+            BrightWhite = Bright | White,
+
+            // By intention
+            FileName = LightGrey,
+            Warning = Yellow,
+            ResultError = BrightRed,
+            ResultSuccess = BrightGreen,
+            ResultExpectedFailure = Warning,
+
+            Error = BrightRed,
+            Success = Green,
+
+            OriginalExpression = Cyan,
+            ReconstructedExpression = Yellow,
+
+            SecondaryText = LightGrey,
+            Headers = White
+        };
+
+        // Use constructed object for RAII guard
+        Colour( Code _colourCode );
+        Colour( Colour const& other );
+        ~Colour();
+
+        // Use static method for one-shot changes
+        static void use( Code _colourCode );
+
+    private:
+        bool m_moved;
+    };
+
+    inline std::ostream& operator << ( std::ostream& os, Colour const& ) { return os; }
+
+} // end namespace Catch
+
+// #included from: catch_interfaces_reporter.h
+#define TWOBLUECUBES_CATCH_INTERFACES_REPORTER_H_INCLUDED
+
+#include <string>
+#include <ostream>
+#include <map>
+
+namespace Catch
+{
+    struct ReporterConfig {
+        explicit ReporterConfig( Ptr<IConfig const> const& _fullConfig )
+        :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
+
+        ReporterConfig( Ptr<IConfig const> const& _fullConfig, std::ostream& _stream )
+        :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
+
+        std::ostream& stream() const    { return *m_stream; }
+        Ptr<IConfig const> fullConfig() const { return m_fullConfig; }
+
+    private:
+        std::ostream* m_stream;
+        Ptr<IConfig const> m_fullConfig;
+    };
+
+    struct ReporterPreferences {
+        ReporterPreferences()
+        : shouldRedirectStdOut( false )
+        {}
+
+        bool shouldRedirectStdOut;
+    };
+
+    template<typename T>
+    struct LazyStat : Option<T> {
+        LazyStat() : used( false ) {}
+        LazyStat& operator=( T const& _value ) {
+            Option<T>::operator=( _value );
+            used = false;
+            return *this;
+        }
+        void reset() {
+            Option<T>::reset();
+            used = false;
+        }
+        bool used;
+    };
+
+    struct TestRunInfo {
+        TestRunInfo( std::string const& _name ) : name( _name ) {}
+        std::string name;
+    };
+    struct GroupInfo {
+        GroupInfo(  std::string const& _name,
+                    std::size_t _groupIndex,
+                    std::size_t _groupsCount )
+        :   name( _name ),
+            groupIndex( _groupIndex ),
+            groupsCounts( _groupsCount )
+        {}
+
+        std::string name;
+        std::size_t groupIndex;
+        std::size_t groupsCounts;
+    };
+
+    struct AssertionStats {
+        AssertionStats( AssertionResult const& _assertionResult,
+                        std::vector<MessageInfo> const& _infoMessages,
+                        Totals const& _totals )
+        :   assertionResult( _assertionResult ),
+            infoMessages( _infoMessages ),
+            totals( _totals )
+        {
+            if( assertionResult.hasMessage() ) {
+                // Copy message into messages list.
+                // !TBD This should have been done earlier, somewhere
+                MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
+                builder << assertionResult.getMessage();
+                builder.m_info.message = builder.m_stream.str();
+
+                infoMessages.push_back( builder.m_info );
+            }
+        }
+        virtual ~AssertionStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        AssertionStats( AssertionStats const& )              = default;
+        AssertionStats( AssertionStats && )                  = default;
+        AssertionStats& operator = ( AssertionStats const& ) = default;
+        AssertionStats& operator = ( AssertionStats && )     = default;
+#  endif
+
+        AssertionResult assertionResult;
+        std::vector<MessageInfo> infoMessages;
+        Totals totals;
+    };
+
+    struct SectionStats {
+        SectionStats(   SectionInfo const& _sectionInfo,
+                        Counts const& _assertions,
+                        double _durationInSeconds,
+                        bool _missingAssertions )
+        :   sectionInfo( _sectionInfo ),
+            assertions( _assertions ),
+            durationInSeconds( _durationInSeconds ),
+            missingAssertions( _missingAssertions )
+        {}
+        virtual ~SectionStats();
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        SectionStats( SectionStats const& )              = default;
+        SectionStats( SectionStats && )                  = default;
+        SectionStats& operator = ( SectionStats const& ) = default;
+        SectionStats& operator = ( SectionStats && )     = default;
+#  endif
+
+        SectionInfo sectionInfo;
+        Counts assertions;
+        double durationInSeconds;
+        bool missingAssertions;
+    };
+
+    struct TestCaseStats {
+        TestCaseStats(  TestCaseInfo const& _testInfo,
+                        Totals const& _totals,
+                        std::string const& _stdOut,
+                        std::string const& _stdErr,
+                        bool _aborting )
+        : testInfo( _testInfo ),
+            totals( _totals ),
+            stdOut( _stdOut ),
+            stdErr( _stdErr ),
+            aborting( _aborting )
+        {}
+        virtual ~TestCaseStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestCaseStats( TestCaseStats const& )              = default;
+        TestCaseStats( TestCaseStats && )                  = default;
+        TestCaseStats& operator = ( TestCaseStats const& ) = default;
+        TestCaseStats& operator = ( TestCaseStats && )     = default;
+#  endif
+
+        TestCaseInfo testInfo;
+        Totals totals;
+        std::string stdOut;
+        std::string stdErr;
+        bool aborting;
+    };
+
+    struct TestGroupStats {
+        TestGroupStats( GroupInfo const& _groupInfo,
+                        Totals const& _totals,
+                        bool _aborting )
+        :   groupInfo( _groupInfo ),
+            totals( _totals ),
+            aborting( _aborting )
+        {}
+        TestGroupStats( GroupInfo const& _groupInfo )
+        :   groupInfo( _groupInfo ),
+            aborting( false )
+        {}
+        virtual ~TestGroupStats();
+
+#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestGroupStats( TestGroupStats const& )              = default;
+        TestGroupStats( TestGroupStats && )                  = default;
+        TestGroupStats& operator = ( TestGroupStats const& ) = default;
+        TestGroupStats& operator = ( TestGroupStats && )     = default;
+#  endif
+
+        GroupInfo groupInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+    struct TestRunStats {
+        TestRunStats(   TestRunInfo const& _runInfo,
+                        Totals const& _totals,
+                        bool _aborting )
+        :   runInfo( _runInfo ),
+            totals( _totals ),
+            aborting( _aborting )
+        {}
+        virtual ~TestRunStats();
+
+#  ifndef CATCH_CONFIG_CPP11_GENERATED_METHODS
+        TestRunStats( TestRunStats const& _other )
+        :   runInfo( _other.runInfo ),
+            totals( _other.totals ),
+            aborting( _other.aborting )
+        {}
+#  else
+        TestRunStats( TestRunStats const& )              = default;
+        TestRunStats( TestRunStats && )                  = default;
+        TestRunStats& operator = ( TestRunStats const& ) = default;
+        TestRunStats& operator = ( TestRunStats && )     = default;
+#  endif
+
+        TestRunInfo runInfo;
+        Totals totals;
+        bool aborting;
+    };
+
+    class MultipleReporters;
+
+    struct IStreamingReporter : IShared {
+        virtual ~IStreamingReporter();
+
+        // Implementing class must also provide the following static method:
+        // static std::string getDescription();
+
+        virtual ReporterPreferences getPreferences() const = 0;
+
+        virtual void noMatchingTestCases( std::string const& spec ) = 0;
+
+        virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
+
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
+
+        virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
+
+        // The return value indicates if the messages buffer should be cleared:
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
+
+        virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
+        virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
+
+        virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
+
+        virtual MultipleReporters* tryAsMulti() { return CATCH_NULL; }
+    };
+
+    struct IReporterFactory : IShared {
+        virtual ~IReporterFactory();
+        virtual IStreamingReporter* create( ReporterConfig const& config ) const = 0;
+        virtual std::string getDescription() const = 0;
+    };
+
+    struct IReporterRegistry {
+        typedef std::map<std::string, Ptr<IReporterFactory> > FactoryMap;
+        typedef std::vector<Ptr<IReporterFactory> > Listeners;
+
+        virtual ~IReporterRegistry();
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const = 0;
+        virtual FactoryMap const& getFactories() const = 0;
+        virtual Listeners const& getListeners() const = 0;
+    };
+
+    Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter );
+
+}
+
+#include <limits>
+#include <algorithm>
+
+namespace Catch {
+
+    inline std::size_t listTests( Config const& config ) {
+
+        TestSpec testSpec = config.testSpec();
+        if( config.testSpec().hasFilters() )
+            Catch::cout() << "Matching test cases:\n";
+        else {
+            Catch::cout() << "All available test cases:\n";
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+        }
+
+        std::size_t matchedTests = 0;
+        TextAttributes nameAttr, descAttr, tagsAttr;
+        nameAttr.setInitialIndent( 2 ).setIndent( 4 );
+        descAttr.setIndent( 4 );
+        tagsAttr.setIndent( 6 );
+
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            matchedTests++;
+            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+            Colour::Code colour = testCaseInfo.isHidden()
+                ? Colour::SecondaryText
+                : Colour::None;
+            Colour colourGuard( colour );
+
+            Catch::cout() << Text( testCaseInfo.name, nameAttr ) << std::endl;
+            if( config.listExtraInfo() ) {
+                Catch::cout() << "    " << testCaseInfo.lineInfo << std::endl;
+                std::string description = testCaseInfo.description;
+                if( description.empty() )
+                    description = "(NO DESCRIPTION)";
+                Catch::cout() << Text( description, descAttr ) << std::endl;
+            }
+            if( !testCaseInfo.tags.empty() )
+                Catch::cout() << Text( testCaseInfo.tagsAsString, tagsAttr ) << std::endl;
+        }
+
+        if( !config.testSpec().hasFilters() )
+            Catch::cout() << pluralise( matchedTests, "test case" ) << '\n' << std::endl;
+        else
+            Catch::cout() << pluralise( matchedTests, "matching test case" ) << '\n' << std::endl;
+        return matchedTests;
+    }
+
+    inline std::size_t listTestsNamesOnly( Config const& config ) {
+        TestSpec testSpec = config.testSpec();
+        if( !config.testSpec().hasFilters() )
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+        std::size_t matchedTests = 0;
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            matchedTests++;
+            TestCaseInfo const& testCaseInfo = it->getTestCaseInfo();
+            if( startsWith( testCaseInfo.name, '#' ) )
+               Catch::cout() << '"' << testCaseInfo.name << '"';
+            else
+               Catch::cout() << testCaseInfo.name;
+            if ( config.listExtraInfo() )
+                Catch::cout() << "\t@" << testCaseInfo.lineInfo;
+            Catch::cout() << std::endl;
+        }
+        return matchedTests;
+    }
+
+    struct TagInfo {
+        TagInfo() : count ( 0 ) {}
+        void add( std::string const& spelling ) {
+            ++count;
+            spellings.insert( spelling );
+        }
+        std::string all() const {
+            std::string out;
+            for( std::set<std::string>::const_iterator it = spellings.begin(), itEnd = spellings.end();
+                        it != itEnd;
+                        ++it )
+                out += "[" + *it + "]";
+            return out;
+        }
+        std::set<std::string> spellings;
+        std::size_t count;
+    };
+
+    inline std::size_t listTags( Config const& config ) {
+        TestSpec testSpec = config.testSpec();
+        if( config.testSpec().hasFilters() )
+            Catch::cout() << "Tags for matching test cases:\n";
+        else {
+            Catch::cout() << "All available tags:\n";
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "*" ).testSpec();
+        }
+
+        std::map<std::string, TagInfo> tagCounts;
+
+        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
+        for( std::vector<TestCase>::const_iterator it = matchedTestCases.begin(), itEnd = matchedTestCases.end();
+                it != itEnd;
+                ++it ) {
+            for( std::set<std::string>::const_iterator  tagIt = it->getTestCaseInfo().tags.begin(),
+                                                        tagItEnd = it->getTestCaseInfo().tags.end();
+                    tagIt != tagItEnd;
+                    ++tagIt ) {
+                std::string tagName = *tagIt;
+                std::string lcaseTagName = toLower( tagName );
+                std::map<std::string, TagInfo>::iterator countIt = tagCounts.find( lcaseTagName );
+                if( countIt == tagCounts.end() )
+                    countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
+                countIt->second.add( tagName );
+            }
+        }
+
+        for( std::map<std::string, TagInfo>::const_iterator countIt = tagCounts.begin(),
+                                                            countItEnd = tagCounts.end();
+                countIt != countItEnd;
+                ++countIt ) {
+            std::ostringstream oss;
+            oss << "  " << std::setw(2) << countIt->second.count << "  ";
+            Text wrapper( countIt->second.all(), TextAttributes()
+                                                    .setInitialIndent( 0 )
+                                                    .setIndent( oss.str().size() )
+                                                    .setWidth( CATCH_CONFIG_CONSOLE_WIDTH-10 ) );
+            Catch::cout() << oss.str() << wrapper << '\n';
+        }
+        Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
+        return tagCounts.size();
+    }
+
+    inline std::size_t listReporters( Config const& /*config*/ ) {
+        Catch::cout() << "Available reporters:\n";
+        IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
+        IReporterRegistry::FactoryMap::const_iterator itBegin = factories.begin(), itEnd = factories.end(), it;
+        std::size_t maxNameLen = 0;
+        for(it = itBegin; it != itEnd; ++it )
+            maxNameLen = (std::max)( maxNameLen, it->first.size() );
+
+        for(it = itBegin; it != itEnd; ++it ) {
+            Text wrapper( it->second->getDescription(), TextAttributes()
+                                                        .setInitialIndent( 0 )
+                                                        .setIndent( 7+maxNameLen )
+                                                        .setWidth( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 ) );
+            Catch::cout() << "  "
+                    << it->first
+                    << ':'
+                    << std::string( maxNameLen - it->first.size() + 2, ' ' )
+                    << wrapper << '\n';
+        }
+        Catch::cout() << std::endl;
+        return factories.size();
+    }
+
+    inline Option<std::size_t> list( Config const& config ) {
+        Option<std::size_t> listedCount;
+        if( config.listTests() || ( config.listExtraInfo() && !config.listTestNamesOnly() ) )
+            listedCount = listedCount.valueOr(0) + listTests( config );
+        if( config.listTestNamesOnly() )
+            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( config );
+        if( config.listTags() )
+            listedCount = listedCount.valueOr(0) + listTags( config );
+        if( config.listReporters() )
+            listedCount = listedCount.valueOr(0) + listReporters( config );
+        return listedCount;
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_run_context.hpp
+#define TWOBLUECUBES_CATCH_RUNNER_IMPL_HPP_INCLUDED
+
+// #included from: catch_test_case_tracker.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_TRACKER_HPP_INCLUDED
+
+#include <algorithm>
+#include <string>
+#include <assert.h>
+#include <vector>
+#include <stdexcept>
+
+CATCH_INTERNAL_SUPPRESS_ETD_WARNINGS
+
+namespace Catch {
+namespace TestCaseTracking {
+
+    struct NameAndLocation {
+        std::string name;
+        SourceLineInfo location;
+
+        NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
+        :   name( _name ),
+            location( _location )
+        {}
+    };
+
+    struct ITracker : SharedImpl<> {
+        virtual ~ITracker();
+
+        // static queries
+        virtual NameAndLocation const& nameAndLocation() const = 0;
+
+        // dynamic queries
+        virtual bool isComplete() const = 0; // Successfully completed or failed
+        virtual bool isSuccessfullyCompleted() const = 0;
+        virtual bool isOpen() const = 0; // Started but not complete
+        virtual bool hasChildren() const = 0;
+
+        virtual ITracker& parent() = 0;
+
+        // actions
+        virtual void close() = 0; // Successfully complete
+        virtual void fail() = 0;
+        virtual void markAsNeedingAnotherRun() = 0;
+
+        virtual void addChild( Ptr<ITracker> const& child ) = 0;
+        virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) = 0;
+        virtual void openChild() = 0;
+
+        // Debug/ checking
+        virtual bool isSectionTracker() const = 0;
+        virtual bool isIndexTracker() const = 0;
+    };
+
+    class  TrackerContext {
+
+        enum RunState {
+            NotStarted,
+            Executing,
+            CompletedCycle
+        };
+
+        Ptr<ITracker> m_rootTracker;
+        ITracker* m_currentTracker;
+        RunState m_runState;
+
+    public:
+
+        static TrackerContext& instance() {
+            static TrackerContext s_instance;
+            return s_instance;
+        }
+
+        TrackerContext()
+        :   m_currentTracker( CATCH_NULL ),
+            m_runState( NotStarted )
+        {}
+
+        ITracker& startRun();
+
+        void endRun() {
+            m_rootTracker.reset();
+            m_currentTracker = CATCH_NULL;
+            m_runState = NotStarted;
+        }
+
+        void startCycle() {
+            m_currentTracker = m_rootTracker.get();
+            m_runState = Executing;
+        }
+        void completeCycle() {
+            m_runState = CompletedCycle;
+        }
+
+        bool completedCycle() const {
+            return m_runState == CompletedCycle;
+        }
+        ITracker& currentTracker() {
+            return *m_currentTracker;
+        }
+        void setCurrentTracker( ITracker* tracker ) {
+            m_currentTracker = tracker;
+        }
+    };
+
+    class TrackerBase : public ITracker {
+    protected:
+        enum CycleState {
+            NotStarted,
+            Executing,
+            ExecutingChildren,
+            NeedsAnotherRun,
+            CompletedSuccessfully,
+            Failed
+        };
+        class TrackerHasName {
+            NameAndLocation m_nameAndLocation;
+        public:
+            TrackerHasName( NameAndLocation const& nameAndLocation ) : m_nameAndLocation( nameAndLocation ) {}
+            bool operator ()( Ptr<ITracker> const& tracker ) {
+                return
+                    tracker->nameAndLocation().name == m_nameAndLocation.name &&
+                    tracker->nameAndLocation().location == m_nameAndLocation.location;
+            }
+        };
+        typedef std::vector<Ptr<ITracker> > Children;
+        NameAndLocation m_nameAndLocation;
+        TrackerContext& m_ctx;
+        ITracker* m_parent;
+        Children m_children;
+        CycleState m_runState;
+    public:
+        TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+        :   m_nameAndLocation( nameAndLocation ),
+            m_ctx( ctx ),
+            m_parent( parent ),
+            m_runState( NotStarted )
+        {}
+        virtual ~TrackerBase();
+
+        virtual NameAndLocation const& nameAndLocation() const CATCH_OVERRIDE {
+            return m_nameAndLocation;
+        }
+        virtual bool isComplete() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully || m_runState == Failed;
+        }
+        virtual bool isSuccessfullyCompleted() const CATCH_OVERRIDE {
+            return m_runState == CompletedSuccessfully;
+        }
+        virtual bool isOpen() const CATCH_OVERRIDE {
+            return m_runState != NotStarted && !isComplete();
+        }
+        virtual bool hasChildren() const CATCH_OVERRIDE {
+            return !m_children.empty();
+        }
+
+        virtual void addChild( Ptr<ITracker> const& child ) CATCH_OVERRIDE {
+            m_children.push_back( child );
+        }
+
+        virtual ITracker* findChild( NameAndLocation const& nameAndLocation ) CATCH_OVERRIDE {
+            Children::const_iterator it = std::find_if( m_children.begin(), m_children.end(), TrackerHasName( nameAndLocation ) );
+            return( it != m_children.end() )
+                ? it->get()
+                : CATCH_NULL;
+        }
+        virtual ITracker& parent() CATCH_OVERRIDE {
+            assert( m_parent ); // Should always be non-null except for root
+            return *m_parent;
+        }
+
+        virtual void openChild() CATCH_OVERRIDE {
+            if( m_runState != ExecutingChildren ) {
+                m_runState = ExecutingChildren;
+                if( m_parent )
+                    m_parent->openChild();
+            }
+        }
+
+        virtual bool isSectionTracker() const CATCH_OVERRIDE { return false; }
+        virtual bool isIndexTracker() const CATCH_OVERRIDE { return false; }
+
+        void open() {
+            m_runState = Executing;
+            moveToThis();
+            if( m_parent )
+                m_parent->openChild();
+        }
+
+        virtual void close() CATCH_OVERRIDE {
+
+            // Close any still open children (e.g. generators)
+            while( &m_ctx.currentTracker() != this )
+                m_ctx.currentTracker().close();
+
+            switch( m_runState ) {
+                case NotStarted:
+                case CompletedSuccessfully:
+                case Failed:
+                    throw std::logic_error( "Illogical state" );
+
+                case NeedsAnotherRun:
+                    break;;
+
+                case Executing:
+                    m_runState = CompletedSuccessfully;
+                    break;
+                case ExecutingChildren:
+                    if( m_children.empty() || m_children.back()->isComplete() )
+                        m_runState = CompletedSuccessfully;
+                    break;
+
+                default:
+                    throw std::logic_error( "Unexpected state" );
+            }
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void fail() CATCH_OVERRIDE {
+            m_runState = Failed;
+            if( m_parent )
+                m_parent->markAsNeedingAnotherRun();
+            moveToParent();
+            m_ctx.completeCycle();
+        }
+        virtual void markAsNeedingAnotherRun() CATCH_OVERRIDE {
+            m_runState = NeedsAnotherRun;
+        }
+    private:
+        void moveToParent() {
+            assert( m_parent );
+            m_ctx.setCurrentTracker( m_parent );
+        }
+        void moveToThis() {
+            m_ctx.setCurrentTracker( this );
+        }
+    };
+
+    class SectionTracker : public TrackerBase {
+        std::vector<std::string> m_filters;
+    public:
+        SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
+        :   TrackerBase( nameAndLocation, ctx, parent )
+        {
+            if( parent ) {
+                while( !parent->isSectionTracker() )
+                    parent = &parent->parent();
+
+                SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
+                addNextFilters( parentSection.m_filters );
+            }
+        }
+        virtual ~SectionTracker();
+
+        virtual bool isSectionTracker() const CATCH_OVERRIDE { return true; }
+
+        static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
+            SectionTracker* section = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+                assert( childTracker );
+                assert( childTracker->isSectionTracker() );
+                section = static_cast<SectionTracker*>( childTracker );
+            }
+            else {
+                section = new SectionTracker( nameAndLocation, ctx, &currentTracker );
+                currentTracker.addChild( section );
+            }
+            if( !ctx.completedCycle() )
+                section->tryOpen();
+            return *section;
+        }
+
+        void tryOpen() {
+            if( !isComplete() && (m_filters.empty() || m_filters[0].empty() ||  m_filters[0] == m_nameAndLocation.name ) )
+                open();
+        }
+
+        void addInitialFilters( std::vector<std::string> const& filters ) {
+            if( !filters.empty() ) {
+                m_filters.push_back(""); // Root - should never be consulted
+                m_filters.push_back(""); // Test Case - not a section filter
+                m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
+            }
+        }
+        void addNextFilters( std::vector<std::string> const& filters ) {
+            if( filters.size() > 1 )
+                m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
+        }
+    };
+
+    class IndexTracker : public TrackerBase {
+        int m_size;
+        int m_index;
+    public:
+        IndexTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent, int size )
+        :   TrackerBase( nameAndLocation, ctx, parent ),
+            m_size( size ),
+            m_index( -1 )
+        {}
+        virtual ~IndexTracker();
+
+        virtual bool isIndexTracker() const CATCH_OVERRIDE { return true; }
+
+        static IndexTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation, int size ) {
+            IndexTracker* tracker = CATCH_NULL;
+
+            ITracker& currentTracker = ctx.currentTracker();
+            if( ITracker* childTracker = currentTracker.findChild( nameAndLocation ) ) {
+                assert( childTracker );
+                assert( childTracker->isIndexTracker() );
+                tracker = static_cast<IndexTracker*>( childTracker );
+            }
+            else {
+                tracker = new IndexTracker( nameAndLocation, ctx, &currentTracker, size );
+                currentTracker.addChild( tracker );
+            }
+
+            if( !ctx.completedCycle() && !tracker->isComplete() ) {
+                if( tracker->m_runState != ExecutingChildren && tracker->m_runState != NeedsAnotherRun )
+                    tracker->moveNext();
+                tracker->open();
+            }
+
+            return *tracker;
+        }
+
+        int index() const { return m_index; }
+
+        void moveNext() {
+            m_index++;
+            m_children.clear();
+        }
+
+        virtual void close() CATCH_OVERRIDE {
+            TrackerBase::close();
+            if( m_runState == CompletedSuccessfully && m_index < m_size-1 )
+                m_runState = Executing;
+        }
+    };
+
+    inline ITracker& TrackerContext::startRun() {
+        m_rootTracker = new SectionTracker( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, CATCH_NULL );
+        m_currentTracker = CATCH_NULL;
+        m_runState = Executing;
+        return *m_rootTracker;
+    }
+
+} // namespace TestCaseTracking
+
+using TestCaseTracking::ITracker;
+using TestCaseTracking::TrackerContext;
+using TestCaseTracking::SectionTracker;
+using TestCaseTracking::IndexTracker;
+
+} // namespace Catch
+
+CATCH_INTERNAL_UNSUPPRESS_ETD_WARNINGS
+
+// #included from: catch_fatal_condition.hpp
+#define TWOBLUECUBES_CATCH_FATAL_CONDITION_H_INCLUDED
+
+namespace Catch {
+
+    // Report the error condition
+    inline void reportFatal( std::string const& message ) {
+        IContext& context = Catch::getCurrentContext();
+        IResultCapture* resultCapture = context.getResultCapture();
+        resultCapture->handleFatalErrorCondition( message );
+    }
+
+} // namespace Catch
+
+#if defined ( CATCH_PLATFORM_WINDOWS ) /////////////////////////////////////////
+// #included from: catch_windows_h_proxy.h
+
+#define TWOBLUECUBES_CATCH_WINDOWS_H_PROXY_H_INCLUDED
+
+#ifdef CATCH_DEFINES_NOMINMAX
+#  define NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+#  define WIN32_LEAN_AND_MEAN
+#endif
+
+#ifdef __AFXDLL
+#include <AfxWin.h>
+#else
+#include <windows.h>
+#endif
+
+#ifdef CATCH_DEFINES_NOMINMAX
+#  undef NOMINMAX
+#endif
+#ifdef CATCH_DEFINES_WIN32_LEAN_AND_MEAN
+#  undef WIN32_LEAN_AND_MEAN
+#endif
+
+
+#  if !defined ( CATCH_CONFIG_WINDOWS_SEH )
+
+namespace Catch {
+    struct FatalConditionHandler {
+        void reset() {}
+    };
+}
+
+#  else // CATCH_CONFIG_WINDOWS_SEH is defined
+
+namespace Catch {
+
+    struct SignalDefs { DWORD id; const char* name; };
+    extern SignalDefs signalDefs[];
+    // There is no 1-1 mapping between signals and windows exceptions.
+    // Windows can easily distinguish between SO and SigSegV,
+    // but SigInt, SigTerm, etc are handled differently.
+    SignalDefs signalDefs[] = {
+        { EXCEPTION_ILLEGAL_INSTRUCTION,  "SIGILL - Illegal instruction signal" },
+        { EXCEPTION_STACK_OVERFLOW, "SIGSEGV - Stack overflow" },
+        { EXCEPTION_ACCESS_VIOLATION, "SIGSEGV - Segmentation violation signal" },
+        { EXCEPTION_INT_DIVIDE_BY_ZERO, "Divide by zero error" },
+    };
+
+    struct FatalConditionHandler {
+
+        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
+            for (int i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+                if (ExceptionInfo->ExceptionRecord->ExceptionCode == signalDefs[i].id) {
+                    reportFatal(signalDefs[i].name);
+                }
+            }
+            // If its not an exception we care about, pass it along.
+            // This stops us from eating debugger breaks etc.
+            return EXCEPTION_CONTINUE_SEARCH;
+        }
+
+        FatalConditionHandler() {
+            isSet = true;
+            // 32k seems enough for Catch to handle stack overflow,
+            // but the value was found experimentally, so there is no strong guarantee
+            guaranteeSize = 32 * 1024;
+            exceptionHandlerHandle = CATCH_NULL;
+            // Register as first handler in current chain
+            exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
+            // Pass in guarantee size to be filled
+            SetThreadStackGuarantee(&guaranteeSize);
+        }
+
+        static void reset() {
+            if (isSet) {
+                // Unregister handler and restore the old guarantee
+                RemoveVectoredExceptionHandler(exceptionHandlerHandle);
+                SetThreadStackGuarantee(&guaranteeSize);
+                exceptionHandlerHandle = CATCH_NULL;
+                isSet = false;
+            }
+        }
+
+        ~FatalConditionHandler() {
+            reset();
+        }
+    private:
+        static bool isSet;
+        static ULONG guaranteeSize;
+        static PVOID exceptionHandlerHandle;
+    };
+
+    bool FatalConditionHandler::isSet = false;
+    ULONG FatalConditionHandler::guaranteeSize = 0;
+    PVOID FatalConditionHandler::exceptionHandlerHandle = CATCH_NULL;
+
+} // namespace Catch
+
+#  endif // CATCH_CONFIG_WINDOWS_SEH
+
+#else // Not Windows - assumed to be POSIX compatible //////////////////////////
+
+#  if !defined(CATCH_CONFIG_POSIX_SIGNALS)
+
+namespace Catch {
+    struct FatalConditionHandler {
+        void reset() {}
+    };
+}
+
+#  else // CATCH_CONFIG_POSIX_SIGNALS is defined
+
+#include <signal.h>
+
+namespace Catch {
+
+    struct SignalDefs {
+        int id;
+        const char* name;
+    };
+    extern SignalDefs signalDefs[];
+    SignalDefs signalDefs[] = {
+            { SIGINT,  "SIGINT - Terminal interrupt signal" },
+            { SIGILL,  "SIGILL - Illegal instruction signal" },
+            { SIGFPE,  "SIGFPE - Floating point error signal" },
+            { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
+            { SIGTERM, "SIGTERM - Termination request signal" },
+            { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
+    };
+
+    struct FatalConditionHandler {
+
+        static bool isSet;
+        static struct sigaction oldSigActions [sizeof(signalDefs)/sizeof(SignalDefs)];
+        static stack_t oldSigStack;
+        static char altStackMem[SIGSTKSZ];
+
+        static void handleSignal( int sig ) {
+            std::string name = "<unknown signal>";
+            for (std::size_t i = 0; i < sizeof(signalDefs) / sizeof(SignalDefs); ++i) {
+                SignalDefs &def = signalDefs[i];
+                if (sig == def.id) {
+                    name = def.name;
+                    break;
+                }
+            }
+            reset();
+            reportFatal(name);
+            raise( sig );
+        }
+
+        FatalConditionHandler() {
+            isSet = true;
+            stack_t sigStack;
+            sigStack.ss_sp = altStackMem;
+            sigStack.ss_size = SIGSTKSZ;
+            sigStack.ss_flags = 0;
+            sigaltstack(&sigStack, &oldSigStack);
+            struct sigaction sa = { 0 };
+
+            sa.sa_handler = handleSignal;
+            sa.sa_flags = SA_ONSTACK;
+            for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
+                sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
+            }
+        }
+
+        ~FatalConditionHandler() {
+            reset();
+        }
+        static void reset() {
+            if( isSet ) {
+                // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
+                for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
+                    sigaction(signalDefs[i].id, &oldSigActions[i], CATCH_NULL);
+                }
+                // Return the old stack
+                sigaltstack(&oldSigStack, CATCH_NULL);
+                isSet = false;
+            }
+        }
+    };
+
+    bool FatalConditionHandler::isSet = false;
+    struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
+    stack_t FatalConditionHandler::oldSigStack = {};
+    char FatalConditionHandler::altStackMem[SIGSTKSZ] = {};
+
+} // namespace Catch
+
+#  endif // CATCH_CONFIG_POSIX_SIGNALS
+
+#endif // not Windows
+
+#include <set>
+#include <string>
+
+namespace Catch {
+
+    class StreamRedirect {
+
+    public:
+        StreamRedirect( std::ostream& stream, std::string& targetString )
+        :   m_stream( stream ),
+            m_prevBuf( stream.rdbuf() ),
+            m_targetString( targetString )
+        {
+            stream.rdbuf( m_oss.rdbuf() );
+        }
+
+        ~StreamRedirect() {
+            m_targetString += m_oss.str();
+            m_stream.rdbuf( m_prevBuf );
+        }
+
+    private:
+        std::ostream& m_stream;
+        std::streambuf* m_prevBuf;
+        std::ostringstream m_oss;
+        std::string& m_targetString;
+    };
+
+    // StdErr has two constituent streams in C++, std::cerr and std::clog
+    // This means that we need to redirect 2 streams into 1 to keep proper
+    // order of writes and cannot use StreamRedirect on its own
+    class StdErrRedirect {
+    public:
+        StdErrRedirect(std::string& targetString)
+        :m_cerrBuf( cerr().rdbuf() ), m_clogBuf(clog().rdbuf()),
+        m_targetString(targetString){
+            cerr().rdbuf(m_oss.rdbuf());
+            clog().rdbuf(m_oss.rdbuf());
+        }
+        ~StdErrRedirect() {
+            m_targetString += m_oss.str();
+            cerr().rdbuf(m_cerrBuf);
+            clog().rdbuf(m_clogBuf);
+        }
+    private:
+        std::streambuf* m_cerrBuf;
+        std::streambuf* m_clogBuf;
+        std::ostringstream m_oss;
+        std::string& m_targetString;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class RunContext : public IResultCapture, public IRunner {
+
+        RunContext( RunContext const& );
+        void operator =( RunContext const& );
+
+    public:
+
+        explicit RunContext( Ptr<IConfig const> const& _config, Ptr<IStreamingReporter> const& reporter )
+        :   m_runInfo( _config->name() ),
+            m_context( getCurrentMutableContext() ),
+            m_activeTestCase( CATCH_NULL ),
+            m_config( _config ),
+            m_reporter( reporter ),
+            m_shouldReportUnexpected ( true )
+        {
+            m_context.setRunner( this );
+            m_context.setConfig( m_config );
+            m_context.setResultCapture( this );
+            m_reporter->testRunStarting( m_runInfo );
+        }
+
+        virtual ~RunContext() {
+            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, aborting() ) );
+        }
+
+        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount ) {
+            m_reporter->testGroupStarting( GroupInfo( testSpec, groupIndex, groupsCount ) );
+        }
+        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount ) {
+            m_reporter->testGroupEnded( TestGroupStats( GroupInfo( testSpec, groupIndex, groupsCount ), totals, aborting() ) );
+        }
+
+        Totals runTest( TestCase const& testCase ) {
+            Totals prevTotals = m_totals;
+
+            std::string redirectedCout;
+            std::string redirectedCerr;
+
+            TestCaseInfo testInfo = testCase.getTestCaseInfo();
+
+            m_reporter->testCaseStarting( testInfo );
+
+            m_activeTestCase = &testCase;
+
+            do {
+                ITracker& rootTracker = m_trackerContext.startRun();
+                assert( rootTracker.isSectionTracker() );
+                static_cast<SectionTracker&>( rootTracker ).addInitialFilters( m_config->getSectionsToRun() );
+                do {
+                    m_trackerContext.startCycle();
+                    m_testCaseTracker = &SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( testInfo.name, testInfo.lineInfo ) );
+                    runCurrentTest( redirectedCout, redirectedCerr );
+                }
+                while( !m_testCaseTracker->isSuccessfullyCompleted() && !aborting() );
+            }
+            // !TBD: deprecated - this will be replaced by indexed trackers
+            while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
+
+            Totals deltaTotals = m_totals.delta( prevTotals );
+            if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) {
+                deltaTotals.assertions.failed++;
+                deltaTotals.testCases.passed--;
+                deltaTotals.testCases.failed++;
+            }
+            m_totals.testCases += deltaTotals.testCases;
+            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
+                                                        deltaTotals,
+                                                        redirectedCout,
+                                                        redirectedCerr,
+                                                        aborting() ) );
+
+            m_activeTestCase = CATCH_NULL;
+            m_testCaseTracker = CATCH_NULL;
+
+            return deltaTotals;
+        }
+
+        Ptr<IConfig const> config() const {
+            return m_config;
+        }
+
+    private: // IResultCapture
+
+        virtual void assertionEnded( AssertionResult const& result ) {
+            if( result.getResultType() == ResultWas::Ok ) {
+                m_totals.assertions.passed++;
+            }
+            else if( !result.isOk() ) {
+                m_totals.assertions.failed++;
+            }
+
+            // We have no use for the return value (whether messages should be cleared), because messages were made scoped
+            // and should be let to clear themselves out.
+            static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
+
+            // Reset working state
+            m_lastAssertionInfo = AssertionInfo( "", m_lastAssertionInfo.lineInfo, "{Unknown expression after the reported line}" , m_lastAssertionInfo.resultDisposition );
+            m_lastResult = result;
+        }
+
+        virtual bool lastAssertionPassed()
+        {
+            return m_totals.assertions.passed == (m_prevPassed + 1);
+        }
+
+        virtual void assertionPassed()
+        {
+            m_totals.assertions.passed++;
+            m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}";
+            m_lastAssertionInfo.macroName = "";
+        }
+
+        virtual void assertionRun()
+        {
+            m_prevPassed = m_totals.assertions.passed;
+        }
+
+        virtual bool sectionStarted (
+            SectionInfo const& sectionInfo,
+            Counts& assertions
+        )
+        {
+            ITracker& sectionTracker = SectionTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( sectionInfo.name, sectionInfo.lineInfo ) );
+            if( !sectionTracker.isOpen() )
+                return false;
+            m_activeSections.push_back( &sectionTracker );
+
+            m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
+
+            m_reporter->sectionStarting( sectionInfo );
+
+            assertions = m_totals.assertions;
+
+            return true;
+        }
+        bool testForMissingAssertions( Counts& assertions ) {
+            if( assertions.total() != 0 )
+                return false;
+            if( !m_config->warnAboutMissingAssertions() )
+                return false;
+            if( m_trackerContext.currentTracker().hasChildren() )
+                return false;
+            m_totals.assertions.failed++;
+            assertions.failed++;
+            return true;
+        }
+
+        virtual void sectionEnded( SectionEndInfo const& endInfo ) {
+            Counts assertions = m_totals.assertions - endInfo.prevAssertions;
+            bool missingAssertions = testForMissingAssertions( assertions );
+
+            if( !m_activeSections.empty() ) {
+                m_activeSections.back()->close();
+                m_activeSections.pop_back();
+            }
+
+            m_reporter->sectionEnded( SectionStats( endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions ) );
+            m_messages.clear();
+        }
+
+        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) {
+            if( m_unfinishedSections.empty() )
+                m_activeSections.back()->fail();
+            else
+                m_activeSections.back()->close();
+            m_activeSections.pop_back();
+
+            m_unfinishedSections.push_back( endInfo );
+        }
+
+        virtual void pushScopedMessage( MessageInfo const& message ) {
+            m_messages.push_back( message );
+        }
+
+        virtual void popScopedMessage( MessageInfo const& message ) {
+            m_messages.erase( std::remove( m_messages.begin(), m_messages.end(), message ), m_messages.end() );
+        }
+
+        virtual std::string getCurrentTestName() const {
+            return m_activeTestCase
+                ? m_activeTestCase->getTestCaseInfo().name
+                : std::string();
+        }
+
+        virtual const AssertionResult* getLastResult() const {
+            return &m_lastResult;
+        }
+
+        virtual void exceptionEarlyReported() {
+            m_shouldReportUnexpected = false;
+        }
+
+        virtual void handleFatalErrorCondition( std::string const& message ) {
+            // Don't rebuild the result -- the stringification itself can cause more fatal errors
+            // Instead, fake a result data.
+            AssertionResultData tempResult;
+            tempResult.resultType = ResultWas::FatalErrorCondition;
+            tempResult.message = message;
+            AssertionResult result(m_lastAssertionInfo, tempResult);
+
+            getResultCapture().assertionEnded(result);
+
+            handleUnfinishedSections();
+
+            // Recreate section for test case (as we will lose the one that was in scope)
+            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+
+            Counts assertions;
+            assertions.failed = 1;
+            SectionStats testCaseSectionStats( testCaseSection, assertions, 0, false );
+            m_reporter->sectionEnded( testCaseSectionStats );
+
+            TestCaseInfo testInfo = m_activeTestCase->getTestCaseInfo();
+
+            Totals deltaTotals;
+            deltaTotals.testCases.failed = 1;
+            deltaTotals.assertions.failed = 1;
+            m_reporter->testCaseEnded( TestCaseStats(   testInfo,
+                                                        deltaTotals,
+                                                        std::string(),
+                                                        std::string(),
+                                                        false ) );
+            m_totals.testCases.failed++;
+            testGroupEnded( std::string(), m_totals, 1, 1 );
+            m_reporter->testRunEnded( TestRunStats( m_runInfo, m_totals, false ) );
+        }
+
+    public:
+        // !TBD We need to do this another way!
+        bool aborting() const {
+            return m_totals.assertions.failed == static_cast<std::size_t>( m_config->abortAfter() );
+        }
+
+    private:
+
+        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr ) {
+            TestCaseInfo const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
+            SectionInfo testCaseSection( testCaseInfo.lineInfo, testCaseInfo.name, testCaseInfo.description );
+            m_reporter->sectionStarting( testCaseSection );
+            Counts prevAssertions = m_totals.assertions;
+            double duration = 0;
+            m_shouldReportUnexpected = true;
+            try {
+                m_lastAssertionInfo = AssertionInfo( "TEST_CASE", testCaseInfo.lineInfo, "", ResultDisposition::Normal );
+
+                seedRng( *m_config );
+
+                Timer timer;
+                timer.start();
+                if( m_reporter->getPreferences().shouldRedirectStdOut ) {
+                    StreamRedirect coutRedir( Catch::cout(), redirectedCout );
+                    StdErrRedirect errRedir( redirectedCerr );
+                    invokeActiveTestCase();
+                }
+                else {
+                    invokeActiveTestCase();
+                }
+                duration = timer.getElapsedSeconds();
+            }
+            catch( TestFailureException& ) {
+                // This just means the test was aborted due to failure
+            }
+            catch(...) {
+                // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
+                // are reported without translation at the point of origin.
+                if (m_shouldReportUnexpected) {
+                    makeUnexpectedResultBuilder().useActiveException();
+                }
+            }
+            m_testCaseTracker->close();
+            handleUnfinishedSections();
+            m_messages.clear();
+
+            Counts assertions = m_totals.assertions - prevAssertions;
+            bool missingAssertions = testForMissingAssertions( assertions );
+
+            if( testCaseInfo.okToFail() ) {
+                std::swap( assertions.failedButOk, assertions.failed );
+                m_totals.assertions.failed -= assertions.failedButOk;
+                m_totals.assertions.failedButOk += assertions.failedButOk;
+            }
+
+            SectionStats testCaseSectionStats( testCaseSection, assertions, duration, missingAssertions );
+            m_reporter->sectionEnded( testCaseSectionStats );
+        }
+
+        void invokeActiveTestCase() {
+            FatalConditionHandler fatalConditionHandler; // Handle signals
+            m_activeTestCase->invoke();
+            fatalConditionHandler.reset();
+        }
+
+    private:
+
+        ResultBuilder makeUnexpectedResultBuilder() const {
+            return ResultBuilder(   m_lastAssertionInfo.macroName,
+                                    m_lastAssertionInfo.lineInfo,
+                                    m_lastAssertionInfo.capturedExpression,
+                                    m_lastAssertionInfo.resultDisposition );
+        }
+
+        void handleUnfinishedSections() {
+            // If sections ended prematurely due to an exception we stored their
+            // infos here so we can tear them down outside the unwind process.
+            for( std::vector<SectionEndInfo>::const_reverse_iterator it = m_unfinishedSections.rbegin(),
+                        itEnd = m_unfinishedSections.rend();
+                    it != itEnd;
+                    ++it )
+                sectionEnded( *it );
+            m_unfinishedSections.clear();
+        }
+
+        TestRunInfo m_runInfo;
+        IMutableContext& m_context;
+        TestCase const* m_activeTestCase;
+        ITracker* m_testCaseTracker;
+        ITracker* m_currentSectionTracker;
+        AssertionResult m_lastResult;
+
+        Ptr<IConfig const> m_config;
+        Totals m_totals;
+        Ptr<IStreamingReporter> m_reporter;
+        std::vector<MessageInfo> m_messages;
+        AssertionInfo m_lastAssertionInfo;
+        std::vector<SectionEndInfo> m_unfinishedSections;
+        std::vector<ITracker*> m_activeSections;
+        TrackerContext m_trackerContext;
+        size_t m_prevPassed;
+        bool m_shouldReportUnexpected;
+    };
+
+    IResultCapture& getResultCapture() {
+        if( IResultCapture* capture = getCurrentContext().getResultCapture() )
+            return *capture;
+        else
+            throw std::logic_error( "No result capture instance" );
+    }
+
+} // end namespace Catch
+
+// #included from: internal/catch_version.h
+#define TWOBLUECUBES_CATCH_VERSION_H_INCLUDED
+
+namespace Catch {
+
+    // Versioning information
+    struct Version {
+        Version(    unsigned int _majorVersion,
+                    unsigned int _minorVersion,
+                    unsigned int _patchNumber,
+                    char const * const _branchName,
+                    unsigned int _buildNumber );
+
+        unsigned int const majorVersion;
+        unsigned int const minorVersion;
+        unsigned int const patchNumber;
+
+        // buildNumber is only used if branchName is not null
+        char const * const branchName;
+        unsigned int const buildNumber;
+
+        friend std::ostream& operator << ( std::ostream& os, Version const& version );
+
+    private:
+        void operator=( Version const& );
+    };
+
+    inline Version libraryVersion();
+}
+
+#include <fstream>
+#include <stdlib.h>
+#include <limits>
+
+namespace Catch {
+
+    Ptr<IStreamingReporter> createReporter( std::string const& reporterName, Ptr<Config> const& config ) {
+        Ptr<IStreamingReporter> reporter = getRegistryHub().getReporterRegistry().create( reporterName, config.get() );
+        if( !reporter ) {
+            std::ostringstream oss;
+            oss << "No reporter registered with name: '" << reporterName << "'";
+            throw std::domain_error( oss.str() );
+        }
+        return reporter;
+    }
+
+#if !defined(CATCH_CONFIG_DEFAULT_REPORTER)
+#define CATCH_CONFIG_DEFAULT_REPORTER "console"
+#endif
+
+    Ptr<IStreamingReporter> makeReporter( Ptr<Config> const& config ) {
+        std::vector<std::string> reporters = config->getReporterNames();
+        if( reporters.empty() )
+            reporters.push_back( CATCH_CONFIG_DEFAULT_REPORTER );
+
+        Ptr<IStreamingReporter> reporter;
+        for( std::vector<std::string>::const_iterator it = reporters.begin(), itEnd = reporters.end();
+                it != itEnd;
+                ++it )
+            reporter = addReporter( reporter, createReporter( *it, config ) );
+        return reporter;
+    }
+    Ptr<IStreamingReporter> addListeners( Ptr<IConfig const> const& config, Ptr<IStreamingReporter> reporters ) {
+        IReporterRegistry::Listeners listeners = getRegistryHub().getReporterRegistry().getListeners();
+        for( IReporterRegistry::Listeners::const_iterator it = listeners.begin(), itEnd = listeners.end();
+                it != itEnd;
+                ++it )
+            reporters = addReporter(reporters, (*it)->create( ReporterConfig( config ) ) );
+        return reporters;
+    }
+
+    Totals runTests( Ptr<Config> const& config ) {
+
+        Ptr<IConfig const> iconfig = config.get();
+
+        Ptr<IStreamingReporter> reporter = makeReporter( config );
+        reporter = addListeners( iconfig, reporter );
+
+        RunContext context( iconfig, reporter );
+
+        Totals totals;
+
+        context.testGroupStarting( config->name(), 1, 1 );
+
+        TestSpec testSpec = config->testSpec();
+        if( !testSpec.hasFilters() )
+            testSpec = TestSpecParser( ITagAliasRegistry::get() ).parse( "~[.]" ).testSpec(); // All not hidden tests
+
+        std::vector<TestCase> const& allTestCases = getAllTestCasesSorted( *iconfig );
+        for( std::vector<TestCase>::const_iterator it = allTestCases.begin(), itEnd = allTestCases.end();
+                it != itEnd;
+                ++it ) {
+            if( !context.aborting() && matchTest( *it, testSpec, *iconfig ) )
+                totals += context.runTest( *it );
+            else
+                reporter->skipTest( *it );
+        }
+
+        context.testGroupEnded( iconfig->name(), totals, 1, 1 );
+        return totals;
+    }
+
+    void applyFilenamesAsTags( IConfig const& config ) {
+        std::vector<TestCase> const& tests = getAllTestCasesSorted( config );
+        for(std::size_t i = 0; i < tests.size(); ++i ) {
+            TestCase& test = const_cast<TestCase&>( tests[i] );
+            std::set<std::string> tags = test.tags;
+
+            std::string filename = test.lineInfo.file;
+            std::string::size_type lastSlash = filename.find_last_of( "\\/" );
+            if( lastSlash != std::string::npos )
+                filename = filename.substr( lastSlash+1 );
+
+            std::string::size_type lastDot = filename.find_last_of( '.' );
+            if( lastDot != std::string::npos )
+                filename = filename.substr( 0, lastDot );
+
+            tags.insert( '#' + filename );
+            setTags( test, tags );
+        }
+    }
+
+    class Session : NonCopyable {
+        static bool alreadyInstantiated;
+
+    public:
+
+        struct OnUnusedOptions { enum DoWhat { Ignore, Fail }; };
+
+        Session()
+        : m_cli( makeCommandLineParser() ) {
+            if( alreadyInstantiated ) {
+                std::string msg = "Only one instance of Catch::Session can ever be used";
+                Catch::cerr() << msg << std::endl;
+                throw std::logic_error( msg );
+            }
+            alreadyInstantiated = true;
+        }
+        ~Session() {
+            Catch::cleanUp();
+        }
+
+        void showHelp( std::string const& processName ) {
+            Catch::cout() << "\nCatch v" << libraryVersion() << "\n";
+
+            m_cli.usage( Catch::cout(), processName );
+            Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
+        }
+
+        int applyCommandLine( int argc, char const* const* const argv, OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+            try {
+                m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
+                m_unusedTokens = m_cli.parseInto( Clara::argsToVector( argc, argv ), m_configData );
+                if( m_configData.showHelp )
+                    showHelp( m_configData.processName );
+                m_config.reset();
+            }
+            catch( std::exception& ex ) {
+                {
+                    Colour colourGuard( Colour::Red );
+                    Catch::cerr()
+                        << "\nError(s) in input:\n"
+                        << Text( ex.what(), TextAttributes().setIndent(2) )
+                        << "\n\n";
+                }
+                m_cli.usage( Catch::cout(), m_configData.processName );
+                return (std::numeric_limits<int>::max)();
+            }
+            return 0;
+        }
+
+        void useConfigData( ConfigData const& _configData ) {
+            m_configData = _configData;
+            m_config.reset();
+        }
+
+        int run( int argc, char const* const* const argv ) {
+
+            int returnCode = applyCommandLine( argc, argv );
+            if( returnCode == 0 )
+                returnCode = run();
+            return returnCode;
+        }
+
+    #if defined(WIN32) && defined(UNICODE)
+        int run( int argc, wchar_t const* const* const argv ) {
+
+            char **utf8Argv = new char *[ argc ];
+
+            for ( int i = 0; i < argc; ++i ) {
+                int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
+
+                utf8Argv[ i ] = new char[ bufSize ];
+
+                WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
+            }
+
+            int returnCode = applyCommandLine( argc, utf8Argv );
+            if( returnCode == 0 )
+                returnCode = run();
+
+            for ( int i = 0; i < argc; ++i )
+                delete [] utf8Argv[ i ];
+
+            delete [] utf8Argv;
+
+            return returnCode;
+        }
+    #endif
+
+        int run() {
+            if( m_configData.showHelp )
+                return 0;
+
+            try
+            {
+                config(); // Force config to be constructed
+
+                seedRng( *m_config );
+
+                if( m_configData.filenamesAsTags )
+                    applyFilenamesAsTags( *m_config );
+
+                // Handle list request
+                if( Option<std::size_t> listed = list( config() ) )
+                    return static_cast<int>( *listed );
+
+                return static_cast<int>( runTests( m_config ).assertions.failed );
+            }
+            catch( std::exception& ex ) {
+                Catch::cerr() << ex.what() << std::endl;
+                return (std::numeric_limits<int>::max)();
+            }
+        }
+
+        Clara::CommandLine<ConfigData> const& cli() const {
+            return m_cli;
+        }
+        std::vector<Clara::Parser::Token> const& unusedTokens() const {
+            return m_unusedTokens;
+        }
+        ConfigData& configData() {
+            return m_configData;
+        }
+        Config& config() {
+            if( !m_config )
+                m_config = new Config( m_configData );
+            return *m_config;
+        }
+    private:
+        Clara::CommandLine<ConfigData> m_cli;
+        std::vector<Clara::Parser::Token> m_unusedTokens;
+        ConfigData m_configData;
+        Ptr<Config> m_config;
+    };
+
+    bool Session::alreadyInstantiated = false;
+
+} // end namespace Catch
+
+// #included from: catch_registry_hub.hpp
+#define TWOBLUECUBES_CATCH_REGISTRY_HUB_HPP_INCLUDED
+
+// #included from: catch_test_case_registry_impl.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_REGISTRY_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <set>
+#include <sstream>
+#include <algorithm>
+
+namespace Catch {
+
+    struct RandomNumberGenerator {
+        typedef std::ptrdiff_t result_type;
+
+        result_type operator()( result_type n ) const { return std::rand() % n; }
+
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+        static constexpr result_type min() { return 0; }
+        static constexpr result_type max() { return 1000000; }
+        result_type operator()() const { return std::rand() % max(); }
+#endif
+        template<typename V>
+        static void shuffle( V& vector ) {
+            RandomNumberGenerator rng;
+#ifdef CATCH_CONFIG_CPP11_SHUFFLE
+            std::shuffle( vector.begin(), vector.end(), rng );
+#else
+            std::random_shuffle( vector.begin(), vector.end(), rng );
+#endif
+        }
+    };
+
+    inline std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
+
+        std::vector<TestCase> sorted = unsortedTestCases;
+
+        switch( config.runOrder() ) {
+            case RunTests::InLexicographicalOrder:
+                std::sort( sorted.begin(), sorted.end() );
+                break;
+            case RunTests::InRandomOrder:
+                {
+                    seedRng( config );
+                    RandomNumberGenerator::shuffle( sorted );
+                }
+                break;
+            case RunTests::InDeclarationOrder:
+                // already in declaration order
+                break;
+        }
+        return sorted;
+    }
+    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
+        return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
+    }
+
+    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
+        std::set<TestCase> seenFunctions;
+        for( std::vector<TestCase>::const_iterator it = functions.begin(), itEnd = functions.end();
+            it != itEnd;
+            ++it ) {
+            std::pair<std::set<TestCase>::const_iterator, bool> prev = seenFunctions.insert( *it );
+            if( !prev.second ) {
+                std::ostringstream ss;
+
+                ss  << Colour( Colour::Red )
+                    << "error: TEST_CASE( \"" << it->name << "\" ) already defined.\n"
+                    << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << '\n'
+                    << "\tRedefined at " << it->getTestCaseInfo().lineInfo << std::endl;
+
+                throw std::runtime_error(ss.str());
+            }
+        }
+    }
+
+    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
+        std::vector<TestCase> filtered;
+        filtered.reserve( testCases.size() );
+        for( std::vector<TestCase>::const_iterator it = testCases.begin(), itEnd = testCases.end();
+                it != itEnd;
+                ++it )
+            if( matchTest( *it, testSpec, config ) )
+                filtered.push_back( *it );
+        return filtered;
+    }
+    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
+        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
+    }
+
+    class TestRegistry : public ITestCaseRegistry {
+    public:
+        TestRegistry()
+        :   m_currentSortOrder( RunTests::InDeclarationOrder ),
+            m_unnamedCount( 0 )
+        {}
+        virtual ~TestRegistry();
+
+        virtual void registerTest( TestCase const& testCase ) {
+            std::string name = testCase.getTestCaseInfo().name;
+            if( name.empty() ) {
+                std::ostringstream oss;
+                oss << "Anonymous test case " << ++m_unnamedCount;
+                return registerTest( testCase.withName( oss.str() ) );
+            }
+            m_functions.push_back( testCase );
+        }
+
+        virtual std::vector<TestCase> const& getAllTests() const {
+            return m_functions;
+        }
+        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const {
+            if( m_sortedFunctions.empty() )
+                enforceNoDuplicateTestCases( m_functions );
+
+            if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
+                m_sortedFunctions = sortTests( config, m_functions );
+                m_currentSortOrder = config.runOrder();
+            }
+            return m_sortedFunctions;
+        }
+
+    private:
+        std::vector<TestCase> m_functions;
+        mutable RunTests::InWhatOrder m_currentSortOrder;
+        mutable std::vector<TestCase> m_sortedFunctions;
+        size_t m_unnamedCount;
+        std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class FreeFunctionTestCase : public SharedImpl<ITestCase> {
+    public:
+
+        FreeFunctionTestCase( TestFunction fun ) : m_fun( fun ) {}
+
+        virtual void invoke() const {
+            m_fun();
+        }
+
+    private:
+        virtual ~FreeFunctionTestCase();
+
+        TestFunction m_fun;
+    };
+
+    inline std::string extractClassName( std::string const& classOrQualifiedMethodName ) {
+        std::string className = classOrQualifiedMethodName;
+        if( startsWith( className, '&' ) )
+        {
+            std::size_t lastColons = className.rfind( "::" );
+            std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
+            if( penultimateColons == std::string::npos )
+                penultimateColons = 1;
+            className = className.substr( penultimateColons, lastColons-penultimateColons );
+        }
+        return className;
+    }
+
+    void registerTestCase
+        (   ITestCase* testCase,
+            char const* classOrQualifiedMethodName,
+            NameAndDesc const& nameAndDesc,
+            SourceLineInfo const& lineInfo ) {
+
+        getMutableRegistryHub().registerTest
+            ( makeTestCase
+                (   testCase,
+                    extractClassName( classOrQualifiedMethodName ),
+                    nameAndDesc.name,
+                    nameAndDesc.description,
+                    lineInfo ) );
+    }
+    void registerTestCaseFunction
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
+        registerTestCase( new FreeFunctionTestCase( function ), "", nameAndDesc, lineInfo );
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    AutoReg::AutoReg
+        (   TestFunction function,
+            SourceLineInfo const& lineInfo,
+            NameAndDesc const& nameAndDesc ) {
+        registerTestCaseFunction( function, lineInfo, nameAndDesc );
+    }
+
+    AutoReg::~AutoReg() {}
+
+} // end namespace Catch
+
+// #included from: catch_reporter_registry.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRY_HPP_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+    class ReporterRegistry : public IReporterRegistry {
+
+    public:
+
+        virtual ~ReporterRegistry() CATCH_OVERRIDE {}
+
+        virtual IStreamingReporter* create( std::string const& name, Ptr<IConfig const> const& config ) const CATCH_OVERRIDE {
+            FactoryMap::const_iterator it =  m_factories.find( name );
+            if( it == m_factories.end() )
+                return CATCH_NULL;
+            return it->second->create( ReporterConfig( config ) );
+        }
+
+        void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) {
+            m_factories.insert( std::make_pair( name, factory ) );
+        }
+        void registerListener( Ptr<IReporterFactory> const& factory ) {
+            m_listeners.push_back( factory );
+        }
+
+        virtual FactoryMap const& getFactories() const CATCH_OVERRIDE {
+            return m_factories;
+        }
+        virtual Listeners const& getListeners() const CATCH_OVERRIDE {
+            return m_listeners;
+        }
+
+    private:
+        FactoryMap m_factories;
+        Listeners m_listeners;
+    };
+}
+
+// #included from: catch_exception_translator_registry.hpp
+#define TWOBLUECUBES_CATCH_EXCEPTION_TRANSLATOR_REGISTRY_HPP_INCLUDED
+
+#ifdef __OBJC__
+#import "Foundation/Foundation.h"
+#endif
+
+namespace Catch {
+
+    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
+    public:
+        ~ExceptionTranslatorRegistry() {
+            deleteAll( m_translators );
+        }
+
+        virtual void registerTranslator( const IExceptionTranslator* translator ) {
+            m_translators.push_back( translator );
+        }
+
+        virtual std::string translateActiveException() const {
+            try {
+#ifdef __OBJC__
+                // In Objective-C try objective-c exceptions first
+                @try {
+                    return tryTranslators();
+                }
+                @catch (NSException *exception) {
+                    return Catch::toString( [exception description] );
+                }
+#else
+                return tryTranslators();
+#endif
+            }
+            catch( TestFailureException& ) {
+                throw;
+            }
+            catch( std::exception& ex ) {
+                return ex.what();
+            }
+            catch( std::string& msg ) {
+                return msg;
+            }
+            catch( const char* msg ) {
+                return msg;
+            }
+            catch(...) {
+                return "Unknown exception";
+            }
+        }
+
+        std::string tryTranslators() const {
+            if( m_translators.empty() )
+                throw;
+            else
+                return m_translators[0]->translate( m_translators.begin()+1, m_translators.end() );
+        }
+
+    private:
+        std::vector<const IExceptionTranslator*> m_translators;
+    };
+}
+
+// #included from: catch_tag_alias_registry.h
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_H_INCLUDED
+
+#include <map>
+
+namespace Catch {
+
+    class TagAliasRegistry : public ITagAliasRegistry {
+    public:
+        virtual ~TagAliasRegistry();
+        virtual Option<TagAlias> find( std::string const& alias ) const;
+        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const;
+        void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
+
+    private:
+        std::map<std::string, TagAlias> m_registry;
+    };
+
+} // end namespace Catch
+
+namespace Catch {
+
+    namespace {
+
+        class RegistryHub : public IRegistryHub, public IMutableRegistryHub {
+
+            RegistryHub( RegistryHub const& );
+            void operator=( RegistryHub const& );
+
+        public: // IRegistryHub
+            RegistryHub() {
+            }
+            virtual IReporterRegistry const& getReporterRegistry() const CATCH_OVERRIDE {
+                return m_reporterRegistry;
+            }
+            virtual ITestCaseRegistry const& getTestCaseRegistry() const CATCH_OVERRIDE {
+                return m_testCaseRegistry;
+            }
+            virtual IExceptionTranslatorRegistry& getExceptionTranslatorRegistry() CATCH_OVERRIDE {
+                return m_exceptionTranslatorRegistry;
+            }
+            virtual ITagAliasRegistry const& getTagAliasRegistry() const CATCH_OVERRIDE {
+                return m_tagAliasRegistry;
+            }
+
+        public: // IMutableRegistryHub
+            virtual void registerReporter( std::string const& name, Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+                m_reporterRegistry.registerReporter( name, factory );
+            }
+            virtual void registerListener( Ptr<IReporterFactory> const& factory ) CATCH_OVERRIDE {
+                m_reporterRegistry.registerListener( factory );
+            }
+            virtual void registerTest( TestCase const& testInfo ) CATCH_OVERRIDE {
+                m_testCaseRegistry.registerTest( testInfo );
+            }
+            virtual void registerTranslator( const IExceptionTranslator* translator ) CATCH_OVERRIDE {
+                m_exceptionTranslatorRegistry.registerTranslator( translator );
+            }
+            virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) CATCH_OVERRIDE {
+                m_tagAliasRegistry.add( alias, tag, lineInfo );
+            }
+
+        private:
+            TestRegistry m_testCaseRegistry;
+            ReporterRegistry m_reporterRegistry;
+            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
+            TagAliasRegistry m_tagAliasRegistry;
+        };
+
+        // Single, global, instance
+        inline RegistryHub*& getTheRegistryHub() {
+            static RegistryHub* theRegistryHub = CATCH_NULL;
+            if( !theRegistryHub )
+                theRegistryHub = new RegistryHub();
+            return theRegistryHub;
+        }
+    }
+
+    IRegistryHub& getRegistryHub() {
+        return *getTheRegistryHub();
+    }
+    IMutableRegistryHub& getMutableRegistryHub() {
+        return *getTheRegistryHub();
+    }
+    void cleanUp() {
+        delete getTheRegistryHub();
+        getTheRegistryHub() = CATCH_NULL;
+        cleanUpContext();
+    }
+    std::string translateActiveException() {
+        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_notimplemented_exception.hpp
+#define TWOBLUECUBES_CATCH_NOTIMPLEMENTED_EXCEPTION_HPP_INCLUDED
+
+#include <sstream>
+
+namespace Catch {
+
+    NotImplementedException::NotImplementedException( SourceLineInfo const& lineInfo )
+    :   m_lineInfo( lineInfo ) {
+        std::ostringstream oss;
+        oss << lineInfo << ": function ";
+        oss << "not implemented";
+        m_what = oss.str();
+    }
+
+    const char* NotImplementedException::what() const CATCH_NOEXCEPT {
+        return m_what.c_str();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_context_impl.hpp
+#define TWOBLUECUBES_CATCH_CONTEXT_IMPL_HPP_INCLUDED
+
+// #included from: catch_stream.hpp
+#define TWOBLUECUBES_CATCH_STREAM_HPP_INCLUDED
+
+#include <stdexcept>
+#include <cstdio>
+#include <iostream>
+
+namespace Catch {
+
+    template<typename WriterF, size_t bufferSize=256>
+    class StreamBufImpl : public StreamBufBase {
+        char data[bufferSize];
+        WriterF m_writer;
+
+    public:
+        StreamBufImpl() {
+            setp( data, data + sizeof(data) );
+        }
+
+        ~StreamBufImpl() CATCH_NOEXCEPT {
+            sync();
+        }
+
+    private:
+        int overflow( int c ) {
+            sync();
+
+            if( c != EOF ) {
+                if( pbase() == epptr() )
+                    m_writer( std::string( 1, static_cast<char>( c ) ) );
+                else
+                    sputc( static_cast<char>( c ) );
+            }
+            return 0;
+        }
+
+        int sync() {
+            if( pbase() != pptr() ) {
+                m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
+                setp( pbase(), epptr() );
+            }
+            return 0;
+        }
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    FileStream::FileStream( std::string const& filename ) {
+        m_ofs.open( filename.c_str() );
+        if( m_ofs.fail() ) {
+            std::ostringstream oss;
+            oss << "Unable to open file: '" << filename << '\'';
+            throw std::domain_error( oss.str() );
+        }
+    }
+
+    std::ostream& FileStream::stream() const {
+        return m_ofs;
+    }
+
+    struct OutputDebugWriter {
+
+        void operator()( std::string const&str ) {
+            writeToDebugConsole( str );
+        }
+    };
+
+    DebugOutStream::DebugOutStream()
+    :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
+        m_os( m_streamBuf.get() )
+    {}
+
+    std::ostream& DebugOutStream::stream() const {
+        return m_os;
+    }
+
+    // Store the streambuf from cout up-front because
+    // cout may get redirected when running tests
+    CoutStream::CoutStream()
+    :   m_os( Catch::cout().rdbuf() )
+    {}
+
+    std::ostream& CoutStream::stream() const {
+        return m_os;
+    }
+
+#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
+    std::ostream& cout() {
+        return std::cout;
+    }
+    std::ostream& cerr() {
+        return std::cerr;
+    }
+    std::ostream& clog() {
+        return std::clog;
+    }
+#endif
+}
+
+namespace Catch {
+
+    class Context : public IMutableContext {
+
+        Context() : m_config( CATCH_NULL ), m_runner( CATCH_NULL ), m_resultCapture( CATCH_NULL ) {}
+        Context( Context const& );
+        void operator=( Context const& );
+
+    public:
+        virtual ~Context() {
+            deleteAllValues( m_generatorsByTestName );
+        }
+
+    public: // IContext
+        virtual IResultCapture* getResultCapture() {
+            return m_resultCapture;
+        }
+        virtual IRunner* getRunner() {
+            return m_runner;
+        }
+        virtual size_t getGeneratorIndex( std::string const& fileInfo, size_t totalSize ) {
+            return getGeneratorsForCurrentTest()
+            .getGeneratorInfo( fileInfo, totalSize )
+            .getCurrentIndex();
+        }
+        virtual bool advanceGeneratorsForCurrentTest() {
+            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+            return generators && generators->moveNext();
+        }
+
+        virtual Ptr<IConfig const> getConfig() const {
+            return m_config;
+        }
+
+    public: // IMutableContext
+        virtual void setResultCapture( IResultCapture* resultCapture ) {
+            m_resultCapture = resultCapture;
+        }
+        virtual void setRunner( IRunner* runner ) {
+            m_runner = runner;
+        }
+        virtual void setConfig( Ptr<IConfig const> const& config ) {
+            m_config = config;
+        }
+
+        friend IMutableContext& getCurrentMutableContext();
+
+    private:
+        IGeneratorsForTest* findGeneratorsForCurrentTest() {
+            std::string testName = getResultCapture()->getCurrentTestName();
+
+            std::map<std::string, IGeneratorsForTest*>::const_iterator it =
+                m_generatorsByTestName.find( testName );
+            return it != m_generatorsByTestName.end()
+                ? it->second
+                : CATCH_NULL;
+        }
+
+        IGeneratorsForTest& getGeneratorsForCurrentTest() {
+            IGeneratorsForTest* generators = findGeneratorsForCurrentTest();
+            if( !generators ) {
+                std::string testName = getResultCapture()->getCurrentTestName();
+                generators = createGeneratorsForTest();
+                m_generatorsByTestName.insert( std::make_pair( testName, generators ) );
+            }
+            return *generators;
+        }
+
+    private:
+        Ptr<IConfig const> m_config;
+        IRunner* m_runner;
+        IResultCapture* m_resultCapture;
+        std::map<std::string, IGeneratorsForTest*> m_generatorsByTestName;
+    };
+
+    namespace {
+        Context* currentContext = CATCH_NULL;
+    }
+    IMutableContext& getCurrentMutableContext() {
+        if( !currentContext )
+            currentContext = new Context();
+        return *currentContext;
+    }
+    IContext& getCurrentContext() {
+        return getCurrentMutableContext();
+    }
+
+    void cleanUpContext() {
+        delete currentContext;
+        currentContext = CATCH_NULL;
+    }
+}
+
+// #included from: catch_console_colour_impl.hpp
+#define TWOBLUECUBES_CATCH_CONSOLE_COLOUR_IMPL_HPP_INCLUDED
+
+// #included from: catch_errno_guard.hpp
+#define TWOBLUECUBES_CATCH_ERRNO_GUARD_HPP_INCLUDED
+
+#include <cerrno>
+
+namespace Catch {
+
+    class ErrnoGuard {
+    public:
+        ErrnoGuard():m_oldErrno(errno){}
+        ~ErrnoGuard() { errno = m_oldErrno; }
+    private:
+        int m_oldErrno;
+    };
+
+}
+
+namespace Catch {
+    namespace {
+
+        struct IColourImpl {
+            virtual ~IColourImpl() {}
+            virtual void use( Colour::Code _colourCode ) = 0;
+        };
+
+        struct NoColourImpl : IColourImpl {
+            void use( Colour::Code ) {}
+
+            static IColourImpl* instance() {
+                static NoColourImpl s_instance;
+                return &s_instance;
+            }
+        };
+
+    } // anon namespace
+} // namespace Catch
+
+#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
+#   ifdef CATCH_PLATFORM_WINDOWS
+#       define CATCH_CONFIG_COLOUR_WINDOWS
+#   else
+#       define CATCH_CONFIG_COLOUR_ANSI
+#   endif
+#endif
+
+#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
+
+namespace Catch {
+namespace {
+
+    class Win32ColourImpl : public IColourImpl {
+    public:
+        Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
+        {
+            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
+            GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
+            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
+            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
+        }
+
+        virtual void use( Colour::Code _colourCode ) {
+            switch( _colourCode ) {
+                case Colour::None:      return setTextAttribute( originalForegroundAttributes );
+                case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+                case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
+                case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
+                case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
+                case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
+                case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
+                case Colour::Grey:      return setTextAttribute( 0 );
+
+                case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
+                case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
+                case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
+                case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
+
+                case Colour::Bright: throw std::logic_error( "not a colour" );
+            }
+        }
+
+    private:
+        void setTextAttribute( WORD _textAttribute ) {
+            SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
+        }
+        HANDLE stdoutHandle;
+        WORD originalForegroundAttributes;
+        WORD originalBackgroundAttributes;
+    };
+
+    IColourImpl* platformColourInstance() {
+        static Win32ColourImpl s_instance;
+
+        Ptr<IConfig const> config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = !isDebuggerActive()
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
+            ? &s_instance
+            : NoColourImpl::instance();
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
+
+#include <unistd.h>
+
+namespace Catch {
+namespace {
+
+    // use POSIX/ ANSI console terminal codes
+    // Thanks to Adam Strzelecki for original contribution
+    // (http://github.com/nanoant)
+    // https://github.com/philsquared/Catch/pull/131
+    class PosixColourImpl : public IColourImpl {
+    public:
+        virtual void use( Colour::Code _colourCode ) {
+            switch( _colourCode ) {
+                case Colour::None:
+                case Colour::White:     return setColour( "[0m" );
+                case Colour::Red:       return setColour( "[0;31m" );
+                case Colour::Green:     return setColour( "[0;32m" );
+                case Colour::Blue:      return setColour( "[0;34m" );
+                case Colour::Cyan:      return setColour( "[0;36m" );
+                case Colour::Yellow:    return setColour( "[0;33m" );
+                case Colour::Grey:      return setColour( "[1;30m" );
+
+                case Colour::LightGrey:     return setColour( "[0;37m" );
+                case Colour::BrightRed:     return setColour( "[1;31m" );
+                case Colour::BrightGreen:   return setColour( "[1;32m" );
+                case Colour::BrightWhite:   return setColour( "[1;37m" );
+
+                case Colour::Bright: throw std::logic_error( "not a colour" );
+            }
+        }
+        static IColourImpl* instance() {
+            static PosixColourImpl s_instance;
+            return &s_instance;
+        }
+
+    private:
+        void setColour( const char* _escapeCode ) {
+            Catch::cout() << '\033' << _escapeCode;
+        }
+    };
+
+    IColourImpl* platformColourInstance() {
+        ErrnoGuard guard;
+        Ptr<IConfig const> config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
+            ? PosixColourImpl::instance()
+            : NoColourImpl::instance();
+    }
+
+} // end anon namespace
+} // end namespace Catch
+
+#else  // not Windows or ANSI ///////////////////////////////////////////////
+
+namespace Catch {
+
+    static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
+
+} // end namespace Catch
+
+#endif // Windows/ ANSI/ None
+
+namespace Catch {
+
+    Colour::Colour( Code _colourCode ) : m_moved( false ) { use( _colourCode ); }
+    Colour::Colour( Colour const& _other ) : m_moved( false ) { const_cast<Colour&>( _other ).m_moved = true; }
+    Colour::~Colour(){ if( !m_moved ) use( None ); }
+
+    void Colour::use( Code _colourCode ) {
+        static IColourImpl* impl = platformColourInstance();
+        impl->use( _colourCode );
+    }
+
+} // end namespace Catch
+
+// #included from: catch_generators_impl.hpp
+#define TWOBLUECUBES_CATCH_GENERATORS_IMPL_HPP_INCLUDED
+
+#include <vector>
+#include <string>
+#include <map>
+
+namespace Catch {
+
+    struct GeneratorInfo : IGeneratorInfo {
+
+        GeneratorInfo( std::size_t size )
+        :   m_size( size ),
+            m_currentIndex( 0 )
+        {}
+
+        bool moveNext() {
+            if( ++m_currentIndex == m_size ) {
+                m_currentIndex = 0;
+                return false;
+            }
+            return true;
+        }
+
+        std::size_t getCurrentIndex() const {
+            return m_currentIndex;
+        }
+
+        std::size_t m_size;
+        std::size_t m_currentIndex;
+    };
+
+    ///////////////////////////////////////////////////////////////////////////
+
+    class GeneratorsForTest : public IGeneratorsForTest {
+
+    public:
+        ~GeneratorsForTest() {
+            deleteAll( m_generatorsInOrder );
+        }
+
+        IGeneratorInfo& getGeneratorInfo( std::string const& fileInfo, std::size_t size ) {
+            std::map<std::string, IGeneratorInfo*>::const_iterator it = m_generatorsByName.find( fileInfo );
+            if( it == m_generatorsByName.end() ) {
+                IGeneratorInfo* info = new GeneratorInfo( size );
+                m_generatorsByName.insert( std::make_pair( fileInfo, info ) );
+                m_generatorsInOrder.push_back( info );
+                return *info;
+            }
+            return *it->second;
+        }
+
+        bool moveNext() {
+            std::vector<IGeneratorInfo*>::const_iterator it = m_generatorsInOrder.begin();
+            std::vector<IGeneratorInfo*>::const_iterator itEnd = m_generatorsInOrder.end();
+            for(; it != itEnd; ++it ) {
+                if( (*it)->moveNext() )
+                    return true;
+            }
+            return false;
+        }
+
+    private:
+        std::map<std::string, IGeneratorInfo*> m_generatorsByName;
+        std::vector<IGeneratorInfo*> m_generatorsInOrder;
+    };
+
+    IGeneratorsForTest* createGeneratorsForTest()
+    {
+        return new GeneratorsForTest();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_assertionresult.hpp
+#define TWOBLUECUBES_CATCH_ASSERTIONRESULT_HPP_INCLUDED
+
+namespace Catch {
+
+    AssertionInfo::AssertionInfo():macroName(""), capturedExpression(""), resultDisposition(ResultDisposition::Normal), secondArg(""){}
+
+    AssertionInfo::AssertionInfo(   char const * _macroName,
+                                    SourceLineInfo const& _lineInfo,
+                                    char const * _capturedExpression,
+                                    ResultDisposition::Flags _resultDisposition,
+                                    char const * _secondArg)
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        capturedExpression( _capturedExpression ),
+        resultDisposition( _resultDisposition ),
+        secondArg( _secondArg )
+    {}
+
+    AssertionResult::AssertionResult() {}
+
+    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
+    :   m_info( info ),
+        m_resultData( data )
+    {}
+
+    AssertionResult::~AssertionResult() {}
+
+    // Result was a success
+    bool AssertionResult::succeeded() const {
+        return Catch::isOk( m_resultData.resultType );
+    }
+
+    // Result was a success, or failure is suppressed
+    bool AssertionResult::isOk() const {
+        return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
+    }
+
+    ResultWas::OfType AssertionResult::getResultType() const {
+        return m_resultData.resultType;
+    }
+
+    bool AssertionResult::hasExpression() const {
+        return m_info.capturedExpression[0] != 0;
+    }
+
+    bool AssertionResult::hasMessage() const {
+        return !m_resultData.message.empty();
+    }
+
+    std::string capturedExpressionWithSecondArgument( char const * capturedExpression, char const * secondArg ) {
+        return (secondArg[0] == 0 || secondArg[0] == '"' && secondArg[1] == '"')
+            ? capturedExpression
+            : std::string(capturedExpression) + ", " + secondArg;
+    }
+
+    std::string AssertionResult::getExpression() const {
+        if( isFalseTest( m_info.resultDisposition ) )
+            return '!' + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
+        else
+            return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
+    }
+    std::string AssertionResult::getExpressionInMacro() const {
+        if( m_info.macroName[0] == 0 )
+            return capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg);
+        else
+            return std::string(m_info.macroName) + "( " + capturedExpressionWithSecondArgument(m_info.capturedExpression, m_info.secondArg) + " )";
+    }
+
+    bool AssertionResult::hasExpandedExpression() const {
+        return hasExpression() && getExpandedExpression() != getExpression();
+    }
+
+    std::string AssertionResult::getExpandedExpression() const {
+        return m_resultData.reconstructExpression();
+    }
+
+    std::string AssertionResult::getMessage() const {
+        return m_resultData.message;
+    }
+    SourceLineInfo AssertionResult::getSourceInfo() const {
+        return m_info.lineInfo;
+    }
+
+    std::string AssertionResult::getTestMacroName() const {
+        return m_info.macroName;
+    }
+
+    void AssertionResult::discardDecomposedExpression() const {
+        m_resultData.decomposedExpression = CATCH_NULL;
+    }
+
+    void AssertionResult::expandDecomposedExpression() const {
+        m_resultData.reconstructExpression();
+    }
+
+} // end namespace Catch
+
+// #included from: catch_test_case_info.hpp
+#define TWOBLUECUBES_CATCH_TEST_CASE_INFO_HPP_INCLUDED
+
+#include <cctype>
+
+namespace Catch {
+
+    inline TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
+        if( startsWith( tag, '.' ) ||
+            tag == "hide" ||
+            tag == "!hide" )
+            return TestCaseInfo::IsHidden;
+        else if( tag == "!throws" )
+            return TestCaseInfo::Throws;
+        else if( tag == "!shouldfail" )
+            return TestCaseInfo::ShouldFail;
+        else if( tag == "!mayfail" )
+            return TestCaseInfo::MayFail;
+        else if( tag == "!nonportable" )
+            return TestCaseInfo::NonPortable;
+        else
+            return TestCaseInfo::None;
+    }
+    inline bool isReservedTag( std::string const& tag ) {
+        return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( tag[0] );
+    }
+    inline void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
+        if( isReservedTag( tag ) ) {
+            std::ostringstream ss;
+            ss << Colour(Colour::Red)
+               << "Tag name [" << tag << "] not allowed.\n"
+               << "Tag names starting with non alpha-numeric characters are reserved\n"
+               << Colour(Colour::FileName)
+               << _lineInfo << '\n';
+            throw std::runtime_error(ss.str());
+        }
+    }
+
+    TestCase makeTestCase(  ITestCase* _testCase,
+                            std::string const& _className,
+                            std::string const& _name,
+                            std::string const& _descOrTags,
+                            SourceLineInfo const& _lineInfo )
+    {
+        bool isHidden( startsWith( _name, "./" ) ); // Legacy support
+
+        // Parse out tags
+        std::set<std::string> tags;
+        std::string desc, tag;
+        bool inTag = false;
+        for( std::size_t i = 0; i < _descOrTags.size(); ++i ) {
+            char c = _descOrTags[i];
+            if( !inTag ) {
+                if( c == '[' )
+                    inTag = true;
+                else
+                    desc += c;
+            }
+            else {
+                if( c == ']' ) {
+                    TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
+                    if( prop == TestCaseInfo::IsHidden )
+                        isHidden = true;
+                    else if( prop == TestCaseInfo::None )
+                        enforceNotReservedTag( tag, _lineInfo );
+
+                    tags.insert( tag );
+                    tag.clear();
+                    inTag = false;
+                }
+                else
+                    tag += c;
+            }
+        }
+        if( isHidden ) {
+            tags.insert( "hide" );
+            tags.insert( "." );
+        }
+
+        TestCaseInfo info( _name, _className, desc, tags, _lineInfo );
+        return TestCase( _testCase, info );
+    }
+
+    void setTags( TestCaseInfo& testCaseInfo, std::set<std::string> const& tags )
+    {
+        testCaseInfo.tags = tags;
+        testCaseInfo.lcaseTags.clear();
+
+        std::ostringstream oss;
+        for( std::set<std::string>::const_iterator it = tags.begin(), itEnd = tags.end(); it != itEnd; ++it ) {
+            oss << '[' << *it << ']';
+            std::string lcaseTag = toLower( *it );
+            testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
+            testCaseInfo.lcaseTags.insert( lcaseTag );
+        }
+        testCaseInfo.tagsAsString = oss.str();
+    }
+
+    TestCaseInfo::TestCaseInfo( std::string const& _name,
+                                std::string const& _className,
+                                std::string const& _description,
+                                std::set<std::string> const& _tags,
+                                SourceLineInfo const& _lineInfo )
+    :   name( _name ),
+        className( _className ),
+        description( _description ),
+        lineInfo( _lineInfo ),
+        properties( None )
+    {
+        setTags( *this, _tags );
+    }
+
+    TestCaseInfo::TestCaseInfo( TestCaseInfo const& other )
+    :   name( other.name ),
+        className( other.className ),
+        description( other.description ),
+        tags( other.tags ),
+        lcaseTags( other.lcaseTags ),
+        tagsAsString( other.tagsAsString ),
+        lineInfo( other.lineInfo ),
+        properties( other.properties )
+    {}
+
+    bool TestCaseInfo::isHidden() const {
+        return ( properties & IsHidden ) != 0;
+    }
+    bool TestCaseInfo::throws() const {
+        return ( properties & Throws ) != 0;
+    }
+    bool TestCaseInfo::okToFail() const {
+        return ( properties & (ShouldFail | MayFail ) ) != 0;
+    }
+    bool TestCaseInfo::expectedToFail() const {
+        return ( properties & (ShouldFail ) ) != 0;
+    }
+
+    TestCase::TestCase( ITestCase* testCase, TestCaseInfo const& info ) : TestCaseInfo( info ), test( testCase ) {}
+
+    TestCase::TestCase( TestCase const& other )
+    :   TestCaseInfo( other ),
+        test( other.test )
+    {}
+
+    TestCase TestCase::withName( std::string const& _newName ) const {
+        TestCase other( *this );
+        other.name = _newName;
+        return other;
+    }
+
+    void TestCase::swap( TestCase& other ) {
+        test.swap( other.test );
+        name.swap( other.name );
+        className.swap( other.className );
+        description.swap( other.description );
+        tags.swap( other.tags );
+        lcaseTags.swap( other.lcaseTags );
+        tagsAsString.swap( other.tagsAsString );
+        std::swap( TestCaseInfo::properties, static_cast<TestCaseInfo&>( other ).properties );
+        std::swap( lineInfo, other.lineInfo );
+    }
+
+    void TestCase::invoke() const {
+        test->invoke();
+    }
+
+    bool TestCase::operator == ( TestCase const& other ) const {
+        return  test.get() == other.test.get() &&
+                name == other.name &&
+                className == other.className;
+    }
+
+    bool TestCase::operator < ( TestCase const& other ) const {
+        return name < other.name;
+    }
+    TestCase& TestCase::operator = ( TestCase const& other ) {
+        TestCase temp( other );
+        swap( temp );
+        return *this;
+    }
+
+    TestCaseInfo const& TestCase::getTestCaseInfo() const
+    {
+        return *this;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_version.hpp
+#define TWOBLUECUBES_CATCH_VERSION_HPP_INCLUDED
+
+namespace Catch {
+
+    Version::Version
+        (   unsigned int _majorVersion,
+            unsigned int _minorVersion,
+            unsigned int _patchNumber,
+            char const * const _branchName,
+            unsigned int _buildNumber )
+    :   majorVersion( _majorVersion ),
+        minorVersion( _minorVersion ),
+        patchNumber( _patchNumber ),
+        branchName( _branchName ),
+        buildNumber( _buildNumber )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, Version const& version ) {
+        os  << version.majorVersion << '.'
+            << version.minorVersion << '.'
+            << version.patchNumber;
+        // branchName is never null -> 0th char is \0 if it is empty
+        if (version.branchName[0]) {
+            os << '-' << version.branchName
+               << '.' << version.buildNumber;
+        }
+        return os;
+    }
+
+    inline Version libraryVersion() {
+        static Version version( 1, 9, 7, "", 0 );
+        return version;
+    }
+
+}
+
+// #included from: catch_message.hpp
+#define TWOBLUECUBES_CATCH_MESSAGE_HPP_INCLUDED
+
+namespace Catch {
+
+    MessageInfo::MessageInfo(   std::string const& _macroName,
+                                SourceLineInfo const& _lineInfo,
+                                ResultWas::OfType _type )
+    :   macroName( _macroName ),
+        lineInfo( _lineInfo ),
+        type( _type ),
+        sequence( ++globalCount )
+    {}
+
+    // This may need protecting if threading support is added
+    unsigned int MessageInfo::globalCount = 0;
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    ScopedMessage::ScopedMessage( MessageBuilder const& builder )
+    : m_info( builder.m_info )
+    {
+        m_info.message = builder.m_stream.str();
+        getResultCapture().pushScopedMessage( m_info );
+    }
+    ScopedMessage::ScopedMessage( ScopedMessage const& other )
+    : m_info( other.m_info )
+    {}
+
+    ScopedMessage::~ScopedMessage() {
+        if ( !std::uncaught_exception() ){
+            getResultCapture().popScopedMessage(m_info);
+        }
+    }
+
+} // end namespace Catch
+
+// #included from: catch_legacy_reporter_adapter.hpp
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_HPP_INCLUDED
+
+// #included from: catch_legacy_reporter_adapter.h
+#define TWOBLUECUBES_CATCH_LEGACY_REPORTER_ADAPTER_H_INCLUDED
+
+namespace Catch
+{
+    // Deprecated
+    struct IReporter : IShared {
+        virtual ~IReporter();
+
+        virtual bool shouldRedirectStdout() const = 0;
+
+        virtual void StartTesting() = 0;
+        virtual void EndTesting( Totals const& totals ) = 0;
+        virtual void StartGroup( std::string const& groupName ) = 0;
+        virtual void EndGroup( std::string const& groupName, Totals const& totals ) = 0;
+        virtual void StartTestCase( TestCaseInfo const& testInfo ) = 0;
+        virtual void EndTestCase( TestCaseInfo const& testInfo, Totals const& totals, std::string const& stdOut, std::string const& stdErr ) = 0;
+        virtual void StartSection( std::string const& sectionName, std::string const& description ) = 0;
+        virtual void EndSection( std::string const& sectionName, Counts const& assertions ) = 0;
+        virtual void NoAssertionsInSection( std::string const& sectionName ) = 0;
+        virtual void NoAssertionsInTestCase( std::string const& testName ) = 0;
+        virtual void Aborted() = 0;
+        virtual void Result( AssertionResult const& result ) = 0;
+    };
+
+    class LegacyReporterAdapter : public SharedImpl<IStreamingReporter>
+    {
+    public:
+        LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter );
+        virtual ~LegacyReporterAdapter();
+
+        virtual ReporterPreferences getPreferences() const;
+        virtual void noMatchingTestCases( std::string const& );
+        virtual void testRunStarting( TestRunInfo const& );
+        virtual void testGroupStarting( GroupInfo const& groupInfo );
+        virtual void testCaseStarting( TestCaseInfo const& testInfo );
+        virtual void sectionStarting( SectionInfo const& sectionInfo );
+        virtual void assertionStarting( AssertionInfo const& );
+        virtual bool assertionEnded( AssertionStats const& assertionStats );
+        virtual void sectionEnded( SectionStats const& sectionStats );
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats );
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats );
+        virtual void testRunEnded( TestRunStats const& testRunStats );
+        virtual void skipTest( TestCaseInfo const& );
+
+    private:
+        Ptr<IReporter> m_legacyReporter;
+    };
+}
+
+namespace Catch
+{
+    LegacyReporterAdapter::LegacyReporterAdapter( Ptr<IReporter> const& legacyReporter )
+    :   m_legacyReporter( legacyReporter )
+    {}
+    LegacyReporterAdapter::~LegacyReporterAdapter() {}
+
+    ReporterPreferences LegacyReporterAdapter::getPreferences() const {
+        ReporterPreferences prefs;
+        prefs.shouldRedirectStdOut = m_legacyReporter->shouldRedirectStdout();
+        return prefs;
+    }
+
+    void LegacyReporterAdapter::noMatchingTestCases( std::string const& ) {}
+    void LegacyReporterAdapter::testRunStarting( TestRunInfo const& ) {
+        m_legacyReporter->StartTesting();
+    }
+    void LegacyReporterAdapter::testGroupStarting( GroupInfo const& groupInfo ) {
+        m_legacyReporter->StartGroup( groupInfo.name );
+    }
+    void LegacyReporterAdapter::testCaseStarting( TestCaseInfo const& testInfo ) {
+        m_legacyReporter->StartTestCase( testInfo );
+    }
+    void LegacyReporterAdapter::sectionStarting( SectionInfo const& sectionInfo ) {
+        m_legacyReporter->StartSection( sectionInfo.name, sectionInfo.description );
+    }
+    void LegacyReporterAdapter::assertionStarting( AssertionInfo const& ) {
+        // Not on legacy interface
+    }
+
+    bool LegacyReporterAdapter::assertionEnded( AssertionStats const& assertionStats ) {
+        if( assertionStats.assertionResult.getResultType() != ResultWas::Ok ) {
+            for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+                    it != itEnd;
+                    ++it ) {
+                if( it->type == ResultWas::Info ) {
+                    ResultBuilder rb( it->macroName.c_str(), it->lineInfo, "", ResultDisposition::Normal );
+                    rb << it->message;
+                    rb.setResultType( ResultWas::Info );
+                    AssertionResult result = rb.build();
+                    m_legacyReporter->Result( result );
+                }
+            }
+        }
+        m_legacyReporter->Result( assertionStats.assertionResult );
+        return true;
+    }
+    void LegacyReporterAdapter::sectionEnded( SectionStats const& sectionStats ) {
+        if( sectionStats.missingAssertions )
+            m_legacyReporter->NoAssertionsInSection( sectionStats.sectionInfo.name );
+        m_legacyReporter->EndSection( sectionStats.sectionInfo.name, sectionStats.assertions );
+    }
+    void LegacyReporterAdapter::testCaseEnded( TestCaseStats const& testCaseStats ) {
+        m_legacyReporter->EndTestCase
+            (   testCaseStats.testInfo,
+                testCaseStats.totals,
+                testCaseStats.stdOut,
+                testCaseStats.stdErr );
+    }
+    void LegacyReporterAdapter::testGroupEnded( TestGroupStats const& testGroupStats ) {
+        if( testGroupStats.aborting )
+            m_legacyReporter->Aborted();
+        m_legacyReporter->EndGroup( testGroupStats.groupInfo.name, testGroupStats.totals );
+    }
+    void LegacyReporterAdapter::testRunEnded( TestRunStats const& testRunStats ) {
+        m_legacyReporter->EndTesting( testRunStats.totals );
+    }
+    void LegacyReporterAdapter::skipTest( TestCaseInfo const& ) {
+    }
+}
+
+// #included from: catch_timer.hpp
+
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wc++11-long-long"
+#endif
+
+#ifdef CATCH_PLATFORM_WINDOWS
+
+#else
+
+#include <sys/time.h>
+
+#endif
+
+namespace Catch {
+
+    namespace {
+#ifdef CATCH_PLATFORM_WINDOWS
+        UInt64 getCurrentTicks() {
+            static UInt64 hz=0, hzo=0;
+            if (!hz) {
+                QueryPerformanceFrequency( reinterpret_cast<LARGE_INTEGER*>( &hz ) );
+                QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &hzo ) );
+            }
+            UInt64 t;
+            QueryPerformanceCounter( reinterpret_cast<LARGE_INTEGER*>( &t ) );
+            return ((t-hzo)*1000000)/hz;
+        }
+#else
+        UInt64 getCurrentTicks() {
+            timeval t;
+            gettimeofday(&t,CATCH_NULL);
+            return static_cast<UInt64>( t.tv_sec ) * 1000000ull + static_cast<UInt64>( t.tv_usec );
+        }
+#endif
+    }
+
+    void Timer::start() {
+        m_ticks = getCurrentTicks();
+    }
+    unsigned int Timer::getElapsedMicroseconds() const {
+        return static_cast<unsigned int>(getCurrentTicks() - m_ticks);
+    }
+    unsigned int Timer::getElapsedMilliseconds() const {
+        return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
+    }
+    double Timer::getElapsedSeconds() const {
+        return getElapsedMicroseconds()/1000000.0;
+    }
+
+} // namespace Catch
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+// #included from: catch_common.hpp
+#define TWOBLUECUBES_CATCH_COMMON_HPP_INCLUDED
+
+#include <cstring>
+#include <cctype>
+
+namespace Catch {
+
+    bool startsWith( std::string const& s, std::string const& prefix ) {
+        return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
+    }
+    bool startsWith( std::string const& s, char prefix ) {
+        return !s.empty() && s[0] == prefix;
+    }
+    bool endsWith( std::string const& s, std::string const& suffix ) {
+        return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
+    }
+    bool endsWith( std::string const& s, char suffix ) {
+        return !s.empty() && s[s.size()-1] == suffix;
+    }
+    bool contains( std::string const& s, std::string const& infix ) {
+        return s.find( infix ) != std::string::npos;
+    }
+    char toLowerCh(char c) {
+        return static_cast<char>( std::tolower( c ) );
+    }
+    void toLowerInPlace( std::string& s ) {
+        std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
+    }
+    std::string toLower( std::string const& s ) {
+        std::string lc = s;
+        toLowerInPlace( lc );
+        return lc;
+    }
+    std::string trim( std::string const& str ) {
+        static char const* whitespaceChars = "\n\r\t ";
+        std::string::size_type start = str.find_first_not_of( whitespaceChars );
+        std::string::size_type end = str.find_last_not_of( whitespaceChars );
+
+        return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
+    }
+
+    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
+        bool replaced = false;
+        std::size_t i = str.find( replaceThis );
+        while( i != std::string::npos ) {
+            replaced = true;
+            str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
+            if( i < str.size()-withThis.size() )
+                i = str.find( replaceThis, i+withThis.size() );
+            else
+                i = std::string::npos;
+        }
+        return replaced;
+    }
+
+    pluralise::pluralise( std::size_t count, std::string const& label )
+    :   m_count( count ),
+        m_label( label )
+    {}
+
+    std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
+        os << pluraliser.m_count << ' ' << pluraliser.m_label;
+        if( pluraliser.m_count != 1 )
+            os << 's';
+        return os;
+    }
+
+    SourceLineInfo::SourceLineInfo() : file(""), line( 0 ){}
+    SourceLineInfo::SourceLineInfo( char const* _file, std::size_t _line )
+    :   file( _file ),
+        line( _line )
+    {}
+    bool SourceLineInfo::empty() const {
+        return file[0] == '\0';
+    }
+    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const {
+        return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
+    }
+    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const {
+        return line < other.line || ( line == other.line && (std::strcmp(file, other.file) < 0));
+    }
+
+    void seedRng( IConfig const& config ) {
+        if( config.rngSeed() != 0 )
+            std::srand( config.rngSeed() );
+    }
+    unsigned int rngSeed() {
+        return getCurrentContext().getConfig()->rngSeed();
+    }
+
+    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
+#ifndef __GNUG__
+        os << info.file << '(' << info.line << ')';
+#else
+        os << info.file << ':' << info.line;
+#endif
+        return os;
+    }
+
+    void throwLogicError( std::string const& message, SourceLineInfo const& locationInfo ) {
+        std::ostringstream oss;
+        oss << locationInfo << ": Internal Catch error: '" << message << '\'';
+        if( alwaysTrue() )
+            throw std::logic_error( oss.str() );
+    }
+}
+
+// #included from: catch_section.hpp
+#define TWOBLUECUBES_CATCH_SECTION_HPP_INCLUDED
+
+namespace Catch {
+
+    SectionInfo::SectionInfo
+        (   SourceLineInfo const& _lineInfo,
+            std::string const& _name,
+            std::string const& _description )
+    :   name( _name ),
+        description( _description ),
+        lineInfo( _lineInfo )
+    {}
+
+    Section::Section( SectionInfo const& info )
+    :   m_info( info ),
+        m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
+    {
+        m_timer.start();
+    }
+
+#if defined(_MSC_VER)
+#pragma warning(push)
+#pragma warning(disable:4996) // std::uncaught_exception is deprecated in C++17
+#endif
+    Section::~Section() {
+        if( m_sectionIncluded ) {
+            SectionEndInfo endInfo( m_info, m_assertions, m_timer.getElapsedSeconds() );
+            if( std::uncaught_exception() )
+                getResultCapture().sectionEndedEarly( endInfo );
+            else
+                getResultCapture().sectionEnded( endInfo );
+        }
+    }
+#if defined(_MSC_VER)
+#pragma warning(pop)
+#endif
+
+    // This indicates whether the section should be executed or not
+    Section::operator bool() const {
+        return m_sectionIncluded;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_debugger.hpp
+#define TWOBLUECUBES_CATCH_DEBUGGER_HPP_INCLUDED
+
+#ifdef CATCH_PLATFORM_MAC
+
+    #include <assert.h>
+    #include <stdbool.h>
+    #include <sys/types.h>
+    #include <unistd.h>
+    #include <sys/sysctl.h>
+
+    namespace Catch{
+
+        // The following function is taken directly from the following technical note:
+        // http://developer.apple.com/library/mac/#qa/qa2004/qa1361.html
+
+        // Returns true if the current process is being debugged (either
+        // running under the debugger or has a debugger attached post facto).
+        bool isDebuggerActive(){
+
+            int                 mib[4];
+            struct kinfo_proc   info;
+            size_t              size;
+
+            // Initialize the flags so that, if sysctl fails for some bizarre
+            // reason, we get a predictable result.
+
+            info.kp_proc.p_flag = 0;
+
+            // Initialize mib, which tells sysctl the info we want, in this case
+            // we're looking for information about a specific process ID.
+
+            mib[0] = CTL_KERN;
+            mib[1] = KERN_PROC;
+            mib[2] = KERN_PROC_PID;
+            mib[3] = getpid();
+
+            // Call sysctl.
+
+            size = sizeof(info);
+            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, CATCH_NULL, 0) != 0 ) {
+                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
+                return false;
+            }
+
+            // We're being debugged if the P_TRACED flag is set.
+
+            return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
+        }
+    } // namespace Catch
+
+#elif defined(CATCH_PLATFORM_LINUX)
+    #include <fstream>
+    #include <string>
+
+    namespace Catch{
+        // The standard POSIX way of detecting a debugger is to attempt to
+        // ptrace() the process, but this needs to be done from a child and not
+        // this process itself to still allow attaching to this process later
+        // if wanted, so is rather heavy. Under Linux we have the PID of the
+        // "debugger" (which doesn't need to be gdb, of course, it could also
+        // be strace, for example) in /proc/$PID/status, so just get it from
+        // there instead.
+        bool isDebuggerActive(){
+            // Libstdc++ has a bug, where std::ifstream sets errno to 0
+            // This way our users can properly assert over errno values
+            ErrnoGuard guard;
+            std::ifstream in("/proc/self/status");
+            for( std::string line; std::getline(in, line); ) {
+                static const int PREFIX_LEN = 11;
+                if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
+                    // We're traced if the PID is not 0 and no other PID starts
+                    // with 0 digit, so it's enough to check for just a single
+                    // character.
+                    return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
+                }
+            }
+
+            return false;
+        }
+    } // namespace Catch
+#elif defined(_MSC_VER)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#elif defined(__MINGW32__)
+    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
+    namespace Catch {
+        bool isDebuggerActive() {
+            return IsDebuggerPresent() != 0;
+        }
+    }
+#else
+    namespace Catch {
+       inline bool isDebuggerActive() { return false; }
+    }
+#endif // Platform
+
+#ifdef CATCH_PLATFORM_WINDOWS
+
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            ::OutputDebugStringA( text.c_str() );
+        }
+    }
+#else
+    namespace Catch {
+        void writeToDebugConsole( std::string const& text ) {
+            // !TBD: Need a version for Mac/ XCode and other IDEs
+            Catch::cout() << text;
+        }
+    }
+#endif // Platform
+
+// #included from: catch_tostring.hpp
+#define TWOBLUECUBES_CATCH_TOSTRING_HPP_INCLUDED
+
+namespace Catch {
+
+namespace Detail {
+
+    const std::string unprintableString = "{?}";
+
+    namespace {
+        const int hexThreshold = 255;
+
+        struct Endianness {
+            enum Arch { Big, Little };
+
+            static Arch which() {
+                union _{
+                    int asInt;
+                    char asChar[sizeof (int)];
+                } u;
+
+                u.asInt = 1;
+                return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
+            }
+        };
+    }
+
+    std::string rawMemoryToString( const void *object, std::size_t size )
+    {
+        // Reverse order for little endian architectures
+        int i = 0, end = static_cast<int>( size ), inc = 1;
+        if( Endianness::which() == Endianness::Little ) {
+            i = end-1;
+            end = inc = -1;
+        }
+
+        unsigned char const *bytes = static_cast<unsigned char const *>(object);
+        std::ostringstream os;
+        os << "0x" << std::setfill('0') << std::hex;
+        for( ; i != end; i += inc )
+             os << std::setw(2) << static_cast<unsigned>(bytes[i]);
+       return os.str();
+    }
+}
+
+std::string toString( std::string const& value ) {
+    std::string s = value;
+    if( getCurrentContext().getConfig()->showInvisibles() ) {
+        for(size_t i = 0; i < s.size(); ++i ) {
+            std::string subs;
+            switch( s[i] ) {
+            case '\n': subs = "\\n"; break;
+            case '\t': subs = "\\t"; break;
+            default: break;
+            }
+            if( !subs.empty() ) {
+                s = s.substr( 0, i ) + subs + s.substr( i+1 );
+                ++i;
+            }
+        }
+    }
+    return '"' + s + '"';
+}
+std::string toString( std::wstring const& value ) {
+
+    std::string s;
+    s.reserve( value.size() );
+    for(size_t i = 0; i < value.size(); ++i )
+        s += value[i] <= 0xff ? static_cast<char>( value[i] ) : '?';
+    return Catch::toString( s );
+}
+
+std::string toString( const char* const value ) {
+    return value ? Catch::toString( std::string( value ) ) : std::string( "{null string}" );
+}
+
+std::string toString( char* const value ) {
+    return Catch::toString( static_cast<const char*>( value ) );
+}
+
+std::string toString( const wchar_t* const value )
+{
+    return value ? Catch::toString( std::wstring(value) ) : std::string( "{null string}" );
+}
+
+std::string toString( wchar_t* const value )
+{
+    return Catch::toString( static_cast<const wchar_t*>( value ) );
+}
+
+std::string toString( int value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ')';
+    return oss.str();
+}
+
+std::string toString( unsigned long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ')';
+    return oss.str();
+}
+
+std::string toString( unsigned int value ) {
+    return Catch::toString( static_cast<unsigned long>( value ) );
+}
+
+template<typename T>
+std::string fpToString( T value, int precision ) {
+    std::ostringstream oss;
+    oss << std::setprecision( precision )
+        << std::fixed
+        << value;
+    std::string d = oss.str();
+    std::size_t i = d.find_last_not_of( '0' );
+    if( i != std::string::npos && i != d.size()-1 ) {
+        if( d[i] == '.' )
+            i++;
+        d = d.substr( 0, i+1 );
+    }
+    return d;
+}
+
+std::string toString( const double value ) {
+    return fpToString( value, 10 );
+}
+std::string toString( const float value ) {
+    return fpToString( value, 5 ) + 'f';
+}
+
+std::string toString( bool value ) {
+    return value ? "true" : "false";
+}
+
+std::string toString( char value ) {
+    if ( value == '\r' )
+        return "'\\r'";
+    if ( value == '\f' )
+        return "'\\f'";
+    if ( value == '\n' )
+        return "'\\n'";
+    if ( value == '\t' )
+        return "'\\t'";
+    if ( '\0' <= value && value < ' ' )
+        return toString( static_cast<unsigned int>( value ) );
+    char chstr[] = "' '";
+    chstr[1] = value;
+    return chstr;
+}
+
+std::string toString( signed char value ) {
+    return toString( static_cast<char>( value ) );
+}
+
+std::string toString( unsigned char value ) {
+    return toString( static_cast<char>( value ) );
+}
+
+#ifdef CATCH_CONFIG_CPP11_LONG_LONG
+std::string toString( long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ')';
+    return oss.str();
+}
+std::string toString( unsigned long long value ) {
+    std::ostringstream oss;
+    oss << value;
+    if( value > Detail::hexThreshold )
+        oss << " (0x" << std::hex << value << ')';
+    return oss.str();
+}
+#endif
+
+#ifdef CATCH_CONFIG_CPP11_NULLPTR
+std::string toString( std::nullptr_t ) {
+    return "nullptr";
+}
+#endif
+
+#ifdef __OBJC__
+    std::string toString( NSString const * const& nsstring ) {
+        if( !nsstring )
+            return "nil";
+        return "@" + toString([nsstring UTF8String]);
+    }
+    std::string toString( NSString * CATCH_ARC_STRONG & nsstring ) {
+        if( !nsstring )
+            return "nil";
+        return "@" + toString([nsstring UTF8String]);
+    }
+    std::string toString( NSObject* const& nsObject ) {
+        return toString( [nsObject description] );
+    }
+#endif
+
+} // end namespace Catch
+
+// #included from: catch_result_builder.hpp
+#define TWOBLUECUBES_CATCH_RESULT_BUILDER_HPP_INCLUDED
+
+namespace Catch {
+
+    ResultBuilder::ResultBuilder(   char const* macroName,
+                                    SourceLineInfo const& lineInfo,
+                                    char const* capturedExpression,
+                                    ResultDisposition::Flags resultDisposition,
+                                    char const* secondArg )
+    :   m_assertionInfo( macroName, lineInfo, capturedExpression, resultDisposition, secondArg ),
+        m_shouldDebugBreak( false ),
+        m_shouldThrow( false ),
+        m_guardException( false ),
+        m_usedStream( false )
+    {}
+
+    ResultBuilder::~ResultBuilder() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+        if ( m_guardException ) {
+            stream().oss << "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
+            captureResult( ResultWas::ThrewException );
+            getCurrentContext().getResultCapture()->exceptionEarlyReported();
+        }
+#endif
+    }
+
+    ResultBuilder& ResultBuilder::setResultType( ResultWas::OfType result ) {
+        m_data.resultType = result;
+        return *this;
+    }
+    ResultBuilder& ResultBuilder::setResultType( bool result ) {
+        m_data.resultType = result ? ResultWas::Ok : ResultWas::ExpressionFailed;
+        return *this;
+    }
+
+    void ResultBuilder::endExpression( DecomposedExpression const& expr ) {
+        // Flip bool results if FalseTest flag is set
+        if( isFalseTest( m_assertionInfo.resultDisposition ) ) {
+            m_data.negate( expr.isBinaryExpression() );
+        }
+
+        getResultCapture().assertionRun();
+
+        if(getCurrentContext().getConfig()->includeSuccessfulResults() || m_data.resultType != ResultWas::Ok)
+        {
+            AssertionResult result = build( expr );
+            handleResult( result );
+        }
+        else
+            getResultCapture().assertionPassed();
+    }
+
+    void ResultBuilder::useActiveException( ResultDisposition::Flags resultDisposition ) {
+        m_assertionInfo.resultDisposition = resultDisposition;
+        stream().oss << Catch::translateActiveException();
+        captureResult( ResultWas::ThrewException );
+    }
+
+    void ResultBuilder::captureResult( ResultWas::OfType resultType ) {
+        setResultType( resultType );
+        captureExpression();
+    }
+
+    void ResultBuilder::captureExpectedException( std::string const& expectedMessage ) {
+        if( expectedMessage.empty() )
+            captureExpectedException( Matchers::Impl::MatchAllOf<std::string>() );
+        else
+            captureExpectedException( Matchers::Equals( expectedMessage ) );
+    }
+
+    void ResultBuilder::captureExpectedException( Matchers::Impl::MatcherBase<std::string> const& matcher ) {
+
+        assert( !isFalseTest( m_assertionInfo.resultDisposition ) );
+        AssertionResultData data = m_data;
+        data.resultType = ResultWas::Ok;
+        data.reconstructedExpression = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg);
+
+        std::string actualMessage = Catch::translateActiveException();
+        if( !matcher.match( actualMessage ) ) {
+            data.resultType = ResultWas::ExpressionFailed;
+            data.reconstructedExpression = actualMessage;
+        }
+        AssertionResult result( m_assertionInfo, data );
+        handleResult( result );
+    }
+
+    void ResultBuilder::captureExpression() {
+        AssertionResult result = build();
+        handleResult( result );
+    }
+
+    void ResultBuilder::handleResult( AssertionResult const& result )
+    {
+        getResultCapture().assertionEnded( result );
+
+        if( !result.isOk() ) {
+            if( getCurrentContext().getConfig()->shouldDebugBreak() )
+                m_shouldDebugBreak = true;
+            if( getCurrentContext().getRunner()->aborting() || (m_assertionInfo.resultDisposition & ResultDisposition::Normal) )
+                m_shouldThrow = true;
+        }
+    }
+
+    void ResultBuilder::react() {
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+        if (m_shouldDebugBreak) {
+            ///////////////////////////////////////////////////////////////////
+            // To inspect the state during test, you need to go one level up the callstack
+            // To go back to the test and change execution, jump over the throw statement
+            ///////////////////////////////////////////////////////////////////
+            CATCH_BREAK_INTO_DEBUGGER();
+        }
+#endif
+        if( m_shouldThrow )
+            throw Catch::TestFailureException();
+    }
+
+    bool ResultBuilder::shouldDebugBreak() const { return m_shouldDebugBreak; }
+    bool ResultBuilder::allowThrows() const { return getCurrentContext().getConfig()->allowThrows(); }
+
+    AssertionResult ResultBuilder::build() const
+    {
+        return build( *this );
+    }
+
+    // CAVEAT: The returned AssertionResult stores a pointer to the argument expr,
+    //         a temporary DecomposedExpression, which in turn holds references to
+    //         operands, possibly temporary as well.
+    //         It should immediately be passed to handleResult; if the expression
+    //         needs to be reported, its string expansion must be composed before
+    //         the temporaries are destroyed.
+    AssertionResult ResultBuilder::build( DecomposedExpression const& expr ) const
+    {
+        assert( m_data.resultType != ResultWas::Unknown );
+        AssertionResultData data = m_data;
+
+        if(m_usedStream)
+            data.message = m_stream().oss.str();
+        data.decomposedExpression = &expr; // for lazy reconstruction
+        return AssertionResult( m_assertionInfo, data );
+    }
+
+    void ResultBuilder::reconstructExpression( std::string& dest ) const {
+        dest = capturedExpressionWithSecondArgument(m_assertionInfo.capturedExpression, m_assertionInfo.secondArg);
+    }
+
+    void ResultBuilder::setExceptionGuard() {
+        m_guardException = true;
+    }
+    void ResultBuilder::unsetExceptionGuard() {
+        m_guardException = false;
+    }
+
+} // end namespace Catch
+
+// #included from: catch_tag_alias_registry.hpp
+#define TWOBLUECUBES_CATCH_TAG_ALIAS_REGISTRY_HPP_INCLUDED
+
+namespace Catch {
+
+    TagAliasRegistry::~TagAliasRegistry() {}
+
+    Option<TagAlias> TagAliasRegistry::find( std::string const& alias ) const {
+        std::map<std::string, TagAlias>::const_iterator it = m_registry.find( alias );
+        if( it != m_registry.end() )
+            return it->second;
+        else
+            return Option<TagAlias>();
+    }
+
+    std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
+        std::string expandedTestSpec = unexpandedTestSpec;
+        for( std::map<std::string, TagAlias>::const_iterator it = m_registry.begin(), itEnd = m_registry.end();
+                it != itEnd;
+                ++it ) {
+            std::size_t pos = expandedTestSpec.find( it->first );
+            if( pos != std::string::npos ) {
+                expandedTestSpec =  expandedTestSpec.substr( 0, pos ) +
+                                    it->second.tag +
+                                    expandedTestSpec.substr( pos + it->first.size() );
+            }
+        }
+        return expandedTestSpec;
+    }
+
+    void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
+
+        if( !startsWith( alias, "[@" ) || !endsWith( alias, ']' ) ) {
+            std::ostringstream oss;
+            oss << Colour( Colour::Red )
+                << "error: tag alias, \"" << alias << "\" is not of the form [@alias name].\n"
+                << Colour( Colour::FileName )
+                << lineInfo << '\n';
+            throw std::domain_error( oss.str().c_str() );
+        }
+        if( !m_registry.insert( std::make_pair( alias, TagAlias( tag, lineInfo ) ) ).second ) {
+            std::ostringstream oss;
+            oss << Colour( Colour::Red )
+                << "error: tag alias, \"" << alias << "\" already registered.\n"
+                << "\tFirst seen at "
+                << Colour( Colour::Red ) << find(alias)->lineInfo << '\n'
+                << Colour( Colour::Red ) << "\tRedefined at "
+                << Colour( Colour::FileName) << lineInfo << '\n';
+            throw std::domain_error( oss.str().c_str() );
+        }
+    }
+
+    ITagAliasRegistry::~ITagAliasRegistry() {}
+
+    ITagAliasRegistry const& ITagAliasRegistry::get() {
+        return getRegistryHub().getTagAliasRegistry();
+    }
+
+    RegistrarForTagAliases::RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ) {
+        getMutableRegistryHub().registerTagAlias( alias, tag, lineInfo );
+    }
+
+} // end namespace Catch
+
+// #included from: catch_matchers_string.hpp
+
+namespace Catch {
+namespace Matchers {
+
+    namespace StdString {
+
+        CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
+        :   m_caseSensitivity( caseSensitivity ),
+            m_str( adjustString( str ) )
+        {}
+        std::string CasedString::adjustString( std::string const& str ) const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? toLower( str )
+                   : str;
+        }
+        std::string CasedString::caseSensitivitySuffix() const {
+            return m_caseSensitivity == CaseSensitive::No
+                   ? " (case insensitive)"
+                   : std::string();
+        }
+
+        StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
+        : m_comparator( comparator ),
+          m_operation( operation ) {
+        }
+
+        std::string StringMatcherBase::describe() const {
+            std::string description;
+            description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
+                                        m_comparator.caseSensitivitySuffix().size());
+            description += m_operation;
+            description += ": \"";
+            description += m_comparator.m_str;
+            description += "\"";
+            description += m_comparator.caseSensitivitySuffix();
+            return description;
+        }
+
+        EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
+
+        bool EqualsMatcher::match( std::string const& source ) const {
+            return m_comparator.adjustString( source ) == m_comparator.m_str;
+        }
+
+        ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
+
+        bool ContainsMatcher::match( std::string const& source ) const {
+            return contains( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
+
+        bool StartsWithMatcher::match( std::string const& source ) const {
+            return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+        EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
+
+        bool EndsWithMatcher::match( std::string const& source ) const {
+            return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
+        }
+
+    } // namespace StdString
+
+    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
+        return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
+    }
+
+} // namespace Matchers
+} // namespace Catch
+// #included from: ../reporters/catch_reporter_multi.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_MULTI_HPP_INCLUDED
+
+namespace Catch {
+
+class MultipleReporters : public SharedImpl<IStreamingReporter> {
+    typedef std::vector<Ptr<IStreamingReporter> > Reporters;
+    Reporters m_reporters;
+
+public:
+    void add( Ptr<IStreamingReporter> const& reporter ) {
+        m_reporters.push_back( reporter );
+    }
+
+public: // IStreamingReporter
+
+    virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+        return m_reporters[0]->getPreferences();
+    }
+
+    virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->noMatchingTestCases( spec );
+    }
+
+    virtual void testRunStarting( TestRunInfo const& testRunInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunStarting( testRunInfo );
+    }
+
+    virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupStarting( groupInfo );
+    }
+
+    virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseStarting( testInfo );
+    }
+
+    virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionStarting( sectionInfo );
+    }
+
+    virtual void assertionStarting( AssertionInfo const& assertionInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->assertionStarting( assertionInfo );
+    }
+
+    // The return value indicates if the messages buffer should be cleared:
+    virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+        bool clearBuffer = false;
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            clearBuffer |= (*it)->assertionEnded( assertionStats );
+        return clearBuffer;
+    }
+
+    virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->sectionEnded( sectionStats );
+    }
+
+    virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testCaseEnded( testCaseStats );
+    }
+
+    virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testGroupEnded( testGroupStats );
+    }
+
+    virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->testRunEnded( testRunStats );
+    }
+
+    virtual void skipTest( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+        for( Reporters::const_iterator it = m_reporters.begin(), itEnd = m_reporters.end();
+                it != itEnd;
+                ++it )
+            (*it)->skipTest( testInfo );
+    }
+
+    virtual MultipleReporters* tryAsMulti() CATCH_OVERRIDE {
+        return this;
+    }
+
+};
+
+Ptr<IStreamingReporter> addReporter( Ptr<IStreamingReporter> const& existingReporter, Ptr<IStreamingReporter> const& additionalReporter ) {
+    Ptr<IStreamingReporter> resultingReporter;
+
+    if( existingReporter ) {
+        MultipleReporters* multi = existingReporter->tryAsMulti();
+        if( !multi ) {
+            multi = new MultipleReporters;
+            resultingReporter = Ptr<IStreamingReporter>( multi );
+            if( existingReporter )
+                multi->add( existingReporter );
+        }
+        else
+            resultingReporter = existingReporter;
+        multi->add( additionalReporter );
+    }
+    else
+        resultingReporter = additionalReporter;
+
+    return resultingReporter;
+}
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_xml.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_XML_HPP_INCLUDED
+
+// #included from: catch_reporter_bases.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_BASES_HPP_INCLUDED
+
+#include <cstring>
+#include <cfloat>
+#include <cstdio>
+#include <assert.h>
+
+namespace Catch {
+
+    namespace {
+        // Because formatting using c++ streams is stateful, drop down to C is required
+        // Alternatively we could use stringstream, but its performance is... not good.
+        std::string getFormattedDuration( double duration ) {
+            // Max exponent + 1 is required to represent the whole part
+            // + 1 for decimal point
+            // + 3 for the 3 decimal places
+            // + 1 for null terminator
+            const size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
+            char buffer[maxDoubleSize];
+
+            // Save previous errno, to prevent sprintf from overwriting it
+            ErrnoGuard guard;
+#ifdef _MSC_VER
+            sprintf_s(buffer, "%.3f", duration);
+#else
+            sprintf(buffer, "%.3f", duration);
+#endif
+            return std::string(buffer);
+        }
+    }
+
+    struct StreamingReporterBase : SharedImpl<IStreamingReporter> {
+
+        StreamingReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
+
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
+
+        virtual ~StreamingReporterBase() CATCH_OVERRIDE;
+
+        virtual void noMatchingTestCases( std::string const& ) CATCH_OVERRIDE {}
+
+        virtual void testRunStarting( TestRunInfo const& _testRunInfo ) CATCH_OVERRIDE {
+            currentTestRunInfo = _testRunInfo;
+        }
+        virtual void testGroupStarting( GroupInfo const& _groupInfo ) CATCH_OVERRIDE {
+            currentGroupInfo = _groupInfo;
+        }
+
+        virtual void testCaseStarting( TestCaseInfo const& _testInfo ) CATCH_OVERRIDE {
+            currentTestCaseInfo = _testInfo;
+        }
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+            m_sectionStack.push_back( _sectionInfo );
+        }
+
+        virtual void sectionEnded( SectionStats const& /* _sectionStats */ ) CATCH_OVERRIDE {
+            m_sectionStack.pop_back();
+        }
+        virtual void testCaseEnded( TestCaseStats const& /* _testCaseStats */ ) CATCH_OVERRIDE {
+            currentTestCaseInfo.reset();
+        }
+        virtual void testGroupEnded( TestGroupStats const& /* _testGroupStats */ ) CATCH_OVERRIDE {
+            currentGroupInfo.reset();
+        }
+        virtual void testRunEnded( TestRunStats const& /* _testRunStats */ ) CATCH_OVERRIDE {
+            currentTestCaseInfo.reset();
+            currentGroupInfo.reset();
+            currentTestRunInfo.reset();
+        }
+
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {
+            // Don't do anything with this by default.
+            // It can optionally be overridden in the derived class.
+        }
+
+        Ptr<IConfig const> m_config;
+        std::ostream& stream;
+
+        LazyStat<TestRunInfo> currentTestRunInfo;
+        LazyStat<GroupInfo> currentGroupInfo;
+        LazyStat<TestCaseInfo> currentTestCaseInfo;
+
+        std::vector<SectionInfo> m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
+    };
+
+    struct CumulativeReporterBase : SharedImpl<IStreamingReporter> {
+        template<typename T, typename ChildNodeT>
+        struct Node : SharedImpl<> {
+            explicit Node( T const& _value ) : value( _value ) {}
+            virtual ~Node() {}
+
+            typedef std::vector<Ptr<ChildNodeT> > ChildNodes;
+            T value;
+            ChildNodes children;
+        };
+        struct SectionNode : SharedImpl<> {
+            explicit SectionNode( SectionStats const& _stats ) : stats( _stats ) {}
+            virtual ~SectionNode();
+
+            bool operator == ( SectionNode const& other ) const {
+                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
+            }
+            bool operator == ( Ptr<SectionNode> const& other ) const {
+                return operator==( *other );
+            }
+
+            SectionStats stats;
+            typedef std::vector<Ptr<SectionNode> > ChildSections;
+            typedef std::vector<AssertionStats> Assertions;
+            ChildSections childSections;
+            Assertions assertions;
+            std::string stdOut;
+            std::string stdErr;
+        };
+
+        struct BySectionInfo {
+            BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
+            BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
+            bool operator() ( Ptr<SectionNode> const& node ) const {
+                return ((node->stats.sectionInfo.name == m_other.name) &&
+                        (node->stats.sectionInfo.lineInfo == m_other.lineInfo));
+            }
+        private:
+            void operator=( BySectionInfo const& );
+            SectionInfo const& m_other;
+        };
+
+        typedef Node<TestCaseStats, SectionNode> TestCaseNode;
+        typedef Node<TestGroupStats, TestCaseNode> TestGroupNode;
+        typedef Node<TestRunStats, TestGroupNode> TestRunNode;
+
+        CumulativeReporterBase( ReporterConfig const& _config )
+        :   m_config( _config.fullConfig() ),
+            stream( _config.stream() )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = false;
+        }
+        ~CumulativeReporterBase();
+
+        virtual ReporterPreferences getPreferences() const CATCH_OVERRIDE {
+            return m_reporterPrefs;
+        }
+
+        virtual void testRunStarting( TestRunInfo const& ) CATCH_OVERRIDE {}
+        virtual void testGroupStarting( GroupInfo const& ) CATCH_OVERRIDE {}
+
+        virtual void testCaseStarting( TestCaseInfo const& ) CATCH_OVERRIDE {}
+
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+            SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
+            Ptr<SectionNode> node;
+            if( m_sectionStack.empty() ) {
+                if( !m_rootSection )
+                    m_rootSection = new SectionNode( incompleteStats );
+                node = m_rootSection;
+            }
+            else {
+                SectionNode& parentNode = *m_sectionStack.back();
+                SectionNode::ChildSections::const_iterator it =
+                    std::find_if(   parentNode.childSections.begin(),
+                                    parentNode.childSections.end(),
+                                    BySectionInfo( sectionInfo ) );
+                if( it == parentNode.childSections.end() ) {
+                    node = new SectionNode( incompleteStats );
+                    parentNode.childSections.push_back( node );
+                }
+                else
+                    node = *it;
+            }
+            m_sectionStack.push_back( node );
+            m_deepestSection = node;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+            assert( !m_sectionStack.empty() );
+            SectionNode& sectionNode = *m_sectionStack.back();
+            sectionNode.assertions.push_back( assertionStats );
+            // AssertionResult holds a pointer to a temporary DecomposedExpression,
+            // which getExpandedExpression() calls to build the expression string.
+            // Our section stack copy of the assertionResult will likely outlive the
+            // temporary, so it must be expanded or discarded now to avoid calling
+            // a destroyed object later.
+            prepareExpandedExpression( sectionNode.assertions.back().assertionResult );
+            return true;
+        }
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+            assert( !m_sectionStack.empty() );
+            SectionNode& node = *m_sectionStack.back();
+            node.stats = sectionStats;
+            m_sectionStack.pop_back();
+        }
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+            Ptr<TestCaseNode> node = new TestCaseNode( testCaseStats );
+            assert( m_sectionStack.size() == 0 );
+            node->children.push_back( m_rootSection );
+            m_testCases.push_back( node );
+            m_rootSection.reset();
+
+            assert( m_deepestSection );
+            m_deepestSection->stdOut = testCaseStats.stdOut;
+            m_deepestSection->stdErr = testCaseStats.stdErr;
+        }
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+            Ptr<TestGroupNode> node = new TestGroupNode( testGroupStats );
+            node->children.swap( m_testCases );
+            m_testGroups.push_back( node );
+        }
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+            Ptr<TestRunNode> node = new TestRunNode( testRunStats );
+            node->children.swap( m_testGroups );
+            m_testRuns.push_back( node );
+            testRunEndedCumulative();
+        }
+        virtual void testRunEndedCumulative() = 0;
+
+        virtual void skipTest( TestCaseInfo const& ) CATCH_OVERRIDE {}
+
+        virtual void prepareExpandedExpression( AssertionResult& result ) const {
+            if( result.isOk() )
+                result.discardDecomposedExpression();
+            else
+                result.expandDecomposedExpression();
+        }
+
+        Ptr<IConfig const> m_config;
+        std::ostream& stream;
+        std::vector<AssertionStats> m_assertions;
+        std::vector<std::vector<Ptr<SectionNode> > > m_sections;
+        std::vector<Ptr<TestCaseNode> > m_testCases;
+        std::vector<Ptr<TestGroupNode> > m_testGroups;
+
+        std::vector<Ptr<TestRunNode> > m_testRuns;
+
+        Ptr<SectionNode> m_rootSection;
+        Ptr<SectionNode> m_deepestSection;
+        std::vector<Ptr<SectionNode> > m_sectionStack;
+        ReporterPreferences m_reporterPrefs;
+
+    };
+
+    template<char C>
+    char const* getLineOfChars() {
+        static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
+        if( !*line ) {
+            std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
+            line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
+        }
+        return line;
+    }
+
+    struct TestEventListenerBase : StreamingReporterBase {
+        TestEventListenerBase( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config )
+        {}
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {}
+        virtual bool assertionEnded( AssertionStats const& ) CATCH_OVERRIDE {
+            return false;
+        }
+    };
+
+} // end namespace Catch
+
+// #included from: ../internal/catch_reporter_registrars.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_REGISTRARS_HPP_INCLUDED
+
+namespace Catch {
+
+    template<typename T>
+    class LegacyReporterRegistrar {
+
+        class ReporterFactory : public IReporterFactory {
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new LegacyReporterAdapter( new T( config ) );
+            }
+
+            virtual std::string getDescription() const {
+                return T::getDescription();
+            }
+        };
+
+    public:
+
+        LegacyReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+        }
+    };
+
+    template<typename T>
+    class ReporterRegistrar {
+
+        class ReporterFactory : public SharedImpl<IReporterFactory> {
+
+            // *** Please Note ***:
+            // - If you end up here looking at a compiler error because it's trying to register
+            // your custom reporter class be aware that the native reporter interface has changed
+            // to IStreamingReporter. The "legacy" interface, IReporter, is still supported via
+            // an adapter. Just use REGISTER_LEGACY_REPORTER to take advantage of the adapter.
+            // However please consider updating to the new interface as the old one is now
+            // deprecated and will probably be removed quite soon!
+            // Please contact me via github if you have any questions at all about this.
+            // In fact, ideally, please contact me anyway to let me know you've hit this - as I have
+            // no idea who is actually using custom reporters at all (possibly no-one!).
+            // The new interface is designed to minimise exposure to interface changes in the future.
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new T( config );
+            }
+
+            virtual std::string getDescription() const {
+                return T::getDescription();
+            }
+        };
+
+    public:
+
+        ReporterRegistrar( std::string const& name ) {
+            getMutableRegistryHub().registerReporter( name, new ReporterFactory() );
+        }
+    };
+
+    template<typename T>
+    class ListenerRegistrar {
+
+        class ListenerFactory : public SharedImpl<IReporterFactory> {
+
+            virtual IStreamingReporter* create( ReporterConfig const& config ) const {
+                return new T( config );
+            }
+            virtual std::string getDescription() const {
+                return std::string();
+            }
+        };
+
+    public:
+
+        ListenerRegistrar() {
+            getMutableRegistryHub().registerListener( new ListenerFactory() );
+        }
+    };
+}
+
+#define INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) \
+    namespace{ Catch::LegacyReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+#define INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType ) \
+    namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); }
+
+// Deprecated - use the form without INTERNAL_
+#define INTERNAL_CATCH_REGISTER_LISTENER( listenerType ) \
+    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
+#define CATCH_REGISTER_LISTENER( listenerType ) \
+    namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; }
+
+// #included from: ../internal/catch_xmlwriter.hpp
+#define TWOBLUECUBES_CATCH_XMLWRITER_HPP_INCLUDED
+
+#include <sstream>
+#include <string>
+#include <vector>
+#include <iomanip>
+
+namespace Catch {
+
+    class XmlEncode {
+    public:
+        enum ForWhat { ForTextNodes, ForAttributes };
+
+        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes )
+        :   m_str( str ),
+            m_forWhat( forWhat )
+        {}
+
+        void encodeTo( std::ostream& os ) const {
+
+            // Apostrophe escaping not necessary if we always use " to write attributes
+            // (see: http://www.w3.org/TR/xml/#syntax)
+
+            for( std::size_t i = 0; i < m_str.size(); ++ i ) {
+                char c = m_str[i];
+                switch( c ) {
+                    case '<':   os << "&lt;"; break;
+                    case '&':   os << "&amp;"; break;
+
+                    case '>':
+                        // See: http://www.w3.org/TR/xml/#syntax
+                        if( i > 2 && m_str[i-1] == ']' && m_str[i-2] == ']' )
+                            os << "&gt;";
+                        else
+                            os << c;
+                        break;
+
+                    case '\"':
+                        if( m_forWhat == ForAttributes )
+                            os << "&quot;";
+                        else
+                            os << c;
+                        break;
+
+                    default:
+                        // Escape control chars - based on contribution by @espenalb in PR #465 and
+                        // by @mrpi PR #588
+                        if ( ( c >= 0 && c < '\x09' ) || ( c > '\x0D' && c < '\x20') || c=='\x7F' ) {
+                            // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
+                            os << "\\x" << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
+                               << static_cast<int>( c );
+                        }
+                        else
+                            os << c;
+                }
+            }
+        }
+
+        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
+            xmlEncode.encodeTo( os );
+            return os;
+        }
+
+    private:
+        std::string m_str;
+        ForWhat m_forWhat;
+    };
+
+    class XmlWriter {
+    public:
+
+        class ScopedElement {
+        public:
+            ScopedElement( XmlWriter* writer )
+            :   m_writer( writer )
+            {}
+
+            ScopedElement( ScopedElement const& other )
+            :   m_writer( other.m_writer ){
+                other.m_writer = CATCH_NULL;
+            }
+
+            ~ScopedElement() {
+                if( m_writer )
+                    m_writer->endElement();
+            }
+
+            ScopedElement& writeText( std::string const& text, bool indent = true ) {
+                m_writer->writeText( text, indent );
+                return *this;
+            }
+
+            template<typename T>
+            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
+                m_writer->writeAttribute( name, attribute );
+                return *this;
+            }
+
+        private:
+            mutable XmlWriter* m_writer;
+        };
+
+        XmlWriter()
+        :   m_tagIsOpen( false ),
+            m_needsNewline( false ),
+            m_os( Catch::cout() )
+        {
+            writeDeclaration();
+        }
+
+        XmlWriter( std::ostream& os )
+        :   m_tagIsOpen( false ),
+            m_needsNewline( false ),
+            m_os( os )
+        {
+            writeDeclaration();
+        }
+
+        ~XmlWriter() {
+            while( !m_tags.empty() )
+                endElement();
+        }
+
+        XmlWriter& startElement( std::string const& name ) {
+            ensureTagClosed();
+            newlineIfNecessary();
+            m_os << m_indent << '<' << name;
+            m_tags.push_back( name );
+            m_indent += "  ";
+            m_tagIsOpen = true;
+            return *this;
+        }
+
+        ScopedElement scopedElement( std::string const& name ) {
+            ScopedElement scoped( this );
+            startElement( name );
+            return scoped;
+        }
+
+        XmlWriter& endElement() {
+            newlineIfNecessary();
+            m_indent = m_indent.substr( 0, m_indent.size()-2 );
+            if( m_tagIsOpen ) {
+                m_os << "/>";
+                m_tagIsOpen = false;
+            }
+            else {
+                m_os << m_indent << "</" << m_tags.back() << ">";
+            }
+            m_os << std::endl;
+            m_tags.pop_back();
+            return *this;
+        }
+
+        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute ) {
+            if( !name.empty() && !attribute.empty() )
+                m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
+            return *this;
+        }
+
+        XmlWriter& writeAttribute( std::string const& name, bool attribute ) {
+            m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
+            return *this;
+        }
+
+        template<typename T>
+        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
+            std::ostringstream oss;
+            oss << attribute;
+            return writeAttribute( name, oss.str() );
+        }
+
+        XmlWriter& writeText( std::string const& text, bool indent = true ) {
+            if( !text.empty() ){
+                bool tagWasOpen = m_tagIsOpen;
+                ensureTagClosed();
+                if( tagWasOpen && indent )
+                    m_os << m_indent;
+                m_os << XmlEncode( text );
+                m_needsNewline = true;
+            }
+            return *this;
+        }
+
+        XmlWriter& writeComment( std::string const& text ) {
+            ensureTagClosed();
+            m_os << m_indent << "<!--" << text << "-->";
+            m_needsNewline = true;
+            return *this;
+        }
+
+        void writeStylesheetRef( std::string const& url ) {
+            m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
+        }
+
+        XmlWriter& writeBlankLine() {
+            ensureTagClosed();
+            m_os << '\n';
+            return *this;
+        }
+
+        void ensureTagClosed() {
+            if( m_tagIsOpen ) {
+                m_os << ">" << std::endl;
+                m_tagIsOpen = false;
+            }
+        }
+
+    private:
+        XmlWriter( XmlWriter const& );
+        void operator=( XmlWriter const& );
+
+        void writeDeclaration() {
+            m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
+        }
+
+        void newlineIfNecessary() {
+            if( m_needsNewline ) {
+                m_os << std::endl;
+                m_needsNewline = false;
+            }
+        }
+
+        bool m_tagIsOpen;
+        bool m_needsNewline;
+        std::vector<std::string> m_tags;
+        std::string m_indent;
+        std::ostream& m_os;
+    };
+
+}
+
+namespace Catch {
+    class XmlReporter : public StreamingReporterBase {
+    public:
+        XmlReporter( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config ),
+            m_xml(_config.stream()),
+            m_sectionDepth( 0 )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
+
+        virtual ~XmlReporter() CATCH_OVERRIDE;
+
+        static std::string getDescription() {
+            return "Reports test results as an XML document";
+        }
+
+        virtual std::string getStylesheetRef() const {
+            return std::string();
+        }
+
+        void writeSourceInfo( SourceLineInfo const& sourceInfo ) {
+            m_xml
+                .writeAttribute( "filename", sourceInfo.file )
+                .writeAttribute( "line", sourceInfo.line );
+        }
+
+    public: // StreamingReporterBase
+
+        virtual void noMatchingTestCases( std::string const& s ) CATCH_OVERRIDE {
+            StreamingReporterBase::noMatchingTestCases( s );
+        }
+
+        virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
+            StreamingReporterBase::testRunStarting( testInfo );
+            std::string stylesheetRef = getStylesheetRef();
+            if( !stylesheetRef.empty() )
+                m_xml.writeStylesheetRef( stylesheetRef );
+            m_xml.startElement( "Catch" );
+            if( !m_config->name().empty() )
+                m_xml.writeAttribute( "name", m_config->name() );
+        }
+
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+            StreamingReporterBase::testGroupStarting( groupInfo );
+            m_xml.startElement( "Group" )
+                .writeAttribute( "name", groupInfo.name );
+        }
+
+        virtual void testCaseStarting( TestCaseInfo const& testInfo ) CATCH_OVERRIDE {
+            StreamingReporterBase::testCaseStarting(testInfo);
+            m_xml.startElement( "TestCase" )
+                .writeAttribute( "name", trim( testInfo.name ) )
+                .writeAttribute( "description", testInfo.description )
+                .writeAttribute( "tags", testInfo.tagsAsString );
+
+            writeSourceInfo( testInfo.lineInfo );
+
+            if ( m_config->showDurations() == ShowDurations::Always )
+                m_testCaseTimer.start();
+            m_xml.ensureTagClosed();
+        }
+
+        virtual void sectionStarting( SectionInfo const& sectionInfo ) CATCH_OVERRIDE {
+            StreamingReporterBase::sectionStarting( sectionInfo );
+            if( m_sectionDepth++ > 0 ) {
+                m_xml.startElement( "Section" )
+                    .writeAttribute( "name", trim( sectionInfo.name ) )
+                    .writeAttribute( "description", sectionInfo.description );
+                writeSourceInfo( sectionInfo.lineInfo );
+                m_xml.ensureTagClosed();
+            }
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE { }
+
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+
+            AssertionResult const& result = assertionStats.assertionResult;
+
+            bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+            if( includeResults ) {
+                // Print any info messages in <Info> tags.
+                for( std::vector<MessageInfo>::const_iterator it = assertionStats.infoMessages.begin(), itEnd = assertionStats.infoMessages.end();
+                     it != itEnd;
+                     ++it ) {
+                    if( it->type == ResultWas::Info ) {
+                        m_xml.scopedElement( "Info" )
+                                .writeText( it->message );
+                    } else if ( it->type == ResultWas::Warning ) {
+                        m_xml.scopedElement( "Warning" )
+                                .writeText( it->message );
+                    }
+                }
+            }
+
+            // Drop out if result was successful but we're not printing them.
+            if( !includeResults && result.getResultType() != ResultWas::Warning )
+                return true;
+
+            // Print the expression if there is one.
+            if( result.hasExpression() ) {
+                m_xml.startElement( "Expression" )
+                    .writeAttribute( "success", result.succeeded() )
+                    .writeAttribute( "type", result.getTestMacroName() );
+
+                writeSourceInfo( result.getSourceInfo() );
+
+                m_xml.scopedElement( "Original" )
+                    .writeText( result.getExpression() );
+                m_xml.scopedElement( "Expanded" )
+                    .writeText( result.getExpandedExpression() );
+            }
+
+            // And... Print a result applicable to each result type.
+            switch( result.getResultType() ) {
+                case ResultWas::ThrewException:
+                    m_xml.startElement( "Exception" );
+                    writeSourceInfo( result.getSourceInfo() );
+                    m_xml.writeText( result.getMessage() );
+                    m_xml.endElement();
+                    break;
+                case ResultWas::FatalErrorCondition:
+                    m_xml.startElement( "FatalErrorCondition" );
+                    writeSourceInfo( result.getSourceInfo() );
+                    m_xml.writeText( result.getMessage() );
+                    m_xml.endElement();
+                    break;
+                case ResultWas::Info:
+                    m_xml.scopedElement( "Info" )
+                        .writeText( result.getMessage() );
+                    break;
+                case ResultWas::Warning:
+                    // Warning will already have been written
+                    break;
+                case ResultWas::ExplicitFailure:
+                    m_xml.startElement( "Failure" );
+                    writeSourceInfo( result.getSourceInfo() );
+                    m_xml.writeText( result.getMessage() );
+                    m_xml.endElement();
+                    break;
+                default:
+                    break;
+            }
+
+            if( result.hasExpression() )
+                m_xml.endElement();
+
+            return true;
+        }
+
+        virtual void sectionEnded( SectionStats const& sectionStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::sectionEnded( sectionStats );
+            if( --m_sectionDepth > 0 ) {
+                XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
+                e.writeAttribute( "successes", sectionStats.assertions.passed );
+                e.writeAttribute( "failures", sectionStats.assertions.failed );
+                e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
+
+                if ( m_config->showDurations() == ShowDurations::Always )
+                    e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
+
+                m_xml.endElement();
+            }
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::testCaseEnded( testCaseStats );
+            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
+            e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
+
+            if ( m_config->showDurations() == ShowDurations::Always )
+                e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
+
+            if( !testCaseStats.stdOut.empty() )
+                m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
+            if( !testCaseStats.stdErr.empty() )
+                m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
+
+            m_xml.endElement();
+        }
+
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::testGroupEnded( testGroupStats );
+            // TODO: Check testGroupStats.aborting and act accordingly.
+            m_xml.scopedElement( "OverallResults" )
+                .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
+                .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
+                .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
+            m_xml.endElement();
+        }
+
+        virtual void testRunEnded( TestRunStats const& testRunStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::testRunEnded( testRunStats );
+            m_xml.scopedElement( "OverallResults" )
+                .writeAttribute( "successes", testRunStats.totals.assertions.passed )
+                .writeAttribute( "failures", testRunStats.totals.assertions.failed )
+                .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
+            m_xml.endElement();
+        }
+
+    private:
+        Timer m_testCaseTimer;
+        XmlWriter m_xml;
+        int m_sectionDepth;
+    };
+
+     INTERNAL_CATCH_REGISTER_REPORTER( "xml", XmlReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_junit.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_JUNIT_HPP_INCLUDED
+
+#include <assert.h>
+
+namespace Catch {
+
+    namespace {
+        std::string getCurrentTimestamp() {
+            // Beware, this is not reentrant because of backward compatibility issues
+            // Also, UTC only, again because of backward compatibility (%z is C++11)
+            time_t rawtime;
+            std::time(&rawtime);
+            const size_t timeStampSize = sizeof("2017-01-16T17:06:45Z");
+
+#ifdef _MSC_VER
+            std::tm timeInfo = {};
+            gmtime_s(&timeInfo, &rawtime);
+#else
+            std::tm* timeInfo;
+            timeInfo = std::gmtime(&rawtime);
+#endif
+
+            char timeStamp[timeStampSize];
+            const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
+
+#ifdef _MSC_VER
+            std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
+#else
+            std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
+#endif
+            return std::string(timeStamp);
+        }
+
+    }
+
+    class JunitReporter : public CumulativeReporterBase {
+    public:
+        JunitReporter( ReporterConfig const& _config )
+        :   CumulativeReporterBase( _config ),
+            xml( _config.stream() ),
+            unexpectedExceptions( 0 ),
+            m_okToFail( false )
+        {
+            m_reporterPrefs.shouldRedirectStdOut = true;
+        }
+
+        virtual ~JunitReporter() CATCH_OVERRIDE;
+
+        static std::string getDescription() {
+            return "Reports test results in an XML format that looks like Ant's junitreport target";
+        }
+
+        virtual void noMatchingTestCases( std::string const& /*spec*/ ) CATCH_OVERRIDE {}
+
+        virtual void testRunStarting( TestRunInfo const& runInfo ) CATCH_OVERRIDE {
+            CumulativeReporterBase::testRunStarting( runInfo );
+            xml.startElement( "testsuites" );
+        }
+
+        virtual void testGroupStarting( GroupInfo const& groupInfo ) CATCH_OVERRIDE {
+            suiteTimer.start();
+            stdOutForSuite.str("");
+            stdErrForSuite.str("");
+            unexpectedExceptions = 0;
+            CumulativeReporterBase::testGroupStarting( groupInfo );
+        }
+
+        virtual void testCaseStarting( TestCaseInfo const& testCaseInfo ) CATCH_OVERRIDE {
+            m_okToFail = testCaseInfo.okToFail();
+        }
+        virtual bool assertionEnded( AssertionStats const& assertionStats ) CATCH_OVERRIDE {
+            if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
+                unexpectedExceptions++;
+            return CumulativeReporterBase::assertionEnded( assertionStats );
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) CATCH_OVERRIDE {
+            stdOutForSuite << testCaseStats.stdOut;
+            stdErrForSuite << testCaseStats.stdErr;
+            CumulativeReporterBase::testCaseEnded( testCaseStats );
+        }
+
+        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) CATCH_OVERRIDE {
+            double suiteTime = suiteTimer.getElapsedSeconds();
+            CumulativeReporterBase::testGroupEnded( testGroupStats );
+            writeGroup( *m_testGroups.back(), suiteTime );
+        }
+
+        virtual void testRunEndedCumulative() CATCH_OVERRIDE {
+            xml.endElement();
+        }
+
+        void writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
+            XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
+            TestGroupStats const& stats = groupNode.value;
+            xml.writeAttribute( "name", stats.groupInfo.name );
+            xml.writeAttribute( "errors", unexpectedExceptions );
+            xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
+            xml.writeAttribute( "tests", stats.totals.assertions.total() );
+            xml.writeAttribute( "hostname", "tbd" ); // !TBD
+            if( m_config->showDurations() == ShowDurations::Never )
+                xml.writeAttribute( "time", "" );
+            else
+                xml.writeAttribute( "time", suiteTime );
+            xml.writeAttribute( "timestamp", getCurrentTimestamp() );
+
+            // Write test cases
+            for( TestGroupNode::ChildNodes::const_iterator
+                    it = groupNode.children.begin(), itEnd = groupNode.children.end();
+                    it != itEnd;
+                    ++it )
+                writeTestCase( **it );
+
+            xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite.str() ), false );
+            xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite.str() ), false );
+        }
+
+        void writeTestCase( TestCaseNode const& testCaseNode ) {
+            TestCaseStats const& stats = testCaseNode.value;
+
+            // All test cases have exactly one section - which represents the
+            // test case itself. That section may have 0-n nested sections
+            assert( testCaseNode.children.size() == 1 );
+            SectionNode const& rootSection = *testCaseNode.children.front();
+
+            std::string className = stats.testInfo.className;
+
+            if( className.empty() ) {
+                if( rootSection.childSections.empty() )
+                    className = "global";
+            }
+            writeSection( className, "", rootSection );
+        }
+
+        void writeSection(  std::string const& className,
+                            std::string const& rootName,
+                            SectionNode const& sectionNode ) {
+            std::string name = trim( sectionNode.stats.sectionInfo.name );
+            if( !rootName.empty() )
+                name = rootName + '/' + name;
+
+            if( !sectionNode.assertions.empty() ||
+                !sectionNode.stdOut.empty() ||
+                !sectionNode.stdErr.empty() ) {
+                XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
+                if( className.empty() ) {
+                    xml.writeAttribute( "classname", name );
+                    xml.writeAttribute( "name", "root" );
+                }
+                else {
+                    xml.writeAttribute( "classname", className );
+                    xml.writeAttribute( "name", name );
+                }
+                xml.writeAttribute( "time", Catch::toString( sectionNode.stats.durationInSeconds ) );
+
+                writeAssertions( sectionNode );
+
+                if( !sectionNode.stdOut.empty() )
+                    xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
+                if( !sectionNode.stdErr.empty() )
+                    xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
+            }
+            for( SectionNode::ChildSections::const_iterator
+                    it = sectionNode.childSections.begin(),
+                    itEnd = sectionNode.childSections.end();
+                    it != itEnd;
+                    ++it )
+                if( className.empty() )
+                    writeSection( name, "", **it );
+                else
+                    writeSection( className, name, **it );
+        }
+
+        void writeAssertions( SectionNode const& sectionNode ) {
+            for( SectionNode::Assertions::const_iterator
+                    it = sectionNode.assertions.begin(), itEnd = sectionNode.assertions.end();
+                    it != itEnd;
+                    ++it )
+                writeAssertion( *it );
+        }
+        void writeAssertion( AssertionStats const& stats ) {
+            AssertionResult const& result = stats.assertionResult;
+            if( !result.isOk() ) {
+                std::string elementName;
+                switch( result.getResultType() ) {
+                    case ResultWas::ThrewException:
+                    case ResultWas::FatalErrorCondition:
+                        elementName = "error";
+                        break;
+                    case ResultWas::ExplicitFailure:
+                        elementName = "failure";
+                        break;
+                    case ResultWas::ExpressionFailed:
+                        elementName = "failure";
+                        break;
+                    case ResultWas::DidntThrowException:
+                        elementName = "failure";
+                        break;
+
+                    // We should never see these here:
+                    case ResultWas::Info:
+                    case ResultWas::Warning:
+                    case ResultWas::Ok:
+                    case ResultWas::Unknown:
+                    case ResultWas::FailureBit:
+                    case ResultWas::Exception:
+                        elementName = "internalError";
+                        break;
+                }
+
+                XmlWriter::ScopedElement e = xml.scopedElement( elementName );
+
+                xml.writeAttribute( "message", result.getExpandedExpression() );
+                xml.writeAttribute( "type", result.getTestMacroName() );
+
+                std::ostringstream oss;
+                if( !result.getMessage().empty() )
+                    oss << result.getMessage() << '\n';
+                for( std::vector<MessageInfo>::const_iterator
+                        it = stats.infoMessages.begin(),
+                        itEnd = stats.infoMessages.end();
+                            it != itEnd;
+                            ++it )
+                    if( it->type == ResultWas::Info )
+                        oss << it->message << '\n';
+
+                oss << "at " << result.getSourceInfo();
+                xml.writeText( oss.str(), false );
+            }
+        }
+
+        XmlWriter xml;
+        Timer suiteTimer;
+        std::ostringstream stdOutForSuite;
+        std::ostringstream stdErrForSuite;
+        unsigned int unexpectedExceptions;
+        bool m_okToFail;
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "junit", JunitReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_console.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_CONSOLE_HPP_INCLUDED
+
+#include <cfloat>
+#include <cstdio>
+
+namespace Catch {
+
+    struct ConsoleReporter : StreamingReporterBase {
+        ConsoleReporter( ReporterConfig const& _config )
+        :   StreamingReporterBase( _config ),
+            m_headerPrinted( false )
+        {}
+
+        virtual ~ConsoleReporter() CATCH_OVERRIDE;
+        static std::string getDescription() {
+            return "Reports test results as plain lines of text";
+        }
+
+        virtual void noMatchingTestCases( std::string const& spec ) CATCH_OVERRIDE {
+            stream << "No test cases matched '" << spec << '\'' << std::endl;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) CATCH_OVERRIDE {
+        }
+
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) CATCH_OVERRIDE {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
+
+            // Drop out if result was successful but we're not printing them.
+            if( !includeResults && result.getResultType() != ResultWas::Warning )
+                return false;
+
+            lazyPrint();
+
+            AssertionPrinter printer( stream, _assertionStats, includeResults );
+            printer.print();
+            stream << std::endl;
+            return true;
+        }
+
+        virtual void sectionStarting( SectionInfo const& _sectionInfo ) CATCH_OVERRIDE {
+            m_headerPrinted = false;
+            StreamingReporterBase::sectionStarting( _sectionInfo );
+        }
+        virtual void sectionEnded( SectionStats const& _sectionStats ) CATCH_OVERRIDE {
+            if( _sectionStats.missingAssertions ) {
+                lazyPrint();
+                Colour colour( Colour::ResultError );
+                if( m_sectionStack.size() > 1 )
+                    stream << "\nNo assertions in section";
+                else
+                    stream << "\nNo assertions in test case";
+                stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
+            }
+            if( m_config->showDurations() == ShowDurations::Always ) {
+                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+            }
+            if( m_headerPrinted ) {
+                m_headerPrinted = false;
+            }
+            StreamingReporterBase::sectionEnded( _sectionStats );
+        }
+
+        virtual void testCaseEnded( TestCaseStats const& _testCaseStats ) CATCH_OVERRIDE {
+            StreamingReporterBase::testCaseEnded( _testCaseStats );
+            m_headerPrinted = false;
+        }
+        virtual void testGroupEnded( TestGroupStats const& _testGroupStats ) CATCH_OVERRIDE {
+            if( currentGroupInfo.used ) {
+                printSummaryDivider();
+                stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
+                printTotals( _testGroupStats.totals );
+                stream << '\n' << std::endl;
+            }
+            StreamingReporterBase::testGroupEnded( _testGroupStats );
+        }
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) CATCH_OVERRIDE {
+            printTotalsDivider( _testRunStats.totals );
+            printTotals( _testRunStats.totals );
+            stream << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+    private:
+
+        class AssertionPrinter {
+            void operator= ( AssertionPrinter const& );
+        public:
+            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+            :   stream( _stream ),
+                stats( _stats ),
+                result( _stats.assertionResult ),
+                colour( Colour::None ),
+                message( result.getMessage() ),
+                messages( _stats.infoMessages ),
+                printInfoMessages( _printInfoMessages )
+            {
+                switch( result.getResultType() ) {
+                    case ResultWas::Ok:
+                        colour = Colour::Success;
+                        passOrFail = "PASSED";
+                        //if( result.hasMessage() )
+                        if( _stats.infoMessages.size() == 1 )
+                            messageLabel = "with message";
+                        if( _stats.infoMessages.size() > 1 )
+                            messageLabel = "with messages";
+                        break;
+                    case ResultWas::ExpressionFailed:
+                        if( result.isOk() ) {
+                            colour = Colour::Success;
+                            passOrFail = "FAILED - but was ok";
+                        }
+                        else {
+                            colour = Colour::Error;
+                            passOrFail = "FAILED";
+                        }
+                        if( _stats.infoMessages.size() == 1 )
+                            messageLabel = "with message";
+                        if( _stats.infoMessages.size() > 1 )
+                            messageLabel = "with messages";
+                        break;
+                    case ResultWas::ThrewException:
+                        colour = Colour::Error;
+                        passOrFail = "FAILED";
+                        messageLabel = "due to unexpected exception with ";
+                        if (_stats.infoMessages.size() == 1)
+                            messageLabel += "message";
+                        if (_stats.infoMessages.size() > 1)
+                            messageLabel += "messages";
+                        break;
+                    case ResultWas::FatalErrorCondition:
+                        colour = Colour::Error;
+                        passOrFail = "FAILED";
+                        messageLabel = "due to a fatal error condition";
+                        break;
+                    case ResultWas::DidntThrowException:
+                        colour = Colour::Error;
+                        passOrFail = "FAILED";
+                        messageLabel = "because no exception was thrown where one was expected";
+                        break;
+                    case ResultWas::Info:
+                        messageLabel = "info";
+                        break;
+                    case ResultWas::Warning:
+                        messageLabel = "warning";
+                        break;
+                    case ResultWas::ExplicitFailure:
+                        passOrFail = "FAILED";
+                        colour = Colour::Error;
+                        if( _stats.infoMessages.size() == 1 )
+                            messageLabel = "explicitly with message";
+                        if( _stats.infoMessages.size() > 1 )
+                            messageLabel = "explicitly with messages";
+                        break;
+                    // These cases are here to prevent compiler warnings
+                    case ResultWas::Unknown:
+                    case ResultWas::FailureBit:
+                    case ResultWas::Exception:
+                        passOrFail = "** internal error **";
+                        colour = Colour::Error;
+                        break;
+                }
+            }
+
+            void print() const {
+                printSourceInfo();
+                if( stats.totals.assertions.total() > 0 ) {
+                    if( result.isOk() )
+                        stream << '\n';
+                    printResultType();
+                    printOriginalExpression();
+                    printReconstructedExpression();
+                }
+                else {
+                    stream << '\n';
+                }
+                printMessage();
+            }
+
+        private:
+            void printResultType() const {
+                if( !passOrFail.empty() ) {
+                    Colour colourGuard( colour );
+                    stream << passOrFail << ":\n";
+                }
+            }
+            void printOriginalExpression() const {
+                if( result.hasExpression() ) {
+                    Colour colourGuard( Colour::OriginalExpression );
+                    stream  << "  ";
+                    stream << result.getExpressionInMacro();
+                    stream << '\n';
+                }
+            }
+            void printReconstructedExpression() const {
+                if( result.hasExpandedExpression() ) {
+                    stream << "with expansion:\n";
+                    Colour colourGuard( Colour::ReconstructedExpression );
+                    stream << Text( result.getExpandedExpression(), TextAttributes().setIndent(2) ) << '\n';
+                }
+            }
+            void printMessage() const {
+                if( !messageLabel.empty() )
+                    stream << messageLabel << ':' << '\n';
+                for( std::vector<MessageInfo>::const_iterator it = messages.begin(), itEnd = messages.end();
+                        it != itEnd;
+                        ++it ) {
+                    // If this assertion is a warning ignore any INFO messages
+                    if( printInfoMessages || it->type != ResultWas::Info )
+                        stream << Text( it->message, TextAttributes().setIndent(2) ) << '\n';
+                }
+            }
+            void printSourceInfo() const {
+                Colour colourGuard( Colour::FileName );
+                stream << result.getSourceInfo() << ": ";
+            }
+
+            std::ostream& stream;
+            AssertionStats const& stats;
+            AssertionResult const& result;
+            Colour::Code colour;
+            std::string passOrFail;
+            std::string messageLabel;
+            std::string message;
+            std::vector<MessageInfo> messages;
+            bool printInfoMessages;
+        };
+
+        void lazyPrint() {
+
+            if( !currentTestRunInfo.used )
+                lazyPrintRunInfo();
+            if( !currentGroupInfo.used )
+                lazyPrintGroupInfo();
+
+            if( !m_headerPrinted ) {
+                printTestCaseAndSectionHeader();
+                m_headerPrinted = true;
+            }
+        }
+        void lazyPrintRunInfo() {
+            stream  << '\n' << getLineOfChars<'~'>() << '\n';
+            Colour colour( Colour::SecondaryText );
+            stream  << currentTestRunInfo->name
+                    << " is a Catch v"  << libraryVersion() << " host application.\n"
+                    << "Run with -? for options\n\n";
+
+            if( m_config->rngSeed() != 0 )
+                stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
+
+            currentTestRunInfo.used = true;
+        }
+        void lazyPrintGroupInfo() {
+            if( !currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1 ) {
+                printClosedHeader( "Group: " + currentGroupInfo->name );
+                currentGroupInfo.used = true;
+            }
+        }
+        void printTestCaseAndSectionHeader() {
+            assert( !m_sectionStack.empty() );
+            printOpenHeader( currentTestCaseInfo->name );
+
+            if( m_sectionStack.size() > 1 ) {
+                Colour colourGuard( Colour::Headers );
+
+                std::vector<SectionInfo>::const_iterator
+                    it = m_sectionStack.begin()+1, // Skip first section (test case)
+                    itEnd = m_sectionStack.end();
+                for( ; it != itEnd; ++it )
+                    printHeaderString( it->name, 2 );
+            }
+
+            SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
+
+            if( !lineInfo.empty() ){
+                stream << getLineOfChars<'-'>() << '\n';
+                Colour colourGuard( Colour::FileName );
+                stream << lineInfo << '\n';
+            }
+            stream << getLineOfChars<'.'>() << '\n' << std::endl;
+        }
+
+        void printClosedHeader( std::string const& _name ) {
+            printOpenHeader( _name );
+            stream << getLineOfChars<'.'>() << '\n';
+        }
+        void printOpenHeader( std::string const& _name ) {
+            stream  << getLineOfChars<'-'>() << '\n';
+            {
+                Colour colourGuard( Colour::Headers );
+                printHeaderString( _name );
+            }
+        }
+
+        // if string has a : in first line will set indent to follow it on
+        // subsequent lines
+        void printHeaderString( std::string const& _string, std::size_t indent = 0 ) {
+            std::size_t i = _string.find( ": " );
+            if( i != std::string::npos )
+                i+=2;
+            else
+                i = 0;
+            stream << Text( _string, TextAttributes()
+                                        .setIndent( indent+i)
+                                        .setInitialIndent( indent ) ) << '\n';
+        }
+
+        struct SummaryColumn {
+
+            SummaryColumn( std::string const& _label, Colour::Code _colour )
+            :   label( _label ),
+                colour( _colour )
+            {}
+            SummaryColumn addRow( std::size_t count ) {
+                std::ostringstream oss;
+                oss << count;
+                std::string row = oss.str();
+                for( std::vector<std::string>::iterator it = rows.begin(); it != rows.end(); ++it ) {
+                    while( it->size() < row.size() )
+                        *it = ' ' + *it;
+                    while( it->size() > row.size() )
+                        row = ' ' + row;
+                }
+                rows.push_back( row );
+                return *this;
+            }
+
+            std::string label;
+            Colour::Code colour;
+            std::vector<std::string> rows;
+
+        };
+
+        void printTotals( Totals const& totals ) {
+            if( totals.testCases.total() == 0 ) {
+                stream << Colour( Colour::Warning ) << "No tests ran\n";
+            }
+            else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
+                stream << Colour( Colour::ResultSuccess ) << "All tests passed";
+                stream << " ("
+                        << pluralise( totals.assertions.passed, "assertion" ) << " in "
+                        << pluralise( totals.testCases.passed, "test case" ) << ')'
+                        << '\n';
+            }
+            else {
+
+                std::vector<SummaryColumn> columns;
+                columns.push_back( SummaryColumn( "", Colour::None )
+                                        .addRow( totals.testCases.total() )
+                                        .addRow( totals.assertions.total() ) );
+                columns.push_back( SummaryColumn( "passed", Colour::Success )
+                                        .addRow( totals.testCases.passed )
+                                        .addRow( totals.assertions.passed ) );
+                columns.push_back( SummaryColumn( "failed", Colour::ResultError )
+                                        .addRow( totals.testCases.failed )
+                                        .addRow( totals.assertions.failed ) );
+                columns.push_back( SummaryColumn( "failed as expected", Colour::ResultExpectedFailure )
+                                        .addRow( totals.testCases.failedButOk )
+                                        .addRow( totals.assertions.failedButOk ) );
+
+                printSummaryRow( "test cases", columns, 0 );
+                printSummaryRow( "assertions", columns, 1 );
+            }
+        }
+        void printSummaryRow( std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row ) {
+            for( std::vector<SummaryColumn>::const_iterator it = cols.begin(); it != cols.end(); ++it ) {
+                std::string value = it->rows[row];
+                if( it->label.empty() ) {
+                    stream << label << ": ";
+                    if( value != "0" )
+                        stream << value;
+                    else
+                        stream << Colour( Colour::Warning ) << "- none -";
+                }
+                else if( value != "0" ) {
+                    stream  << Colour( Colour::LightGrey ) << " | ";
+                    stream  << Colour( it->colour )
+                            << value << ' ' << it->label;
+                }
+            }
+            stream << '\n';
+        }
+
+        static std::size_t makeRatio( std::size_t number, std::size_t total ) {
+            std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number/ total : 0;
+            return ( ratio == 0 && number > 0 ) ? 1 : ratio;
+        }
+        static std::size_t& findMax( std::size_t& i, std::size_t& j, std::size_t& k ) {
+            if( i > j && i > k )
+                return i;
+            else if( j > k )
+                return j;
+            else
+                return k;
+        }
+
+        void printTotalsDivider( Totals const& totals ) {
+            if( totals.testCases.total() > 0 ) {
+                std::size_t failedRatio = makeRatio( totals.testCases.failed, totals.testCases.total() );
+                std::size_t failedButOkRatio = makeRatio( totals.testCases.failedButOk, totals.testCases.total() );
+                std::size_t passedRatio = makeRatio( totals.testCases.passed, totals.testCases.total() );
+                while( failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH-1 )
+                    findMax( failedRatio, failedButOkRatio, passedRatio )++;
+                while( failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH-1 )
+                    findMax( failedRatio, failedButOkRatio, passedRatio )--;
+
+                stream << Colour( Colour::Error ) << std::string( failedRatio, '=' );
+                stream << Colour( Colour::ResultExpectedFailure ) << std::string( failedButOkRatio, '=' );
+                if( totals.testCases.allPassed() )
+                    stream << Colour( Colour::ResultSuccess ) << std::string( passedRatio, '=' );
+                else
+                    stream << Colour( Colour::Success ) << std::string( passedRatio, '=' );
+            }
+            else {
+                stream << Colour( Colour::Warning ) << std::string( CATCH_CONFIG_CONSOLE_WIDTH-1, '=' );
+            }
+            stream << '\n';
+        }
+        void printSummaryDivider() {
+            stream << getLineOfChars<'-'>() << '\n';
+        }
+
+    private:
+        bool m_headerPrinted;
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "console", ConsoleReporter )
+
+} // end namespace Catch
+
+// #included from: ../reporters/catch_reporter_compact.hpp
+#define TWOBLUECUBES_CATCH_REPORTER_COMPACT_HPP_INCLUDED
+
+namespace Catch {
+
+    struct CompactReporter : StreamingReporterBase {
+
+        CompactReporter( ReporterConfig const& _config )
+        : StreamingReporterBase( _config )
+        {}
+
+        virtual ~CompactReporter();
+
+        static std::string getDescription() {
+            return "Reports test results on a single line, suitable for IDEs";
+        }
+
+        virtual ReporterPreferences getPreferences() const {
+            ReporterPreferences prefs;
+            prefs.shouldRedirectStdOut = false;
+            return prefs;
+        }
+
+        virtual void noMatchingTestCases( std::string const& spec ) {
+            stream << "No test cases matched '" << spec << '\'' << std::endl;
+        }
+
+        virtual void assertionStarting( AssertionInfo const& ) {}
+
+        virtual bool assertionEnded( AssertionStats const& _assertionStats ) {
+            AssertionResult const& result = _assertionStats.assertionResult;
+
+            bool printInfoMessages = true;
+
+            // Drop out if result was successful and we're not printing those
+            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
+                if( result.getResultType() != ResultWas::Warning )
+                    return false;
+                printInfoMessages = false;
+            }
+
+            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
+            printer.print();
+
+            stream << std::endl;
+            return true;
+        }
+
+        virtual void sectionEnded(SectionStats const& _sectionStats) CATCH_OVERRIDE {
+            if (m_config->showDurations() == ShowDurations::Always) {
+                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
+            }
+        }
+
+        virtual void testRunEnded( TestRunStats const& _testRunStats ) {
+            printTotals( _testRunStats.totals );
+            stream << '\n' << std::endl;
+            StreamingReporterBase::testRunEnded( _testRunStats );
+        }
+
+    private:
+        class AssertionPrinter {
+            void operator= ( AssertionPrinter const& );
+        public:
+            AssertionPrinter( std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages )
+            : stream( _stream )
+            , stats( _stats )
+            , result( _stats.assertionResult )
+            , messages( _stats.infoMessages )
+            , itMessage( _stats.infoMessages.begin() )
+            , printInfoMessages( _printInfoMessages )
+            {}
+
+            void print() {
+                printSourceInfo();
+
+                itMessage = messages.begin();
+
+                switch( result.getResultType() ) {
+                    case ResultWas::Ok:
+                        printResultType( Colour::ResultSuccess, passedString() );
+                        printOriginalExpression();
+                        printReconstructedExpression();
+                        if ( ! result.hasExpression() )
+                            printRemainingMessages( Colour::None );
+                        else
+                            printRemainingMessages();
+                        break;
+                    case ResultWas::ExpressionFailed:
+                        if( result.isOk() )
+                            printResultType( Colour::ResultSuccess, failedString() + std::string( " - but was ok" ) );
+                        else
+                            printResultType( Colour::Error, failedString() );
+                        printOriginalExpression();
+                        printReconstructedExpression();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::ThrewException:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "unexpected exception with message:" );
+                        printMessage();
+                        printExpressionWas();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::FatalErrorCondition:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "fatal error condition with message:" );
+                        printMessage();
+                        printExpressionWas();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::DidntThrowException:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "expected exception, got none" );
+                        printExpressionWas();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::Info:
+                        printResultType( Colour::None, "info" );
+                        printMessage();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::Warning:
+                        printResultType( Colour::None, "warning" );
+                        printMessage();
+                        printRemainingMessages();
+                        break;
+                    case ResultWas::ExplicitFailure:
+                        printResultType( Colour::Error, failedString() );
+                        printIssue( "explicitly" );
+                        printRemainingMessages( Colour::None );
+                        break;
+                    // These cases are here to prevent compiler warnings
+                    case ResultWas::Unknown:
+                    case ResultWas::FailureBit:
+                    case ResultWas::Exception:
+                        printResultType( Colour::Error, "** internal error **" );
+                        break;
+                }
+            }
+
+        private:
+            // Colour::LightGrey
+
+            static Colour::Code dimColour() { return Colour::FileName; }
+
+#ifdef CATCH_PLATFORM_MAC
+            static const char* failedString() { return "FAILED"; }
+            static const char* passedString() { return "PASSED"; }
+#else
+            static const char* failedString() { return "failed"; }
+            static const char* passedString() { return "passed"; }
+#endif
+
+            void printSourceInfo() const {
+                Colour colourGuard( Colour::FileName );
+                stream << result.getSourceInfo() << ':';
+            }
+
+            void printResultType( Colour::Code colour, std::string const& passOrFail ) const {
+                if( !passOrFail.empty() ) {
+                    {
+                        Colour colourGuard( colour );
+                        stream << ' ' << passOrFail;
+                    }
+                    stream << ':';
+                }
+            }
+
+            void printIssue( std::string const& issue ) const {
+                stream << ' ' << issue;
+            }
+
+            void printExpressionWas() {
+                if( result.hasExpression() ) {
+                    stream << ';';
+                    {
+                        Colour colour( dimColour() );
+                        stream << " expression was:";
+                    }
+                    printOriginalExpression();
+                }
+            }
+
+            void printOriginalExpression() const {
+                if( result.hasExpression() ) {
+                    stream << ' ' << result.getExpression();
+                }
+            }
+
+            void printReconstructedExpression() const {
+                if( result.hasExpandedExpression() ) {
+                    {
+                        Colour colour( dimColour() );
+                        stream << " for: ";
+                    }
+                    stream << result.getExpandedExpression();
+                }
+            }
+
+            void printMessage() {
+                if ( itMessage != messages.end() ) {
+                    stream << " '" << itMessage->message << '\'';
+                    ++itMessage;
+                }
+            }
+
+            void printRemainingMessages( Colour::Code colour = dimColour() ) {
+                if ( itMessage == messages.end() )
+                    return;
+
+                // using messages.end() directly yields compilation error:
+                std::vector<MessageInfo>::const_iterator itEnd = messages.end();
+                const std::size_t N = static_cast<std::size_t>( std::distance( itMessage, itEnd ) );
+
+                {
+                    Colour colourGuard( colour );
+                    stream << " with " << pluralise( N, "message" ) << ':';
+                }
+
+                for(; itMessage != itEnd; ) {
+                    // If this assertion is a warning ignore any INFO messages
+                    if( printInfoMessages || itMessage->type != ResultWas::Info ) {
+                        stream << " '" << itMessage->message << '\'';
+                        if ( ++itMessage != itEnd ) {
+                            Colour colourGuard( dimColour() );
+                            stream << " and";
+                        }
+                    }
+                }
+            }
+
+        private:
+            std::ostream& stream;
+            AssertionStats const& stats;
+            AssertionResult const& result;
+            std::vector<MessageInfo> messages;
+            std::vector<MessageInfo>::const_iterator itMessage;
+            bool printInfoMessages;
+        };
+
+        // Colour, message variants:
+        // - white: No tests ran.
+        // -   red: Failed [both/all] N test cases, failed [both/all] M assertions.
+        // - white: Passed [both/all] N test cases (no assertions).
+        // -   red: Failed N tests cases, failed M assertions.
+        // - green: Passed [both/all] N tests cases with M assertions.
+
+        std::string bothOrAll( std::size_t count ) const {
+            return count == 1 ? std::string() : count == 2 ? "both " : "all " ;
+        }
+
+        void printTotals( const Totals& totals ) const {
+            if( totals.testCases.total() == 0 ) {
+                stream << "No tests ran.";
+            }
+            else if( totals.testCases.failed == totals.testCases.total() ) {
+                Colour colour( Colour::ResultError );
+                const std::string qualify_assertions_failed =
+                    totals.assertions.failed == totals.assertions.total() ?
+                        bothOrAll( totals.assertions.failed ) : std::string();
+                stream <<
+                    "Failed " << bothOrAll( totals.testCases.failed )
+                              << pluralise( totals.testCases.failed, "test case"  ) << ", "
+                    "failed " << qualify_assertions_failed <<
+                                 pluralise( totals.assertions.failed, "assertion" ) << '.';
+            }
+            else if( totals.assertions.total() == 0 ) {
+                stream <<
+                    "Passed " << bothOrAll( totals.testCases.total() )
+                              << pluralise( totals.testCases.total(), "test case" )
+                              << " (no assertions).";
+            }
+            else if( totals.assertions.failed ) {
+                Colour colour( Colour::ResultError );
+                stream <<
+                    "Failed " << pluralise( totals.testCases.failed, "test case"  ) << ", "
+                    "failed " << pluralise( totals.assertions.failed, "assertion" ) << '.';
+            }
+            else {
+                Colour colour( Colour::ResultSuccess );
+                stream <<
+                    "Passed " << bothOrAll( totals.testCases.passed )
+                              << pluralise( totals.testCases.passed, "test case"  ) <<
+                    " with "  << pluralise( totals.assertions.passed, "assertion" ) << '.';
+            }
+        }
+    };
+
+    INTERNAL_CATCH_REGISTER_REPORTER( "compact", CompactReporter )
+
+} // end namespace Catch
+
+namespace Catch {
+    // These are all here to avoid warnings about not having any out of line
+    // virtual methods
+    NonCopyable::~NonCopyable() {}
+    IShared::~IShared() {}
+    IStream::~IStream() CATCH_NOEXCEPT {}
+    FileStream::~FileStream() CATCH_NOEXCEPT {}
+    CoutStream::~CoutStream() CATCH_NOEXCEPT {}
+    DebugOutStream::~DebugOutStream() CATCH_NOEXCEPT {}
+    StreamBufBase::~StreamBufBase() CATCH_NOEXCEPT {}
+    IContext::~IContext() {}
+    IResultCapture::~IResultCapture() {}
+    ITestCase::~ITestCase() {}
+    ITestCaseRegistry::~ITestCaseRegistry() {}
+    IRegistryHub::~IRegistryHub() {}
+    IMutableRegistryHub::~IMutableRegistryHub() {}
+    IExceptionTranslator::~IExceptionTranslator() {}
+    IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() {}
+    IReporter::~IReporter() {}
+    IReporterFactory::~IReporterFactory() {}
+    IReporterRegistry::~IReporterRegistry() {}
+    IStreamingReporter::~IStreamingReporter() {}
+    AssertionStats::~AssertionStats() {}
+    SectionStats::~SectionStats() {}
+    TestCaseStats::~TestCaseStats() {}
+    TestGroupStats::~TestGroupStats() {}
+    TestRunStats::~TestRunStats() {}
+    CumulativeReporterBase::SectionNode::~SectionNode() {}
+    CumulativeReporterBase::~CumulativeReporterBase() {}
+
+    StreamingReporterBase::~StreamingReporterBase() {}
+    ConsoleReporter::~ConsoleReporter() {}
+    CompactReporter::~CompactReporter() {}
+    IRunner::~IRunner() {}
+    IMutableContext::~IMutableContext() {}
+    IConfig::~IConfig() {}
+    XmlReporter::~XmlReporter() {}
+    JunitReporter::~JunitReporter() {}
+    TestRegistry::~TestRegistry() {}
+    FreeFunctionTestCase::~FreeFunctionTestCase() {}
+    IGeneratorInfo::~IGeneratorInfo() {}
+    IGeneratorsForTest::~IGeneratorsForTest() {}
+    WildcardPattern::~WildcardPattern() {}
+    TestSpec::Pattern::~Pattern() {}
+    TestSpec::NamePattern::~NamePattern() {}
+    TestSpec::TagPattern::~TagPattern() {}
+    TestSpec::ExcludedPattern::~ExcludedPattern() {}
+    Matchers::Impl::MatcherUntypedBase::~MatcherUntypedBase() {}
+
+    void Config::dummy() {}
+
+    namespace TestCaseTracking {
+        ITracker::~ITracker() {}
+        TrackerBase::~TrackerBase() {}
+        SectionTracker::~SectionTracker() {}
+        IndexTracker::~IndexTracker() {}
+    }
+}
+
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+#endif
+
+#ifdef CATCH_CONFIG_MAIN
+// #included from: internal/catch_default_main.hpp
+#define TWOBLUECUBES_CATCH_DEFAULT_MAIN_HPP_INCLUDED
+
+#ifndef __OBJC__
+
+#if defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
+// Standard C/C++ Win32 Unicode wmain entry point
+extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
+#else
+// Standard C/C++ main entry point
+int main (int argc, char * argv[]) {
+#endif
+
+    int result = Catch::Session().run( argc, argv );
+    return ( result < 0xff ? result : 0xff );
+}
+
+#else // __OBJC__
+
+// Objective-C entry point
+int main (int argc, char * const argv[]) {
+#if !CATCH_ARC_ENABLED
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+#endif
+
+    Catch::registerTestMethods();
+    int result = Catch::Session().run( argc, (char* const*)argv );
+
+#if !CATCH_ARC_ENABLED
+    [pool drain];
+#endif
+
+    return ( result < 0xff ? result : 0xff );
+}
+
+#endif // __OBJC__
+
+#endif
+
+#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
+#  undef CLARA_CONFIG_MAIN
+#endif
+
+//////
+
+// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
+#ifdef CATCH_CONFIG_PREFIX_ALL
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#else
+#define CATCH_REQUIRE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr  )
+#endif
+
+#define CATCH_REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#define CATCH_REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
+
+#define CATCH_CHECK( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
+#define CATCH_CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
+
+#define CATCH_CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr )
+#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#define CATCH_CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr )
+
+#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#else
+#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif
+
+#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define CATCH_SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
+#define CATCH_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
+#define CATCH_SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CATCH_CAPTURE", #msg " := " << Catch::toString(msg) )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+    #define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+    #define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+    #define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+    #define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+    #define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+    #define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+    #define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+    #define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#else
+    #define CATCH_TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+    #define CATCH_TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+    #define CATCH_METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define CATCH_REGISTER_TEST_CASE( function, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( function, name, description )
+    #define CATCH_SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+    #define CATCH_FAIL( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
+    #define CATCH_FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
+    #define CATCH_SUCCEED( msg ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg )
+#endif
+#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define CATCH_REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define CATCH_REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define CATCH_GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define CATCH_SCENARIO( name, tags ) CATCH_TEST_CASE( "Scenario: " name, tags )
+#define CATCH_SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define CATCH_GIVEN( desc )    CATCH_SECTION( std::string( "Given: ") + desc, "" )
+#define CATCH_WHEN( desc )     CATCH_SECTION( std::string( " When: ") + desc, "" )
+#define CATCH_AND_WHEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
+#define CATCH_THEN( desc )     CATCH_SECTION( std::string( " Then: ") + desc, "" )
+#define CATCH_AND_THEN( desc ) CATCH_SECTION( std::string( "  And: ") + desc, "" )
+
+// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
+#else
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE", Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST_NO_TRY( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+
+#else
+#define REQUIRE( expr ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, expr  )
+#define REQUIRE_FALSE( expr ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, expr )
+#endif
+
+#define REQUIRE_THROWS( expr ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, "", expr )
+#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
+#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
+#define REQUIRE_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, expr )
+
+#define CHECK( expr ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_FALSE( expr ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, expr )
+#define CHECKED_IF( expr ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECKED_ELSE( expr ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_NOFAIL( expr ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, expr )
+
+#define CHECK_THROWS( expr )  INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, "", expr )
+#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
+#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
+#define CHECK_NOTHROW( expr ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, expr )
+
+#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
+
+#if defined(CATCH_CONFIG_FAST_COMPILE)
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT_NO_TRY( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#else
+#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
+#endif
+
+#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
+#define SCOPED_INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
+#define CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
+#define SCOPED_CAPTURE( msg ) INTERNAL_CATCH_INFO( "CAPTURE", #msg " := " << Catch::toString(msg) )
+
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
+#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
+#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
+#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
+#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
+#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
+#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
+#else
+#define TEST_CASE( name, description ) INTERNAL_CATCH_TESTCASE( name, description )
+    #define TEST_CASE_METHOD( className, name, description ) INTERNAL_CATCH_TEST_CASE_METHOD( className, name, description )
+    #define METHOD_AS_TEST_CASE( method, name, description ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, name, description )
+    #define REGISTER_TEST_CASE( method, name, description ) INTERNAL_CATCH_REGISTER_TESTCASE( method, name, description )
+    #define SECTION( name, description ) INTERNAL_CATCH_SECTION( name, description )
+    #define FAIL( msg ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, msg )
+    #define FAIL_CHECK( msg ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, msg )
+    #define SUCCEED( msg ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, msg )
+#endif
+#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE( "", "" )
+
+#define REGISTER_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_REPORTER( name, reporterType )
+#define REGISTER_LEGACY_REPORTER( name, reporterType ) INTERNAL_CATCH_REGISTER_LEGACY_REPORTER( name, reporterType )
+
+#define GENERATE( expr) INTERNAL_CATCH_GENERATE( expr )
+
+#endif
+
+#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
+
+// "BDD-style" convenience wrappers
+#ifdef CATCH_CONFIG_VARIADIC_MACROS
+#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
+#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
+#else
+#define SCENARIO( name, tags ) TEST_CASE( "Scenario: " name, tags )
+#define SCENARIO_METHOD( className, name, tags ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " name, tags )
+#endif
+#define GIVEN( desc )    SECTION( std::string("   Given: ") + desc, "" )
+#define WHEN( desc )     SECTION( std::string("    When: ") + desc, "" )
+#define AND_WHEN( desc ) SECTION( std::string("And when: ") + desc, "" )
+#define THEN( desc )     SECTION( std::string("    Then: ") + desc, "" )
+#define AND_THEN( desc ) SECTION( std::string("     And: ") + desc, "" )
+
+using Catch::Detail::Approx;
+
+// #included from: internal/catch_reenable_warnings.h
+
+#define TWOBLUECUBES_CATCH_REENABLE_WARNINGS_H_INCLUDED
+
+#ifdef __clang__
+#    ifdef __ICC // icpc defines the __clang__ macro
+#        pragma warning(pop)
+#    else
+#        pragma clang diagnostic pop
+#    endif
+#elif defined __GNUC__
+#    pragma GCC diagnostic pop
+#endif
+
+#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/README.md b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..7fd444c273d6d1543cbf89e0aee7a6ca54f43ea9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/README.md
@@ -0,0 +1,6 @@
+visit IOTSHARING.COM for more
+# esp32_webserver
+
+This is for Arduino esp8266 I modified to adapt with esp32
+
+Just put it in Arduino/libraries folder and run examples
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/examples/esp32webserver1/esp32webserver1.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/examples/esp32webserver1/esp32webserver1.ino
new file mode 100644
index 0000000000000000000000000000000000000000..5bd2716d8fc18e2622b34de0f205a5879474e4cb
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/examples/esp32webserver1/esp32webserver1.ino
@@ -0,0 +1,72 @@
+#include <WiFiClient.h>
+#include <ESP32WebServer.h>
+#include <WiFi.h>
+#include <ESPmDNS.h>
+
+const char* ssid = "mieu mieu 01";
+const char* password = "09471919479";
+
+ESP32WebServer server(80);
+
+const int led = 13;
+
+void handleRoot() {
+  digitalWrite(led, 1);
+  server.send(200, "text/plain", "hello from ESP32!");
+  digitalWrite(led, 0);
+}
+
+void handleNotFound(){
+  digitalWrite(led, 1);
+  String message = "File Not Found\n\n";
+  message += "URI: ";
+  message += server.uri();
+  message += "\nMethod: ";
+  message += (server.method() == HTTP_GET)?"GET":"POST";
+  message += "\nArguments: ";
+  message += server.args();
+  message += "\n";
+  for (uint8_t i=0; i<server.args(); i++){
+    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
+  }
+  server.send(404, "text/plain", message);
+  digitalWrite(led, 0);
+}
+
+void setup(void){
+  pinMode(led, OUTPUT);
+  digitalWrite(led, 0);
+  Serial.begin(115200);
+  WiFi.begin(ssid, password);
+  Serial.println("");
+
+  // Wait for connection
+  while (WiFi.status() != WL_CONNECTED) {
+    delay(500);
+    Serial.print(".");
+  }
+  Serial.println("");
+  Serial.print("Connected to ");
+  Serial.println(ssid);
+  Serial.print("IP address: ");
+  Serial.println(WiFi.localIP());
+
+  if (MDNS.begin("esp32")) {
+    Serial.println("MDNS responder started");
+  }
+
+  server.on("/", handleRoot);
+
+  server.on("/inline", [](){
+    server.send(200, "text/plain", "this works as well");
+  });
+
+  server.onNotFound(handleNotFound);
+
+  server.begin();
+  Serial.println("HTTP server started");
+}
+
+void loop(void){
+  server.handleClient();
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/examples/esp32webserver2/esp32webserver2.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/examples/esp32webserver2/esp32webserver2.ino
new file mode 100644
index 0000000000000000000000000000000000000000..eb45eb1788d93085f066eca9447070d33aaf53da
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/examples/esp32webserver2/esp32webserver2.ino
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2015, Majenko Technologies
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright notice, this
+ *   list of conditions and the following disclaimer in the documentation and/or
+ *   other materials provided with the distribution.
+ *
+ * * Neither the name of Majenko Technologies nor the names of its
+ *   contributors may be used to endorse or promote products derived from
+ *   this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <WiFiClient.h>
+#include <ESP32WebServer.h>
+#include <WiFi.h>
+#include <ESPmDNS.h>
+
+const char* ssid = "mieu mieu 01";
+const char* password = "09471919479";
+
+ESP32WebServer server ( 80 );
+
+const int led = 13;
+
+void handleRoot() {
+  digitalWrite ( led, 1 );
+  char temp[400];
+  int sec = millis() / 1000;
+  int min = sec / 60;
+  int hr = min / 60;
+
+  snprintf ( temp, 400,
+
+"<html>\
+  <head>\
+    <meta http-equiv='refresh' content='5'/>\
+    <title>ESP32 Demo</title>\
+    <style>\
+      body { background-color: #cccccc; font-family: Arial, Helvetica, Sans-Serif; Color: #000088; }\
+    </style>\
+  </head>\
+  <body>\
+    <h1>Hello from ESP32!</h1>\
+    <p>Uptime: %02d:%02d:%02d</p>\
+    <img src=\"/test.svg\" />\
+  </body>\
+</html>",
+
+    hr, min % 60, sec % 60
+  );
+  server.send ( 200, "text/html", temp );
+  digitalWrite ( led, 0 );
+}
+
+void handleNotFound() {
+  digitalWrite ( led, 1 );
+  String message = "File Not Found\n\n";
+  message += "URI: ";
+  message += server.uri();
+  message += "\nMethod: ";
+  message += ( server.method() == HTTP_GET ) ? "GET" : "POST";
+  message += "\nArguments: ";
+  message += server.args();
+  message += "\n";
+
+  for ( uint8_t i = 0; i < server.args(); i++ ) {
+    message += " " + server.argName ( i ) + ": " + server.arg ( i ) + "\n";
+  }
+
+  server.send ( 404, "text/plain", message );
+  digitalWrite ( led, 0 );
+}
+
+void setup ( void ) {
+  pinMode ( led, OUTPUT );
+  digitalWrite ( led, 0 );
+  Serial.begin ( 115200 );
+  WiFi.begin ( ssid, password );
+  Serial.println ( "" );
+
+  // Wait for connection
+  while ( WiFi.status() != WL_CONNECTED ) {
+    delay ( 500 );
+    Serial.print ( "." );
+  }
+
+  Serial.println ( "" );
+  Serial.print ( "Connected to " );
+  Serial.println ( ssid );
+  Serial.print ( "IP address: " );
+  Serial.println ( WiFi.localIP() );
+
+  if ( MDNS.begin ( "esp32" ) ) {
+    Serial.println ( "MDNS responder started" );
+  }
+
+  server.on ( "/", handleRoot );
+  server.on ( "/test.svg", drawGraph );
+  server.on ( "/inline", []() {
+    server.send ( 200, "text/plain", "this works as well" );
+  } );
+  server.onNotFound ( handleNotFound );
+  server.begin();
+  Serial.println ( "HTTP server started" );
+}
+
+void loop ( void ) {
+  server.handleClient();
+}
+
+void drawGraph() {
+  String out = "";
+  char temp[100];
+  out += "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"400\" height=\"150\">\n";
+  out += "<rect width=\"400\" height=\"150\" fill=\"rgb(250, 230, 210)\" stroke-width=\"1\" stroke=\"rgb(0, 0, 0)\" />\n";
+  out += "<g stroke=\"black\">\n";
+  int y = rand() % 130;
+  for (int x = 10; x < 390; x+= 10) {
+    int y2 = rand() % 130;
+    sprintf(temp, "<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" stroke-width=\"1\" />\n", x, 140 - y, x + 10, 140 - y2);
+    out += temp;
+    y = y2;
+  }
+  out += "</g>\n</svg>\n";
+
+  server.send ( 200, "image/svg+xml", out);
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/keywords.txt b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/keywords.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f8910752eceeab028b609ab71b0aa3af43f9a6e9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/keywords.txt
@@ -0,0 +1,36 @@
+#######################################
+# Syntax Coloring Map For Ultrasound
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+ESP32WebServer	KEYWORD1
+HTTPMethod	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+begin	KEYWORD2
+handleClient	KEYWORD2
+on	KEYWORD2
+addHandler	KEYWORD2
+uri	KEYWORD2
+method	KEYWORD2
+client	KEYWORD2
+send	KEYWORD2
+arg	KEYWORD2
+argName	KEYWORD2
+args	KEYWORD2
+hasArg	KEYWORD2
+onNotFound	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
+HTTP_GET	LITERAL1
+HTTP_POST	LITERAL1
+HTTP_ANY	LITERAL1
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/library.properties b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/library.properties
new file mode 100644
index 0000000000000000000000000000000000000000..28128300a3713271462c214d232bc4be7e755f77
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/library.properties
@@ -0,0 +1,9 @@
+name=ESP32WebServer
+version=1.0
+author=Ivan Grokhotkov
+maintainer=Ivan Grokhtkov <ivan@ESP32.com>
+sentence=Simple web server library
+paragraph=The library supports HTTP GET and POST requests, provides argument parsing, handles one client at a time.
+category=Communication
+url=
+architectures=ESP32
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/ESP32WebServer.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/ESP32WebServer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..81bd5cac968fd8d29c158b2734ce643a4d39a532
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/ESP32WebServer.cpp
@@ -0,0 +1,537 @@
+/*
+  ESP32WebServer.cpp - Dead simple web-server.
+  Supports only one simultaneous client, knows how to handle GET and POST.
+
+  Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
+
+  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
+  Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
+*/
+
+
+#include <Arduino.h>
+#include <libb64/cencode.h>
+#include "WiFiServer.h"
+#include "WiFiClient.h"
+#include "ESP32WebServer.h"
+#include "FS.h"
+#include "detail/RequestHandlersImpl.h"
+
+//#define DEBUG_ESP_HTTP_SERVER
+#ifdef DEBUG_ESP_PORT
+#define DEBUG_OUTPUT DEBUG_ESP_PORT
+#else
+#define DEBUG_OUTPUT Serial
+#endif
+
+const char * AUTHORIZATION_HEADER = "Authorization";
+
+ESP32WebServer::ESP32WebServer(IPAddress addr, int port)
+: _server(addr, port)
+, _currentMethod(HTTP_ANY)
+, _currentVersion(0)
+, _currentStatus(HC_NONE)
+, _statusChange(0)
+, _currentHandler(0)
+, _firstHandler(0)
+, _lastHandler(0)
+, _currentArgCount(0)
+, _currentArgs(0)
+, _headerKeysCount(0)
+, _currentHeaders(0)
+, _contentLength(0)
+, _chunked(false)
+{
+}
+
+ESP32WebServer::ESP32WebServer(int port)
+: _server(port)
+, _currentMethod(HTTP_ANY)
+, _currentVersion(0)
+, _currentStatus(HC_NONE)
+, _statusChange(0)
+, _currentHandler(0)
+, _firstHandler(0)
+, _lastHandler(0)
+, _currentArgCount(0)
+, _currentArgs(0)
+, _headerKeysCount(0)
+, _currentHeaders(0)
+, _contentLength(0)
+, _chunked(false)
+{
+}
+
+ESP32WebServer::~ESP32WebServer() {
+  if (_currentHeaders)
+    delete[]_currentHeaders;
+  _headerKeysCount = 0;
+  RequestHandler* handler = _firstHandler;
+  while (handler) {
+    RequestHandler* next = handler->next();
+    delete handler;
+    handler = next;
+  }
+  close();
+}
+
+void ESP32WebServer::begin() {
+  _currentStatus = HC_NONE;
+  _server.begin();
+  if(!_headerKeysCount)
+    collectHeaders(0, 0);
+}
+
+bool ESP32WebServer::authenticate(const char * username, const char * password){
+  if(hasHeader(AUTHORIZATION_HEADER)){
+    String authReq = header(AUTHORIZATION_HEADER);
+    if(authReq.startsWith("Basic")){
+      authReq = authReq.substring(6);
+      authReq.trim();
+      char toencodeLen = strlen(username)+strlen(password)+1;
+      char *toencode = new char[toencodeLen + 1];
+      if(toencode == NULL){
+        authReq = String();
+        return false;
+      }
+      char *encoded = new char[base64_encode_expected_len(toencodeLen)+1];
+      if(encoded == NULL){
+        authReq = String();
+        delete[] toencode;
+        return false;
+      }
+      sprintf(toencode, "%s:%s", username, password);
+      if(base64_encode_chars(toencode, toencodeLen, encoded) > 0 && authReq.equals(encoded)){
+        authReq = String();
+        delete[] toencode;
+        delete[] encoded;
+        return true;
+      }
+      delete[] toencode;
+      delete[] encoded;
+    }
+    authReq = String();
+  }
+  return false;
+}
+
+void ESP32WebServer::requestAuthentication(){
+  sendHeader("WWW-Authenticate", "Basic realm=\"Login Required\"");
+  send(401);
+}
+
+void ESP32WebServer::on(const String &uri, ESP32WebServer::THandlerFunction handler) {
+  on(uri, HTTP_ANY, handler);
+}
+
+void ESP32WebServer::on(const String &uri, HTTPMethod method, ESP32WebServer::THandlerFunction fn) {
+  on(uri, method, fn, _fileUploadHandler);
+}
+
+void ESP32WebServer::on(const String &uri, HTTPMethod method, ESP32WebServer::THandlerFunction fn, ESP32WebServer::THandlerFunction ufn) {
+  _addRequestHandler(new FunctionRequestHandler(fn, ufn, uri, method));
+}
+
+void ESP32WebServer::addHandler(RequestHandler* handler) {
+    _addRequestHandler(handler);
+}
+
+void ESP32WebServer::_addRequestHandler(RequestHandler* handler) {
+    if (!_lastHandler) {
+      _firstHandler = handler;
+      _lastHandler = handler;
+    }
+    else {
+      _lastHandler->next(handler);
+      _lastHandler = handler;
+    }
+}
+
+void ESP32WebServer::serveStatic(const char* uri, FS& fs, const char* path, const char* cache_header) {
+    _addRequestHandler(new StaticRequestHandler(fs, path, uri, cache_header));
+}
+
+void ESP32WebServer::handleClient() {
+  if (_currentStatus == HC_NONE) {
+    WiFiClient client = _server.available();
+    if (!client) {
+      return;
+    }
+
+#ifdef DEBUG_ESP_HTTP_SERVER
+    log_e("New client");
+#endif
+
+    _currentClient = client;
+    _currentStatus = HC_WAIT_READ;
+    _statusChange = millis();
+  }
+
+  if (!_currentClient.connected()) {
+    _currentClient = WiFiClient();
+    _currentStatus = HC_NONE;
+    return;
+  }
+
+  // Wait for data from client to become available
+  if (_currentStatus == HC_WAIT_READ) {
+    if (!_currentClient.available()) {
+      if (millis() - _statusChange > HTTP_MAX_DATA_WAIT) {
+        _currentClient = WiFiClient();
+        _currentStatus = HC_NONE;
+      }
+      yield();
+      return;
+    }
+
+    if (!_parseRequest(_currentClient)) {
+      _currentClient = WiFiClient();
+      _currentStatus = HC_NONE;
+      return;
+    }
+    _currentClient.setTimeout(HTTP_MAX_SEND_WAIT);
+    _contentLength = CONTENT_LENGTH_NOT_SET;
+    _handleRequest();
+
+    if (!_currentClient.connected()) {
+      _currentClient = WiFiClient();
+      _currentStatus = HC_NONE;
+      return;
+    } else {
+      _currentStatus = HC_WAIT_CLOSE;
+      _statusChange = millis();
+      return;
+    }
+  }
+
+  if (_currentStatus == HC_WAIT_CLOSE) {
+    if (millis() - _statusChange > HTTP_MAX_CLOSE_WAIT) {
+      _currentClient = WiFiClient();
+      _currentStatus = HC_NONE;
+    } else {
+      yield();
+      return;
+    }
+  }
+}
+
+void ESP32WebServer::close() {
+  _server.end();
+}
+
+void ESP32WebServer::stop() {
+  close();
+}
+
+void ESP32WebServer::sendHeader(const String& name, const String& value, bool first) {
+  String headerLine = name;
+  headerLine += ": ";
+  headerLine += value;
+  headerLine += "\r\n";
+
+  if (first) {
+    _responseHeaders = headerLine + _responseHeaders;
+  }
+  else {
+    _responseHeaders += headerLine;
+  }
+}
+
+void ESP32WebServer::setContentLength(size_t contentLength) {
+    _contentLength = contentLength;
+}
+
+void ESP32WebServer::_prepareHeader(String& response, int code, const char* content_type, size_t contentLength) {
+    response = "HTTP/1."+String(_currentVersion)+" ";
+    response += String(code);
+    response += " ";
+    response += _responseCodeToString(code);
+    response += "\r\n";
+
+    if (!content_type)
+        content_type = "text/html";
+
+    sendHeader("Content-Type", content_type, true);
+    if (_contentLength == CONTENT_LENGTH_NOT_SET) {
+        sendHeader("Content-Length", String(contentLength));
+    } else if (_contentLength != CONTENT_LENGTH_UNKNOWN) {
+        sendHeader("Content-Length", String(_contentLength));
+    } else if(_contentLength == CONTENT_LENGTH_UNKNOWN && _currentVersion){ //HTTP/1.1 or above client
+      //let's do chunked
+      _chunked = true;
+      sendHeader("Accept-Ranges","none");
+      sendHeader("Transfer-Encoding","chunked");
+    }
+    sendHeader("Connection", "close");
+
+    response += _responseHeaders;
+    response += "\r\n";
+    _responseHeaders = String();
+}
+
+void ESP32WebServer::send(int code, const char* content_type, const String& content) {
+    String header;
+    // Can we asume the following?
+    //if(code == 200 && content.length() == 0 && _contentLength == CONTENT_LENGTH_NOT_SET)
+    //  _contentLength = CONTENT_LENGTH_UNKNOWN;
+    _prepareHeader(header, code, content_type, content.length());
+    _currentClient.write(header.c_str(), header.length());
+    if(content.length())
+      sendContent(content);
+}
+
+void ESP32WebServer::send_P(int code, PGM_P content_type, PGM_P content) {
+    size_t contentLength = 0;
+
+    if (content != NULL) {
+        contentLength = strlen_P(content);
+    }
+
+    String header;
+    char type[64];
+    memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
+    _prepareHeader(header, code, (const char* )type, contentLength);
+    _currentClient.write(header.c_str(), header.length());
+    sendContent_P(content);
+}
+
+void ESP32WebServer::send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength) {
+    String header;
+    char type[64];
+    memccpy_P((void*)type, (PGM_VOID_P)content_type, 0, sizeof(type));
+    _prepareHeader(header, code, (const char* )type, contentLength);
+    sendContent(header);
+    sendContent_P(content, contentLength);
+}
+
+void ESP32WebServer::send(int code, char* content_type, const String& content) {
+  send(code, (const char*)content_type, content);
+}
+
+void ESP32WebServer::send(int code, const String& content_type, const String& content) {
+  send(code, (const char*)content_type.c_str(), content);
+}
+
+void ESP32WebServer::sendContent(const String& content) {
+  const char * footer = "\r\n";
+  size_t len = content.length();
+  if(_chunked) {
+    char * chunkSize = (char *)malloc(11);
+    if(chunkSize){
+      sprintf(chunkSize, "%x%s", len, footer);
+      _currentClient.write(chunkSize, strlen(chunkSize));
+      free(chunkSize);
+    }
+  }
+  _currentClient.write(content.c_str(), len);
+  if(_chunked){
+    _currentClient.write(footer, 2);
+  }
+}
+
+void ESP32WebServer::sendContent_P(PGM_P content) {
+  sendContent_P(content, strlen_P(content));
+}
+
+void ESP32WebServer::sendContent_P(PGM_P content, size_t size) {
+  const char * footer = "\r\n";
+  if(_chunked) {
+    char * chunkSize = (char *)malloc(11);
+    if(chunkSize){
+      sprintf(chunkSize, "%x%s", size, footer);
+      _currentClient.write(chunkSize, strlen(chunkSize));
+      free(chunkSize);
+    }
+  }
+  size_t chunkSize = 20;
+  int idx = 0;
+  while(size != 0){
+      if(size < chunkSize){
+        _currentClient.write(&content[idx], size);
+        size = 0;
+      } else {
+        _currentClient.write(&content[idx], chunkSize);
+        size -= chunkSize;
+        idx += chunkSize;
+      }
+    yield();
+  }
+  if(_chunked){
+    _currentClient.write(footer, 2);
+  }
+}
+
+
+String ESP32WebServer::arg(String name) {
+  for (int i = 0; i < _currentArgCount; ++i) {
+    if ( _currentArgs[i].key == name )
+      return _currentArgs[i].value;
+  }
+  return String();
+}
+
+String ESP32WebServer::arg(int i) {
+  if (i < _currentArgCount)
+    return _currentArgs[i].value;
+  return String();
+}
+
+String ESP32WebServer::argName(int i) {
+  if (i < _currentArgCount)
+    return _currentArgs[i].key;
+  return String();
+}
+
+int ESP32WebServer::args() {
+  return _currentArgCount;
+}
+
+bool ESP32WebServer::hasArg(String  name) {
+  for (int i = 0; i < _currentArgCount; ++i) {
+    if (_currentArgs[i].key == name)
+      return true;
+  }
+  return false;
+}
+
+
+String ESP32WebServer::header(String name) {
+  for (int i = 0; i < _headerKeysCount; ++i) {
+    if (_currentHeaders[i].key.equalsIgnoreCase(name))
+      return _currentHeaders[i].value;
+  }
+  return String();
+}
+
+void ESP32WebServer::collectHeaders(const char* headerKeys[], const size_t headerKeysCount) {
+  _headerKeysCount = headerKeysCount + 1;
+  if (_currentHeaders)
+     delete[]_currentHeaders;
+  _currentHeaders = new RequestArgument[_headerKeysCount];
+  _currentHeaders[0].key = AUTHORIZATION_HEADER;
+  for (int i = 1; i < _headerKeysCount; i++){
+    _currentHeaders[i].key = headerKeys[i-1];
+  }
+}
+
+String ESP32WebServer::header(int i) {
+  if (i < _headerKeysCount)
+    return _currentHeaders[i].value;
+  return String();
+}
+
+String ESP32WebServer::headerName(int i) {
+  if (i < _headerKeysCount)
+    return _currentHeaders[i].key;
+  return String();
+}
+
+int ESP32WebServer::headers() {
+  return _headerKeysCount;
+}
+
+bool ESP32WebServer::hasHeader(String name) {
+  for (int i = 0; i < _headerKeysCount; ++i) {
+    if ((_currentHeaders[i].key.equalsIgnoreCase(name)) &&  (_currentHeaders[i].value.length() > 0))
+      return true;
+  }
+  return false;
+}
+
+String ESP32WebServer::hostHeader() {
+  return _hostHeader;
+}
+
+void ESP32WebServer::onFileUpload(THandlerFunction fn) {
+  _fileUploadHandler = fn;
+}
+
+void ESP32WebServer::onNotFound(THandlerFunction fn) {
+  _notFoundHandler = fn;
+}
+
+void ESP32WebServer::_handleRequest() {
+  bool handled = false;
+  if (!_currentHandler){
+#ifdef DEBUG_ESP_HTTP_SERVER
+    DEBUG_OUTPUT.println("request handler not found");
+#endif
+  }
+  else {
+    handled = _currentHandler->handle(*this, _currentMethod, _currentUri);
+#ifdef DEBUG_ESP_HTTP_SERVER
+    if (!handled) {
+      DEBUG_OUTPUT.println("request handler failed to handle request");
+    }
+#endif
+  }
+
+  if (!handled) {
+    if(_notFoundHandler) {
+      _notFoundHandler();
+    }
+    else {
+      send(404, "text/plain", String("Not found: ") + _currentUri);
+    }
+  }
+
+  _currentUri = String();
+}
+
+String ESP32WebServer::_responseCodeToString(int code) {
+  switch (code) {
+    case 100: return F("Continue");
+    case 101: return F("Switching Protocols");
+    case 200: return F("OK");
+    case 201: return F("Created");
+    case 202: return F("Accepted");
+    case 203: return F("Non-Authoritative Information");
+    case 204: return F("No Content");
+    case 205: return F("Reset Content");
+    case 206: return F("Partial Content");
+    case 300: return F("Multiple Choices");
+    case 301: return F("Moved Permanently");
+    case 302: return F("Found");
+    case 303: return F("See Other");
+    case 304: return F("Not Modified");
+    case 305: return F("Use Proxy");
+    case 307: return F("Temporary Redirect");
+    case 400: return F("Bad Request");
+    case 401: return F("Unauthorized");
+    case 402: return F("Payment Required");
+    case 403: return F("Forbidden");
+    case 404: return F("Not Found");
+    case 405: return F("Method Not Allowed");
+    case 406: return F("Not Acceptable");
+    case 407: return F("Proxy Authentication Required");
+    case 408: return F("Request Time-out");
+    case 409: return F("Conflict");
+    case 410: return F("Gone");
+    case 411: return F("Length Required");
+    case 412: return F("Precondition Failed");
+    case 413: return F("Request Entity Too Large");
+    case 414: return F("Request-URI Too Large");
+    case 415: return F("Unsupported Media Type");
+    case 416: return F("Requested range not satisfiable");
+    case 417: return F("Expectation Failed");
+    case 500: return F("Internal Server Error");
+    case 501: return F("Not Implemented");
+    case 502: return F("Bad Gateway");
+    case 503: return F("Service Unavailable");
+    case 504: return F("Gateway Time-out");
+    case 505: return F("HTTP Version not supported");
+    default:  return "";
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/ESP32WebServer.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/ESP32WebServer.h
new file mode 100644
index 0000000000000000000000000000000000000000..2401a35eafcafb929da2a66a4337b71c5e45c044
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/ESP32WebServer.h
@@ -0,0 +1,192 @@
+/*
+  ESP32WebServer.h - Dead simple web-server.
+  Supports only one simultaneous client, knows how to handle GET and POST.
+
+  Copyright (c) 2014 Ivan Grokhotkov. All rights reserved.
+
+  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
+  Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
+*/
+
+
+#ifndef ESP32WEBSERVER_H
+#define ESP32WEBSERVER_H
+
+#include <functional>
+#include <WiFi.h>
+
+enum HTTPMethod { HTTP_ANY, HTTP_GET, HTTP_POST, HTTP_PUT, HTTP_PATCH, HTTP_DELETE, HTTP_OPTIONS };
+enum HTTPUploadStatus { UPLOAD_FILE_START, UPLOAD_FILE_WRITE, UPLOAD_FILE_END,
+                        UPLOAD_FILE_ABORTED };
+enum HTTPClientStatus { HC_NONE, HC_WAIT_READ, HC_WAIT_CLOSE };
+
+#define HTTP_DOWNLOAD_UNIT_SIZE 1460
+#define HTTP_UPLOAD_BUFLEN 2048
+#define HTTP_MAX_DATA_WAIT 1000 //ms to wait for the client to send the request
+#define HTTP_MAX_POST_WAIT 1000 //ms to wait for POST data to arrive
+#define HTTP_MAX_SEND_WAIT 5000 //ms to wait for data chunk to be ACKed
+#define HTTP_MAX_CLOSE_WAIT 2000 //ms to wait for the client to close the connection
+
+#define CONTENT_LENGTH_UNKNOWN ((size_t) -1)
+#define CONTENT_LENGTH_NOT_SET ((size_t) -2)
+
+class ESP32WebServer;
+
+typedef struct {
+  HTTPUploadStatus status;
+  String  filename;
+  String  name;
+  String  type;
+  size_t  totalSize;    // file size
+  size_t  currentSize;  // size of data currently in buf
+  uint8_t buf[HTTP_UPLOAD_BUFLEN];
+} HTTPUpload;
+
+#include "detail/RequestHandler.h"
+
+namespace fs {
+class FS;
+}
+
+class ESP32WebServer
+{
+public:
+  ESP32WebServer(IPAddress addr, int port = 80);
+  ESP32WebServer(int port = 80);
+  ~ESP32WebServer();
+
+  void begin();
+  void handleClient();
+
+  void close();
+  void stop();
+
+  bool authenticate(const char * username, const char * password);
+  void requestAuthentication();
+
+  typedef std::function<void(void)> THandlerFunction;
+  void on(const String &uri, THandlerFunction handler);
+  void on(const String &uri, HTTPMethod method, THandlerFunction fn);
+  void on(const String &uri, HTTPMethod method, THandlerFunction fn, THandlerFunction ufn);
+  void addHandler(RequestHandler* handler);
+  void serveStatic(const char* uri, fs::FS& fs, const char* path, const char* cache_header = NULL );
+  void onNotFound(THandlerFunction fn);  //called when handler is not assigned
+  void onFileUpload(THandlerFunction fn); //handle file uploads
+
+  String uri() { return _currentUri; }
+  HTTPMethod method() { return _currentMethod; }
+  WiFiClient client() { return _currentClient; }
+  HTTPUpload& upload() { return _currentUpload; }
+
+  String arg(String name);        // get request argument value by name
+  String arg(int i);              // get request argument value by number
+  String argName(int i);          // get request argument name by number
+  int args();                     // get arguments count
+  bool hasArg(String name);       // check if argument exists
+  void collectHeaders(const char* headerKeys[], const size_t headerKeysCount); // set the request headers to collect
+  String header(String name);      // get request header value by name
+  String header(int i);              // get request header value by number
+  String headerName(int i);          // get request header name by number
+  int headers();                     // get header count
+  bool hasHeader(String name);       // check if header exists
+
+  String hostHeader();            // get request host header if available or empty String if not
+
+  // send response to the client
+  // code - HTTP response code, can be 200 or 404
+  // content_type - HTTP content type, like "text/plain" or "image/png"
+  // content - actual content body
+  void send(int code, const char* content_type = NULL, const String& content = String(""));
+  void send(int code, char* content_type, const String& content);
+  void send(int code, const String& content_type, const String& content);
+  void send_P(int code, PGM_P content_type, PGM_P content);
+  void send_P(int code, PGM_P content_type, PGM_P content, size_t contentLength);
+
+  void setContentLength(size_t contentLength);
+  void sendHeader(const String& name, const String& value, bool first = false);
+  void sendContent(const String& content);
+  void sendContent_P(PGM_P content);
+  void sendContent_P(PGM_P content, size_t size);
+
+  static String urlDecode(const String& text);
+
+template<typename T> size_t streamFile(T &file, const String& contentType){
+  setContentLength(file.size());
+  if (String(file.name()).endsWith(".gz") &&
+      contentType != "application/x-gzip" &&
+      contentType != "application/octet-stream"){
+    sendHeader("Content-Encoding", "gzip");
+  }
+  send(200, contentType, "");
+  uint8_t buf[20];
+  size_t fsize = 0;
+  while(file.available()){
+      int got = file.read(buf, 20);
+      _currentClient.write(buf, got);
+      fsize += got;
+      yield();
+  }
+  return fsize;
+}
+
+protected:
+  void _addRequestHandler(RequestHandler* handler);
+  void _handleRequest();
+  bool _parseRequest(WiFiClient& client);
+  void _parseArguments(String data);
+  static String _responseCodeToString(int code);
+  bool _parseForm(WiFiClient& client, String boundary, uint32_t len);
+  bool _parseFormUploadAborted();
+  void _uploadWriteByte(uint8_t b);
+  uint8_t _uploadReadByte(WiFiClient& client);
+  void _prepareHeader(String& response, int code, const char* content_type, size_t contentLength);
+  bool _collectHeader(const char* headerName, const char* headerValue);
+
+  struct RequestArgument {
+    String key;
+    String value;
+  };
+
+  WiFiServer  _server;
+
+  WiFiClient  _currentClient;
+  HTTPMethod  _currentMethod;
+  String      _currentUri;
+  uint8_t     _currentVersion;
+  HTTPClientStatus _currentStatus;
+  unsigned long _statusChange;
+
+  RequestHandler*  _currentHandler;
+  RequestHandler*  _firstHandler;
+  RequestHandler*  _lastHandler;
+  THandlerFunction _notFoundHandler;
+  THandlerFunction _fileUploadHandler;
+
+  int              _currentArgCount;
+  RequestArgument* _currentArgs;
+  HTTPUpload       _currentUpload;
+
+  int              _headerKeysCount;
+  RequestArgument* _currentHeaders;
+  size_t           _contentLength;
+  String           _responseHeaders;
+
+  String           _hostHeader;
+  bool             _chunked;
+
+};
+
+
+#endif //ESP32WEBSERVER_H
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/Parsing.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/Parsing.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..45094944f08c69dfb1ec389a2032a434aa351a1c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/Parsing.cpp
@@ -0,0 +1,607 @@
+/*
+  Parsing.cpp - HTTP request parsing.
+
+  Copyright (c) 2015 Ivan Grokhotkov. All rights reserved.
+
+  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
+  Modified 8 May 2015 by Hristo Gochkov (proper post and file upload handling)
+*/
+
+#include <Arduino.h>
+#include "WiFiServer.h"
+#include "WiFiClient.h"
+#include "ESP32WebServer.h"
+
+//#define DEBUG_ESP_HTTP_SERVER
+#ifdef DEBUG_ESP_PORT
+#define DEBUG_OUTPUT DEBUG_ESP_PORT
+#else
+#define DEBUG_OUTPUT Serial
+#endif
+
+static char* readBytesWithTimeout(WiFiClient& client, size_t maxLength, size_t& dataLength, int timeout_ms)
+{
+  char *buf = nullptr;
+  dataLength = 0;
+  while (dataLength < maxLength) {
+    int tries = timeout_ms;
+    size_t newLength;
+    while (!(newLength = client.available()) && tries--) delay(1);
+    if (!newLength) {
+      break;
+    }
+    if (!buf) {
+      buf = (char *) malloc(newLength + 1);
+      if (!buf) {
+        return nullptr;
+      }
+    }
+    else {
+      char* newBuf = (char *) realloc(buf, dataLength + newLength + 1);
+      if (!newBuf) {
+        free(buf);
+        return nullptr;
+      }
+      buf = newBuf;
+    }
+    client.readBytes(buf + dataLength, newLength);
+    dataLength += newLength;
+    buf[dataLength] = '\0';
+  }
+  return buf;
+}
+
+bool ESP32WebServer::_parseRequest(WiFiClient& client) {
+  // Read the first line of HTTP request
+  String req = client.readStringUntil('\r');
+  client.readStringUntil('\n');
+  //reset header value
+  for (int i = 0; i < _headerKeysCount; ++i) {
+    _currentHeaders[i].value =String();
+   }
+
+  // First line of HTTP request looks like "GET /path HTTP/1.1"
+  // Retrieve the "/path" part by finding the spaces
+  int addr_start = req.indexOf(' ');
+  int addr_end = req.indexOf(' ', addr_start + 1);
+  if (addr_start == -1 || addr_end == -1) {
+#ifdef DEBUG_ESP_HTTP_SERVER
+    DEBUG_OUTPUT.print("Invalid request: ");
+    DEBUG_OUTPUT.println(req);
+#endif
+    return false;
+  }
+
+  String methodStr = req.substring(0, addr_start);
+  String url = req.substring(addr_start + 1, addr_end);
+  String versionEnd = req.substring(addr_end + 8);
+  _currentVersion = atoi(versionEnd.c_str());
+  String searchStr = "";
+  int hasSearch = url.indexOf('?');
+  if (hasSearch != -1){
+    searchStr = urlDecode(url.substring(hasSearch + 1));
+    url = url.substring(0, hasSearch);
+  }
+  _currentUri = url;
+  _chunked = false;
+
+  HTTPMethod method = HTTP_GET;
+  if (methodStr == "POST") {
+    method = HTTP_POST;
+  } else if (methodStr == "DELETE") {
+    method = HTTP_DELETE;
+  } else if (methodStr == "OPTIONS") {
+    method = HTTP_OPTIONS;
+  } else if (methodStr == "PUT") {
+    method = HTTP_PUT;
+  } else if (methodStr == "PATCH") {
+    method = HTTP_PATCH;
+  }
+  _currentMethod = method;
+
+#ifdef DEBUG_ESP_HTTP_SERVER
+  DEBUG_OUTPUT.print("method: ");
+  DEBUG_OUTPUT.print(methodStr);
+  DEBUG_OUTPUT.print(" url: ");
+  DEBUG_OUTPUT.print(url);
+  DEBUG_OUTPUT.print(" search: ");
+  DEBUG_OUTPUT.println(searchStr);
+#endif
+
+  //attach handler
+  RequestHandler* handler;
+  for (handler = _firstHandler; handler; handler = handler->next()) {
+    if (handler->canHandle(_currentMethod, _currentUri))
+      break;
+  }
+  _currentHandler = handler;
+
+  String formData;
+  // below is needed only when POST type request
+  if (method == HTTP_POST || method == HTTP_PUT || method == HTTP_PATCH || method == HTTP_DELETE){
+    String boundaryStr;
+    String headerName;
+    String headerValue;
+    bool isForm = false;
+    bool isEncoded = false;
+    uint32_t contentLength = 0;
+    //parse headers
+    while(1){
+      req = client.readStringUntil('\r');
+      client.readStringUntil('\n');
+      if (req == "") break;//no moar headers
+      int headerDiv = req.indexOf(':');
+      if (headerDiv == -1){
+        break;
+      }
+      headerName = req.substring(0, headerDiv);
+      headerValue = req.substring(headerDiv + 1);
+      headerValue.trim();
+       _collectHeader(headerName.c_str(),headerValue.c_str());
+
+      #ifdef DEBUG_ESP_HTTP_SERVER
+      DEBUG_OUTPUT.print("headerName: ");
+      DEBUG_OUTPUT.println(headerName);
+      DEBUG_OUTPUT.print("headerValue: ");
+      DEBUG_OUTPUT.println(headerValue);
+      #endif
+
+      if (headerName.equalsIgnoreCase("Content-Type")){
+        if (headerValue.startsWith("text/plain")){
+          isForm = false;
+        } else if (headerValue.startsWith("application/x-www-form-urlencoded")){
+          isForm = false;
+          isEncoded = true;
+        } else if (headerValue.startsWith("multipart/")){
+          boundaryStr = headerValue.substring(headerValue.indexOf('=')+1);
+          isForm = true;
+        }
+      } else if (headerName.equalsIgnoreCase("Content-Length")){
+        contentLength = headerValue.toInt();
+      } else if (headerName.equalsIgnoreCase("Host")){
+        _hostHeader = headerValue;
+      }
+    }
+
+    if (!isForm){
+      size_t plainLength;
+      char* plainBuf = readBytesWithTimeout(client, contentLength, plainLength, HTTP_MAX_POST_WAIT);
+      if (plainLength < contentLength) {
+      	free(plainBuf);
+      	return false;
+      }
+      if (contentLength > 0) {
+        if (searchStr != "") searchStr += '&';
+        if(isEncoded){
+          //url encoded form
+          String decoded = urlDecode(plainBuf);
+          size_t decodedLen = decoded.length();
+          memcpy(plainBuf, decoded.c_str(), decodedLen);
+          plainBuf[decodedLen] = 0;
+          searchStr += plainBuf;
+        }
+        _parseArguments(searchStr);
+        if(!isEncoded){
+          //plain post json or other data
+          RequestArgument& arg = _currentArgs[_currentArgCount++];
+          arg.key = "plain";
+          arg.value = String(plainBuf);
+        }
+
+  #ifdef DEBUG_ESP_HTTP_SERVER
+        DEBUG_OUTPUT.print("Plain: ");
+        DEBUG_OUTPUT.println(plainBuf);
+  #endif
+        free(plainBuf);
+      }
+    }
+
+    if (isForm){
+      _parseArguments(searchStr);
+      if (!_parseForm(client, boundaryStr, contentLength)) {
+        return false;
+      }
+    }
+  } else {
+    String headerName;
+    String headerValue;
+    //parse headers
+    while(1){
+      req = client.readStringUntil('\r');
+      client.readStringUntil('\n');
+      if (req == "") break;//no moar headers
+      int headerDiv = req.indexOf(':');
+      if (headerDiv == -1){
+        break;
+      }
+      headerName = req.substring(0, headerDiv);
+      headerValue = req.substring(headerDiv + 2);
+      _collectHeader(headerName.c_str(),headerValue.c_str());
+
+	  #ifdef DEBUG_ESP_HTTP_SERVER
+	  DEBUG_OUTPUT.print("headerName: ");
+	  DEBUG_OUTPUT.println(headerName);
+	  DEBUG_OUTPUT.print("headerValue: ");
+	  DEBUG_OUTPUT.println(headerValue);
+	  #endif
+
+	  if (headerName.equalsIgnoreCase("Host")){
+        _hostHeader = headerValue;
+      }
+    }
+    _parseArguments(searchStr);
+  }
+  client.flush();
+
+#ifdef DEBUG_ESP_HTTP_SERVER
+  DEBUG_OUTPUT.print("Request: ");
+  DEBUG_OUTPUT.println(url);
+  DEBUG_OUTPUT.print(" Arguments: ");
+  DEBUG_OUTPUT.println(searchStr);
+#endif
+
+  return true;
+}
+
+bool ESP32WebServer::_collectHeader(const char* headerName, const char* headerValue) {
+  for (int i = 0; i < _headerKeysCount; i++) {
+    if (_currentHeaders[i].key.equalsIgnoreCase(headerName)) {
+            _currentHeaders[i].value=headerValue;
+            return true;
+        }
+  }
+  return false;
+}
+
+void ESP32WebServer::_parseArguments(String data) {
+#ifdef DEBUG_ESP_HTTP_SERVER
+  DEBUG_OUTPUT.print("args: ");
+  DEBUG_OUTPUT.println(data);
+#endif
+  if (_currentArgs)
+    delete[] _currentArgs;
+  _currentArgs = 0;
+  if (data.length() == 0) {
+    _currentArgCount = 0;
+    _currentArgs = new RequestArgument[1];
+    return;
+  }
+  _currentArgCount = 1;
+
+  for (int i = 0; i < (int)data.length(); ) {
+    i = data.indexOf('&', i);
+    if (i == -1)
+      break;
+    ++i;
+    ++_currentArgCount;
+  }
+#ifdef DEBUG_ESP_HTTP_SERVER
+  DEBUG_OUTPUT.print("args count: ");
+  DEBUG_OUTPUT.println(_currentArgCount);
+#endif
+
+  _currentArgs = new RequestArgument[_currentArgCount+1];
+  int pos = 0;
+  int iarg;
+  for (iarg = 0; iarg < _currentArgCount;) {
+    int equal_sign_index = data.indexOf('=', pos);
+    int next_arg_index = data.indexOf('&', pos);
+#ifdef DEBUG_ESP_HTTP_SERVER
+    DEBUG_OUTPUT.print("pos ");
+    DEBUG_OUTPUT.print(pos);
+    DEBUG_OUTPUT.print("=@ ");
+    DEBUG_OUTPUT.print(equal_sign_index);
+    DEBUG_OUTPUT.print(" &@ ");
+    DEBUG_OUTPUT.println(next_arg_index);
+#endif
+    if ((equal_sign_index == -1) || ((equal_sign_index > next_arg_index) && (next_arg_index != -1))) {
+#ifdef DEBUG_ESP_HTTP_SERVER
+      DEBUG_OUTPUT.print("arg missing value: ");
+      DEBUG_OUTPUT.println(iarg);
+#endif
+      if (next_arg_index == -1)
+        break;
+      pos = next_arg_index + 1;
+      continue;
+    }
+    RequestArgument& arg = _currentArgs[iarg];
+    arg.key = data.substring(pos, equal_sign_index);
+	arg.value = data.substring(equal_sign_index + 1, next_arg_index);
+#ifdef DEBUG_ESP_HTTP_SERVER
+    DEBUG_OUTPUT.print("arg ");
+    DEBUG_OUTPUT.print(iarg);
+    DEBUG_OUTPUT.print(" key: ");
+    DEBUG_OUTPUT.print(arg.key);
+    DEBUG_OUTPUT.print(" value: ");
+    DEBUG_OUTPUT.println(arg.value);
+#endif
+    ++iarg;
+    if (next_arg_index == -1)
+      break;
+    pos = next_arg_index + 1;
+  }
+  _currentArgCount = iarg;
+#ifdef DEBUG_ESP_HTTP_SERVER
+  DEBUG_OUTPUT.print("args count: ");
+  DEBUG_OUTPUT.println(_currentArgCount);
+#endif
+
+}
+
+void ESP32WebServer::_uploadWriteByte(uint8_t b){
+  if (_currentUpload.currentSize == HTTP_UPLOAD_BUFLEN){
+    if(_currentHandler && _currentHandler->canUpload(_currentUri))
+      _currentHandler->upload(*this, _currentUri, _currentUpload);
+    _currentUpload.totalSize += _currentUpload.currentSize;
+    _currentUpload.currentSize = 0;
+  }
+  _currentUpload.buf[_currentUpload.currentSize++] = b;
+}
+
+uint8_t ESP32WebServer::_uploadReadByte(WiFiClient& client){
+  int res = client.read();
+  if(res == -1){
+    while(!client.available() && client.connected())
+      yield();
+    res = client.read();
+  }
+  return (uint8_t)res;
+}
+
+bool ESP32WebServer::_parseForm(WiFiClient& client, String boundary, uint32_t len){
+  (void) len;
+#ifdef DEBUG_ESP_HTTP_SERVER
+  DEBUG_OUTPUT.print("Parse Form: Boundary: ");
+  DEBUG_OUTPUT.print(boundary);
+  DEBUG_OUTPUT.print(" Length: ");
+  DEBUG_OUTPUT.println(len);
+#endif
+  String line;
+  int retry = 0;
+  do {
+    line = client.readStringUntil('\r');
+    ++retry;
+  } while (line.length() == 0 && retry < 3);
+
+  client.readStringUntil('\n');
+  //start reading the form
+  if (line == ("--"+boundary)){
+    RequestArgument* postArgs = new RequestArgument[32];
+    int postArgsLen = 0;
+    while(1){
+      String argName;
+      String argValue;
+      String argType;
+      String argFilename;
+      bool argIsFile = false;
+
+      line = client.readStringUntil('\r');
+      client.readStringUntil('\n');
+      if (line.length() > 19 && line.substring(0, 19).equalsIgnoreCase("Content-Disposition")){
+        int nameStart = line.indexOf('=');
+        if (nameStart != -1){
+          argName = line.substring(nameStart+2);
+          nameStart = argName.indexOf('=');
+          if (nameStart == -1){
+            argName = argName.substring(0, argName.length() - 1);
+          } else {
+            argFilename = argName.substring(nameStart+2, argName.length() - 1);
+            argName = argName.substring(0, argName.indexOf('"'));
+            argIsFile = true;
+#ifdef DEBUG_ESP_HTTP_SERVER
+            DEBUG_OUTPUT.print("PostArg FileName: ");
+            DEBUG_OUTPUT.println(argFilename);
+#endif
+            //use GET to set the filename if uploading using blob
+            if (argFilename == "blob" && hasArg("filename")) argFilename = arg("filename");
+          }
+#ifdef DEBUG_ESP_HTTP_SERVER
+          DEBUG_OUTPUT.print("PostArg Name: ");
+          DEBUG_OUTPUT.println(argName);
+#endif
+          argType = "text/plain";
+          line = client.readStringUntil('\r');
+          client.readStringUntil('\n');
+          if (line.length() > 12 && line.substring(0, 12).equalsIgnoreCase("Content-Type")){
+            argType = line.substring(line.indexOf(':')+2);
+            //skip next line
+            client.readStringUntil('\r');
+            client.readStringUntil('\n');
+          }
+#ifdef DEBUG_ESP_HTTP_SERVER
+          DEBUG_OUTPUT.print("PostArg Type: ");
+          DEBUG_OUTPUT.println(argType);
+#endif
+          if (!argIsFile){
+            while(1){
+              line = client.readStringUntil('\r');
+              client.readStringUntil('\n');
+              if (line.startsWith("--"+boundary)) break;
+              if (argValue.length() > 0) argValue += "\n";
+              argValue += line;
+            }
+#ifdef DEBUG_ESP_HTTP_SERVER
+            DEBUG_OUTPUT.print("PostArg Value: ");
+            DEBUG_OUTPUT.println(argValue);
+            DEBUG_OUTPUT.println();
+#endif
+
+            RequestArgument& arg = postArgs[postArgsLen++];
+            arg.key = argName;
+            arg.value = argValue;
+
+            if (line == ("--"+boundary+"--")){
+#ifdef DEBUG_ESP_HTTP_SERVER
+              DEBUG_OUTPUT.println("Done Parsing POST");
+#endif
+              break;
+            }
+          } else {
+            _currentUpload.status = UPLOAD_FILE_START;
+            _currentUpload.name = argName;
+            _currentUpload.filename = argFilename;
+            _currentUpload.type = argType;
+            _currentUpload.totalSize = 0;
+            _currentUpload.currentSize = 0;
+#ifdef DEBUG_ESP_HTTP_SERVER
+            DEBUG_OUTPUT.print("Start File: ");
+            DEBUG_OUTPUT.print(_currentUpload.filename);
+            DEBUG_OUTPUT.print(" Type: ");
+            DEBUG_OUTPUT.println(_currentUpload.type);
+#endif
+            if(_currentHandler && _currentHandler->canUpload(_currentUri))
+              _currentHandler->upload(*this, _currentUri, _currentUpload);
+            _currentUpload.status = UPLOAD_FILE_WRITE;
+            uint8_t argByte = _uploadReadByte(client);
+readfile:
+            while(argByte != 0x0D){
+              if (!client.connected()) return _parseFormUploadAborted();
+              _uploadWriteByte(argByte);
+              argByte = _uploadReadByte(client);
+            }
+
+            argByte = _uploadReadByte(client);
+            if (!client.connected()) return _parseFormUploadAborted();
+            if (argByte == 0x0A){
+              argByte = _uploadReadByte(client);
+              if (!client.connected()) return _parseFormUploadAborted();
+              if ((char)argByte != '-'){
+                //continue reading the file
+                _uploadWriteByte(0x0D);
+                _uploadWriteByte(0x0A);
+                goto readfile;
+              } else {
+                argByte = _uploadReadByte(client);
+                if (!client.connected()) return _parseFormUploadAborted();
+                if ((char)argByte != '-'){
+                  //continue reading the file
+                  _uploadWriteByte(0x0D);
+                  _uploadWriteByte(0x0A);
+                  _uploadWriteByte((uint8_t)('-'));
+                  goto readfile;
+                }
+              }
+
+              uint8_t endBuf[boundary.length()];
+              client.readBytes(endBuf, boundary.length());
+
+              if (strstr((const char*)endBuf, boundary.c_str()) != NULL){
+                if(_currentHandler && _currentHandler->canUpload(_currentUri))
+                  _currentHandler->upload(*this, _currentUri, _currentUpload);
+                _currentUpload.totalSize += _currentUpload.currentSize;
+                _currentUpload.status = UPLOAD_FILE_END;
+                if(_currentHandler && _currentHandler->canUpload(_currentUri))
+                  _currentHandler->upload(*this, _currentUri, _currentUpload);
+#ifdef DEBUG_ESP_HTTP_SERVER
+                DEBUG_OUTPUT.print("End File: ");
+                DEBUG_OUTPUT.print(_currentUpload.filename);
+                DEBUG_OUTPUT.print(" Type: ");
+                DEBUG_OUTPUT.print(_currentUpload.type);
+                DEBUG_OUTPUT.print(" Size: ");
+                DEBUG_OUTPUT.println(_currentUpload.totalSize);
+#endif
+                line = client.readStringUntil(0x0D);
+                client.readStringUntil(0x0A);
+                if (line == "--"){
+#ifdef DEBUG_ESP_HTTP_SERVER
+                  DEBUG_OUTPUT.println("Done Parsing POST");
+#endif
+                  break;
+                }
+                continue;
+              } else {
+                _uploadWriteByte(0x0D);
+                _uploadWriteByte(0x0A);
+                _uploadWriteByte((uint8_t)('-'));
+                _uploadWriteByte((uint8_t)('-'));
+                uint32_t i = 0;
+                while(i < boundary.length()){
+                  _uploadWriteByte(endBuf[i++]);
+                }
+                argByte = _uploadReadByte(client);
+                goto readfile;
+              }
+            } else {
+              _uploadWriteByte(0x0D);
+              goto readfile;
+            }
+            break;
+          }
+        }
+      }
+    }
+
+    int iarg;
+    int totalArgs = ((32 - postArgsLen) < _currentArgCount)?(32 - postArgsLen):_currentArgCount;
+    for (iarg = 0; iarg < totalArgs; iarg++){
+      RequestArgument& arg = postArgs[postArgsLen++];
+      arg.key = _currentArgs[iarg].key;
+      arg.value = _currentArgs[iarg].value;
+    }
+    if (_currentArgs) delete[] _currentArgs;
+    _currentArgs = new RequestArgument[postArgsLen];
+    for (iarg = 0; iarg < postArgsLen; iarg++){
+      RequestArgument& arg = _currentArgs[iarg];
+      arg.key = postArgs[iarg].key;
+      arg.value = postArgs[iarg].value;
+    }
+    _currentArgCount = iarg;
+    if (postArgs) delete[] postArgs;
+    return true;
+  }
+#ifdef DEBUG_ESP_HTTP_SERVER
+  DEBUG_OUTPUT.print("Error: line: ");
+  DEBUG_OUTPUT.println(line);
+#endif
+  return false;
+}
+
+String ESP32WebServer::urlDecode(const String& text)
+{
+	String decoded = "";
+	char temp[] = "0x00";
+	unsigned int len = text.length();
+	unsigned int i = 0;
+	while (i < len)
+	{
+		char decodedChar;
+		char encodedChar = text.charAt(i++);
+		if ((encodedChar == '%') && (i + 1 < len))
+		{
+			temp[2] = text.charAt(i++);
+			temp[3] = text.charAt(i++);
+
+			decodedChar = strtol(temp, NULL, 16);
+		}
+		else {
+			if (encodedChar == '+')
+			{
+				decodedChar = ' ';
+			}
+			else {
+				decodedChar = encodedChar;  // normal ascii char
+			}
+		}
+		decoded += decodedChar;
+	}
+	return decoded;
+}
+
+bool ESP32WebServer::_parseFormUploadAborted(){
+  _currentUpload.status = UPLOAD_FILE_ABORTED;
+  if(_currentHandler && _currentHandler->canUpload(_currentUri))
+    _currentHandler->upload(*this, _currentUri, _currentUpload);
+  return false;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/detail/RequestHandler.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/detail/RequestHandler.h
new file mode 100644
index 0000000000000000000000000000000000000000..2c216f39142c02dd4948085117057adf79b98338
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/detail/RequestHandler.h
@@ -0,0 +1,19 @@
+#ifndef REQUESTHANDLER_H
+#define REQUESTHANDLER_H
+
+class RequestHandler {
+public:
+    virtual ~RequestHandler() { }
+    virtual bool canHandle(HTTPMethod method, String uri) { (void) method; (void) uri; return false; }
+    virtual bool canUpload(String uri) { (void) uri; return false; }
+    virtual bool handle(ESP32WebServer& server, HTTPMethod requestMethod, String requestUri) { (void) server; (void) requestMethod; (void) requestUri; return false; }
+    virtual void upload(ESP32WebServer& server, String requestUri, HTTPUpload& upload) { (void) server; (void) requestUri; (void) upload; }
+
+    RequestHandler* next() { return _next; }
+    void next(RequestHandler* r) { _next = r; }
+
+private:
+    RequestHandler* _next = nullptr;
+};
+
+#endif //REQUESTHANDLER_H
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/detail/RequestHandlersImpl.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/detail/RequestHandlersImpl.h
new file mode 100644
index 0000000000000000000000000000000000000000..b69b753c382cdf1b0b77e568a175d52a35da019d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP32WebServer/src/detail/RequestHandlersImpl.h
@@ -0,0 +1,153 @@
+#ifndef REQUESTHANDLERSIMPL_H
+#define REQUESTHANDLERSIMPL_H
+
+#include "RequestHandler.h"
+
+class FunctionRequestHandler : public RequestHandler {
+public:
+    FunctionRequestHandler(ESP32WebServer::THandlerFunction fn, ESP32WebServer::THandlerFunction ufn, const String &uri, HTTPMethod method)
+    : _fn(fn)
+    , _ufn(ufn)
+    , _uri(uri)
+    , _method(method)
+    {
+    }
+
+    bool canHandle(HTTPMethod requestMethod, String requestUri) override  {
+        if (_method != HTTP_ANY && _method != requestMethod)
+            return false;
+
+        if (requestUri != _uri)
+            return false;
+
+        return true;
+    }
+
+    bool canUpload(String requestUri) override  {
+        if (!_ufn || !canHandle(HTTP_POST, requestUri))
+            return false;
+
+        return true;
+    }
+
+    bool handle(ESP32WebServer& server, HTTPMethod requestMethod, String requestUri) override {
+        (void) server;
+        if (!canHandle(requestMethod, requestUri))
+            return false;
+
+        _fn();
+        return true;
+    }
+
+    void upload(ESP32WebServer& server, String requestUri, HTTPUpload& upload) override {
+        (void) server;
+        (void) upload;
+        if (canUpload(requestUri))
+            _ufn();
+    }
+
+protected:
+    ESP32WebServer::THandlerFunction _fn;
+    ESP32WebServer::THandlerFunction _ufn;
+    String _uri;
+    HTTPMethod _method;
+};
+
+class StaticRequestHandler : public RequestHandler {
+public:
+    StaticRequestHandler(FS& fs, const char* path, const char* uri, const char* cache_header)
+    : _fs(fs)
+    , _uri(uri)
+    , _path(path)
+    , _cache_header(cache_header)
+    {
+        _isFile = fs.exists(path);
+        log_e("StaticRequestHandler: path=%s uri=%s isFile=%d, cache_header=%s\r\n", path, uri, _isFile, cache_header);
+        _baseUriLength = _uri.length();
+    }
+
+    bool canHandle(HTTPMethod requestMethod, String requestUri) override  {
+        if (requestMethod != HTTP_GET)
+            return false;
+
+        if ((_isFile && requestUri != _uri) || !requestUri.startsWith(_uri))
+            return false;
+
+        return true;
+    }
+
+    bool handle(ESP32WebServer& server, HTTPMethod requestMethod, String requestUri) override {
+        if (!canHandle(requestMethod, requestUri))
+            return false;
+
+        log_e("StaticRequestHandler::handle: request=%s _uri=%s\r\n", requestUri.c_str(), _uri.c_str());
+
+        String path(_path);
+
+        if (!_isFile) {
+            // Base URI doesn't point to a file.
+            // If a directory is requested, look for index file.
+            if (requestUri.endsWith("/")) requestUri += "index.htm";
+
+            // Append whatever follows this URI in request to get the file path.
+            path += requestUri.substring(_baseUriLength);
+        }
+        log_e("StaticRequestHandler::handle: path=%s, isFile=%d\r\n", path.c_str(), _isFile);
+
+        String contentType = getContentType(path);
+
+        // look for gz file, only if the original specified path is not a gz.  So part only works to send gzip via content encoding when a non compressed is asked for
+        // if you point the the path to gzip you will serve the gzip as content type "application/x-gzip", not text or javascript etc...
+        if (!path.endsWith(".gz") && !_fs.exists(path))  {
+            String pathWithGz = path + ".gz";
+            if(_fs.exists(pathWithGz))
+                path += ".gz";
+        }
+
+        File f = _fs.open(path, "r");
+        if (!f)
+            return false;
+
+        if (_cache_header.length() != 0)
+            server.sendHeader("Cache-Control", _cache_header);
+
+        server.streamFile(f, contentType);
+        return true;
+    }
+
+    static String getContentType(const String& path) {
+        if (path.endsWith(".html")) return "text/html";
+        else if (path.endsWith(".htm")) return "text/html";
+        else if (path.endsWith(".css")) return "text/css";
+        else if (path.endsWith(".txt")) return "text/plain";
+        else if (path.endsWith(".js")) return "application/javascript";
+        else if (path.endsWith(".png")) return "image/png";
+        else if (path.endsWith(".gif")) return "image/gif";
+        else if (path.endsWith(".jpg")) return "image/jpeg";
+        else if (path.endsWith(".ico")) return "image/x-icon";
+        else if (path.endsWith(".svg")) return "image/svg+xml";
+        else if (path.endsWith(".ttf")) return "application/x-font-ttf";
+        else if (path.endsWith(".otf")) return "application/x-font-opentype";
+        else if (path.endsWith(".woff")) return "application/font-woff";
+        else if (path.endsWith(".woff2")) return "application/font-woff2";
+        else if (path.endsWith(".eot")) return "application/vnd.ms-fontobject";
+        else if (path.endsWith(".sfnt")) return "application/font-sfnt";
+        else if (path.endsWith(".xml")) return "text/xml";
+        else if (path.endsWith(".pdf")) return "application/pdf";
+        else if (path.endsWith(".zip")) return "application/zip";
+        else if(path.endsWith(".gz")) return "application/x-gzip";
+        else if (path.endsWith(".appcache")) return "text/cache-manifest";
+        return "application/octet-stream";
+    }
+
+protected:
+    FS _fs;
+    String _uri;
+    String _path;
+    String _cache_header;
+    bool _isFile;
+    size_t _baseUriLength;
+};
+
+
+#endif //REQUESTHANDLERSIMPL_H
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplay.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplay.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8fc226519b37df85862d0ff3e72bd26a4691eaa
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplay.cpp
@@ -0,0 +1,810 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#include "OLEDDisplay.h"
+
+bool OLEDDisplay::init() {
+  if (!this->connect()) {
+    DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Can't establish connection to display\n");
+    return false;
+  }
+  this->buffer = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
+  if(!this->buffer) {
+    DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create display\n");
+    return false;
+  }
+
+  #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+  this->buffer_back = (uint8_t*) malloc(sizeof(uint8_t) * DISPLAY_BUFFER_SIZE);
+  if(!this->buffer_back) {
+    DEBUG_OLEDDISPLAY("[OLEDDISPLAY][init] Not enough memory to create back buffer\n");
+    free(this->buffer);
+    return false;
+  }
+  #endif
+
+  sendInitCommands();
+  resetDisplay();
+
+  return true;
+}
+
+void OLEDDisplay::end() {
+  if (this->buffer) free(this->buffer);
+  #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+  if (this->buffer_back) free(this->buffer_back);
+  #endif
+}
+
+void OLEDDisplay::resetDisplay(void) {
+  clear();
+  #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+  memset(buffer_back, 1, DISPLAY_BUFFER_SIZE);
+  #endif
+  display();
+}
+
+void OLEDDisplay::setColor(OLEDDISPLAY_COLOR color) {
+  this->color = color;
+}
+
+void OLEDDisplay::setPixel(int16_t x, int16_t y) {
+  if (x >= 0 && x < 128 && y >= 0 && y < 64) {
+    switch (color) {
+      case WHITE:   buffer[x + (y / 8) * DISPLAY_WIDTH] |=  (1 << (y & 7)); break;
+      case BLACK:   buffer[x + (y / 8) * DISPLAY_WIDTH] &= ~(1 << (y & 7)); break;
+      case INVERSE: buffer[x + (y / 8) * DISPLAY_WIDTH] ^=  (1 << (y & 7)); break;
+    }
+  }
+}
+
+// Bresenham's algorithm - thx wikipedia and Adafruit_GFX
+void OLEDDisplay::drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1) {
+  int16_t steep = abs(y1 - y0) > abs(x1 - x0);
+  if (steep) {
+    _swap_int16_t(x0, y0);
+    _swap_int16_t(x1, y1);
+  }
+
+  if (x0 > x1) {
+    _swap_int16_t(x0, x1);
+    _swap_int16_t(y0, y1);
+  }
+
+  int16_t dx, dy;
+  dx = x1 - x0;
+  dy = abs(y1 - y0);
+
+  int16_t err = dx / 2;
+  int16_t ystep;
+
+  if (y0 < y1) {
+    ystep = 1;
+  } else {
+    ystep = -1;
+  }
+
+  for (; x0<=x1; x0++) {
+    if (steep) {
+      setPixel(y0, x0);
+    } else {
+      setPixel(x0, y0);
+    }
+    err -= dy;
+    if (err < 0) {
+      y0 += ystep;
+      err += dx;
+    }
+  }
+}
+
+void OLEDDisplay::drawRect(int16_t x, int16_t y, int16_t width, int16_t height) {
+  drawHorizontalLine(x, y, width);
+  drawVerticalLine(x, y, height);
+  drawVerticalLine(x + width - 1, y, height);
+  drawHorizontalLine(x, y + height - 1, width);
+}
+
+void OLEDDisplay::fillRect(int16_t xMove, int16_t yMove, int16_t width, int16_t height) {
+  for (int16_t x = xMove; x < xMove + width; x++) {
+    drawVerticalLine(x, yMove, height);
+  }
+}
+
+void OLEDDisplay::drawCircle(int16_t x0, int16_t y0, int16_t radius) {
+  int16_t x = 0, y = radius;
+	int16_t dp = 1 - radius;
+	do {
+		if (dp < 0)
+			dp = dp + 2 * (++x) + 3;
+		else
+			dp = dp + 2 * (++x) - 2 * (--y) + 5;
+
+		setPixel(x0 + x, y0 + y);     //For the 8 octants
+		setPixel(x0 - x, y0 + y);
+		setPixel(x0 + x, y0 - y);
+		setPixel(x0 - x, y0 - y);
+		setPixel(x0 + y, y0 + x);
+		setPixel(x0 - y, y0 + x);
+		setPixel(x0 + y, y0 - x);
+		setPixel(x0 - y, y0 - x);
+
+	} while (x < y);
+
+  setPixel(x0 + radius, y0);
+  setPixel(x0, y0 + radius);
+  setPixel(x0 - radius, y0);
+  setPixel(x0, y0 - radius);
+}
+
+void OLEDDisplay::drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads) {
+  int16_t x = 0, y = radius;
+  int16_t dp = 1 - radius;
+  while (x < y) {
+    if (dp < 0)
+      dp = dp + 2 * (++x) + 3;
+    else
+      dp = dp + 2 * (++x) - 2 * (--y) + 5;
+    if (quads & 0x1) {
+      setPixel(x0 + x, y0 - y);
+      setPixel(x0 + y, y0 - x);
+    }
+    if (quads & 0x2) {
+      setPixel(x0 - y, y0 - x);
+      setPixel(x0 - x, y0 - y);
+    }
+    if (quads & 0x4) {
+      setPixel(x0 - y, y0 + x);
+      setPixel(x0 - x, y0 + y);
+    }
+    if (quads & 0x8) {
+      setPixel(x0 + x, y0 + y);
+      setPixel(x0 + y, y0 + x);
+    }
+  }
+  if (quads & 0x1 && quads & 0x8) {
+    setPixel(x0 + radius, y0);
+  }
+  if (quads & 0x4 && quads & 0x8) {
+    setPixel(x0, y0 + radius);
+  }
+  if (quads & 0x2 && quads & 0x4) {
+    setPixel(x0 - radius, y0);
+  }
+  if (quads & 0x1 && quads & 0x2) {
+    setPixel(x0, y0 - radius);
+  }
+}
+
+
+void OLEDDisplay::fillCircle(int16_t x0, int16_t y0, int16_t radius) {
+  int16_t x = 0, y = radius;
+	int16_t dp = 1 - radius;
+	do {
+		if (dp < 0)
+			dp = dp + 2 * (++x) + 3;
+		else
+			dp = dp + 2 * (++x) - 2 * (--y) + 5;
+
+    drawHorizontalLine(x0 - x, y0 - y, 2*x);
+    drawHorizontalLine(x0 - x, y0 + y, 2*x);
+    drawHorizontalLine(x0 - y, y0 - x, 2*y);
+    drawHorizontalLine(x0 - y, y0 + x, 2*y);
+
+
+	} while (x < y);
+  drawHorizontalLine(x0 - radius, y0, 2 * radius);
+
+}
+
+void OLEDDisplay::drawHorizontalLine(int16_t x, int16_t y, int16_t length) {
+  if (y < 0 || y >= DISPLAY_HEIGHT) { return; }
+
+  if (x < 0) {
+    length += x;
+    x = 0;
+  }
+
+  if ( (x + length) > DISPLAY_WIDTH) {
+    length = (DISPLAY_WIDTH - x);
+  }
+
+  if (length <= 0) { return; }
+
+  uint8_t * bufferPtr = buffer;
+  bufferPtr += (y >> 3) * DISPLAY_WIDTH;
+  bufferPtr += x;
+
+  uint8_t drawBit = 1 << (y & 7);
+
+  switch (color) {
+    case WHITE:   while (length--) {
+        *bufferPtr++ |= drawBit;
+      }; break;
+    case BLACK:   drawBit = ~drawBit;   while (length--) {
+        *bufferPtr++ &= drawBit;
+      }; break;
+    case INVERSE: while (length--) {
+        *bufferPtr++ ^= drawBit;
+      }; break;
+  }
+}
+
+void OLEDDisplay::drawVerticalLine(int16_t x, int16_t y, int16_t length) {
+  if (x < 0 || x >= DISPLAY_WIDTH) return;
+
+  if (y < 0) {
+    length += y;
+    y = 0;
+  }
+
+  if ( (y + length) > DISPLAY_HEIGHT) {
+    length = (DISPLAY_HEIGHT - y);
+  }
+
+  if (length <= 0) return;
+
+
+  uint8_t yOffset = y & 7;
+  uint8_t drawBit;
+  uint8_t *bufferPtr = buffer;
+
+  bufferPtr += (y >> 3) * DISPLAY_WIDTH;
+  bufferPtr += x;
+
+  if (yOffset) {
+    yOffset = 8 - yOffset;
+    drawBit = ~(0xFF >> (yOffset));
+
+    if (length < yOffset) {
+      drawBit &= (0xFF >> (yOffset - length));
+    }
+
+    switch (color) {
+      case WHITE:   *bufferPtr |=  drawBit; break;
+      case BLACK:   *bufferPtr &= ~drawBit; break;
+      case INVERSE: *bufferPtr ^=  drawBit; break;
+    }
+
+    if (length < yOffset) return;
+
+    length -= yOffset;
+    bufferPtr += DISPLAY_WIDTH;
+  }
+
+  if (length >= 8) {
+    switch (color) {
+      case WHITE:
+      case BLACK:
+        drawBit = (color == WHITE) ? 0xFF : 0x00;
+        do {
+          *bufferPtr = drawBit;
+          bufferPtr += DISPLAY_WIDTH;
+          length -= 8;
+        } while (length >= 8);
+        break;
+      case INVERSE:
+        do {
+          *bufferPtr = ~(*bufferPtr);
+          bufferPtr += DISPLAY_WIDTH;
+          length -= 8;
+        } while (length >= 8);
+        break;
+    }
+  }
+
+  if (length > 0) {
+    drawBit = (1 << (length & 7)) - 1;
+    switch (color) {
+      case WHITE:   *bufferPtr |=  drawBit; break;
+      case BLACK:   *bufferPtr &= ~drawBit; break;
+      case INVERSE: *bufferPtr ^=  drawBit; break;
+    }
+  }
+}
+
+void OLEDDisplay::drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress) {
+  uint16_t radius = height / 2;
+  uint16_t xRadius = x + radius;
+  uint16_t yRadius = y + radius;
+  uint16_t doubleRadius = 2 * radius;
+  uint16_t innerRadius = radius - 2;
+
+  setColor(WHITE);
+  drawCircleQuads(xRadius, yRadius, radius, 0b00000110);
+  drawHorizontalLine(xRadius, y, width - doubleRadius + 1);
+  drawHorizontalLine(xRadius, y + height, width - doubleRadius + 1);
+  drawCircleQuads(x + width - radius, yRadius, radius, 0b00001001);
+
+  uint16_t maxProgressWidth = (width - doubleRadius - 1) * progress / 100;
+
+  fillCircle(xRadius, yRadius, innerRadius);
+  fillRect(xRadius + 1, y + 2, maxProgressWidth, height - 3);
+  fillCircle(xRadius + maxProgressWidth, yRadius, innerRadius);
+}
+
+void OLEDDisplay::drawFastImage(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *image) {
+  drawInternal(xMove, yMove, width, height, image, 0, 0);
+}
+
+void OLEDDisplay::drawXbm(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *xbm) {
+  int16_t widthInXbm = (width + 7) / 8;
+  uint8_t data;
+
+  for(int16_t y = 0; y < height; y++) {
+    for(int16_t x = 0; x < width; x++ ) {
+      if (x & 7) {
+        data >>= 1; // Move a bit
+      } else {  // Read new data every 8 bit
+        data = pgm_read_byte(xbm + (x / 8) + y * widthInXbm);
+      }
+      // if there is a bit draw it
+      if (data & 0x01) {
+        setPixel(xMove + x, yMove + y);
+      }
+    }
+  }
+}
+
+void OLEDDisplay::drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth) {
+  uint8_t textHeight       = pgm_read_byte(fontData + HEIGHT_POS);
+  uint8_t firstChar        = pgm_read_byte(fontData + FIRST_CHAR_POS);
+  uint16_t sizeOfJumpTable = pgm_read_byte(fontData + CHAR_NUM_POS)  * JUMPTABLE_BYTES;
+
+  uint8_t cursorX         = 0;
+  uint8_t cursorY         = 0;
+
+  switch (textAlignment) {
+    case TEXT_ALIGN_CENTER_BOTH:
+      yMove -= textHeight >> 1;
+    // Fallthrough
+    case TEXT_ALIGN_CENTER:
+      xMove -= textWidth >> 1; // divide by 2
+      break;
+    case TEXT_ALIGN_RIGHT:
+      xMove -= textWidth;
+      break;
+  }
+
+  // Don't draw anything if it is not on the screen.
+  if (xMove + textWidth  < 0 || xMove > DISPLAY_WIDTH ) {return;}
+  if (yMove + textHeight < 0 || yMove > DISPLAY_HEIGHT) {return;}
+
+  for (uint16_t j = 0; j < textLength; j++) {
+    int16_t xPos = xMove + cursorX;
+    int16_t yPos = yMove + cursorY;
+
+    byte code = text[j];
+    if (code >= firstChar) {
+      byte charCode = code - firstChar;
+
+      // 4 Bytes per char code
+      byte msbJumpToChar    = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES );                  // MSB  \ JumpAddress
+      byte lsbJumpToChar    = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_LSB);   // LSB /
+      byte charByteSize     = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_SIZE);  // Size
+      byte currentCharWidth = pgm_read_byte( fontData + JUMPTABLE_START + charCode * JUMPTABLE_BYTES + JUMPTABLE_WIDTH); // Width
+
+      // Test if the char is drawable
+      if (!(msbJumpToChar == 255 && lsbJumpToChar == 255)) {
+        // Get the position of the char data
+        uint16_t charDataPosition = JUMPTABLE_START + sizeOfJumpTable + ((msbJumpToChar << 8) + lsbJumpToChar);
+        drawInternal(xPos, yPos, currentCharWidth, textHeight, fontData, charDataPosition, charByteSize);
+      }
+
+      cursorX += currentCharWidth;
+    }
+  }
+}
+
+
+void OLEDDisplay::drawString(int16_t xMove, int16_t yMove, String strUser) {
+  uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
+
+  // char* text must be freed!
+  char* text = utf8ascii(strUser);
+
+  uint16_t yOffset = 0;
+  // If the string should be centered vertically too
+  // we need to now how heigh the string is.
+  if (textAlignment == TEXT_ALIGN_CENTER_BOTH) {
+    uint16_t lb = 0;
+    // Find number of linebreaks in text
+    for (uint16_t i=0;text[i] != 0; i++) {
+      lb += (text[i] == 10);
+    }
+    // Calculate center
+    yOffset = (lb * lineHeight) / 2;
+  }
+
+  uint16_t line = 0;
+  char* textPart = strtok(text,"\n");
+  while (textPart != NULL) {
+    uint16_t length = strlen(textPart);
+    drawStringInternal(xMove, yMove - yOffset + (line++) * lineHeight, textPart, length, getStringWidth(textPart, length));
+    textPart = strtok(NULL, "\n");
+  }
+  free(text);
+}
+
+void OLEDDisplay::drawStringMaxWidth(int16_t xMove, int16_t yMove, uint16_t maxLineWidth, String strUser) {
+  uint16_t firstChar  = pgm_read_byte(fontData + FIRST_CHAR_POS);
+  uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
+
+  char* text = utf8ascii(strUser);
+
+  uint16_t length = strlen(text);
+  uint16_t lastDrawnPos = 0;
+  uint16_t lineNumber = 0;
+  uint16_t strWidth = 0;
+
+  uint16_t preferredBreakpoint = 0;
+  uint16_t widthAtBreakpoint = 0;
+
+  for (uint16_t i = 0; i < length; i++) {
+    strWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[i] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH);
+
+    // Always try to break on a space or dash
+    if (text[i] == ' ' || text[i]== '-') {
+      preferredBreakpoint = i;
+      widthAtBreakpoint = strWidth;
+    }
+
+    if (strWidth >= maxLineWidth) {
+      if (preferredBreakpoint == 0) {
+        preferredBreakpoint = i;
+        widthAtBreakpoint = strWidth;
+      }
+      drawStringInternal(xMove, yMove + (lineNumber++) * lineHeight , &text[lastDrawnPos], preferredBreakpoint - lastDrawnPos, widthAtBreakpoint);
+      lastDrawnPos = preferredBreakpoint + 1;
+      // It is possible that we did not draw all letters to i so we need
+      // to account for the width of the chars from `i - preferredBreakpoint`
+      // by calculating the width we did not draw yet.
+      strWidth = strWidth - widthAtBreakpoint;
+      preferredBreakpoint = 0;
+    }
+  }
+
+  // Draw last part if needed
+  if (lastDrawnPos < length) {
+    drawStringInternal(xMove, yMove + lineNumber * lineHeight , &text[lastDrawnPos], length - lastDrawnPos, getStringWidth(&text[lastDrawnPos], length - lastDrawnPos));
+  }
+
+  free(text);
+}
+
+uint16_t OLEDDisplay::getStringWidth(const char* text, uint16_t length) {
+  uint16_t firstChar        = pgm_read_byte(fontData + FIRST_CHAR_POS);
+
+  uint16_t stringWidth = 0;
+  uint16_t maxWidth = 0;
+
+  while (length--) {
+    stringWidth += pgm_read_byte(fontData + JUMPTABLE_START + (text[length] - firstChar) * JUMPTABLE_BYTES + JUMPTABLE_WIDTH);
+    if (text[length] == 10) {
+      maxWidth = max(maxWidth, stringWidth);
+      stringWidth = 0;
+    }
+  }
+
+  return max(maxWidth, stringWidth);
+}
+
+uint16_t OLEDDisplay::getStringWidth(String strUser) {
+  char* text = utf8ascii(strUser);
+  uint16_t length = strlen(text);
+  uint16_t width = getStringWidth(text, length);
+  free(text);
+  return width;
+}
+
+void OLEDDisplay::setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment) {
+  this->textAlignment = textAlignment;
+}
+
+void OLEDDisplay::setFont(const char *fontData) {
+  this->fontData = fontData;
+}
+
+void OLEDDisplay::displayOn(void) {
+  sendCommand(DISPLAYON);
+}
+
+void OLEDDisplay::displayOff(void) {
+  sendCommand(DISPLAYOFF);
+}
+
+void OLEDDisplay::invertDisplay(void) {
+  sendCommand(INVERTDISPLAY);
+}
+
+void OLEDDisplay::normalDisplay(void) {
+  sendCommand(NORMALDISPLAY);
+}
+
+void OLEDDisplay::setContrast(char contrast) {
+  sendCommand(SETCONTRAST);
+  sendCommand(contrast);
+}
+
+void OLEDDisplay::flipScreenVertically() {
+  sendCommand(SEGREMAP | 0x01);
+  sendCommand(COMSCANDEC);           //Rotate screen 180 Deg
+}
+
+void OLEDDisplay::clear(void) {
+  memset(buffer, 0, DISPLAY_BUFFER_SIZE);
+}
+
+void OLEDDisplay::drawLogBuffer(uint16_t xMove, uint16_t yMove) {
+  uint16_t lineHeight = pgm_read_byte(fontData + HEIGHT_POS);
+  // Always align left
+  setTextAlignment(TEXT_ALIGN_LEFT);
+
+  // State values
+  uint16_t length   = 0;
+  uint16_t line     = 0;
+  uint16_t lastPos  = 0;
+
+  for (uint16_t i=0;i<this->logBufferFilled;i++){
+    // Everytime we have a \n print
+    if (this->logBuffer[i] == 10) {
+      length++;
+      // Draw string on line `line` from lastPos to length
+      // Passing 0 as the lenght because we are in TEXT_ALIGN_LEFT
+      drawStringInternal(xMove, yMove + (line++) * lineHeight, &this->logBuffer[lastPos], length, 0);
+      // Remember last pos
+      lastPos = i;
+      // Reset length
+      length = 0;
+    } else {
+      // Count chars until next linebreak
+      length++;
+    }
+  }
+  // Draw the remaining string
+  if (length > 0) {
+    drawStringInternal(xMove, yMove + line * lineHeight, &this->logBuffer[lastPos], length, 0);
+  }
+}
+
+bool OLEDDisplay::setLogBuffer(uint16_t lines, uint16_t chars){
+  if (logBuffer != NULL) free(logBuffer);
+  uint16_t size = lines * chars;
+  if (size > 0) {
+    this->logBufferLine     = 0;      // Lines printed
+    this->logBufferMaxLines = lines;  // Lines max printable
+    this->logBufferSize     = size;   // Total number of characters the buffer can hold
+    this->logBuffer         = (char *) malloc(size * sizeof(uint8_t));
+    if(!this->logBuffer) {
+      DEBUG_OLEDDISPLAY("[OLEDDISPLAY][setLogBuffer] Not enough memory to create log buffer\n");
+      return false;
+    }
+  }
+  return true;
+}
+
+size_t OLEDDisplay::write(uint8_t c) {
+  if (this->logBufferSize > 0) {
+    // Don't waste space on \r\n line endings, dropping \r
+    if (c == 13) return 1;
+
+    bool maxLineNotReached = this->logBufferLine < this->logBufferMaxLines;
+    bool bufferNotFull = this->logBufferFilled < this->logBufferSize;
+
+    // Can we write to the buffer?
+    if (bufferNotFull && maxLineNotReached) {
+      this->logBuffer[logBufferFilled] = utf8ascii(c);
+      this->logBufferFilled++;
+      // Keep track of lines written
+      if (c == 10) this->logBufferLine++;
+    } else {
+      // Max line number is reached
+      if (!maxLineNotReached) this->logBufferLine--;
+
+      // Find the end of the first line
+      uint16_t firstLineEnd = 0;
+      for (uint16_t i=0;i<this->logBufferFilled;i++) {
+        if (this->logBuffer[i] == 10){
+          // Include last char too
+          firstLineEnd = i + 1;
+          break;
+        }
+      }
+      // If there was a line ending
+      if (firstLineEnd > 0) {
+        // Calculate the new logBufferFilled value
+        this->logBufferFilled = logBufferFilled - firstLineEnd;
+        // Now we move the lines infront of the buffer
+        memcpy(this->logBuffer, &this->logBuffer[firstLineEnd], logBufferFilled);
+      } else {
+        // Let's reuse the buffer if it was full
+        if (!bufferNotFull) {
+          this->logBufferFilled = 0;
+        }// else {
+        //  Nothing to do here
+        //}
+      }
+      write(c);
+    }
+  }
+  // We are always writing all uint8_t to the buffer
+  return 1;
+}
+
+size_t OLEDDisplay::write(const char* str) {
+  if (str == NULL) return 0;
+  size_t length = strlen(str);
+  for (size_t i = 0; i < length; i++) {
+    write(str[i]);
+  }
+  return length;
+}
+
+// Private functions
+void OLEDDisplay::sendInitCommands(void) {
+  sendCommand(DISPLAYOFF);
+  sendCommand(SETDISPLAYCLOCKDIV);
+  sendCommand(0xF0); // Increase speed of the display max ~96Hz
+  sendCommand(SETMULTIPLEX);
+  sendCommand(0x3F);
+  sendCommand(SETDISPLAYOFFSET);
+  sendCommand(0x00);
+  sendCommand(SETSTARTLINE);
+  sendCommand(CHARGEPUMP);
+  sendCommand(0x14);
+  sendCommand(MEMORYMODE);
+  sendCommand(0x00);
+  sendCommand(SEGREMAP);
+  sendCommand(COMSCANINC);
+  sendCommand(SETCOMPINS);
+  sendCommand(0x12);
+  sendCommand(SETCONTRAST);
+  sendCommand(0xCF);
+  sendCommand(SETPRECHARGE);
+  sendCommand(0xF1);
+  sendCommand(DISPLAYALLON_RESUME);
+  sendCommand(NORMALDISPLAY);
+  sendCommand(0x2e);            // stop scroll
+  sendCommand(DISPLAYON);
+}
+
+void inline OLEDDisplay::drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *data, uint16_t offset, uint16_t bytesInData) {
+  if (width < 0 || height < 0) return;
+  if (yMove + height < 0 || yMove > DISPLAY_HEIGHT)  return;
+  if (xMove + width  < 0 || xMove > DISPLAY_WIDTH)   return;
+
+  uint8_t  rasterHeight = 1 + ((height - 1) >> 3); // fast ceil(height / 8.0)
+  int8_t   yOffset      = yMove & 7;
+
+  bytesInData = bytesInData == 0 ? width * rasterHeight : bytesInData;
+
+  int16_t initYMove   = yMove;
+  int8_t  initYOffset = yOffset;
+
+
+  for (uint16_t i = 0; i < bytesInData; i++) {
+
+    // Reset if next horizontal drawing phase is started.
+    if ( i % rasterHeight == 0) {
+      yMove   = initYMove;
+      yOffset = initYOffset;
+    }
+
+    byte currentByte = pgm_read_byte(data + offset + i);
+
+    int16_t xPos = xMove + (i / rasterHeight);
+    int16_t yPos = ((yMove >> 3) + (i % rasterHeight)) * DISPLAY_WIDTH;
+
+    int16_t yScreenPos = yMove + yOffset;
+    int16_t dataPos    = xPos  + yPos;
+
+    if (dataPos >=  0  && dataPos < DISPLAY_BUFFER_SIZE &&
+        xPos    >=  0  && xPos    < DISPLAY_WIDTH ) {
+
+      if (yOffset >= 0) {
+        switch (this->color) {
+          case WHITE:   buffer[dataPos] |= currentByte << yOffset; break;
+          case BLACK:   buffer[dataPos] &= ~(currentByte << yOffset); break;
+          case INVERSE: buffer[dataPos] ^= currentByte << yOffset; break;
+        }
+        if (dataPos < (DISPLAY_BUFFER_SIZE - DISPLAY_WIDTH)) {
+          switch (this->color) {
+            case WHITE:   buffer[dataPos + DISPLAY_WIDTH] |= currentByte >> (8 - yOffset); break;
+            case BLACK:   buffer[dataPos + DISPLAY_WIDTH] &= ~(currentByte >> (8 - yOffset)); break;
+            case INVERSE: buffer[dataPos + DISPLAY_WIDTH] ^= currentByte >> (8 - yOffset); break;
+          }
+        }
+      } else {
+        // Make new offset position
+        yOffset = -yOffset;
+
+        switch (this->color) {
+          case WHITE:   buffer[dataPos] |= currentByte >> yOffset; break;
+          case BLACK:   buffer[dataPos] &= ~(currentByte >> yOffset); break;
+          case INVERSE: buffer[dataPos] ^= currentByte >> yOffset; break;
+        }
+
+        // Prepare for next iteration by moving one block up
+        yMove -= 8;
+
+        // and setting the new yOffset
+        yOffset = 8 - yOffset;
+      }
+
+      yield();
+    }
+  }
+}
+
+// Code form http://playground.arduino.cc/Main/Utf8ascii
+uint8_t OLEDDisplay::utf8ascii(byte ascii) {
+  static uint8_t LASTCHAR;
+
+  if ( ascii < 128 ) { // Standard ASCII-set 0..0x7F handling
+    LASTCHAR = 0;
+    return ascii;
+  }
+
+  uint8_t last = LASTCHAR;   // get last char
+  LASTCHAR = ascii;
+
+  switch (last) {    // conversion depnding on first UTF8-character
+    case 0xC2: return  (ascii);  break;
+    case 0xC3: return  (ascii | 0xC0);  break;
+    case 0x82: if (ascii == 0xAC) return (0x80);    // special case Euro-symbol
+  }
+
+  return  0; // otherwise: return zero, if character has to be ignored
+}
+
+// You need to free the char!
+char* OLEDDisplay::utf8ascii(String str) {
+  uint16_t k = 0;
+  uint16_t length = str.length() + 1;
+
+  // Copy the string into a char array
+  char* s = (char*) malloc(length * sizeof(char));
+  if(!s) {
+    DEBUG_OLEDDISPLAY("[OLEDDISPLAY][utf8ascii] Can't allocate another char array. Drop support for UTF-8.\n");
+    return (char*) str.c_str();
+  }
+  str.toCharArray(s, length);
+
+  length--;
+
+  for (uint16_t i=0; i < length; i++) {
+    char c = utf8ascii(s[i]);
+    if (c!=0) {
+      s[k++]=c;
+    }
+  }
+
+  s[k]=0;
+
+  // This will leak 's' be sure to free it in the calling function.
+  return s;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplay.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplay.h
new file mode 100644
index 0000000000000000000000000000000000000000..81537a24fa54ba017715d4971fb094f6f6b3e791
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplay.h
@@ -0,0 +1,268 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#ifndef OLEDDISPLAY_h
+#define OLEDDISPLAY_h
+
+#include <Arduino.h>
+#include "OLEDDisplayFonts.h"
+
+//#define DEBUG_OLEDDISPLAY(...) Serial.printf( __VA_ARGS__ )
+
+#ifndef DEBUG_OLEDDISPLAY
+#define DEBUG_OLEDDISPLAY(...)
+#endif
+
+// Use DOUBLE BUFFERING by default
+#ifndef OLEDDISPLAY_REDUCE_MEMORY
+#define OLEDDISPLAY_DOUBLE_BUFFER
+#endif
+
+
+// Display settings
+#define DISPLAY_WIDTH 128
+#define DISPLAY_HEIGHT 64
+#define DISPLAY_BUFFER_SIZE 1024
+
+// Header Values
+#define JUMPTABLE_BYTES 4
+
+#define JUMPTABLE_LSB   1
+#define JUMPTABLE_SIZE  2
+#define JUMPTABLE_WIDTH 3
+#define JUMPTABLE_START 4
+
+#define WIDTH_POS 0
+#define HEIGHT_POS 1
+#define FIRST_CHAR_POS 2
+#define CHAR_NUM_POS 3
+
+
+// Display commands
+#define CHARGEPUMP 0x8D
+#define COLUMNADDR 0x21
+#define COMSCANDEC 0xC8
+#define COMSCANINC 0xC0
+#define DISPLAYALLON 0xA5
+#define DISPLAYALLON_RESUME 0xA4
+#define DISPLAYOFF 0xAE
+#define DISPLAYON 0xAF
+#define EXTERNALVCC 0x1
+#define INVERTDISPLAY 0xA7
+#define MEMORYMODE 0x20
+#define NORMALDISPLAY 0xA6
+#define PAGEADDR 0x22
+#define SEGREMAP 0xA0
+#define SETCOMPINS 0xDA
+#define SETCONTRAST 0x81
+#define SETDISPLAYCLOCKDIV 0xD5
+#define SETDISPLAYOFFSET 0xD3
+#define SETHIGHCOLUMN 0x10
+#define SETLOWCOLUMN 0x00
+#define SETMULTIPLEX 0xA8
+#define SETPRECHARGE 0xD9
+#define SETSEGMENTREMAP 0xA1
+#define SETSTARTLINE 0x40
+#define SETVCOMDETECT 0xDB
+#define SWITCHCAPVCC 0x2
+
+#ifndef _swap_int16_t
+#define _swap_int16_t(a, b) { int16_t t = a; a = b; b = t; }
+#endif
+
+enum OLEDDISPLAY_COLOR {
+  BLACK = 0,
+  WHITE = 1,
+  INVERSE = 2
+};
+
+enum OLEDDISPLAY_TEXT_ALIGNMENT {
+  TEXT_ALIGN_LEFT = 0,
+  TEXT_ALIGN_RIGHT = 1,
+  TEXT_ALIGN_CENTER = 2,
+  TEXT_ALIGN_CENTER_BOTH = 3
+};
+
+
+class OLEDDisplay : public Print {
+  public:
+    // Initialize the display
+    bool init();
+
+    // Free the memory used by the display
+    void end();
+
+    // Cycle through the initialization
+    void resetDisplay(void);
+
+    /* Drawing functions */
+    // Sets the color of all pixel operations
+    void setColor(OLEDDISPLAY_COLOR color);
+
+    // Draw a pixel at given position
+    void setPixel(int16_t x, int16_t y);
+
+    // Draw a line from position 0 to position 1
+    void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1);
+
+    // Draw the border of a rectangle at the given location
+    void drawRect(int16_t x, int16_t y, int16_t width, int16_t height);
+
+    // Fill the rectangle
+    void fillRect(int16_t x, int16_t y, int16_t width, int16_t height);
+
+    // Draw the border of a circle
+    void drawCircle(int16_t x, int16_t y, int16_t radius);
+
+    // Draw all Quadrants specified in the quads bit mask
+    void drawCircleQuads(int16_t x0, int16_t y0, int16_t radius, uint8_t quads);
+
+    // Fill circle
+    void fillCircle(int16_t x, int16_t y, int16_t radius);
+
+    // Draw a line horizontally
+    void drawHorizontalLine(int16_t x, int16_t y, int16_t length);
+
+    // Draw a lin vertically
+    void drawVerticalLine(int16_t x, int16_t y, int16_t length);
+
+    // Draws a rounded progress bar with the outer dimensions given by width and height. Progress is 
+    // a unsigned byte value between 0 and 100
+    void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress);
+
+    // Draw a bitmap in the internal image format
+    void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const char *image);
+
+    // Draw a XBM
+    void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const char *xbm);
+
+    /* Text functions */
+
+    // Draws a string at the given location
+    void drawString(int16_t x, int16_t y, String text);
+
+    // Draws a String with a maximum width at the given location.
+    // If the given String is wider than the specified width
+    // The text will be wrapped to the next line at a space or dash
+    void drawStringMaxWidth(int16_t x, int16_t y, uint16_t maxLineWidth, String text);
+
+    // Returns the width of the const char* with the current
+    // font settings
+    uint16_t getStringWidth(const char* text, uint16_t length);
+
+    // Convencience method for the const char version
+    uint16_t getStringWidth(String text);
+
+    // Specifies relative to which anchor point
+    // the text is rendered. Available constants:
+    // TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH
+    void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment);
+
+    // Sets the current font. Available default fonts
+    // ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24
+    void setFont(const char *fontData);
+
+    /* Display functions */
+
+    // Turn the display on
+    void displayOn(void);
+
+    // Turn the display offs
+    void displayOff(void);
+
+    // Inverted display mode
+    void invertDisplay(void);
+
+    // Normal display mode
+    void normalDisplay(void);
+
+    // Set display contrast
+    void setContrast(char contrast);
+
+    // Turn the display upside down
+    void flipScreenVertically();
+
+    // Write the buffer to the display memory
+    virtual void display(void) = 0;
+
+    // Clear the local pixel buffer
+    void clear(void);
+
+    // Log buffer implementation
+
+    // This will define the lines and characters you can
+    // print to the screen. When you exeed the buffer size (lines * chars)
+    // the output may be truncated due to the size constraint.
+    bool setLogBuffer(uint16_t lines, uint16_t chars);
+
+    // Draw the log buffer at position (x, y)
+    void drawLogBuffer(uint16_t x, uint16_t y);
+
+    // Implementent needed function to be compatible with Print class
+    size_t write(uint8_t c);
+    size_t write(const char* s);
+
+    uint8_t            *buffer;
+
+    #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+    uint8_t            *buffer_back;
+    #endif
+
+  protected:
+
+    OLEDDISPLAY_TEXT_ALIGNMENT   textAlignment = TEXT_ALIGN_LEFT;
+    OLEDDISPLAY_COLOR            color         = WHITE;
+
+    const char          *fontData              = ArialMT_Plain_10;
+
+    // State values for logBuffer
+    uint16_t   logBufferSize                   = 0;
+    uint16_t   logBufferFilled                 = 0;
+    uint16_t   logBufferLine                   = 0;
+    uint16_t   logBufferMaxLines               = 0;
+    char      *logBuffer                       = NULL;
+
+    // Send a command to the display (low level function)
+    virtual void sendCommand(uint8_t com) {};
+
+    // Connect to the display
+    virtual bool connect() {};
+
+    // Send all the init commands
+    void sendInitCommands();
+
+    // converts utf8 characters to extended ascii
+    static char* utf8ascii(String s);
+    static byte utf8ascii(byte ascii);
+
+    void inline drawInternal(int16_t xMove, int16_t yMove, int16_t width, int16_t height, const char *data, uint16_t offset, uint16_t bytesInData) __attribute__((always_inline));
+
+    void drawStringInternal(int16_t xMove, int16_t yMove, char* text, uint16_t textLength, uint16_t textWidth);
+
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplayFonts.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplayFonts.h
new file mode 100644
index 0000000000000000000000000000000000000000..6dd21ef60b9919ca2dd24935340f3a5f518735a8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplayFonts.h
@@ -0,0 +1,1274 @@
+#ifndef OLEDDISPLAYFONTS_h
+#define OLEDDISPLAYFONTS_h
+
+const char ArialMT_Plain_10[] PROGMEM = {
+  0x0A, // Width: 10
+  0x0D, // Height: 13
+  0x20, // First Char: 32
+  0xE0, // Numbers of Chars: 224
+
+  // Jump Table:
+  0xFF, 0xFF, 0x00, 0x03,  // 32:65535
+  0x00, 0x00, 0x04, 0x03,  // 33:0
+  0x00, 0x04, 0x05, 0x04,  // 34:4
+  0x00, 0x09, 0x09, 0x06,  // 35:9
+  0x00, 0x12, 0x0A, 0x06,  // 36:18
+  0x00, 0x1C, 0x10, 0x09,  // 37:28
+  0x00, 0x2C, 0x0E, 0x07,  // 38:44
+  0x00, 0x3A, 0x01, 0x02,  // 39:58
+  0x00, 0x3B, 0x06, 0x03,  // 40:59
+  0x00, 0x41, 0x06, 0x03,  // 41:65
+  0x00, 0x47, 0x05, 0x04,  // 42:71
+  0x00, 0x4C, 0x09, 0x06,  // 43:76
+  0x00, 0x55, 0x04, 0x03,  // 44:85
+  0x00, 0x59, 0x03, 0x03,  // 45:89
+  0x00, 0x5C, 0x04, 0x03,  // 46:92
+  0x00, 0x60, 0x05, 0x03,  // 47:96
+  0x00, 0x65, 0x0A, 0x06,  // 48:101
+  0x00, 0x6F, 0x08, 0x06,  // 49:111
+  0x00, 0x77, 0x0A, 0x06,  // 50:119
+  0x00, 0x81, 0x0A, 0x06,  // 51:129
+  0x00, 0x8B, 0x0B, 0x06,  // 52:139
+  0x00, 0x96, 0x0A, 0x06,  // 53:150
+  0x00, 0xA0, 0x0A, 0x06,  // 54:160
+  0x00, 0xAA, 0x09, 0x06,  // 55:170
+  0x00, 0xB3, 0x0A, 0x06,  // 56:179
+  0x00, 0xBD, 0x0A, 0x06,  // 57:189
+  0x00, 0xC7, 0x04, 0x03,  // 58:199
+  0x00, 0xCB, 0x04, 0x03,  // 59:203
+  0x00, 0xCF, 0x0A, 0x06,  // 60:207
+  0x00, 0xD9, 0x09, 0x06,  // 61:217
+  0x00, 0xE2, 0x09, 0x06,  // 62:226
+  0x00, 0xEB, 0x0B, 0x06,  // 63:235
+  0x00, 0xF6, 0x14, 0x0A,  // 64:246
+  0x01, 0x0A, 0x0E, 0x07,  // 65:266
+  0x01, 0x18, 0x0C, 0x07,  // 66:280
+  0x01, 0x24, 0x0C, 0x07,  // 67:292
+  0x01, 0x30, 0x0B, 0x07,  // 68:304
+  0x01, 0x3B, 0x0C, 0x07,  // 69:315
+  0x01, 0x47, 0x09, 0x06,  // 70:327
+  0x01, 0x50, 0x0D, 0x08,  // 71:336
+  0x01, 0x5D, 0x0C, 0x07,  // 72:349
+  0x01, 0x69, 0x04, 0x03,  // 73:361
+  0x01, 0x6D, 0x08, 0x05,  // 74:365
+  0x01, 0x75, 0x0E, 0x07,  // 75:373
+  0x01, 0x83, 0x0C, 0x06,  // 76:387
+  0x01, 0x8F, 0x10, 0x08,  // 77:399
+  0x01, 0x9F, 0x0C, 0x07,  // 78:415
+  0x01, 0xAB, 0x0E, 0x08,  // 79:427
+  0x01, 0xB9, 0x0B, 0x07,  // 80:441
+  0x01, 0xC4, 0x0E, 0x08,  // 81:452
+  0x01, 0xD2, 0x0C, 0x07,  // 82:466
+  0x01, 0xDE, 0x0C, 0x07,  // 83:478
+  0x01, 0xEA, 0x0B, 0x06,  // 84:490
+  0x01, 0xF5, 0x0C, 0x07,  // 85:501
+  0x02, 0x01, 0x0D, 0x07,  // 86:513
+  0x02, 0x0E, 0x11, 0x09,  // 87:526
+  0x02, 0x1F, 0x0E, 0x07,  // 88:543
+  0x02, 0x2D, 0x0D, 0x07,  // 89:557
+  0x02, 0x3A, 0x0C, 0x06,  // 90:570
+  0x02, 0x46, 0x06, 0x03,  // 91:582
+  0x02, 0x4C, 0x06, 0x03,  // 92:588
+  0x02, 0x52, 0x04, 0x03,  // 93:594
+  0x02, 0x56, 0x09, 0x05,  // 94:598
+  0x02, 0x5F, 0x0C, 0x06,  // 95:607
+  0x02, 0x6B, 0x03, 0x03,  // 96:619
+  0x02, 0x6E, 0x0A, 0x06,  // 97:622
+  0x02, 0x78, 0x0A, 0x06,  // 98:632
+  0x02, 0x82, 0x0A, 0x05,  // 99:642
+  0x02, 0x8C, 0x0A, 0x06,  // 100:652
+  0x02, 0x96, 0x0A, 0x06,  // 101:662
+  0x02, 0xA0, 0x05, 0x03,  // 102:672
+  0x02, 0xA5, 0x0A, 0x06,  // 103:677
+  0x02, 0xAF, 0x0A, 0x06,  // 104:687
+  0x02, 0xB9, 0x04, 0x02,  // 105:697
+  0x02, 0xBD, 0x04, 0x02,  // 106:701
+  0x02, 0xC1, 0x08, 0x05,  // 107:705
+  0x02, 0xC9, 0x04, 0x02,  // 108:713
+  0x02, 0xCD, 0x10, 0x08,  // 109:717
+  0x02, 0xDD, 0x0A, 0x06,  // 110:733
+  0x02, 0xE7, 0x0A, 0x06,  // 111:743
+  0x02, 0xF1, 0x0A, 0x06,  // 112:753
+  0x02, 0xFB, 0x0A, 0x06,  // 113:763
+  0x03, 0x05, 0x05, 0x03,  // 114:773
+  0x03, 0x0A, 0x08, 0x05,  // 115:778
+  0x03, 0x12, 0x06, 0x03,  // 116:786
+  0x03, 0x18, 0x0A, 0x06,  // 117:792
+  0x03, 0x22, 0x09, 0x05,  // 118:802
+  0x03, 0x2B, 0x0E, 0x07,  // 119:811
+  0x03, 0x39, 0x0A, 0x05,  // 120:825
+  0x03, 0x43, 0x09, 0x05,  // 121:835
+  0x03, 0x4C, 0x0A, 0x05,  // 122:844
+  0x03, 0x56, 0x06, 0x03,  // 123:854
+  0x03, 0x5C, 0x04, 0x03,  // 124:860
+  0x03, 0x60, 0x05, 0x03,  // 125:864
+  0x03, 0x65, 0x09, 0x06,  // 126:869
+  0xFF, 0xFF, 0x00, 0x00,  // 127:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 128:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 129:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 130:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 131:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 132:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 133:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 134:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 135:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 136:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 137:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 138:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 139:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 140:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 141:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 142:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 143:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 144:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 145:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 146:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 147:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 148:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 149:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 150:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 151:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 152:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 153:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 154:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 155:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 156:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 157:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 158:65535
+  0xFF, 0xFF, 0x00, 0x0A,  // 159:65535
+  0xFF, 0xFF, 0x00, 0x03,  // 160:65535
+  0x03, 0x6E, 0x04, 0x03,  // 161:878
+  0x03, 0x72, 0x0A, 0x06,  // 162:882
+  0x03, 0x7C, 0x0C, 0x06,  // 163:892
+  0x03, 0x88, 0x0A, 0x06,  // 164:904
+  0x03, 0x92, 0x0A, 0x06,  // 165:914
+  0x03, 0x9C, 0x04, 0x03,  // 166:924
+  0x03, 0xA0, 0x0A, 0x06,  // 167:928
+  0x03, 0xAA, 0x05, 0x03,  // 168:938
+  0x03, 0xAF, 0x0D, 0x07,  // 169:943
+  0x03, 0xBC, 0x07, 0x04,  // 170:956
+  0x03, 0xC3, 0x0A, 0x06,  // 171:963
+  0x03, 0xCD, 0x09, 0x06,  // 172:973
+  0x03, 0xD6, 0x03, 0x03,  // 173:982
+  0x03, 0xD9, 0x0D, 0x07,  // 174:985
+  0x03, 0xE6, 0x0B, 0x06,  // 175:998
+  0x03, 0xF1, 0x07, 0x04,  // 176:1009
+  0x03, 0xF8, 0x0A, 0x05,  // 177:1016
+  0x04, 0x02, 0x05, 0x03,  // 178:1026
+  0x04, 0x07, 0x05, 0x03,  // 179:1031
+  0x04, 0x0C, 0x05, 0x03,  // 180:1036
+  0x04, 0x11, 0x0A, 0x06,  // 181:1041
+  0x04, 0x1B, 0x09, 0x05,  // 182:1051
+  0x04, 0x24, 0x03, 0x03,  // 183:1060
+  0x04, 0x27, 0x06, 0x03,  // 184:1063
+  0x04, 0x2D, 0x05, 0x03,  // 185:1069
+  0x04, 0x32, 0x07, 0x04,  // 186:1074
+  0x04, 0x39, 0x0A, 0x06,  // 187:1081
+  0x04, 0x43, 0x10, 0x08,  // 188:1091
+  0x04, 0x53, 0x10, 0x08,  // 189:1107
+  0x04, 0x63, 0x10, 0x08,  // 190:1123
+  0x04, 0x73, 0x0A, 0x06,  // 191:1139
+  0x04, 0x7D, 0x0E, 0x07,  // 192:1149
+  0x04, 0x8B, 0x0E, 0x07,  // 193:1163
+  0x04, 0x99, 0x0E, 0x07,  // 194:1177
+  0x04, 0xA7, 0x0E, 0x07,  // 195:1191
+  0x04, 0xB5, 0x0E, 0x07,  // 196:1205
+  0x04, 0xC3, 0x0E, 0x07,  // 197:1219
+  0x04, 0xD1, 0x12, 0x0A,  // 198:1233
+  0x04, 0xE3, 0x0C, 0x07,  // 199:1251
+  0x04, 0xEF, 0x0C, 0x07,  // 200:1263
+  0x04, 0xFB, 0x0C, 0x07,  // 201:1275
+  0x05, 0x07, 0x0C, 0x07,  // 202:1287
+  0x05, 0x13, 0x0C, 0x07,  // 203:1299
+  0x05, 0x1F, 0x05, 0x03,  // 204:1311
+  0x05, 0x24, 0x04, 0x03,  // 205:1316
+  0x05, 0x28, 0x04, 0x03,  // 206:1320
+  0x05, 0x2C, 0x05, 0x03,  // 207:1324
+  0x05, 0x31, 0x0B, 0x07,  // 208:1329
+  0x05, 0x3C, 0x0C, 0x07,  // 209:1340
+  0x05, 0x48, 0x0E, 0x08,  // 210:1352
+  0x05, 0x56, 0x0E, 0x08,  // 211:1366
+  0x05, 0x64, 0x0E, 0x08,  // 212:1380
+  0x05, 0x72, 0x0E, 0x08,  // 213:1394
+  0x05, 0x80, 0x0E, 0x08,  // 214:1408
+  0x05, 0x8E, 0x0A, 0x06,  // 215:1422
+  0x05, 0x98, 0x0D, 0x08,  // 216:1432
+  0x05, 0xA5, 0x0C, 0x07,  // 217:1445
+  0x05, 0xB1, 0x0C, 0x07,  // 218:1457
+  0x05, 0xBD, 0x0C, 0x07,  // 219:1469
+  0x05, 0xC9, 0x0C, 0x07,  // 220:1481
+  0x05, 0xD5, 0x0D, 0x07,  // 221:1493
+  0x05, 0xE2, 0x0B, 0x07,  // 222:1506
+  0x05, 0xED, 0x0C, 0x06,  // 223:1517
+  0x05, 0xF9, 0x0A, 0x06,  // 224:1529
+  0x06, 0x03, 0x0A, 0x06,  // 225:1539
+  0x06, 0x0D, 0x0A, 0x06,  // 226:1549
+  0x06, 0x17, 0x0A, 0x06,  // 227:1559
+  0x06, 0x21, 0x0A, 0x06,  // 228:1569
+  0x06, 0x2B, 0x0A, 0x06,  // 229:1579
+  0x06, 0x35, 0x10, 0x09,  // 230:1589
+  0x06, 0x45, 0x0A, 0x05,  // 231:1605
+  0x06, 0x4F, 0x0A, 0x06,  // 232:1615
+  0x06, 0x59, 0x0A, 0x06,  // 233:1625
+  0x06, 0x63, 0x0A, 0x06,  // 234:1635
+  0x06, 0x6D, 0x0A, 0x06,  // 235:1645
+  0x06, 0x77, 0x05, 0x03,  // 236:1655
+  0x06, 0x7C, 0x04, 0x03,  // 237:1660
+  0x06, 0x80, 0x05, 0x03,  // 238:1664
+  0x06, 0x85, 0x05, 0x03,  // 239:1669
+  0x06, 0x8A, 0x0A, 0x06,  // 240:1674
+  0x06, 0x94, 0x0A, 0x06,  // 241:1684
+  0x06, 0x9E, 0x0A, 0x06,  // 242:1694
+  0x06, 0xA8, 0x0A, 0x06,  // 243:1704
+  0x06, 0xB2, 0x0A, 0x06,  // 244:1714
+  0x06, 0xBC, 0x0A, 0x06,  // 245:1724
+  0x06, 0xC6, 0x0A, 0x06,  // 246:1734
+  0x06, 0xD0, 0x09, 0x05,  // 247:1744
+  0x06, 0xD9, 0x0A, 0x06,  // 248:1753
+  0x06, 0xE3, 0x0A, 0x06,  // 249:1763
+  0x06, 0xED, 0x0A, 0x06,  // 250:1773
+  0x06, 0xF7, 0x0A, 0x06,  // 251:1783
+  0x07, 0x01, 0x0A, 0x06,  // 252:1793
+  0x07, 0x0B, 0x09, 0x05,  // 253:1803
+  0x07, 0x14, 0x0A, 0x06,  // 254:1812
+  0x07, 0x1E, 0x09, 0x05,  // 255:1822
+
+  // Font Data:
+  0x00,0x00,0xF8,0x02,  // 33
+  0x38,0x00,0x00,0x00,0x38, // 34
+  0xA0,0x03,0xE0,0x00,0xB8,0x03,0xE0,0x00,0xB8, // 35
+  0x30,0x01,0x28,0x02,0xF8,0x07,0x48,0x02,0x90,0x01,  // 36
+  0x00,0x00,0x30,0x00,0x48,0x00,0x30,0x03,0xC0,0x00,0xB0,0x01,0x48,0x02,0x80,0x01,  // 37
+  0x80,0x01,0x50,0x02,0x68,0x02,0xA8,0x02,0x18,0x01,0x80,0x03,0x80,0x02,  // 38
+  0x38, // 39
+  0xE0,0x03,0x10,0x04,0x08,0x08,  // 40
+  0x08,0x08,0x10,0x04,0xE0,0x03,  // 41
+  0x28,0x00,0x18,0x00,0x28, // 42
+  0x40,0x00,0x40,0x00,0xF0,0x01,0x40,0x00,0x40, // 43
+  0x00,0x00,0x00,0x06,  // 44
+  0x80,0x00,0x80, // 45
+  0x00,0x00,0x00,0x02,  // 46
+  0x00,0x03,0xE0,0x00,0x18, // 47
+  0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01,  // 48
+  0x00,0x00,0x20,0x00,0x10,0x00,0xF8,0x03,  // 49
+  0x10,0x02,0x08,0x03,0x88,0x02,0x48,0x02,0x30,0x02,  // 50
+  0x10,0x01,0x08,0x02,0x48,0x02,0x48,0x02,0xB0,0x01,  // 51
+  0xC0,0x00,0xA0,0x00,0x90,0x00,0x88,0x00,0xF8,0x03,0x80, // 52
+  0x60,0x01,0x38,0x02,0x28,0x02,0x28,0x02,0xC8,0x01,  // 53
+  0xF0,0x01,0x28,0x02,0x28,0x02,0x28,0x02,0xD0,0x01,  // 54
+  0x08,0x00,0x08,0x03,0xC8,0x00,0x38,0x00,0x08, // 55
+  0xB0,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0xB0,0x01,  // 56
+  0x70,0x01,0x88,0x02,0x88,0x02,0x88,0x02,0xF0,0x01,  // 57
+  0x00,0x00,0x20,0x02,  // 58
+  0x00,0x00,0x20,0x06,  // 59
+  0x00,0x00,0x40,0x00,0xA0,0x00,0xA0,0x00,0x10,0x01,  // 60
+  0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0,0x00,0xA0, // 61
+  0x00,0x00,0x10,0x01,0xA0,0x00,0xA0,0x00,0x40, // 62
+  0x10,0x00,0x08,0x00,0x08,0x00,0xC8,0x02,0x48,0x00,0x30, // 63
+  0x00,0x00,0xC0,0x03,0x30,0x04,0xD0,0x09,0x28,0x0A,0x28,0x0A,0xC8,0x0B,0x68,0x0A,0x10,0x05,0xE0,0x04,  // 64
+  0x00,0x02,0xC0,0x01,0xB0,0x00,0x88,0x00,0xB0,0x00,0xC0,0x01,0x00,0x02,  // 65
+  0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0xF0,0x01,  // 66
+  0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x10,0x01,  // 67
+  0x00,0x00,0xF8,0x03,0x08,0x02,0x08,0x02,0x10,0x01,0xE0, // 68
+  0x00,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,0x48,0x02,  // 69
+  0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x08, // 70
+  0x00,0x00,0xE0,0x00,0x10,0x01,0x08,0x02,0x48,0x02,0x50,0x01,0xC0, // 71
+  0x00,0x00,0xF8,0x03,0x40,0x00,0x40,0x00,0x40,0x00,0xF8,0x03,  // 72
+  0x00,0x00,0xF8,0x03,  // 73
+  0x00,0x03,0x00,0x02,0x00,0x02,0xF8,0x01,  // 74
+  0x00,0x00,0xF8,0x03,0x80,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02,  // 75
+  0x00,0x00,0xF8,0x03,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,  // 76
+  0x00,0x00,0xF8,0x03,0x30,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x30,0x00,0xF8,0x03,  // 77
+  0x00,0x00,0xF8,0x03,0x30,0x00,0x40,0x00,0x80,0x01,0xF8,0x03,  // 78
+  0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x02,0x08,0x02,0xF0,0x01,  // 79
+  0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0x48,0x00,0x30, // 80
+  0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x02,0x08,0x03,0x08,0x03,0xF0,0x02,  // 81
+  0x00,0x00,0xF8,0x03,0x48,0x00,0x48,0x00,0xC8,0x00,0x30,0x03,  // 82
+  0x00,0x00,0x30,0x01,0x48,0x02,0x48,0x02,0x48,0x02,0x90,0x01,  // 83
+  0x00,0x00,0x08,0x00,0x08,0x00,0xF8,0x03,0x08,0x00,0x08, // 84
+  0x00,0x00,0xF8,0x01,0x00,0x02,0x00,0x02,0x00,0x02,0xF8,0x01,  // 85
+  0x08,0x00,0x70,0x00,0x80,0x01,0x00,0x02,0x80,0x01,0x70,0x00,0x08, // 86
+  0x18,0x00,0xE0,0x01,0x00,0x02,0xF0,0x01,0x08,0x00,0xF0,0x01,0x00,0x02,0xE0,0x01,0x18, // 87
+  0x00,0x02,0x08,0x01,0x90,0x00,0x60,0x00,0x90,0x00,0x08,0x01,0x00,0x02,  // 88
+  0x08,0x00,0x10,0x00,0x20,0x00,0xC0,0x03,0x20,0x00,0x10,0x00,0x08, // 89
+  0x08,0x03,0x88,0x02,0xC8,0x02,0x68,0x02,0x38,0x02,0x18,0x02,  // 90
+  0x00,0x00,0xF8,0x0F,0x08,0x08,  // 91
+  0x18,0x00,0xE0,0x00,0x00,0x03,  // 92
+  0x08,0x08,0xF8,0x0F,  // 93
+  0x40,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x40, // 94
+  0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,0x00,0x08,  // 95
+  0x08,0x00,0x10, // 96
+  0x00,0x00,0x00,0x03,0xA0,0x02,0xA0,0x02,0xE0,0x03,  // 97
+  0x00,0x00,0xF8,0x03,0x20,0x02,0x20,0x02,0xC0,0x01,  // 98
+  0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0x40,0x01,  // 99
+  0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xF8,0x03,  // 100
+  0x00,0x00,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02,  // 101
+  0x20,0x00,0xF0,0x03,0x28, // 102
+  0x00,0x00,0xC0,0x05,0x20,0x0A,0x20,0x0A,0xE0,0x07,  // 103
+  0x00,0x00,0xF8,0x03,0x20,0x00,0x20,0x00,0xC0,0x03,  // 104
+  0x00,0x00,0xE8,0x03,  // 105
+  0x00,0x08,0xE8,0x07,  // 106
+  0xF8,0x03,0x80,0x00,0xC0,0x01,0x20,0x02,  // 107
+  0x00,0x00,0xF8,0x03,  // 108
+  0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03,  // 109
+  0x00,0x00,0xE0,0x03,0x20,0x00,0x20,0x00,0xC0,0x03,  // 110
+  0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xC0,0x01,  // 111
+  0x00,0x00,0xE0,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01,  // 112
+  0x00,0x00,0xC0,0x01,0x20,0x02,0x20,0x02,0xE0,0x0F,  // 113
+  0x00,0x00,0xE0,0x03,0x20, // 114
+  0x40,0x02,0xA0,0x02,0xA0,0x02,0x20,0x01,  // 115
+  0x20,0x00,0xF8,0x03,0x20,0x02,  // 116
+  0x00,0x00,0xE0,0x01,0x00,0x02,0x00,0x02,0xE0,0x03,  // 117
+  0x20,0x00,0xC0,0x01,0x00,0x02,0xC0,0x01,0x20, // 118
+  0xE0,0x01,0x00,0x02,0xC0,0x01,0x20,0x00,0xC0,0x01,0x00,0x02,0xE0,0x01,  // 119
+  0x20,0x02,0x40,0x01,0x80,0x00,0x40,0x01,0x20,0x02,  // 120
+  0x20,0x00,0xC0,0x09,0x00,0x06,0xC0,0x01,0x20, // 121
+  0x20,0x02,0x20,0x03,0xA0,0x02,0x60,0x02,0x20,0x02,  // 122
+  0x80,0x00,0x78,0x0F,0x08,0x08,  // 123
+  0x00,0x00,0xF8,0x0F,  // 124
+  0x08,0x08,0x78,0x0F,0x80, // 125
+  0xC0,0x00,0x40,0x00,0xC0,0x00,0x80,0x00,0xC0, // 126
+  0x00,0x00,0xA0,0x0F,  // 161
+  0x00,0x00,0xC0,0x01,0xA0,0x0F,0x78,0x02,0x40,0x01,  // 162
+  0x40,0x02,0x70,0x03,0xC8,0x02,0x48,0x02,0x08,0x02,0x10,0x02,  // 163
+  0x00,0x00,0xE0,0x01,0x20,0x01,0x20,0x01,0xE0,0x01,  // 164
+  0x48,0x01,0x70,0x01,0xC0,0x03,0x70,0x01,0x48,0x01,  // 165
+  0x00,0x00,0x38,0x0F,  // 166
+  0xD0,0x04,0x28,0x09,0x48,0x09,0x48,0x0A,0x90,0x05,  // 167
+  0x08,0x00,0x00,0x00,0x08, // 168
+  0xE0,0x00,0x10,0x01,0x48,0x02,0xA8,0x02,0xA8,0x02,0x10,0x01,0xE0, // 169
+  0x68,0x00,0x68,0x00,0x68,0x00,0x78, // 170
+  0x00,0x00,0x80,0x01,0x40,0x02,0x80,0x01,0x40,0x02,  // 171
+  0x20,0x00,0x20,0x00,0x20,0x00,0x20,0x00,0xE0, // 172
+  0x80,0x00,0x80, // 173
+  0xE0,0x00,0x10,0x01,0xE8,0x02,0x68,0x02,0xC8,0x02,0x10,0x01,0xE0, // 174
+  0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02,0x00,0x02, // 175
+  0x00,0x00,0x38,0x00,0x28,0x00,0x38, // 176
+  0x40,0x02,0x40,0x02,0xF0,0x03,0x40,0x02,0x40,0x02,  // 177
+  0x48,0x00,0x68,0x00,0x58, // 178
+  0x48,0x00,0x58,0x00,0x68, // 179
+  0x00,0x00,0x10,0x00,0x08, // 180
+  0x00,0x00,0xE0,0x0F,0x00,0x02,0x00,0x02,0xE0,0x03,  // 181
+  0x70,0x00,0xF8,0x0F,0x08,0x00,0xF8,0x0F,0x08, // 182
+  0x00,0x00,0x40, // 183
+  0x00,0x00,0x00,0x14,0x00,0x18,  // 184
+  0x00,0x00,0x10,0x00,0x78, // 185
+  0x30,0x00,0x48,0x00,0x48,0x00,0x30, // 186
+  0x00,0x00,0x40,0x02,0x80,0x01,0x40,0x02,0x80,0x01,  // 187
+  0x00,0x00,0x10,0x02,0x78,0x01,0xC0,0x00,0x20,0x01,0x90,0x01,0xC8,0x03,0x00,0x01,  // 188
+  0x00,0x00,0x10,0x02,0x78,0x01,0x80,0x00,0x60,0x00,0x50,0x02,0x48,0x03,0xC0,0x02,  // 189
+  0x48,0x00,0x58,0x00,0x68,0x03,0x80,0x00,0x60,0x01,0x90,0x01,0xC8,0x03,0x00,0x01,  // 190
+  0x00,0x00,0x00,0x06,0x00,0x09,0xA0,0x09,0x00,0x04,  // 191
+  0x00,0x02,0xC0,0x01,0xB0,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02,  // 192
+  0x00,0x02,0xC0,0x01,0xB0,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02,  // 193
+  0x00,0x02,0xC0,0x01,0xB2,0x00,0x89,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02,  // 194
+  0x00,0x02,0xC2,0x01,0xB1,0x00,0x8A,0x00,0xB1,0x00,0xC0,0x01,0x00,0x02,  // 195
+  0x00,0x02,0xC0,0x01,0xB2,0x00,0x88,0x00,0xB2,0x00,0xC0,0x01,0x00,0x02,  // 196
+  0x00,0x02,0xC0,0x01,0xBE,0x00,0x8A,0x00,0xBE,0x00,0xC0,0x01,0x00,0x02,  // 197
+  0x00,0x03,0xC0,0x00,0xE0,0x00,0x98,0x00,0x88,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x48,0x02,  // 198
+  0x00,0x00,0xF0,0x01,0x08,0x02,0x08,0x16,0x08,0x1A,0x10,0x01,  // 199
+  0x00,0x00,0xF8,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02,  // 200
+  0x00,0x00,0xF8,0x03,0x48,0x02,0x4A,0x02,0x49,0x02,0x48,0x02,  // 201
+  0x00,0x00,0xFA,0x03,0x49,0x02,0x4A,0x02,0x48,0x02,0x48,0x02,  // 202
+  0x00,0x00,0xF8,0x03,0x4A,0x02,0x48,0x02,0x4A,0x02,0x48,0x02,  // 203
+  0x00,0x00,0xF9,0x03,0x02, // 204
+  0x02,0x00,0xF9,0x03,  // 205
+  0x01,0x00,0xFA,0x03,  // 206
+  0x02,0x00,0xF8,0x03,0x02, // 207
+  0x40,0x00,0xF8,0x03,0x48,0x02,0x48,0x02,0x10,0x01,0xE0, // 208
+  0x00,0x00,0xFA,0x03,0x31,0x00,0x42,0x00,0x81,0x01,0xF8,0x03,  // 209
+  0x00,0x00,0xF0,0x01,0x08,0x02,0x09,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01,  // 210
+  0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x08,0x02,0xF0,0x01,  // 211
+  0x00,0x00,0xF0,0x01,0x08,0x02,0x0A,0x02,0x09,0x02,0x0A,0x02,0xF0,0x01,  // 212
+  0x00,0x00,0xF0,0x01,0x0A,0x02,0x09,0x02,0x0A,0x02,0x09,0x02,0xF0,0x01,  // 213
+  0x00,0x00,0xF0,0x01,0x0A,0x02,0x08,0x02,0x0A,0x02,0x08,0x02,0xF0,0x01,  // 214
+  0x10,0x01,0xA0,0x00,0xE0,0x00,0xA0,0x00,0x10,0x01,  // 215
+  0x00,0x00,0xF0,0x02,0x08,0x03,0xC8,0x02,0x28,0x02,0x18,0x03,0xE8, // 216
+  0x00,0x00,0xF8,0x01,0x01,0x02,0x02,0x02,0x00,0x02,0xF8,0x01,  // 217
+  0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x00,0x02,0xF8,0x01,  // 218
+  0x00,0x00,0xF8,0x01,0x02,0x02,0x01,0x02,0x02,0x02,0xF8,0x01,  // 219
+  0x00,0x00,0xF8,0x01,0x02,0x02,0x00,0x02,0x02,0x02,0xF8,0x01,  // 220
+  0x08,0x00,0x10,0x00,0x20,0x00,0xC2,0x03,0x21,0x00,0x10,0x00,0x08, // 221
+  0x00,0x00,0xF8,0x03,0x10,0x01,0x10,0x01,0x10,0x01,0xE0, // 222
+  0x00,0x00,0xF0,0x03,0x08,0x01,0x48,0x02,0xB0,0x02,0x80,0x01,  // 223
+  0x00,0x00,0x00,0x03,0xA4,0x02,0xA8,0x02,0xE0,0x03,  // 224
+  0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE0,0x03,  // 225
+  0x00,0x00,0x00,0x03,0xA8,0x02,0xA4,0x02,0xE8,0x03,  // 226
+  0x00,0x00,0x08,0x03,0xA4,0x02,0xA8,0x02,0xE4,0x03,  // 227
+  0x00,0x00,0x00,0x03,0xA8,0x02,0xA0,0x02,0xE8,0x03,  // 228
+  0x00,0x00,0x00,0x03,0xAE,0x02,0xAA,0x02,0xEE,0x03,  // 229
+  0x00,0x00,0x40,0x03,0xA0,0x02,0xA0,0x02,0xC0,0x01,0xA0,0x02,0xA0,0x02,0xC0,0x02,  // 230
+  0x00,0x00,0xC0,0x01,0x20,0x16,0x20,0x1A,0x40,0x01,  // 231
+  0x00,0x00,0xC0,0x01,0xA4,0x02,0xA8,0x02,0xC0,0x02,  // 232
+  0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC0,0x02,  // 233
+  0x00,0x00,0xC0,0x01,0xA8,0x02,0xA4,0x02,0xC8,0x02,  // 234
+  0x00,0x00,0xC0,0x01,0xA8,0x02,0xA0,0x02,0xC8,0x02,  // 235
+  0x00,0x00,0xE4,0x03,0x08, // 236
+  0x08,0x00,0xE4,0x03,  // 237
+  0x08,0x00,0xE4,0x03,0x08, // 238
+  0x08,0x00,0xE0,0x03,0x08, // 239
+  0x00,0x00,0xC0,0x01,0x28,0x02,0x38,0x02,0xE0,0x01,  // 240
+  0x00,0x00,0xE8,0x03,0x24,0x00,0x28,0x00,0xC4,0x03,  // 241
+  0x00,0x00,0xC0,0x01,0x24,0x02,0x28,0x02,0xC0,0x01,  // 242
+  0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC0,0x01,  // 243
+  0x00,0x00,0xC0,0x01,0x28,0x02,0x24,0x02,0xC8,0x01,  // 244
+  0x00,0x00,0xC8,0x01,0x24,0x02,0x28,0x02,0xC4,0x01,  // 245
+  0x00,0x00,0xC0,0x01,0x28,0x02,0x20,0x02,0xC8,0x01,  // 246
+  0x40,0x00,0x40,0x00,0x50,0x01,0x40,0x00,0x40, // 247
+  0x00,0x00,0xC0,0x02,0xA0,0x03,0x60,0x02,0xA0,0x01,  // 248
+  0x00,0x00,0xE0,0x01,0x04,0x02,0x08,0x02,0xE0,0x03,  // 249
+  0x00,0x00,0xE0,0x01,0x08,0x02,0x04,0x02,0xE0,0x03,  // 250
+  0x00,0x00,0xE8,0x01,0x04,0x02,0x08,0x02,0xE0,0x03,  // 251
+  0x00,0x00,0xE0,0x01,0x08,0x02,0x00,0x02,0xE8,0x03,  // 252
+  0x20,0x00,0xC0,0x09,0x08,0x06,0xC4,0x01,0x20, // 253
+  0x00,0x00,0xF8,0x0F,0x20,0x02,0x20,0x02,0xC0,0x01,  // 254
+  0x20,0x00,0xC8,0x09,0x00,0x06,0xC8,0x01,0x20  // 255
+};
+
+const char ArialMT_Plain_16[] PROGMEM = {
+  0x10, // Width: 16
+  0x13, // Height: 19
+  0x20, // First Char: 32
+  0xE0, // Numbers of Chars: 224
+
+  // Jump Table:
+  0xFF, 0xFF, 0x00, 0x04,  // 32:65535
+  0x00, 0x00, 0x08, 0x04,  // 33:0
+  0x00, 0x08, 0x0D, 0x06,  // 34:8
+  0x00, 0x15, 0x1A, 0x09,  // 35:21
+  0x00, 0x2F, 0x17, 0x09,  // 36:47
+  0x00, 0x46, 0x26, 0x0E,  // 37:70
+  0x00, 0x6C, 0x1D, 0x0B,  // 38:108
+  0x00, 0x89, 0x04, 0x03,  // 39:137
+  0x00, 0x8D, 0x0C, 0x05,  // 40:141
+  0x00, 0x99, 0x0B, 0x05,  // 41:153
+  0x00, 0xA4, 0x0D, 0x06,  // 42:164
+  0x00, 0xB1, 0x17, 0x09,  // 43:177
+  0x00, 0xC8, 0x09, 0x04,  // 44:200
+  0x00, 0xD1, 0x0B, 0x05,  // 45:209
+  0x00, 0xDC, 0x08, 0x04,  // 46:220
+  0x00, 0xE4, 0x0A, 0x04,  // 47:228
+  0x00, 0xEE, 0x17, 0x09,  // 48:238
+  0x01, 0x05, 0x11, 0x09,  // 49:261
+  0x01, 0x16, 0x17, 0x09,  // 50:278
+  0x01, 0x2D, 0x17, 0x09,  // 51:301
+  0x01, 0x44, 0x17, 0x09,  // 52:324
+  0x01, 0x5B, 0x17, 0x09,  // 53:347
+  0x01, 0x72, 0x17, 0x09,  // 54:370
+  0x01, 0x89, 0x16, 0x09,  // 55:393
+  0x01, 0x9F, 0x17, 0x09,  // 56:415
+  0x01, 0xB6, 0x17, 0x09,  // 57:438
+  0x01, 0xCD, 0x05, 0x04,  // 58:461
+  0x01, 0xD2, 0x06, 0x04,  // 59:466
+  0x01, 0xD8, 0x17, 0x09,  // 60:472
+  0x01, 0xEF, 0x17, 0x09,  // 61:495
+  0x02, 0x06, 0x17, 0x09,  // 62:518
+  0x02, 0x1D, 0x16, 0x09,  // 63:541
+  0x02, 0x33, 0x2F, 0x10,  // 64:563
+  0x02, 0x62, 0x1D, 0x0B,  // 65:610
+  0x02, 0x7F, 0x1D, 0x0B,  // 66:639
+  0x02, 0x9C, 0x20, 0x0C,  // 67:668
+  0x02, 0xBC, 0x20, 0x0C,  // 68:700
+  0x02, 0xDC, 0x1D, 0x0B,  // 69:732
+  0x02, 0xF9, 0x19, 0x0A,  // 70:761
+  0x03, 0x12, 0x20, 0x0C,  // 71:786
+  0x03, 0x32, 0x1D, 0x0C,  // 72:818
+  0x03, 0x4F, 0x05, 0x04,  // 73:847
+  0x03, 0x54, 0x14, 0x08,  // 74:852
+  0x03, 0x68, 0x1D, 0x0B,  // 75:872
+  0x03, 0x85, 0x17, 0x09,  // 76:901
+  0x03, 0x9C, 0x23, 0x0D,  // 77:924
+  0x03, 0xBF, 0x1D, 0x0C,  // 78:959
+  0x03, 0xDC, 0x20, 0x0C,  // 79:988
+  0x03, 0xFC, 0x1C, 0x0B,  // 80:1020
+  0x04, 0x18, 0x20, 0x0C,  // 81:1048
+  0x04, 0x38, 0x1D, 0x0C,  // 82:1080
+  0x04, 0x55, 0x1D, 0x0B,  // 83:1109
+  0x04, 0x72, 0x19, 0x0A,  // 84:1138
+  0x04, 0x8B, 0x1D, 0x0C,  // 85:1163
+  0x04, 0xA8, 0x1C, 0x0B,  // 86:1192
+  0x04, 0xC4, 0x2B, 0x0F,  // 87:1220
+  0x04, 0xEF, 0x20, 0x0B,  // 88:1263
+  0x05, 0x0F, 0x19, 0x0B,  // 89:1295
+  0x05, 0x28, 0x1A, 0x0A,  // 90:1320
+  0x05, 0x42, 0x0C, 0x04,  // 91:1346
+  0x05, 0x4E, 0x0B, 0x04,  // 92:1358
+  0x05, 0x59, 0x09, 0x04,  // 93:1369
+  0x05, 0x62, 0x14, 0x08,  // 94:1378
+  0x05, 0x76, 0x1B, 0x09,  // 95:1398
+  0x05, 0x91, 0x07, 0x05,  // 96:1425
+  0x05, 0x98, 0x17, 0x09,  // 97:1432
+  0x05, 0xAF, 0x17, 0x09,  // 98:1455
+  0x05, 0xC6, 0x14, 0x08,  // 99:1478
+  0x05, 0xDA, 0x17, 0x09,  // 100:1498
+  0x05, 0xF1, 0x17, 0x09,  // 101:1521
+  0x06, 0x08, 0x0A, 0x04,  // 102:1544
+  0x06, 0x12, 0x17, 0x09,  // 103:1554
+  0x06, 0x29, 0x14, 0x09,  // 104:1577
+  0x06, 0x3D, 0x05, 0x04,  // 105:1597
+  0x06, 0x42, 0x06, 0x04,  // 106:1602
+  0x06, 0x48, 0x17, 0x08,  // 107:1608
+  0x06, 0x5F, 0x05, 0x04,  // 108:1631
+  0x06, 0x64, 0x23, 0x0D,  // 109:1636
+  0x06, 0x87, 0x14, 0x09,  // 110:1671
+  0x06, 0x9B, 0x17, 0x09,  // 111:1691
+  0x06, 0xB2, 0x17, 0x09,  // 112:1714
+  0x06, 0xC9, 0x18, 0x09,  // 113:1737
+  0x06, 0xE1, 0x0D, 0x05,  // 114:1761
+  0x06, 0xEE, 0x14, 0x08,  // 115:1774
+  0x07, 0x02, 0x0B, 0x04,  // 116:1794
+  0x07, 0x0D, 0x14, 0x09,  // 117:1805
+  0x07, 0x21, 0x13, 0x08,  // 118:1825
+  0x07, 0x34, 0x1F, 0x0C,  // 119:1844
+  0x07, 0x53, 0x14, 0x08,  // 120:1875
+  0x07, 0x67, 0x13, 0x08,  // 121:1895
+  0x07, 0x7A, 0x14, 0x08,  // 122:1914
+  0x07, 0x8E, 0x0F, 0x05,  // 123:1934
+  0x07, 0x9D, 0x06, 0x04,  // 124:1949
+  0x07, 0xA3, 0x0E, 0x05,  // 125:1955
+  0x07, 0xB1, 0x17, 0x09,  // 126:1969
+  0xFF, 0xFF, 0x00, 0x00,  // 127:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 128:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 129:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 130:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 131:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 132:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 133:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 134:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 135:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 136:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 137:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 138:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 139:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 140:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 141:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 142:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 143:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 144:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 145:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 146:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 147:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 148:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 149:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 150:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 151:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 152:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 153:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 154:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 155:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 156:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 157:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 158:65535
+  0xFF, 0xFF, 0x00, 0x10,  // 159:65535
+  0xFF, 0xFF, 0x00, 0x04,  // 160:65535
+  0x07, 0xC8, 0x09, 0x05,  // 161:1992
+  0x07, 0xD1, 0x17, 0x09,  // 162:2001
+  0x07, 0xE8, 0x17, 0x09,  // 163:2024
+  0x07, 0xFF, 0x14, 0x09,  // 164:2047
+  0x08, 0x13, 0x1A, 0x09,  // 165:2067
+  0x08, 0x2D, 0x06, 0x04,  // 166:2093
+  0x08, 0x33, 0x17, 0x09,  // 167:2099
+  0x08, 0x4A, 0x07, 0x05,  // 168:2122
+  0x08, 0x51, 0x23, 0x0C,  // 169:2129
+  0x08, 0x74, 0x0E, 0x06,  // 170:2164
+  0x08, 0x82, 0x14, 0x09,  // 171:2178
+  0x08, 0x96, 0x17, 0x09,  // 172:2198
+  0x08, 0xAD, 0x0B, 0x05,  // 173:2221
+  0x08, 0xB8, 0x23, 0x0C,  // 174:2232
+  0x08, 0xDB, 0x19, 0x09,  // 175:2267
+  0x08, 0xF4, 0x0D, 0x06,  // 176:2292
+  0x09, 0x01, 0x17, 0x09,  // 177:2305
+  0x09, 0x18, 0x0E, 0x05,  // 178:2328
+  0x09, 0x26, 0x0D, 0x05,  // 179:2342
+  0x09, 0x33, 0x0A, 0x05,  // 180:2355
+  0x09, 0x3D, 0x17, 0x09,  // 181:2365
+  0x09, 0x54, 0x19, 0x09,  // 182:2388
+  0x09, 0x6D, 0x08, 0x05,  // 183:2413
+  0x09, 0x75, 0x0C, 0x05,  // 184:2421
+  0x09, 0x81, 0x0B, 0x05,  // 185:2433
+  0x09, 0x8C, 0x0D, 0x06,  // 186:2444
+  0x09, 0x99, 0x17, 0x09,  // 187:2457
+  0x09, 0xB0, 0x26, 0x0D,  // 188:2480
+  0x09, 0xD6, 0x26, 0x0D,  // 189:2518
+  0x09, 0xFC, 0x26, 0x0D,  // 190:2556
+  0x0A, 0x22, 0x1A, 0x0A,  // 191:2594
+  0x0A, 0x3C, 0x1D, 0x0B,  // 192:2620
+  0x0A, 0x59, 0x1D, 0x0B,  // 193:2649
+  0x0A, 0x76, 0x1D, 0x0B,  // 194:2678
+  0x0A, 0x93, 0x1D, 0x0B,  // 195:2707
+  0x0A, 0xB0, 0x1D, 0x0B,  // 196:2736
+  0x0A, 0xCD, 0x1D, 0x0B,  // 197:2765
+  0x0A, 0xEA, 0x2C, 0x10,  // 198:2794
+  0x0B, 0x16, 0x20, 0x0C,  // 199:2838
+  0x0B, 0x36, 0x1D, 0x0B,  // 200:2870
+  0x0B, 0x53, 0x1D, 0x0B,  // 201:2899
+  0x0B, 0x70, 0x1D, 0x0B,  // 202:2928
+  0x0B, 0x8D, 0x1D, 0x0B,  // 203:2957
+  0x0B, 0xAA, 0x05, 0x04,  // 204:2986
+  0x0B, 0xAF, 0x07, 0x04,  // 205:2991
+  0x0B, 0xB6, 0x0A, 0x04,  // 206:2998
+  0x0B, 0xC0, 0x07, 0x04,  // 207:3008
+  0x0B, 0xC7, 0x20, 0x0C,  // 208:3015
+  0x0B, 0xE7, 0x1D, 0x0C,  // 209:3047
+  0x0C, 0x04, 0x20, 0x0C,  // 210:3076
+  0x0C, 0x24, 0x20, 0x0C,  // 211:3108
+  0x0C, 0x44, 0x20, 0x0C,  // 212:3140
+  0x0C, 0x64, 0x20, 0x0C,  // 213:3172
+  0x0C, 0x84, 0x20, 0x0C,  // 214:3204
+  0x0C, 0xA4, 0x17, 0x09,  // 215:3236
+  0x0C, 0xBB, 0x20, 0x0C,  // 216:3259
+  0x0C, 0xDB, 0x1D, 0x0C,  // 217:3291
+  0x0C, 0xF8, 0x1D, 0x0C,  // 218:3320
+  0x0D, 0x15, 0x1D, 0x0C,  // 219:3349
+  0x0D, 0x32, 0x1D, 0x0C,  // 220:3378
+  0x0D, 0x4F, 0x19, 0x0B,  // 221:3407
+  0x0D, 0x68, 0x1D, 0x0B,  // 222:3432
+  0x0D, 0x85, 0x17, 0x0A,  // 223:3461
+  0x0D, 0x9C, 0x17, 0x09,  // 224:3484
+  0x0D, 0xB3, 0x17, 0x09,  // 225:3507
+  0x0D, 0xCA, 0x17, 0x09,  // 226:3530
+  0x0D, 0xE1, 0x17, 0x09,  // 227:3553
+  0x0D, 0xF8, 0x17, 0x09,  // 228:3576
+  0x0E, 0x0F, 0x17, 0x09,  // 229:3599
+  0x0E, 0x26, 0x29, 0x0E,  // 230:3622
+  0x0E, 0x4F, 0x14, 0x08,  // 231:3663
+  0x0E, 0x63, 0x17, 0x09,  // 232:3683
+  0x0E, 0x7A, 0x17, 0x09,  // 233:3706
+  0x0E, 0x91, 0x17, 0x09,  // 234:3729
+  0x0E, 0xA8, 0x17, 0x09,  // 235:3752
+  0x0E, 0xBF, 0x05, 0x04,  // 236:3775
+  0x0E, 0xC4, 0x07, 0x04,  // 237:3780
+  0x0E, 0xCB, 0x0A, 0x04,  // 238:3787
+  0x0E, 0xD5, 0x07, 0x04,  // 239:3797
+  0x0E, 0xDC, 0x17, 0x09,  // 240:3804
+  0x0E, 0xF3, 0x14, 0x09,  // 241:3827
+  0x0F, 0x07, 0x17, 0x09,  // 242:3847
+  0x0F, 0x1E, 0x17, 0x09,  // 243:3870
+  0x0F, 0x35, 0x17, 0x09,  // 244:3893
+  0x0F, 0x4C, 0x17, 0x09,  // 245:3916
+  0x0F, 0x63, 0x17, 0x09,  // 246:3939
+  0x0F, 0x7A, 0x17, 0x09,  // 247:3962
+  0x0F, 0x91, 0x17, 0x0A,  // 248:3985
+  0x0F, 0xA8, 0x14, 0x09,  // 249:4008
+  0x0F, 0xBC, 0x14, 0x09,  // 250:4028
+  0x0F, 0xD0, 0x14, 0x09,  // 251:4048
+  0x0F, 0xE4, 0x14, 0x09,  // 252:4068
+  0x0F, 0xF8, 0x13, 0x08,  // 253:4088
+  0x10, 0x0B, 0x17, 0x09,  // 254:4107
+  0x10, 0x22, 0x13, 0x08,  // 255:4130
+
+  // Font Data:
+  0x00,0x00,0x00,0x00,0x00,0x00,0xF8,0x5F,  // 33
+  0x00,0x00,0x00,0x78,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x78, // 34
+  0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08,0x00,0x80,0x78,0x00,0xC0,0x0F,0x00,0xB8,0x08,0x00,0x80,0x08,  // 35
+  0x00,0x00,0x00,0xE0,0x10,0x00,0x10,0x21,0x00,0x08,0x41,0x00,0xFC,0xFF,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 36
+  0x00,0x00,0x00,0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x61,0x00,0xF0,0x18,0x00,0x00,0x06,0x00,0xC0,0x01,0x00,0x30,0x3C,0x00,0x08,0x42,0x00,0x00,0x42,0x00,0x00,0x42,0x00,0x00,0x3C,  // 37
+  0x00,0x00,0x00,0x00,0x1C,0x00,0x70,0x22,0x00,0x88,0x41,0x00,0x08,0x43,0x00,0x88,0x44,0x00,0x70,0x28,0x00,0x00,0x10,0x00,0x00,0x28,0x00,0x00,0x44, // 38
+  0x00,0x00,0x00,0x78,  // 39
+  0x00,0x00,0x00,0x80,0x3F,0x00,0x70,0xC0,0x01,0x08,0x00,0x02,  // 40
+  0x00,0x00,0x00,0x08,0x00,0x02,0x70,0xC0,0x01,0x80,0x3F, // 41
+  0x10,0x00,0x00,0xD0,0x00,0x00,0x38,0x00,0x00,0xD0,0x00,0x00,0x10, // 42
+  0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0xC0,0x1F,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 43
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC0,0x01, // 44
+  0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 45
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x40,  // 46
+  0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18,  // 47
+  0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xE0,0x1F, // 48
+  0x00,0x00,0x00,0x00,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0x00,0x10,0x00,0x00,0xF8,0x7F, // 49
+  0x00,0x00,0x00,0x20,0x40,0x00,0x10,0x60,0x00,0x08,0x50,0x00,0x08,0x48,0x00,0x08,0x44,0x00,0x10,0x43,0x00,0xE0,0x40, // 50
+  0x00,0x00,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x88,0x41,0x00,0xF0,0x22,0x00,0x00,0x1C, // 51
+  0x00,0x0C,0x00,0x00,0x0A,0x00,0x00,0x09,0x00,0xC0,0x08,0x00,0x20,0x08,0x00,0x10,0x08,0x00,0xF8,0x7F,0x00,0x00,0x08, // 52
+  0x00,0x00,0x00,0xC0,0x11,0x00,0xB8,0x20,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x08,0x21,0x00,0x08,0x1E, // 53
+  0x00,0x00,0x00,0xE0,0x1F,0x00,0x10,0x21,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x88,0x40,0x00,0x10,0x21,0x00,0x20,0x1E, // 54
+  0x00,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x78,0x00,0x08,0x07,0x00,0xC8,0x00,0x00,0x28,0x00,0x00,0x18,  // 55
+  0x00,0x00,0x00,0x60,0x1C,0x00,0x90,0x22,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 56
+  0x00,0x00,0x00,0xE0,0x11,0x00,0x10,0x22,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x08,0x44,0x00,0x10,0x22,0x00,0xE0,0x1F, // 57
+  0x00,0x00,0x00,0x40,0x40, // 58
+  0x00,0x00,0x00,0x40,0xC0,0x01,  // 59
+  0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x40,0x10, // 60
+  0x00,0x00,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08, // 61
+  0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x05,0x00,0x00,0x02, // 62
+  0x00,0x00,0x00,0x60,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0x08,0x5C,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0,  // 63
+  0x00,0x00,0x00,0x00,0x3F,0x00,0xC0,0x40,0x00,0x20,0x80,0x00,0x10,0x1E,0x01,0x10,0x21,0x01,0x88,0x40,0x02,0x48,0x40,0x02,0x48,0x40,0x02,0x48,0x20,0x02,0x88,0x7C,0x02,0xC8,0x43,0x02,0x10,0x40,0x02,0x10,0x20,0x01,0x60,0x10,0x01,0x80,0x8F, // 64
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x08,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 65
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x90,0x22,0x00,0x60,0x1C, // 66
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,  // 67
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F,  // 68
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 69
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08, // 70
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x12,0x00,0x00,0x0E,  // 71
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0xF8,0x7F, // 72
+  0x00,0x00,0x00,0xF8,0x7F, // 73
+  0x00,0x00,0x00,0x00,0x38,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0xF8,0x3F,  // 74
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x04,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0x80,0x03,0x00,0x40,0x04,0x00,0x20,0x18,0x00,0x10,0x20,0x00,0x08,0x40, // 75
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40, // 76
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0xF8,0x7F, // 77
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x80,0x00,0x00,0x00,0x03,0x00,0x00,0x04,0x00,0x00,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 78
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F,  // 79
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x10,0x01,0x00,0xE0,  // 80
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x08,0x50,0x00,0x08,0x50,0x00,0x10,0x20,0x00,0x20,0x70,0x00,0xC0,0x4F,  // 81
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x02,0x00,0x08,0x06,0x00,0x08,0x1A,0x00,0x10,0x21,0x00,0xE0,0x40, // 82
+  0x00,0x00,0x00,0x60,0x10,0x00,0x90,0x20,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x10,0x22,0x00,0x20,0x1C, // 83
+  0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0x7F,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 84
+  0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 85
+  0x00,0x00,0x00,0x18,0x00,0x00,0xE0,0x00,0x00,0x00,0x07,0x00,0x00,0x18,0x00,0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x07,0x00,0xE0,0x00,0x00,0x18,  // 86
+  0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x03,0x00,0x70,0x00,0x00,0x08,0x00,0x00,0x70,0x00,0x00,0x80,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1E,0x00,0xE0,0x01,0x00,0x18, // 87
+  0x00,0x40,0x00,0x08,0x20,0x00,0x10,0x10,0x00,0x60,0x0C,0x00,0x80,0x02,0x00,0x00,0x01,0x00,0x80,0x02,0x00,0x60,0x0C,0x00,0x10,0x10,0x00,0x08,0x20,0x00,0x00,0x40,  // 88
+  0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x00,0x7E,0x00,0x80,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 89
+  0x00,0x40,0x00,0x08,0x60,0x00,0x08,0x58,0x00,0x08,0x44,0x00,0x08,0x43,0x00,0x88,0x40,0x00,0x68,0x40,0x00,0x18,0x40,0x00,0x08,0x40,  // 90
+  0x00,0x00,0x00,0xF8,0xFF,0x03,0x08,0x00,0x02,0x08,0x00,0x02,  // 91
+  0x18,0x00,0x00,0xE0,0x01,0x00,0x00,0x1E,0x00,0x00,0x60, // 92
+  0x08,0x00,0x02,0x08,0x00,0x02,0xF8,0xFF,0x03, // 93
+  0x00,0x01,0x00,0xC0,0x00,0x00,0x30,0x00,0x00,0x08,0x00,0x00,0x30,0x00,0x00,0xC0,0x00,0x00,0x00,0x01,  // 94
+  0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 95
+  0x00,0x00,0x00,0x08,0x00,0x00,0x10, // 96
+  0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 97
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 98
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,  // 99
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xF8,0x7F, // 100
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 101
+  0x40,0x00,0x00,0xF0,0x7F,0x00,0x48,0x00,0x00,0x48,  // 102
+  0x00,0x00,0x00,0x00,0x1F,0x01,0x80,0x20,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x40,0x40,0x02,0x80,0x20,0x01,0xC0,0xFF, // 103
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F,  // 104
+  0x00,0x00,0x00,0xC8,0x7F, // 105
+  0x00,0x00,0x02,0xC8,0xFF,0x01,  // 106
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x06,0x00,0x00,0x19,0x00,0x80,0x20,0x00,0x40,0x40, // 107
+  0x00,0x00,0x00,0xF8,0x7F, // 108
+  0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F, // 109
+  0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x80,0x7F,  // 110
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 111
+  0x00,0x00,0x00,0xC0,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 112
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0xC0,0xFF,0x03,  // 113
+  0x00,0x00,0x00,0xC0,0x7F,0x00,0x80,0x00,0x00,0x40,0x00,0x00,0x40, // 114
+  0x00,0x00,0x00,0x80,0x23,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x38,  // 115
+  0x40,0x00,0x00,0xF0,0x7F,0x00,0x40,0x40,0x00,0x40,0x40, // 116
+  0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F,  // 117
+  0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0, // 118
+  0xC0,0x00,0x00,0x00,0x1F,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x00,0x03,0x00,0xC0,0x00,0x00,0x00,0x03,0x00,0x00,0x1C,0x00,0x00,0x60,0x00,0x00,0x1F,0x00,0xC0, // 119
+  0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x40,0x40,  // 120
+  0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x00,0xE0,0x01,0x00,0x38,0x00,0x00,0x07,0x00,0xC0, // 121
+  0x40,0x40,0x00,0x40,0x60,0x00,0x40,0x58,0x00,0x40,0x44,0x00,0x40,0x43,0x00,0xC0,0x40,0x00,0x40,0x40,  // 122
+  0x00,0x04,0x00,0x00,0x04,0x00,0xF0,0xFB,0x01,0x08,0x00,0x02,0x08,0x00,0x02, // 123
+  0x00,0x00,0x00,0xF8,0xFF,0x03,  // 124
+  0x08,0x00,0x02,0x08,0x00,0x02,0xF0,0xFB,0x01,0x00,0x04,0x00,0x00,0x04,  // 125
+  0x00,0x02,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x01,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01, // 126
+  0x00,0x00,0x00,0x00,0x00,0x00,0x40,0xFF,0x03, // 161
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x03,0x40,0xF0,0x00,0x40,0x4E,0x00,0xC0,0x41,0x00,0xB8,0x20,0x00,0x00,0x11, // 162
+  0x00,0x41,0x00,0xE0,0x31,0x00,0x10,0x2F,0x00,0x08,0x21,0x00,0x08,0x21,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x20,0x20, // 163
+  0x00,0x00,0x00,0x40,0x0B,0x00,0x80,0x04,0x00,0x40,0x08,0x00,0x40,0x08,0x00,0x80,0x04,0x00,0x40,0x0B,  // 164
+  0x08,0x0A,0x00,0x10,0x0A,0x00,0x60,0x0A,0x00,0x80,0x0B,0x00,0x00,0x7E,0x00,0x80,0x0B,0x00,0x60,0x0A,0x00,0x10,0x0A,0x00,0x08,0x0A,  // 165
+  0x00,0x00,0x00,0xF8,0xF1,0x03,  // 166
+  0x00,0x86,0x00,0x70,0x09,0x01,0xC8,0x10,0x02,0x88,0x10,0x02,0x08,0x21,0x02,0x08,0x61,0x02,0x30,0xD2,0x01,0x00,0x0C, // 167
+  0x08,0x00,0x00,0x00,0x00,0x00,0x08, // 168
+  0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xC8,0x47,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x28,0x48,0x00,0x48,0x44,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 169
+  0xD0,0x00,0x00,0x48,0x01,0x00,0x28,0x01,0x00,0x28,0x01,0x00,0xF0,0x01,  // 170
+  0x00,0x00,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,0x00,0x00,0x04,0x00,0x00,0x1B,0x00,0x80,0x20,  // 171
+  0x00,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x00,0x00,0x80,0x0F, // 172
+  0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08,0x00,0x00,0x08, // 173
+  0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0xE8,0x4F,0x00,0x28,0x41,0x00,0x28,0x41,0x00,0x28,0x43,0x00,0x28,0x45,0x00,0xC8,0x48,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F, // 174
+  0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04,0x00,0x00,0x04, // 175
+  0x00,0x00,0x00,0x30,0x00,0x00,0x48,0x00,0x00,0x48,0x00,0x00,0x30, // 176
+  0x00,0x00,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0xE0,0x4F,0x00,0x00,0x41,0x00,0x00,0x41,0x00,0x00,0x41, // 177
+  0x10,0x01,0x00,0x88,0x01,0x00,0x48,0x01,0x00,0x48,0x01,0x00,0x30,0x01,  // 178
+  0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x28,0x01,0x00,0xD8, // 179
+  0x00,0x00,0x00,0x00,0x00,0x00,0x10,0x00,0x00,0x08,  // 180
+  0x00,0x00,0x00,0xC0,0xFF,0x03,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F, // 181
+  0xF0,0x00,0x00,0xF8,0x00,0x00,0xF8,0x01,0x00,0xF8,0x01,0x00,0xF8,0xFF,0x03,0x08,0x00,0x00,0x08,0x00,0x00,0xF8,0xFF,0x03,0x08, // 182
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,  // 183
+  0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x80,0x02,0x00,0x00,0x03,  // 184
+  0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x00,0x00,0xF8,0x01, // 185
+  0xF0,0x00,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0x08,0x01,0x00,0xF0, // 186
+  0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04,0x00,0x80,0x20,0x00,0x00,0x1B,0x00,0x00,0x04, // 187
+  0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x21,0x00,0x00,0x10,0x00,0x00,0x0C,0x00,0x00,0x02,0x00,0x80,0x01,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20,  // 188
+  0x00,0x00,0x00,0x10,0x00,0x00,0x08,0x40,0x00,0xF8,0x31,0x00,0x00,0x08,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x60,0x44,0x00,0x10,0x62,0x00,0x08,0x52,0x00,0x00,0x52,0x00,0x00,0x4C,  // 189
+  0x90,0x00,0x00,0x08,0x01,0x00,0x08,0x41,0x00,0x28,0x21,0x00,0xD8,0x18,0x00,0x00,0x04,0x00,0x00,0x03,0x00,0x80,0x00,0x00,0x40,0x30,0x00,0x30,0x28,0x00,0x08,0x24,0x00,0x00,0x7E,0x00,0x00,0x20,  // 190
+  0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x10,0x01,0x00,0x08,0x02,0x40,0x07,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x01,0x00,0xC0,  // 191
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x71,0x04,0x00,0x0A,0x04,0x00,0x70,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 192
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x70,0x04,0x00,0x0A,0x04,0x00,0x71,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 193
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x71,0x04,0x00,0x82,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 194
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x09,0x04,0x00,0x72,0x04,0x00,0x81,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 195
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x72,0x04,0x00,0x08,0x04,0x00,0x72,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 196
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x1C,0x00,0x80,0x07,0x00,0x7E,0x04,0x00,0x0A,0x04,0x00,0x7E,0x04,0x00,0x80,0x07,0x00,0x00,0x1C,0x00,0x00,0x60, // 197
+  0x00,0x60,0x00,0x00,0x18,0x00,0x00,0x06,0x00,0x80,0x05,0x00,0x60,0x04,0x00,0x18,0x04,0x00,0x08,0x04,0x00,0x08,0x04,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,  // 198
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x08,0x40,0x02,0x08,0xC0,0x02,0x08,0x40,0x03,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,  // 199
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 200
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 201
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x09,0x41,0x00,0x09,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 202
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x0A,0x41,0x00,0x08,0x41,0x00,0x08,0x41,0x00,0x08,0x40, // 203
+  0x01,0x00,0x00,0xFA,0x7F, // 204
+  0x00,0x00,0x00,0xFA,0x7F,0x00,0x01, // 205
+  0x02,0x00,0x00,0xF9,0x7F,0x00,0x01,0x00,0x00,0x02,  // 206
+  0x02,0x00,0x00,0xF8,0x7F,0x00,0x02, // 207
+  0x00,0x02,0x00,0xF8,0x7F,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x42,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F,  // 208
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x10,0x00,0x00,0x60,0x00,0x00,0x82,0x00,0x00,0x01,0x03,0x00,0x02,0x04,0x00,0x01,0x18,0x00,0x00,0x20,0x00,0xF8,0x7F, // 209
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F,  // 210
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F,  // 211
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F,  // 212
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x0A,0x40,0x00,0x09,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F,  // 213
+  0x00,0x00,0x00,0xC0,0x0F,0x00,0x20,0x10,0x00,0x10,0x20,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x08,0x40,0x00,0x0A,0x40,0x00,0x10,0x20,0x00,0x20,0x10,0x00,0xC0,0x0F,  // 214
+  0x00,0x00,0x00,0x40,0x10,0x00,0x80,0x08,0x00,0x00,0x05,0x00,0x00,0x07,0x00,0x00,0x05,0x00,0x80,0x08,0x00,0x40,0x10, // 215
+  0x00,0x00,0x00,0xC0,0x4F,0x00,0x20,0x30,0x00,0x10,0x30,0x00,0x08,0x4C,0x00,0x08,0x42,0x00,0x08,0x41,0x00,0xC8,0x40,0x00,0x30,0x20,0x00,0x30,0x10,0x00,0xC8,0x0F,  // 216
+  0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 217
+  0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 218
+  0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x01,0x40,0x00,0x01,0x40,0x00,0x02,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 219
+  0x00,0x00,0x00,0xF8,0x1F,0x00,0x00,0x20,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x02,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xF8,0x1F, // 220
+  0x08,0x00,0x00,0x30,0x00,0x00,0x40,0x00,0x00,0x80,0x01,0x00,0x02,0x7E,0x00,0x81,0x01,0x00,0x40,0x00,0x00,0x30,0x00,0x00,0x08, // 221
+  0x00,0x00,0x00,0xF8,0x7F,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x20,0x10,0x00,0x40,0x08,0x00,0x80,0x07, // 222
+  0x00,0x00,0x00,0xE0,0x7F,0x00,0x10,0x00,0x00,0x08,0x20,0x00,0x88,0x43,0x00,0x70,0x42,0x00,0x00,0x44,0x00,0x00,0x38, // 223
+  0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 224
+  0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 225
+  0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x42,0x00,0x50,0x22,0x00,0x80,0x7F, // 226
+  0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x50,0x42,0x00,0x48,0x22,0x00,0x80,0x7F, // 227
+  0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 228
+  0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x5C,0x44,0x00,0x54,0x44,0x00,0x5C,0x42,0x00,0x40,0x22,0x00,0x80,0x7F, // 229
+  0x00,0x00,0x00,0x00,0x39,0x00,0x80,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x40,0x22,0x00,0x80,0x3F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 230
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x02,0x40,0xC0,0x02,0x40,0x40,0x03,0x80,0x20,  // 231
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x48,0x44,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 232
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 233
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x48,0x44,0x00,0x48,0x44,0x00,0x90,0x24,0x00,0x00,0x17, // 234
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x24,0x00,0x50,0x44,0x00,0x40,0x44,0x00,0x50,0x44,0x00,0x80,0x24,0x00,0x00,0x17, // 235
+  0x08,0x00,0x00,0xD0,0x7F, // 236
+  0x00,0x00,0x00,0xD0,0x7F,0x00,0x08, // 237
+  0x10,0x00,0x00,0xC8,0x7F,0x00,0x08,0x00,0x00,0x10,  // 238
+  0x10,0x00,0x00,0xC0,0x7F,0x00,0x10, // 239
+  0x00,0x00,0x00,0x00,0x1F,0x00,0xA0,0x20,0x00,0x68,0x40,0x00,0x58,0x40,0x00,0x70,0x40,0x00,0xE8,0x20,0x00,0x00,0x1F, // 240
+  0x00,0x00,0x00,0xC0,0x7F,0x00,0x90,0x00,0x00,0x48,0x00,0x00,0x50,0x00,0x00,0x48,0x00,0x00,0x80,0x7F,  // 241
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 242
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 243
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x48,0x40,0x00,0x90,0x20,0x00,0x00,0x1F, // 244
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x48,0x40,0x00,0x50,0x40,0x00,0x88,0x20,0x00,0x00,0x1F, // 245
+  0x00,0x00,0x00,0x00,0x1F,0x00,0x80,0x20,0x00,0x50,0x40,0x00,0x40,0x40,0x00,0x50,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 246
+  0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x80,0x0A,0x00,0x00,0x02,0x00,0x00,0x02,0x00,0x00,0x02, // 247
+  0x00,0x00,0x00,0x00,0x5F,0x00,0x80,0x30,0x00,0x40,0x48,0x00,0x40,0x44,0x00,0x40,0x42,0x00,0x80,0x21,0x00,0x40,0x1F, // 248
+  0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x08,0x40,0x00,0x10,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F,  // 249
+  0x00,0x00,0x00,0xC0,0x3F,0x00,0x00,0x40,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x08,0x20,0x00,0xC0,0x7F,  // 250
+  0x00,0x00,0x00,0xC0,0x3F,0x00,0x10,0x40,0x00,0x08,0x40,0x00,0x08,0x40,0x00,0x10,0x20,0x00,0xC0,0x7F,  // 251
+  0x00,0x00,0x00,0xD0,0x3F,0x00,0x00,0x40,0x00,0x10,0x40,0x00,0x00,0x40,0x00,0x00,0x20,0x00,0xC0,0x7F,  // 252
+  0xC0,0x01,0x00,0x00,0x06,0x02,0x00,0x38,0x02,0x10,0xE0,0x01,0x08,0x38,0x00,0x00,0x07,0x00,0xC0, // 253
+  0x00,0x00,0x00,0xF8,0xFF,0x03,0x80,0x20,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x40,0x40,0x00,0x80,0x20,0x00,0x00,0x1F, // 254
+  0xC0,0x01,0x00,0x00,0x06,0x02,0x10,0x38,0x02,0x00,0xE0,0x01,0x10,0x38,0x00,0x00,0x07,0x00,0xC0  // 255
+};
+const char ArialMT_Plain_24[] PROGMEM = {
+  0x18, // Width: 24
+  0x1C, // Height: 28
+  0x20, // First Char: 32
+  0xE0, // Numbers of Chars: 224
+
+  // Jump Table:
+  0xFF, 0xFF, 0x00, 0x07,  // 32:65535
+  0x00, 0x00, 0x13, 0x07,  // 33:0
+  0x00, 0x13, 0x1A, 0x09,  // 34:19
+  0x00, 0x2D, 0x33, 0x0D,  // 35:45
+  0x00, 0x60, 0x2F, 0x0D,  // 36:96
+  0x00, 0x8F, 0x4F, 0x15,  // 37:143
+  0x00, 0xDE, 0x3B, 0x10,  // 38:222
+  0x01, 0x19, 0x0A, 0x05,  // 39:281
+  0x01, 0x23, 0x1C, 0x08,  // 40:291
+  0x01, 0x3F, 0x1B, 0x08,  // 41:319
+  0x01, 0x5A, 0x21, 0x09,  // 42:346
+  0x01, 0x7B, 0x32, 0x0E,  // 43:379
+  0x01, 0xAD, 0x10, 0x07,  // 44:429
+  0x01, 0xBD, 0x1B, 0x08,  // 45:445
+  0x01, 0xD8, 0x0F, 0x07,  // 46:472
+  0x01, 0xE7, 0x19, 0x07,  // 47:487
+  0x02, 0x00, 0x2F, 0x0D,  // 48:512
+  0x02, 0x2F, 0x23, 0x0D,  // 49:559
+  0x02, 0x52, 0x2F, 0x0D,  // 50:594
+  0x02, 0x81, 0x2F, 0x0D,  // 51:641
+  0x02, 0xB0, 0x2F, 0x0D,  // 52:688
+  0x02, 0xDF, 0x2F, 0x0D,  // 53:735
+  0x03, 0x0E, 0x2F, 0x0D,  // 54:782
+  0x03, 0x3D, 0x2D, 0x0D,  // 55:829
+  0x03, 0x6A, 0x2F, 0x0D,  // 56:874
+  0x03, 0x99, 0x2F, 0x0D,  // 57:921
+  0x03, 0xC8, 0x0F, 0x07,  // 58:968
+  0x03, 0xD7, 0x10, 0x07,  // 59:983
+  0x03, 0xE7, 0x2F, 0x0E,  // 60:999
+  0x04, 0x16, 0x2F, 0x0E,  // 61:1046
+  0x04, 0x45, 0x2E, 0x0E,  // 62:1093
+  0x04, 0x73, 0x2E, 0x0D,  // 63:1139
+  0x04, 0xA1, 0x5B, 0x18,  // 64:1185
+  0x04, 0xFC, 0x3B, 0x10,  // 65:1276
+  0x05, 0x37, 0x3B, 0x10,  // 66:1335
+  0x05, 0x72, 0x3F, 0x11,  // 67:1394
+  0x05, 0xB1, 0x3F, 0x11,  // 68:1457
+  0x05, 0xF0, 0x3B, 0x10,  // 69:1520
+  0x06, 0x2B, 0x35, 0x0F,  // 70:1579
+  0x06, 0x60, 0x43, 0x13,  // 71:1632
+  0x06, 0xA3, 0x3B, 0x11,  // 72:1699
+  0x06, 0xDE, 0x0F, 0x07,  // 73:1758
+  0x06, 0xED, 0x27, 0x0C,  // 74:1773
+  0x07, 0x14, 0x3F, 0x10,  // 75:1812
+  0x07, 0x53, 0x2F, 0x0D,  // 76:1875
+  0x07, 0x82, 0x43, 0x14,  // 77:1922
+  0x07, 0xC5, 0x3B, 0x11,  // 78:1989
+  0x08, 0x00, 0x47, 0x13,  // 79:2048
+  0x08, 0x47, 0x3A, 0x10,  // 80:2119
+  0x08, 0x81, 0x47, 0x13,  // 81:2177
+  0x08, 0xC8, 0x3F, 0x11,  // 82:2248
+  0x09, 0x07, 0x3B, 0x10,  // 83:2311
+  0x09, 0x42, 0x35, 0x0F,  // 84:2370
+  0x09, 0x77, 0x3B, 0x11,  // 85:2423
+  0x09, 0xB2, 0x39, 0x10,  // 86:2482
+  0x09, 0xEB, 0x59, 0x17,  // 87:2539
+  0x0A, 0x44, 0x3B, 0x10,  // 88:2628
+  0x0A, 0x7F, 0x3D, 0x10,  // 89:2687
+  0x0A, 0xBC, 0x37, 0x0F,  // 90:2748
+  0x0A, 0xF3, 0x14, 0x07,  // 91:2803
+  0x0B, 0x07, 0x1B, 0x07,  // 92:2823
+  0x0B, 0x22, 0x18, 0x07,  // 93:2850
+  0x0B, 0x3A, 0x2A, 0x0B,  // 94:2874
+  0x0B, 0x64, 0x34, 0x0D,  // 95:2916
+  0x0B, 0x98, 0x11, 0x08,  // 96:2968
+  0x0B, 0xA9, 0x2F, 0x0D,  // 97:2985
+  0x0B, 0xD8, 0x33, 0x0D,  // 98:3032
+  0x0C, 0x0B, 0x2B, 0x0C,  // 99:3083
+  0x0C, 0x36, 0x2F, 0x0D,  // 100:3126
+  0x0C, 0x65, 0x2F, 0x0D,  // 101:3173
+  0x0C, 0x94, 0x1A, 0x07,  // 102:3220
+  0x0C, 0xAE, 0x2F, 0x0D,  // 103:3246
+  0x0C, 0xDD, 0x2F, 0x0D,  // 104:3293
+  0x0D, 0x0C, 0x0F, 0x05,  // 105:3340
+  0x0D, 0x1B, 0x10, 0x05,  // 106:3355
+  0x0D, 0x2B, 0x2F, 0x0C,  // 107:3371
+  0x0D, 0x5A, 0x0F, 0x05,  // 108:3418
+  0x0D, 0x69, 0x47, 0x14,  // 109:3433
+  0x0D, 0xB0, 0x2F, 0x0D,  // 110:3504
+  0x0D, 0xDF, 0x2F, 0x0D,  // 111:3551
+  0x0E, 0x0E, 0x33, 0x0D,  // 112:3598
+  0x0E, 0x41, 0x30, 0x0D,  // 113:3649
+  0x0E, 0x71, 0x1E, 0x08,  // 114:3697
+  0x0E, 0x8F, 0x2B, 0x0C,  // 115:3727
+  0x0E, 0xBA, 0x1B, 0x07,  // 116:3770
+  0x0E, 0xD5, 0x2F, 0x0D,  // 117:3797
+  0x0F, 0x04, 0x2A, 0x0C,  // 118:3844
+  0x0F, 0x2E, 0x42, 0x11,  // 119:3886
+  0x0F, 0x70, 0x2B, 0x0C,  // 120:3952
+  0x0F, 0x9B, 0x2A, 0x0C,  // 121:3995
+  0x0F, 0xC5, 0x2B, 0x0C,  // 122:4037
+  0x0F, 0xF0, 0x1C, 0x08,  // 123:4080
+  0x10, 0x0C, 0x10, 0x06,  // 124:4108
+  0x10, 0x1C, 0x1B, 0x08,  // 125:4124
+  0x10, 0x37, 0x32, 0x0E,  // 126:4151
+  0xFF, 0xFF, 0x00, 0x00,  // 127:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 128:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 129:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 130:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 131:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 132:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 133:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 134:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 135:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 136:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 137:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 138:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 139:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 140:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 141:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 142:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 143:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 144:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 145:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 146:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 147:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 148:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 149:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 150:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 151:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 152:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 153:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 154:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 155:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 156:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 157:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 158:65535
+  0xFF, 0xFF, 0x00, 0x18,  // 159:65535
+  0xFF, 0xFF, 0x00, 0x07,  // 160:65535
+  0x10, 0x69, 0x14, 0x08,  // 161:4201
+  0x10, 0x7D, 0x2B, 0x0D,  // 162:4221
+  0x10, 0xA8, 0x2F, 0x0D,  // 163:4264
+  0x10, 0xD7, 0x33, 0x0D,  // 164:4311
+  0x11, 0x0A, 0x31, 0x0D,  // 165:4362
+  0x11, 0x3B, 0x10, 0x06,  // 166:4411
+  0x11, 0x4B, 0x2F, 0x0D,  // 167:4427
+  0x11, 0x7A, 0x19, 0x08,  // 168:4474
+  0x11, 0x93, 0x46, 0x12,  // 169:4499
+  0x11, 0xD9, 0x1A, 0x09,  // 170:4569
+  0x11, 0xF3, 0x27, 0x0D,  // 171:4595
+  0x12, 0x1A, 0x2F, 0x0E,  // 172:4634
+  0x12, 0x49, 0x1B, 0x08,  // 173:4681
+  0x12, 0x64, 0x46, 0x12,  // 174:4708
+  0x12, 0xAA, 0x31, 0x0D,  // 175:4778
+  0x12, 0xDB, 0x1E, 0x0A,  // 176:4827
+  0x12, 0xF9, 0x33, 0x0D,  // 177:4857
+  0x13, 0x2C, 0x1A, 0x08,  // 178:4908
+  0x13, 0x46, 0x1A, 0x08,  // 179:4934
+  0x13, 0x60, 0x19, 0x08,  // 180:4960
+  0x13, 0x79, 0x2F, 0x0E,  // 181:4985
+  0x13, 0xA8, 0x31, 0x0D,  // 182:5032
+  0x13, 0xD9, 0x12, 0x08,  // 183:5081
+  0x13, 0xEB, 0x18, 0x08,  // 184:5099
+  0x14, 0x03, 0x16, 0x08,  // 185:5123
+  0x14, 0x19, 0x1E, 0x09,  // 186:5145
+  0x14, 0x37, 0x2E, 0x0D,  // 187:5175
+  0x14, 0x65, 0x4F, 0x14,  // 188:5221
+  0x14, 0xB4, 0x4B, 0x14,  // 189:5300
+  0x14, 0xFF, 0x4B, 0x14,  // 190:5375
+  0x15, 0x4A, 0x33, 0x0F,  // 191:5450
+  0x15, 0x7D, 0x3B, 0x10,  // 192:5501
+  0x15, 0xB8, 0x3B, 0x10,  // 193:5560
+  0x15, 0xF3, 0x3B, 0x10,  // 194:5619
+  0x16, 0x2E, 0x3B, 0x10,  // 195:5678
+  0x16, 0x69, 0x3B, 0x10,  // 196:5737
+  0x16, 0xA4, 0x3B, 0x10,  // 197:5796
+  0x16, 0xDF, 0x5B, 0x18,  // 198:5855
+  0x17, 0x3A, 0x3F, 0x11,  // 199:5946
+  0x17, 0x79, 0x3B, 0x10,  // 200:6009
+  0x17, 0xB4, 0x3B, 0x10,  // 201:6068
+  0x17, 0xEF, 0x3B, 0x10,  // 202:6127
+  0x18, 0x2A, 0x3B, 0x10,  // 203:6186
+  0x18, 0x65, 0x11, 0x07,  // 204:6245
+  0x18, 0x76, 0x11, 0x07,  // 205:6262
+  0x18, 0x87, 0x15, 0x07,  // 206:6279
+  0x18, 0x9C, 0x15, 0x07,  // 207:6300
+  0x18, 0xB1, 0x3F, 0x11,  // 208:6321
+  0x18, 0xF0, 0x3B, 0x11,  // 209:6384
+  0x19, 0x2B, 0x47, 0x13,  // 210:6443
+  0x19, 0x72, 0x47, 0x13,  // 211:6514
+  0x19, 0xB9, 0x47, 0x13,  // 212:6585
+  0x1A, 0x00, 0x47, 0x13,  // 213:6656
+  0x1A, 0x47, 0x47, 0x13,  // 214:6727
+  0x1A, 0x8E, 0x2B, 0x0E,  // 215:6798
+  0x1A, 0xB9, 0x47, 0x13,  // 216:6841
+  0x1B, 0x00, 0x3B, 0x11,  // 217:6912
+  0x1B, 0x3B, 0x3B, 0x11,  // 218:6971
+  0x1B, 0x76, 0x3B, 0x11,  // 219:7030
+  0x1B, 0xB1, 0x3B, 0x11,  // 220:7089
+  0x1B, 0xEC, 0x3D, 0x10,  // 221:7148
+  0x1C, 0x29, 0x3A, 0x10,  // 222:7209
+  0x1C, 0x63, 0x37, 0x0F,  // 223:7267
+  0x1C, 0x9A, 0x2F, 0x0D,  // 224:7322
+  0x1C, 0xC9, 0x2F, 0x0D,  // 225:7369
+  0x1C, 0xF8, 0x2F, 0x0D,  // 226:7416
+  0x1D, 0x27, 0x2F, 0x0D,  // 227:7463
+  0x1D, 0x56, 0x2F, 0x0D,  // 228:7510
+  0x1D, 0x85, 0x2F, 0x0D,  // 229:7557
+  0x1D, 0xB4, 0x53, 0x15,  // 230:7604
+  0x1E, 0x07, 0x2B, 0x0C,  // 231:7687
+  0x1E, 0x32, 0x2F, 0x0D,  // 232:7730
+  0x1E, 0x61, 0x2F, 0x0D,  // 233:7777
+  0x1E, 0x90, 0x2F, 0x0D,  // 234:7824
+  0x1E, 0xBF, 0x2F, 0x0D,  // 235:7871
+  0x1E, 0xEE, 0x11, 0x07,  // 236:7918
+  0x1E, 0xFF, 0x11, 0x07,  // 237:7935
+  0x1F, 0x10, 0x15, 0x07,  // 238:7952
+  0x1F, 0x25, 0x15, 0x07,  // 239:7973
+  0x1F, 0x3A, 0x2F, 0x0D,  // 240:7994
+  0x1F, 0x69, 0x2F, 0x0D,  // 241:8041
+  0x1F, 0x98, 0x2F, 0x0D,  // 242:8088
+  0x1F, 0xC7, 0x2F, 0x0D,  // 243:8135
+  0x1F, 0xF6, 0x2F, 0x0D,  // 244:8182
+  0x20, 0x25, 0x2F, 0x0D,  // 245:8229
+  0x20, 0x54, 0x2F, 0x0D,  // 246:8276
+  0x20, 0x83, 0x32, 0x0D,  // 247:8323
+  0x20, 0xB5, 0x33, 0x0F,  // 248:8373
+  0x20, 0xE8, 0x2F, 0x0D,  // 249:8424
+  0x21, 0x17, 0x2F, 0x0D,  // 250:8471
+  0x21, 0x46, 0x2F, 0x0D,  // 251:8518
+  0x21, 0x75, 0x2F, 0x0D,  // 252:8565
+  0x21, 0xA4, 0x2A, 0x0C,  // 253:8612
+  0x21, 0xCE, 0x2F, 0x0D,  // 254:8654
+  0x21, 0xFD, 0x2A, 0x0C,  // 255:8701
+
+  // Font Data:
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x33,0x00,0xE0,0xFF,0x33, // 33
+  0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,  // 34
+  0x00,0x0C,0x03,0x00,0x00,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x33,0x00,0x00,0x0C,0x3F,0x00,0x00,0xFC,0x0F,0x00,0x80,0xFF,0x03,0x00,0xE0,0x0F,0x03,0x00,0x60,0x0C,0x03,0x00,0x00,0x0C,0x03, // 35
+  0x00,0x00,0x00,0x00,0x80,0x07,0x06,0x00,0xC0,0x0F,0x1E,0x00,0xC0,0x18,0x1C,0x00,0x60,0x18,0x38,0x00,0x60,0x30,0x30,0x00,0xF0,0xFF,0xFF,0x00,0x60,0x30,0x30,0x00,0x60,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0xC1,0x1F,0x00,0x00,0x81,0x07, // 36
+  0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x20,0x20,0x00,0x60,0x30,0x38,0x00,0xC0,0x1F,0x1E,0x00,0x80,0x8F,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x8F,0x0F,0x00,0xC0,0xC3,0x1F,0x00,0xE0,0x60,0x30,0x00,0x20,0x20,0x20,0x00,0x00,0x20,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F, // 37
+  0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x00,0xC0,0x0F,0x00,0x80,0xE3,0x1C,0x00,0xC0,0x77,0x38,0x00,0xE0,0x3C,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x78,0x30,0x00,0xE0,0xEC,0x38,0x00,0xC0,0x8F,0x1B,0x00,0x80,0x03,0x1F,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xC0,0x38,0x00,0x00,0x00,0x10, // 38
+  0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xE0,0x07,  // 39
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x0F,0x00,0x00,0xFE,0x7F,0x00,0x80,0x0F,0xF0,0x01,0xC0,0x01,0x80,0x03,0x60,0x00,0x00,0x06,0x20,0x00,0x00,0x04,  // 40
+  0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x04,0x60,0x00,0x00,0x06,0xC0,0x01,0x80,0x03,0x80,0x0F,0xF0,0x01,0x00,0xFE,0x7F,0x00,0x00,0xF0,0x0F, // 41
+  0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0x80,0x04,0x00,0x00,0x80,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x80,0x04,0x00,0x00,0x80, // 42
+  0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xFF,0x0F,0x00,0x00,0xFF,0x0F,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,  // 43
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x03,0x00,0x00,0xF0,0x01,  // 44
+  0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 45
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 46
+  0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x3F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60, // 47
+  0x00,0x00,0x00,0x00,0x00,0xFE,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x01,0x1C,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x03, // 48
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 49
+  0x00,0x00,0x00,0x00,0x00,0x03,0x30,0x00,0xC0,0x03,0x38,0x00,0xC0,0x00,0x3C,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x33,0x00,0x60,0x80,0x31,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x30,0x00,0xC0,0x30,0x30,0x00,0xC0,0x1F,0x30,0x00,0x00,0x0F,0x30, // 50
+  0x00,0x00,0x00,0x00,0x00,0x01,0x06,0x00,0xC0,0x01,0x0E,0x00,0xC0,0x00,0x1C,0x00,0x60,0x00,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x0F,0x00,0x00,0x80,0x07, // 51
+  0x00,0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x03,0x00,0x00,0x3C,0x03,0x00,0x00,0x0E,0x03,0x00,0x80,0x07,0x03,0x00,0xC0,0x01,0x03,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x03,0x00,0x00,0x00,0x03, // 52
+  0x00,0x00,0x00,0x00,0x00,0x30,0x06,0x00,0x80,0x3F,0x0E,0x00,0xE0,0x1F,0x18,0x00,0x60,0x08,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x0C,0x30,0x00,0x60,0x18,0x1C,0x00,0x60,0xF0,0x0F,0x00,0x00,0xE0,0x03, // 53
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x03,0x00,0x80,0xFF,0x0F,0x00,0xC0,0x63,0x1C,0x00,0xC0,0x30,0x38,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0x60,0x18,0x30,0x00,0xE0,0x30,0x18,0x00,0xC0,0xF1,0x0F,0x00,0x80,0xC1,0x07, // 54
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x3C,0x00,0x60,0x80,0x3F,0x00,0x60,0xE0,0x03,0x00,0x60,0x78,0x00,0x00,0x60,0x0E,0x00,0x00,0x60,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60, // 55
+  0x00,0x00,0x00,0x00,0x00,0x80,0x07,0x00,0x80,0xC7,0x1F,0x00,0xC0,0x6F,0x18,0x00,0xE0,0x38,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xE0,0x38,0x30,0x00,0xC0,0x6F,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 56
+  0x00,0x00,0x00,0x00,0x00,0x1F,0x0C,0x00,0x80,0x7F,0x1C,0x00,0xC0,0x61,0x38,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0xC0,0x30,0x00,0x60,0x60,0x18,0x00,0xC0,0x31,0x1E,0x00,0x80,0xFF,0x0F,0x00,0x00,0xFE,0x01, // 57
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 58
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x30,0x03,0x00,0x06,0xF0,0x01,  // 59
+  0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x04,0x01,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x03,0x06, // 60
+  0x00,0x00,0x00,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01, // 61
+  0x00,0x00,0x00,0x00,0x00,0x03,0x06,0x00,0x00,0x06,0x03,0x00,0x00,0x06,0x03,0x00,0x00,0x04,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0x8C,0x01,0x00,0x00,0xD8,0x00,0x00,0x00,0xD8,0x00,0x00,0x00,0x50,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x20,  // 62
+  0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x33,0x00,0x60,0xE0,0x00,0x00,0x60,0x30,0x00,0x00,0xC0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x00,0x07,  // 63
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x0F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x1E,0xF0,0x00,0x00,0x07,0xC0,0x01,0x80,0xC3,0x87,0x01,0xC0,0xF1,0x9F,0x03,0xC0,0x38,0x18,0x03,0xC0,0x0C,0x30,0x03,0x60,0x0E,0x30,0x06,0x60,0x06,0x30,0x06,0x60,0x06,0x18,0x06,0x60,0x06,0x0C,0x06,0x60,0x0C,0x1E,0x06,0x60,0xF8,0x3F,0x06,0xE0,0xFE,0x31,0x06,0xC0,0x0E,0x30,0x06,0xC0,0x01,0x18,0x03,0x80,0x03,0x1C,0x03,0x00,0x07,0x8F,0x01,0x00,0xFE,0x87,0x01,0x00,0xF8,0xC1,0x00,0x00,0x00,0x40, // 64
+  0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE0,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 65
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0xC0,0x78,0x30,0x00,0xC0,0xFF,0x18,0x00,0x80,0xC7,0x1F,0x00,0x00,0x80,0x07, // 66
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 67
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 68
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 69
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60, // 70
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x60,0x30,0x00,0x60,0x60,0x30,0x00,0xE0,0x60,0x38,0x00,0xC0,0x60,0x18,0x00,0xC0,0x61,0x18,0x00,0x80,0xE3,0x0F,0x00,0x00,0xE2,0x0F, // 71
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 72
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 73
+  0x00,0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0xE0,0xFF,0x1F,0x00,0xE0,0xFF,0x0F, // 74
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE7,0x01,0x00,0x80,0x83,0x07,0x00,0xC0,0x01,0x0F,0x00,0xE0,0x00,0x1E,0x00,0x60,0x00,0x38,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 75
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30, // 76
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0xFE,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x07,0x00,0x00,0xFE,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 77
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x0F,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 78
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 79
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0x60,0x60,0x00,0x00,0xC0,0x30,0x00,0x00,0xC0,0x3F,0x00,0x00,0x00,0x0F,  // 80
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x0C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x18,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x36,0x00,0x60,0x00,0x36,0x00,0xE0,0x00,0x3C,0x00,0xC0,0x00,0x1C,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x3F,0x00,0x00,0xFF,0x77,0x00,0x00,0xFC,0x61, // 81
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x70,0x00,0x00,0x60,0xF0,0x00,0x00,0x60,0xF0,0x03,0x00,0x60,0xB0,0x07,0x00,0xE0,0x18,0x1F,0x00,0xC0,0x1F,0x3C,0x00,0x80,0x0F,0x30,0x00,0x00,0x00,0x20, // 82
+  0x00,0x00,0x00,0x00,0x00,0x00,0x03,0x00,0x00,0x07,0x0F,0x00,0xC0,0x1F,0x1C,0x00,0xC0,0x18,0x18,0x00,0x60,0x38,0x38,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x70,0x30,0x00,0xC0,0x60,0x18,0x00,0xC0,0xE1,0x18,0x00,0x80,0xC3,0x0F,0x00,0x00,0x83,0x07, // 83
+  0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 84
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 85
+  0x20,0x00,0x00,0x00,0xE0,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x01,0x00,0x00,0xC0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xF8,0x01,0x00,0x00,0x3E,0x00,0x00,0xC0,0x0F,0x00,0x00,0xE0,0x01,0x00,0x00,0x20, // 86
+  0x60,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0x80,0xFF,0x00,0x00,0x00,0xF8,0x0F,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x3F,0x00,0x00,0xE0,0x0F,0x00,0x00,0xFC,0x01,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3F,0x00,0x00,0x00,0x30,0x00,0x00,0x80,0x3F,0x00,0x00,0xF8,0x0F,0x00,0x80,0xFF,0x00,0x00,0xE0,0x07,0x00,0x00,0x60, // 87
+  0x00,0x00,0x20,0x00,0x20,0x00,0x30,0x00,0x60,0x00,0x3C,0x00,0xE0,0x01,0x1E,0x00,0xC0,0x83,0x07,0x00,0x00,0xCF,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0x38,0x00,0x00,0x00,0xFE,0x01,0x00,0x00,0xCF,0x03,0x00,0xC0,0x03,0x07,0x00,0xE0,0x01,0x1E,0x00,0x60,0x00,0x3C,0x00,0x20,0x00,0x30,0x00,0x00,0x00,0x20, // 88
+  0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x3C,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 89
+  0x00,0x00,0x30,0x00,0x60,0x00,0x38,0x00,0x60,0x00,0x3C,0x00,0x60,0x00,0x37,0x00,0x60,0x80,0x33,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0x60,0x0E,0x30,0x00,0x60,0x07,0x30,0x00,0xE0,0x01,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x30, // 90
+  0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,  // 91
+  0x60,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x3F,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xE0,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 92
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,  // 93
+  0x00,0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xE0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x20,  // 94
+  0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,  // 95
+  0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x80, // 96
+  0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 97
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 98
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 99
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 100
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 101
+  0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x06,0x00,0x00,0x60,0x06,0x00,0x00,0x60,0x06,  // 102
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x83,0x01,0x00,0xF8,0x8F,0x03,0x00,0x1C,0x1C,0x07,0x00,0x0E,0x38,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x06,0x30,0x06,0x00,0x0C,0x18,0x07,0x00,0x18,0x8C,0x03,0x00,0xFE,0xFF,0x01,0x00,0xFE,0xFF, // 103
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 104
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F, // 105
+  0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x60,0xFE,0xFF,0x07,0x60,0xFE,0xFF,0x03,  // 106
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0xF0,0x01,0x00,0x00,0x98,0x07,0x00,0x00,0x0C,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x02,0x30,0x00,0x00,0x00,0x20, // 107
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 108
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x04,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 109
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 110
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 111
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x18,0x0C,0x00,0x00,0x0C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xE0,0x03, // 112
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0C,0x18,0x00,0x00,0x18,0x0C,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,  // 113
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0x00,0x0C,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x06,  // 114
+  0x00,0x00,0x00,0x00,0x00,0x38,0x0C,0x00,0x00,0x7C,0x1C,0x00,0x00,0xEE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x31,0x00,0x00,0xC6,0x31,0x00,0x00,0x8E,0x39,0x00,0x00,0x9C,0x1F,0x00,0x00,0x18,0x0F, // 115
+  0x00,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30, // 116
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 117
+  0x00,0x06,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xC0,0x07,0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1F,0x00,0x00,0xC0,0x07,0x00,0x00,0xF8,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x06,  // 118
+  0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xE0,0x03,0x00,0x00,0x7C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7C,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x80,0x1F,0x00,0x00,0xF0,0x03,0x00,0x00,0x7E,0x00,0x00,0x00,0x0E,  // 119
+  0x00,0x02,0x20,0x00,0x00,0x06,0x30,0x00,0x00,0x1E,0x3C,0x00,0x00,0x38,0x0E,0x00,0x00,0xF0,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x07,0x00,0x00,0x38,0x0E,0x00,0x00,0x1C,0x3C,0x00,0x00,0x0E,0x30,0x00,0x00,0x02,0x20, // 120
+  0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0x00,0xC0,0x1F,0x00,0x00,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06,  // 121
+  0x00,0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x3E,0x00,0x00,0x06,0x37,0x00,0x00,0xC6,0x33,0x00,0x00,0xE6,0x30,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x30,0x00,0x00,0x1E,0x30,0x00,0x00,0x06,0x30, // 122
+  0x00,0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x03,0x00,0xC0,0x7F,0xFE,0x03,0xE0,0x3F,0xFC,0x07,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,  // 123
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x0F,0xE0,0xFF,0xFF,0x0F,  // 124
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x06,0x60,0x00,0x00,0x06,0xE0,0x3F,0xFC,0x07,0xC0,0x7F,0xFF,0x03,0x00,0xC0,0x03,0x00,0x00,0x80,0x01, // 125
+  0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,  // 126
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE6,0xFF,0x07,0x00,0xE6,0xFF,0x07,  // 161
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x9C,0x07,0x00,0x0E,0x78,0x00,0x00,0x06,0x3F,0x00,0x00,0xF6,0x30,0x00,0x00,0x0E,0x30,0x00,0xE0,0x0D,0x1C,0x00,0x00,0x1C,0x0E,0x00,0x00,0x10,0x06, // 162
+  0x00,0x60,0x10,0x00,0x00,0x60,0x38,0x00,0x00,0x7F,0x1C,0x00,0xC0,0xFF,0x1F,0x00,0xE0,0xE0,0x19,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x18,0x00,0x60,0x60,0x30,0x00,0xE0,0x00,0x30,0x00,0xC0,0x01,0x30,0x00,0x80,0x01,0x38,0x00,0x00,0x00,0x10, // 163
+  0x00,0x00,0x00,0x00,0x00,0x02,0x04,0x00,0x00,0xF7,0x0E,0x00,0x00,0xFE,0x07,0x00,0x00,0x0C,0x03,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x06,0x06,0x00,0x00,0x0C,0x03,0x00,0x00,0xFE,0x07,0x00,0x00,0xF7,0x0E,0x00,0x00,0x02,0x04, // 164
+  0xE0,0x60,0x06,0x00,0xC0,0x61,0x06,0x00,0x80,0x67,0x06,0x00,0x00,0x7E,0x06,0x00,0x00,0x7C,0x06,0x00,0x00,0xF0,0x3F,0x00,0x00,0xF0,0x3F,0x00,0x00,0x7C,0x06,0x00,0x00,0x7E,0x06,0x00,0x80,0x67,0x06,0x00,0xC0,0x61,0x06,0x00,0xE0,0x60,0x06,0x00,0x20, // 165
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x7F,0xF8,0x0F,0xE0,0x7F,0xF8,0x0F,  // 166
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x80,0xF3,0xC1,0x00,0xC0,0x1F,0xC3,0x03,0xE0,0x0C,0x07,0x03,0x60,0x1C,0x06,0x06,0x60,0x18,0x0C,0x06,0x60,0x30,0x1C,0x06,0xE0,0x70,0x38,0x07,0xC0,0xE1,0xF4,0x03,0x80,0xC1,0xE7,0x01,0x00,0x80,0x03, // 167
+  0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60, // 168
+  0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x79,0x1C,0x00,0xC0,0xFE,0x19,0x00,0x60,0x86,0x31,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x03,0x33,0x00,0x60,0x87,0x33,0x00,0xC0,0x86,0x19,0x00,0xC0,0x85,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8,  // 169
+  0x00,0x00,0x00,0x00,0xC0,0x1C,0x00,0x00,0xE0,0x3E,0x00,0x00,0x60,0x32,0x00,0x00,0x60,0x32,0x00,0x00,0xE0,0x3F,0x00,0x00,0xC0,0x3F,  // 170
+  0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x84,0x10,0x00,0x00,0xE0,0x03,0x00,0x00,0x78,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x04,0x10, // 171
+  0x00,0x00,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFC,0x01, // 172
+  0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01,0x00,0x00,0x80,0x01, // 173
+  0x00,0xF8,0x00,0x00,0x00,0xFE,0x03,0x00,0x00,0x07,0x07,0x00,0x80,0x01,0x0C,0x00,0xC0,0x01,0x1C,0x00,0xC0,0xFE,0x1B,0x00,0x60,0xFE,0x33,0x00,0x60,0x66,0x30,0x00,0x60,0x66,0x30,0x00,0x60,0xE6,0x30,0x00,0x60,0xFE,0x31,0x00,0x60,0x3C,0x33,0x00,0xC0,0x00,0x1A,0x00,0xC0,0x01,0x1C,0x00,0x80,0x01,0x0C,0x00,0x00,0x07,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xF8,  // 174
+  0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0x0C, // 175
+  0x00,0x00,0x00,0x00,0x80,0x03,0x00,0x00,0x40,0x04,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x20,0x08,0x00,0x00,0x40,0x04,0x00,0x00,0x80,0x03,  // 176
+  0x00,0x00,0x00,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0xFF,0x3F,0x00,0x00,0xFF,0x3F,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30,0x00,0x00,0x60,0x30, // 177
+  0x40,0x20,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x38,0x00,0x00,0x20,0x2C,0x00,0x00,0x20,0x26,0x00,0x00,0xE0,0x23,0x00,0x00,0xC0,0x21,  // 178
+  0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x00,0x00,0x20,0x22,0x00,0x00,0xE0,0x3D,0x00,0x00,0xC0,0x1D,  // 179
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 180
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0xFF,0x07,0x00,0xFE,0xFF,0x07,0x00,0x00,0x1C,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x1C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 181
+  0x00,0x0F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xC0,0x3F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0x7F,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x60,0x00,0x00,0x00,0x60, // 182
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0x60,  // 183
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0x02,0x00,0x00,0xC0,0x02,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x01,  // 184
+  0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F,  // 185
+  0x00,0x00,0x00,0x00,0x80,0x0F,0x00,0x00,0xC0,0x1F,0x00,0x00,0xE0,0x38,0x00,0x00,0x60,0x30,0x00,0x00,0xE0,0x38,0x00,0x00,0xC0,0x1F,0x00,0x00,0x80,0x0F,  // 186
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x84,0x10,0x00,0x00,0x1C,0x1C,0x00,0x00,0x78,0x0F,0x00,0x00,0xE0,0x03,0x00,0x00,0x80,  // 187
+  0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x38,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x0C,0x00,0xC0,0x01,0x0E,0x00,0xE0,0x80,0x0B,0x00,0x60,0xC0,0x08,0x00,0x00,0xE0,0x3F,0x00,0x00,0xE0,0x3F,0x00,0x00,0x00,0x08, // 188
+  0x00,0x00,0x00,0x00,0x80,0x01,0x00,0x00,0xC0,0x00,0x00,0x00,0xC0,0x00,0x20,0x00,0xE0,0x3F,0x30,0x00,0xE0,0x3F,0x1C,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x01,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x4E,0x20,0x00,0x00,0x67,0x30,0x00,0xC0,0x21,0x38,0x00,0xE0,0x20,0x2C,0x00,0x60,0x20,0x26,0x00,0x00,0xE0,0x27,0x00,0x00,0xC0,0x21, // 189
+  0x40,0x10,0x00,0x00,0x60,0x30,0x00,0x00,0x20,0x20,0x00,0x00,0x20,0x22,0x20,0x00,0x20,0x22,0x30,0x00,0xE0,0x3D,0x38,0x00,0xC0,0x1D,0x0E,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xE0,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x0E,0x0C,0x00,0x00,0x07,0x0E,0x00,0x80,0x83,0x0B,0x00,0xE0,0xC0,0x08,0x00,0x60,0xE0,0x3F,0x00,0x20,0xE0,0x3F,0x00,0x00,0x00,0x08, // 190
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x00,0x00,0x00,0xF8,0x03,0x00,0x00,0x1E,0x03,0x00,0x00,0x07,0x07,0x00,0xE6,0x03,0x06,0x00,0xE6,0x01,0x06,0x00,0x00,0x00,0x06,0x00,0x00,0x00,0x07,0x00,0x00,0x80,0x03,0x00,0x00,0xC0,0x01,0x00,0x00,0xC0, // 191
+  0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x82,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE8,0x83,0x01,0x00,0x80,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 192
+  0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x80,0x8F,0x01,0x00,0xE8,0x83,0x01,0x00,0x6E,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x82,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 193
+  0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x88,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xE6,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x08,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 194
+  0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x0C,0xFE,0x01,0x00,0x8E,0x8F,0x01,0x00,0xE6,0x83,0x01,0x00,0x66,0x80,0x01,0x00,0xEC,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0E,0xFE,0x01,0x00,0x06,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 195
+  0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x8C,0x8F,0x01,0x00,0xEC,0x83,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0x83,0x01,0x00,0x8C,0x8F,0x01,0x00,0x0C,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 196
+  0x00,0x00,0x30,0x00,0x00,0x00,0x3E,0x00,0x00,0x80,0x0F,0x00,0x00,0xF0,0x03,0x00,0x00,0xFE,0x01,0x00,0x9C,0x8F,0x01,0x00,0xE2,0x83,0x01,0x00,0x62,0x80,0x01,0x00,0xE2,0x83,0x01,0x00,0x9C,0x8F,0x01,0x00,0x00,0xFE,0x01,0x00,0x00,0xF0,0x03,0x00,0x00,0x80,0x0F,0x00,0x00,0x00,0x3E,0x00,0x00,0x00,0x30, // 197
+  0x00,0x00,0x30,0x00,0x00,0x00,0x3C,0x00,0x00,0x00,0x0F,0x00,0x00,0xC0,0x03,0x00,0x00,0xF0,0x01,0x00,0x00,0xBC,0x01,0x00,0x00,0x8F,0x01,0x00,0xC0,0x83,0x01,0x00,0xE0,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0x60,0x80,0x01,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 198
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0x60,0x00,0x30,0x02,0x60,0x00,0x30,0x02,0x60,0x00,0xF0,0x02,0x60,0x00,0xB0,0x03,0x60,0x00,0x30,0x01,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0F,0x00,0x00,0x02,0x03, // 199
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 200
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6E,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x62,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 201
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x66,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x68,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 202
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x6C,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30, // 203
+  0x00,0x00,0x00,0x00,0x02,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xEE,0xFF,0x3F,0x00,0x08, // 204
+  0x00,0x00,0x00,0x00,0x08,0x00,0x00,0x00,0xEE,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x02, // 205
+  0x08,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE6,0xFF,0x3F,0x00,0xE6,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x08, // 206
+  0x0C,0x00,0x00,0x00,0x0C,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x0C,0x00,0x00,0x00,0x0C, // 207
+  0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x30,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x03,0x0E,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 208
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0xC0,0x01,0x00,0x00,0x8C,0x03,0x00,0x00,0x0E,0x0E,0x00,0x00,0x06,0x3C,0x00,0x00,0x06,0x70,0x00,0x00,0x0C,0xE0,0x01,0x00,0x0C,0x80,0x03,0x00,0x0E,0x00,0x0F,0x00,0x06,0x00,0x1C,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F, // 209
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x62,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 210
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0x68,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x62,0x00,0x30,0x00,0xE0,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 211
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x68,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xE8,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 212
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xCC,0x00,0x18,0x00,0xEE,0x00,0x38,0x00,0x66,0x00,0x30,0x00,0x66,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x6E,0x00,0x30,0x00,0xE6,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 213
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x01,0x00,0x00,0xFF,0x07,0x00,0x80,0x07,0x0F,0x00,0xC0,0x01,0x1C,0x00,0xC0,0x00,0x18,0x00,0xE0,0x00,0x38,0x00,0x6C,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x6C,0x00,0x30,0x00,0xEC,0x00,0x38,0x00,0xC0,0x00,0x18,0x00,0xC0,0x01,0x1C,0x00,0x80,0x07,0x0F,0x00,0x00,0xFF,0x07,0x00,0x00,0xFC,0x01, // 214
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x06,0x03,0x00,0x00,0x8E,0x03,0x00,0x00,0xDC,0x01,0x00,0x00,0xF8,0x00,0x00,0x00,0x70,0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0xDC,0x01,0x00,0x00,0x8E,0x03,0x00,0x00,0x06,0x03, // 215
+  0x00,0x00,0x00,0x00,0x00,0xFC,0x21,0x00,0x00,0xFF,0x77,0x00,0x80,0x07,0x3F,0x00,0xC0,0x01,0x1E,0x00,0xC0,0x00,0x1F,0x00,0xE0,0x80,0x3B,0x00,0x60,0xC0,0x31,0x00,0x60,0xE0,0x30,0x00,0x60,0x70,0x30,0x00,0x60,0x38,0x30,0x00,0x60,0x1C,0x30,0x00,0xE0,0x0E,0x38,0x00,0xC0,0x07,0x18,0x00,0xC0,0x03,0x1C,0x00,0xE0,0x07,0x0F,0x00,0x70,0xFF,0x07,0x00,0x20,0xFC,0x01, // 216
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x02,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 217
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x08,0x00,0x30,0x00,0x0E,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x02,0x00,0x30,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 218
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x08,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x06,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x08,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 219
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x03,0x00,0xE0,0xFF,0x0F,0x00,0x00,0x00,0x1C,0x00,0x00,0x00,0x38,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x0C,0x00,0x30,0x00,0x0C,0x00,0x38,0x00,0x00,0x00,0x1C,0x00,0xE0,0xFF,0x0F,0x00,0xE0,0xFF,0x03, // 220
+  0x20,0x00,0x00,0x00,0x60,0x00,0x00,0x00,0xC0,0x01,0x00,0x00,0x80,0x03,0x00,0x00,0x00,0x07,0x00,0x00,0x00,0x1E,0x00,0x00,0x00,0x3C,0x00,0x00,0x08,0xF0,0x3F,0x00,0x0E,0xF0,0x3F,0x00,0x06,0x3C,0x00,0x00,0x02,0x1E,0x00,0x00,0x00,0x07,0x00,0x00,0xC0,0x03,0x00,0x00,0xE0,0x01,0x00,0x00,0x60,0x00,0x00,0x00,0x20, // 221
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0x3F,0x00,0xE0,0xFF,0x3F,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x06,0x00,0x00,0x03,0x07,0x00,0x00,0x86,0x03,0x00,0x00,0xFE,0x01,0x00,0x00,0xF8,  // 222
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,0xFF,0x3F,0x00,0xC0,0xFF,0x3F,0x00,0xC0,0x00,0x00,0x00,0x60,0x00,0x08,0x00,0x60,0x00,0x1C,0x00,0x60,0x00,0x38,0x00,0xE0,0x78,0x30,0x00,0xC0,0x7F,0x30,0x00,0x80,0xC7,0x30,0x00,0x00,0x80,0x39,0x00,0x00,0x80,0x1F,0x00,0x00,0x00,0x0F, // 223
+  0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x20,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x18,0x00,0x00,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 224
+  0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x00,0x86,0x31,0x00,0x80,0x86,0x31,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x18,0x00,0x20,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 225
+  0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x80,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0x60,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0x80,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 226
+  0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0xC0,0x1C,0x1F,0x00,0xE0,0x8C,0x39,0x00,0x60,0x86,0x31,0x00,0x60,0x86,0x31,0x00,0xC0,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xE0,0xCE,0x0C,0x00,0x60,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 227
+  0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0xC0,0x8C,0x39,0x00,0xC0,0x86,0x31,0x00,0x00,0x86,0x31,0x00,0x00,0xC6,0x30,0x00,0xC0,0xC6,0x18,0x00,0xC0,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 228
+  0x00,0x00,0x00,0x00,0x00,0x18,0x0E,0x00,0x00,0x1C,0x1F,0x00,0x00,0x8C,0x39,0x00,0x70,0x86,0x31,0x00,0x88,0x86,0x31,0x00,0x88,0xC6,0x30,0x00,0x88,0xC6,0x18,0x00,0x70,0xCE,0x0C,0x00,0x00,0xFC,0x1F,0x00,0x00,0xF8,0x3F,0x00,0x00,0x00,0x20, // 229
+  0x00,0x00,0x00,0x00,0x00,0x10,0x0F,0x00,0x00,0x9C,0x1F,0x00,0x00,0xCC,0x39,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0x66,0x18,0x00,0x00,0x6E,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xFC,0x1F,0x00,0x00,0xCC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xCC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xE0,0x04, // 230
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x02,0x00,0x06,0x30,0x02,0x00,0x06,0xF0,0x02,0x00,0x06,0xB0,0x03,0x00,0x0E,0x38,0x01,0x00,0x1C,0x1C,0x00,0x00,0x18,0x0C, // 231
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x20,0xCE,0x38,0x00,0x60,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x80,0xC6,0x30,0x00,0x00,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 232
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x00,0xCE,0x38,0x00,0x80,0xC6,0x30,0x00,0xE0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x20,0xCE,0x38,0x00,0x00,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 233
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0x80,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0x60,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0x80,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 234
+  0x00,0x00,0x00,0x00,0x00,0xE0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0xDC,0x1C,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0x00,0xC6,0x30,0x00,0xC0,0xCE,0x38,0x00,0xC0,0xDC,0x18,0x00,0x00,0xF8,0x0C,0x00,0x00,0xF0,0x04, // 235
+  0x00,0x00,0x00,0x00,0x20,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0xE0,0xFE,0x3F,0x00,0x80, // 236
+  0x00,0x00,0x00,0x00,0x80,0x00,0x00,0x00,0xE0,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0x20, // 237
+  0x80,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x60,0xFE,0x3F,0x00,0x60,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0x80, // 238
+  0xC0,0x00,0x00,0x00,0xC0,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F,0x00,0xC0,0x00,0x00,0x00,0xC0, // 239
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1D,0x1C,0x00,0xA0,0x0F,0x38,0x00,0xA0,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0F,0x38,0x00,0x20,0x1F,0x1C,0x00,0x00,0xFC,0x0F,0x00,0x00,0xE0,0x07, // 240
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x3F,0x00,0xC0,0xFE,0x3F,0x00,0xE0,0x18,0x00,0x00,0x60,0x0C,0x00,0x00,0x60,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xC0,0x06,0x00,0x00,0xE0,0x0E,0x00,0x00,0x60,0xFC,0x3F,0x00,0x00,0xF8,0x3F, // 241
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x20,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x80,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 242
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x38,0x00,0x80,0x06,0x30,0x00,0xE0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x20,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 243
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0x80,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0x80,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 244
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0xC0,0x1C,0x1C,0x00,0xE0,0x0E,0x38,0x00,0x60,0x06,0x30,0x00,0x60,0x06,0x30,0x00,0xC0,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xE0,0x1C,0x1C,0x00,0x60,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 245
+  0x00,0x00,0x00,0x00,0x00,0xF0,0x07,0x00,0x00,0xF8,0x0F,0x00,0x00,0x1C,0x1C,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0xC0,0x0E,0x38,0x00,0xC0,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x07, // 246
+  0x00,0x00,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0xB6,0x01,0x00,0x00,0xB6,0x01,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,  // 247
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xF0,0x67,0x00,0x00,0xF8,0x7F,0x00,0x00,0x1C,0x1C,0x00,0x00,0x0E,0x3F,0x00,0x00,0x86,0x33,0x00,0x00,0xE6,0x31,0x00,0x00,0x76,0x30,0x00,0x00,0x3E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xFF,0x0F,0x00,0x00,0xF3,0x07, // 248
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x20,0x00,0x38,0x00,0x60,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0x00,0x00,0x18,0x00,0x00,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 249
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x00,0x00,0x38,0x00,0x00,0x00,0x30,0x00,0x80,0x00,0x30,0x00,0xE0,0x00,0x30,0x00,0x60,0x00,0x18,0x00,0x20,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 250
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0x80,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0x60,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0x80,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 251
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xFE,0x0F,0x00,0x00,0xFE,0x1F,0x00,0xC0,0x00,0x38,0x00,0xC0,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0x00,0x00,0x30,0x00,0xC0,0x00,0x18,0x00,0xC0,0x00,0x0C,0x00,0x00,0xFE,0x3F,0x00,0x00,0xFE,0x3F, // 252
+  0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0x00,0xF0,0x01,0x06,0x00,0x80,0x0F,0x07,0x80,0x00,0xFE,0x03,0xE0,0x00,0xFC,0x00,0x60,0xC0,0x1F,0x00,0x20,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06,  // 253
+  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0xFF,0xFF,0x07,0xE0,0xFF,0xFF,0x07,0x00,0x1C,0x18,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x06,0x30,0x00,0x00,0x0E,0x38,0x00,0x00,0x1C,0x1C,0x00,0x00,0xF8,0x0F,0x00,0x00,0xF0,0x03, // 254
+  0x00,0x00,0x00,0x00,0x00,0x0E,0x00,0x00,0x00,0x7E,0x00,0x06,0xC0,0xF0,0x01,0x06,0xC0,0x80,0x0F,0x07,0x00,0x00,0xFE,0x03,0x00,0x00,0xFC,0x00,0xC0,0xC0,0x1F,0x00,0xC0,0xF8,0x03,0x00,0x00,0x3E,0x00,0x00,0x00,0x06 // 255
+};
+#endif 
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplayUi.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplayUi.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3fb4326a4d6ead0584b61e2d6187b1e8b3241575
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplayUi.cpp
@@ -0,0 +1,406 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include "OLEDDisplayUi.h"
+
+OLEDDisplayUi::OLEDDisplayUi(OLEDDisplay *display) {
+  this->display = display;
+}
+
+void OLEDDisplayUi::init() {
+  this->display->init();
+}
+
+void OLEDDisplayUi::setTargetFPS(uint8_t fps){
+  float oldInterval = this->updateInterval;
+  this->updateInterval = ((float) 1.0 / (float) fps) * 1000;
+
+  // Calculate new ticksPerFrame
+  float changeRatio = oldInterval / (float) this->updateInterval;
+  this->ticksPerFrame *= changeRatio;
+  this->ticksPerTransition *= changeRatio;
+}
+
+// -/------ Automatic controll ------\-
+
+void OLEDDisplayUi::enableAutoTransition(){
+  this->autoTransition = true;
+}
+void OLEDDisplayUi::disableAutoTransition(){
+  this->autoTransition = false;
+}
+void OLEDDisplayUi::setAutoTransitionForwards(){
+  this->state.frameTransitionDirection = 1;
+  this->lastTransitionDirection = 1;
+}
+void OLEDDisplayUi::setAutoTransitionBackwards(){
+  this->state.frameTransitionDirection = -1;
+  this->lastTransitionDirection = -1;
+}
+void OLEDDisplayUi::setTimePerFrame(uint16_t time){
+  this->ticksPerFrame = (int) ( (float) time / (float) updateInterval);
+}
+void OLEDDisplayUi::setTimePerTransition(uint16_t time){
+  this->ticksPerTransition = (int) ( (float) time / (float) updateInterval);
+}
+
+// -/------ Customize indicator position and style -------\-
+void OLEDDisplayUi::enableIndicator(){
+  this->state.isIndicatorDrawen = true;
+}
+
+void OLEDDisplayUi::disableIndicator(){
+  this->state.isIndicatorDrawen = false;
+}
+
+void OLEDDisplayUi::enableAllIndicators(){
+  this->shouldDrawIndicators = true;
+}
+
+void OLEDDisplayUi::disableAllIndicators(){
+  this->shouldDrawIndicators = false;
+}
+
+void OLEDDisplayUi::setIndicatorPosition(IndicatorPosition pos) {
+  this->indicatorPosition = pos;
+}
+void OLEDDisplayUi::setIndicatorDirection(IndicatorDirection dir) {
+  this->indicatorDirection = dir;
+}
+void OLEDDisplayUi::setActiveSymbol(const char* symbol) {
+  this->activeSymbol = symbol;
+}
+void OLEDDisplayUi::setInactiveSymbol(const char* symbol) {
+  this->inactiveSymbol = symbol;
+}
+
+
+// -/----- Frame settings -----\-
+void OLEDDisplayUi::setFrameAnimation(AnimationDirection dir) {
+  this->frameAnimationDirection = dir;
+}
+void OLEDDisplayUi::setFrames(FrameCallback* frameFunctions, uint8_t frameCount) {
+  this->frameFunctions = frameFunctions;
+  this->frameCount     = frameCount;
+  this->resetState();
+}
+
+// -/----- Overlays ------\-
+void OLEDDisplayUi::setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount){
+  this->overlayFunctions = overlayFunctions;
+  this->overlayCount     = overlayCount;
+}
+
+// -/----- Loading Process -----\-
+
+void OLEDDisplayUi::setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction) {
+  this->loadingDrawFunction = loadingDrawFunction;
+}
+
+void OLEDDisplayUi::runLoadingProcess(LoadingStage* stages, uint8_t stagesCount) {
+  uint8_t progress = 0;
+  uint8_t increment = 100 / stagesCount;
+
+  for (uint8_t i = 0; i < stagesCount; i++) {
+    display->clear();
+    this->loadingDrawFunction(this->display, &stages[i], progress);
+    display->display();
+
+    stages[i].callback();
+
+    progress += increment;
+    yield();
+  }
+
+  display->clear();
+  this->loadingDrawFunction(this->display, &stages[stagesCount-1], progress);
+  display->display();
+
+  delay(150);
+}
+
+// -/----- Manuel control -----\-
+void OLEDDisplayUi::nextFrame() {
+  if (this->state.frameState != IN_TRANSITION) {
+    this->state.manuelControll = true;
+    this->state.frameState = IN_TRANSITION;
+    this->state.ticksSinceLastStateSwitch = 0;
+    this->lastTransitionDirection = this->state.frameTransitionDirection;
+    this->state.frameTransitionDirection = 1;
+  }
+}
+void OLEDDisplayUi::previousFrame() {
+  if (this->state.frameState != IN_TRANSITION) {
+    this->state.manuelControll = true;
+    this->state.frameState = IN_TRANSITION;
+    this->state.ticksSinceLastStateSwitch = 0;
+    this->lastTransitionDirection = this->state.frameTransitionDirection;
+    this->state.frameTransitionDirection = -1;
+  }
+}
+
+void OLEDDisplayUi::switchToFrame(uint8_t frame) {
+  if (frame >= this->frameCount) return;
+  this->state.ticksSinceLastStateSwitch = 0;
+  if (frame == this->state.currentFrame) return;
+  this->state.frameState = FIXED;
+  this->state.currentFrame = frame;
+  this->state.isIndicatorDrawen = true;
+}
+
+void OLEDDisplayUi::transitionToFrame(uint8_t frame) {
+  if (frame >= this->frameCount) return;
+  this->state.ticksSinceLastStateSwitch = 0;
+  if (frame == this->state.currentFrame) return;
+  this->nextFrameNumber = frame;
+  this->lastTransitionDirection = this->state.frameTransitionDirection;
+  this->state.manuelControll = true;
+  this->state.frameState = IN_TRANSITION;
+  this->state.frameTransitionDirection = frame < this->state.currentFrame ? -1 : 1;
+}
+
+
+// -/----- State information -----\-
+OLEDDisplayUiState* OLEDDisplayUi::getUiState(){
+  return &this->state;
+}
+
+
+int8_t OLEDDisplayUi::update(){
+  long frameStart = millis();
+  int8_t timeBudget = this->updateInterval - (frameStart - this->state.lastUpdate);
+  if ( timeBudget <= 0) {
+    // Implement frame skipping to ensure time budget is keept
+    if (this->autoTransition && this->state.lastUpdate != 0) this->state.ticksSinceLastStateSwitch += ceil(-timeBudget / this->updateInterval);
+
+    this->state.lastUpdate = frameStart;
+    this->tick();
+  }
+  return this->updateInterval - (millis() - frameStart);
+}
+
+
+void OLEDDisplayUi::tick() {
+  this->state.ticksSinceLastStateSwitch++;
+
+  switch (this->state.frameState) {
+    case IN_TRANSITION:
+        if (this->state.ticksSinceLastStateSwitch >= this->ticksPerTransition){
+          this->state.frameState = FIXED;
+          this->state.currentFrame = getNextFrameNumber();
+          this->state.ticksSinceLastStateSwitch = 0;
+          this->nextFrameNumber = -1;
+        }
+      break;
+    case FIXED:
+      // Revert manuelControll
+      if (this->state.manuelControll) {
+        this->state.frameTransitionDirection = this->lastTransitionDirection;
+        this->state.manuelControll = false;
+      }
+      if (this->state.ticksSinceLastStateSwitch >= this->ticksPerFrame){
+          if (this->autoTransition){
+            this->state.frameState = IN_TRANSITION;
+          }
+          this->state.ticksSinceLastStateSwitch = 0;
+      }
+      break;
+  }
+
+  this->display->clear();
+  this->drawFrame();
+  if (shouldDrawIndicators) {
+    this->drawIndicator();
+  }
+  this->drawOverlays();
+  this->display->display();
+}
+
+void OLEDDisplayUi::resetState() {
+  this->state.lastUpdate = 0;
+  this->state.ticksSinceLastStateSwitch = 0;
+  this->state.frameState = FIXED;
+  this->state.currentFrame = 0;
+  this->state.isIndicatorDrawen = true;
+}
+
+void OLEDDisplayUi::drawFrame(){
+  switch (this->state.frameState){
+     case IN_TRANSITION: {
+       float progress = (float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition;
+       int16_t x, y, x1, y1;
+       switch(this->frameAnimationDirection){
+        case SLIDE_LEFT:
+          x = -128 * progress;
+          y = 0;
+          x1 = x + 128;
+          y1 = 0;
+          break;
+        case SLIDE_RIGHT:
+          x = 128 * progress;
+          y = 0;
+          x1 = x - 128;
+          y1 = 0;
+          break;
+        case SLIDE_UP:
+          x = 0;
+          y = -64 * progress;
+          x1 = 0;
+          y1 = y + 64;
+          break;
+        case SLIDE_DOWN:
+          x = 0;
+          y = 64 * progress;
+          x1 = 0;
+          y1 = y - 64;
+          break;
+       }
+
+       // Invert animation if direction is reversed.
+       int8_t dir = this->state.frameTransitionDirection >= 0 ? 1 : -1;
+       x *= dir; y *= dir; x1 *= dir; y1 *= dir;
+
+       bool drawenCurrentFrame;
+
+
+       // Prope each frameFunction for the indicator Drawen state
+       this->enableIndicator();
+       (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, x, y);
+       drawenCurrentFrame = this->state.isIndicatorDrawen;
+
+       this->enableIndicator();
+       (this->frameFunctions[this->getNextFrameNumber()])(this->display, &this->state, x1, y1);
+
+       // Build up the indicatorDrawState
+       if (drawenCurrentFrame && !this->state.isIndicatorDrawen) {
+         // Drawen now but not next
+         this->indicatorDrawState = 2;
+       } else if (!drawenCurrentFrame && this->state.isIndicatorDrawen) {
+         // Not drawen now but next
+         this->indicatorDrawState = 1;
+       } else if (!drawenCurrentFrame && !this->state.isIndicatorDrawen) {
+         // Not drawen in both frames
+         this->indicatorDrawState = 3;
+       }
+
+       // If the indicator isn't draw in the current frame
+       // reflect it in state.isIndicatorDrawen
+       if (!drawenCurrentFrame) this->state.isIndicatorDrawen = false;
+
+       break;
+     }
+     case FIXED:
+      // Always assume that the indicator is drawn!
+      // And set indicatorDrawState to "not known yet"
+      this->indicatorDrawState = 0;
+      this->enableIndicator();
+      (this->frameFunctions[this->state.currentFrame])(this->display, &this->state, 0, 0);
+      break;
+  }
+}
+
+void OLEDDisplayUi::drawIndicator() {
+
+    // Only draw if the indicator is invisible
+    // for both frames or
+    // the indiactor is shown and we are IN_TRANSITION
+    if (this->indicatorDrawState == 3 || (!this->state.isIndicatorDrawen && this->state.frameState != IN_TRANSITION)) {
+      return;
+    }
+
+    uint8_t posOfHighlightFrame;
+    float indicatorFadeProgress = 0;
+
+    // if the indicator needs to be slided in we want to
+    // highlight the next frame in the transition
+    uint8_t frameToHighlight = this->indicatorDrawState == 1 ? this->getNextFrameNumber() : this->state.currentFrame;
+
+    // Calculate the frame that needs to be highlighted
+    // based on the Direction the indiactor is drawn
+    switch (this->indicatorDirection){
+      case LEFT_RIGHT:
+        posOfHighlightFrame = frameToHighlight;
+        break;
+      case RIGHT_LEFT:
+        posOfHighlightFrame = this->frameCount - frameToHighlight;
+        break;
+    }
+
+    switch (this->indicatorDrawState) {
+      case 1: // Indicator was not drawn in this frame but will be in next
+        // Slide IN
+        indicatorFadeProgress = 1 - ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition);
+        break;
+      case 2: // Indicator was drawn in this frame but not in next
+        // Slide OUT
+        indicatorFadeProgress = ((float) this->state.ticksSinceLastStateSwitch / (float) this->ticksPerTransition);
+        break;
+    }
+
+    uint16_t frameStartPos = (12 * frameCount / 2);
+    const char *image;
+    uint16_t x,y;
+    for (byte i = 0; i < this->frameCount; i++) {
+
+      switch (this->indicatorPosition){
+        case TOP:
+          y = 0 - (8 * indicatorFadeProgress);
+          x = 64 - frameStartPos + 12 * i;
+          break;
+        case BOTTOM:
+          y = 56 + (8 * indicatorFadeProgress);
+          x = 64 - frameStartPos + 12 * i;
+          break;
+        case RIGHT:
+          x = 120 + (8 * indicatorFadeProgress);
+          y = 32 - frameStartPos + 2 + 12 * i;
+          break;
+        case LEFT:
+          x = 0 - (8 * indicatorFadeProgress);
+          y = 32 - frameStartPos + 2 + 12 * i;
+          break;
+      }
+
+      if (posOfHighlightFrame == i) {
+         image = this->activeSymbol;
+      } else {
+         image = this->inactiveSymbol;
+      }
+
+      this->display->drawFastImage(x, y, 8, 8, image);
+    }
+}
+
+void OLEDDisplayUi::drawOverlays() {
+ for (uint8_t i=0;i<this->overlayCount;i++){
+    (this->overlayFunctions[i])(this->display, &this->state);
+ }
+}
+
+uint8_t OLEDDisplayUi::getNextFrameNumber(){
+  if (this->nextFrameNumber != -1) return this->nextFrameNumber;
+  return (this->state.currentFrame + this->frameCount + this->state.frameTransitionDirection) % this->frameCount;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplayUi.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplayUi.h
new file mode 100644
index 0000000000000000000000000000000000000000..35a1e99bcf449c3d2f5f4405f223949005fce497
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/OLEDDisplayUi.h
@@ -0,0 +1,305 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#ifndef OLEDDISPLAYUI_h
+#define OLEDDISPLAYUI_h
+
+#include <Arduino.h>
+#include "OLEDDisplay.h"
+
+//#define DEBUG_OLEDDISPLAYUI(...) Serial.printf( __VA_ARGS__ )
+
+#ifndef DEBUG_OLEDDISPLAYUI
+#define DEBUG_OLEDDISPLAYUI(...)
+#endif
+
+enum AnimationDirection {
+  SLIDE_UP,
+  SLIDE_DOWN,
+  SLIDE_LEFT,
+  SLIDE_RIGHT
+};
+
+enum IndicatorPosition {
+  TOP,
+  RIGHT,
+  BOTTOM,
+  LEFT
+};
+
+enum IndicatorDirection {
+  LEFT_RIGHT,
+  RIGHT_LEFT
+};
+
+enum FrameState {
+  IN_TRANSITION,
+  FIXED
+};
+
+
+const char ANIMATION_activeSymbol[] PROGMEM = {
+  0x00, 0x18, 0x3c, 0x7e, 0x7e, 0x3c, 0x18, 0x00
+};
+
+const char ANIMATION_inactiveSymbol[] PROGMEM = {
+  0x00, 0x0, 0x0, 0x18, 0x18, 0x0, 0x0, 0x00
+};
+
+
+// Structure of the UiState
+struct OLEDDisplayUiState {
+  uint64_t     lastUpdate                = 0;
+  uint16_t      ticksSinceLastStateSwitch = 0;
+
+  FrameState    frameState                = FIXED;
+  uint8_t       currentFrame              = 0;
+
+  bool          isIndicatorDrawen         = true;
+
+  // Normal = 1, Inverse = -1;
+  int8_t        frameTransitionDirection  = 1;
+
+  bool          manuelControll            = false;
+
+  // Custom data that can be used by the user
+  void*         userData                  = NULL;
+};
+
+struct LoadingStage {
+  const char* process;
+  void (*callback)();
+};
+
+typedef void (*FrameCallback)(OLEDDisplay *display,  OLEDDisplayUiState* state, int16_t x, int16_t y);
+typedef void (*OverlayCallback)(OLEDDisplay *display,  OLEDDisplayUiState* state);
+typedef void (*LoadingDrawFunction)(OLEDDisplay *display, LoadingStage* stage, uint8_t progress);
+
+class OLEDDisplayUi {
+  private:
+    OLEDDisplay             *display;
+
+    // Symbols for the Indicator
+    IndicatorPosition   indicatorPosition         = BOTTOM;
+    IndicatorDirection  indicatorDirection        = LEFT_RIGHT;
+
+    const char*         activeSymbol              = ANIMATION_activeSymbol;
+    const char*         inactiveSymbol            = ANIMATION_inactiveSymbol;
+
+    bool                shouldDrawIndicators      = true;
+
+    // Values for the Frames
+    AnimationDirection  frameAnimationDirection   = SLIDE_RIGHT;
+
+    int8_t              lastTransitionDirection   = 1;
+
+    uint16_t            ticksPerFrame             = 151; // ~ 5000ms at 30 FPS
+    uint16_t            ticksPerTransition        = 15;  // ~  500ms at 30 FPS
+
+    bool                autoTransition            = true;
+
+    FrameCallback*      frameFunctions;
+    uint8_t             frameCount                = 0;
+
+    // Internally used to transition to a specific frame
+    int8_t              nextFrameNumber           = -1;
+
+    // Values for Overlays
+    OverlayCallback*    overlayFunctions;
+    uint8_t             overlayCount              = 0;
+
+    // Will the Indicator be drawen
+    // 3 Not drawn in both frames
+    // 2 Drawn this frame but not next
+    // 1 Not drown this frame but next
+    // 0 Not known yet
+    uint8_t                indicatorDrawState        = 1;
+
+    // Loading screen
+    LoadingDrawFunction loadingDrawFunction       = [](OLEDDisplay *display, LoadingStage* stage, uint8_t progress) {
+      display->setTextAlignment(TEXT_ALIGN_CENTER);
+      display->setFont(ArialMT_Plain_10);
+      display->drawString(64, 18, stage->process);
+      display->drawProgressBar(4, 32, 120, 8, progress);
+    };
+
+    // UI State
+    OLEDDisplayUiState      state;
+
+    // Bookeeping for update
+    uint8_t             updateInterval            = 33;
+
+    uint8_t             getNextFrameNumber();
+    void                drawIndicator();
+    void                drawFrame();
+    void                drawOverlays();
+    void                tick();
+    void                resetState();
+
+  public:
+
+    OLEDDisplayUi(OLEDDisplay *display);
+
+    /**
+     * Initialise the display
+     */
+    void init();
+
+    /**
+     * Configure the internal used target FPS
+     */
+    void setTargetFPS(uint8_t fps);
+
+    // Automatic Controll
+    /**
+     * Enable automatic transition to next frame after the some time can be configured with `setTimePerFrame` and `setTimePerTransition`.
+     */
+    void enableAutoTransition();
+
+    /**
+     * Disable automatic transition to next frame.
+     */
+    void disableAutoTransition();
+
+    /**
+     * Set the direction if the automatic transitioning
+     */
+    void setAutoTransitionForwards();
+    void setAutoTransitionBackwards();
+
+    /**
+     *  Set the approx. time a frame is displayed
+     */
+    void setTimePerFrame(uint16_t time);
+
+    /**
+     * Set the approx. time a transition will take
+     */
+    void setTimePerTransition(uint16_t time);
+
+    // Customize indicator position and style
+
+    /**
+     * Draw the indicator.
+     * This is the defaut state for all frames if
+     * the indicator was hidden on the previous frame
+     * it will be slided in.
+     */
+    void enableIndicator();
+
+    /**
+     * Don't draw the indicator.
+     * This will slide out the indicator
+     * when transitioning to the next frame.
+     */
+    void disableIndicator();
+
+    /**
+     * Enable drawing of indicators
+     */
+    void enableAllIndicators();
+
+    /**
+     * Disable draw of indicators.
+     */
+    void disableAllIndicators();
+
+    /**
+     * Set the position of the indicator bar.
+     */
+    void setIndicatorPosition(IndicatorPosition pos);
+
+    /**
+     * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING
+     */
+    void setIndicatorDirection(IndicatorDirection dir);
+
+    /**
+     * Set the symbol to indicate an active frame in the indicator bar.
+     */
+    void setActiveSymbol(const char* symbol);
+
+    /**
+     * Set the symbol to indicate an inactive frame in the indicator bar.
+     */
+    void setInactiveSymbol(const char* symbol);
+
+
+    // Frame settings
+
+    /**
+     * Configure what animation is used to transition from one frame to another
+     */
+    void setFrameAnimation(AnimationDirection dir);
+
+    /**
+     * Add frame drawing functions
+     */
+    void setFrames(FrameCallback* frameFunctions, uint8_t frameCount);
+
+    // Overlay
+
+    /**
+     * Add overlays drawing functions that are draw independent of the Frames
+     */
+    void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount);
+
+
+    // Loading animation
+    /**
+     * Set the function that will draw each step
+     * in the loading animation
+     */
+    void setLoadingDrawFunction(LoadingDrawFunction loadingFunction);
+
+
+    /**
+     * Run the loading process
+     */
+    void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount);
+
+
+    // Manual Control
+    void nextFrame();
+    void previousFrame();
+
+    /**
+     * Switch without transition to frame `frame`.
+     */
+    void switchToFrame(uint8_t frame);
+
+    /**
+     * Transition to frame `frame`, when the `frame` number is bigger than the current
+     * frame the forward animation will be used, otherwise the backwards animation is used.
+     */
+    void transitionToFrame(uint8_t frame);
+
+    // State Info
+    OLEDDisplayUiState* getUiState();
+
+    int8_t update();
+};
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/README.md b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..01c134c3506041cdd547a76b78b673f1d2659b49
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/README.md
@@ -0,0 +1,393 @@
+esp8266-oled-ssd1306 [![Build Status](https://travis-ci.org/squix78/esp8266-oled-ssd1306.svg?branch=dev-branch-3.0.0)](https://travis-ci.org/squix78/esp8266-oled-ssd1306)
+============
+
+> We just released version 3.0.0. Please have a look at our [upgrade guide](UPGRADE-3.0.md)
+
+This is a driver for the SSD1306 based 128x64 pixel OLED display running on the Arduino/ESP8266 platform.
+Can be used with either the I2C or SPI version of the display
+
+You can either download this library as a zip file and unpack it to your Arduino/libraries folder or (once it has been added) choose it from the Arduino library manager.
+
+It is also available as a platformio library. Just execute the following command:
+```
+platformio lib install 562
+```
+
+## Credits
+This library has initially been written by Daniel Eichhorn (@squix78). Many thanks go to Fabrice Weinberg (@FWeinb) for optimizing and refactoring many aspects of the library. Also many thanks to the many committers who helped to add new features and who fixed many bugs.
+The init sequence for the SSD1306 was inspired by Adafruit's library for the same display.
+
+## Usage
+
+Check out the examples folder for a few comprehensive demonstrations how to use the library. Also check out the ESP8266 Weather Station library (https://github.com/squix78/esp8266-weather-station) which uses the OLED library to display beautiful weather information.
+
+## Upgrade
+
+The API changed a lot with the 3.0 release. If you were using this library with older versions please have a look at the [Upgrade Guide](UPGRADE-3.0.md).
+
+## Features
+
+* Draw pixels at given coordinates
+* Draw lines from given coordinates to given coordinates
+* Draw or fill a rectangle with given dimensions
+* Draw Text at given coordinates:
+ * Define Alignment: Left, Right and Center
+ * Set the Fontface you want to use (see section Fonts below)
+ * Limit the width of the text by an amount of pixels. Before this widths will be reached, the renderer will wrap the text to a new line if possible
+* Display content in automatically side scrolling carousel
+ * Define transition cycles
+ * Define how long one frame will be displayed
+ * Draw the different frames in callback methods
+ * One indicator per frame will be automatically displayed. The active frame will be displayed from inactive once
+
+## Fonts
+
+Fonts are defined in a proprietary but open format. You can create new font files by choosing from a given list
+of open sourced Fonts from this web app: http://oleddisplay.squix.ch
+Choose the font family, style and size, check the preview image and if you like what you see click the "Create" button. This will create the font array in a text area form where you can copy and paste it into a new or existing header file.
+
+
+![FontTool](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/FontTool.png)
+
+## Hardware Abstraction
+
+The library supports different protocols to access the OLED display. Currently there is support for I2C using the built in Wire.h library, I2C by using the much faster BRZO I2C library [https://github.com/pasko-zh/brzo_i2c] written in assembler and it also supports displays which come with the SPI interface.
+
+### I2C with Wire.h
+
+```C++
+#include <Wire.h>  
+#include "SSD1306.h"
+
+SSD1306  display(ADDRESS, SDA, SDC);
+```
+or for a SH1106:
+```C++
+#include <Wire.h>  
+#include "SH1106.h"
+
+SH1106  display(ADDRESS, SDA, SDC);
+```
+
+### I2C with brzo_i2c
+
+```C++
+#include <brzo_i2c.h>
+#include "SSD1306Brzo.h"
+
+SSD1306Brzo display(ADDRESS, SDA, SDC);
+```
+or for the SH1106:
+```C++
+#include <brzo_i2c.h>
+#include "SH1106Brzo.h"
+
+SH1106Brzo display(ADDRESS, SDA, SDC);
+```
+
+### SPI
+
+```C++
+#include <SPI.h>
+#include "SSD1306Spi.h"
+
+SSD1306Spi display(RES, DC, CS);
+```
+or for the SH1106:
+```C++
+#include <SPI.h>
+#include "SH1106Spi.h"
+
+SH1106Spi display(RES, DC, CS);
+```
+
+## API
+
+### Display Control
+
+```C++
+// Initialize the display
+void init();
+
+// Free the memory used by the display
+void end();
+
+// Cycle through the initialization
+void resetDisplay(void);
+
+// Connect again to the display through I2C
+void reconnect(void);
+
+// Turn the display on
+void displayOn(void);
+
+// Turn the display offs
+void displayOff(void);
+
+// Clear the local pixel buffer
+void clear(void);
+
+// Write the buffer to the display memory
+void display(void);
+
+// Inverted display mode
+void invertDisplay(void);
+
+// Normal display mode
+void normalDisplay(void);
+
+// Set display contrast
+void setContrast(char contrast);
+
+// Turn the display upside down
+void flipScreenVertically();
+```
+
+## Pixel drawing
+
+```C++
+
+/* Drawing functions */
+// Sets the color of all pixel operations
+void setColor(OLEDDISPLAY_COLOR color);
+
+// Draw a pixel at given position
+void setPixel(int16_t x, int16_t y);
+
+// Draw a line from position 0 to position 1
+void drawLine(int16_t x0, int16_t y0, int16_t x1, int16_t y1);
+
+// Draw the border of a rectangle at the given location
+void drawRect(int16_t x, int16_t y, int16_t width, int16_t height);
+
+// Fill the rectangle
+void fillRect(int16_t x, int16_t y, int16_t width, int16_t height);
+
+// Draw the border of a circle
+void drawCircle(int16_t x, int16_t y, int16_t radius);
+
+// Fill circle
+void fillCircle(int16_t x, int16_t y, int16_t radius);
+
+// Draw a line horizontally
+void drawHorizontalLine(int16_t x, int16_t y, int16_t length);
+
+// Draw a lin vertically
+void drawVerticalLine(int16_t x, int16_t y, int16_t length);
+
+// Draws a rounded progress bar with the outer dimensions given by width and height. Progress is
+// a unsigned byte value between 0 and 100
+void drawProgressBar(uint16_t x, uint16_t y, uint16_t width, uint16_t height, uint8_t progress);
+
+// Draw a bitmap in the internal image format
+void drawFastImage(int16_t x, int16_t y, int16_t width, int16_t height, const char *image);
+
+// Draw a XBM
+void drawXbm(int16_t x, int16_t y, int16_t width, int16_t height, const char* xbm);
+```
+
+## Text operations
+
+``` C++
+void drawString(int16_t x, int16_t y, String text);
+
+// Draws a String with a maximum width at the given location.
+// If the given String is wider than the specified width
+// The text will be wrapped to the next line at a space or dash
+void drawStringMaxWidth(int16_t x, int16_t y, int16_t maxLineWidth, String text);
+
+// Returns the width of the const char* with the current
+// font settings
+uint16_t getStringWidth(const char* text, uint16_t length);
+
+// Convencience method for the const char version
+uint16_t getStringWidth(String text);
+
+// Specifies relative to which anchor point
+// the text is rendered. Available constants:
+// TEXT_ALIGN_LEFT, TEXT_ALIGN_CENTER, TEXT_ALIGN_RIGHT, TEXT_ALIGN_CENTER_BOTH
+void setTextAlignment(OLEDDISPLAY_TEXT_ALIGNMENT textAlignment);
+
+// Sets the current font. Available default fonts
+// ArialMT_Plain_10, ArialMT_Plain_16, ArialMT_Plain_24
+// Or create one with the font tool at http://oleddisplay.squix.ch
+void setFont(const char* fontData);
+```
+
+## Ui Library (OLEDDisplayUi)
+
+The Ui Library is used to provide a basic set of Ui elements called, `Frames` and `Overlays`. A `Frame` is used to provide
+information the default behaviour is to display a `Frame` for a defined time and than move to the next. The library also provides an `Indicator` that will be updated accordingly. An `Overlay` on the other hand is a pieces of information (e.g. a clock) that is displayed always at the same position.
+
+
+```C++
+/**
+ * Initialise the display
+ */
+void init();
+
+/**
+ * Configure the internal used target FPS
+ */
+void setTargetFPS(uint8_t fps);
+
+/**
+ * Enable automatic transition to next frame after the some time can be configured with
+ * `setTimePerFrame` and `setTimePerTransition`.
+ */
+void enableAutoTransition();
+
+/**
+ * Disable automatic transition to next frame.
+ */
+void disableAutoTransition();
+
+/**
+ * Set the direction if the automatic transitioning
+ */
+void setAutoTransitionForwards();
+void setAutoTransitionBackwards();
+
+/**
+ *  Set the approx. time a frame is displayed
+ */
+void setTimePerFrame(uint16_t time);
+
+/**
+ * Set the approx. time a transition will take
+ */
+void setTimePerTransition(uint16_t time);
+
+/**
+ * Draw the indicator.
+ * This is the default state for all frames if
+ * the indicator was hidden on the previous frame
+ * it will be slided in.
+ */
+void enableIndicator();
+
+/**
+ * Don't draw the indicator.
+ * This will slide out the indicator
+ * when transitioning to the next frame.
+ */
+void disableIndicator();
+
+/**
+ * Enable drawing of all indicators.
+ */
+void enableAllIndicators();
+
+/**
+ * Disable drawing of all indicators.
+ */
+void disableAllIndicators();
+
+/**
+ * Set the position of the indicator bar.
+ */
+void setIndicatorPosition(IndicatorPosition pos);
+
+/**
+ * Set the direction of the indicator bar. Defining the order of frames ASCENDING / DESCENDING
+ */
+void setIndicatorDirection(IndicatorDirection dir);
+
+/**
+ * Set the symbol to indicate an active frame in the indicator bar.
+ */
+void setActiveSymbol(const char* symbol);
+
+/**
+ * Set the symbol to indicate an inactive frame in the indicator bar.
+ */
+void setInactiveSymbol(const char* symbol);
+
+/**
+ * Configure what animation is used to transition from one frame to another
+ */
+void setFrameAnimation(AnimationDirection dir);
+
+/**
+ * Add frame drawing functions
+ */
+void setFrames(FrameCallback* frameFunctions, uint8_t frameCount);
+
+/**
+ * Add overlays drawing functions that are draw independent of the Frames
+ */
+void setOverlays(OverlayCallback* overlayFunctions, uint8_t overlayCount);
+
+/**
+ * Set the function that will draw each step
+ * in the loading animation
+ */
+void setLoadingDrawFunction(LoadingDrawFunction loadingDrawFunction);
+
+/**
+ * Run the loading process
+ */
+void runLoadingProcess(LoadingStage* stages, uint8_t stagesCount);
+
+// Manuell Controll
+void nextFrame();
+void previousFrame();
+
+/**
+ * Switch without transition to frame `frame`.
+ */
+void switchToFrame(uint8_t frame);
+
+/**
+ * Transition to frame `frame`, when the `frame` number is bigger than the current
+ * frame the forward animation will be used, otherwise the backwards animation is used.
+ */
+void transitionToFrame(uint8_t frame);
+
+// State Info
+OLEDDisplayUiState* getUiState();
+
+// This needs to be called in the main loop
+// the returned value is the remaining time (in ms)
+// you have to draw after drawing to keep the frame budget.
+int8_t update();
+```
+
+## Example: SSD1306Demo
+
+### Frame 1
+![DemoFrame1](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame1.jpg)
+
+This frame shows three things:
+ * How to draw an xbm image
+ * How to draw a static text which is not moved by the frame transition
+ * The active/inactive frame indicators
+
+### Frame 2
+![DemoFrame2](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame2.jpg)
+
+Currently there are one fontface with three sizes included in the library: Arial 10, 16 and 24. Once the converter is published you will be able to convert any ttf font into the used format.
+
+### Frame 3
+
+![DemoFrame3](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame3.jpg)
+
+This frame demonstrates the text alignment. The coordinates in the frame show relative to which position the texts have been rendered.
+
+### Frame 4
+
+![DemoFrame4](https://github.com/squix78/esp8266-oled-ssd1306/raw/master/resources/DemoFrame4.jpg)
+
+This shows how to use define a maximum width after which the driver automatically wraps a word to the next line. This comes in very handy if you have longer texts to display.
+
+### SPI version
+
+![SPIVersion](https://github.com/neptune2/esp8266-oled-ssd1306/raw/master/resources/SPI_version.jpg)
+
+This shows the code working on the SPI version of the display. See demo code for ESP8266 pins used.
+
+## Project using this library
+
+ * [QRCode ESP8266](https://github.com/anunpanya/ESP8266_QRcode) (by @anunpanya)
+ * [Scan I2C](https://github.com/hallard/Scan-I2C-WiFi) (by @hallard)
+ * [Weather Station](https://github.com/squix78/esp8266-weather-station) (by @squix)
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106.h
new file mode 100644
index 0000000000000000000000000000000000000000..55dd4090efc0d138d571d7418b4ef6360b1f610d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106.h
@@ -0,0 +1,36 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#ifndef SH1106_h
+#define SH1106_h
+#include "SH1106Wire.h"
+
+// For make SH1106 an alias for SH1106Wire
+typedef SH1106Wire SH1106;
+
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106Brzo.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106Brzo.h
new file mode 100644
index 0000000000000000000000000000000000000000..385630b3267a7191fe0c81a21fe34d4b5de0a2e4
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106Brzo.h
@@ -0,0 +1,133 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#ifndef SH1106Brzo_h
+#define SH1106Brzo_h
+
+#include "OLEDDisplay.h"
+#include <brzo_i2c.h>
+
+#if F_CPU == 160000000L
+  #define BRZO_I2C_SPEED 1000
+#else
+  #define BRZO_I2C_SPEED 800
+#endif
+
+class SH1106Brzo : public OLEDDisplay {
+  private:
+      uint8_t             _address;
+      uint8_t             _sda;
+      uint8_t             _scl;
+
+  public:
+    SH1106Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl) {
+      this->_address = _address;
+      this->_sda = _sda;
+      this->_scl = _scl;
+    }
+
+    bool connect(){
+      brzo_i2c_setup(_sda, _scl, 0);
+      return true;
+    }
+
+    void display(void) {
+    #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+       uint8_t minBoundY = ~0;
+       uint8_t maxBoundY = 0;
+
+       uint8_t minBoundX = ~0;
+       uint8_t maxBoundX = 0;
+       uint8_t x, y;
+
+       // Calculate the Y bounding box of changes
+       // and copy buffer[pos] to buffer_back[pos];
+       for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) {
+         for (x = 0; x < DISPLAY_WIDTH; x++) {
+          uint16_t pos = x + y * DISPLAY_WIDTH;
+          if (buffer[pos] != buffer_back[pos]) {
+            minBoundY = _min(minBoundY, y);
+            maxBoundY = _max(maxBoundY, y);
+            minBoundX = _min(minBoundX, x);
+            maxBoundX = _max(maxBoundX, x);
+          }
+          buffer_back[pos] = buffer[pos];
+        }
+        yield();
+       }
+
+       // If the minBoundY wasn't updated
+       // we can savely assume that buffer_back[pos] == buffer[pos]
+       // holdes true for all values of pos
+       if (minBoundY == ~0) return;
+
+       byte k = 0;
+       uint8_t sendBuffer[17];
+       sendBuffer[0] = 0x40;
+
+       // Calculate the colum offset 
+       uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F;
+       uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 );
+
+       brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED);
+
+       for (y = minBoundY; y <= maxBoundY; y++) {
+         sendCommand(0xB0 + y);
+         sendCommand(minBoundXp2H);
+         sendCommand(minBoundXp2L);
+         for (x = minBoundX; x <= maxBoundX; x++) {
+             k++;
+             sendBuffer[k] = buffer[x + y * DISPLAY_WIDTH];
+             if (k == 16)  {
+               brzo_i2c_write(sendBuffer, 17, true);
+               k = 0;
+             }
+         }
+         if (k != 0) {
+           brzo_i2c_write(sendBuffer, k + 1, true);
+           k = 0;
+         }
+         yield();
+       }
+       if (k != 0) {
+         brzo_i2c_write(sendBuffer, k + 1, true);
+       }
+       brzo_i2c_end_transaction();
+     #else
+     #endif
+    }
+
+  private:
+    inline void sendCommand(uint8_t com) __attribute__((always_inline)){
+      uint8_t command[2] = {0x80 /* command mode */, com};
+      brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED);
+      brzo_i2c_write(command, 2, true);
+      brzo_i2c_end_transaction();
+    }
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106Spi.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106Spi.h
new file mode 100644
index 0000000000000000000000000000000000000000..0a64e27568ff9fc1270e13ec0eec221ac9337a67
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106Spi.h
@@ -0,0 +1,128 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#ifndef SH1106Spi_h
+#define SH1106Spi_h
+
+#include "OLEDDisplay.h"
+#include <SPI.h>
+
+class SH1106Spi : public OLEDDisplay {
+  private:
+      uint8_t             _rst;
+      uint8_t             _dc;
+
+  public:
+
+    SH1106Spi(uint8_t _rst, uint8_t _dc) {
+      this->_rst = _rst;
+      this->_dc  = _dc;
+    }
+
+    bool connect(){
+      pinMode(_dc, OUTPUT);
+      pinMode(_rst, OUTPUT);
+
+      SPI.begin ();
+      SPI.setClockDivider (SPI_CLOCK_DIV2);
+
+      // Pulse Reset low for 10ms
+      digitalWrite(_rst, HIGH);
+      delay(1);
+      digitalWrite(_rst, LOW);
+      delay(10);
+      digitalWrite(_rst, HIGH);
+      return true;
+    }
+
+    void display(void) {
+    #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+       uint8_t minBoundY = ~0;
+       uint8_t maxBoundY = 0;
+
+       uint8_t minBoundX = ~0;
+       uint8_t maxBoundX = 0;
+
+       uint8_t x, y;
+
+       // Calculate the Y bounding box of changes
+       // and copy buffer[pos] to buffer_back[pos];
+       for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) {
+         for (x = 0; x < DISPLAY_WIDTH; x++) {
+          uint16_t pos = x + y * DISPLAY_WIDTH;
+          if (buffer[pos] != buffer_back[pos]) {
+            minBoundY = _min(minBoundY, y);
+            maxBoundY = _max(maxBoundY, y);
+            minBoundX = _min(minBoundX, x);
+            maxBoundX = _max(maxBoundX, x);
+          }
+          buffer_back[pos] = buffer[pos];
+        }
+        yield();
+       }
+
+       // If the minBoundY wasn't updated
+       // we can savely assume that buffer_back[pos] == buffer[pos]
+       // holdes true for all values of pos
+       if (minBoundY == ~0) return;
+
+       // Calculate the colum offset
+       uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F;
+       uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 );
+
+       for (y = minBoundY; y <= maxBoundY; y++) {
+         sendCommand(0xB0 + y);
+         sendCommand(minBoundXp2H);
+         sendCommand(minBoundXp2L);
+         digitalWrite(_dc, HIGH);   // data mode
+         for (x = minBoundX; x <= maxBoundX; x++) {
+           SPI.transfer(buffer[x + y * DISPLAY_WIDTH]);
+         }
+         yield();
+       }
+     #else
+      for (uint8_t y=0; y<DISPLAY_HEIGHT/8; y++) {
+        sendCommand(0xB0 + y);
+        sendCommand(0x02);
+        sendCommand(0x10);
+        digitalWrite(_dc, HIGH);   // data mode
+        for( uint8_t x=0; x < DISPLAY_WIDTH; x++) {
+          SPI.transfer(buffer[x + y * DISPLAY_WIDTH]);
+        }
+        yield();
+      }
+     #endif
+    }
+
+  private:
+    inline void sendCommand(uint8_t com) __attribute__((always_inline)){
+      digitalWrite(_dc, LOW);
+      SPI.transfer(com);
+    }
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106Wire.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106Wire.h
new file mode 100644
index 0000000000000000000000000000000000000000..f0784b9423051a4e91bf2e237b38347a2f6b6ede
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SH1106Wire.h
@@ -0,0 +1,152 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#ifndef SH1106Wire_h
+#define SH1106Wire_h
+
+#include "OLEDDisplay.h"
+#include <Wire.h>
+
+#define SH1106_SET_PUMP_VOLTAGE 0X30
+#define SH1106_SET_PUMP_MODE 0XAD
+#define SH1106_PUMP_ON 0X8B
+#define SH1106_PUMP_OFF 0X8A
+//--------------------------------------
+
+class SH1106Wire : public OLEDDisplay {
+  private:
+      uint8_t             _address;
+      uint8_t             _sda;
+      uint8_t             _scl;
+
+  public:
+    SH1106Wire(uint8_t _address, uint8_t _sda, uint8_t _scl) {
+      this->_address = _address;
+      this->_sda = _sda;
+      this->_scl = _scl;
+    }
+
+    bool connect() {
+      Wire.begin(this->_sda, this->_scl);
+      // Let's use ~700khz if ESP8266 is in 160Mhz mode
+      // this will be limited to ~400khz if the ESP8266 in 80Mhz mode.
+      Wire.setClock(700000);
+      return true;
+    }
+
+    void display(void) {
+      #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+        uint8_t minBoundY = ~0;
+        uint8_t maxBoundY = 0;
+
+        uint8_t minBoundX = ~0;
+        uint8_t maxBoundX = 0;
+
+        uint8_t x, y;
+
+        // Calculate the Y bounding box of changes
+        // and copy buffer[pos] to buffer_back[pos];
+        for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) {
+          for (x = 0; x < DISPLAY_WIDTH; x++) {
+           uint16_t pos = x + y * DISPLAY_WIDTH;
+           if (buffer[pos] != buffer_back[pos]) {
+             minBoundY = _min(minBoundY, y);
+             maxBoundY = _max(maxBoundY, y);
+             minBoundX = _min(minBoundX, x);
+             maxBoundX = _max(maxBoundX, x);
+           }
+           buffer_back[pos] = buffer[pos];
+         }
+         yield();
+        }
+
+        // If the minBoundY wasn't updated
+        // we can savely assume that buffer_back[pos] == buffer[pos]
+        // holdes true for all values of pos
+        if (minBoundY == ~0) return;
+
+        // Calculate the colum offset
+        uint8_t minBoundXp2H = (minBoundX + 2) & 0x0F;
+        uint8_t minBoundXp2L = 0x10 | ((minBoundX + 2) >> 4 );
+
+        byte k = 0;
+        for (y = minBoundY; y <= maxBoundY; y++) {
+          sendCommand(0xB0 + y);
+          sendCommand(minBoundXp2H);
+          sendCommand(minBoundXp2L);
+          for (x = minBoundX; x <= maxBoundX; x++) {
+            if (k == 0) {
+              Wire.beginTransmission(_address);
+              Wire.write(0x40);
+            }
+            Wire.write(buffer[x + y * DISPLAY_WIDTH]);
+            k++;
+            if (k == 16)  {
+              Wire.endTransmission();
+              k = 0;
+            }
+          }
+          if (k != 0)  {
+            Wire.endTransmission();
+            k = 0;
+          }
+          yield();
+        }
+
+        if (k != 0) {
+          Wire.endTransmission();
+        }
+      #else
+        uint8_t * p = &buffer[0];
+        for (uint8_t y=0; y<8; y++) {
+          sendCommand(0xB0+y);
+          sendCommand(0x02);
+          sendCommand(0x10);
+          for( uint8_t x=0; x<8; x++) {
+            Wire.beginTransmission(_address);
+            Wire.write(0x40);
+            for (uint8_t k = 0; k < 16; k++) {
+              Wire.write(*p++);
+            }
+            Wire.endTransmission();
+          }
+        }
+      #endif
+    }
+
+  private:
+    inline void sendCommand(uint8_t command) __attribute__((always_inline)){
+      Wire.beginTransmission(_address);
+      Wire.write(0x80);
+      Wire.write(command);
+      Wire.endTransmission();
+    }
+
+
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3b790949319e3d4e54f9edd06040c6b8cab8f62
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306.h
@@ -0,0 +1,36 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#ifndef SSD1306_h
+#define SSD1306_h
+#include "SSD1306Wire.h"
+
+// For legacy support make SSD1306 an alias for SSD1306
+typedef SSD1306Wire SSD1306;
+
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306Brzo.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306Brzo.h
new file mode 100644
index 0000000000000000000000000000000000000000..3b99d82d2a196dbc0836b8e4ad13d7fa46b60813
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306Brzo.h
@@ -0,0 +1,149 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#ifndef SSD1306Brzo_h
+#define SSD1306Brzo_h
+
+#include "OLEDDisplay.h"
+#include <brzo_i2c.h>
+
+#if F_CPU == 160000000L
+  #define BRZO_I2C_SPEED 1000
+#else
+  #define BRZO_I2C_SPEED 800
+#endif
+
+class SSD1306Brzo : public OLEDDisplay {
+  private:
+      uint8_t             _address;
+      uint8_t             _sda;
+      uint8_t             _scl;
+
+  public:
+    SSD1306Brzo(uint8_t _address, uint8_t _sda, uint8_t _scl) {
+      this->_address = _address;
+      this->_sda = _sda;
+      this->_scl = _scl;
+    }
+
+    bool connect(){
+      brzo_i2c_setup(_sda, _scl, 0);
+      return true;
+    }
+
+    void display(void) {
+    #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+       uint8_t minBoundY = ~0;
+       uint8_t maxBoundY = 0;
+
+       uint8_t minBoundX = ~0;
+       uint8_t maxBoundX = 0;
+
+       uint8_t x, y;
+
+       // Calculate the Y bounding box of changes
+       // and copy buffer[pos] to buffer_back[pos];
+       for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) {
+         for (x = 0; x < DISPLAY_WIDTH; x++) {
+          uint16_t pos = x + y * DISPLAY_WIDTH;
+          if (buffer[pos] != buffer_back[pos]) {
+            minBoundY = _min(minBoundY, y);
+            maxBoundY = _max(maxBoundY, y);
+            minBoundX = _min(minBoundX, x);
+            maxBoundX = _max(maxBoundX, x);
+          }
+          buffer_back[pos] = buffer[pos];
+        }
+        yield();
+       }
+
+       // If the minBoundY wasn't updated
+       // we can savely assume that buffer_back[pos] == buffer[pos]
+       // holdes true for all values of pos
+       if (minBoundY == ~0) return;
+
+       sendCommand(COLUMNADDR);
+       sendCommand(minBoundX);
+       sendCommand(maxBoundX);
+
+       sendCommand(PAGEADDR);
+       sendCommand(minBoundY);
+       sendCommand(maxBoundY);
+
+       byte k = 0;
+       uint8_t sendBuffer[17];
+       sendBuffer[0] = 0x40;
+       brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED);
+       for (y = minBoundY; y <= maxBoundY; y++) {
+           for (x = minBoundX; x <= maxBoundX; x++) {
+               k++;
+               sendBuffer[k] = buffer[x + y * DISPLAY_WIDTH];
+               if (k == 16)  {
+                 brzo_i2c_write(sendBuffer, 17, true);
+                 k = 0;
+               }
+           }
+           yield();
+       }
+       brzo_i2c_write(sendBuffer, k + 1, true);
+       brzo_i2c_end_transaction();
+     #else
+       // No double buffering
+       sendCommand(COLUMNADDR);
+       sendCommand(0x0);
+       sendCommand(0x7F);
+
+       sendCommand(PAGEADDR);
+       sendCommand(0x0);
+       sendCommand(0x7);
+
+       uint8_t sendBuffer[17];
+       sendBuffer[0] = 0x40;
+       brzo_i2c_start_transaction(this->_address, BRZO_I2C_SPEED);
+       for (uint16_t i=0; i<DISPLAY_BUFFER_SIZE; i++) {
+         for (uint8_t x=1; x<17; x++) {
+           sendBuffer[x] = buffer[i];
+           i++;
+         }
+         i--;
+         brzo_i2c_write(sendBuffer,  17,  true);
+         yield();
+       }
+       brzo_i2c_end_transaction();
+     #endif
+    }
+
+  private:
+    inline void sendCommand(uint8_t com) __attribute__((always_inline)){
+      uint8_t command[2] = {0x80 /* command mode */, com};
+      brzo_i2c_start_transaction(_address, BRZO_I2C_SPEED);
+      brzo_i2c_write(command, 2, true);
+      brzo_i2c_end_transaction();
+    }
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306Spi.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306Spi.h
new file mode 100644
index 0000000000000000000000000000000000000000..21794587ac612cb951706546dadb2d2bb0c23593
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306Spi.h
@@ -0,0 +1,150 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#ifndef SSD1306Spi_h
+#define SSD1306Spi_h
+
+#include "OLEDDisplay.h"
+#include <SPI.h>
+
+#if F_CPU == 160000000L
+  #define BRZO_I2C_SPEED 1000
+#else
+  #define BRZO_I2C_SPEED 800
+#endif
+
+class SSD1306Spi : public OLEDDisplay {
+  private:
+      uint8_t             _rst;
+      uint8_t             _dc;
+      uint8_t             _cs;
+
+  public:
+    SSD1306Spi(uint8_t _rst, uint8_t _dc, uint8_t _cs) {
+      this->_rst = _rst;
+      this->_dc  = _dc;
+      this->_cs  = _cs;
+    }
+
+    bool connect(){
+      pinMode(_dc, OUTPUT);
+      pinMode(_cs, OUTPUT);
+      pinMode(_rst, OUTPUT);
+
+      SPI.begin ();
+      SPI.setClockDivider (SPI_CLOCK_DIV2);
+
+      // Pulse Reset low for 10ms
+      digitalWrite(_rst, HIGH);
+      delay(1);
+      digitalWrite(_rst, LOW);
+      delay(10);
+      digitalWrite(_rst, HIGH);
+      return true;
+    }
+
+    void display(void) {
+    #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+       uint8_t minBoundY = ~0;
+       uint8_t maxBoundY = 0;
+
+       uint8_t minBoundX = ~0;
+       uint8_t maxBoundX = 0;
+
+       uint8_t x, y;
+
+       // Calculate the Y bounding box of changes
+       // and copy buffer[pos] to buffer_back[pos];
+       for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) {
+         for (x = 0; x < DISPLAY_WIDTH; x++) {
+          uint16_t pos = x + y * DISPLAY_WIDTH;
+          if (buffer[pos] != buffer_back[pos]) {
+            minBoundY = _min(minBoundY, y);
+            maxBoundY = _max(maxBoundY, y);
+            minBoundX = _min(minBoundX, x);
+            maxBoundX = _max(maxBoundX, x);
+          }
+          buffer_back[pos] = buffer[pos];
+        }
+        yield();
+       }
+
+       // If the minBoundY wasn't updated
+       // we can savely assume that buffer_back[pos] == buffer[pos]
+       // holdes true for all values of pos
+       if (minBoundY == ~0) return;
+
+       sendCommand(COLUMNADDR);
+       sendCommand(minBoundX);
+       sendCommand(maxBoundX);
+
+       sendCommand(PAGEADDR);
+       sendCommand(minBoundY);
+       sendCommand(maxBoundY);
+
+       digitalWrite(_cs, HIGH);
+       digitalWrite(_dc, HIGH);   // data mode
+       digitalWrite(_cs, LOW);
+       for (y = minBoundY; y <= maxBoundY; y++) {
+         for (x = minBoundX; x <= maxBoundX; x++) {
+           SPI.transfer(buffer[x + y * DISPLAY_WIDTH]);
+         }
+         yield();
+       }
+       digitalWrite(_cs, HIGH);
+     #else
+       // No double buffering
+       sendCommand(COLUMNADDR);
+       sendCommand(0x0);
+       sendCommand(0x7F);
+
+       sendCommand(PAGEADDR);
+       sendCommand(0x0);
+       sendCommand(0x7);
+
+        digitalWrite(_cs, HIGH);
+        digitalWrite(_dc, HIGH);   // data mode
+        digitalWrite(_cs, LOW);
+        for (uint16_t i=0; i<DISPLAY_BUFFER_SIZE; i++) {
+          SPI.transfer(buffer[i]);
+          yield();
+        }
+        digitalWrite(_cs, HIGH);
+     #endif
+    }
+
+  private:
+    inline void sendCommand(uint8_t com) __attribute__((always_inline)){
+      digitalWrite(_cs, HIGH);
+      digitalWrite(_dc, LOW);
+      digitalWrite(_cs, LOW);
+      SPI.transfer(com);
+      digitalWrite(_cs, HIGH);
+    }
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306Wire.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306Wire.h
new file mode 100644
index 0000000000000000000000000000000000000000..ac12becd0964a7d93b20e3e8a42a5db9ab9c253e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/SSD1306Wire.h
@@ -0,0 +1,147 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * Credits for parts of this code go to Mike Rankin. Thank you so much for sharing!
+ */
+
+#ifndef SSD1306Wire_h
+#define SSD1306Wire_h
+
+#include "OLEDDisplay.h"
+#include <Wire.h>
+
+class SSD1306Wire : public OLEDDisplay {
+  private:
+      uint8_t             _address;
+      uint8_t             _sda;
+      uint8_t             _scl;
+
+  public:
+    SSD1306Wire(uint8_t _address, uint8_t _sda, uint8_t _scl) {
+      this->_address = _address;
+      this->_sda = _sda;
+      this->_scl = _scl;
+    }
+
+    bool connect() {
+      Wire.begin(this->_sda, this->_scl);
+      // Let's use ~700khz if ESP8266 is in 160Mhz mode
+      // this will be limited to ~400khz if the ESP8266 in 80Mhz mode.
+      Wire.setClock(700000);
+      return true;
+    }
+
+    void display(void) {
+      #ifdef OLEDDISPLAY_DOUBLE_BUFFER
+        uint8_t minBoundY = ~0;
+        uint8_t maxBoundY = 0;
+
+        uint8_t minBoundX = ~0;
+        uint8_t maxBoundX = 0;
+        uint8_t x, y;
+
+        // Calculate the Y bounding box of changes
+        // and copy buffer[pos] to buffer_back[pos];
+        for (y = 0; y < (DISPLAY_HEIGHT / 8); y++) {
+          for (x = 0; x < DISPLAY_WIDTH; x++) {
+           uint16_t pos = x + y * DISPLAY_WIDTH;
+           if (buffer[pos] != buffer_back[pos]) {
+             minBoundY = _min(minBoundY, y);
+             maxBoundY = _max(maxBoundY, y);
+             minBoundX = _min(minBoundX, x);
+             maxBoundX = _max(maxBoundX, x);
+           }
+           buffer_back[pos] = buffer[pos];
+         }
+         yield();
+        }
+
+        // If the minBoundY wasn't updated
+        // we can savely assume that buffer_back[pos] == buffer[pos]
+        // holdes true for all values of pos
+        if (minBoundY == ~0) return;
+
+        sendCommand(COLUMNADDR);
+        sendCommand(minBoundX);
+        sendCommand(maxBoundX);
+
+        sendCommand(PAGEADDR);
+        sendCommand(minBoundY);
+        sendCommand(maxBoundY);
+
+        byte k = 0;
+        for (y = minBoundY; y <= maxBoundY; y++) {
+          for (x = minBoundX; x <= maxBoundX; x++) {
+            if (k == 0) {
+              Wire.beginTransmission(_address);
+              Wire.write(0x40);
+            }
+            Wire.write(buffer[x + y * DISPLAY_WIDTH]);
+            k++;
+            if (k == 16)  {
+              Wire.endTransmission();
+              k = 0;
+            }
+          }
+          yield();
+        }
+
+        if (k != 0) {
+          Wire.endTransmission();
+        }
+      #else
+
+        sendCommand(COLUMNADDR);
+        sendCommand(0x0);
+        sendCommand(0x7F);
+
+        sendCommand(PAGEADDR);
+        sendCommand(0x0);
+        sendCommand(0x7);
+
+        for (uint16_t i=0; i < DISPLAY_BUFFER_SIZE; i++) {
+          Wire.beginTransmission(this->_address);
+          Wire.write(0x40);
+          for (uint8_t x = 0; x < 16; x++) {
+            Wire.write(buffer[i]);
+            i++;
+          }
+          i--;
+          Wire.endTransmission();
+        }
+      #endif
+    }
+
+  private:
+    inline void sendCommand(uint8_t command) __attribute__((always_inline)){
+      Wire.beginTransmission(_address);
+      Wire.write(0x80);
+      Wire.write(command);
+      Wire.endTransmission();
+    }
+
+
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/UPGRADE-3.0.md b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/UPGRADE-3.0.md
new file mode 100644
index 0000000000000000000000000000000000000000..e7a315bc63fd7ceb597fba61a617ad9dac334fb4
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/UPGRADE-3.0.md
@@ -0,0 +1,125 @@
+# Upgrade from 2.0 to 3.0
+
+While developing version 3.0 we made some breaking changes to the public
+API of this library. This document will help you update your code to work with
+version 3.0
+
+## Font Definitions
+
+To get better performance and a smaller font definition format, we change the memory
+layout of the font definition format. If you are using custom fonts not included in
+this library we updated the font generator [here](http://oleddisplay.squix.ch/#/home).
+Please update your fonts to be working with 3.0 by selecting the respective version in the dropdown.
+
+
+## Architectural Changes
+
+To become a more versatile library for the SSD1306 chipset we abstracted the
+hardware connection into subclasses of the base display class now called `OLEDDisplay`.
+This library is currently shipping with three implementations:
+
+  * `SSD1306Wire` implementing the I2C protocol using the Wire Library.    
+  * `SSD1306Brzo` implementing the I2C protocol using the faster [`brzo_i2c`](https://github.com/pasko-zh/brzo_i2c) library.
+  * `SSD1306Spi` implementing the SPI protocol.
+
+To keep backwards compatiblity with the old API `SSD1306` is an alias of `SSD1306Wire`.
+If you are not using the UI components you don't have to change anything to keep your code working.
+
+## Name Changes
+
+[Naming things is hard](http://martinfowler.com/bliki/TwoHardThings.html), to better reflect our intention with this library
+we changed the name of the base class to `OLEDDisplay` and the UI library accordingly to `OLEDDisplayUi`.
+As a consequence the type definitions of all frame and overlay related functions changed.
+This means that you have to update all your frame drawing callbacks from:
+
+```c
+bool frame1(SSD1306 *display,  SSD1306UiState* state, int x, int y);
+```
+
+too
+
+```c
+void frame1(OLEDDisplay *display,  OLEDDisplayUiState* state, int16_t x, int16_t y);
+```
+
+And your overlay drawing functions from:
+
+```c
+bool overlay1(SSD1306 *display,  SSD1306UiState* state);
+```
+
+too
+
+```c
+void overlay1(OLEDDisplay *display,  OLEDDisplayUiState* state);
+```
+
+## New Features
+
+### Loading Animation
+
+While using this library ourself we noticed a pattern emerging. We want to drawing
+a loading progress while connecting to WiFi and updating weather data etc.
+
+The simplest thing was to add the function `drawProgressBar(x, y, width,  height, progress)`
+,where `progress` is between `0` and `100`, right to the `OLEDDisplay` class.
+
+But we didn't stop there. We added a new feature to the `OLEDDisplayUi` called `LoadingStages`.
+You can define your loading process like this:
+
+```c++
+LoadingStage loadingStages[] = {
+  {
+    .process = "Connect to WiFi",
+    .callback = []() {
+      // Connect to WiFi
+    }
+  },
+  {
+    .process = "Get time from NTP",
+    .callback = []() {
+      // Get current time via NTP
+    }
+  }
+  // more steps
+};
+
+int LOADING_STAGES_COUNT = sizeof(loadingStages) / sizeof(LoadingStage);
+```
+
+After defining your array of `LoadingStages` you can then run the loading process by using
+`ui.runLoadingProcess(loadingStages, LOADING_STAGES_COUNT)`. This will give you a
+nice little loading animation you can see in the beginning of [this](https://vimeo.com/168362918)
+video.
+
+To further customize this you are free to define your own `LoadingDrawFunction` like this:
+
+```c
+void myLoadingDraw(OLEDDisplay *display, LoadingStage* stage, uint8_t progress) {
+  display->setTextAlignment(TEXT_ALIGN_CENTER);
+  display->setFont(ArialMT_Plain_10);
+  // stage->process contains the text of the current progress e.q. "Connect to WiFi"
+  display->drawString(64, 18, stage->process);
+  // you could just print the current process without the progress bar
+  display->drawString(64, 28, progress);
+}
+```
+
+After defining a function like that, you can pass it to the Ui library by use
+`ui.setLoadingDrawFunction(myLoadingDraw)`.
+
+
+### Text Logging
+
+It is always useful to display some text on the display without worrying to much
+where it goes and managing it. In 3.0 we made the `OLEDDisplay` class implement
+[`Print`](https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/cores/arduino/Print.h)
+so you can use it like you would use `Serial`. We calls this feature `LogBuffer`
+and the only thing you have to do is to define how many lines you want to display
+and how many characters there are on average on each. This is done by calling
+`setLogBuffer(lines, chars);`. If there is not enough memory the function will
+return false.
+
+After that you can draw the `LogBuffer` anywhere you want by calling `drawLogBuffer(x, y)`.
+(Note: You have to call `display()` to update the screen)
+We made a [video](https://www.youtube.com/watch?v=8Fiss77A3TE) showing this feature in action.
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino
new file mode 100644
index 0000000000000000000000000000000000000000..e9db7d645a610f8c42d857ba20a774b2fe9cd89e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306ClockDemo/SSD1306ClockDemo.ino
@@ -0,0 +1,211 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+#include <TimeLib.h>
+
+// Include the correct display library
+// For a connection via I2C using Wire include
+#include <Wire.h>  // Only needed for Arduino 1.6.5 and earlier
+#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
+// or #include "SH1106.h" alis for `#include "SH1106Wire.h"`
+// For a connection via I2C using brzo_i2c (must be installed) include
+// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
+// #include "SSD1306Brzo.h"
+// #include "SH1106Brzo.h"
+// For a connection via SPI include
+// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
+// #include "SSD1306Spi.h"
+// #include "SH1106SPi.h"
+
+// Include the UI lib
+#include "OLEDDisplayUi.h"
+
+// Include custom images
+#include "images.h"
+
+// Use the corresponding display class:
+
+// Initialize the OLED display using SPI
+// D5 -> CLK
+// D7 -> MOSI (DOUT)
+// D0 -> RES
+// D2 -> DC
+// D8 -> CS
+// SSD1306Spi        display(D0, D2, D8);
+// or
+// SH1106Spi         display(D0, D2);
+
+// Initialize the OLED display using brzo_i2c
+// D3 -> SDA
+// D5 -> SCL
+// SSD1306Brzo display(0x3c, D3, D5);
+// or
+// SH1106Brzo  display(0x3c, D3, D5);
+
+// Initialize the OLED display using Wire library
+SSD1306  display(0x3c, D3, D5);
+// SH1106 display(0x3c, D3, D5);
+
+OLEDDisplayUi ui ( &display );
+
+int screenW = 128;
+int screenH = 64;
+int clockCenterX = screenW/2;
+int clockCenterY = ((screenH-16)/2)+16;   // top yellow part is 16 px height
+int clockRadius = 23;
+
+// utility function for digital clock display: prints leading 0
+String twoDigits(int digits){
+  if(digits < 10) {
+    String i = '0'+String(digits);
+    return i;
+  }
+  else {
+    return String(digits);
+  }
+}
+
+void clockOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
+
+}
+
+void analogClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
+//  ui.disableIndicator();
+
+  // Draw the clock face
+//  display->drawCircle(clockCenterX + x, clockCenterY + y, clockRadius);
+  display->drawCircle(clockCenterX + x, clockCenterY + y, 2);
+  //
+  //hour ticks
+  for( int z=0; z < 360;z= z + 30 ){
+  //Begin at 0° and stop at 360°
+    float angle = z ;
+    angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
+    int x2 = ( clockCenterX + ( sin(angle) * clockRadius ) );
+    int y2 = ( clockCenterY - ( cos(angle) * clockRadius ) );
+    int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
+    int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 8 ) ) ) );
+    display->drawLine( x2 + x , y2 + y , x3 + x , y3 + y);
+  }
+
+  // display second hand
+  float angle = second() * 6 ;
+  angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
+  int x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
+  int y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 5 ) ) ) );
+  display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
+  //
+  // display minute hand
+  angle = minute() * 6 ;
+  angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
+  x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
+  y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 4 ) ) ) );
+  display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
+  //
+  // display hour hand
+  angle = hour() * 30 + int( ( minute() / 12 ) * 6 )   ;
+  angle = ( angle / 57.29577951 ) ; //Convert degrees to radians
+  x3 = ( clockCenterX + ( sin(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
+  y3 = ( clockCenterY - ( cos(angle) * ( clockRadius - ( clockRadius / 2 ) ) ) );
+  display->drawLine( clockCenterX + x , clockCenterY + y , x3 + x , y3 + y);
+}
+
+void digitalClockFrame(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
+  String timenow = String(hour())+":"+twoDigits(minute())+":"+twoDigits(second());
+  display->setTextAlignment(TEXT_ALIGN_CENTER);
+  display->setFont(ArialMT_Plain_24);
+  display->drawString(clockCenterX + x , clockCenterY + y, timenow );
+}
+
+// This array keeps function pointers to all frames
+// frames are the single views that slide in
+FrameCallback frames[] = { analogClockFrame, digitalClockFrame };
+
+// how many frames are there?
+int frameCount = 2;
+
+// Overlays are statically drawn on top of a frame eg. a clock
+OverlayCallback overlays[] = { clockOverlay };
+int overlaysCount = 1;
+
+void setup() {
+  Serial.begin(9600);
+  Serial.println();
+
+	// The ESP is capable of rendering 60fps in 80Mhz mode
+	// but that won't give you much time for anything else
+	// run it in 160Mhz mode or just set it to 30 fps
+  ui.setTargetFPS(60);
+
+	// Customize the active and inactive symbol
+  ui.setActiveSymbol(activeSymbol);
+  ui.setInactiveSymbol(inactiveSymbol);
+
+  // You can change this to
+  // TOP, LEFT, BOTTOM, RIGHT
+  ui.setIndicatorPosition(TOP);
+
+  // Defines where the first frame is located in the bar.
+  ui.setIndicatorDirection(LEFT_RIGHT);
+
+  // You can change the transition that is used
+  // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
+  ui.setFrameAnimation(SLIDE_LEFT);
+
+  // Add frames
+  ui.setFrames(frames, frameCount);
+
+  // Add overlays
+  ui.setOverlays(overlays, overlaysCount);
+
+  // Initialising the UI will init the display too.
+  ui.init();
+
+  display.flipScreenVertically();
+
+  unsigned long secsSinceStart = millis();
+  // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
+  const unsigned long seventyYears = 2208988800UL;
+  // subtract seventy years:
+  unsigned long epoch = secsSinceStart - seventyYears * SECS_PER_HOUR;
+  setTime(epoch);
+
+}
+
+
+void loop() {
+  int remainingTimeBudget = ui.update();
+
+  if (remainingTimeBudget > 0) {
+    // You can do some work here
+    // Don't do stuff if you are below your
+    // time budget.
+    delay(remainingTimeBudget);
+
+  }
+
+
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306ClockDemo/images.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306ClockDemo/images.h
new file mode 100644
index 0000000000000000000000000000000000000000..a220a27c2b6637946f5df26f500f0eb88998a2c8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306ClockDemo/images.h
@@ -0,0 +1,21 @@
+const char activeSymbol[] PROGMEM = {
+    B00000000,
+    B00000000,
+    B00011000,
+    B00100100,
+    B01000010,
+    B01000010,
+    B00100100,
+    B00011000
+};
+
+const char inactiveSymbol[] PROGMEM = {
+    B00000000,
+    B00000000,
+    B00000000,
+    B00000000,
+    B00011000,
+    B00011000,
+    B00000000,
+    B00000000
+};
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino
new file mode 100644
index 0000000000000000000000000000000000000000..cf37fb0412002492388ed3d71fbb1ec58866c233
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306DrawingDemo/SSD1306DrawingDemo.ino
@@ -0,0 +1,229 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+ // Include the correct display library
+ // For a connection via I2C using Wire include
+ #include <Wire.h>  // Only needed for Arduino 1.6.5 and earlier
+ #include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
+ // or #include "SH1106.h" alis for `#include "SH1106Wire.h"`
+ // For a connection via I2C using brzo_i2c (must be installed) include
+ // #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
+ // #include "SSD1306Brzo.h"
+ // #include "SH1106Brzo.h"
+ // For a connection via SPI include
+ // #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
+ // #include "SSD1306Spi.h"
+ // #include "SH1106SPi.h"
+
+ // Use the corresponding display class:
+
+ // Initialize the OLED display using SPI
+ // D5 -> CLK
+ // D7 -> MOSI (DOUT)
+ // D0 -> RES
+ // D2 -> DC
+ // D8 -> CS
+ // SSD1306Spi        display(D0, D2, D8);
+ // or
+ // SH1106Spi         display(D0, D2);
+
+ // Initialize the OLED display using brzo_i2c
+ // D3 -> SDA
+ // D5 -> SCL
+ // SSD1306Brzo display(0x3c, D3, D5);
+ // or
+ // SH1106Brzo  display(0x3c, D3, D5);
+
+ // Initialize the OLED display using Wire library
+ SSD1306  display(0x3c, D3, D5);
+ // SH1106 display(0x3c, D3, D5);
+
+// Adapted from Adafruit_SSD1306
+void drawLines() {
+  for (int16_t i=0; i<DISPLAY_WIDTH; i+=4) {
+    display.drawLine(0, 0, i, DISPLAY_HEIGHT-1);
+    display.display();
+    delay(10);
+  }
+  for (int16_t i=0; i<DISPLAY_HEIGHT; i+=4) {
+    display.drawLine(0, 0, DISPLAY_WIDTH-1, i);
+    display.display();
+    delay(10);
+  }
+  delay(250);
+
+  display.clear();
+  for (int16_t i=0; i<DISPLAY_WIDTH; i+=4) {
+    display.drawLine(0, DISPLAY_HEIGHT-1, i, 0);
+    display.display();
+    delay(10);
+  }
+  for (int16_t i=DISPLAY_HEIGHT-1; i>=0; i-=4) {
+    display.drawLine(0, DISPLAY_HEIGHT-1, DISPLAY_WIDTH-1, i);
+    display.display();
+    delay(10);
+  }
+  delay(250);
+
+  display.clear();
+  for (int16_t i=DISPLAY_WIDTH-1; i>=0; i-=4) {
+    display.drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, i, 0);
+    display.display();
+    delay(10);
+  }
+  for (int16_t i=DISPLAY_HEIGHT-1; i>=0; i-=4) {
+    display.drawLine(DISPLAY_WIDTH-1, DISPLAY_HEIGHT-1, 0, i);
+    display.display();
+    delay(10);
+  }
+  delay(250);
+  display.clear();
+  for (int16_t i=0; i<DISPLAY_HEIGHT; i+=4) {
+    display.drawLine(DISPLAY_WIDTH-1, 0, 0, i);
+    display.display();
+    delay(10);
+  }
+  for (int16_t i=0; i<DISPLAY_WIDTH; i+=4) {
+    display.drawLine(DISPLAY_WIDTH-1, 0, i, DISPLAY_HEIGHT-1);
+    display.display();
+    delay(10);
+  }
+  delay(250);
+}
+
+// Adapted from Adafruit_SSD1306
+void drawRect(void) {
+  for (int16_t i=0; i<DISPLAY_HEIGHT/2; i+=2) {
+    display.drawRect(i, i, DISPLAY_WIDTH-2*i, DISPLAY_HEIGHT-2*i);
+    display.display();
+    delay(10);
+  }
+}
+
+// Adapted from Adafruit_SSD1306
+void fillRect(void) {
+  uint8_t color = 1;
+  for (int16_t i=0; i<DISPLAY_HEIGHT/2; i+=3) {
+    display.setColor((color % 2 == 0) ? BLACK : WHITE); // alternate colors
+    display.fillRect(i, i, DISPLAY_WIDTH - i*2, DISPLAY_HEIGHT - i*2);
+    display.display();
+    delay(10);
+    color++;
+  }
+  // Reset back to WHITE
+  display.setColor(WHITE);
+}
+
+// Adapted from Adafruit_SSD1306
+void drawCircle(void) {
+  for (int16_t i=0; i<DISPLAY_HEIGHT; i+=2) {
+    display.drawCircle(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, i);
+    display.display();
+    delay(10);
+  }
+  delay(1000);
+  display.clear();
+
+  // This will draw the part of the circel in quadrant 1
+  // Quadrants are numberd like this:
+  //   0010 | 0001
+  //  ------|-----
+  //   0100 | 1000
+  //
+  display.drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000001);
+  display.display();
+  delay(200);
+  display.drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000011);
+  display.display();
+  delay(200);
+  display.drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00000111);
+  display.display();
+  delay(200);
+  display.drawCircleQuads(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, DISPLAY_HEIGHT/4, 0b00001111);
+  display.display();
+}
+
+void printBuffer(void) {
+  // Initialize the log buffer
+  // allocate memory to store 8 lines of text and 30 chars per line.
+  display.setLogBuffer(5, 30);
+
+  // Some test data
+  const char* test[] = {
+      "Hello",
+      "World" ,
+      "----",
+      "Show off",
+      "how",
+      "the log buffer",
+      "is",
+      "working.",
+      "Even",
+      "scrolling is",
+      "working"
+  };
+
+  for (uint8_t i = 0; i < 11; i++) {
+    display.clear();
+    // Print to the screen
+    display.println(test[i]);
+    // Draw it to the internal screen buffer
+    display.drawLogBuffer(0, 0);
+    // Display it on the screen
+    display.display();
+    delay(500);
+  }
+}
+
+void setup() {
+  display.init();
+
+  // display.flipScreenVertically();
+
+  display.setContrast(255);
+
+  drawLines();
+  delay(1000);
+  display.clear();
+
+  drawRect();
+  delay(1000);
+  display.clear();
+
+  fillRect();
+  delay(1000);
+  display.clear();
+
+  drawCircle();
+  delay(1000);
+  display.clear();
+
+  printBuffer();
+  delay(1000);
+  display.clear();
+}
+
+void loop() { }
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306OTADemo/SSD1306OTADemo.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306OTADemo/SSD1306OTADemo.ino
new file mode 100644
index 0000000000000000000000000000000000000000..6460ff9e4a308dffa396d0db473c6708e0c14f34
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306OTADemo/SSD1306OTADemo.ino
@@ -0,0 +1,119 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+// WiFi includes
+ #include <ESP8266WiFi.h>
+
+ // OTA Includes
+ #include <ESP8266mDNS.h>
+ #include <ArduinoOTA.h>
+
+ const char *ssid         = "[Your SSID]";
+ const char *password     = "[Your Password]";
+
+
+// Include the correct display library
+// For a connection via I2C using Wire include
+#include <Wire.h>  // Only needed for Arduino 1.6.5 and earlier
+#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
+// or #include "SH1106.h" alis for `#include "SH1106Wire.h"`
+// For a connection via I2C using brzo_i2c (must be installed) include
+// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
+// #include "SSD1306Brzo.h"
+// #include "SH1106Brzo.h"
+// For a connection via SPI include
+// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
+// #include "SSD1306Spi.h"
+// #include "SH1106SPi.h"
+
+// Use the corresponding display class:
+
+// Initialize the OLED display using SPI
+// D5 -> CLK
+// D7 -> MOSI (DOUT)
+// D0 -> RES
+// D2 -> DC
+// D8 -> CS
+// SSD1306Spi        display(D0, D2, D8);
+// or
+// SH1106Spi         display(D0, D2);
+
+// Initialize the OLED display using brzo_i2c
+// D3 -> SDA
+// D5 -> SCL
+// SSD1306Brzo display(0x3c, D3, D5);
+// or
+// SH1106Brzo  display(0x3c, D3, D5);
+
+// Initialize the OLED display using Wire library
+SSD1306  display(0x3c, D3, D5);
+// SH1106 display(0x3c, D3, D5);
+
+
+void setup() {
+  WiFi.begin ( ssid, password );
+
+  // Wait for connection
+  while ( WiFi.status() != WL_CONNECTED ) {
+    delay ( 10 );
+  }
+
+  display.init();
+  display.flipScreenVertically();
+  display.setContrast(255);
+
+  ArduinoOTA.begin();
+  ArduinoOTA.onStart([]() {
+    display.clear();
+    display.setFont(ArialMT_Plain_10);
+    display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
+    display.drawString(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2 - 10, "OTA Update");
+    display.display();
+  });
+
+  ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
+    display.drawProgressBar(4, 32, 120, 8, progress / (total / 100) );
+    display.display();
+  });
+
+  ArduinoOTA.onEnd([]() {
+    display.clear();
+    display.setFont(ArialMT_Plain_10);
+    display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
+    display.drawString(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, "Restart");
+    display.display();
+  });
+
+  // Align text vertical/horizontal center
+  display.setTextAlignment(TEXT_ALIGN_CENTER_BOTH);
+  display.setFont(ArialMT_Plain_10);
+  display.drawString(DISPLAY_WIDTH/2, DISPLAY_HEIGHT/2, "Ready for OTA:\n" + WiFi.localIP().toString());
+  display.display();
+}
+
+void loop() {
+  ArduinoOTA.handle();
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino
new file mode 100644
index 0000000000000000000000000000000000000000..ed7401915861f89d9a1f4dd6f697bc82b44f3069
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306SimpleDemo/SSD1306SimpleDemo.ino
@@ -0,0 +1,187 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+// Include the correct display library
+// For a connection via I2C using Wire include
+#include <Wire.h>  // Only needed for Arduino 1.6.5 and earlier
+#include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
+// or #include "SH1106.h" alis for `#include "SH1106Wire.h"`
+// For a connection via I2C using brzo_i2c (must be installed) include
+// #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
+// #include "SSD1306Brzo.h"
+// #include "SH1106Brzo.h"
+// For a connection via SPI include
+// #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
+// #include "SSD1306Spi.h"
+// #include "SH1106SPi.h"
+
+// Include custom images
+#include "images.h"
+
+// Initialize the OLED display using SPI
+// D5 -> CLK
+// D7 -> MOSI (DOUT)
+// D0 -> RES
+// D2 -> DC
+// D8 -> CS
+// SSD1306Spi        display(D0, D2, D8);
+// or
+// SH1106Spi         display(D0, D2);
+
+// Initialize the OLED display using brzo_i2c
+// D3 -> SDA
+// D5 -> SCL
+// SSD1306Brzo display(0x3c, D3, D5);
+// or
+// SH1106Brzo  display(0x3c, D3, D5);
+
+// Initialize the OLED display using Wire library
+SSD1306  display(0x3c, D3, D5);
+// SH1106 display(0x3c, D3, D5);
+
+
+#define DEMO_DURATION 3000
+typedef void (*Demo)(void);
+
+int demoMode = 0;
+int counter = 1;
+
+void setup() {
+  Serial.begin(115200);
+  Serial.println();
+  Serial.println();
+
+
+  // Initialising the UI will init the display too.
+  display.init();
+
+  display.flipScreenVertically();
+  display.setFont(ArialMT_Plain_10);
+
+}
+
+void drawFontFaceDemo() {
+    // Font Demo1
+    // create more fonts at http://oleddisplay.squix.ch/
+    display.setTextAlignment(TEXT_ALIGN_LEFT);
+    display.setFont(ArialMT_Plain_10);
+    display.drawString(0, 0, "Hello world");
+    display.setFont(ArialMT_Plain_16);
+    display.drawString(0, 10, "Hello world");
+    display.setFont(ArialMT_Plain_24);
+    display.drawString(0, 26, "Hello world");
+}
+
+void drawTextFlowDemo() {
+    display.setFont(ArialMT_Plain_10);
+    display.setTextAlignment(TEXT_ALIGN_LEFT);
+    display.drawStringMaxWidth(0, 0, 128,
+      "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore." );
+}
+
+void drawTextAlignmentDemo() {
+    // Text alignment demo
+  display.setFont(ArialMT_Plain_10);
+
+  // The coordinates define the left starting point of the text
+  display.setTextAlignment(TEXT_ALIGN_LEFT);
+  display.drawString(0, 10, "Left aligned (0,10)");
+
+  // The coordinates define the center of the text
+  display.setTextAlignment(TEXT_ALIGN_CENTER);
+  display.drawString(64, 22, "Center aligned (64,22)");
+
+  // The coordinates define the right end of the text
+  display.setTextAlignment(TEXT_ALIGN_RIGHT);
+  display.drawString(128, 33, "Right aligned (128,33)");
+}
+
+void drawRectDemo() {
+      // Draw a pixel at given position
+    for (int i = 0; i < 10; i++) {
+      display.setPixel(i, i);
+      display.setPixel(10 - i, i);
+    }
+    display.drawRect(12, 12, 20, 20);
+
+    // Fill the rectangle
+    display.fillRect(14, 14, 17, 17);
+
+    // Draw a line horizontally
+    display.drawHorizontalLine(0, 40, 20);
+
+    // Draw a line horizontally
+    display.drawVerticalLine(40, 0, 20);
+}
+
+void drawCircleDemo() {
+  for (int i=1; i < 8; i++) {
+    display.setColor(WHITE);
+    display.drawCircle(32, 32, i*3);
+    if (i % 2 == 0) {
+      display.setColor(BLACK);
+    }
+    display.fillCircle(96, 32, 32 - i* 3);
+  }
+}
+
+void drawProgressBarDemo() {
+  int progress = (counter / 5) % 100;
+  // draw the progress bar
+  display.drawProgressBar(0, 32, 120, 10, progress);
+
+  // draw the percentage as String
+  display.setTextAlignment(TEXT_ALIGN_CENTER);
+  display.drawString(64, 15, String(progress) + "%");
+}
+
+void drawImageDemo() {
+    // see http://blog.squix.org/2015/05/esp8266-nodemcu-how-to-create-xbm.html
+    // on how to create xbm files
+    display.drawXbm(34, 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
+}
+
+Demo demos[] = {drawFontFaceDemo, drawTextFlowDemo, drawTextAlignmentDemo, drawRectDemo, drawCircleDemo, drawProgressBarDemo, drawImageDemo};
+int demoLength = (sizeof(demos) / sizeof(Demo));
+long timeSinceLastModeSwitch = 0;
+
+void loop() {
+  // clear the display
+  display.clear();
+  // draw the current demo method
+  demos[demoMode]();
+
+  display.setTextAlignment(TEXT_ALIGN_RIGHT);
+  display.drawString(10, 128, String(millis()));
+  // write the buffer to the display
+  display.display();
+
+  if (millis() - timeSinceLastModeSwitch > DEMO_DURATION) {
+    demoMode = (demoMode + 1)  % demoLength;
+    timeSinceLastModeSwitch = millis();
+  }
+  counter++;
+  delay(10);
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306SimpleDemo/images.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306SimpleDemo/images.h
new file mode 100644
index 0000000000000000000000000000000000000000..9daf8c1a25b9d006fc67f49c6868563d13c7ae77
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306SimpleDemo/images.h
@@ -0,0 +1,28 @@
+#define WiFi_Logo_width 60
+#define WiFi_Logo_height 36
+const char WiFi_Logo_bits[] PROGMEM = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
+  0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+  0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF,
+  0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00,
+  0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C,
+  0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00,
+  0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C,
+  0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00,
+  0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C,
+  0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00,
+  0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C,
+  0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00,
+  0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F,
+  0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00,
+  0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF,
+  0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00,
+  0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
+  0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  };
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306UiDemo/SSD1306UiDemo.ino b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306UiDemo/SSD1306UiDemo.ino
new file mode 100644
index 0000000000000000000000000000000000000000..31357569ff3849b6c1098165aa96f9880129327f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306UiDemo/SSD1306UiDemo.ino
@@ -0,0 +1,189 @@
+/**
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 by Daniel Eichhorn
+ * Copyright (c) 2016 by Fabrice Weinberg
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ */
+
+ // Include the correct display library
+ // For a connection via I2C using Wire include
+ #include <Wire.h>  // Only needed for Arduino 1.6.5 and earlier
+ #include "SSD1306.h" // alias for `#include "SSD1306Wire.h"`
+ // or #include "SH1106.h" alis for `#include "SH1106Wire.h"`
+ // For a connection via I2C using brzo_i2c (must be installed) include
+ // #include <brzo_i2c.h> // Only needed for Arduino 1.6.5 and earlier
+ // #include "SSD1306Brzo.h"
+ // #include "SH1106Brzo.h"
+ // For a connection via SPI include
+ // #include <SPI.h> // Only needed for Arduino 1.6.5 and earlier
+ // #include "SSD1306Spi.h"
+ // #include "SH1106SPi.h"
+
+// Include the UI lib
+#include "OLEDDisplayUi.h"
+
+// Include custom images
+#include "images.h"
+
+// Use the corresponding display class:
+
+// Initialize the OLED display using SPI
+// D5 -> CLK
+// D7 -> MOSI (DOUT)
+// D0 -> RES
+// D2 -> DC
+// D8 -> CS
+// SSD1306Spi        display(D0, D2, D8);
+// or
+// SH1106Spi         display(D0, D2);
+
+// Initialize the OLED display using brzo_i2c
+// D3 -> SDA
+// D5 -> SCL
+// SSD1306Brzo display(0x3c, D3, D5);
+// or
+// SH1106Brzo  display(0x3c, D3, D5);
+
+// Initialize the OLED display using Wire library
+SSD1306  display(0x3c, D3, D5);
+// SH1106 display(0x3c, D3, D5);
+
+OLEDDisplayUi ui     ( &display );
+
+void msOverlay(OLEDDisplay *display, OLEDDisplayUiState* state) {
+  display->setTextAlignment(TEXT_ALIGN_RIGHT);
+  display->setFont(ArialMT_Plain_10);
+  display->drawString(128, 0, String(millis()));
+}
+
+void drawFrame1(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
+  // draw an xbm image.
+  // Please note that everything that should be transitioned
+  // needs to be drawn relative to x and y
+
+  display->drawXbm(x + 34, y + 14, WiFi_Logo_width, WiFi_Logo_height, WiFi_Logo_bits);
+}
+
+void drawFrame2(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
+  // Demonstrates the 3 included default sizes. The fonts come from SSD1306Fonts.h file
+  // Besides the default fonts there will be a program to convert TrueType fonts into this format
+  display->setTextAlignment(TEXT_ALIGN_LEFT);
+  display->setFont(ArialMT_Plain_10);
+  display->drawString(0 + x, 10 + y, "Arial 10");
+
+  display->setFont(ArialMT_Plain_16);
+  display->drawString(0 + x, 20 + y, "Arial 16");
+
+  display->setFont(ArialMT_Plain_24);
+  display->drawString(0 + x, 34 + y, "Arial 24");
+}
+
+void drawFrame3(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
+  // Text alignment demo
+  display->setFont(ArialMT_Plain_10);
+
+  // The coordinates define the left starting point of the text
+  display->setTextAlignment(TEXT_ALIGN_LEFT);
+  display->drawString(0 + x, 11 + y, "Left aligned (0,10)");
+
+  // The coordinates define the center of the text
+  display->setTextAlignment(TEXT_ALIGN_CENTER);
+  display->drawString(64 + x, 22 + y, "Center aligned (64,22)");
+
+  // The coordinates define the right end of the text
+  display->setTextAlignment(TEXT_ALIGN_RIGHT);
+  display->drawString(128 + x, 33 + y, "Right aligned (128,33)");
+}
+
+void drawFrame4(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
+  // Demo for drawStringMaxWidth:
+  // with the third parameter you can define the width after which words will be wrapped.
+  // Currently only spaces and "-" are allowed for wrapping
+  display->setTextAlignment(TEXT_ALIGN_LEFT);
+  display->setFont(ArialMT_Plain_10);
+  display->drawStringMaxWidth(0 + x, 10 + y, 128, "Lorem ipsum\n dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore.");
+}
+
+void drawFrame5(OLEDDisplay *display, OLEDDisplayUiState* state, int16_t x, int16_t y) {
+
+}
+
+// This array keeps function pointers to all frames
+// frames are the single views that slide in
+FrameCallback frames[] = { drawFrame1, drawFrame2, drawFrame3, drawFrame4, drawFrame5 };
+
+// how many frames are there?
+int frameCount = 5;
+
+// Overlays are statically drawn on top of a frame eg. a clock
+OverlayCallback overlays[] = { msOverlay };
+int overlaysCount = 1;
+
+void setup() {
+  Serial.begin(115200);
+  Serial.println();
+  Serial.println();
+
+	// The ESP is capable of rendering 60fps in 80Mhz mode
+	// but that won't give you much time for anything else
+	// run it in 160Mhz mode or just set it to 30 fps
+  ui.setTargetFPS(60);
+
+	// Customize the active and inactive symbol
+  ui.setActiveSymbol(activeSymbol);
+  ui.setInactiveSymbol(inactiveSymbol);
+
+  // You can change this to
+  // TOP, LEFT, BOTTOM, RIGHT
+  ui.setIndicatorPosition(BOTTOM);
+
+  // Defines where the first frame is located in the bar.
+  ui.setIndicatorDirection(LEFT_RIGHT);
+
+  // You can change the transition that is used
+  // SLIDE_LEFT, SLIDE_RIGHT, SLIDE_UP, SLIDE_DOWN
+  ui.setFrameAnimation(SLIDE_LEFT);
+
+  // Add frames
+  ui.setFrames(frames, frameCount);
+
+  // Add overlays
+  ui.setOverlays(overlays, overlaysCount);
+
+  // Initialising the UI will init the display too.
+  ui.init();
+
+  display.flipScreenVertically();
+
+}
+
+
+void loop() {
+  int remainingTimeBudget = ui.update();
+
+  if (remainingTimeBudget > 0) {
+    // You can do some work here
+    // Don't do stuff if you are below your
+    // time budget.
+    delay(remainingTimeBudget);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306UiDemo/images.h b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306UiDemo/images.h
new file mode 100644
index 0000000000000000000000000000000000000000..8b876a369e6f2609700798da9da47431e5e0db21
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/examples/SSD1306UiDemo/images.h
@@ -0,0 +1,50 @@
+#define WiFi_Logo_width 60
+#define WiFi_Logo_height 36
+const char WiFi_Logo_bits[] PROGMEM = {
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
+  0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF,
+  0xFF, 0x03, 0x00, 0x00, 0x00, 0xFC, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
+  0x00, 0xFF, 0xFF, 0xFF, 0x07, 0xC0, 0x83, 0x01, 0x80, 0xFF, 0xFF, 0xFF,
+  0x01, 0x00, 0x07, 0x00, 0xC0, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x0C, 0x00,
+  0xC0, 0xFF, 0xFF, 0x7C, 0x00, 0x60, 0x0C, 0x00, 0xC0, 0x31, 0x46, 0x7C,
+  0xFC, 0x77, 0x08, 0x00, 0xE0, 0x23, 0xC6, 0x3C, 0xFC, 0x67, 0x18, 0x00,
+  0xE0, 0x23, 0xE4, 0x3F, 0x1C, 0x00, 0x18, 0x00, 0xE0, 0x23, 0x60, 0x3C,
+  0x1C, 0x70, 0x18, 0x00, 0xE0, 0x03, 0x60, 0x3C, 0x1C, 0x70, 0x18, 0x00,
+  0xE0, 0x07, 0x60, 0x3C, 0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C,
+  0xFC, 0x73, 0x18, 0x00, 0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00,
+  0xE0, 0x87, 0x70, 0x3C, 0x1C, 0x70, 0x18, 0x00, 0xE0, 0x8F, 0x71, 0x3C,
+  0x1C, 0x70, 0x18, 0x00, 0xC0, 0xFF, 0xFF, 0x3F, 0x00, 0x00, 0x08, 0x00,
+  0xC0, 0xFF, 0xFF, 0x1F, 0x00, 0x00, 0x0C, 0x00, 0x80, 0xFF, 0xFF, 0x1F,
+  0x00, 0x00, 0x06, 0x00, 0x80, 0xFF, 0xFF, 0x0F, 0x00, 0x00, 0x07, 0x00,
+  0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0xF8, 0xFF, 0xFF,
+  0xFF, 0x7F, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0x01, 0x00, 0x00,
+  0x00, 0x00, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xFF,
+  0x7F, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE0, 0xFF, 0x1F, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x80, 0xFF, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  };
+
+const char activeSymbol[] PROGMEM = {
+    B00000000,
+    B00000000,
+    B00011000,
+    B00100100,
+    B01000010,
+    B01000010,
+    B00100100,
+    B00011000
+};
+
+const char inactiveSymbol[] PROGMEM = {
+    B00000000,
+    B00000000,
+    B00000000,
+    B00000000,
+    B00011000,
+    B00011000,
+    B00000000,
+    B00000000
+};
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/library.json b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..862d171673544485dc81d73f36a75bc802faa440
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/library.json
@@ -0,0 +1,28 @@
+{
+  "name": "ESP8266_SSD1306",
+  "version": "3.2.7",
+  "keywords": "ssd1306, oled, display, i2c",
+  "description": "A I2C display driver for SSD1306 oled displays connected to an ESP8266 or ESP32",
+  "repository":
+  {
+    "type": "git",
+    "url": "https://github.com/squix78/esp8266-oled-ssd1306.git"
+  },
+  "authors":
+  [
+    {
+        "name": "Daniel Eichhorn",
+        "email": "squix78@gmail.com",
+        "url": "http://blog.squix.ch"
+    },
+    {
+        "name": "Fabrice Weinberg",
+        "email": "fabrice@weinberg.me"
+    }
+  ],
+  "frameworks": "arduino",
+  "platforms": [
+    "espressif8266",
+    "espressif32"
+  ]
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/library.properties b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/library.properties
new file mode 100644
index 0000000000000000000000000000000000000000..4f8de4cf3745d2af820bfc3ab762ac81f30e5114
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/library.properties
@@ -0,0 +1,9 @@
+name=ESP8266 and ESP32 Oled Driver for SSD1306 display
+version=3.2.7
+author=Daniel Eichhorn, Fabrice Weinberg
+maintainer=Daniel Eichhorn <squix78@gmail.com>
+sentence=A I2C display driver for SSD1306 oled displays connected to an ESP8266 or ESP32
+paragraph=A I2C display driver for SSD1306 oled displays connected to an ESP8266 or ESP32
+category=Display
+url=https://github.com/squix78/esp8266-oled-ssd1306
+architectures=esp8266,esp32
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/license b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/license
new file mode 100644
index 0000000000000000000000000000000000000000..706c10fed8a1231cc3c1575369db6f5aee34e3fa
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/license
@@ -0,0 +1,24 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 by Daniel Eichhorn
+Copyright (c) 2016 by Fabrice Weinberg
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
+
+See more at http://blog.squix.ch
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame1.jpg b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame1.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..536b570db09931ec634b38777033432276bafed8
Binary files /dev/null and b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame1.jpg differ
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame2.jpg b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame2.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..8dccbcd0210b5c1dd7bad824982c1ecbf5c1edc2
Binary files /dev/null and b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame2.jpg differ
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame3.jpg b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame3.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..49e07ab53a6bbcfcc5922d5730c8492aaf8ba7bd
Binary files /dev/null and b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame3.jpg differ
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame4.jpg b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame4.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..99cbe1bc41ce7fe01700f5cd00bf04da68f9602a
Binary files /dev/null and b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/DemoFrame4.jpg differ
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/FontTool.png b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/FontTool.png
new file mode 100644
index 0000000000000000000000000000000000000000..c7bb222bed6067b921d8a3e479b56e4981a185d4
Binary files /dev/null and b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/FontTool.png differ
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/SPI_version.jpg b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/SPI_version.jpg
new file mode 100644
index 0000000000000000000000000000000000000000..115c9f3bccef1177204c88ef8b8cb8f22daa9e7c
Binary files /dev/null and b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/SPI_version.jpg differ
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/xbmPreview.png b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/xbmPreview.png
new file mode 100644
index 0000000000000000000000000000000000000000..70ea3a53a843f220f18d2ef7cc0698516a990ba6
Binary files /dev/null and b/ESP-1ch-Gateway-v5.0-master/libraries/ESP8266_Oled_Driver_for_SSD1306_display/resources/xbmPreview.png differ
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/LoRaCode/LoRaCode.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/LoRaCode/LoRaCode.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..09fd2b62f4068a2ad4cfefb8c9ca9bf05ae35a23
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/LoRaCode/LoRaCode.cpp
@@ -0,0 +1,581 @@
+// LoRa encoding and decoding functions
+// Copyright (c) 2016, 2017 Maarten Westenberg (mw12554@hotmail.com)
+// Version 1.2.0
+// Date: 2016-10-23
+// Version 1.2.0 on April 20, 2017
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// Author: Maarten Westenberg
+//
+// The protocols used in this code: 
+// 1. LoRA Specification version V1.0 and V1.1 for Gateway-Node communication
+//	
+// 2. Semtech Basic communication protocol between Lora gateway and server version 3.0.0
+//	https://github.com/Lora-net/packet_forwarder/blob/master/PROTOCOL.TXT
+//
+// Notes: 
+// The lCode specification is documented on a sparate page on github.
+//
+// Todo:
+// The luminescense is read as a 16-bit value in the library and converted to
+//	a float value in order to get proper scaling etc. In the lCode lib it is 
+//	coded as a 2-byte value over the air, which might be incorrect for lux values
+//	over 650 lux (which IS posible since bright daylight has more than 1000 lux).
+//	So XXX we have to add another byte to cover values above 65
+// ----------------------------------------------------------------------------------------
+
+#define DEBUG 0
+
+#if defined(__AVR__)
+#include <avr/pgmspace.h>
+#include <Arduino.h>
+#include <Battery.h>
+#elif defined(ARDUINO_ARCH_ESP8266) | defined(ESP32)
+#include <ESP.h>
+#elif defined(__MKL26Z64__)
+#include <Arduino.h>
+#else
+#error Unknown architecture in aes.cpp
+#endif
+
+#include "LoRaCode.h"
+
+int ldebug=DEBUG;
+
+// --------------------------------------------------------------------------------
+// Encode Temperature. 
+// We use 2 bytes for temperature, first contains the integer partial_sort.
+// We add 100 so we effectively will measure temperatures between -100 and 154 degrees.
+// Second byte contains the fractional part in 2 decimals (00-99).
+// --------------------------------------------------------------------------------
+int LoRaCode::eTemperature(float val, byte *msg) {
+	int len=0;
+	byte i= (byte) (val+100);					// Integer part
+	byte f= (byte) ( ((float)(val - (float)i -100)) * 100); // decimal part
+	msg[len++] = ((byte)O_TEMP << 2) | 0x01;	// Last 2 bits are 0x01, two bytes
+	msg[len++] = i;
+	msg[len++] = f;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Temperature "));
+		Serial.print(i-100);					// For readibility
+		Serial.print(".");
+		Serial.println(f);
+	}
+#endif
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Code Humidity in the *msg
+// The humidity of the sensor is between 0-100%, we encode this value * 2 so that
+// byte value is betwene 0 and 199.
+// --------------------------------------------------------------------------------
+int LoRaCode::eHumidity(float val, byte *msg) {
+	int len=0;
+	byte i = (byte) ((float)val *2);				// Value times 2
+	msg[len++] = ((byte)O_HUMI << 2) | 0x00;	// Last 2 bits are 0x00, one byte 
+	msg[len++] = i;								//
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Humidity "));
+		Serial.println((float)(i/2));			// For readibility
+	}
+#endif
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Code the Airpressure.
+// The coded value is the measured value minus 900. This gives an airpressure
+// range from 850-1104
+// --------------------------------------------------------------------------------
+int LoRaCode::eAirpressure(float val, byte *msg) {
+	int len=0;
+	byte i = (byte) ((float)val -850);			// Value times 2
+	msg[len++] = ((byte)O_AIRP << 2) | 0x00;	// Last 2 bits are 0x00, one byte 
+	msg[len++] = i;								//
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Airpressure "));
+		Serial.println((float)(i + 850));		// For readibility
+	}
+#endif
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// GPS lat and lng encoding
+// There are several ways to encode GPS data. Eother by mapping on 3 or 4 bytes of
+// Floating, or by just multiplying with 1,000,000 for example (gives 6 digits
+// precision which is enough for almost all applications)
+// --------------------------------------------------------------------------------
+int LoRaCode::eGps(double lat, double lng, byte *msg) {
+#if DEBUG>0
+	if (ldebug>=1) {
+		Serial.print(F(" LAT: ")); Serial.print(lat,6);
+		Serial.print(F(" LNG: ")); Serial.println(lng,6);
+	}
+#endif
+	int len=0;
+	long factor = 1000000;
+	msg[len++] = ((byte)O_GPS << 2) | 0x00;		// Last 2 bits are 0x00, don't care
+	const long calculatedLat = (long)(lat * factor);
+	msg[len++] = (calculatedLat >> (8*3)) & 0xFF;
+	msg[len++] = (calculatedLat >> (8*2)) & 0xFF;
+	msg[len++] = (calculatedLat >> (8*1)) & 0xFF;
+	msg[len++] = (calculatedLat >> (8*0)) & 0xFF;
+	const long calculatedLng = (long)(lng * factor);
+	msg[len++] = (calculatedLng >> (8*3)) & 0xFF;
+	msg[len++] = (calculatedLng >> (8*2)) & 0xFF;
+	msg[len++] = (calculatedLng >> (8*1)) & 0xFF;
+	msg[len++] = (calculatedLng >> (8*0)) & 0xFF;
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Code the extended GPS format
+// latitude and longitude are coded just like the short format
+// Altitude is a long containing the altitude in cm
+// --------------------------------------------------------------------------------
+int LoRaCode::eGpsL(double lat, double lng, long alt, int sat,
+					byte *msg) {
+	if (ldebug>=1) {
+		Serial.print(F(" LAT: ")); Serial.print(lat,6);
+		Serial.print(F(" LNG: ")); Serial.print(lng,6);
+		Serial.print(F(" ALT: ")); Serial.print(alt/100);
+		Serial.print(F(" SAT: ")); Serial.println(sat);
+	}
+	int len=0;
+	long factor = 1000000;
+	msg[len++] = ((byte)O_GPSL << 2) | 0x00;	// Last 2 bits are 0x00, don't care
+	const long calculatedLat = (long)(lat * factor);
+	msg[len++] = (calculatedLat >> (8*3)) & 0xFF;
+	msg[len++] = (calculatedLat >> (8*2)) & 0xFF;
+	msg[len++] = (calculatedLat >> (8*1)) & 0xFF;
+	msg[len++] = (calculatedLat >> (8*0)) & 0xFF;
+	const long calculatedLng = (long)(lng * factor);
+	msg[len++] = (calculatedLng >> (8*3)) & 0xFF;
+	msg[len++] = (calculatedLng >> (8*2)) & 0xFF;
+	msg[len++] = (calculatedLng >> (8*1)) & 0xFF;
+	msg[len++] = (calculatedLng >> (8*0)) & 0xFF;
+	const long calculatedAlt = (long)(alt);		// Altitude is integer specified in cm (converted later to m)
+	msg[len++] = (calculatedAlt >> (8*3)) & 0xFF;
+	msg[len++] = (calculatedAlt >> (8*2)) & 0xFF;
+	msg[len++] = (calculatedAlt >> (8*1)) & 0xFF;
+	msg[len++] = (calculatedAlt >> (8*0)) & 0xFF;
+	msg[len++] = sat & 0xFF;						// Positive number, assumed always to be less than 255
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Encode the 1-bit PIR value
+// --------------------------------------------------------------------------------
+int LoRaCode::ePir(int val, byte *msg) {
+	int i = (byte) ( val );	
+	int len=0;										//
+	msg[len++] = (O_PIR << 2) | 0x00;				// Last 2 bits are 0x00, one byte 
+	msg[len++] = i;	
+	return(len);
+}
+
+
+// --------------------------------------------------------------------------------
+// Encode Airquality Value.
+// Airquality through SDS011 sensors is measured with two float values
+// and read as a 10-bit value (0-1024). We use a 2 x 2-byte value
+// --------------------------------------------------------------------------------
+int LoRaCode::eAirquality(int pm25, int pm10, byte *msg) {
+	int len=0;
+
+	uint16_t val = (uint16_t) (pm25);
+	msg[len++] = (O_AQ << 2) | 0x03;				// Last 2 bits are 0x03, so 4 bytes data
+	msg[len++] = (val >> 8) & 0xFF;
+	msg[len++] = val & 0xFF;
+	
+	val = (uint16_t) (pm10);
+	msg[len++] = (val >> 8) & 0xFF;
+	msg[len++] = val & 0xFF;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Airquality <"));
+		Serial.print(len);
+		Serial.print(F("> "));
+		Serial.println(val);						// For readibility
+	}
+#endif
+	return(len);
+}
+
+
+// --------------------------------------------------------------------------------
+// Encode a Multi-Button sensor. This sensor is a concentrator that receives
+// several sensor values over 433MHz and retransmits them over LoRa 
+//	The LoRa sensor node contains the main address (LoRa id), the concentrated
+// sendors have their own unique address/channel combination.
+// --------------------------------------------------------------------------------
+int	LoRaCode::eMbuttons(byte val, unsigned long address, unsigned short channel, byte *msg) {
+	int len=0;
+	msg[len++] = (O_MB << 2) | 0x00;			// Several bytes, do not care
+	
+	msg[len++] = val;							// First code the value
+		
+	msg[len++] = (address >> (8*3)) & 0xFF;		// Address
+	msg[len++] = (address >> (8*2)) & 0xFF;
+	msg[len++] = (address >> (8*1)) & 0xFF;
+	msg[len++] = (address >> (8*0)) & 0xFF;
+	
+	msg[len++] = (channel >> (8*1)) & 0xFF;		// Channel
+	msg[len++] = (channel >> (8*0)) & 0xFF;
+	
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Multi-Button "));
+		Serial.println(val);						// For readibility
+	}
+#endif	
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Moisture detection with two metal sensors in fluid or soil
+//
+// --------------------------------------------------------------------------------
+int LoRaCode::eMoist(int val, byte *msg) {
+	int len=0;
+	msg[len++] = (O_MOIST << 2) | 0x00;				// Last 2 bits are 0x00, 1 byte
+	msg[len++] = (val  / 4 ) & 0xFF;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Moisture "));
+		Serial.println(val);						// For readibility
+	}
+#endif
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Luminescense detection with two metal sensors in fluid or soil
+// The value to be decoded is 2-byte value which gives
+// an integer resolution from 0 to 65535
+// It is possible to add a third byte for extra resolution (2 decimals).
+// --------------------------------------------------------------------------------
+int LoRaCode::eLuminescense(float val, byte *msg) {
+	int len=0;
+	uint16_t lux = (uint16_t) (val);
+	// now determine the fraction for a third byte
+	msg[len++] = (O_LUMI << 2) | 0x01;				// Last 2 bits are 0x00, 2 bytes resolution
+	msg[len++] = (lux >> (8*1)) & 0xFF;				// LSB
+	msg[len++] = (lux >> (8*0)) & 0xFF;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Lumi "));
+		Serial.println(val);						// For readibility
+	}
+#endif
+	return(len);
+}
+
+int LoRaCode::eLuminescenseL(float val, byte *msg) {
+	int len=0;
+	uint16_t lux = (uint16_t) (val);
+	// now determine the fraction for a third byte
+	uint8_t frac = (uint8_t) ((val-lux) * 100);
+
+	msg[len++] = (O_LUMI << 2) | 0x02;				// Last 2 bits are 0x00, 3 bytes resolution
+	msg[len++] = (lux >> (8*1)) & 0xFF;				// LSB
+	msg[len++] = (lux >> (8*0)) & 0xFF;
+	msg[len++] = (frac) & 0xFF;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Lumi L="));
+		Serial.print(lux);						// For readibility
+		Serial.print('.');
+		Serial.println(frac);						// For readibility
+	}
+#endif
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Distance detection 
+// In this case we use 2 bytes for 0-65535 mm (which is more than we need)
+// NOTE: The sensor will report in mm resolution, but the server side
+// will decode in cm resolution with one decimal fraction (if available)
+// --------------------------------------------------------------------------------
+int LoRaCode::eDistance(int val, byte *msg) {
+	int len=0;
+	msg[len++] = (O_DIST << 2) | 0x01;				// Last 2 bits are 0x00, 2 bytes resolution
+	msg[len++] = (val >> (8*1)) & 0xFF;	// LSB
+	msg[len++] = (val >> (8*0)) & 0xFF;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Dist "));
+		Serial.println(val);						// For readibility
+	}
+#endif
+	return(len);
+}
+
+
+// --------------------------------------------------------------------------------
+// Encode Gas concentration Value.
+// Airquality through MQxx or AQxx sensors is measured through the Analog port
+// and read as a 10-bit value (0-1024). We use a 2-byte value
+// In order to tell the server which sensor is reporting (we can have several)
+// --------------------------------------------------------------------------------
+int LoRaCode::eGas(int val, byte *msg) {
+	int len=0;
+	msg[len++] = (O_GAS << 2) | 0x01;				// Last 2 bits are 0x01, 2 bytes
+	msg[len++] = (val >> 8) & 0xFF;
+	msg[len++] = val & 0xFF;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Gas "));
+		Serial.println(val);						// For readibility
+	}
+#endif
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Encode Battery Value.
+// Battery Voltage is between 2 and 4 Volts (sometimes 12V)
+// We therefore multiply by 20, reported values are between 1 and 255, so
+// sensor values between 0,05 and 12.75 Volts
+// --------------------------------------------------------------------------------
+int LoRaCode::eBattery(float val, byte *msg) {
+	int len=0;
+	int i = (byte) ((float)val *20);				// Value times 20
+	msg[len++] = (O_BATT << 2) | 0x00;				// Last 2 bits are 0x00, one byte 
+	msg[len++] = i;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Battery "));
+		Serial.println((float)(i/20));				// For readibility
+	}
+#endif
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Encode AD Converter value for pin adc0 and adv1
+// Battery Voltage is between 2 and 4 Volts (sometimes 12V)
+// We therefore multiply by 20, reported values are between 1 and 255, so
+// sensor values between 0,05 and 12.75 Volts
+// --------------------------------------------------------------------------------
+int LoRaCode::eAdc0(int val, byte *msg) {
+	int len=0;
+	msg[len++] = (O_ADC0 << 2) | 0x00;				// Last 2 bits are 0x00, 1 byte
+	msg[len++] = (val  / 4 ) & 0xFF;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Adc0 "));
+		Serial.println(val);						// For readibility
+	}
+#endif
+	return(len);
+}
+int LoRaCode::eAdc1(int val, byte *msg) {
+	int len=0;
+	msg[len++] = (O_ADC1 << 2) | 0x00;				// Last 2 bits are 0x00, 1 byte
+	msg[len++] = (val  / 4 ) & 0xFF;
+#if DEBUG>0
+	if (ldebug >=1) {
+		Serial.print(F("lcode:: Add Adc1 "));
+		Serial.println(val);						// For readibility
+	}
+#endif
+	return(len);
+}
+
+
+
+// --------------------------------------------------------------------------------
+// Function to encode the sensor values to the Payload message
+// Input: Opcode and Value
+// Output: msg
+// return: Number of bytes added to msg
+// --------------------------------------------------------------------------------
+int LoRaCode::eVal (int opcode, byte *val, byte *msg) {
+	int len=0;
+	switch (opcode) {
+		case O_TEMP:								// Temperature
+			len += eTemperature((float) *val, msg);
+		break;
+		case O_HUMI:								// Humidity
+			len += eHumidity((float) *val, msg);
+		break;
+		case O_AIRP:								// Airpressure
+			len += eAirpressure((float) *val, msg);	
+		break;
+		case O_GPS:									// GPS short Info
+			//len += eGps(lat, lng, msg);
+		break;
+		case O_PIR:									// PIR 
+			len += ePir((int) *val, msg);
+		break;
+
+		case O_MOIST:								// Moisture
+			len += eMoist((int) *val, msg);
+		break;
+		case O_LUMI:								// Luminescense
+			len += eLuminescense((int) *val, msg);
+		break;
+		case O_BATT:								// Battery
+			len += eBattery((float) *val, msg);
+		break;
+		default:
+#if DEBUG>0
+			Serial.println("lCode:: Error opcode not known");
+#endif
+		break;
+	}
+	return(len);
+}
+
+// --------------------------------------------------------------------------------
+// Function to encode the Payload message (final step)
+// This function will modify the first byte of the string to encode the length
+// and the parity. Payload begins at position 1, byte 0 is reserved
+// --------------------------------------------------------------------------------
+bool LoRaCode::eMsg (byte * msg, int len) {
+	unsigned char par = 0;
+	if (len > 64) {
+#if DEBUG>0
+		Serial.println("lCodeMsg:: Error, string to long");
+#endif
+		return(false);
+	}
+#if DEBUG>0
+	if (ldebug>=1) {Serial.print(F("LCodeMsg:: <")); Serial.print(len); Serial.print(F("> ")); }
+#endif
+	// else we probably have a good message
+	msg[0] = ( len << 1 ) | (0x80);
+	
+	// Now we have to calculate the Parity for the message
+	for (int i=0; i< len; i++) {
+	  byte cc = msg[i];
+	  par ^= cc;
+	}
+	
+	// Now we have par as one byte and need to XOR the bits of that byte.
+	unsigned char pp = 8;							// width of byte in #bits
+	while (pp > 1) {
+		par ^= par >> (pp/2);
+		pp -= pp/2;
+	}
+
+	if (par & 0x01) {								// if odd number of 1's in string
+#if DEBUG>0
+		if (ldebug>=1) Serial.print(F(" odd "));
+#endif
+		msg[0] |= 0x01;								// Add another 1 to make parity even
+	}
+	else {
+#if DEBUG>0
+		if (ldebug>=1) Serial.print(F(" even "));
+#endif
+	}
+	return(true);
+}
+
+// --------------------------------------------------------------------------------
+// Print an encoded string
+// --------------------------------------------------------------------------------
+void LoRaCode::lPrint (byte *msg, int len) {
+	Serial.print(F("lCode:  "));
+	for (int i=0; i< len; i++) {
+		if (msg[i]<10) Serial.print('0');
+		Serial.print(msg[i],HEX);
+		Serial.print(" ");
+	}
+	Serial.println();
+}
+
+// --------------------------------------------------------------------------------
+// Decode first byte of message containing length
+// --------------------------------------------------------------------------------
+int LoRaCode::dLen (byte *msg) {
+	if ( ! (msg[0] & 0x80 )) return(-1);		// Error
+	int len = msg[0] & 0x7F;					// Blank out first bit which is always 1 with LoRaCode
+	return (len >> 1);
+}
+
+// --------------------------------------------------------------------------------
+// Decode message. One function for all, always returns a float
+// We expect that the buffer is large enough and will contain all bytes required
+// by that specific encoding
+// XXX This function needs finishig (never used at the moment)
+// PARAMETERS:
+//	msg: Contains the message as a byte string
+//	val: contains the decoded value as a float (xxx)
+//	mode: Contains the opcode of the decoded command
+// RETURN:
+//	The number of bytes read from the buffer
+// --------------------------------------------------------------------------------
+int LoRaCode::dMsg (byte *msg, byte *val, byte *mode) {
+	float res;
+	byte len = msg[0] & 0x03;					// Last 2 bits
+	*mode = (byte) (msg[0] >> 2);
+	switch (*mode) {
+		case O_TEMP:
+			*val = (float) msg[1]- 100 + ( (float) msg[2] / 100);
+			return(3);
+		break;
+		case O_HUMI:
+			*val = (float) msg[1] / 2;
+			return(2);
+		break;
+		case O_AIRP:
+			*val = 0;
+			return(2);
+		break;
+		case O_GPS:							// Returning one float does not work for GPS. function never used
+			*val = 0;
+			return(9);
+		break;
+		case O_GPSL:
+			*val = 0;
+			return(14);
+		break;
+		case O_PIR:
+			*val = msg[1];
+			return(2);
+		break;
+		case O_AQ:
+			*val = 0;
+			return(3);
+		break;
+		case O_BATT:
+			*val = (float) msg[1] / 20;
+			return(2);
+		break;
+		case O_STAT:
+			*val = 0;
+			return(1);
+		case O_1CH:
+			*val = msg[1];
+			return(2);
+		break;
+		case O_SF:
+			*val = msg[1];
+			return(2);
+		break;
+		case O_TIM:						// Timing of the wait cyclus
+			val[0] = msg[1];
+			val[1] = msg[2];
+			return(3);					// total lengt 2 bytes data + 1 byte opcode
+		break;
+		default:
+			return(0);
+	}
+	return (0);
+}
+
+// Variable declaration
+LoRaCode lcode;
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/LoRaCode/LoRaCode.h b/ESP-1ch-Gateway-v5.0-master/libraries/LoRaCode/LoRaCode.h
new file mode 100644
index 0000000000000000000000000000000000000000..340c8090b19d49daf649016da2df71720d61a87b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/LoRaCode/LoRaCode.h
@@ -0,0 +1,103 @@
+// LoRa encoding and decoding functions
+// Copyright (c) 2016 Maarten Westenberg 
+// Version 1.1.0
+// Date: 2016-10-23
+//
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the MIT License
+// which accompanies this distribution, and is available at
+// https://opensource.org/licenses/mit-license.php
+//
+// Author: Maarten Westenberg
+//
+// The protocols used in this code: 
+// 1. LoRA Specification version V1.0 and V1.1 for Gateway-Node communication
+//	
+// 2. Semtech Basic communication protocol between Lora gateway and server version 3.0.0
+//	https://github.com/Lora-net/packet_forwarder/blob/master/PROTOCOL.TXT
+//
+// Notes: 
+// The lCode specification is documented on a sparate page on github.
+//
+// Todo:
+// The luminescense is read as a 16-bit value in the library and converted to
+//	a float value in order to get proper scaling etc. In the lCode lib it is 
+//	coded as a 2-byte value over the air, which might be incorrect for lux values
+//	over 650 lux (which IS posible since bright daylight has more than 1000 lux).
+//	So XXX we have to add another byte to cover values above 65
+// ----------------------------------------------------------------------------------------
+
+#ifndef LoRaCode_h
+#define LoRaCode_h
+
+// Op Codes
+#define O_TEMP		0x01				// Temperature is a one-byte code
+#define O_HUMI		0x02				// Humidity is a one-byte code
+#define O_AIRP		0x03				// Air pressure is a one-byte code
+#define O_GPS		0x04				// Short version: ONLY 3 bytes LAT and 3 bytes LONG
+#define O_GPSL		0x05				// Long GPS
+#define O_PIR		0x06				// Movement, 1 bit (=1 byte)
+#define O_AQ		0x07				// Airquality
+#define O_RTC		0x08				// Real Time Clock
+#define O_COMPASS	0x09				// Compass
+#define O_MB		0x0A				// Multi Sensors 433
+#define O_MOIST 	0x0B				// Moisture	is one-byte
+#define O_LUMI  	0x0C				// Luminescense u16
+#define O_DIST		0x0D				// Distance is 2-byte
+#define O_GAS		0x0E				// GAS 
+// 0x0F
+
+// 0x10									// 16 values
+// 0x11
+// ..
+// 0x1F
+
+#define O_BATT		0x20				// Internal Battery
+#define O_ADC0		0x21				// AD converter on pin 0
+#define O_ADC1		0x22
+
+// Reserved for LoRa messages (especially downstream)
+#define O_STAT		0x30				// Ask for status message from node
+#define O_SF		0x31				// Spreading factor change OFF=0, values 7-12
+#define O_TIM		0x32				// Timing of the wait cyclus (20 to 7200 seconds)
+#define O_1CH		0x33				// Single channel: Channel Value=0-9, OFF==255
+#define O_LOC		0x34				// Ask for the location. Responds with GPS (if available)
+
+// ..
+// 0x3F
+
+class LoRaCode
+{
+	public:
+
+		int		eVal(int opcode, byte *val, byte *msg);
+		int		eTemperature(float val, byte *msg);
+		int		eHumidity(float val, byte *msg);
+		int		eAirpressure(float val, byte *msg);
+		int		eGps(double lat, double lng, byte *msg);
+		int		eGpsL(double lat, double lng, long alt, int sat, byte *msg);
+		int		ePir(int val, byte *msg);
+		int		eAirquality(int pm25, int pm10, byte *msg);			// value 0 (good) -1024 (gas)
+		int		eMbuttons(byte val, unsigned long address, unsigned short channel, byte *msg);		// concentrator for multi-buttons
+		int		eMoist(int val, byte *msg);							// 255 is dry, 0 is wet
+		int		eLuminescense(float val, byte *msg);				// val contains light intensity
+		int		eLuminescenseL(float val, byte *msg);				// long contains light intensity
+		int		eDistance(int val, byte *msg);
+		int		eGas(int val, byte *msg);
+		
+		// opcodes 0x0F until 0x1F
+		int		eBattery(float val, byte *msg);
+		int		eAdc0(int val, byte *msg);							// Pin A0 has 1024 values, we use 256
+		int		eAdc1(int val, byte *msg);							// Pin A1 has 1024 values, we use 256
+		
+		bool	eMsg(byte *msg, int len);
+		void	lPrint(byte *msg, int len);
+	
+		//Decoding (downstream)
+		int		dLen (byte *msg);
+		int		dMsg (byte *msg, byte *val, byte *mode);
+		
+};
+
+extern LoRaCode lcode;
+#endif
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/LoRaCode/desktop.ini b/ESP-1ch-Gateway-v5.0-master/libraries/LoRaCode/desktop.ini
new file mode 100644
index 0000000000000000000000000000000000000000..1dc07a8f445f9dfb92f82f55c28c3982d78f9420
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/LoRaCode/desktop.ini
@@ -0,0 +1,5 @@
+[.ShellClassInfo]
+InfoTip=Deze map wordt online gedeeld.
+IconFile=C:\Program Files\Google\Drive\googledrivesync.exe
+IconIndex=16
+    
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/README.md b/ESP-1ch-Gateway-v5.0-master/libraries/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b84a59e1ddef978a08f254488b28c5aeccdb0295
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/README.md
@@ -0,0 +1,33 @@
+# Single Channel LoRaWAN Gateway
+
+Version 5.1.0, May 10, 2018  
+Author: M. Westenberg (mw12554@hotmail.com)  
+Copyright: M. Westenberg (mw12554@hotmail.com)  
+
+All rights reserved. This program and the accompanying materials are made available under the terms 
+of the MIT License which accompanies this distribution, and is available at
+https://opensource.org/licenses/mit-license.php  
+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.
+
+Maintained by Maarten Westenberg (mw12554@hotmail.com)For information on installing libraries, see: http://www.arduino.cc/en/Guide/Libraries
+
+# Libraries Needed
+
+These are the libraries you need for compiling the Gateway:
+
+- ArduinoJson (version 1.51)				-> ArduinoJson.h
+- ESP8266 Oled library SH1106, (V 4.0.0 by Weinberg)	-> sh1106.h
+- ESP8266 Oled library SSD1306 (V 4.0.0 by Weinberg)	-> ssd1306.h
+- WifiManager (Version 0.12.0 by Tzapu)			-> WiFiManager.h
+
+
+Some Libraries are not installed in the Arduino directory, but in the sketch directory:
+- aes
+- ESP8266-Oled_Driver_for_SSD1306_display
+- gBase64
+- Streaming
+- Time
+- WiFiEsp
+
+Libraries are specified in the ESP-sc-gway.ino file as include files.
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Streaming/Examples/streaming_example/streaming_example.pde b/ESP-1ch-Gateway-v5.0-master/libraries/Streaming/Examples/streaming_example/streaming_example.pde
new file mode 100644
index 0000000000000000000000000000000000000000..886dedcafb72b532605ff5ea6715d3299be85a79
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Streaming/Examples/streaming_example/streaming_example.pde
@@ -0,0 +1,22 @@
+#include <Streaming.h>
+
+void setup()
+{
+  Serial.begin(9600);
+  int lettera = 'A';
+  int month = 4, day = 17, year = 2009;
+  
+  Serial << "This is an example of the new streaming" << endl;
+  Serial << "library.  This allows you to print variables" << endl;
+  Serial << "and strings without having to type line after" << endl;
+  Serial << "line of Serial.print() calls.  Examples: " << endl;
+  
+  Serial << "A is " << lettera << "." << endl;
+  Serial << "The current date is " << day << "-" << month << "-" << year << "." << endl;
+  
+  Serial << "You can use modifiers too, for example:" << endl;
+  Serial << _BYTE(lettera) << " is " << _HEX(lettera) << " in hex. " << endl;
+}
+
+void loop()
+{}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Streaming/Streaming.h b/ESP-1ch-Gateway-v5.0-master/libraries/Streaming/Streaming.h
new file mode 100644
index 0000000000000000000000000000000000000000..005baef412c4362c39c31189cea549918adb5636
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Streaming/Streaming.h
@@ -0,0 +1,105 @@
+/*
+Streaming.h - Arduino library for supporting the << streaming operator
+Copyright (c) 2010-2012 Mikal Hart.  All rights reserved.
+
+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 ARDUINO_STREAMING
+#define ARDUINO_STREAMING
+
+#if defined(ARDUINO) && ARDUINO >= 100
+#include "Arduino.h"
+#else
+#include "WProgram.h"
+#endif
+
+#define STREAMING_LIBRARY_VERSION 5
+
+// Generic template
+template<class T> 
+inline Print &operator <<(Print &stream, T arg) 
+{ stream.print(arg); return stream; }
+
+struct _BASED 
+{ 
+  long val; 
+  int base;
+  _BASED(long v, int b): val(v), base(b) 
+  {}
+};
+
+#if ARDUINO >= 100
+
+struct _BYTE_CODE
+{
+	byte val;
+	_BYTE_CODE(byte v) : val(v)
+	{}
+};
+#define _BYTE(a)    _BYTE_CODE(a)
+
+inline Print &operator <<(Print &obj, const _BYTE_CODE &arg)
+{ obj.write(arg.val); return obj; } 
+
+#else
+
+#define _BYTE(a)    _BASED(a, BYTE)
+
+#endif
+
+#define _HEX(a)     _BASED(a, HEX)
+#define _DEC(a)     _BASED(a, DEC)
+#define _OCT(a)     _BASED(a, OCT)
+#define _BIN(a)     _BASED(a, BIN)
+
+// Specialization for class _BASED
+// Thanks to Arduino forum user Ben Combee who suggested this 
+// clever technique to allow for expressions like
+//   Serial << _HEX(a);
+
+inline Print &operator <<(Print &obj, const _BASED &arg)
+{ obj.print(arg.val, arg.base); return obj; } 
+
+#if ARDUINO >= 18
+// Specialization for class _FLOAT
+// Thanks to Michael Margolis for suggesting a way
+// to accommodate Arduino 0018's floating point precision
+// feature like this:
+//   Serial << _FLOAT(gps_latitude, 6); // 6 digits of precision
+
+struct _FLOAT
+{
+  float val;
+  int digits;
+  _FLOAT(double v, int d): val(v), digits(d)
+  {}
+};
+
+inline Print &operator <<(Print &obj, const _FLOAT &arg)
+{ obj.print(arg.val, arg.digits); return obj; }
+#endif
+
+// Specialization for enum _EndLineCode
+// Thanks to Arduino forum user Paul V. who suggested this
+// clever technique to allow for expressions like
+//   Serial << "Hello!" << endl;
+
+enum _EndLineCode { endl };
+
+inline Print &operator <<(Print &obj, _EndLineCode arg) 
+{ obj.println(); return obj; }
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Streaming/keywords.txt b/ESP-1ch-Gateway-v5.0-master/libraries/Streaming/keywords.txt
new file mode 100644
index 0000000000000000000000000000000000000000..bd36e000947708d5baa39e7fe2671a6c827bc66a
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Streaming/keywords.txt
@@ -0,0 +1,25 @@
+#######################################
+# Syntax Coloring Map for Streaming
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+Streaming	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+_HEX	KEYWORD2
+_DEC	KEYWORD2
+_OCT	KEYWORD2
+_BIN	KEYWORD2
+_BYTE	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
+endl	LITERAL1
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/DateStrings.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/Time/DateStrings.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..489bb150c12d8cda16d47f0788f83620fb9d7c2a
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/DateStrings.cpp
@@ -0,0 +1,97 @@
+/* DateStrings.cpp
+ * Definitions for date strings for use with the Time library
+ *
+ * Updated for Arduino 1.5.7 18 July 2014
+ *
+ * No memory is consumed in the sketch if your code does not call any of the string methods
+ * You can change the text of the strings, make sure the short strings are each exactly 3 characters 
+ * the long strings can be any length up to the constant dt_MAX_STRING_LEN defined in Time.h
+ * 
+ */
+
+#if defined(__AVR__)
+#include <avr/pgmspace.h>
+#else
+// for compatiblity with Arduino Due and Teensy 3.0 and maybe others?
+#define PROGMEM
+#define PGM_P  const char *
+#define pgm_read_byte(addr) (*(const unsigned char *)(addr))
+#define pgm_read_word(addr) (*(const unsigned char **)(addr))
+#define strcpy_P(dest, src) strcpy((dest), (src))
+#endif
+#include <string.h> // for strcpy_P or strcpy
+#include "Time.h"
+ 
+// the short strings for each day or month must be exactly dt_SHORT_STR_LEN
+#define dt_SHORT_STR_LEN  3 // the length of short strings
+
+static char buffer[dt_MAX_STRING_LEN+1];  // must be big enough for longest string and the terminating null
+
+const char monthStr0[] PROGMEM = "";
+const char monthStr1[] PROGMEM = "January";
+const char monthStr2[] PROGMEM = "February";
+const char monthStr3[] PROGMEM = "March";
+const char monthStr4[] PROGMEM = "April";
+const char monthStr5[] PROGMEM = "May";
+const char monthStr6[] PROGMEM = "June";
+const char monthStr7[] PROGMEM = "July";
+const char monthStr8[] PROGMEM = "August";
+const char monthStr9[] PROGMEM = "September";
+const char monthStr10[] PROGMEM = "October";
+const char monthStr11[] PROGMEM = "November";
+const char monthStr12[] PROGMEM = "December";
+
+const PROGMEM char * const PROGMEM monthNames_P[] =
+{
+    monthStr0,monthStr1,monthStr2,monthStr3,monthStr4,monthStr5,monthStr6,
+    monthStr7,monthStr8,monthStr9,monthStr10,monthStr11,monthStr12
+};
+
+const char monthShortNames_P[] PROGMEM = "ErrJanFebMarAprMayJunJulAugSepOctNovDec";
+
+const char dayStr0[] PROGMEM = "Err";
+const char dayStr1[] PROGMEM = "Sunday";
+const char dayStr2[] PROGMEM = "Monday";
+const char dayStr3[] PROGMEM = "Tuesday";
+const char dayStr4[] PROGMEM = "Wednesday";
+const char dayStr5[] PROGMEM = "Thursday";
+const char dayStr6[] PROGMEM = "Friday";
+const char dayStr7[] PROGMEM = "Saturday";
+
+const PROGMEM char * const PROGMEM dayNames_P[] =
+{
+   dayStr0,dayStr1,dayStr2,dayStr3,dayStr4,dayStr5,dayStr6,dayStr7
+};
+
+const char dayShortNames_P[] PROGMEM = "ErrSunMonTueWedThuFriSat";
+
+/* functions to return date strings */
+
+char* monthStr(uint8_t month)
+{
+    strcpy_P(buffer, (PGM_P)pgm_read_word(&(monthNames_P[month])));
+    return buffer;
+}
+
+char* monthShortStr(uint8_t month)
+{
+   for (int i=0; i < dt_SHORT_STR_LEN; i++)      
+      buffer[i] = pgm_read_byte(&(monthShortNames_P[i+ (month*dt_SHORT_STR_LEN)]));  
+   buffer[dt_SHORT_STR_LEN] = 0;
+   return buffer;
+}
+
+char* dayStr(uint8_t day) 
+{
+   strcpy_P(buffer, (PGM_P)pgm_read_word(&(dayNames_P[day])));
+   return buffer;
+}
+
+char* dayShortStr(uint8_t day) 
+{
+   uint8_t index = day*dt_SHORT_STR_LEN;
+   for (int i=0; i < dt_SHORT_STR_LEN; i++)      
+      buffer[i] = pgm_read_byte(&(dayShortNames_P[index + i]));  
+   buffer[dt_SHORT_STR_LEN] = 0; 
+   return buffer;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/Readme.txt b/ESP-1ch-Gateway-v5.0-master/libraries/Time/Readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..67b148ecd07e1b8d3bb5306df46786d449e959ab
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/Readme.txt
@@ -0,0 +1,131 @@
+Readme file for Arduino Time Library
+
+Time is a library that provides timekeeping functionality for Arduino.
+
+The code is derived from the Playground DateTime library but is updated
+to provide an API that is more flexable and easier to use.
+
+A primary goal was to enable date and time functionality that can be used with
+a variety of external time sources with minimum differences required in sketch logic.
+
+Example sketches illustrate how similar sketch code can be used with: a Real Time Clock,
+internet NTP time service, GPS time data, and Serial time messages from a computer
+for time synchronization.
+
+The functions available in the library include:
+
+hour();            // the hour now  (0-23)
+minute();          // the minute now (0-59)          
+second();          // the second now (0-59) 
+day();             // the day now (1-31)
+weekday();         // day of the week, Sunday is day 0 
+month();           // the month now (1-12)
+year();            // the full four digit year: (2009, 2010 etc) 
+
+there are also functions to return the hour in 12 hour format
+hourFormat12();    // the hour now in 12 hour format
+isAM();            // returns true if time now is AM 
+isPM();            // returns true if time now is PM
+
+now();             // returns the current time as seconds since Jan 1 1970 
+
+The time and date functions can take an optional parameter for the time. This prevents
+errors if the time rolls over between elements. For example, if a new minute begins
+between getting the minute and second, the values will be inconsistent. Using the 
+following functions eliminates this probglem 
+  time_t t = now(); // store the current time in time variable t 
+  hour(t);          // returns the hour for the given time t
+  minute(t);        // returns the minute for the given time t
+  second(t);        // returns the second for the given time t 
+  day(t);           // the day for the given time t 
+  weekday(t);       // day of the week for the given time t  
+  month(t);         // the month for the given time t 
+  year(t);          // the year for the given time t  
+  
+  
+Functions for managing the timer services are:  
+setTime(t);             // set the system time to the give time t
+setTime(hr,min,sec,day,mnth,yr); // alternative to above, yr is 2 or 4 digit yr (2010 or 10 sets year to 2010)
+adjustTime(adjustment); // adjust system time by adding the adjustment value
+
+timeStatus();       // indicates if time has been set and recently synchronized
+                    // returns one of the following enumerations:
+    timeNotSet      // the time has never been set, the clock started at Jan 1 1970
+    timeNeedsSync   // the time had been set but a sync attempt did not succeed
+    timeSet         // the time is set and is synced
+Time and Date values are not valid if the status is timeNotSet. Otherwise values can be used but 
+the returned time may have drifted if the status is timeNeedsSync. 	
+
+setSyncProvider(getTimeFunction);  // set the external time provider
+setSyncInterval(interval);         // set the number of seconds between re-sync
+
+
+There are many convenience macros in the time.h file for time constants and conversion of time units.
+
+To use the library, copy the download to the Library directory.
+
+The Time directory contains the Time library and some example sketches
+illustrating how the library can be used with various time sources:
+
+- TimeSerial.pde shows Arduino as a clock without external hardware.
+  It is synchronized by time messages sent over the serial port.
+  A companion Processing sketch will automatically provide these messages
+  if it is running and connected to the Arduino serial port. 
+
+- TimeSerialDateStrings.pde adds day and month name strings to the sketch above
+  Short (3 character) and long strings are available to print the days of 
+  the week and names of the months. 
+  
+- TimeRTC uses a DS1307 real time clock to provide time synchronization.
+  A basic RTC library named DS1307RTC is included in the download.
+  To run this sketch the DS1307RTC library must be installed.
+
+- TimeRTCSet is similar to the above and adds the ability to set the Real Time Clock 
+
+- TimeRTCLog demonstrates how to calculate the difference between times. 
+  It is a vary simple logger application that monitors events on digtial pins
+  and prints (to the serial port) the time of an event and the time period since the previous event.
+  
+- TimeNTP uses the Arduino Ethernet shield to access time using the internet NTP time service.
+  The NTP protocol uses UDP and the UdpBytewise library is required, see:
+  http://bitbucket.org/bjoern/arduino_osc/src/14667490521f/libraries/Ethernet/
+
+- TimeGPS gets time from a GPS
+  This requires the TinyGPS library from Mikal Hart:
+  http://arduiniana.org/libraries/TinyGPS
+
+Differences between this code and the playground DateTime library
+although the Time library is based on the DateTime codebase, the API has changed.
+Changes in the Time library API:
+- time elements are functions returning int (they are variables in DateTime)
+- Years start from 1970 
+- days of the week and months start from 1 (they start from 0 in DateTime)
+- DateStrings do not require a seperate library
+- time elements can be accessed non-atomically (in DateTime they are always atomic)
+- function added to automatically sync time with extrnal source
+- localTime and maketime parameters changed, localTime renamed to breakTime
+ 
+Technical notes:
+
+Internal system time is based on the standard Unix time_t.
+The value is the number of seconds since Jan 1 1970.
+System time begins at zero when the sketch starts.
+  
+The internal time can be automatically synchronized at regular intervals to an external time source.
+This is enabled by calling the setSyncProvider(provider) function - the provider argument is
+the address of a function that returns the current time as a time_t.
+See the sketches in the examples directory for usage.
+
+The default interval for re-syncing the time is 5 minutes but can be changed by calling the 
+setSyncInterval( interval) method to set the number of seconds between re-sync attempts.
+
+The Time library defines a structure for holding time elements that is a compact version of the  C tm structure.
+All the members of the Arduino tm structure are bytes and the year is offset from 1970.
+Convenience macros provide conversion to and from the Arduino format.
+
+Low level functions to convert between system time and individual time elements are provided:                    
+  breakTime( time, &tm);  // break time_t into elements stored in tm struct
+  makeTime( &tm);  // return time_t  from elements stored in tm struct 
+
+The DS1307RTC library included in the download provides an example of how a time provider
+can use the low level functions to interface with the Time library.
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/Time.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/Time/Time.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b12baece44fc98d9ad597a0650961499aab9deaa
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/Time.cpp
@@ -0,0 +1,321 @@
+/*
+  time.c - low level time and date functions
+  Copyright (c) Michael Margolis 2009-2014
+
+  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
+  
+  1.0  6  Jan 2010 - initial release
+  1.1  12 Feb 2010 - fixed leap year calculation error
+  1.2  1  Nov 2010 - fixed setTime bug (thanks to Korman for this)
+  1.3  24 Mar 2012 - many edits by Paul Stoffregen: fixed timeStatus() to update
+                     status, updated examples for Arduino 1.0, fixed ARM
+                     compatibility issues, added TimeArduinoDue and TimeTeensy3
+                     examples, add error checking and messages to RTC examples,
+                     add examples to DS1307RTC library.
+  1.4  5  Sep 2014 - compatibility with Arduino 1.5.7
+*/
+
+#if ARDUINO >= 100
+#include <Arduino.h> 
+#else
+#include <WProgram.h> 
+#endif
+
+#include "Time.h"
+
+static tmElements_t tm;          // a cache of time elements
+static time_t cacheTime;   // the time the cache was updated
+static uint32_t syncInterval = 300;  // time sync will be attempted after this many seconds
+
+void refreshCache(time_t t) {
+  if (t != cacheTime) {
+    breakTime(t, tm); 
+    cacheTime = t; 
+  }
+}
+
+int hour() { // the hour now 
+  return hour(now()); 
+}
+
+int hour(time_t t) { // the hour for the given time
+  refreshCache(t);
+  return tm.Hour;  
+}
+
+int hourFormat12() { // the hour now in 12 hour format
+  return hourFormat12(now()); 
+}
+
+int hourFormat12(time_t t) { // the hour for the given time in 12 hour format
+  refreshCache(t);
+  if( tm.Hour == 0 )
+    return 12; // 12 midnight
+  else if( tm.Hour  > 12)
+    return tm.Hour - 12 ;
+  else
+    return tm.Hour ;
+}
+
+uint8_t isAM() { // returns true if time now is AM
+  return !isPM(now()); 
+}
+
+uint8_t isAM(time_t t) { // returns true if given time is AM
+  return !isPM(t);  
+}
+
+uint8_t isPM() { // returns true if PM
+  return isPM(now()); 
+}
+
+uint8_t isPM(time_t t) { // returns true if PM
+  return (hour(t) >= 12); 
+}
+
+int minute() {
+  return minute(now()); 
+}
+
+int minute(time_t t) { // the minute for the given time
+  refreshCache(t);
+  return tm.Minute;  
+}
+
+int second() {
+  return second(now()); 
+}
+
+int second(time_t t) {  // the second for the given time
+  refreshCache(t);
+  return tm.Second;
+}
+
+int day(){
+  return(day(now())); 
+}
+
+int day(time_t t) { // the day for the given time (0-6)
+  refreshCache(t);
+  return tm.Day;
+}
+
+int weekday() {   // Sunday is day 1
+  return  weekday(now()); 
+}
+
+int weekday(time_t t) {
+  refreshCache(t);
+  return tm.Wday;
+}
+   
+int month(){
+  return month(now()); 
+}
+
+int month(time_t t) {  // the month for the given time
+  refreshCache(t);
+  return tm.Month;
+}
+
+int year() {  // as in Processing, the full four digit year: (2009, 2010 etc) 
+  return year(now()); 
+}
+
+int year(time_t t) { // the year for the given time
+  refreshCache(t);
+  return tmYearToCalendar(tm.Year);
+}
+
+/*============================================================================*/	
+/* functions to convert to and from system time */
+/* These are for interfacing with time serivces and are not normally needed in a sketch */
+
+// leap year calulator expects year argument as years offset from 1970
+#define LEAP_YEAR(Y)     ( ((1970+Y)>0) && !((1970+Y)%4) && ( ((1970+Y)%100) || !((1970+Y)%400) ) )
+
+static  const uint8_t monthDays[]={31,28,31,30,31,30,31,31,30,31,30,31}; // API starts months from 1, this array starts from 0
+ 
+void breakTime(time_t timeInput, tmElements_t &tm){
+// break the given time_t into time components
+// this is a more compact version of the C library localtime function
+// note that year is offset from 1970 !!!
+
+  uint8_t year;
+  uint8_t month, monthLength;
+  uint32_t time;
+  unsigned long days;
+
+  time = (uint32_t)timeInput;
+  tm.Second = time % 60;
+  time /= 60; // now it is minutes
+  tm.Minute = time % 60;
+  time /= 60; // now it is hours
+  tm.Hour = time % 24;
+  time /= 24; // now it is days
+  tm.Wday = ((time + 4) % 7) + 1;  // Sunday is day 1 
+  
+  year = 0;  
+  days = 0;
+  while((unsigned)(days += (LEAP_YEAR(year) ? 366 : 365)) <= time) {
+    year++;
+  }
+  tm.Year = year; // year is offset from 1970 
+  
+  days -= LEAP_YEAR(year) ? 366 : 365;
+  time  -= days; // now it is days in this year, starting at 0
+  
+  days=0;
+  month=0;
+  monthLength=0;
+  for (month=0; month<12; month++) {
+    if (month==1) { // february
+      if (LEAP_YEAR(year)) {
+        monthLength=29;
+      } else {
+        monthLength=28;
+      }
+    } else {
+      monthLength = monthDays[month];
+    }
+    
+    if (time >= monthLength) {
+      time -= monthLength;
+    } else {
+        break;
+    }
+  }
+  tm.Month = month + 1;  // jan is month 1  
+  tm.Day = time + 1;     // day of month
+}
+
+time_t makeTime(tmElements_t &tm){   
+// assemble time elements into time_t 
+// note year argument is offset from 1970 (see macros in time.h to convert to other formats)
+// previous version used full four digit year (or digits since 2000),i.e. 2009 was 2009 or 9
+  
+  int i;
+  uint32_t seconds;
+
+  // seconds from 1970 till 1 jan 00:00:00 of the given year
+  seconds= tm.Year*(SECS_PER_DAY * 365);
+  for (i = 0; i < tm.Year; i++) {
+    if (LEAP_YEAR(i)) {
+      seconds +=  SECS_PER_DAY;   // add extra days for leap years
+    }
+  }
+  
+  // add days for this year, months start from 1
+  for (i = 1; i < tm.Month; i++) {
+    if ( (i == 2) && LEAP_YEAR(tm.Year)) { 
+      seconds += SECS_PER_DAY * 29;
+    } else {
+      seconds += SECS_PER_DAY * monthDays[i-1];  //monthDay array starts from 0
+    }
+  }
+  seconds+= (tm.Day-1) * SECS_PER_DAY;
+  seconds+= tm.Hour * SECS_PER_HOUR;
+  seconds+= tm.Minute * SECS_PER_MIN;
+  seconds+= tm.Second;
+  return (time_t)seconds; 
+}
+/*=====================================================*/	
+/* Low level system time functions  */
+
+static uint32_t sysTime = 0;
+static uint32_t prevMillis = 0;
+static uint32_t nextSyncTime = 0;
+static timeStatus_t Status = timeNotSet;
+
+getExternalTime getTimePtr;  // pointer to external sync function
+//setExternalTime setTimePtr; // not used in this version
+
+#ifdef TIME_DRIFT_INFO   // define this to get drift data
+time_t sysUnsyncedTime = 0; // the time sysTime unadjusted by sync  
+#endif
+
+
+time_t now() {
+	// calculate number of seconds passed since last call to now()
+  while (millis() - prevMillis >= 1000) {
+		// millis() and prevMillis are both unsigned ints thus the subtraction will always be the absolute value of the difference
+    sysTime++;
+    prevMillis += 1000;	
+#ifdef TIME_DRIFT_INFO
+    sysUnsyncedTime++; // this can be compared to the synced time to measure long term drift     
+#endif
+  }
+  if (nextSyncTime <= sysTime) {
+    if (getTimePtr != 0) {
+      time_t t = getTimePtr();
+      if (t != 0) {
+        setTime(t);
+      } else {
+        nextSyncTime = sysTime + syncInterval;
+        Status = (Status == timeNotSet) ?  timeNotSet : timeNeedsSync;
+      }
+    }
+  }  
+  return (time_t)sysTime;
+}
+
+void setTime(time_t t) { 
+#ifdef TIME_DRIFT_INFO
+ if(sysUnsyncedTime == 0) 
+   sysUnsyncedTime = t;   // store the time of the first call to set a valid Time   
+#endif
+
+  sysTime = (uint32_t)t;  
+  nextSyncTime = (uint32_t)t + syncInterval;
+  Status = timeSet;
+  prevMillis = millis();  // restart counting from now (thanks to Korman for this fix)
+} 
+
+void setTime(int hr,int min,int sec,int dy, int mnth, int yr){
+ // year can be given as full four digit year or two digts (2010 or 10 for 2010);  
+ //it is converted to years since 1970
+  if( yr > 99)
+      yr = yr - 1970;
+  else
+      yr += 30;  
+  tm.Year = yr;
+  tm.Month = mnth;
+  tm.Day = dy;
+  tm.Hour = hr;
+  tm.Minute = min;
+  tm.Second = sec;
+  setTime(makeTime(tm));
+}
+
+void adjustTime(long adjustment) {
+  sysTime += adjustment;
+}
+
+// indicates if time has been set and recently synchronized
+timeStatus_t timeStatus() {
+  now(); // required to actually update the status
+  return Status;
+}
+
+void setSyncProvider( getExternalTime getTimeFunction){
+  getTimePtr = getTimeFunction;  
+  nextSyncTime = sysTime;
+  now(); // this will sync the clock
+}
+
+void setSyncInterval(time_t interval){ // set the number of seconds between re-sync
+  syncInterval = (uint32_t)interval;
+  nextSyncTime = sysTime + syncInterval;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/Time.h b/ESP-1ch-Gateway-v5.0-master/libraries/Time/Time.h
new file mode 100644
index 0000000000000000000000000000000000000000..a79b0801ed2f4dcf3098112cd69bc219662265dc
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/Time.h
@@ -0,0 +1 @@
+#include "TimeLib.h"
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/TimeLib.h b/ESP-1ch-Gateway-v5.0-master/libraries/Time/TimeLib.h
new file mode 100644
index 0000000000000000000000000000000000000000..61519f7dcbec3027ba02d30aeef73d5ab94c425b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/TimeLib.h
@@ -0,0 +1,144 @@
+/*
+  time.h - low level time and date functions
+*/
+
+/*
+  July 3 2011 - fixed elapsedSecsThisWeek macro (thanks Vincent Valdy for this)
+              - fixed  daysToTime_t macro (thanks maniacbug)
+*/     
+
+#ifndef _Time_h
+#ifdef __cplusplus
+#define _Time_h
+
+#include <inttypes.h>
+#ifndef __AVR__
+#include <sys/types.h> // for __time_t_defined, but avr libc lacks sys/types.h
+#endif
+
+
+#if !defined(__time_t_defined) // avoid conflict with newlib or other posix libc
+typedef unsigned long time_t;
+#endif
+
+
+// This ugly hack allows us to define C++ overloaded functions, when included
+// from within an extern "C", as newlib's sys/stat.h does.  Actually it is
+// intended to include "time.h" from the C library (on ARM, but AVR does not
+// have that file at all).  On Mac and Windows, the compiler will find this
+// "Time.h" instead of the C library "time.h", so we may cause other weird
+// and unpredictable effects by conflicting with the C library header "time.h",
+// but at least this hack lets us define C++ functions as intended.  Hopefully
+// nothing too terrible will result from overriding the C library header?!
+extern "C++" {
+typedef enum {timeNotSet, timeNeedsSync, timeSet
+}  timeStatus_t ;
+
+typedef enum {
+    dowInvalid, dowSunday, dowMonday, dowTuesday, dowWednesday, dowThursday, dowFriday, dowSaturday
+} timeDayOfWeek_t;
+
+typedef enum {
+    tmSecond, tmMinute, tmHour, tmWday, tmDay,tmMonth, tmYear, tmNbrFields
+} tmByteFields;	   
+
+typedef struct  { 
+  uint8_t Second; 
+  uint8_t Minute; 
+  uint8_t Hour; 
+  uint8_t Wday;   // day of week, sunday is day 1
+  uint8_t Day;
+  uint8_t Month; 
+  uint8_t Year;   // offset from 1970; 
+} 	tmElements_t, TimeElements, *tmElementsPtr_t;
+
+//convenience macros to convert to and from tm years 
+#define  tmYearToCalendar(Y) ((Y) + 1970)  // full four digit year 
+#define  CalendarYrToTm(Y)   ((Y) - 1970)
+#define  tmYearToY2k(Y)      ((Y) - 30)    // offset is from 2000
+#define  y2kYearToTm(Y)      ((Y) + 30)   
+
+typedef time_t(*getExternalTime)();
+//typedef void  (*setExternalTime)(const time_t); // not used in this version
+
+
+/*==============================================================================*/
+/* Useful Constants */
+#define SECS_PER_MIN  (60UL)
+#define SECS_PER_HOUR (3600UL)
+#define SECS_PER_DAY  (SECS_PER_HOUR * 24UL)
+#define DAYS_PER_WEEK (7UL)
+#define SECS_PER_WEEK (SECS_PER_DAY * DAYS_PER_WEEK)
+#define SECS_PER_YEAR (SECS_PER_WEEK * 52UL)
+#define SECS_YR_2000  (946684800UL) // the time at the start of y2k
+ 
+/* Useful Macros for getting elapsed time */
+#define numberOfSeconds(_time_) (_time_ % SECS_PER_MIN)  
+#define numberOfMinutes(_time_) ((_time_ / SECS_PER_MIN) % SECS_PER_MIN) 
+#define numberOfHours(_time_) (( _time_% SECS_PER_DAY) / SECS_PER_HOUR)
+#define dayOfWeek(_time_)  ((( _time_ / SECS_PER_DAY + 4)  % DAYS_PER_WEEK)+1) // 1 = Sunday
+#define elapsedDays(_time_) ( _time_ / SECS_PER_DAY)  // this is number of days since Jan 1 1970
+#define elapsedSecsToday(_time_)  (_time_ % SECS_PER_DAY)   // the number of seconds since last midnight 
+// The following macros are used in calculating alarms and assume the clock is set to a date later than Jan 1 1971
+// Always set the correct time before settting alarms
+#define previousMidnight(_time_) (( _time_ / SECS_PER_DAY) * SECS_PER_DAY)  // time at the start of the given day
+#define nextMidnight(_time_) ( previousMidnight(_time_)  + SECS_PER_DAY )   // time at the end of the given day 
+#define elapsedSecsThisWeek(_time_)  (elapsedSecsToday(_time_) +  ((dayOfWeek(_time_)-1) * SECS_PER_DAY) )   // note that week starts on day 1
+#define previousSunday(_time_)  (_time_ - elapsedSecsThisWeek(_time_))      // time at the start of the week for the given time
+#define nextSunday(_time_) ( previousSunday(_time_)+SECS_PER_WEEK)          // time at the end of the week for the given time
+
+
+/* Useful Macros for converting elapsed time to a time_t */
+#define minutesToTime_t ((M)) ( (M) * SECS_PER_MIN)  
+#define hoursToTime_t   ((H)) ( (H) * SECS_PER_HOUR)  
+#define daysToTime_t    ((D)) ( (D) * SECS_PER_DAY) // fixed on Jul 22 2011
+#define weeksToTime_t   ((W)) ( (W) * SECS_PER_WEEK)   
+
+/*============================================================================*/
+/*  time and date functions   */
+int     hour();            // the hour now 
+int     hour(time_t t);    // the hour for the given time
+int     hourFormat12();    // the hour now in 12 hour format
+int     hourFormat12(time_t t); // the hour for the given time in 12 hour format
+uint8_t isAM();            // returns true if time now is AM
+uint8_t isAM(time_t t);    // returns true the given time is AM
+uint8_t isPM();            // returns true if time now is PM
+uint8_t isPM(time_t t);    // returns true the given time is PM
+int     minute();          // the minute now 
+int     minute(time_t t);  // the minute for the given time
+int     second();          // the second now 
+int     second(time_t t);  // the second for the given time
+int     day();             // the day now 
+int     day(time_t t);     // the day for the given time
+int     weekday();         // the weekday now (Sunday is day 1) 
+int     weekday(time_t t); // the weekday for the given time 
+int     month();           // the month now  (Jan is month 1)
+int     month(time_t t);   // the month for the given time
+int     year();            // the full four digit year: (2009, 2010 etc) 
+int     year(time_t t);    // the year for the given time
+
+time_t now();              // return the current time as seconds since Jan 1 1970 
+void    setTime(time_t t);
+void    setTime(int hr,int min,int sec,int day, int month, int yr);
+void    adjustTime(long adjustment);
+
+/* date strings */ 
+#define dt_MAX_STRING_LEN 9 // length of longest date string (excluding terminating null)
+char* monthStr(uint8_t month);
+char* dayStr(uint8_t day);
+char* monthShortStr(uint8_t month);
+char* dayShortStr(uint8_t day);
+	
+/* time sync functions	*/
+timeStatus_t timeStatus(); // indicates if time has been set and recently synchronized
+void    setSyncProvider( getExternalTime getTimeFunction); // identify the external time provider
+void    setSyncInterval(time_t interval); // set the number of seconds between re-sync
+
+/* low level functions to convert to and from system time                     */
+void breakTime(time_t time, tmElements_t &tm);  // break time_t into elements
+time_t makeTime(tmElements_t &tm);  // convert time elements into time_t
+
+} // extern "C++"
+#endif // __cplusplus
+#endif /* _Time_h */
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/Processing/SyncArduinoClock/SyncArduinoClock.pde b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/Processing/SyncArduinoClock/SyncArduinoClock.pde
new file mode 100644
index 0000000000000000000000000000000000000000..4313be33c2a4ccdbc2ad904ca41ab35a3e3fee1b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/Processing/SyncArduinoClock/SyncArduinoClock.pde
@@ -0,0 +1,78 @@
+/**
+ * SyncArduinoClock. 
+ *
+ * portIndex must be set to the port connected to the Arduino
+ * 
+ * The current time is sent in response to request message from Arduino 
+ * or by clicking the display window 
+ *
+ * The time message is 11 ASCII text characters; a header (the letter 'T')
+ * followed by the ten digit system time (unix time)
+ */
+ 
+
+import processing.serial.*;
+import java.util.Date;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+public static final short portIndex = 0;  // select the com port, 0 is the first port
+public static final String TIME_HEADER = "T"; //header for arduino serial time message 
+public static final char TIME_REQUEST = 7;  // ASCII bell character 
+public static final char LF = 10;     // ASCII linefeed
+public static final char CR = 13;     // ASCII linefeed
+Serial myPort;     // Create object from Serial class
+
+void setup() {  
+  size(200, 200);
+  println(Serial.list());
+  println(" Connecting to -> " + Serial.list()[portIndex]);
+  myPort = new Serial(this,Serial.list()[portIndex], 9600);
+  println(getTimeNow());
+}
+
+void draw()
+{
+  textSize(20);
+  textAlign(CENTER);
+  fill(0);
+  text("Click to send\nTime Sync", 0, 75, 200, 175);
+  if ( myPort.available() > 0) {  // If data is available,
+    char val = char(myPort.read());         // read it and store it in val
+    if(val == TIME_REQUEST){
+       long t = getTimeNow();
+       sendTimeMessage(TIME_HEADER, t);   
+    }
+    else
+    { 
+       if(val == LF)
+           ; //igonore
+       else if(val == CR)           
+         println();
+       else  
+         print(val); // echo everying but time request
+    }
+  }  
+}
+
+void mousePressed() {  
+  sendTimeMessage( TIME_HEADER, getTimeNow());   
+}
+
+
+void sendTimeMessage(String header, long time) {  
+  String timeStr = String.valueOf(time);  
+  myPort.write(header);  // send header and time to arduino
+  myPort.write(timeStr); 
+  myPort.write('\n');  
+}
+
+long getTimeNow(){
+  // java time is in ms, we want secs    
+  Date d = new Date();
+  Calendar cal = new GregorianCalendar();
+  long current = d.getTime()/1000;
+  long timezone = cal.get(cal.ZONE_OFFSET)/1000;
+  long daylight = cal.get(cal.DST_OFFSET)/1000;
+  return current + timezone + daylight; 
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/Processing/SyncArduinoClock/readme.txt b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/Processing/SyncArduinoClock/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..da9721d7b9834bb20c0d938d05115b2eed14e536
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/Processing/SyncArduinoClock/readme.txt
@@ -0,0 +1,9 @@
+SyncArduinoClock is a Processing sketch that responds to Arduino requests for 
+time synchronization messages.
+
+The portIndex must be set the Serial port connected to Arduino.
+
+Download TimeSerial.pde onto Arduino and you should see the time 
+message displayed when you run SyncArduinoClock in Processing.
+The Arduino time is set from the time on your computer through the 
+Processing sketch. 
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeArduinoDue/TimeArduinoDue.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeArduinoDue/TimeArduinoDue.ino
new file mode 100644
index 0000000000000000000000000000000000000000..f0a9a95df2dcce7d7f1e8908c87f9a6ad9e407db
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeArduinoDue/TimeArduinoDue.ino
@@ -0,0 +1,71 @@
+/*
+ * TimeRTC.pde
+ * example code illustrating Time library with Real Time Clock.
+ *
+ * This example requires Markus Lange's Arduino Due RTC Library 
+ * https://github.com/MarkusLange/Arduino-Due-RTC-Library
+ */
+
+#include <TimeLib.h>
+#include <rtc_clock.h>
+
+// Select the Slowclock source
+//RTC_clock rtc_clock(RC);
+RTC_clock rtc_clock(XTAL);
+
+void setup()  {
+  Serial.begin(9600);
+  rtc_clock.init();
+  if (rtc_clock.date_already_set() == 0) {
+    // Unfortunately, the Arduino Due hardware does not seem to
+    // be designed to maintain the RTC clock state when the
+    // board resets.  Markus described it thusly: "Uhh the Due
+    // does reset with the NRSTB pin.  This resets the full chip
+    // with all backup regions including RTC, RTT and SC.  Only
+    // if the reset is done with the NRST pin will these regions
+    // stay with their old values."
+    rtc_clock.set_time(__TIME__);
+    rtc_clock.set_date(__DATE__);
+    // However, this might work on other unofficial SAM3X boards
+    // with different reset circuitry than Arduino Due?
+  }
+  setSyncProvider(getArduinoDueTime);
+  if(timeStatus()!= timeSet) 
+     Serial.println("Unable to sync with the RTC");
+  else
+     Serial.println("RTC has set the system time");      
+}
+
+time_t getArduinoDueTime()
+{
+  return rtc_clock.unixtime();
+}
+
+void loop()
+{
+   digitalClockDisplay();
+   delay(1000);
+}
+
+void digitalClockDisplay(){
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  Serial.print(month());
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+void printDigits(int digits){
+  // utility function for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeGPS/TimeGPS.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeGPS/TimeGPS.ino
new file mode 100644
index 0000000000000000000000000000000000000000..fea9698867b35abf43e35bdb4fb41d69a01f27dc
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeGPS/TimeGPS.ino
@@ -0,0 +1,87 @@
+/*
+ * TimeGPS.pde
+ * example code illustrating time synced from a GPS
+ * 
+ */
+
+#include <TimeLib.h>
+#include <TinyGPS.h>       // http://arduiniana.org/libraries/TinyGPS/
+#include <SoftwareSerial.h>
+// TinyGPS and SoftwareSerial libraries are the work of Mikal Hart
+
+SoftwareSerial SerialGPS = SoftwareSerial(10, 11);  // receive on pin 10
+TinyGPS gps; 
+
+// To use a hardware serial port, which is far more efficient than
+// SoftwareSerial, uncomment this line and remove SoftwareSerial
+//#define SerialGPS Serial1
+
+// Offset hours from gps time (UTC)
+const int offset = 1;   // Central European Time
+//const int offset = -5;  // Eastern Standard Time (USA)
+//const int offset = -4;  // Eastern Daylight Time (USA)
+//const int offset = -8;  // Pacific Standard Time (USA)
+//const int offset = -7;  // Pacific Daylight Time (USA)
+
+// Ideally, it should be possible to learn the time zone
+// based on the GPS position data.  However, that would
+// require a complex library, probably incorporating some
+// sort of database using Eric Muller's time zone shape
+// maps, at http://efele.net/maps/tz/
+
+time_t prevDisplay = 0; // when the digital clock was displayed
+
+void setup()
+{
+  Serial.begin(9600);
+  while (!Serial) ; // Needed for Leonardo only
+  SerialGPS.begin(4800);
+  Serial.println("Waiting for GPS time ... ");
+}
+
+void loop()
+{
+  while (SerialGPS.available()) {
+    if (gps.encode(SerialGPS.read())) { // process gps messages
+      // when TinyGPS reports new data...
+      unsigned long age;
+      int Year;
+      byte Month, Day, Hour, Minute, Second;
+      gps.crack_datetime(&Year, &Month, &Day, &Hour, &Minute, &Second, NULL, &age);
+      if (age < 500) {
+        // set the Time to the latest GPS reading
+        setTime(Hour, Minute, Second, Day, Month, Year);
+        adjustTime(offset * SECS_PER_HOUR);
+      }
+    }
+  }
+  if (timeStatus()!= timeNotSet) {
+    if (now() != prevDisplay) { //update the display only if the time has changed
+      prevDisplay = now();
+      digitalClockDisplay();  
+    }
+  }
+}
+
+void digitalClockDisplay(){
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  Serial.print(month());
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+void printDigits(int digits) {
+  // utility function for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeNTP/TimeNTP.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeNTP/TimeNTP.ino
new file mode 100644
index 0000000000000000000000000000000000000000..17a908f821b7f6978d98f84e5918a8d8af5345d8
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeNTP/TimeNTP.ino
@@ -0,0 +1,135 @@
+/*
+ * Time_NTP.pde
+ * Example showing time sync to NTP time source
+ *
+ * This sketch uses the Ethernet library
+ */
+ 
+#include <TimeLib.h>
+#include <Ethernet.h>
+#include <EthernetUdp.h>
+#include <SPI.h>
+
+byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; 
+// NTP Servers:
+IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
+// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov
+// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov
+
+
+const int timeZone = 1;     // Central European Time
+//const int timeZone = -5;  // Eastern Standard Time (USA)
+//const int timeZone = -4;  // Eastern Daylight Time (USA)
+//const int timeZone = -8;  // Pacific Standard Time (USA)
+//const int timeZone = -7;  // Pacific Daylight Time (USA)
+
+
+EthernetUDP Udp;
+unsigned int localPort = 8888;  // local port to listen for UDP packets
+
+void setup() 
+{
+  Serial.begin(9600);
+  while (!Serial) ; // Needed for Leonardo only
+  delay(250);
+  Serial.println("TimeNTP Example");
+  if (Ethernet.begin(mac) == 0) {
+    // no point in carrying on, so do nothing forevermore:
+    while (1) {
+      Serial.println("Failed to configure Ethernet using DHCP");
+      delay(10000);
+    }
+  }
+  Serial.print("IP number assigned by DHCP is ");
+  Serial.println(Ethernet.localIP());
+  Udp.begin(localPort);
+  Serial.println("waiting for sync");
+  setSyncProvider(getNtpTime);
+}
+
+time_t prevDisplay = 0; // when the digital clock was displayed
+
+void loop()
+{  
+  if (timeStatus() != timeNotSet) {
+    if (now() != prevDisplay) { //update the display only if time has changed
+      prevDisplay = now();
+      digitalClockDisplay();  
+    }
+  }
+}
+
+void digitalClockDisplay(){
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  Serial.print(month());
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+void printDigits(int digits){
+  // utility for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
+/*-------- NTP code ----------*/
+
+const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
+byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
+
+time_t getNtpTime()
+{
+  while (Udp.parsePacket() > 0) ; // discard any previously received packets
+  Serial.println("Transmit NTP Request");
+  sendNTPpacket(timeServer);
+  uint32_t beginWait = millis();
+  while (millis() - beginWait < 1500) {
+    int size = Udp.parsePacket();
+    if (size >= NTP_PACKET_SIZE) {
+      Serial.println("Receive NTP Response");
+      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
+      unsigned long secsSince1900;
+      // convert four bytes starting at location 40 to a long integer
+      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
+      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
+      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
+      secsSince1900 |= (unsigned long)packetBuffer[43];
+      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
+    }
+  }
+  Serial.println("No NTP Response :-(");
+  return 0; // return 0 if unable to get the time
+}
+
+// send an NTP request to the time server at the given address
+void sendNTPpacket(IPAddress &address)
+{
+  // set all bytes in the buffer to 0
+  memset(packetBuffer, 0, NTP_PACKET_SIZE);
+  // Initialize values needed to form NTP request
+  // (see URL above for details on the packets)
+  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
+  packetBuffer[1] = 0;     // Stratum, or type of clock
+  packetBuffer[2] = 6;     // Polling Interval
+  packetBuffer[3] = 0xEC;  // Peer Clock Precision
+  // 8 bytes of zero for Root Delay & Root Dispersion
+  packetBuffer[12]  = 49;
+  packetBuffer[13]  = 0x4E;
+  packetBuffer[14]  = 49;
+  packetBuffer[15]  = 52;
+  // all NTP fields have been given values, now
+  // you can send a packet requesting a timestamp:                 
+  Udp.beginPacket(address, 123); //NTP requests are to port 123
+  Udp.write(packetBuffer, NTP_PACKET_SIZE);
+  Udp.endPacket();
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeNTP_ESP8266WiFi/TimeNTP_ESP8266WiFi.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeNTP_ESP8266WiFi/TimeNTP_ESP8266WiFi.ino
new file mode 100644
index 0000000000000000000000000000000000000000..cab64883fa1362ddae939e8c11406ba8c8f7f05f
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeNTP_ESP8266WiFi/TimeNTP_ESP8266WiFi.ino
@@ -0,0 +1,143 @@
+/*
+ * Time_NTP.pde
+ * Example showing time sync to NTP time source
+ *
+ * This sketch uses the ESP8266WiFi library
+ */
+ 
+#include <TimeLib.h> 
+#include <ESP8266WiFi.h>
+#include <WiFiUdp.h>
+
+const char ssid[] = "*************";  //  your network SSID (name)
+const char pass[] = "********";       // your network password
+
+// NTP Servers:
+IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
+// IPAddress timeServer(132, 163, 4, 102); // time-b.timefreq.bldrdoc.gov
+// IPAddress timeServer(132, 163, 4, 103); // time-c.timefreq.bldrdoc.gov
+
+
+const int timeZone = 1;     // Central European Time
+//const int timeZone = -5;  // Eastern Standard Time (USA)
+//const int timeZone = -4;  // Eastern Daylight Time (USA)
+//const int timeZone = -8;  // Pacific Standard Time (USA)
+//const int timeZone = -7;  // Pacific Daylight Time (USA)
+
+
+WiFiUDP Udp;
+unsigned int localPort = 8888;  // local port to listen for UDP packets
+
+void setup() 
+{
+  Serial.begin(9600);
+  while (!Serial) ; // Needed for Leonardo only
+  delay(250);
+  Serial.println("TimeNTP Example");
+  Serial.print("Connecting to ");
+  Serial.println(ssid);
+  WiFi.begin(ssid, pass);
+  
+  while (WiFi.status() != WL_CONNECTED) {
+    delay(500);
+    Serial.print(".");
+  }
+
+  
+  Serial.print("IP number assigned by DHCP is ");
+  Serial.println(WiFi.localIP());
+  Serial.println("Starting UDP");
+  Udp.begin(localPort);
+  Serial.print("Local port: ");
+  Serial.println(Udp.localPort());
+  Serial.println("waiting for sync");
+  setSyncProvider(getNtpTime);
+}
+
+time_t prevDisplay = 0; // when the digital clock was displayed
+
+void loop()
+{  
+  if (timeStatus() != timeNotSet) {
+    if (now() != prevDisplay) { //update the display only if time has changed
+      prevDisplay = now();
+      digitalClockDisplay();  
+    }
+  }
+}
+
+void digitalClockDisplay(){
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(".");
+  Serial.print(month());
+  Serial.print(".");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+
+
+void printDigits(int digits){
+  // utility for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
+/*-------- NTP code ----------*/
+
+const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
+byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
+
+time_t getNtpTime()
+{
+  while (Udp.parsePacket() > 0) ; // discard any previously received packets
+  Serial.println("Transmit NTP Request");
+  sendNTPpacket(timeServer);
+  uint32_t beginWait = millis();
+  while (millis() - beginWait < 1500) {
+    int size = Udp.parsePacket();
+    if (size >= NTP_PACKET_SIZE) {
+      Serial.println("Receive NTP Response");
+      Udp.read(packetBuffer, NTP_PACKET_SIZE);  // read packet into the buffer
+      unsigned long secsSince1900;
+      // convert four bytes starting at location 40 to a long integer
+      secsSince1900 =  (unsigned long)packetBuffer[40] << 24;
+      secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
+      secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
+      secsSince1900 |= (unsigned long)packetBuffer[43];
+      return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
+    }
+  }
+  Serial.println("No NTP Response :-(");
+  return 0; // return 0 if unable to get the time
+}
+
+// send an NTP request to the time server at the given address
+void sendNTPpacket(IPAddress &address)
+{
+  // set all bytes in the buffer to 0
+  memset(packetBuffer, 0, NTP_PACKET_SIZE);
+  // Initialize values needed to form NTP request
+  // (see URL above for details on the packets)
+  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
+  packetBuffer[1] = 0;     // Stratum, or type of clock
+  packetBuffer[2] = 6;     // Polling Interval
+  packetBuffer[3] = 0xEC;  // Peer Clock Precision
+  // 8 bytes of zero for Root Delay & Root Dispersion
+  packetBuffer[12]  = 49;
+  packetBuffer[13]  = 0x4E;
+  packetBuffer[14]  = 49;
+  packetBuffer[15]  = 52;
+  // all NTP fields have been given values, now
+  // you can send a packet requesting a timestamp:                 
+  Udp.beginPacket(address, 123); //NTP requests are to port 123
+  Udp.write(packetBuffer, NTP_PACKET_SIZE);
+  Udp.endPacket();
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeRTC/TimeRTC.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeRTC/TimeRTC.ino
new file mode 100644
index 0000000000000000000000000000000000000000..fa10ff6f58d278901dbae84db306b1403e353497
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeRTC/TimeRTC.ino
@@ -0,0 +1,55 @@
+/*
+ * TimeRTC.pde
+ * example code illustrating Time library with Real Time Clock.
+ * 
+ */
+
+#include <TimeLib.h>
+#include <Wire.h>
+#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t
+
+void setup()  {
+  Serial.begin(9600);
+  while (!Serial) ; // wait until Arduino Serial Monitor opens
+  setSyncProvider(RTC.get);   // the function to get the time from the RTC
+  if(timeStatus()!= timeSet) 
+     Serial.println("Unable to sync with the RTC");
+  else
+     Serial.println("RTC has set the system time");      
+}
+
+void loop()
+{
+  if (timeStatus() == timeSet) {
+    digitalClockDisplay();
+  } else {
+    Serial.println("The time has not been set.  Please run the Time");
+    Serial.println("TimeRTCSet example, or DS1307RTC SetTime example.");
+    Serial.println();
+    delay(4000);
+  }
+  delay(1000);
+}
+
+void digitalClockDisplay(){
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  Serial.print(month());
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+void printDigits(int digits){
+  // utility function for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeRTCLog/TimeRTCLog.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeRTCLog/TimeRTCLog.ino
new file mode 100644
index 0000000000000000000000000000000000000000..0b250c2020ba21f27fec919bf0a0be04378094a4
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeRTCLog/TimeRTCLog.ino
@@ -0,0 +1,107 @@
+/*
+ * TimeRTCLogger.pde
+ * example code illustrating adding and subtracting Time.
+ * 
+ * this sketch logs pin state change events
+ * the time of the event and time since the previous event is calculated and sent to the serial port. 
+ */
+
+#include <TimeLib.h>
+#include <Wire.h>
+#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t
+
+const int nbrInputPins  = 6;             // monitor 6 digital pins 
+const int inputPins[nbrInputPins] = {2,3,4,5,6,7};  // pins to monitor
+boolean state[nbrInputPins] ;            // the state of the monitored pins
+time_t  prevEventTime[nbrInputPins] ;    // the time of the previous event
+
+void setup()  {
+  Serial.begin(9600);
+  setSyncProvider(RTC.get);   // the function to sync the time from the RTC  
+  for(int i=0; i < nbrInputPins; i++){
+     pinMode( inputPins[i], INPUT);
+     // uncomment these lines if pull-up resistors are wanted
+     // pinMode( inputPins[i], INPUT_PULLUP);
+     // state[i] = HIGH;
+  }
+}
+
+void loop()
+{
+   for(int i=0; i < nbrInputPins; i++)
+   {
+     boolean val = digitalRead(inputPins[i]); 
+     if(val != state[i])
+     {
+        time_t duration = 0; // the time since the previous event
+        state[i] = val;
+        time_t timeNow = now();
+        if(prevEventTime[i] > 0)  
+           // if this was not the first state change, calculate the time from the previous change
+           duration = duration = timeNow - prevEventTime[i];         
+        logEvent(inputPins[i], val, timeNow, duration );  // log the event
+        prevEventTime[i] = timeNow;                       // store the time for this event  
+     }
+   }
+}
+
+void logEvent( int pin, boolean state, time_t timeNow, time_t duration)
+{
+   Serial.print("Pin ");
+   Serial.print(pin);
+   if( state == HIGH)
+      Serial.print(" went High at ");
+   else   
+     Serial.print(" went  Low at ");
+   showTime(timeNow); 
+   if(duration > 0){
+     // only display duration if greater than 0  
+     Serial.print(", Duration was ");
+     showDuration(duration);
+   }
+   Serial.println();
+}
+
+
+void showTime(time_t t){
+  // display the given time 
+  Serial.print(hour(t));
+  printDigits(minute(t));
+  printDigits(second(t));
+  Serial.print(" ");
+  Serial.print(day(t));
+  Serial.print(" ");
+  Serial.print(month(t));
+  Serial.print(" ");
+  Serial.print(year(t)); 
+}
+
+void printDigits(int digits){
+  // utility function for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
+void showDuration(time_t duration){
+// prints the duration in days, hours, minutes and seconds
+  if(duration >= SECS_PER_DAY){
+     Serial.print(duration / SECS_PER_DAY);
+     Serial.print(" day(s) "); 
+     duration = duration % SECS_PER_DAY;     
+  }
+  if(duration >= SECS_PER_HOUR){
+     Serial.print(duration / SECS_PER_HOUR);
+     Serial.print(" hour(s) "); 
+     duration = duration % SECS_PER_HOUR;     
+  }
+  if(duration >= SECS_PER_MIN){
+     Serial.print(duration / SECS_PER_MIN);
+     Serial.print(" minute(s) "); 
+     duration = duration % SECS_PER_MIN;     
+  }
+  Serial.print(duration);
+  Serial.print(" second(s) ");   
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeRTCSet/TimeRTCSet.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeRTCSet/TimeRTCSet.ino
new file mode 100644
index 0000000000000000000000000000000000000000..49c49f3740cdcade51d5d4aa4236729ba72c8f5b
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeRTCSet/TimeRTCSet.ino
@@ -0,0 +1,80 @@
+/*
+ * TimeRTCSet.pde
+ * example code illustrating Time library with Real Time Clock.
+ *
+ * RTC clock is set in response to serial port time message 
+ * A Processing example sketch to set the time is included in the download
+ * On Linux, you can use "date +T%s > /dev/ttyACM0" (UTC time zone)
+ */
+
+#include <TimeLib.h>
+#include <Wire.h>
+#include <DS1307RTC.h>  // a basic DS1307 library that returns time as a time_t
+
+
+void setup()  {
+  Serial.begin(9600);
+  while (!Serial) ; // Needed for Leonardo only
+  setSyncProvider(RTC.get);   // the function to get the time from the RTC
+  if (timeStatus() != timeSet) 
+     Serial.println("Unable to sync with the RTC");
+  else
+     Serial.println("RTC has set the system time");      
+}
+
+void loop()
+{
+  if (Serial.available()) {
+    time_t t = processSyncMessage();
+    if (t != 0) {
+      RTC.set(t);   // set the RTC and the system time to the received value
+      setTime(t);          
+    }
+  }
+  digitalClockDisplay();  
+  delay(1000);
+}
+
+void digitalClockDisplay(){
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  Serial.print(month());
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+void printDigits(int digits){
+  // utility function for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
+/*  code to process time sync messages from the serial port   */
+#define TIME_HEADER  "T"   // Header tag for serial time sync message
+
+unsigned long processSyncMessage() {
+  unsigned long pctime = 0L;
+  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 
+
+  if(Serial.find(TIME_HEADER)) {
+     pctime = Serial.parseInt();
+     return pctime;
+     if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
+       pctime = 0L; // return 0 to indicate that the time is not valid
+     }
+  }
+  return pctime;
+}
+
+
+
+
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeSerial/TimeSerial.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeSerial/TimeSerial.ino
new file mode 100644
index 0000000000000000000000000000000000000000..07e609fde171e5a467bf4292d86cfd9d40077de6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeSerial/TimeSerial.ino
@@ -0,0 +1,81 @@
+/* 
+ * TimeSerial.pde
+ * example code illustrating Time library set through serial port messages.
+ *
+ * Messages consist of the letter T followed by ten digit time (as seconds since Jan 1 1970)
+ * you can send the text on the next line using Serial Monitor to set the clock to noon Jan 1 2013
+ T1357041600  
+ *
+ * A Processing example sketch to automatically send the messages is included in the download
+ * On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone)
+ */ 
+ 
+#include <TimeLib.h>
+
+#define TIME_HEADER  "T"   // Header tag for serial time sync message
+#define TIME_REQUEST  7    // ASCII bell character requests a time sync message 
+
+void setup()  {
+  Serial.begin(9600);
+  while (!Serial) ; // Needed for Leonardo only
+  pinMode(13, OUTPUT);
+  setSyncProvider( requestSync);  //set function to call when sync required
+  Serial.println("Waiting for sync message");
+}
+
+void loop(){    
+  if (Serial.available()) {
+    processSyncMessage();
+  }
+  if (timeStatus()!= timeNotSet) {
+    digitalClockDisplay();  
+  }
+  if (timeStatus() == timeSet) {
+    digitalWrite(13, HIGH); // LED on if synced
+  } else {
+    digitalWrite(13, LOW);  // LED off if needs refresh
+  }
+  delay(1000);
+}
+
+void digitalClockDisplay(){
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  Serial.print(month());
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+void printDigits(int digits){
+  // utility function for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
+
+void processSyncMessage() {
+  unsigned long pctime;
+  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013
+
+  if(Serial.find(TIME_HEADER)) {
+     pctime = Serial.parseInt();
+     if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013)
+       setTime(pctime); // Sync Arduino clock to the time received on the serial port
+     }
+  }
+}
+
+time_t requestSync()
+{
+  Serial.write(TIME_REQUEST);  
+  return 0; // the time will be sent later in response to serial mesg
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeSerialDateStrings/TimeSerialDateStrings.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeSerialDateStrings/TimeSerialDateStrings.ino
new file mode 100644
index 0000000000000000000000000000000000000000..95d2568c1763e37b3293f4bccdcecdf0c2b6cd20
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeSerialDateStrings/TimeSerialDateStrings.ino
@@ -0,0 +1,108 @@
+/* 
+ * TimeSerialDateStrings.pde
+ * example code illustrating Time library date strings
+ *
+ * This sketch adds date string functionality to TimeSerial sketch
+ * Also shows how to handle different messages
+ *
+ * A message starting with a time header sets the time
+ * A Processing example sketch to automatically send the messages is inclided in the download
+ * On Linux, you can use "date +T%s\n > /dev/ttyACM0" (UTC time zone)
+ *
+ * A message starting with a format header sets the date format
+
+ * send: Fs\n for short date format
+ * send: Fl\n for long date format 
+ */ 
+ 
+#include <TimeLib.h>
+
+// single character message tags
+#define TIME_HEADER   'T'   // Header tag for serial time sync message
+#define FORMAT_HEADER 'F'   // Header tag indicating a date format message
+#define FORMAT_SHORT  's'   // short month and day strings
+#define FORMAT_LONG   'l'   // (lower case l) long month and day strings
+
+#define TIME_REQUEST  7     // ASCII bell character requests a time sync message 
+
+static boolean isLongFormat = true;
+
+void setup()  {
+  Serial.begin(9600);
+  while (!Serial) ; // Needed for Leonardo only
+  setSyncProvider( requestSync);  //set function to call when sync required
+  Serial.println("Waiting for sync message");
+}
+
+void loop(){    
+  if (Serial.available() > 1) { // wait for at least two characters
+    char c = Serial.read();
+    if( c == TIME_HEADER) {
+      processSyncMessage();
+    }
+    else if( c== FORMAT_HEADER) {
+      processFormatMessage();
+    }
+  }
+  if (timeStatus()!= timeNotSet) {
+    digitalClockDisplay();  
+  }
+  delay(1000);
+}
+
+void digitalClockDisplay() {
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  if(isLongFormat)
+    Serial.print(dayStr(weekday()));
+  else  
+   Serial.print(dayShortStr(weekday()));
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  if(isLongFormat)
+     Serial.print(monthStr(month()));
+  else
+     Serial.print(monthShortStr(month()));
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+void printDigits(int digits) {
+  // utility function for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
+void  processFormatMessage() {
+   char c = Serial.read();
+   if( c == FORMAT_LONG){
+      isLongFormat = true;
+      Serial.println(F("Setting long format"));
+   }
+   else if( c == FORMAT_SHORT) {
+      isLongFormat = false;   
+      Serial.println(F("Setting short format"));
+   }
+}
+
+void processSyncMessage() {
+  unsigned long pctime;
+  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 - paul, perhaps we define in time.h?
+
+   pctime = Serial.parseInt();
+   if( pctime >= DEFAULT_TIME) { // check the integer is a valid time (greater than Jan 1 2013)
+     setTime(pctime); // Sync Arduino clock to the time received on the serial port
+   }
+}
+
+time_t requestSync() {
+  Serial.write(TIME_REQUEST);  
+  return 0; // the time will be sent later in response to serial mesg
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeTeensy3/TimeTeensy3.ino b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeTeensy3/TimeTeensy3.ino
new file mode 100644
index 0000000000000000000000000000000000000000..f68dd8cc7f2ecdeb9ebd14f96720fe8a110252d7
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/examples/TimeTeensy3/TimeTeensy3.ino
@@ -0,0 +1,78 @@
+/*
+ * TimeRTC.pde
+ * example code illustrating Time library with Real Time Clock.
+ * 
+ */
+
+#include <TimeLib.h>
+
+void setup()  {
+  // set the Time library to use Teensy 3.0's RTC to keep time
+  setSyncProvider(getTeensy3Time);
+
+  Serial.begin(115200);
+  while (!Serial);  // Wait for Arduino Serial Monitor to open
+  delay(100);
+  if (timeStatus()!= timeSet) {
+    Serial.println("Unable to sync with the RTC");
+  } else {
+    Serial.println("RTC has set the system time");
+  }
+}
+
+void loop() {
+  if (Serial.available()) {
+    time_t t = processSyncMessage();
+    if (t != 0) {
+      Teensy3Clock.set(t); // set the RTC
+      setTime(t);
+    }
+  }
+  digitalClockDisplay();  
+  delay(1000);
+}
+
+void digitalClockDisplay() {
+  // digital clock display of the time
+  Serial.print(hour());
+  printDigits(minute());
+  printDigits(second());
+  Serial.print(" ");
+  Serial.print(day());
+  Serial.print(" ");
+  Serial.print(month());
+  Serial.print(" ");
+  Serial.print(year()); 
+  Serial.println(); 
+}
+
+time_t getTeensy3Time()
+{
+  return Teensy3Clock.get();
+}
+
+/*  code to process time sync messages from the serial port   */
+#define TIME_HEADER  "T"   // Header tag for serial time sync message
+
+unsigned long processSyncMessage() {
+  unsigned long pctime = 0L;
+  const unsigned long DEFAULT_TIME = 1357041600; // Jan 1 2013 
+
+  if(Serial.find(TIME_HEADER)) {
+     pctime = Serial.parseInt();
+     return pctime;
+     if( pctime < DEFAULT_TIME) { // check the value is a valid time (greater than Jan 1 2013)
+       pctime = 0L; // return 0 to indicate that the time is not valid
+     }
+  }
+  return pctime;
+}
+
+void printDigits(int digits){
+  // utility function for digital clock display: prints preceding colon and leading 0
+  Serial.print(":");
+  if(digits < 10)
+    Serial.print('0');
+  Serial.print(digits);
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/keywords.txt b/ESP-1ch-Gateway-v5.0-master/libraries/Time/keywords.txt
new file mode 100644
index 0000000000000000000000000000000000000000..073f8f8855020d95241a84adad53b20b443bdc75
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/keywords.txt
@@ -0,0 +1,34 @@
+#######################################
+# Syntax Coloring Map For Time
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+time_t	KEYWORD1
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+now	KEYWORD2
+second	KEYWORD2
+minute	KEYWORD2
+hour	KEYWORD2
+day	KEYWORD2
+month	KEYWORD2
+year	KEYWORD2
+isAM	KEYWORD2
+isPM	KEYWORD2
+weekday	KEYWORD2
+setTime	KEYWORD2
+adjustTime	KEYWORD2
+setSyncProvider	KEYWORD2
+setSyncInterval	KEYWORD2
+timeStatus	KEYWORD2
+TimeLib	KEYWORD2
+#######################################
+# Instances (KEYWORD2)
+#######################################
+
+#######################################
+# Constants (LITERAL1)
+#######################################
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/library.json b/ESP-1ch-Gateway-v5.0-master/libraries/Time/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..071e74c9faae40afe31f4ef0c69d5fdad17b3c14
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/library.json
@@ -0,0 +1,22 @@
+{
+"name": "Time",
+"frameworks": "Arduino",
+"keywords": "Time, date, hour, minute, second, day, week, month, year, RTC",
+"description": "Time keeping library",
+"url": "http://playground.arduino.cc/Code/Time",
+"authors":
+[
+{
+        "name": "Michael Margolis"
+},
+{
+        "name": "Paul Stoffregen"
+}
+],
+"repository":
+{
+    "type": "git",
+    "url": "https://github.com/PaulStoffregen/Time"
+}
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/Time/library.properties b/ESP-1ch-Gateway-v5.0-master/libraries/Time/library.properties
new file mode 100644
index 0000000000000000000000000000000000000000..49b1e2a16e8215d7e63ffa8a50e4af41569c9116
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/Time/library.properties
@@ -0,0 +1,10 @@
+name=Time
+version=1.5
+author=Michael Margolis
+maintainer=Paul Stoffregen
+sentence=Timekeeping functionality for Arduino
+paragraph=Date and Time functions, with provisions to synchronize to external time sources like GPS and NTP (Internet).  This library is often used together with TimeAlarms and DS1307RTC.
+category=Timing
+url=http://playground.arduino.cc/code/time
+architectures=*
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/CHANGES.txt b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/CHANGES.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1b9e45b5f02ef6e83b4f8b492ed00db43d23f8eb
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/CHANGES.txt
@@ -0,0 +1,65 @@
+2.2.1 (2017-01-02)
+  * Fixing Issue #69 (Can't declare member function to have static linkage) 
+  * Supporting ESP8266 firmware 2.X
+
+2.1.2 (2016-05-08)
+  * Added retry in EspDrv::wifiDriverInit method
+  * Clean buffer in ScanNetwork (bug #43)
+
+2.1.1 (2016-05-09)
+  * Implemented gatwayIP() and subnetMask() methods
+
+2.1 (2016-03-13)
+  * SSL connection support
+  * Implementation of config and configAP methods
+  * Fixed read methods WiFiEspClient.read(buf, size) and WiFiEspUDP::read(buf, size)
+  * Fixed possible buffer overflow in EspDrv.sendCmdGet
+  * Added beginAP methods similar to WiFi101 library - parameters order for beginAP is now different!
+
+1.6 (2016-02-21)
+  * Improved UDP support
+  * Added WiFiEspClient.remoteIP() method
+  * Consistent use use of uint16_t to manage port numbers
+  * Added AT+CIPDINFO=1 during init to return remote IP and port with IPD
+  * Added AT+CWAUTOCONN=0 during init to disable autoconnect
+
+1.5.1 (2016-02-11)
+  * Fix in EspDrv.getScanNetworks method 
+  * Fix buffer overflow in getFwVersion
+
+1.5 (2016-01-25)
+  * Implemented scanNetworks method
+  * Increased ring buffer size to 32 to read long SSID names
+
+1.4.2 (2016-01-05)
+  * Fixed compilation problem when using WiFiEspClient.print
+
+1.4.1 (2016-01-05)
+  * Speed optimizations
+
+1.4 (2016-01-04)
+  * Reduced dynamic memory footprint
+
+1.3 (2016-01-03)
+  * UDP support (experimental)
+  * Fixed WiFiEspClient.connected and WiFi.status and methods
+  * Connection close detection
+  * Ring buffer optimization
+  * Client peek method fixed
+
+1.2 (2015-12-29)
+  * Redesigned WiFi.init method to accept a Stream object
+  * Use CUR when starting the AP
+  * Small improvements to samples
+
+1.1 (2015-12-20)
+  * Fix for receiving large data packets
+  * Access point mode support
+  * Ring buffer class is now used in EspDrv.cpp
+  * Removed not implemented methods
+  * Prints firmware version if not recognized
+  * Multiple exclamation marks fixed for Arduino Mega
+  * Cleaned up comments
+
+1.0 (2015-12-11)
+  * First stable release
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/LICENSE b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..6b156fe1db9c5cd21ca1c68b7025bae40d0c5764
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/LICENSE
@@ -0,0 +1,675 @@
+GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ 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.
+
+    {one line to give the program's name and a brief idea of what it does.}
+    Copyright (C) {year}  {name of author}
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+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:
+
+    {project}  Copyright (C) {year}  {fullname}
+    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
+<http://www.gnu.org/licenses/>.
+
+  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
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/README.md b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..805281af855a15bff17cc4197ce464dc7f6004f3
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/README.md
@@ -0,0 +1,102 @@
+# WiFiEsp
+
+With an ESP8266 board, WiFiEsp library allows an Arduino board to connect to the internet.
+It can serve as either a server accepting incoming connections or a client making outgoing ones.
+The WiFiEsp library is very similar to the Arduino [WiFi](http://www.arduino.cc/en/Reference/WiFi) and [Ethernet](http://www.arduino.cc/en/Reference/Ethernet) libraries, and many of the function calls are the same. 
+
+Supports ESP SDK version 1.1.1 and above (AT version 0.25 and above).
+
+
+##Features
+
+- APIs compatible with standard Arduino WiFi library.
+- Use AT commands of standard ESP firmware (no need to flash a custom firmware).
+- Support hardware and software serial ports.
+- Configurable tracing level.
+
+##Wiring
+
+The WiFiEsp library has been designed to work with the [ESP WiFi shield](http://www.instructables.com/id/Cheap-Arduino-WiFi-Shield-With-ESP8266/).
+It is a cheap version of the Arduino WiFi shield that uses an ESP-01 module to provide networking capabilities to Arduino boards.
+
+
+##Examples
+
+- [ConnectWPA](https://github.com/bportaluri/WiFiEsp/blob/master/examples/ConnectWPA/ConnectWPA.ino) - Demonstrates how to connect to a network that is encrypted with WPA2 Personal
+- [WebClient](https://github.com/bportaluri/WiFiEsp/blob/master/examples/WebClient/WebClient.ino) - Connect to a remote webserver 
+- [WebClientRepeating](https://github.com/bportaluri/WiFiEsp/blob/master/examples/WebClientRepeating/WebClientRepeating.ino) - Make repeated HTTP calls to a webserver 
+- [WebServer](https://github.com/bportaluri/WiFiEsp/blob/master/examples/WebServer/WebServer.ino) - Serve a webpage from the WiFi shield 
+- [WebServerAP](https://github.com/bportaluri/WiFiEsp/blob/master/examples/WebServerAP/WebServerAP.ino) - Serve a webpage from the WiFi shield starting a local Access Point
+- [WebServerLed](https://github.com/bportaluri/WiFiEsp/blob/master/examples/WebServerLed/WebServerLed.ino) - Turn on and off a led from a webpage
+- [UdpNTPClient](https://github.com/bportaluri/WiFiEsp/blob/master/examples/UdpNTPClient/UdpNTPClient.ino) - Query a Network Time Protocol (NTP) server using UDP
+
+
+##Supported APIs
+
+Most of the standard Arduino WiFi library methods are available. Refer to the [WiFi library page](http://www.arduino.cc/en/Reference/WiFi) for more details.
+
+###WiFiEsp class
+
+- begin() - Not all authentication types
+- disconnect() - YES
+- config()
+- setDNS() - NO (no AT command available)
+- SSID() - YES
+- BSSID() - YES
+- RSSI() - YES
+- encryptionType() - NO (no AT command available)
+- scanNetworks() - YES
+- getSocket()
+- macAddress() - YES
+
+
+###WiFiEspServer class
+
+The WiFiEspServer class creates servers which can send data to and receive data from connected clients (programs running on other computers or devices).
+
+- WiFiEspServer() - YES
+- begin() - YES
+- available() - YES
+- write() - YES
+- print() - YES
+- println() - YES
+
+
+###Client class
+
+The WiFiEspClient class creates clients that can connect to servers and send and receive data.
+
+- WiFiEspClient() - YES
+- connected() - YES
+- connect() - YES
+- write() - YES
+- print() - YES
+- println() - YES
+- available() - YES
+- read() - YES
+- flush() - YES
+- stop() - YES
+
+
+###WiFiEspUDP class
+
+The UDP class enables UDP message to be sent and received.
+
+- WiFiUDP - YES
+- begin() - YES
+- available() - YES
+- beginPacket() - YES
+- endPacket() - YES
+- write() - YES
+- parsePacket() - YES
+- peek()
+- read() - YES
+- flush()
+- stop()
+- remoteIP() - YES
+- remotePort() - YES
+
+
+##Contributing
+
+If you discover a bug or would like to propose a new feature, please open a new [issue](https://github.com/bportaluri/WiFiEsp/issues).
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/ConnectWPA/ConnectWPA.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/ConnectWPA/ConnectWPA.ino
new file mode 100644
index 0000000000000000000000000000000000000000..c6529639f1ae88b3459aae01dfc33512ad60208c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/ConnectWPA/ConnectWPA.ino
@@ -0,0 +1,94 @@
+/*
+ WiFiEsp example: ConnectWPA
+ 
+ This example connects to an encrypted WiFi network using an ESP8266 module.
+ Then it prints the  MAC address of the WiFi shield, the IP address obtained
+ and other network details.
+
+ For more details see: http://yaab-arduino.blogspot.com/p/wifiesp-example-connect.html
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+char ssid[] = "Twim";            // your network SSID (name)
+char pass[] = "12345678";        // your network password
+int status = WL_IDLE_STATUS;     // the Wifi radio's status
+
+void setup()
+{
+  // initialize serial for debugging
+  Serial.begin(115200);
+  // initialize serial for ESP module
+  Serial1.begin(9600);
+  // initialize ESP module
+  WiFi.init(&Serial1);
+
+  // check for the presence of the shield
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    // don't continue
+    while (true);
+  }
+
+  // attempt to connect to WiFi network
+  while ( status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to WPA SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network
+    status = WiFi.begin(ssid, pass);
+  }
+
+  Serial.println("You're connected to the network");
+}
+
+void loop()
+{
+  // print the network connection information every 10 seconds
+  Serial.println();
+  printCurrentNet();
+  printWifiData();
+  
+  delay(10000);
+}
+
+void printWifiData()
+{
+  // print your WiFi shield's IP address
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+
+  // print your MAC address
+  byte mac[6];
+  WiFi.macAddress(mac);
+  char buf[20];
+  sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
+  Serial.print("MAC address: ");
+  Serial.println(buf);
+}
+
+void printCurrentNet()
+{
+  // print the SSID of the network you're attached to
+  Serial.print("SSID: ");
+  Serial.println(WiFi.SSID());
+
+  // print the MAC address of the router you're attached to
+  byte bssid[6];
+  WiFi.BSSID(bssid);
+  char buf[20];
+  sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", bssid[5], bssid[4], bssid[3], bssid[2], bssid[1], bssid[0]);
+  Serial.print("BSSID: ");
+  Serial.println(buf);
+
+  // print the received signal strength
+  long rssi = WiFi.RSSI();
+  Serial.print("Signal strength (RSSI): ");
+  Serial.println(rssi);
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/ScanNetworks/ScanNetworks.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/ScanNetworks/ScanNetworks.ino
new file mode 100644
index 0000000000000000000000000000000000000000..664b3311a12250cf344727fbf93c09fa7ce139c0
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/ScanNetworks/ScanNetworks.ino
@@ -0,0 +1,109 @@
+/*
+ WiFiEsp example: ScanNetworks
+
+ This example  prints the Wifi shield's MAC address, and
+ scans for available Wifi networks using the Wifi shield.
+ Every ten seconds, it scans again. It doesn't actually
+ connect to any network, so no encryption scheme is specified.
+
+ For more details see: http://yaab-arduino.blogspot.com/p/wifiesp.html
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+void setup() {
+  // initialize serial for debugging
+  Serial.begin(115200);
+  // initialize serial for ESP module
+  Serial1.begin(9600);
+  // initialize ESP module
+  WiFi.init(&Serial1);
+
+  // check for the presence of the shield
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    // don't continue
+    while (true);
+  }
+
+  // Print WiFi MAC address
+  printMacAddress();
+}
+
+void loop()
+{
+  // scan for existing networks
+  Serial.println();
+  Serial.println("Scanning available networks...");
+  listNetworks();
+  delay(10000);
+}
+
+
+void printMacAddress()
+{
+  // get your MAC address
+  byte mac[6];
+  WiFi.macAddress(mac);
+  
+  // print MAC address
+  char buf[20];
+  sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X", mac[5], mac[4], mac[3], mac[2], mac[1], mac[0]);
+  Serial.print("MAC address: ");
+  Serial.println(buf);
+}
+
+void listNetworks()
+{
+  // scan for nearby networks
+  int numSsid = WiFi.scanNetworks();
+  if (numSsid == -1) {
+    Serial.println("Couldn't get a wifi connection");
+    while (true);
+  }
+
+  // print the list of networks seen
+  Serial.print("Number of available networks:");
+  Serial.println(numSsid);
+
+  // print the network number and name for each network found
+  for (int thisNet = 0; thisNet < numSsid; thisNet++) {
+    Serial.print(thisNet);
+    Serial.print(") ");
+    Serial.print(WiFi.SSID(thisNet));
+    Serial.print("\tSignal: ");
+    Serial.print(WiFi.RSSI(thisNet));
+    Serial.print(" dBm");
+    Serial.print("\tEncryption: ");
+    printEncryptionType(WiFi.encryptionType(thisNet));
+  }
+}
+
+void printEncryptionType(int thisType) {
+  // read the encryption type and print out the name
+  switch (thisType) {
+    case ENC_TYPE_WEP:
+      Serial.print("WEP");
+      break;
+    case ENC_TYPE_WPA_PSK:
+      Serial.print("WPA_PSK");
+      break;
+    case ENC_TYPE_WPA2_PSK:
+      Serial.print("WPA2_PSK");
+      break;
+    case ENC_TYPE_WPA_WPA2_PSK:
+      Serial.print("WPA_WPA2_PSK");
+      break;
+    case ENC_TYPE_NONE:
+      Serial.print("None");
+      break;
+  }
+  Serial.println();
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/UdpNTPClient/UdpNTPClient.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/UdpNTPClient/UdpNTPClient.ino
new file mode 100644
index 0000000000000000000000000000000000000000..5f21b19b765932dcc6059c431898210fc6bffcc0
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/UdpNTPClient/UdpNTPClient.ino
@@ -0,0 +1,154 @@
+/*
+ WiFiEsp example: UdpNTPClient
+
+ Get the time from a Network Time Protocol (NTP) time server.
+ Demonstrates use of UDP to send and receive data packets
+ For more on NTP time servers and the messages needed to communicate with them,
+ see http://en.wikipedia.org/wiki/Network_Time_Protocol
+
+ NOTE: The serial buffer size must be larger than 36 + packet size
+ In this example we use an UDP packet of 48 bytes so the buffer must be
+ at least 36+48=84 bytes that exceeds the default buffer size (64).
+ You must modify the serial buffer size to 128
+ For HardwareSerial modify _SS_MAX_RX_BUFF in
+   Arduino\hardware\arduino\avr\cores\arduino\SoftwareSerial.h
+ For SoftwareSerial modify _SS_MAX_RX_BUFF in
+   Arduino\hardware\arduino\avr\libraries\SoftwareSerial\SoftwareSerial.h
+*/
+
+#include "WiFiEsp.h"
+#include "WiFiEspUdp.h"
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+char ssid[] = "Twim";            // your network SSID (name)
+char pass[] = "12345678";        // your network password
+int status = WL_IDLE_STATUS;     // the Wifi radio's status
+
+char timeServer[] = "time.nist.gov";  // NTP server
+unsigned int localPort = 2390;        // local port to listen for UDP packets
+
+const int NTP_PACKET_SIZE = 48;  // NTP timestamp is in the first 48 bytes of the message
+const int UDP_TIMEOUT = 2000;    // timeout in miliseconds to wait for an UDP packet to arrive
+
+byte packetBuffer[NTP_PACKET_SIZE]; // buffer to hold incoming and outgoing packets
+
+// A UDP instance to let us send and receive packets over UDP
+WiFiEspUDP Udp;
+
+void setup()
+{
+  // initialize serial for debugging
+  Serial.begin(115200);
+  // initialize serial for ESP module
+  Serial1.begin(9600);
+  // initialize ESP module
+  WiFi.init(&Serial1);
+
+  // check for the presence of the shield
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    // don't continue
+    while (true);
+  }
+
+  // attempt to connect to WiFi network
+  while ( status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to WPA SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network
+    status = WiFi.begin(ssid, pass);
+  }
+
+  // you're connected now, so print out the data
+  Serial.println("You're connected to the network");
+  
+  Udp.begin(localPort);
+}
+
+void loop()
+{
+  sendNTPpacket(timeServer); // send an NTP packet to a time server
+  
+  // wait for a reply for UDP_TIMEOUT miliseconds
+  unsigned long startMs = millis();
+  while (!Udp.available() && (millis() - startMs) < UDP_TIMEOUT) {}
+
+  Serial.println(Udp.parsePacket());
+  if (Udp.parsePacket()) {
+    Serial.println("packet received");
+    // We've received a packet, read the data from it into the buffer
+    Udp.read(packetBuffer, NTP_PACKET_SIZE);
+
+    // the timestamp starts at byte 40 of the received packet and is four bytes,
+    // or two words, long. First, esxtract the two words:
+
+    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
+    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
+    // combine the four bytes (two words) into a long integer
+    // this is NTP time (seconds since Jan 1 1900):
+    unsigned long secsSince1900 = highWord << 16 | lowWord;
+    Serial.print("Seconds since Jan 1 1900 = ");
+    Serial.println(secsSince1900);
+
+    // now convert NTP time into everyday time:
+    Serial.print("Unix time = ");
+    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
+    const unsigned long seventyYears = 2208988800UL;
+    // subtract seventy years:
+    unsigned long epoch = secsSince1900 - seventyYears;
+    // print Unix time:
+    Serial.println(epoch);
+
+
+    // print the hour, minute and second:
+    Serial.print("The UTC time is ");       // UTC is the time at Greenwich Meridian (GMT)
+    Serial.print((epoch  % 86400L) / 3600); // print the hour (86400 equals secs per day)
+    Serial.print(':');
+    if (((epoch % 3600) / 60) < 10) {
+      // In the first 10 minutes of each hour, we'll want a leading '0'
+      Serial.print('0');
+    }
+    Serial.print((epoch  % 3600) / 60); // print the minute (3600 equals secs per minute)
+    Serial.print(':');
+    if ((epoch % 60) < 10) {
+      // In the first 10 seconds of each minute, we'll want a leading '0'
+      Serial.print('0');
+    }
+    Serial.println(epoch % 60); // print the second
+  }
+  // wait ten seconds before asking for the time again
+  delay(10000);
+}
+
+// send an NTP request to the time server at the given address
+void sendNTPpacket(char *ntpSrv)
+{
+  // set all bytes in the buffer to 0
+  memset(packetBuffer, 0, NTP_PACKET_SIZE);
+  // Initialize values needed to form NTP request
+  // (see URL above for details on the packets)
+
+  packetBuffer[0] = 0b11100011;   // LI, Version, Mode
+  packetBuffer[1] = 0;     // Stratum, or type of clock
+  packetBuffer[2] = 6;     // Polling Interval
+  packetBuffer[3] = 0xEC;  // Peer Clock Precision
+  // 8 bytes of zero for Root Delay & Root Dispersion
+  packetBuffer[12]  = 49;
+  packetBuffer[13]  = 0x4E;
+  packetBuffer[14]  = 49;
+  packetBuffer[15]  = 52;
+
+  // all NTP fields have been given values, now
+  // you can send a packet requesting a timestamp:
+  Udp.beginPacket(ntpSrv, 123); //NTP requests are to port 123
+
+  Udp.write(packetBuffer, NTP_PACKET_SIZE);
+
+  Udp.endPacket();
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/UdpSendReceive/UdpSendReceive.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/UdpSendReceive/UdpSendReceive.ino
new file mode 100644
index 0000000000000000000000000000000000000000..58b638eb6d2b16f47e5a53bc2b14174925342b32
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/UdpSendReceive/UdpSendReceive.ino
@@ -0,0 +1,109 @@
+/*
+ WiFiEsp example: WiFi UDP Send and Receive String
+
+ This sketch wait an UDP packet on localPort using a WiFi shield.
+ When a packet is received an 'ACK' packet is sent to the client on port remotePort.
+
+ For more details see: http://yaab-arduino.blogspot.com/p/wifiesp-example-client.html
+*/
+
+
+#include <WiFiEsp.h>
+#include <WiFiEspUdp.h>
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+char ssid[] = "Twim";            // your network SSID (name)
+char pass[] = "12345678";        // your network password
+int status = WL_IDLE_STATUS;     // the Wifi radio's status
+
+unsigned int localPort = 10002;  // local port to listen on
+
+char packetBuffer[255];          // buffer to hold incoming packet
+char ReplyBuffer[] = "ACK";      // a string to send back
+
+WiFiEspUDP Udp;
+
+void setup() {
+  // initialize serial for debugging
+  Serial.begin(115200);
+  // initialize serial for ESP module
+  Serial1.begin(9600);
+  // initialize ESP module
+  WiFi.init(&Serial1);
+
+  // check for the presence of the shield:
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    // don't continue:
+    while (true);
+  }
+
+  // attempt to connect to WiFi network
+  while ( status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to WPA SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network
+    status = WiFi.begin(ssid, pass);
+  }
+  
+  Serial.println("Connected to wifi");
+  printWifiStatus();
+
+  Serial.println("\nStarting connection to server...");
+  // if you get a connection, report back via serial:
+  Udp.begin(localPort);
+  
+  Serial.print("Listening on port ");
+  Serial.println(localPort);
+}
+
+void loop() {
+
+  // if there's data available, read a packet
+  int packetSize = Udp.parsePacket();
+  if (packetSize) {
+    Serial.print("Received packet of size ");
+    Serial.println(packetSize);
+    Serial.print("From ");
+    IPAddress remoteIp = Udp.remoteIP();
+    Serial.print(remoteIp);
+    Serial.print(", port ");
+    Serial.println(Udp.remotePort());
+
+    // read the packet into packetBufffer
+    int len = Udp.read(packetBuffer, 255);
+    if (len > 0) {
+      packetBuffer[len] = 0;
+    }
+    Serial.println("Contents:");
+    Serial.println(packetBuffer);
+
+    // send a reply, to the IP address and port that sent us the packet we received
+    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
+    Udp.write(ReplyBuffer);
+    Udp.endPacket();
+  }
+}
+
+
+void printWifiStatus() {
+  // print the SSID of the network you're attached to:
+  Serial.print("SSID: ");
+  Serial.println(WiFi.SSID());
+
+  // print your WiFi shield's IP address:
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+
+  // print the received signal strength:
+  long rssi = WiFi.RSSI();
+  Serial.print("signal strength (RSSI):");
+  Serial.print(rssi);
+  Serial.println(" dBm");
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebClient/WebClient.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebClient/WebClient.ino
new file mode 100644
index 0000000000000000000000000000000000000000..7e8c802edf1b1e126e1543e4292b20cbfb55c471
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebClient/WebClient.ino
@@ -0,0 +1,106 @@
+/*
+ WiFiEsp example: WebClient
+
+ This sketch connects to google website using an ESP8266 module to
+ perform a simple web search.
+
+ For more details see: http://yaab-arduino.blogspot.com/p/wifiesp-example-client.html
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+char ssid[] = "Twim";            // your network SSID (name)
+char pass[] = "12345678";        // your network password
+int status = WL_IDLE_STATUS;     // the Wifi radio's status
+
+char server[] = "arduino.cc";
+
+// Initialize the Ethernet client object
+WiFiEspClient client;
+
+void setup()
+{
+  // initialize serial for debugging
+  Serial.begin(115200);
+  // initialize serial for ESP module
+  Serial1.begin(9600);
+  // initialize ESP module
+  WiFi.init(&Serial1);
+
+  // check for the presence of the shield
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    // don't continue
+    while (true);
+  }
+
+  // attempt to connect to WiFi network
+  while ( status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to WPA SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network
+    status = WiFi.begin(ssid, pass);
+  }
+
+  // you're connected now, so print out the data
+  Serial.println("You're connected to the network");
+  
+  printWifiStatus();
+
+  Serial.println();
+  Serial.println("Starting connection to server...");
+  // if you get a connection, report back via serial
+  if (client.connect(server, 80)) {
+    Serial.println("Connected to server");
+    // Make a HTTP request
+    client.println("GET /asciilogo.txt HTTP/1.1");
+    client.println("Host: arduino.cc");
+    client.println("Connection: close");
+    client.println();
+  }
+}
+
+void loop()
+{
+  // if there are incoming bytes available
+  // from the server, read them and print them
+  while (client.available()) {
+    char c = client.read();
+    Serial.write(c);
+  }
+
+  // if the server's disconnected, stop the client
+  if (!client.connected()) {
+    Serial.println();
+    Serial.println("Disconnecting from server...");
+    client.stop();
+
+    // do nothing forevermore
+    while (true);
+  }
+}
+
+
+void printWifiStatus()
+{
+  // print the SSID of the network you're attached to
+  Serial.print("SSID: ");
+  Serial.println(WiFi.SSID());
+
+  // print your WiFi shield's IP address
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+
+  // print the received signal strength
+  long rssi = WiFi.RSSI();
+  Serial.print("Signal strength (RSSI):");
+  Serial.print(rssi);
+  Serial.println(" dBm");
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebClientRepeating/WebClientRepeating.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebClientRepeating/WebClientRepeating.ino
new file mode 100644
index 0000000000000000000000000000000000000000..11353c5790790828322167e59dca8663e07dab29
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebClientRepeating/WebClientRepeating.ino
@@ -0,0 +1,121 @@
+/*
+ WiFiEsp example: WebClientRepeating
+
+ This sketch connects to a web server and makes an HTTP request
+ using an Arduino ESP8266 module.
+ It repeats the HTTP call each 10 seconds.
+
+ For more details see: http://yaab-arduino.blogspot.com/p/wifiesp.html
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+char ssid[] = "Twim";            // your network SSID (name)
+char pass[] = "12345678";        // your network password
+int status = WL_IDLE_STATUS;     // the Wifi radio's status
+
+char server[] = "arduino.cc";
+
+unsigned long lastConnectionTime = 0;         // last time you connected to the server, in milliseconds
+const unsigned long postingInterval = 10000L; // delay between updates, in milliseconds
+
+// Initialize the Ethernet client object
+WiFiEspClient client;
+
+void setup()
+{
+  // initialize serial for debugging
+  Serial.begin(115200);
+  // initialize serial for ESP module
+  Serial1.begin(9600);
+  // initialize ESP module
+  WiFi.init(&Serial1);
+
+  // check for the presence of the shield
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    // don't continue
+    while (true);
+  }
+
+  // attempt to connect to WiFi network
+  while ( status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to WPA SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network
+    status = WiFi.begin(ssid, pass);
+  }
+
+  Serial.println("You're connected to the network");
+  
+  printWifiStatus();
+}
+
+void loop()
+{
+  // if there's incoming data from the net connection send it out the serial port
+  // this is for debugging purposes only
+  while (client.available()) {
+    char c = client.read();
+    Serial.write(c);
+  }
+
+  // if 10 seconds have passed since your last connection,
+  // then connect again and send data
+  if (millis() - lastConnectionTime > postingInterval) {
+    httpRequest();
+  }
+}
+
+// this method makes a HTTP connection to the server
+void httpRequest()
+{
+  Serial.println();
+    
+  // close any connection before send a new request
+  // this will free the socket on the WiFi shield
+  client.stop();
+
+  // if there's a successful connection
+  if (client.connect(server, 80)) {
+    Serial.println("Connecting...");
+    
+    // send the HTTP PUT request
+    client.println(F("GET /asciilogo.txt HTTP/1.1"));
+    client.println(F("Host: arduino.cc"));
+    client.println("Connection: close");
+    client.println();
+
+    // note the time that the connection was made
+    lastConnectionTime = millis();
+  }
+  else {
+    // if you couldn't make a connection
+    Serial.println("Connection failed");
+  }
+}
+
+
+void printWifiStatus()
+{
+  // print the SSID of the network you're attached to
+  Serial.print("SSID: ");
+  Serial.println(WiFi.SSID());
+
+  // print your WiFi shield's IP address
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+
+  // print the received signal strength
+  long rssi = WiFi.RSSI();
+  Serial.print("Signal strength (RSSI):");
+  Serial.print(rssi);
+  Serial.println(" dBm");
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebClientSSL/WebClientSSL.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebClientSSL/WebClientSSL.ino
new file mode 100644
index 0000000000000000000000000000000000000000..1923dcdbd6b1bd1059880a99256d0a0f4ef9c6b6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebClientSSL/WebClientSSL.ino
@@ -0,0 +1,106 @@
+/*
+ WiFiEsp example: WebClient
+
+ This sketch connects to google website using an ESP8266 module to
+ perform a simple web search.
+
+ For more details see: http://yaab-arduino.blogspot.com/p/wifiesp-example-client.html
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+char ssid[] = "Twim";            // your network SSID (name)
+char pass[] = "12345678";        // your network password
+int status = WL_IDLE_STATUS;     // the Wifi radio's status
+
+char server[] = "www.google.com";
+
+// Initialize the Ethernet client object
+WiFiEspClient client;
+
+void setup()
+{
+  // initialize serial for debugging
+  Serial.begin(115200);
+  // initialize serial for ESP module
+  Serial1.begin(9600);
+  // initialize ESP module
+  WiFi.init(&Serial1);
+
+  // check for the presence of the shield
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    // don't continue
+    while (true);
+  }
+
+  // attempt to connect to WiFi network
+  while ( status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to WPA SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network
+    status = WiFi.begin(ssid, pass);
+  }
+
+  // you're connected now, so print out the data
+  Serial.println("You're connected to the network");
+  
+  printWifiStatus();
+
+  Serial.println();
+  Serial.println("Starting connection to server...");
+  // if you get a connection, report back via serial
+  if (client.connectSSL(server, 443)) {
+    Serial.println("Connected to server");
+    // Make a HTTP request
+    client.println("GET / HTTP/1.1");
+    client.println("Host: www.google.com");
+    client.println("Connection: close");
+    client.println();
+  }
+}
+
+void loop()
+{
+  // if there are incoming bytes available
+  // from the server, read them and print them
+  while (client.available()) {
+    char c = client.read();
+    Serial.write(c);
+  }
+
+  // if the server's disconnected, stop the client
+  if (!client.connected()) {
+    Serial.println();
+    Serial.println("Disconnecting from server...");
+    client.stop();
+
+    // do nothing forevermore
+    while (true);
+  }
+}
+
+
+void printWifiStatus()
+{
+  // print the SSID of the network you're attached to
+  Serial.print("SSID: ");
+  Serial.println(WiFi.SSID());
+
+  // print your WiFi shield's IP address
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+
+  // print the received signal strength
+  long rssi = WiFi.RSSI();
+  Serial.print("Signal strength (RSSI):");
+  Serial.print(rssi);
+  Serial.println(" dBm");
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebServer/WebServer.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebServer/WebServer.ino
new file mode 100644
index 0000000000000000000000000000000000000000..7322e90bb21b4c088b5fa562bef1f8c716febf1d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebServer/WebServer.ino
@@ -0,0 +1,136 @@
+/*
+ WiFiEsp example: WebServer
+
+ A simple web server that shows the value of the analog input 
+ pins via a web page using an ESP8266 module.
+ This sketch will print the IP address of your ESP8266 module (once connected)
+ to the Serial monitor. From there, you can open that address in a web browser
+ to display the web page.
+ The web page will be automatically refreshed each 20 seconds.
+
+ For more details see: http://yaab-arduino.blogspot.com/p/wifiesp.html
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+char ssid[] = "Twim";            // your network SSID (name)
+char pass[] = "12345678";        // your network password
+int status = WL_IDLE_STATUS;     // the Wifi radio's status
+int reqCount = 0;                // number of requests received
+
+WiFiEspServer server(80);
+
+
+void setup()
+{
+  // initialize serial for debugging
+  Serial.begin(115200);
+  // initialize serial for ESP module
+  Serial1.begin(9600);
+  // initialize ESP module
+  WiFi.init(&Serial1);
+
+  // check for the presence of the shield
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    // don't continue
+    while (true);
+  }
+
+  // attempt to connect to WiFi network
+  while ( status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to WPA SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network
+    status = WiFi.begin(ssid, pass);
+  }
+
+  Serial.println("You're connected to the network");
+  printWifiStatus();
+  
+  // start the web server on port 80
+  server.begin();
+}
+
+
+void loop()
+{
+  // listen for incoming clients
+  WiFiEspClient client = server.available();
+  if (client) {
+    Serial.println("New client");
+    // an http request ends with a blank line
+    boolean currentLineIsBlank = true;
+    while (client.connected()) {
+      if (client.available()) {
+        char c = client.read();
+        Serial.write(c);
+        // if you've gotten to the end of the line (received a newline
+        // character) and the line is blank, the http request has ended,
+        // so you can send a reply
+        if (c == '\n' && currentLineIsBlank) {
+          Serial.println("Sending response");
+          
+          // send a standard http response header
+          // use \r\n instead of many println statements to speedup data send
+          client.print(
+            "HTTP/1.1 200 OK\r\n"
+            "Content-Type: text/html\r\n"
+            "Connection: close\r\n"  // the connection will be closed after completion of the response
+            "Refresh: 20\r\n"        // refresh the page automatically every 20 sec
+            "\r\n");
+          client.print("<!DOCTYPE HTML>\r\n");
+          client.print("<html>\r\n");
+          client.print("<h1>Hello World!</h1>\r\n");
+          client.print("Requests received: ");
+          client.print(++reqCount);
+          client.print("<br>\r\n");
+          client.print("Analog input A0: ");
+          client.print(analogRead(0));
+          client.print("<br>\r\n");
+          client.print("</html>\r\n");
+          break;
+        }
+        if (c == '\n') {
+          // you're starting a new line
+          currentLineIsBlank = true;
+        }
+        else if (c != '\r') {
+          // you've gotten a character on the current line
+          currentLineIsBlank = false;
+        }
+      }
+    }
+    // give the web browser time to receive the data
+    delay(10);
+
+    // close the connection:
+    client.stop();
+    Serial.println("Client disconnected");
+  }
+}
+
+
+void printWifiStatus()
+{
+  // print the SSID of the network you're attached to
+  Serial.print("SSID: ");
+  Serial.println(WiFi.SSID());
+
+  // print your WiFi shield's IP address
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+  
+  // print where to go in the browser
+  Serial.println();
+  Serial.print("To see this page in action, open a browser to http://");
+  Serial.println(ip);
+  Serial.println();
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebServerAP/WebServerAP.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebServerAP/WebServerAP.ino
new file mode 100644
index 0000000000000000000000000000000000000000..4597557aa4aef364ea057b32c0c711edeb559db1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebServerAP/WebServerAP.ino
@@ -0,0 +1,128 @@
+/*
+ WiFiEsp example: WebServerAP
+
+ A simple web server that shows the value of the analog input 
+ pins via a web page using an ESP8266 module.
+ This sketch will start an access point and print the IP address of your
+ ESP8266 module to the Serial monitor. From there, you can open
+ that address in a web browser to display the web page.
+ The web page will be automatically refreshed each 20 seconds.
+
+ For more details see: http://yaab-arduino.blogspot.com/p/wifiesp.html
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+char ssid[] = "TwimEsp";         // your network SSID (name)
+char pass[] = "12345678";        // your network password
+int status = WL_IDLE_STATUS;     // the Wifi radio's status
+int reqCount = 0;                // number of requests received
+
+WiFiEspServer server(80);
+
+// use a ring buffer to increase speed and reduce memory allocation
+RingBuffer buf(8);
+
+void setup()
+{
+  Serial.begin(115200);   // initialize serial for debugging
+  Serial1.begin(9600);    // initialize serial for ESP module
+  WiFi.init(&Serial1);    // initialize ESP module
+
+  // check for the presence of the shield
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    while (true); // don't continue
+  }
+
+  Serial.print("Attempting to start AP ");
+  Serial.println(ssid);
+
+  // uncomment these two lines if you want to set the IP address of the AP
+  //IPAddress localIp(192, 168, 111, 111);
+  //WiFi.configAP(localIp);
+  
+  // start access point
+  status = WiFi.beginAP(ssid, 10, pass, ENC_TYPE_WPA2_PSK);
+
+  Serial.println("Access point started");
+  printWifiStatus();
+  
+  // start the web server on port 80
+  server.begin();
+  Serial.println("Server started");
+}
+
+
+void loop()
+{
+  WiFiEspClient client = server.available();  // listen for incoming clients
+
+  if (client) {                               // if you get a client,
+    Serial.println("New client");             // print a message out the serial port
+    buf.init();                               // initialize the circular buffer
+    while (client.connected()) {              // loop while the client's connected
+      if (client.available()) {               // if there's bytes to read from the client,
+        char c = client.read();               // read a byte, then
+        buf.push(c);                          // push it to the ring buffer
+
+        // you got two newline characters in a row
+        // that's the end of the HTTP request, so send a response
+        if (buf.endsWith("\r\n\r\n")) {
+          sendHttpResponse(client);
+          break;
+        }
+      }
+    }
+    
+    // give the web browser time to receive the data
+    delay(10);
+
+    // close the connection
+    client.stop();
+    Serial.println("Client disconnected");
+  }
+}
+
+void sendHttpResponse(WiFiEspClient client)
+{
+  client.print(
+    "HTTP/1.1 200 OK\r\n"
+    "Content-Type: text/html\r\n"
+    "Connection: close\r\n"  // the connection will be closed after completion of the response
+    "Refresh: 20\r\n"        // refresh the page automatically every 20 sec
+    "\r\n");
+  client.print("<!DOCTYPE HTML>\r\n");
+  client.print("<html>\r\n");
+  client.print("<h1>Hello World!</h1>\r\n");
+  client.print("Requests received: ");
+  client.print(++reqCount);
+  client.print("<br>\r\n");
+  client.print("Analog input A0: ");
+  client.print(analogRead(0));
+  client.print("<br>\r\n");
+  client.print("</html>\r\n");
+}
+
+void printWifiStatus()
+{
+  // print your WiFi shield's IP address
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+
+  // print where to go in the browser
+  Serial.println();
+  Serial.print("To see this page in action, connect to ");
+  Serial.print(ssid);
+  Serial.print(" and open a browser to http://");
+  Serial.println(ip);
+  Serial.println();
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebServerLed/WebServerLed.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebServerLed/WebServerLed.ino
new file mode 100644
index 0000000000000000000000000000000000000000..ab0c0528b9a9d33d37b7efc86c5f004b0e0aab60
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/examples/WebServerLed/WebServerLed.ino
@@ -0,0 +1,141 @@
+/*
+ WiFiEsp example: WebServerLed
+ 
+ A simple web server that lets you turn on and of an LED via a web page.
+ This sketch will print the IP address of your ESP8266 module (once connected)
+ to the Serial monitor. From there, you can open that address in a web browser
+ to turn on and off the LED on pin 13.
+
+ For more details see: http://yaab-arduino.blogspot.com/p/wifiesp.html
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 6/7 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+char ssid[] = "Twim";            // your network SSID (name)
+char pass[] = "12345678";        // your network password
+int status = WL_IDLE_STATUS;
+
+int ledStatus = LOW;
+
+WiFiEspServer server(80);
+
+// use a ring buffer to increase speed and reduce memory allocation
+RingBuffer buf(8);
+
+void setup()
+{
+  Serial.begin(115200);   // initialize serial for debugging
+  Serial1.begin(9600);    // initialize serial for ESP module
+  WiFi.init(&Serial1);    // initialize ESP module
+
+  // check for the presence of the shield
+  if (WiFi.status() == WL_NO_SHIELD) {
+    Serial.println("WiFi shield not present");
+    // don't continue
+    while (true);
+  }
+
+  // attempt to connect to WiFi network
+  while (status != WL_CONNECTED) {
+    Serial.print("Attempting to connect to WPA SSID: ");
+    Serial.println(ssid);
+    // Connect to WPA/WPA2 network
+    status = WiFi.begin(ssid, pass);
+  }
+
+  Serial.println("You're connected to the network");
+  printWifiStatus();
+  
+  // start the web server on port 80
+  server.begin();
+}
+
+
+void loop()
+{
+  WiFiEspClient client = server.available();  // listen for incoming clients
+
+  if (client) {                               // if you get a client,
+    Serial.println("New client");             // print a message out the serial port
+    buf.init();                               // initialize the circular buffer
+    while (client.connected()) {              // loop while the client's connected
+      if (client.available()) {               // if there's bytes to read from the client,
+        char c = client.read();               // read a byte, then
+        buf.push(c);                          // push it to the ring buffer
+
+        // printing the stream to the serial monitor will slow down
+        // the receiving of data from the ESP filling the serial buffer
+        //Serial.write(c);
+        
+        // you got two newline characters in a row
+        // that's the end of the HTTP request, so send a response
+        if (buf.endsWith("\r\n\r\n")) {
+          sendHttpResponse(client);
+          break;
+        }
+
+        // Check to see if the client request was "GET /H" or "GET /L":
+        if (buf.endsWith("GET /H")) {
+          Serial.println("Turn led ON");
+          ledStatus = HIGH;
+          digitalWrite(13, HIGH);
+        }
+        else if (buf.endsWith("GET /L")) {
+          Serial.println("Turn led OFF");
+          ledStatus = LOW;
+          digitalWrite(13, LOW);
+        }
+      }
+    }
+    
+    // close the connection
+    client.stop();
+    Serial.println("Client disconnected");
+  }
+}
+
+
+void sendHttpResponse(WiFiEspClient client)
+{
+  // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
+  // and a content-type so the client knows what's coming, then a blank line:
+  client.println("HTTP/1.1 200 OK");
+  client.println("Content-type:text/html");
+  client.println();
+  
+  // the content of the HTTP response follows the header:
+  client.print("The LED is ");
+  client.print(ledStatus);
+  client.println("<br>");
+  client.println("<br>");
+  
+  client.println("Click <a href=\"/H\">here</a> turn the LED on<br>");
+  client.println("Click <a href=\"/L\">here</a> turn the LED off<br>");
+  
+  // The HTTP response ends with another blank line:
+  client.println();
+}
+
+void printWifiStatus()
+{
+  // print the SSID of the network you're attached to
+  Serial.print("SSID: ");
+  Serial.println(WiFi.SSID());
+
+  // print your WiFi shield's IP address
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+
+  // print where to go in the browser
+  Serial.println();
+  Serial.print("To see this page in action, open a browser to http://");
+  Serial.println(ip);
+  Serial.println();
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/keywords.txt b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/keywords.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3270a7132cdc13b7bb18b30030c2c93422dab466
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/keywords.txt
@@ -0,0 +1,59 @@
+#######################################
+# Syntax Coloring Map For WiFiEsp
+#######################################
+
+#######################################
+# Library (KEYWORD3)
+#######################################
+
+WiFiEsp	KEYWORD3
+
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+WiFiEspClient	KEYWORD1
+WiFiEspServer	KEYWORD1
+WiFiEspUDP	KEYWORD1
+RingBuffer	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+firmwareVersion	KEYWORD2
+status	KEYWORD2
+connect	KEYWORD2
+write	KEYWORD2
+available	KEYWORD2
+config	KEYWORD2
+setDNS	KEYWORD2
+read	KEYWORD2
+flush	KEYWORD2
+stop	KEYWORD2
+connected	KEYWORD2
+begin	KEYWORD2
+disconnect	KEYWORD2
+macAddress	KEYWORD2
+localIP	KEYWORD2
+subnetMask	KEYWORD2
+gatewayIP	KEYWORD2
+scanNetworks	KEYWORD2
+SSID	KEYWORD2
+BSSID		KEYWORD2
+RSSI	KEYWORD2
+encryptionType	KEYWORD2
+getResult	KEYWORD2
+getSocket	KEYWORD2
+beginPacket	KEYWORD2
+endPacket	KEYWORD2
+parsePacket	KEYWORD2
+remoteIP	KEYWORD2
+remotePort	KEYWORD2
+
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/library.properties b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/library.properties
new file mode 100644
index 0000000000000000000000000000000000000000..8321c46b483f9b759d48f155674a24f22cdecf07
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/library.properties
@@ -0,0 +1,9 @@
+name=WiFiEsp
+version=2.2.1
+author=bportaluri
+maintainer=Bruno Portaluri <bportaluri@gmail.com>
+sentence=Arduino WiFi library for ESP8266
+paragraph=Arduino WiFi library for ESP8266. Works only with SDK version 1.1.1 and above (AT version 0.25 and above).
+category=Other
+url=https://github.com/bportaluri/WiFiEsp
+architectures=*
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEsp.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEsp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..70e384ee6aa8d08028c20a50c6c1612d19360444
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEsp.cpp
@@ -0,0 +1,233 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#include "WiFiEsp.h"
+
+
+int16_t 	WiFiEspClass::_state[MAX_SOCK_NUM] = { NA_STATE, NA_STATE, NA_STATE, NA_STATE };
+uint16_t 	WiFiEspClass::_server_port[MAX_SOCK_NUM] = { 0, 0, 0, 0 };
+
+
+uint8_t WiFiEspClass::espMode = 0;
+
+
+WiFiEspClass::WiFiEspClass()
+{
+
+}
+
+void WiFiEspClass::init(Stream *espSerial)
+{
+    LOGINFO(F("Initializing ESP module"));
+	EspDrv::wifiDriverInit(espSerial);
+}
+
+
+
+char* WiFiEspClass::firmwareVersion()
+{
+	return EspDrv::getFwVersion();
+}
+
+
+int WiFiEspClass::begin(char* ssid, const char *passphrase)
+{
+    espMode = 1;
+	if (EspDrv::wifiConnect(ssid, passphrase))
+		return WL_CONNECTED;
+
+	return WL_CONNECT_FAILED;
+}
+
+
+int WiFiEspClass::beginAP(char* ssid, uint8_t channel, const char* pwd, uint8_t enc, bool apOnly)
+{
+	if(apOnly)
+        espMode = 2;
+    else
+        espMode = 3;
+    
+    if (EspDrv::wifiStartAP(ssid, pwd, channel, enc, espMode))
+		return WL_CONNECTED;
+
+	return WL_CONNECT_FAILED;
+}
+
+int WiFiEspClass::beginAP(char* ssid)
+{
+	return beginAP(ssid, 10, "", 0);
+}
+
+int WiFiEspClass::beginAP(char* ssid, uint8_t channel)
+{
+	return beginAP(ssid, channel, "", 0);
+}
+
+
+void WiFiEspClass::config(IPAddress ip)
+{
+	EspDrv::config(ip);
+}
+
+void WiFiEspClass::configAP(IPAddress ip)
+{
+	EspDrv::configAP(ip);
+}
+
+
+
+int WiFiEspClass::disconnect()
+{
+    return EspDrv::disconnect();
+}
+
+uint8_t* WiFiEspClass::macAddress(uint8_t* mac)
+{
+	// TODO we don't need _mac variable
+	uint8_t* _mac = EspDrv::getMacAddress();
+	memcpy(mac, _mac, WL_MAC_ADDR_LENGTH);
+    return mac;
+}
+
+IPAddress WiFiEspClass::localIP()
+{
+	IPAddress ret;
+	if(espMode==1)
+		EspDrv::getIpAddress(ret);
+	else
+		EspDrv::getIpAddressAP(ret);
+	return ret;
+}
+
+IPAddress WiFiEspClass::subnetMask()
+{
+	IPAddress mask;
+	if(espMode==1)
+    EspDrv::getNetmask(mask);
+	return mask;
+}
+
+IPAddress WiFiEspClass::gatewayIP()
+{
+	IPAddress gw;
+	if(espMode==1)
+		EspDrv::getGateway(gw);
+	return gw;
+}
+
+
+char* WiFiEspClass::SSID()
+{
+    return EspDrv::getCurrentSSID();
+}
+
+uint8_t* WiFiEspClass::BSSID(uint8_t* bssid)
+{
+	// TODO we don't need _bssid
+	uint8_t* _bssid = EspDrv::getCurrentBSSID();
+	memcpy(bssid, _bssid, WL_MAC_ADDR_LENGTH);
+    return bssid;
+}
+
+int32_t WiFiEspClass::RSSI()
+{
+    return EspDrv::getCurrentRSSI();
+}
+
+
+int8_t WiFiEspClass::scanNetworks()
+{
+	return EspDrv::getScanNetworks();
+}
+
+char* WiFiEspClass::SSID(uint8_t networkItem)
+{
+	return EspDrv::getSSIDNetoworks(networkItem);
+}
+
+int32_t WiFiEspClass::RSSI(uint8_t networkItem)
+{
+	return EspDrv::getRSSINetoworks(networkItem);
+}
+
+uint8_t WiFiEspClass::encryptionType(uint8_t networkItem)
+{
+    return EspDrv::getEncTypeNetowrks(networkItem);
+}
+
+
+uint8_t WiFiEspClass::status()
+{
+	return EspDrv::getConnectionStatus();
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// Non standard methods
+////////////////////////////////////////////////////////////////////////////
+
+void WiFiEspClass::reset(void)
+{
+	EspDrv::reset();
+}
+
+
+/*
+void ESP8266::hardReset(void)
+{
+connected = false;
+strcpy(ip, "");
+digitalWrite(ESP8266_RST, LOW);
+delay(ESP8266_HARD_RESET_DURACTION);
+digitalWrite(ESP8266_RST, HIGH);
+delay(ESP8266_HARD_RESET_DURACTION);
+}
+*/
+
+
+bool WiFiEspClass::ping(const char *host)
+{
+	return EspDrv::ping(host);
+}
+
+uint8_t WiFiEspClass::getFreeSocket()
+{
+  // ESP Module assigns socket numbers in ascending order, so we will assign them in descending order
+    for (int i = MAX_SOCK_NUM - 1; i >= 0; i--)
+	{
+      if (_state[i] == NA_STATE)
+      {
+          return i;
+      }
+    }
+    return SOCK_NOT_AVAIL;
+}
+
+void WiFiEspClass::allocateSocket(uint8_t sock)
+{
+  _state[sock] = sock;
+}
+
+void WiFiEspClass::releaseSocket(uint8_t sock)
+{
+  _state[sock] = NA_STATE;
+}
+
+
+WiFiEspClass WiFi;
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEsp.h b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEsp.h
new file mode 100644
index 0000000000000000000000000000000000000000..7bf234e232b5fc6c5b0a4a573dccc51e7ee32565
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEsp.h
@@ -0,0 +1,274 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#ifndef WiFiEsp_h
+#define WiFiEsp_h
+
+#include <Arduino.h>
+#include <Stream.h>
+#include <IPAddress.h>
+#include <inttypes.h>
+
+
+#include "WiFiEspClient.h"
+#include "WiFiEspServer.h"
+#include "utility/EspDrv.h"
+#include "utility/RingBuffer.h"
+#include "utility/debug.h"
+
+
+class WiFiEspClass
+{
+
+public:
+
+	static int16_t _state[MAX_SOCK_NUM];
+	static uint16_t _server_port[MAX_SOCK_NUM];
+
+	WiFiEspClass();
+
+
+	/**
+	* Initialize the ESP module.
+	*
+	* param espSerial: the serial interface (HW or SW) used to communicate with the ESP module
+	*/
+	static void init(Stream *espSerial);
+
+
+	/**
+	* Get firmware version
+	*/
+	static char* firmwareVersion();
+
+
+	// NOT IMPLEMENTED
+	//int begin(char* ssid);
+
+	// NOT IMPLEMENTED
+	//int begin(char* ssid, uint8_t key_idx, const char* key);
+
+
+	/**
+	* Start Wifi connection with passphrase
+	* the most secure supported mode will be automatically selected
+	*
+	* param ssid: Pointer to the SSID string.
+	* param passphrase: Passphrase. Valid characters in a passphrase
+	*		  must be between ASCII 32-126 (decimal).
+	*/
+	int begin(char* ssid, const char* passphrase);
+
+
+	/**
+	* Change Ip configuration settings disabling the DHCP client
+	*
+	* param local_ip:	Static ip configuration
+	*/
+	void config(IPAddress local_ip);
+
+
+	// NOT IMPLEMENTED
+	//void config(IPAddress local_ip, IPAddress dns_server);
+
+	// NOT IMPLEMENTED
+	//void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway);
+
+	// NOT IMPLEMENTED
+	//void config(IPAddress local_ip, IPAddress dns_server, IPAddress gateway, IPAddress subnet);
+
+	// NOT IMPLEMENTED
+	//void setDNS(IPAddress dns_server1);
+
+	// NOT IMPLEMENTED
+	//void setDNS(IPAddress dns_server1, IPAddress dns_server2);
+
+	/**
+	* Disconnect from the network
+	*
+	* return: one value of wl_status_t enum
+	*/
+	int disconnect(void);
+
+	/**
+	* Get the interface MAC address.
+	*
+	* return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
+	*/
+	uint8_t* macAddress(uint8_t* mac);
+
+	/**
+	* Get the interface IP address.
+	*
+	* return: Ip address value
+	*/
+	IPAddress localIP();
+
+
+	/**
+	* Get the interface subnet mask address.
+	*
+	* return: subnet mask address value
+	*/
+	IPAddress subnetMask();
+
+	/**
+	* Get the gateway ip address.
+	*
+	* return: gateway ip address value
+	*/
+   IPAddress gatewayIP();
+
+	/**
+	* Return the current SSID associated with the network
+	*
+	* return: ssid string
+	*/
+	char* SSID();
+
+	/**
+	* Return the current BSSID associated with the network.
+	* It is the MAC address of the Access Point
+	*
+	* return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
+	*/
+	uint8_t* BSSID(uint8_t* bssid);
+
+
+	/**
+	* Return the current RSSI /Received Signal Strength in dBm)
+	* associated with the network
+	*
+	* return: signed value
+	*/
+	int32_t RSSI();
+
+
+	/**
+	* Return Connection status.
+	*
+	* return: one of the value defined in wl_status_t
+	*         see https://www.arduino.cc/en/Reference/WiFiStatus
+	*/
+	uint8_t status();
+
+
+    /*
+      * Return the Encryption Type associated with the network
+      *
+      * return: one value of wl_enc_type enum
+      */
+    //uint8_t	encryptionType();
+
+    /*
+     * Start scan WiFi networks available
+     *
+     * return: Number of discovered networks
+     */
+    int8_t scanNetworks();
+
+    /*
+     * Return the SSID discovered during the network scan.
+     *
+     * param networkItem: specify from which network item want to get the information
+	 *
+     * return: ssid string of the specified item on the networks scanned list
+     */
+    char*	SSID(uint8_t networkItem);
+
+    /*
+     * Return the encryption type of the networks discovered during the scanNetworks
+     *
+     * param networkItem: specify from which network item want to get the information
+	 *
+     * return: encryption type (enum wl_enc_type) of the specified item on the networks scanned list
+     */
+    uint8_t	encryptionType(uint8_t networkItem);
+
+    /*
+     * Return the RSSI of the networks discovered during the scanNetworks
+     *
+     * param networkItem: specify from which network item want to get the information
+	 *
+     * return: signed value of RSSI of the specified item on the networks scanned list
+     */
+    int32_t RSSI(uint8_t networkItem);
+
+
+	// NOT IMPLEMENTED
+	//int hostByName(const char* aHostname, IPAddress& aResult);
+
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Non standard methods
+	////////////////////////////////////////////////////////////////////////////
+
+	/**
+	* Start the ESP access point.
+	*
+	* param ssid: Pointer to the SSID string.
+	* param channel: WiFi channel (1-14)
+	* param pwd: Passphrase. Valid characters in a passphrase
+	*		  must be between ASCII 32-126 (decimal).
+	* param enc: encryption type (enum wl_enc_type)
+	* param apOnly: Set to false if you want to run AP and Station modes simultaneously
+	*/
+	int beginAP(char* ssid, uint8_t channel, const char* pwd, uint8_t enc, bool apOnly=true);
+
+	/*
+	* Start the ESP access point with open security.
+	*/
+	int beginAP(char* ssid);
+	int beginAP(char* ssid, uint8_t channel);
+
+	/**
+	* Change IP address of the AP
+	*
+	* param ip:	Static ip configuration
+	*/
+	void configAP(IPAddress ip);
+
+
+
+	/**
+	* Restart the ESP module.
+	*/
+	void reset();
+
+	/**
+	* Ping a host.
+	*/
+	bool ping(const char *host);
+
+
+	friend class WiFiEspClient;
+	friend class WiFiEspServer;
+	friend class WiFiEspUDP;
+
+private:
+	static uint8_t getFreeSocket();
+	static void allocateSocket(uint8_t sock);
+	static void releaseSocket(uint8_t sock);
+
+	static uint8_t espMode;
+};
+
+extern WiFiEspClass WiFi;
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspClient.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspClient.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fda6632796b880f7714cbc4f0224fdb5e4542c54
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspClient.cpp
@@ -0,0 +1,290 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#include <inttypes.h>
+
+#include "WiFiEsp.h"
+#include "WiFiEspClient.h"
+#include "WiFiEspServer.h"
+
+#include "utility/EspDrv.h"
+#include "utility/debug.h"
+
+
+WiFiEspClient::WiFiEspClient() : _sock(255)
+{
+}
+
+WiFiEspClient::WiFiEspClient(uint8_t sock) : _sock(sock)
+{
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Overrided Print methods
+////////////////////////////////////////////////////////////////////////////////
+
+// the standard print method will call write for each character in the buffer
+// this is very slow on ESP
+size_t WiFiEspClient::print(const __FlashStringHelper *ifsh)
+{
+	printFSH(ifsh, false);
+}
+
+// if we do override this, the standard println will call the print
+// method twice
+size_t WiFiEspClient::println(const __FlashStringHelper *ifsh)
+{
+	printFSH(ifsh, true);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Implementation of Client virtual methods
+////////////////////////////////////////////////////////////////////////////////
+
+int WiFiEspClient::connectSSL(const char* host, uint16_t port)
+{
+	return connect(host, port, SSL_MODE);
+}
+
+int WiFiEspClient::connectSSL(IPAddress ip, uint16_t port)
+{
+	char s[16];
+	sprintf(s, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+	return connect(s, port, SSL_MODE);
+}
+
+int WiFiEspClient::connect(const char* host, uint16_t port)
+{
+    return connect(host, port, TCP_MODE);
+}
+
+int WiFiEspClient::connect(IPAddress ip, uint16_t port)
+{
+	char s[16];
+	sprintf(s, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+
+	return connect(s, port, TCP_MODE);
+}
+
+/* Private method */
+int WiFiEspClient::connect(const char* host, uint16_t port, uint8_t protMode)
+{
+	LOGINFO1(F("Connecting to"), host);
+
+	_sock = WiFiEspClass::getFreeSocket();
+
+    if (_sock != NO_SOCKET_AVAIL)
+    {
+    	if (!EspDrv::startClient(host, port, _sock, protMode))
+			return 0;
+
+    	WiFiEspClass::allocateSocket(_sock);
+    }
+	else
+	{
+    	Serial.println(F("No socket available"));
+    	return 0;
+    }
+    return 1;
+}
+
+
+
+size_t WiFiEspClient::write(uint8_t b)
+{
+	  return write(&b, 1);
+}
+
+size_t WiFiEspClient::write(const uint8_t *buf, size_t size)
+{
+	if (_sock >= MAX_SOCK_NUM or size==0)
+	{
+		setWriteError();
+		return 0;
+	}
+
+	bool r = EspDrv::sendData(_sock, buf, size);
+	if (!r)
+	{
+		setWriteError();
+		LOGERROR1(F("Failed to write to socket"), _sock);
+		delay(4000);
+		stop();
+		return 0;
+	}
+
+	return size;
+}
+
+
+
+int WiFiEspClient::available()
+{
+	if (_sock != 255)
+	{
+		int bytes = EspDrv::availData(_sock);
+		if (bytes>0)
+		{
+			return bytes;
+		}
+	}
+
+	return 0;
+}
+
+int WiFiEspClient::read()
+{
+	uint8_t b;
+	if (!available())
+		return -1;
+
+	bool connClose = false;
+	EspDrv::getData(_sock, &b, false, &connClose);
+
+	if (connClose)
+	{
+		WiFiEspClass::releaseSocket(_sock);
+		_sock = 255;
+	}
+
+	return b;
+}
+
+int WiFiEspClient::read(uint8_t* buf, size_t size)
+{
+	if (!available())
+		return -1;
+	return EspDrv::getDataBuf(_sock, buf, size);
+}
+
+int WiFiEspClient::peek()
+{
+	uint8_t b;
+	if (!available())
+		return -1;
+
+	bool connClose = false;
+	EspDrv::getData(_sock, &b, true, &connClose);
+
+	if (connClose)
+	{
+		WiFiEspClass::releaseSocket(_sock);
+		_sock = 255;
+	}
+
+	return b;
+}
+
+
+void WiFiEspClient::flush()
+{
+	while (available())
+		read();
+}
+
+
+
+void WiFiEspClient::stop()
+{
+	if (_sock == 255)
+		return;
+
+	LOGINFO1(F("Disconnecting "), _sock);
+
+	EspDrv::stopClient(_sock);
+
+	WiFiEspClass::releaseSocket(_sock);
+	_sock = 255;
+}
+
+
+uint8_t WiFiEspClient::connected()
+{
+	return (status() == ESTABLISHED);
+}
+
+
+WiFiEspClient::operator bool()
+{
+  return _sock != 255;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Additional WiFi standard methods
+////////////////////////////////////////////////////////////////////////////////
+
+
+uint8_t WiFiEspClient::status()
+{
+	if (_sock == 255)
+	{
+		return CLOSED;
+	}
+
+	if (EspDrv::availData(_sock))
+	{
+		return ESTABLISHED;
+	}
+
+	if (EspDrv::getClientState(_sock))
+	{
+		return ESTABLISHED;
+	}
+
+	WiFiEspClass::releaseSocket(_sock);
+	_sock = 255;
+
+	return CLOSED;
+}
+
+IPAddress WiFiEspClient::remoteIP()
+{
+	IPAddress ret;
+	EspDrv::getRemoteIpAddress(ret);
+	return ret;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Private Methods
+////////////////////////////////////////////////////////////////////////////////
+
+size_t WiFiEspClient::printFSH(const __FlashStringHelper *ifsh, bool appendCrLf)
+{
+	size_t size = strlen_P((char*)ifsh);
+	
+	if (_sock >= MAX_SOCK_NUM or size==0)
+	{
+		setWriteError();
+		return 0;
+	}
+
+	bool r = EspDrv::sendData(_sock, ifsh, size, appendCrLf);
+	if (!r)
+	{
+		setWriteError();
+		LOGERROR1(F("Failed to write to socket"), _sock);
+		delay(4000);
+		stop();
+		return 0;
+	}
+
+	return size;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspClient.h b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspClient.h
new file mode 100644
index 0000000000000000000000000000000000000000..1e3823c264d3901dee1cbd6274c7b3a48b9750f5
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspClient.h
@@ -0,0 +1,144 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#ifndef WiFiEspClient_h
+#define WiFiEspClient_h
+
+
+#include "Arduino.h"
+#include "Print.h"
+#include "Client.h"
+#include "IPAddress.h"
+
+
+
+class WiFiEspClient : public Client
+{
+public:
+  WiFiEspClient();
+  WiFiEspClient(uint8_t sock);
+  
+  
+  // override Print.print method
+  
+  size_t print(const __FlashStringHelper *ifsh);
+  size_t println(const __FlashStringHelper *ifsh);
+
+
+  /*
+  * Connect to the specified IP address and port. The return value indicates success or failure.
+  * Returns true if the connection succeeds, false if not.
+  */
+  virtual int connect(IPAddress ip, uint16_t port);
+
+  /*
+  * Connect to the specified host and port. The return value indicates success or failure.
+  * Returns true if the connection succeeds, false if not.
+  */
+  virtual int connect(const char *host, uint16_t port);
+
+  /*
+  * Connect to the specified IP address and port using SSL. The return value indicates success or failure.
+  * Returns true if the connection succeeds, false if not.
+  */
+  int connectSSL(IPAddress ip, uint16_t port);
+  
+  /*
+  * Connect to the specified host and port using SSL. The return value indicates success or failure.
+  * Returns true if the connection succeeds, false if not.
+  */
+  int connectSSL(const char* host, uint16_t port);
+  
+  /*
+  * Write a character to the server the client is connected to.
+  * Returns the number of characters written.
+  */
+  virtual size_t write(uint8_t);
+
+  /*
+  * Write data to the server the client is connected to.
+  * Returns the number of characters written.
+  */
+  virtual size_t write(const uint8_t *buf, size_t size);
+
+
+  virtual int available();
+
+  /*
+  * Read the next byte received from the server the client is connected to (after the last call to read()).
+  * Returns the next byte (or character), or -1 if none is available.
+  */
+  virtual int read();
+
+
+  virtual int read(uint8_t *buf, size_t size);
+
+  /*
+  * Returns the next byte (character) of incoming serial data without removing it from the internal serial buffer.
+  */
+  virtual int peek();
+
+  /*
+  * Discard any bytes that have been written to the client but not yet read.
+  */
+  virtual void flush();
+
+  /*
+  * Disconnect from the server.
+  */
+  virtual void stop();
+
+  /*
+  * Whether or not the client is connected.
+  * Note that a client is considered connected if the connection has been closed but there is still unread data.
+  * Returns true if the client is connected, false if not.
+  */
+  virtual uint8_t connected();
+
+
+  uint8_t status();
+  
+  virtual operator bool();
+
+  
+  // needed to correctly handle overriding
+  // see http://stackoverflow.com/questions/888235/overriding-a-bases-overloaded-function-in-c
+  using Print::write;
+  using Print::print;
+  using Print::println;
+
+
+  /*
+  * Returns the remote IP address.
+  */
+  IPAddress remoteIP();
+  
+
+  friend class WiFiEspServer;
+
+private:
+
+  uint8_t _sock;     // connection id
+
+  int connect(const char* host, uint16_t port, uint8_t protMode);
+  
+  size_t printFSH(const __FlashStringHelper *ifsh, bool appendCrLf);
+
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspServer.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspServer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..44384bafb3d3575bdf4758cc36814bce994acb6c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspServer.cpp
@@ -0,0 +1,99 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#include "WiFiEspServer.h"
+
+#include "utility/EspDrv.h"
+#include "utility/debug.h"
+
+
+
+WiFiEspServer::WiFiEspServer(uint16_t port)
+{
+	_port = port;
+}
+
+void WiFiEspServer::begin()
+{
+	LOGDEBUG(F("Starting server"));
+
+	/* The ESP Module only allows socket 1 to be used for the server */
+#if 0
+	_sock = WiFiEspClass::getFreeSocket();
+	if (_sock == SOCK_NOT_AVAIL)
+	  {
+	    LOGERROR(F("No socket available for server"));
+	    return;
+	  }
+#else
+	_sock = 1; // If this is already in use, the startServer attempt will fail
+#endif
+	WiFiEspClass::allocateSocket(_sock);
+
+	_started = EspDrv::startServer(_port, _sock);
+
+	if (_started)
+	{
+		LOGINFO1(F("Server started on port"), _port);
+	}
+	else
+	{
+		LOGERROR(F("Server failed to start"));
+	}
+}
+
+WiFiEspClient WiFiEspServer::available(byte* status)
+{
+	// TODO the original method seems to handle automatic server restart
+
+	int bytes = EspDrv::availData(0);
+	if (bytes>0)
+	{
+		LOGINFO1(F("New client"), EspDrv::_connId);
+		WiFiEspClass::allocateSocket(EspDrv::_connId);
+		WiFiEspClient client(EspDrv::_connId);
+		return client;
+	}
+
+    return WiFiEspClient(255);
+}
+
+uint8_t WiFiEspServer::status()
+{
+    return EspDrv::getServerState(0);
+}
+
+size_t WiFiEspServer::write(uint8_t b)
+{
+    return write(&b, 1);
+}
+
+size_t WiFiEspServer::write(const uint8_t *buffer, size_t size)
+{
+	size_t n = 0;
+
+    for (int sock = 0; sock < MAX_SOCK_NUM; sock++)
+    {
+        if (WiFiEspClass::_state[sock] != 0)
+        {
+        	WiFiEspClient client(sock);
+            n += client.write(buffer, size);
+        }
+    }
+    return n;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspServer.h b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspServer.h
new file mode 100644
index 0000000000000000000000000000000000000000..2564c3d5d3bc461820eaf1228437ab08e1e9b4d9
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspServer.h
@@ -0,0 +1,63 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#ifndef WiFiEspServer_h
+#define WiFiEspServer_h
+
+#include <Server.h>
+
+#include "WiFiEsp.h"
+
+
+class WiFiEspClient;
+
+class WiFiEspServer : public Server
+{
+
+public:
+	WiFiEspServer(uint16_t port);
+
+
+	/*
+	* Gets a client that is connected to the server and has data available for reading.
+	* The connection persists when the returned client object goes out of scope; you can close it by calling client.stop().
+	* Returns a Client object; if no Client has data available for reading, this object will evaluate to false in an if-statement.
+	*/
+	WiFiEspClient available(uint8_t* status = NULL);
+
+	/*
+	* Start the TCP server
+	*/
+	void begin();
+
+	virtual size_t write(uint8_t);
+	virtual size_t write(const uint8_t *buf, size_t size);
+
+	uint8_t status();
+
+	using Print::write;
+
+
+private:
+	uint16_t _port;
+	uint8_t _sock;
+	bool _started;
+
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspUdp.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspUdp.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7305aff0574f824088b348f0ec08aacd127ef2d0
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspUdp.cpp
@@ -0,0 +1,193 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#include "WiFiEsp.h"
+#include "WiFiEspUdp.h"
+
+#include "utility/EspDrv.h"
+#include "utility/debug.h"
+
+/* Constructor */
+WiFiEspUDP::WiFiEspUDP() : _sock(NO_SOCKET_AVAIL) {}
+
+
+
+
+/* Start WiFiUDP socket, listening at local port PORT */
+
+uint8_t WiFiEspUDP::begin(uint16_t port)
+{
+    uint8_t sock = WiFiEspClass::getFreeSocket();
+    if (sock != NO_SOCKET_AVAIL)
+    {
+        EspDrv::startClient("0", port, sock, UDP_MODE);
+		
+        WiFiEspClass::allocateSocket(sock);  // allocating the socket for the listener
+        WiFiEspClass::_server_port[sock] = port;
+        _sock = sock;
+        _port = port;
+        return 1;
+    }
+    return 0;
+
+}
+
+
+/* return number of bytes available in the current packet,
+   will return zero if parsePacket hasn't been called yet */
+int WiFiEspUDP::available()
+{
+	 if (_sock != NO_SOCKET_AVAIL)
+	 {
+		int bytes = EspDrv::availData(_sock);
+		if (bytes>0)
+		{
+			return bytes;
+		}
+	}
+
+	return 0;
+}
+
+/* Release any resources being used by this WiFiUDP instance */
+void WiFiEspUDP::stop()
+{
+	  if (_sock == NO_SOCKET_AVAIL)
+	    return;
+
+      // Discard data that might be in the incoming buffer
+      flush();
+      
+      // Stop the listener and return the socket to the pool
+	  EspDrv::stopClient(_sock);
+      WiFiEspClass::_state[_sock] = NA_STATE;
+      WiFiEspClass::_server_port[_sock] = 0;
+
+	  _sock = NO_SOCKET_AVAIL;
+}
+
+int WiFiEspUDP::beginPacket(const char *host, uint16_t port)
+{
+  if (_sock == NO_SOCKET_AVAIL)
+	  _sock = WiFiEspClass::getFreeSocket();
+  if (_sock != NO_SOCKET_AVAIL)
+  {
+	  //EspDrv::startClient(host, port, _sock, UDP_MODE);
+	  _remotePort = port;
+	  strcpy(_remoteHost, host);
+	  WiFiEspClass::allocateSocket(_sock);
+	  return 1;
+  }
+  return 0;
+}
+
+
+int WiFiEspUDP::beginPacket(IPAddress ip, uint16_t port)
+{
+	char s[18];
+	sprintf(s, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+
+	return beginPacket(s, port);
+}
+
+
+int WiFiEspUDP::endPacket()
+{
+	return 1; //ServerDrv::sendUdpData(_sock);
+}
+
+size_t WiFiEspUDP::write(uint8_t byte)
+{
+  return write(&byte, 1);
+}
+
+size_t WiFiEspUDP::write(const uint8_t *buffer, size_t size)
+{
+	bool r = EspDrv::sendDataUdp(_sock, _remoteHost, _remotePort, buffer, size);
+	if (!r)
+	{
+		return 0;
+	}
+
+	return size;
+}
+
+int WiFiEspUDP::parsePacket()
+{
+	return available();
+}
+
+int WiFiEspUDP::read()
+{
+	uint8_t b;
+	if (!available())
+		return -1;
+
+	bool connClose = false;
+	
+    // Read the data and handle the timeout condition
+	if (! EspDrv::getData(_sock, &b, false, &connClose))
+      return -1;  // Timeout occured
+
+	return b;
+}
+
+int WiFiEspUDP::read(uint8_t* buf, size_t size)
+{
+	if (!available())
+		return -1;
+	return EspDrv::getDataBuf(_sock, buf, size);
+}
+
+int WiFiEspUDP::peek()
+{
+  uint8_t b;
+  if (!available())
+    return -1;
+
+  return b;
+}
+
+void WiFiEspUDP::flush()
+{
+	  // Discard all input data
+	  int count = available();
+	  while (count-- > 0)
+	    read();
+}
+
+
+IPAddress  WiFiEspUDP::remoteIP()
+{
+	IPAddress ret;
+	EspDrv::getRemoteIpAddress(ret);
+	return ret;
+}
+
+uint16_t  WiFiEspUDP::remotePort()
+{
+	return EspDrv::getRemotePort();
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////////
+// Private Methods
+////////////////////////////////////////////////////////////////////////////////
+
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspUdp.h b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspUdp.h
new file mode 100644
index 0000000000000000000000000000000000000000..f84b15418c28227e441a3e20d3e202e0cf28fc99
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/WiFiEspUdp.h
@@ -0,0 +1,97 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#ifndef WiFiEspUdp_h
+#define WiFiEspUdp_h
+
+#include <Udp.h>
+
+#define UDP_TX_PACKET_MAX_SIZE 24
+
+class WiFiEspUDP : public UDP {
+private:
+  uint8_t _sock;  // socket ID for Wiz5100
+  uint16_t _port; // local port to listen on
+  
+  
+  uint16_t _remotePort;
+  char _remoteHost[30];
+  
+
+public:
+  WiFiEspUDP();  // Constructor
+
+  virtual uint8_t begin(uint16_t);	// initialize, start listening on specified port. Returns 1 if successful, 0 if there are no sockets available to use
+  virtual void stop();  // Finish with the UDP socket
+
+  // Sending UDP packets
+
+  // Start building up a packet to send to the remote host specific in ip and port
+  // Returns 1 if successful, 0 if there was a problem with the supplied IP address or port
+  virtual int beginPacket(IPAddress ip, uint16_t port);
+
+  // Start building up a packet to send to the remote host specific in host and port
+  // Returns 1 if successful, 0 if there was a problem resolving the hostname or port
+  virtual int beginPacket(const char *host, uint16_t port);
+
+  // Finish off this packet and send it
+  // Returns 1 if the packet was sent successfully, 0 if there was an error
+  virtual int endPacket();
+
+  // Write a single byte into the packet
+  virtual size_t write(uint8_t);
+
+  // Write size bytes from buffer into the packet
+  virtual size_t write(const uint8_t *buffer, size_t size);
+
+  using Print::write;
+
+  // Start processing the next available incoming packet
+  // Returns the size of the packet in bytes, or 0 if no packets are available
+  virtual int parsePacket();
+
+  // Number of bytes remaining in the current packet
+  virtual int available();
+
+  // Read a single byte from the current packet
+  virtual int read();
+
+  // Read up to len bytes from the current packet and place them into buffer
+  // Returns the number of bytes read, or 0 if none are available
+  virtual int read(unsigned char* buffer, size_t len);
+
+  // Read up to len characters from the current packet and place them into buffer
+  // Returns the number of characters read, or 0 if none are available
+  virtual int read(char* buffer, size_t len) { return read((unsigned char*)buffer, len); };
+
+  // Return the next byte from the current packet without moving on to the next byte
+  virtual int peek();
+
+  virtual void flush();	// Finish reading the current packet
+
+  // Return the IP address of the host who sent the current incoming packet
+  virtual IPAddress remoteIP();
+
+  // Return the port of the host who sent the current incoming packet
+  virtual uint16_t remotePort();
+
+
+  friend class WiFiEspServer;
+};
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/EspDrv.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/EspDrv.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b01ac0fcb6f0a82f01be8fbef7030dbd51101a86
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/EspDrv.cpp
@@ -0,0 +1,1127 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#include <Arduino.h>
+#include <avr/pgmspace.h>
+
+#include "utility/EspDrv.h"
+#include "utility/debug.h"
+
+
+#define NUMESPTAGS 5
+
+const char* ESPTAGS[] =
+{
+    "\r\nOK\r\n",
+	"\r\nERROR\r\n",
+	"\r\nFAIL\r\n",
+    "\r\nSEND OK\r\n",
+    " CONNECT\r\n"
+};
+
+typedef enum
+{
+	TAG_OK,
+	TAG_ERROR,
+	TAG_FAIL,
+	TAG_SENDOK,
+	TAG_CONNECT
+} TagsEnum;
+
+
+Stream *EspDrv::espSerial;
+
+RingBuffer EspDrv::ringBuf(32);
+
+// Array of data to cache the information related to the networks discovered
+char 	EspDrv::_networkSsid[][WL_SSID_MAX_LENGTH] = {{"1"},{"2"},{"3"},{"4"},{"5"}};
+int32_t EspDrv::_networkRssi[WL_NETWORKS_LIST_MAXNUM] = { 0 };
+uint8_t EspDrv::_networkEncr[WL_NETWORKS_LIST_MAXNUM] = { 0 };
+
+// Cached values of retrieved data
+char EspDrv::_ssid[] = {0};
+uint8_t EspDrv::_bssid[] = {0};
+uint8_t EspDrv::_mac[] = {0};
+uint8_t EspDrv::_localIp[] = {0};
+char EspDrv::fwVersion[] = {0};
+
+long EspDrv::_bufPos=0;
+uint8_t EspDrv::_connId=0;
+
+uint16_t EspDrv::_remotePort  =0;
+uint8_t EspDrv::_remoteIp[] = {0};
+
+
+void EspDrv::wifiDriverInit(Stream *espSerial)
+{
+	LOGDEBUG(F("> wifiDriverInit"));
+
+	EspDrv::espSerial = espSerial;
+
+	bool initOK = false;
+	
+	for(int i=0; i<5; i++)
+	{
+		if (sendCmd(F("AT")) == TAG_OK)
+		{
+			initOK=true;
+			break;
+		}
+		delay(1000);
+	}
+
+	if (!initOK)
+	{
+		LOGERROR(F("Cannot initialize ESP module"));
+		delay(5000);
+		return;
+	}
+
+	reset();
+
+	// check firmware version
+	getFwVersion();
+
+	// prints a warning message if the firmware is not 1.X or 2.X
+	if ((fwVersion[0] != '1' and fwVersion[0] != '2') or
+		fwVersion[1] != '.')
+	{
+		LOGWARN1(F("Warning: Unsupported firmware"), fwVersion);
+		delay(4000);
+	}
+	else
+	{
+		LOGINFO1(F("Initilization successful -"), fwVersion);
+	}
+}
+
+
+void EspDrv::reset()
+{
+	LOGDEBUG(F("> reset"));
+
+	sendCmd(F("AT+RST"));
+	delay(3000);
+	espEmptyBuf(false);  // empty dirty characters from the buffer
+
+	// disable echo of commands
+	sendCmd(F("ATE0"));
+
+	// set station mode
+	sendCmd(F("AT+CWMODE=1"));
+	delay(200);
+
+	// set multiple connections mode
+	sendCmd(F("AT+CIPMUX=1"));
+
+	// Show remote IP and port with "+IPD"
+	sendCmd(F("AT+CIPDINFO=1"));
+	
+	// Disable autoconnect
+	// Automatic connection can create problems during initialization phase at next boot
+	sendCmd(F("AT+CWAUTOCONN=0"));
+
+	// enable DHCP
+	sendCmd(F("AT+CWDHCP=1,1"));
+	delay(200);
+}
+
+
+
+bool EspDrv::wifiConnect(char* ssid, const char *passphrase)
+{
+	LOGDEBUG(F("> wifiConnect"));
+
+	// TODO
+	// Escape character syntax is needed if "SSID" or "password" contains
+	// any special characters (',', '"' and '/')
+
+    // connect to access point, use CUR mode to avoid connection at boot
+	int ret = sendCmd(F("AT+CWJAP_CUR=\"%s\",\"%s\""), 20000, ssid, passphrase);
+
+	if (ret==TAG_OK)
+	{
+		LOGINFO1(F("Connected to"), ssid);
+		return true;
+	}
+
+	LOGWARN1(F("Failed connecting to"), ssid);
+
+	// clean additional messages logged after the FAIL tag
+	delay(1000);
+	espEmptyBuf(false);
+
+	return false;
+}
+
+
+bool EspDrv::wifiStartAP(char* ssid, const char* pwd, uint8_t channel, uint8_t enc, uint8_t espMode)
+{
+	LOGDEBUG(F("> wifiStartAP"));
+
+	// set AP mode, use CUR mode to avoid automatic start at boot
+    int ret = sendCmd(F("AT+CWMODE_CUR=%d"), 10000, espMode);
+	if (ret!=TAG_OK)
+	{
+		LOGWARN1(F("Failed to set AP mode"), ssid);
+		return false;
+	}
+
+	// TODO
+	// Escape character syntax is needed if "SSID" or "password" contains
+	// any special characters (',', '"' and '/')
+
+	// start access point
+	ret = sendCmd(F("AT+CWSAP_CUR=\"%s\",\"%s\",%d,%d"), 10000, ssid, pwd, channel, enc);
+
+	if (ret!=TAG_OK)
+	{
+		LOGWARN1(F("Failed to start AP"), ssid);
+		return false;
+	}
+	
+	if (espMode==2)
+		sendCmd(F("AT+CWDHCP_CUR=0,1"));    // enable DHCP for AP mode
+	if (espMode==3)
+		sendCmd(F("AT+CWDHCP_CUR=2,1"));    // enable DHCP for station and AP mode
+
+	LOGINFO1(F("Access point started"), ssid);
+	return true;
+}
+
+
+int8_t EspDrv::disconnect()
+{
+	LOGDEBUG(F("> disconnect"));
+
+	if(sendCmd(F("AT+CWQAP"))==TAG_OK)
+		return WL_DISCONNECTED;
+
+	// wait and clear any additional message
+	delay(2000);
+	espEmptyBuf(false);
+
+	return WL_DISCONNECTED;
+}
+
+void EspDrv::config(IPAddress ip)
+{
+	LOGDEBUG(F("> config"));
+
+	// disable station DHCP
+	sendCmd(F("AT+CWDHCP_CUR=1,0"));
+	
+	// it seems we need to wait here...
+	delay(500);
+	
+	char buf[16];
+	sprintf(buf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+
+	int ret = sendCmd(F("AT+CIPSTA_CUR=\"%s\""), 2000, buf);
+	delay(500);
+
+	if (ret==TAG_OK)
+	{
+		LOGINFO1(F("IP address set"), buf);
+	}
+}
+
+void EspDrv::configAP(IPAddress ip)
+{
+	LOGDEBUG(F("> config"));
+	
+    sendCmd(F("AT+CWMODE_CUR=2"));
+	
+	// disable station DHCP
+	sendCmd(F("AT+CWDHCP_CUR=2,0"));
+	
+	// it seems we need to wait here...
+	delay(500);
+	
+	char buf[16];
+	sprintf(buf, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
+
+	int ret = sendCmd(F("AT+CIPAP_CUR=\"%s\""), 2000, buf);
+	delay(500);
+
+	if (ret==TAG_OK)
+	{
+		LOGINFO1(F("IP address set"), buf);
+	}
+}
+
+uint8_t EspDrv::getConnectionStatus()
+{
+	LOGDEBUG(F("> getConnectionStatus"));
+
+/*
+	AT+CIPSTATUS
+
+	Response
+
+		STATUS:<stat>
+		+CIPSTATUS:<link ID>,<type>,<remote_IP>,<remote_port>,<local_port>,<tetype>
+
+	Parameters
+
+		<stat>
+			2: Got IP
+			3: Connected
+			4: Disconnected
+		<link ID> ID of the connection (0~4), for multi-connect
+		<type> string, "TCP" or "UDP"
+		<remote_IP> string, remote IP address.
+		<remote_port> remote port number
+		<local_port> ESP8266 local port number
+		<tetype>
+			0: ESP8266 runs as client
+			1: ESP8266 runs as server
+*/
+
+	char buf[10];
+	if(!sendCmdGet(F("AT+CIPSTATUS"), F("STATUS:"), F("\r\n"), buf, sizeof(buf)))
+		return WL_NO_SHIELD;
+
+	// 4: client disconnected
+	// 5: wifi disconnected
+	int s = atoi(buf);
+	if(s==2 or s==3 or s==4)
+		return WL_CONNECTED;
+	else if(s==5)
+		return WL_DISCONNECTED;
+
+	return WL_IDLE_STATUS;
+}
+
+uint8_t EspDrv::getClientState(uint8_t sock)
+{
+	LOGDEBUG1(F("> getClientState"), sock);
+
+	char findBuf[20];
+	sprintf_P(findBuf, PSTR("+CIPSTATUS:%d,"), sock);
+
+	char buf[10];
+	if (sendCmdGet(F("AT+CIPSTATUS"), findBuf, ",", buf, sizeof(buf)))
+	{
+		LOGDEBUG(F("Connected"));
+		return true;
+	}
+
+	LOGDEBUG(F("Not connected"));
+	return false;
+}
+
+uint8_t* EspDrv::getMacAddress()
+{
+	LOGDEBUG(F("> getMacAddress"));
+
+	memset(_mac, 0, WL_MAC_ADDR_LENGTH);
+
+	char buf[20];
+	if (sendCmdGet(F("AT+CIFSR"), F(":STAMAC,\""), F("\""), buf, sizeof(buf)))
+	{
+		char* token;
+
+		token = strtok(buf, ":");
+		_mac[5] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_mac[4] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_mac[3] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_mac[2] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_mac[1] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_mac[0] = (byte)strtol(token, NULL, 16);
+	}
+	return _mac;
+}
+
+
+void EspDrv::getIpAddress(IPAddress& ip)
+{
+	LOGDEBUG(F("> getIpAddress"));
+
+	char buf[20];
+	if (sendCmdGet(F("AT+CIFSR"), F(":STAIP,\""), F("\""), buf, sizeof(buf)))
+	{
+		char* token;
+
+		token = strtok(buf, ".");
+		_localIp[0] = atoi(token);
+		token = strtok(NULL, ".");
+		_localIp[1] = atoi(token);
+		token = strtok(NULL, ".");
+		_localIp[2] = atoi(token);
+		token = strtok(NULL, ".");
+		_localIp[3] = atoi(token);
+
+		ip = _localIp;
+	}
+}
+
+void EspDrv::getIpAddressAP(IPAddress& ip)
+{
+	LOGDEBUG(F("> getIpAddressAP"));
+
+	char buf[20];
+	if (sendCmdGet(F("AT+CIPAP?"), F("+CIPAP:ip:\""), F("\""), buf, sizeof(buf)))
+	{
+		char* token;
+
+		token = strtok(buf, ".");
+		_localIp[0] = atoi(token);
+		token = strtok(NULL, ".");
+		_localIp[1] = atoi(token);
+		token = strtok(NULL, ".");
+		_localIp[2] = atoi(token);
+		token = strtok(NULL, ".");
+		_localIp[3] = atoi(token);
+
+		ip = _localIp;
+	}
+}
+
+
+
+char* EspDrv::getCurrentSSID()
+{
+	LOGDEBUG(F("> getCurrentSSID"));
+
+	_ssid[0] = 0;
+	sendCmdGet(F("AT+CWJAP?"), F("+CWJAP:\""), F("\""), _ssid, sizeof(_ssid));
+
+	return _ssid;
+}
+
+uint8_t* EspDrv::getCurrentBSSID()
+{
+	LOGDEBUG(F("> getCurrentBSSID"));
+
+	memset(_bssid, 0, WL_MAC_ADDR_LENGTH);
+
+	char buf[20];
+	if (sendCmdGet(F("AT+CWJAP?"), F(",\""), F("\","), buf, sizeof(buf)))
+	{
+		char* token;
+
+		token = strtok(buf, ":");
+		_bssid[5] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_bssid[4] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_bssid[3] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_bssid[2] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_bssid[1] = (byte)strtol(token, NULL, 16);
+		token = strtok(NULL, ":");
+		_bssid[0] = (byte)strtol(token, NULL, 16);
+	}
+	return _bssid;
+
+}
+
+int32_t EspDrv::getCurrentRSSI()
+{
+	LOGDEBUG(F("> getCurrentRSSI"));
+
+    int ret=0;
+	char buf[10];
+	sendCmdGet(F("AT+CWJAP?"), F(",-"), F("\r\n"), buf, sizeof(buf));
+
+	if (isDigit(buf[0])) {
+      ret = -atoi(buf);
+    }
+
+    return ret;
+}
+
+
+uint8_t EspDrv::getScanNetworks()
+{
+    uint8_t ssidListNum = 0;
+    int idx;
+	bool ret = false;
+	
+
+	espEmptyBuf();
+
+	LOGDEBUG(F("----------------------------------------------"));
+	LOGDEBUG(F(">> AT+CWLAP"));
+	
+	espSerial->println(F("AT+CWLAP"));
+
+	char buf[100];
+	
+	idx = readUntil(10000, "+CWLAP:(");
+	
+	while (idx == NUMESPTAGS)
+	{
+		_networkEncr[ssidListNum] = espSerial->parseInt();
+		
+		// discard , and " characters
+		readUntil(1000, "\"");
+
+		idx = readUntil(1000, "\"", false);
+		if(idx==NUMESPTAGS)
+		{
+			memset(_networkSsid[ssidListNum], 0, WL_SSID_MAX_LENGTH );
+			ringBuf.getStrN(_networkSsid[ssidListNum], 1, WL_SSID_MAX_LENGTH-1);
+		}
+		
+		// discard , character
+		readUntil(1000, ",");
+		
+		_networkRssi[ssidListNum] = espSerial->parseInt();
+		
+		idx = readUntil(1000, "+CWLAP:(");
+
+		if(ssidListNum==WL_NETWORKS_LIST_MAXNUM-1)
+			break;
+
+		ssidListNum++;
+	}
+	
+	if (idx==-1)
+		return -1;
+
+	LOGDEBUG1(F("---------------------------------------------- >"), ssidListNum);
+	LOGDEBUG();
+    return ssidListNum;
+}
+
+bool EspDrv::getNetmask(IPAddress& mask) {
+	LOGDEBUG(F("> getNetmask"));
+
+	char buf[20];
+	if (sendCmdGet(F("AT+CIPSTA?"), F("+CIPSTA:netmask:\""), F("\""), buf, sizeof(buf)))
+	{
+		mask.fromString (buf);
+		return true;
+	}
+
+	return false;
+}
+
+bool EspDrv::getGateway(IPAddress& gw)
+{
+	LOGDEBUG(F("> getGateway"));
+
+	char buf[20];
+	if (sendCmdGet(F("AT+CIPSTA?"), F("+CIPSTA:gateway:\""), F("\""), buf, sizeof(buf)))
+	{
+		gw.fromString (buf);
+		return true;
+	}
+
+	return false;
+}
+
+char* EspDrv::getSSIDNetoworks(uint8_t networkItem)
+{
+	if (networkItem >= WL_NETWORKS_LIST_MAXNUM)
+		return NULL;
+
+	return _networkSsid[networkItem];
+}
+
+uint8_t EspDrv::getEncTypeNetowrks(uint8_t networkItem)
+{
+	if (networkItem >= WL_NETWORKS_LIST_MAXNUM)
+		return NULL;
+
+    return _networkEncr[networkItem];
+}
+
+int32_t EspDrv::getRSSINetoworks(uint8_t networkItem)
+{
+	if (networkItem >= WL_NETWORKS_LIST_MAXNUM)
+		return NULL;
+
+    return _networkRssi[networkItem];
+}
+
+char* EspDrv::getFwVersion()
+{
+	LOGDEBUG(F("> getFwVersion"));
+
+	fwVersion[0] = 0;
+
+	sendCmdGet(F("AT+GMR"), F("SDK version:"), F("\r\n"), fwVersion, sizeof(fwVersion));
+
+    return fwVersion;
+}
+
+
+
+bool EspDrv::ping(const char *host)
+{
+	LOGDEBUG(F("> ping"));
+
+	int ret = sendCmd(F("AT+PING=\"%s\""), 8000, host);
+	
+	if (ret==TAG_OK)
+		return true;
+
+	return false;
+}
+
+
+
+// Start server TCP on port specified
+bool EspDrv::startServer(uint16_t port, uint8_t sock)
+{
+	LOGDEBUG1(F("> startServer"), port);
+
+	int ret = sendCmd(F("AT+CIPSERVER=%d,%d"), 1000, sock, port);
+
+	return ret==TAG_OK;
+}
+
+
+bool EspDrv::startClient(const char* host, uint16_t port, uint8_t sock, uint8_t protMode)
+{
+	LOGDEBUG2(F("> startClient"), host, port);
+	
+	// TCP
+	// AT+CIPSTART=<link ID>,"TCP",<remote IP>,<remote port>
+
+	// UDP
+	// AT+CIPSTART=<link ID>,"UDP",<remote IP>,<remote port>[,<UDP local port>,<UDP mode>]
+
+	// for UDP we set a dummy remote port and UDP mode to 2
+	// this allows to specify the target host/port in CIPSEND
+
+	int ret;
+	if (protMode==TCP_MODE)
+		ret = sendCmd(F("AT+CIPSTART=%d,\"TCP\",\"%s\",%u"), 5000, sock, host, port);
+	else if (protMode==SSL_MODE)
+	{
+		// better to put the CIPSSLSIZE here because it is not supported before firmware 1.4
+		sendCmd(F("AT+CIPSSLSIZE=4096"));
+		ret = sendCmd(F("AT+CIPSTART=%d,\"SSL\",\"%s\",%u"), 5000, sock, host, port);
+	}
+	else if (protMode==UDP_MODE)
+		ret = sendCmd(F("AT+CIPSTART=%d,\"UDP\",\"%s\",0,%u,2"), 5000, sock, host, port);
+
+	return ret==TAG_OK;
+}
+
+
+// Start server TCP on port specified
+void EspDrv::stopClient(uint8_t sock)
+{
+	LOGDEBUG1(F("> stopClient"), sock);
+
+	int ret = sendCmd(F("AT+CIPCLOSE=%d"), 4000, sock);
+}
+
+
+uint8_t EspDrv::getServerState(uint8_t sock)
+{
+    return 0;
+}
+
+
+
+////////////////////////////////////////////////////////////////////////////
+// TCP/IP functions
+////////////////////////////////////////////////////////////////////////////
+
+
+
+uint16_t EspDrv::availData(uint8_t connId)
+{
+    //LOGDEBUG(bufPos);
+
+	// if there is data in the buffer
+	if (_bufPos>0)
+	{
+		if (_connId==connId)
+			return _bufPos;
+		else if (_connId==0)
+			return _bufPos;
+	}
+
+
+    int bytes = espSerial->available();
+
+	if (bytes)
+	{
+		//LOGDEBUG1(F("Bytes in the serial buffer: "), bytes);
+		if (espSerial->find((char *)"+IPD,"))
+		{
+			// format is : +IPD,<id>,<len>:<data>
+			// format is : +IPD,<ID>,<len>[,<remote IP>,<remote port>]:<data>
+
+			_connId = espSerial->parseInt();    // <ID>
+			espSerial->read();                  // ,
+			_bufPos = espSerial->parseInt();    // <len>
+			espSerial->read();                  // "
+			_remoteIp[0] = espSerial->parseInt();    // <remote IP>
+			espSerial->read();                  // .
+			_remoteIp[1] = espSerial->parseInt();
+			espSerial->read();                  // .
+			_remoteIp[2] = espSerial->parseInt();
+			espSerial->read();                  // .
+			_remoteIp[3] = espSerial->parseInt();
+			espSerial->read();                  // "
+			espSerial->read();                  // ,
+			_remotePort = espSerial->parseInt();     // <remote port>
+			
+			espSerial->read();                  // :
+
+			LOGDEBUG();
+			LOGDEBUG2(F("Data packet"), _connId, _bufPos);
+
+			if(_connId==connId || connId==0)
+				return _bufPos;
+		}
+	}
+	return 0;
+}
+
+
+bool EspDrv::getData(uint8_t connId, uint8_t *data, bool peek, bool* connClose)
+{
+	if (connId!=_connId)
+		return false;
+
+
+	// see Serial.timedRead
+
+	long _startMillis = millis();
+	do
+	{
+		if (espSerial->available())
+		{
+			if (peek)
+			{
+				*data = (char)espSerial->peek();
+			}
+			else
+			{
+				*data = (char)espSerial->read();
+				_bufPos--;
+			}
+			//Serial.print((char)*data);
+
+			if (_bufPos == 0)
+			{
+				// after the data packet a ",CLOSED" string may be received
+				// this means that the socket is now closed
+
+				delay(5);
+
+				if (espSerial->available())
+				{
+					//LOGDEBUG(".2");
+					//LOGDEBUG(espSerial->peek());
+
+					// 48 = '0'
+					if (espSerial->peek()==48+connId)
+					{
+						int idx = readUntil(500, ",CLOSED\r\n", false);
+						if(idx!=NUMESPTAGS)
+						{
+							LOGERROR(F("Tag CLOSED not found"));
+						}
+
+						LOGDEBUG();
+						LOGDEBUG(F("Connection closed"));
+
+						*connClose=true;
+					}
+				}
+			}
+
+			return true;
+		}
+	} while(millis() - _startMillis < 2000);
+
+    // timed out, reset the buffer
+	LOGERROR1(F("TIMEOUT:"), _bufPos);
+
+    _bufPos = 0;
+	_connId = 0;
+	*data = 0;
+	
+	return false;
+}
+
+/**
+ * Receive the data into a buffer.
+ * It reads up to bufSize bytes.
+ * @return	received data size for success else -1.
+ */
+int EspDrv::getDataBuf(uint8_t connId, uint8_t *buf, uint16_t bufSize)
+{
+	if (connId!=_connId)
+		return false;
+
+	if(_bufPos<bufSize)
+		bufSize = _bufPos;
+	
+	for(int i=0; i<bufSize; i++)
+	{
+		int c = timedRead();
+		//LOGDEBUG(c);
+		if(c==-1)
+			return -1;
+		
+		buf[i] = (char)c;
+		_bufPos--;
+	}
+
+	return bufSize;
+}
+
+
+bool EspDrv::sendData(uint8_t sock, const uint8_t *data, uint16_t len)
+{
+	LOGDEBUG2(F("> sendData:"), sock, len);
+
+	char cmdBuf[20];
+	sprintf_P(cmdBuf, PSTR("AT+CIPSEND=%d,%u"), sock, len);
+	espSerial->println(cmdBuf);
+
+	int idx = readUntil(1000, (char *)">", false);
+	if(idx!=NUMESPTAGS)
+	{
+		LOGERROR(F("Data packet send error (1)"));
+		return false;
+	}
+
+	espSerial->write(data, len);
+
+	idx = readUntil(2000);
+	if(idx!=TAG_SENDOK)
+	{
+		LOGERROR(F("Data packet send error (2)"));
+		return false;
+	}
+
+    return true;
+}
+
+// Overrided sendData method for __FlashStringHelper strings
+bool EspDrv::sendData(uint8_t sock, const __FlashStringHelper *data, uint16_t len, bool appendCrLf)
+{
+	LOGDEBUG2(F("> sendData:"), sock, len);
+
+	char cmdBuf[20];
+	uint16_t len2 = len + 2*appendCrLf;
+	sprintf_P(cmdBuf, PSTR("AT+CIPSEND=%d,%u"), sock, len2);
+	espSerial->println(cmdBuf);
+
+	int idx = readUntil(1000, (char *)">", false);
+	if(idx!=NUMESPTAGS)
+	{
+		LOGERROR(F("Data packet send error (1)"));
+		return false;
+	}
+
+	//espSerial->write(data, len);
+	PGM_P p = reinterpret_cast<PGM_P>(data);
+	for (int i=0; i<len; i++)
+	{
+		unsigned char c = pgm_read_byte(p++);
+		espSerial->write(c);
+	}
+	if (appendCrLf)
+	{
+		espSerial->write('\r');
+		espSerial->write('\n');
+	}
+
+	idx = readUntil(2000);
+	if(idx!=TAG_SENDOK)
+	{
+		LOGERROR(F("Data packet send error (2)"));
+		return false;
+	}
+
+    return true;
+}
+
+bool EspDrv::sendDataUdp(uint8_t sock, const char* host, uint16_t port, const uint8_t *data, uint16_t len)
+{
+	LOGDEBUG2(F("> sendDataUdp:"), sock, len);
+	LOGDEBUG2(F("> sendDataUdp:"), host, port);
+
+	char cmdBuf[40];
+	sprintf_P(cmdBuf, PSTR("AT+CIPSEND=%d,%u,\"%s\",%u"), sock, len, host, port);
+	//LOGDEBUG1(F("> sendDataUdp:"), cmdBuf);
+	espSerial->println(cmdBuf);
+
+	int idx = readUntil(1000, (char *)">", false);
+	if(idx!=NUMESPTAGS)
+	{
+		LOGERROR(F("Data packet send error (1)"));
+		return false;
+	}
+
+	espSerial->write(data, len);
+
+	idx = readUntil(2000);
+	if(idx!=TAG_SENDOK)
+	{
+		LOGERROR(F("Data packet send error (2)"));
+		return false;
+	}
+
+    return true;
+}
+
+
+
+void EspDrv::getRemoteIpAddress(IPAddress& ip)
+{
+	ip = _remoteIp;
+}
+
+uint16_t EspDrv::getRemotePort()
+{
+	return _remotePort;
+}
+
+
+////////////////////////////////////////////////////////////////////////////
+// Utility functions
+////////////////////////////////////////////////////////////////////////////
+
+
+
+/*
+* Sends the AT command and stops if any of the TAGS is found.
+* Extract the string enclosed in the passed tags and returns it in the outStr buffer.
+* Returns true if the string is extracted, false if tags are not found of timed out.
+*/
+bool EspDrv::sendCmdGet(const __FlashStringHelper* cmd, const char* startTag, const char* endTag, char* outStr, int outStrLen)
+{
+    int idx;
+	bool ret = false;
+
+	outStr[0] = 0;
+
+	espEmptyBuf();
+
+	LOGDEBUG(F("----------------------------------------------"));
+	LOGDEBUG1(F(">>"), cmd);
+
+	// send AT command to ESP
+	espSerial->println(cmd);
+
+	// read result until the startTag is found
+	idx = readUntil(1000, startTag);
+
+	if(idx==NUMESPTAGS)
+	{
+		// clean the buffer to get a clean string
+		ringBuf.init();
+
+		// start tag found, search the endTag
+		idx = readUntil(500, endTag);
+
+		if(idx==NUMESPTAGS)
+		{
+			// end tag found
+			// copy result to output buffer avoiding overflow
+			ringBuf.getStrN(outStr, strlen(endTag), outStrLen-1);
+
+			// read the remaining part of the response
+			readUntil(2000);
+
+			ret = true;
+		}
+		else
+		{
+			LOGWARN(F("End tag not found"));
+		}
+	}
+	else if(idx>=0 and idx<NUMESPTAGS)
+	{
+		// the command has returned but no start tag is found
+		LOGDEBUG1(F("No start tag found:"), idx);
+	}
+	else
+	{
+		// the command has returned but no tag is found
+		LOGWARN(F("No tag found"));
+	}
+
+	LOGDEBUG1(F("---------------------------------------------- >"), outStr);
+	LOGDEBUG();
+
+	return ret;
+}
+
+bool EspDrv::sendCmdGet(const __FlashStringHelper* cmd, const __FlashStringHelper* startTag, const __FlashStringHelper* endTag, char* outStr, int outStrLen)
+{
+	char _startTag[strlen_P((char*)startTag)+1];
+	strcpy_P(_startTag,  (char*)startTag);
+
+	char _endTag[strlen_P((char*)endTag)+1];
+	strcpy_P(_endTag,  (char*)endTag);
+
+	return sendCmdGet(cmd, _startTag, _endTag, outStr, outStrLen);
+}
+
+
+/*
+* Sends the AT command and returns the id of the TAG.
+* Return -1 if no tag is found.
+*/
+int EspDrv::sendCmd(const __FlashStringHelper* cmd, int timeout)
+{
+    espEmptyBuf();
+
+	LOGDEBUG(F("----------------------------------------------"));
+	LOGDEBUG1(F(">>"), cmd);
+
+	espSerial->println(cmd);
+
+	int idx = readUntil(timeout);
+
+	LOGDEBUG1(F("---------------------------------------------- >"), idx);
+	LOGDEBUG();
+
+    return idx;
+}
+
+
+/*
+* Sends the AT command and returns the id of the TAG.
+* The additional arguments are formatted into the command using sprintf.
+* Return -1 if no tag is found.
+*/
+int EspDrv::sendCmd(const __FlashStringHelper* cmd, int timeout, ...)
+{
+	char cmdBuf[CMD_BUFFER_SIZE];
+
+	va_list args;
+	va_start (args, timeout);
+	vsnprintf_P (cmdBuf, CMD_BUFFER_SIZE, (char*)cmd, args);
+	va_end (args);
+
+	espEmptyBuf();
+
+	LOGDEBUG(F("----------------------------------------------"));
+	LOGDEBUG1(F(">>"), cmdBuf);
+
+	espSerial->println(cmdBuf);
+
+	int idx = readUntil(timeout);
+
+	LOGDEBUG1(F("---------------------------------------------- >"), idx);
+	LOGDEBUG();
+
+	return idx;
+}
+
+
+// Read from serial until one of the tags is found
+// Returns:
+//   the index of the tag found in the ESPTAGS array
+//   -1 if no tag was found (timeout)
+int EspDrv::readUntil(int timeout, const char* tag, bool findTags)
+{
+	ringBuf.reset();
+
+	char c;
+    unsigned long start = millis();
+	int ret = -1;
+
+	while ((millis() - start < timeout) and ret<0)
+	{
+        if(espSerial->available())
+		{
+            c = (char)espSerial->read();
+			LOGDEBUG0(c);
+			ringBuf.push(c);
+
+			if (tag!=NULL)
+			{
+				if (ringBuf.endsWith(tag))
+				{
+					ret = NUMESPTAGS;
+					//LOGDEBUG1("xxx");
+				}
+			}
+			if(findTags)
+			{
+				for(int i=0; i<NUMESPTAGS; i++)
+				{
+					if (ringBuf.endsWith(ESPTAGS[i]))
+					{
+						ret = i;
+						break;
+					}
+				}
+			}
+		}
+    }
+
+	if (millis() - start >= timeout)
+	{
+		LOGWARN(F(">>> TIMEOUT >>>"));
+	}
+
+    return ret;
+}
+
+
+void EspDrv::espEmptyBuf(bool warn)
+{
+    char c;
+	int i=0;
+	while(espSerial->available() > 0)
+    {
+		c = espSerial->read();
+		if (i>0 and warn==true)
+			LOGDEBUG0(c);
+		i++;
+	}
+	if (i>0 and warn==true)
+    {
+		LOGDEBUG(F(""));
+		LOGDEBUG1(F("Dirty characters in the serial buffer! >"), i);
+	}
+}
+
+
+// copied from Serial::timedRead
+int EspDrv::timedRead()
+{
+  int _timeout = 1000;
+  int c;
+  long _startMillis = millis();
+  do
+  {
+    c = espSerial->read();
+    if (c >= 0) return c;
+  } while(millis() - _startMillis < _timeout);
+
+  return -1; // -1 indicates timeout
+}
+
+
+
+EspDrv espDrv;
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/EspDrv.h b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/EspDrv.h
new file mode 100644
index 0000000000000000000000000000000000000000..4492ddc13279cc604c92a6eba9a319965f190422
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/EspDrv.h
@@ -0,0 +1,340 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#ifndef EspDrv_h
+#define EspDrv_h
+
+#include "Stream.h"
+#include "IPAddress.h"
+
+
+#include "RingBuffer.h"
+
+
+
+// Maximum size of a SSID
+#define WL_SSID_MAX_LENGTH 32
+
+// Size of a MAC-address or BSSID
+#define WL_MAC_ADDR_LENGTH 6
+
+// Size of a MAC-address or BSSID
+#define WL_IPV4_LENGTH 4
+
+// Maximum size of a SSID list
+#define WL_NETWORKS_LIST_MAXNUM	10
+
+// Maxmium number of socket
+#define	MAX_SOCK_NUM		4
+
+// Socket not available constant
+#define SOCK_NOT_AVAIL  255
+
+// Default state value for Wifi state field
+#define NA_STATE -1
+
+#define WL_FW_VER_LENGTH 6
+
+#define NO_SOCKET_AVAIL 255
+
+
+// maximum size of AT command
+#define CMD_BUFFER_SIZE 200
+
+
+typedef enum eProtMode {TCP_MODE, UDP_MODE, SSL_MODE} tProtMode;
+
+
+typedef enum {
+        WL_FAILURE = -1,
+        WL_SUCCESS = 1,
+} wl_error_code_t;
+
+/* Authentication modes */
+enum wl_auth_mode {
+        AUTH_MODE_INVALID,
+        AUTH_MODE_AUTO,
+        AUTH_MODE_OPEN_SYSTEM,
+        AUTH_MODE_SHARED_KEY,
+        AUTH_MODE_WPA,
+        AUTH_MODE_WPA2,
+        AUTH_MODE_WPA_PSK,
+        AUTH_MODE_WPA2_PSK
+};
+
+
+typedef enum {
+	WL_NO_SHIELD = 255,
+	WL_IDLE_STATUS = 0,
+	//WL_NO_SSID_AVAIL,
+	//WL_SCAN_COMPLETED,
+	WL_CONNECTED,
+	WL_CONNECT_FAILED,
+	//WL_CONNECTION_LOST,
+	WL_DISCONNECTED
+} wl_status_t;
+
+/* Encryption modes */
+enum wl_enc_type {
+	ENC_TYPE_NONE = 0,
+	ENC_TYPE_WEP = 1,
+	ENC_TYPE_WPA_PSK = 2,
+	ENC_TYPE_WPA2_PSK = 3,
+	ENC_TYPE_WPA_WPA2_PSK = 4
+};
+
+
+enum wl_tcp_state {
+	CLOSED      = 0,
+	LISTEN      = 1,
+	SYN_SENT    = 2,
+	SYN_RCVD    = 3,
+	ESTABLISHED = 4,
+	FIN_WAIT_1  = 5,
+	FIN_WAIT_2  = 6,
+	CLOSE_WAIT  = 7,
+	CLOSING     = 8,
+	LAST_ACK    = 9,
+	TIME_WAIT   = 10
+};
+
+
+
+class EspDrv
+{
+
+public:
+
+    static void wifiDriverInit(Stream *espSerial);
+
+
+    /* Start Wifi connection with passphrase
+     *
+     * param ssid: Pointer to the SSID string.
+     * param passphrase: Passphrase. Valid characters in a passphrase must be between ASCII 32-126 (decimal).
+     */
+    static bool wifiConnect(char* ssid, const char *passphrase);
+
+
+    /*
+	* Start the Access Point
+	*/
+	static bool wifiStartAP(char* ssid, const char* pwd, uint8_t channel, uint8_t enc, uint8_t espMode);
+
+
+    /*
+	 * Set ip configuration disabling dhcp client
+	 */
+    static void config(IPAddress local_ip);
+
+    /*
+	 * Set ip configuration disabling dhcp client
+	 */
+    static void configAP(IPAddress local_ip);
+
+
+    /*
+     * Disconnect from the network
+     *
+     * return: WL_SUCCESS or WL_FAILURE
+     */
+    static int8_t disconnect();
+
+    /*
+     *
+     *
+     * return: one value of wl_status_t enum
+     */
+    static uint8_t getConnectionStatus();
+
+    /*
+     * Get the interface MAC address.
+     *
+     * return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
+     */
+    static uint8_t* getMacAddress();
+
+    /*
+     * Get the interface IP address.
+     *
+     * return: copy the ip address value in IPAddress object
+     */
+    static void getIpAddress(IPAddress& ip);
+
+	static void getIpAddressAP(IPAddress& ip);
+
+    /*
+     * Get the interface IP netmask.
+     * This can be used to retrieve settings configured through DHCP.
+     *
+     * return: true if successful
+     */
+    static bool getNetmask(IPAddress& mask);
+
+    /*
+     * Get the interface IP gateway.
+     * This can be used to retrieve settings configured through DHCP.
+     *
+     * return: true if successful
+     */
+    static bool getGateway(IPAddress& mask);
+
+    /*
+     * Return the current SSID associated with the network
+     *
+     * return: ssid string
+     */
+    static char* getCurrentSSID();
+
+    /*
+     * Return the current BSSID associated with the network.
+     * It is the MAC address of the Access Point
+     *
+     * return: pointer to uint8_t array with length WL_MAC_ADDR_LENGTH
+     */
+    static uint8_t* getCurrentBSSID();
+
+    /*
+     * Return the current RSSI /Received Signal Strength in dBm)
+     * associated with the network
+     *
+     * return: signed value
+     */
+    static int32_t getCurrentRSSI();
+
+    /*
+     * Get the networks available
+     *
+     * return: Number of discovered networks
+     */
+    static uint8_t getScanNetworks();
+
+	/*
+     * Return the SSID discovered during the network scan.
+     *
+     * param networkItem: specify from which network item want to get the information
+	 *
+     * return: ssid string of the specified item on the networks scanned list
+     */
+    static char* getSSIDNetoworks(uint8_t networkItem);
+
+    /*
+     * Return the RSSI of the networks discovered during the scanNetworks
+     *
+     * param networkItem: specify from which network item want to get the information
+	 *
+     * return: signed value of RSSI of the specified item on the networks scanned list
+     */
+    static int32_t getRSSINetoworks(uint8_t networkItem);
+
+    /*
+     * Return the encryption type of the networks discovered during the scanNetworks
+     *
+     * param networkItem: specify from which network item want to get the information
+	 *
+     * return: encryption type (enum wl_enc_type) of the specified item on the networks scanned list
+     */
+    static uint8_t getEncTypeNetowrks(uint8_t networkItem);
+
+
+    /*
+     * Get the firmware version
+     */
+    static char* getFwVersion();
+
+
+	////////////////////////////////////////////////////////////////////////////
+	// Client/Server methods
+	////////////////////////////////////////////////////////////////////////////
+
+
+    static bool startServer(uint16_t port, uint8_t sock);
+    static bool startClient(const char* host, uint16_t port, uint8_t sock, uint8_t protMode);
+    static void stopClient(uint8_t sock);
+    static uint8_t getServerState(uint8_t sock);
+    static uint8_t getClientState(uint8_t sock);
+    static bool getData(uint8_t connId, uint8_t *data, bool peek, bool* connClose);
+    static int getDataBuf(uint8_t connId, uint8_t *buf, uint16_t bufSize);
+    static bool sendData(uint8_t sock, const uint8_t *data, uint16_t len);
+    static bool sendData(uint8_t sock, const __FlashStringHelper *data, uint16_t len, bool appendCrLf=false);
+	static bool sendDataUdp(uint8_t sock, const char* host, uint16_t port, const uint8_t *data, uint16_t len);
+    static uint16_t availData(uint8_t connId);
+
+
+	static bool ping(const char *host);
+    static void reset();
+
+    static void getRemoteIpAddress(IPAddress& ip);
+    static uint16_t getRemotePort();
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+private:
+	static Stream *espSerial;
+
+	static long _bufPos;
+	static uint8_t _connId;
+
+	static uint16_t _remotePort;
+	static uint8_t  _remoteIp[WL_IPV4_LENGTH];
+
+
+	// firmware version string
+	static char 	fwVersion[WL_FW_VER_LENGTH];
+
+	// settings of requested network
+	static char 	_networkSsid[WL_NETWORKS_LIST_MAXNUM][WL_SSID_MAX_LENGTH];
+	static int32_t 	_networkRssi[WL_NETWORKS_LIST_MAXNUM];
+	static uint8_t 	_networkEncr[WL_NETWORKS_LIST_MAXNUM];
+
+
+	// settings of current selected network
+	static char 	_ssid[WL_SSID_MAX_LENGTH];
+	static uint8_t 	_bssid[WL_MAC_ADDR_LENGTH];
+	static uint8_t 	_mac[WL_MAC_ADDR_LENGTH];
+	static uint8_t  _localIp[WL_IPV4_LENGTH];
+
+
+	// the ring buffer is used to search the tags in the stream
+	static RingBuffer ringBuf;
+
+
+	//static int sendCmd(const char* cmd, int timeout=1000);
+	static int sendCmd(const __FlashStringHelper* cmd, int timeout=1000);
+	static int sendCmd(const __FlashStringHelper* cmd, int timeout, ...);
+
+	static bool sendCmdGet(const __FlashStringHelper* cmd, const char* startTag, const char* endTag, char* outStr, int outStrLen);
+	static bool sendCmdGet(const __FlashStringHelper* cmd, const __FlashStringHelper* startTag, const __FlashStringHelper* endTag, char* outStr, int outStrLen);
+
+	static int readUntil(int timeout, const char* tag=NULL, bool findTags=true);
+
+	static void espEmptyBuf(bool warn=true);
+
+	static int timedRead();
+
+
+	friend class WiFiEsp;
+	friend class WiFiEspServer;
+	friend class WiFiEspClient;
+	friend class WiFiEspUdp;
+};
+
+extern EspDrv espDrv;
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/RingBuffer.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/RingBuffer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e5fa3c43a7cfd2550578780340026b5eb1ecb1e6
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/RingBuffer.cpp
@@ -0,0 +1,105 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#include "RingBuffer.h"
+
+#include <Arduino.h>
+
+RingBuffer::RingBuffer(unsigned int size)
+{
+	_size = size;
+	// add one char to terminate the string
+	ringBuf = new char[size+1];
+	ringBufEnd = &ringBuf[size];
+	init();
+}
+
+RingBuffer::~RingBuffer() {}
+
+void RingBuffer::reset()
+{
+	ringBufP = ringBuf;
+}
+
+void RingBuffer::init()
+{
+	ringBufP = ringBuf;
+	memset(ringBuf, 0, _size+1);
+}
+
+void RingBuffer::push(char c)
+{
+	*ringBufP = c;
+	ringBufP++;
+	if (ringBufP>=ringBufEnd)
+		ringBufP = ringBuf;
+}
+
+
+
+bool RingBuffer::endsWith(const char* str)
+{
+	int findStrLen = strlen(str);
+
+	// b is the start position into the ring buffer
+	char* b = ringBufP-findStrLen;
+	if(b < ringBuf)
+		b = b + _size;
+
+	char *p1 = (char*)&str[0];
+	char *p2 = p1 + findStrLen;
+
+	for(char *p=p1; p<p2; p++)
+	{
+		if(*p != *b)
+			return false;
+
+		b++;
+		if (b == ringBufEnd)
+			b=ringBuf;
+	}
+
+	return true;
+}
+
+
+
+void RingBuffer::getStr(char * destination, unsigned int skipChars)
+{
+	int len = ringBufP-ringBuf-skipChars;
+
+	// copy buffer to destination string
+	strncpy(destination, ringBuf, len);
+
+	// terminate output string
+	//destination[len]=0;
+}
+
+void RingBuffer::getStrN(char * destination, unsigned int skipChars, unsigned int num)
+{
+	int len = ringBufP-ringBuf-skipChars;
+
+	if (len>num)
+		len=num;
+
+	// copy buffer to destination string
+	strncpy(destination, ringBuf, len);
+
+	// terminate output string
+	//destination[len]=0;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/RingBuffer.h b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/RingBuffer.h
new file mode 100644
index 0000000000000000000000000000000000000000..437ff66800884f651c726f94938a67b4ee8dd419
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/RingBuffer.h
@@ -0,0 +1,47 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#ifndef RingBuffer_h
+#define RingBuffer_h
+
+
+class RingBuffer
+{
+public:
+	RingBuffer(unsigned int size);
+	~RingBuffer();
+
+	void reset();
+	void init();
+	void push(char c);
+	int getPos();
+	bool endsWith(const char* str);
+	void getStr(char * destination, unsigned int skipChars);
+	void getStrN(char * destination, unsigned int skipChars, unsigned int num);
+
+
+private:
+
+	unsigned int _size;
+	char* ringBuf;
+	char* ringBufEnd;
+	char* ringBufP;
+
+};
+
+#endif
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/debug.h b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/debug.h
new file mode 100644
index 0000000000000000000000000000000000000000..9557a132556ad9b81f2164a75bde800617930b88
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/src/utility/debug.h
@@ -0,0 +1,47 @@
+/*--------------------------------------------------------------------
+This file is part of the Arduino WiFiEsp library.
+
+The Arduino WiFiEsp library 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.
+
+The Arduino WiFiEsp 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 General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with The Arduino WiFiEsp library.  If not, see
+<http://www.gnu.org/licenses/>.
+--------------------------------------------------------------------*/
+
+#ifndef EspDebug_H
+#define EspDebug_H
+
+#include <stdio.h>
+
+// Change _ESPLOGLEVEL_ to set tracing and logging verbosity
+// 0: DISABLED: no logging
+// 1: ERROR: errors
+// 2: WARN: errors and warnings
+// 3: INFO: errors, warnings and informational (default)
+// 4: DEBUG: errors, warnings, informational and debug
+
+#define _ESPLOGLEVEL_ 3
+
+
+#define LOGERROR(x)    if(_ESPLOGLEVEL_>0) { Serial.print("[WiFiEsp] "); Serial.println(x); }
+#define LOGERROR1(x,y) if(_ESPLOGLEVEL_>2) { Serial.print("[WiFiEsp] "); Serial.print(x); Serial.print(" "); Serial.println(y); }
+#define LOGWARN(x)     if(_ESPLOGLEVEL_>1) { Serial.print("[WiFiEsp] "); Serial.println(x); }
+#define LOGWARN1(x,y)  if(_ESPLOGLEVEL_>2) { Serial.print("[WiFiEsp] "); Serial.print(x); Serial.print(" "); Serial.println(y); }
+#define LOGINFO(x)     if(_ESPLOGLEVEL_>2) { Serial.print("[WiFiEsp] "); Serial.println(x); }
+#define LOGINFO1(x,y)  if(_ESPLOGLEVEL_>2) { Serial.print("[WiFiEsp] "); Serial.print(x); Serial.print(" "); Serial.println(y); }
+
+#define LOGDEBUG(x)      if(_ESPLOGLEVEL_>3) { Serial.println(x); }
+#define LOGDEBUG0(x)     if(_ESPLOGLEVEL_>3) { Serial.print(x); }
+#define LOGDEBUG1(x,y)   if(_ESPLOGLEVEL_>3) { Serial.print(x); Serial.print(" "); Serial.println(y); }
+#define LOGDEBUG2(x,y,z) if(_ESPLOGLEVEL_>3) { Serial.print(x); Serial.print(" "); Serial.print(y); Serial.print(" "); Serial.println(z); }
+
+
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/BasicTest/BasicTest.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/BasicTest/BasicTest.ino
new file mode 100644
index 0000000000000000000000000000000000000000..eb768e8f7f5fc295a44361be9b2bf8e706588247
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/BasicTest/BasicTest.ino
@@ -0,0 +1,142 @@
+/*
+ WiFiEsp test: BasicTest
+ 
+ Performs basic connectivity test and checks.
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 7/6 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+
+char ssid[] = "Twim";     // your network SSID (name)
+char pwd[] = "12345678";  // your network password
+char pwdErr[] = "xxxx";   // wrong password
+
+
+void setup()
+{
+  Serial.begin(115200);
+  Serial1.begin(9600);
+  WiFi.init(&Serial1);
+}
+
+void loop()
+{
+  assertEquals("Firmware version", WiFi.firmwareVersion(), "1.5.2");
+  assertEquals("Status is (WL_DISCONNECTED)", WiFi.status(), WL_DISCONNECTED);
+  assertEquals("Connect", WiFi.begin(ssid, pwd), WL_CONNECTED);
+  assertEquals("Check status (WL_CONNECTED)", WiFi.status(), WL_CONNECTED);
+  assertEquals("Check SSID", WiFi.SSID(), ssid);
+
+  IPAddress ip = WiFi.localIP();
+  assertNotEquals("Check IP Address", ip[0], 0);
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+  
+  byte mac[6]={0,0,0,0,0,0};
+  WiFi.macAddress(mac);
+
+  Serial.print("MAC: ");
+  Serial.print(mac[5], HEX);
+  Serial.print(":");
+  Serial.print(mac[4], HEX);
+  Serial.print(":");
+  Serial.print(mac[3], HEX);
+  Serial.print(":");
+  Serial.print(mac[2], HEX);
+  Serial.print(":");
+  Serial.print(mac[1], HEX);
+  Serial.print(":");
+  Serial.println(mac[0], HEX);
+  Serial.println();
+  
+  assertEquals("Disconnect", WiFi.disconnect(), WL_DISCONNECTED);
+  assertEquals("Check status (WL_DISCONNECTED)", WiFi.status(), WL_DISCONNECTED);
+  assertEquals("IP Address", WiFi.localIP(), 0);
+  assertEquals("Check SSID", WiFi.SSID(), "");
+  assertEquals("Wrong pwd", WiFi.begin(ssid, pwdErr), WL_CONNECT_FAILED);
+
+  IPAddress localIp(192, 168, 168, 111);
+  WiFi.config(localIp);
+  
+  assertEquals("Connect", WiFi.begin(ssid, pwd), WL_CONNECTED);
+  assertEquals("Check status (WL_CONNECTED)", WiFi.status(), WL_CONNECTED);
+
+  ip = WiFi.localIP();
+  assertNotEquals("Check IP Address", ip[0], 0);
+
+
+  Serial.println("END OF TESTS");
+  delay(60000);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+void assertNotEquals(const char* test, int actual, int expected)
+{
+  if(actual!=expected)
+    pass(test);
+  else
+    fail(test, actual, expected);
+}
+
+void assertEquals(const char* test, int actual, int expected)
+{
+  if(actual==expected)
+    pass(test);
+  else
+    fail(test, actual, expected);
+}
+
+void assertEquals(const char* test, char* actual, char* expected)
+{
+  if(strcmp(actual, expected) == 0)
+    pass(test);
+  else
+    fail(test, actual, expected);
+}
+
+
+void pass(const char* test)
+{
+  Serial.print(F("********************************************** "));
+  Serial.print(test);
+  Serial.println(" > PASSED");
+  Serial.println();
+}
+
+void fail(const char* test, char* actual, char* expected)
+{
+  Serial.print(F("********************************************** "));
+  Serial.print(test);
+  Serial.print(" > FAILED");
+  Serial.print(" (actual=\"");
+  Serial.print(actual);
+  Serial.print("\", expected=\"");
+  Serial.print(expected);
+  Serial.println("\")");
+  Serial.println();
+  delay(10000);
+}
+
+void fail(const char* test, int actual, int expected)
+{
+  Serial.print(F("********************************************** "));
+  Serial.print(test);
+  Serial.print(" > FAILED");
+  Serial.print(" (actual=");
+  Serial.print(actual);
+  Serial.print(", expected=");
+  Serial.print(expected);
+  Serial.println(")");
+  Serial.println();
+  delay(10000);
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/ClientTest/ClientTest.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/ClientTest/ClientTest.ino
new file mode 100644
index 0000000000000000000000000000000000000000..0e544efe3550968d00637a9dc728d21dd283b276
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/ClientTest/ClientTest.ino
@@ -0,0 +1,184 @@
+/*
+ WiFiEsp test: ClientTest
+ 
+ Test client functions.
+*/
+
+#include "WiFiEsp.h"
+
+// Emulate Serial1 on pins 7/6 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+
+char ssid[] = "Twim";     // your network SSID (name)
+char pwd[] = "12345678";  // your network password
+
+
+// Initialize the Wifi client library
+WiFiEspClient client;
+
+void setup()
+{
+  Serial.begin(115200);
+  Serial1.begin(9600);
+  WiFi.init(&Serial1);
+}
+
+void loop()
+{
+  bool f;
+  int c;
+
+  
+  assertEquals("Check status WL_DISCONNECTED", WiFi.status(), WL_DISCONNECTED);
+  
+  assertEquals("Connect", WiFi.begin(ssid, pwd), WL_CONNECTED);
+  
+  assertEquals("Check status WL_CONNECTED", WiFi.status(), WL_CONNECTED);
+  
+  IPAddress ip = WiFi.localIP();
+  Serial.print("IP Address: ");
+  Serial.println(ip);
+  
+  assertEquals("Ping", WiFi.ping("www.google.com"), true);
+  
+  assertEquals("Not connected", client.connected(), false);
+  assertEquals("Connect to server", client.connect("www.brainjar.com", 80), 1);
+  assertEquals("Connected", client.connected(), true);
+
+
+  //--------------------------------------------------------------
+  // HTTP request without 'Connection: close' command
+  
+  client.println("GET /java/host/test.html HTTP/1.1");
+  client.println("Host: www.brainjar.com");
+  client.println();
+
+  // wait for the response
+  long _startMillis = millis();
+  while (!client.available() and (millis()-_startMillis < 2000))
+  {
+  }
+  
+  assertEquals("Response received", (millis()-_startMillis < 2000), true);
+  
+  f = client.find("<html>");
+  assertEquals("Response check", f, true);
+  if (f)
+  {
+    while( (c = client.read()) > 0)
+      Serial.print((char)c);
+  }
+
+  assertEquals("Connected", client.connected(), true);
+
+
+  //--------------------------------------------------------------
+  
+  delay(5000);
+  
+  assertEquals("Check status WL_CONNECTED", WiFi.status(), WL_CONNECTED);
+
+  assertEquals("Connected", client.connected(), true);
+
+
+  //--------------------------------------------------------------
+  // HTTP request without 'Connection: close' command
+  
+  client.println("GET /java/host/test.html HTTP/1.1");
+  client.println("Host: www.brainjar.com");
+  client.println("Connection: close");
+  client.println();
+
+
+  // wait for the response
+  _startMillis = millis();
+  while (!client.available() and (millis()-_startMillis < 2000))
+  {
+  }
+  
+  assertEquals("Response received", (millis()-_startMillis < 2000), true);
+  
+  f = client.find("<html>");
+  assertEquals("Response check", f, true);
+  if (f)
+  {
+    while( (c = client.read()) > 0)
+      Serial.print((char)c);
+  }
+
+  //--------------------------------------------------------------
+  
+  assertEquals("Check status WL_CONNECTED", WiFi.status(), WL_CONNECTED);
+  assertEquals("Not connected", client.connected(), false);
+
+  assertEquals("Ping", WiFi.ping("www.google.com"), true);
+
+  assertEquals("Connect", WiFi.disconnect(), WL_DISCONNECTED);
+  assertEquals("Check status WL_DISCONNECTED", WiFi.status(), WL_DISCONNECTED);
+
+  Serial.println("END OF TESTS");
+  delay(30000);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////////
+
+
+void assertEquals(const char* test, int actual, int expected)
+{
+  if(actual==expected)
+    pass(test);
+  else
+    fail(test, actual, expected);
+}
+
+void assertEquals(const char* test, char* actual, char* expected)
+{
+  if(strcmp(actual, expected) == 0)
+    pass(test);
+  else
+    fail(test, actual, expected);
+}
+
+
+void pass(const char* test)
+{
+  Serial.print(F("********************************************** "));
+  Serial.print(test);
+  Serial.println(" > PASSED");
+  Serial.println();
+}
+
+void fail(const char* test, char* actual, char* expected)
+{
+  Serial.print(F("********************************************** "));
+  Serial.print(test);
+  Serial.print(" > FAILED");
+  Serial.print(" (actual=\"");
+  Serial.print(actual);
+  Serial.print("\", expected=\"");
+  Serial.print(expected);
+  Serial.println("\")");
+  Serial.println();
+  delay(10000);
+}
+
+void fail(const char* test, int actual, int expected)
+{
+  Serial.print(F("********************************************** "));
+  Serial.print(test);
+  Serial.print(" > FAILED");
+  Serial.print(" (actual=");
+  Serial.print(actual);
+  Serial.print(", expected=");
+  Serial.print(expected);
+  Serial.println(")");
+  Serial.println();
+  delay(10000);
+}
+
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/EspDebug/EspDebug.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/EspDebug/EspDebug.ino
new file mode 100644
index 0000000000000000000000000000000000000000..6824f6cd22c103a328d6239b08d7e29ce1d47056
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/EspDebug/EspDebug.ino
@@ -0,0 +1,48 @@
+// EspDebug - Test sketch for ESP8266 module
+
+#include "Arduino.h"
+
+// Emulate Serial1 on pins 7/6 if not present
+#ifndef HAVE_HWSERIAL1
+#include "SoftwareSerial.h"
+SoftwareSerial Serial1(6, 7); // RX, TX
+#endif
+
+void setup()
+{
+  Serial.begin(115200); // serial port used for debugging
+  Serial1.begin(9600);  // your ESP's baud rate might be different
+}
+ 
+void loop()
+{
+  if(Serial1.available())  // check if the ESP is sending a message
+  {
+    while(Serial1.available())
+    {
+      int c = Serial1.read(); // read the next character
+      Serial.write((char)c);  // writes data to the serial monitor
+    }
+  }
+ 
+  if(Serial.available())
+  {
+    // wait to let all the input command in the serial buffer
+    delay(10);
+
+    // read the input command in a string
+    String cmd = "";
+    while(Serial.available())
+    {
+      cmd += (char)Serial.read();
+    }
+
+    // print the command and send it to the ESP
+    Serial.println();
+    Serial.print(">>>> ");
+    Serial.println(cmd);
+
+    // send the read character to the ESP
+    Serial1.print(cmd);
+  }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/RingBufferTest/RingBufferTest.ino b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/RingBufferTest/RingBufferTest.ino
new file mode 100644
index 0000000000000000000000000000000000000000..2fb75c0d3b28ad2e68507c053398ade7bf107f42
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/WiFiEsp/test/RingBufferTest/RingBufferTest.ino
@@ -0,0 +1,58 @@
+/*
+ WiFiEsp test: RingBufferTest
+ 
+ Test of the RingBuffer class.
+*/
+
+#include "WiFiEsp.h"
+
+RingBuffer buf(5);
+
+
+void setup()
+{
+  Serial.begin(115200);
+  
+  Serial.println("Starting tests");
+
+  buf.init();
+
+  buf.push('a');
+  assert(10, buf.endsWith("a"), true);
+  assert(11, buf.endsWith("A"), false);
+  assert(12, buf.endsWith("ab"), false);
+
+  buf.push('b');
+  assert(21, buf.endsWith("a"), false);
+  assert(22, buf.endsWith("A"), false);
+  assert(23, buf.endsWith("ab"), true);
+
+  buf.push('c');
+  buf.push('d');
+  buf.push('e');
+  assert(31, buf.endsWith("abcde"), true);
+  assert(32, buf.endsWith("de"), true);
+
+  buf.push('f');
+  assert(43, buf.endsWith("bcdef"), true);
+  assert(44, buf.endsWith("ef"), true);
+
+  Serial.println("Done");
+}
+
+
+void loop()
+{
+  // nothing to do
+}
+
+
+void assert(int i, bool x, bool y)
+{
+  if (x!=y)
+  {
+    Serial.print ("FAIL ");
+    Serial.println(i);
+  }
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/aes/AES-128_V10.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/aes/AES-128_V10.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..734e013c3077d70ac320cdaea8701a44afbb118c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/aes/AES-128_V10.cpp
@@ -0,0 +1,340 @@
+/******************************************************************************************
+#if defined(USE_IDEETRON_AES)
+* Copyright 2015, 2016 Ideetron B.V.
+*
+* This program 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 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************************/
+/******************************************************************************************
+*
+* File:        AES-128_V10.cpp
+* Author:      Gerben den Hartog
+* Compagny:    Ideetron B.V.
+* Website:     http://www.ideetron.nl/LoRa
+* E-mail:      info@ideetron.nl
+******************************************************************************************/
+/****************************************************************************************
+*
+* Created on: 20-10-2015
+* Supported Hardware: ID150119-02 Nexus board with RFM95
+*
+* Firmware Version 1.0
+* First version
+****************************************************************************************/
+
+// This file was taken from
+// https://github.com/Ideetron/RFM95W_Nexus/tree/master/LoRaWAN_V31 for
+// use with LMIC. It was only cosmetically modified:
+//  - All other functions and variables were made static
+//  - Tabs were converted to 2 spaces
+//  - An #include and #if guard was added
+//  - S_Table is now stored in PROGMEM
+
+
+
+/*
+********************************************************************************************
+* Global Variables
+********************************************************************************************
+*/
+
+static unsigned char State[4][4];
+
+//static CONST_TABLE(unsigned char, S_Table)[16][16] = {
+unsigned char S_Table[16][16] = {
+  {0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5,0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76},
+  {0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0,0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0},
+  {0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC,0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15},
+  {0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A,0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75},
+  {0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0,0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84},
+  {0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B,0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF},
+  {0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85,0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8},
+  {0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5,0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2},
+  {0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17,0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73},
+  {0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88,0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB},
+  {0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C,0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79},
+  {0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9,0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08},
+  {0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6,0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A},
+  {0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E,0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E},
+  {0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94,0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF},
+  {0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68,0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16}
+};
+
+//extern "C" void AES_Encrypt(unsigned char *Data, unsigned char *Key);
+void AES_Encrypt(unsigned char *Data, unsigned char *Key);
+static void AES_Add_Round_Key(unsigned char *Round_Key);
+static unsigned char AES_Sub_Byte(unsigned char Byte);
+static void AES_Shift_Rows();
+static void AES_Mix_Collums();
+static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key);
+static void Send_State();
+
+/*
+*****************************************************************************************
+* Description : Function for encrypting data using AES-128
+*
+* Arguments   : *Data   Data to encrypt is a 16 byte long arry
+*               *Key    Key to encrypt data with is a 16 byte long arry
+*****************************************************************************************
+*/
+void AES_Encrypt(unsigned char *Data, unsigned char *Key)
+{
+  unsigned char i;
+  unsigned char Row,Collum;
+  unsigned char Round = 0x00;
+  unsigned char Round_Key[16];
+
+  //Copy input to State arry
+  for(Collum = 0; Collum < 4; Collum++)
+  {
+    for(Row = 0; Row < 4; Row++)
+    {
+      State[Row][Collum] = Data[Row + (4*Collum)];
+    }
+  }
+
+  //Copy key to round key
+  for(i = 0; i < 16; i++)
+  {
+    Round_Key[i] = Key[i];
+  }
+
+  //Add round key
+  AES_Add_Round_Key(Round_Key);
+
+  //Preform 9 full rounds
+  for(Round = 1; Round < 10; Round++)
+  {
+    //Preform Byte substitution with S table
+    for(Collum = 0; Collum < 4; Collum++)
+    {
+      for(Row = 0; Row < 4; Row++)
+      {
+        State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]);
+      }
+    }
+
+    //Preform Row Shift
+    AES_Shift_Rows();
+
+    //Mix Collums
+    AES_Mix_Collums();
+
+    //Calculate new round key
+    AES_Calculate_Round_Key(Round,Round_Key);
+
+    //Add round key
+    AES_Add_Round_Key(Round_Key);
+  }
+
+  //Last round whitout mix collums
+  //Preform Byte substitution with S table
+  for(Collum = 0; Collum < 4; Collum++)
+  {
+    for(Row = 0; Row < 4; Row++)
+    {
+      State[Row][Collum] = AES_Sub_Byte(State[Row][Collum]);
+    }
+  }
+
+  //Shift rows
+  AES_Shift_Rows();
+
+  //Calculate new round key
+  AES_Calculate_Round_Key(Round,Round_Key);
+
+  //Add round Key
+  AES_Add_Round_Key(Round_Key);
+
+  //Copy the State into the data array
+  for(Collum = 0; Collum < 4; Collum++)
+  {
+    for(Row = 0; Row < 4; Row++)
+    {
+      Data[Row + (4*Collum)] = State[Row][Collum];
+    }
+  }
+
+}
+
+/*
+*****************************************************************************************
+* Description : Function that add's the round key for the current round
+*
+* Arguments   : *Round_Key    16 byte long array holding the Round Key
+*****************************************************************************************
+*/
+static void AES_Add_Round_Key(unsigned char *Round_Key)
+{
+  unsigned char Row,Collum;
+
+  for(Collum = 0; Collum < 4; Collum++)
+  {
+    for(Row = 0; Row < 4; Row++)
+    {
+      State[Row][Collum] = State[Row][Collum] ^ Round_Key[Row + (4*Collum)];
+    }
+  }
+}
+
+/*
+*****************************************************************************************
+* Description : Function that substitutes a byte with a byte from the S_Table
+*
+* Arguments   : Byte    The byte that will be substituted
+*
+* Return      : The return is the found byte in the S_Table
+*****************************************************************************************
+*/
+static unsigned char AES_Sub_Byte(unsigned char Byte)
+{
+  unsigned char S_Row,S_Collum;
+  unsigned char S_Byte;
+
+  //Split byte up in Row and Collum
+  S_Row = ((Byte >> 4) & 0x0F);
+  S_Collum = (Byte & 0x0F);
+
+  //Find the correct byte in the S_Table
+  S_Byte = S_Table[S_Row][S_Collum];
+
+  return S_Byte;
+}
+
+/*
+*****************************************************************************************
+* Description : Function that preforms the shift row operation described in the AES standard
+*****************************************************************************************
+*/
+static void AES_Shift_Rows()
+{
+  unsigned char Buffer;
+
+  //Row 0 doesn't change
+
+  //Shift Row 1 one left
+  //Store firt byte in buffer
+  Buffer = State[1][0];
+  //Shift all bytes
+  State[1][0] = State[1][1];
+  State[1][1] = State[1][2];
+  State[1][2] = State[1][3];
+  State[1][3] = Buffer;
+
+  //Shift row 2 two left
+  Buffer = State[2][0];
+  State[2][0] = State[2][2];
+  State[2][2] = Buffer;
+  Buffer = State[2][1];
+  State[2][1] = State[2][3];
+  State[2][3] = Buffer;
+
+  //Shift row 3 three left
+  Buffer = State[3][3];
+  State[3][3] = State[3][2];
+  State[3][2] = State[3][1];
+  State[3][1] = State[3][0];
+  State[3][0] = Buffer;
+}
+
+/*
+*****************************************************************************************
+* Description : Function that preforms the Mix Collums operation described in the AES standard
+*****************************************************************************************
+*/
+static void AES_Mix_Collums()
+{
+  unsigned char Row,Collum;
+  unsigned char a[4], b[4];
+  for(Collum = 0; Collum < 4; Collum++)
+  {
+    for(Row = 0; Row < 4; Row++)
+    {
+      a[Row] = State[Row][Collum];
+      b[Row] = (State[Row][Collum] << 1);
+
+      if((State[Row][Collum] & 0x80) == 0x80)
+      {
+        b[Row] = b[Row] ^ 0x1B;
+      }
+    }
+    State[0][Collum] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3];
+    State[1][Collum] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3];
+    State[2][Collum] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3];
+    State[3][Collum] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3];
+  }
+}
+
+/*
+*****************************************************************************************
+* Description : Function that calculaties the round key for the current round
+*
+* Arguments   :   Round         Number of current Round
+*                *Round_Key     16 byte long array holding the Round Key
+*****************************************************************************************
+*/
+static void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key)
+{
+  unsigned char i,j;
+  unsigned char b;
+  unsigned char Temp[4];
+  unsigned char Buffer;
+  unsigned char Rcon;
+
+  //Calculate first Temp
+  //Copy laste byte from previous key
+  for(i = 0; i < 4; i++)
+  {
+    Temp[i] = Round_Key[i+12];
+  }
+
+  //Rotate Temp
+  Buffer = Temp[0];
+  Temp[0] = Temp[1];
+  Temp[1] = Temp[2];
+  Temp[2] = Temp[3];
+  Temp[3] = Buffer;
+
+  //Substitute Temp
+  for(i = 0; i < 4; i++)
+  {
+    Temp[i] = AES_Sub_Byte(Temp[i]);
+  }
+
+  //Calculate Rcon
+  Rcon = 0x01;
+  while(Round != 1)
+  {
+    b = Rcon & 0x80;
+    Rcon = Rcon << 1;
+    if(b == 0x80)
+    {
+      Rcon = Rcon ^ 0x1b;
+    }
+    Round--;
+  }
+
+  //XOR Rcon
+  Temp[0] = Temp[0] ^ Rcon;
+
+  //Calculate new key
+  for(i = 0; i < 4; i++)
+  {
+    for(j = 0; j < 4; j++)
+    {
+      Round_Key[j + (4*i)] = Round_Key[j + (4*i)] ^ Temp[j];
+      Temp[j] = Round_Key[j + (4*i)];
+    }
+  }
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/aes/AES-128_V10.h b/ESP-1ch-Gateway-v5.0-master/libraries/aes/AES-128_V10.h
new file mode 100644
index 0000000000000000000000000000000000000000..8d206579c5141f51148187dd398d10fe0e1780ad
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/aes/AES-128_V10.h
@@ -0,0 +1,55 @@
+/******************************************************************************************
+* Copyright 2015, 2016 Ideetron B.V.
+*
+* This program 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 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************************/
+
+/******************************************************************************************
+*
+* File:        AES-128_V10.h
+* Author:      Gerben den Hartog
+* Compagny:    Ideetron B.V.
+* Website:     http://www.ideetron.nl/LoRa
+* E-mail:      info@ideetron.nl
+* 
+******************************************************************************************/
+/****************************************************************************************
+*
+* Created on: 			20-10-2015
+* Supported Hardware: ID150119-02 Nexus board with RFM95
+*
+* Firmware Version 1.0
+* First version
+****************************************************************************************/
+
+#ifndef AES128_V10_H
+#define AES128_V10_H
+
+/*
+********************************************************************************************
+* FUNCTION PORTOTYPES
+********************************************************************************************
+*/
+
+void AES_Encrypt(unsigned char *Data, unsigned char *Key);
+void AES_Add_Round_Key(unsigned char *Round_Key);
+unsigned char AES_Sub_Byte(unsigned char Byte);
+void AES_Shift_Rows();
+void AES_Mix_Collums();
+void AES_Calculate_Round_Key(unsigned char Round, unsigned char *Round_Key);
+void Send_State();
+
+#else
+#error "AES128_V10_H not defined"
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/aes/README.md b/ESP-1ch-Gateway-v5.0-master/libraries/aes/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..60f761d14c90e7b4ad968f00e242c79099f5d241
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/aes/README.md
@@ -0,0 +1,27 @@
+AES Library for LoRa messages
+
+This library is part of the Ideetron code made for their Nexus board.
+It is used by LMIC library and by others as this library
+is not the most lightweight in terms of time but it is light in terms of
+code size.
+
+The licensing for this code is different from the 1ch Gateway and is detailed in
+the source code file(s) as follows:
+
+/******************************************************************************************
+#if defined(USE_IDEETRON_AES)
+* Copyright 2015, 2016 Ideetron B.V.
+*
+* This program 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 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 Lesser General Public License for more details.
+*
+* You should have received a copy of the GNU Lesser General Public License
+* along with this program.  If not, see <http://www.gnu.org/licenses/>.
+******************************************************************************************/
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/README.md b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..1839d118f3ce2227b82177cadaf37386c0ba4ec3
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/README.md
@@ -0,0 +1,17 @@
+# ESP32 HTTP Firmware Update (OTA)
+
+[![Codacy Badge](https://api.codacy.com/project/badge/Grade/03b36fac07824cd08884e1f19bb34fcb)](https://www.codacy.com/app/suculent/esp32-http-update?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=suculent/esp32-http-update&amp;utm_campaign=Badge_Grade)
+
+ESP Clone of https://github.com/esp8266/Arduino/tree/master/libraries/ESP8266httpUpdate (most work done by Markus Sattler).
+
+# TL;DR
+
+This is just a quick port of ESP8266httpUpdate for ESP32.
+
+Buildable with Arduino framework for ESP32.
+
+There are things remaining to be solved:
+
+* Some headers are deprecated (will change for ESP32 anyway)
+* Download to SPIFFS with AES-256 decryption
+* Does not support ESP-IDF.
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/examples/httpUpdate/httpUpdate.ino b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/examples/httpUpdate/httpUpdate.ino
new file mode 100644
index 0000000000000000000000000000000000000000..d79594f9a78fab9fc15c5a4c1083109de9d636eb
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/examples/httpUpdate/httpUpdate.ino
@@ -0,0 +1,57 @@
+/**
+ * httpUpdate.ino
+ *
+ *  Created on: 27.11.2015
+ *
+ */
+
+#include <Arduino.h>
+
+#include <WiFi.h>
+
+#include <HTTPClient.h>
+#include <ESP32httpUpdate.h>
+
+#define USE_SERIAL Serial
+
+void setup() {
+
+    USE_SERIAL.begin(115200);
+    // USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+    for(uint8_t t = 4; t > 0; t--) {
+        USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
+        USE_SERIAL.flush();
+        delay(1000);
+    }
+
+    WiFi.begin("SSID", "PASSWORD");
+
+}
+
+void loop() {
+    // wait for WiFi connection
+    if((WiFi.status() == WL_CONNECTED)) {
+
+        t_httpUpdate_return ret = ESPhttpUpdate.update("http://server/file.bin");
+
+        switch(ret) {
+            case HTTP_UPDATE_FAILED:
+                USE_SERIAL.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
+                break;
+
+            case HTTP_UPDATE_NO_UPDATES:
+                USE_SERIAL.println("HTTP_UPDATE_NO_UPDATES");
+                break;
+
+            case HTTP_UPDATE_OK:
+                USE_SERIAL.println("HTTP_UPDATE_OK");
+                break;
+        }
+    }
+}
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/examples/httpUpdateSPIFFS/httpUpdateSPIFFS.ino b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/examples/httpUpdateSPIFFS/httpUpdateSPIFFS.ino
new file mode 100644
index 0000000000000000000000000000000000000000..365461bc224b5e1b96ca61429f2758b4e79a1557
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/examples/httpUpdateSPIFFS/httpUpdateSPIFFS.ino
@@ -0,0 +1,61 @@
+/**
+ * httpUpdateSPIFFS.ino
+ *
+ *  Created on: 27.11.2017
+ *
+ */
+
+#include <Arduino.h>
+
+#include <WiFi.h>
+
+#include <HTTPClient.h>
+#include <ESP32httpUpdate.h>
+
+#define USE_SERIAL Serial
+
+void setup() {
+
+    USE_SERIAL.begin(115200);
+    // USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+    for(uint8_t t = 4; t > 0; t--) {
+        USE_SERIAL.printf("[SETUP] WAIT %d...\n", t);
+        USE_SERIAL.flush();
+        delay(1000);
+    }
+
+    WiFi.begin("SSID", "PASSWORD");
+
+}
+
+void loop() {
+    // wait for WiFi connection
+    if((WiFi.status() == WL_CONNECTED)) {
+
+        USE_SERIAL.println("Update SPIFFS...");
+        t_httpUpdate_return ret = ESPhttpUpdate.updateSpiffs("http://server/spiffs.bin");
+        if(ret == HTTP_UPDATE_OK) {
+            USE_SERIAL.println("Update sketch...");
+            ret = ESPhttpUpdate.update("http://server/file.bin");
+
+            switch(ret) {
+                case HTTP_UPDATE_FAILED:
+                    USE_SERIAL.printf("HTTP_UPDATE_FAILED Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
+                    break;
+
+                case HTTP_UPDATE_NO_UPDATES:
+                    USE_SERIAL.println("HTTP_UPDATE_NO_UPDATES");
+                    break;
+
+                case HTTP_UPDATE_OK:
+                    USE_SERIAL.println("HTTP_UPDATE_OK");
+                    break;
+            }
+        }
+    }
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/library.properties b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/library.properties
new file mode 100644
index 0000000000000000000000000000000000000000..1d2474d1d7f05131cb58ebd597f6d93918d6af0d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/library.properties
@@ -0,0 +1,9 @@
+name=ESP32httpUpdate
+version=2.1.145
+author=Matej Sychra
+maintainer=Matej Sychra
+sentence=Http Update for ESP32
+paragraph=Quick clone of Arduino ESP8266httpUpdate for ESP32 (without HTTPS requirement)
+category=Data Processing
+url=https://github.com/suculent/esp32-http-update
+architectures=esp32
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/src/ESP32httpUpdate.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/src/ESP32httpUpdate.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5aca24d2cd6ae48ff961e9f7889b5d5110f0f627
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/src/ESP32httpUpdate.cpp
@@ -0,0 +1,389 @@
+/**
+ *
+ * @file ESP32HTTPUpdate.cpp
+ * @date 27.11.2017
+ * @author Matej Sychra
+ *
+ * Copyright (c) 2017 Matej Sychra. All rights reserved.
+ * This file is part of the ESP32 Http Updater.
+ *
+ * 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 "ESP32httpUpdate.h"
+#include <StreamString.h>
+
+ESP32HTTPUpdate::ESP32HTTPUpdate(void)
+{
+}
+
+ESP32HTTPUpdate::~ESP32HTTPUpdate(void)
+{
+}
+
+HTTPUpdateResult ESP32HTTPUpdate::update(const String& url, const String& currentVersion,
+        const String& httpsCertificate, bool reboot)
+{
+    rebootOnUpdate(reboot);
+    return update(url, currentVersion, httpsCertificate);
+}
+
+HTTPUpdateResult ESP32HTTPUpdate::update(const String& url, const String& currentVersion)
+{
+    HTTPClient http;
+    http.begin(url);
+    return handleUpdate(http, currentVersion, false);
+}
+
+HTTPUpdateResult ESP32HTTPUpdate::update(const String& url, const String& currentVersion,
+        const String& httpsCertificate)
+{
+    HTTPClient http;
+    const char * cacert = strdup(httpsCertificate.c_str());
+    http.begin(url, cacert);
+    return handleUpdate(http, currentVersion, false);
+}
+
+HTTPUpdateResult ESP32HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion, const String& httpsCertificate)
+{
+    HTTPClient http;
+    const char * cacert = strdup(httpsCertificate.c_str());
+    http.begin(url, cacert);
+    return handleUpdate(http, currentVersion, true);
+}
+
+HTTPUpdateResult ESP32HTTPUpdate::updateSpiffs(const String& url, const String& currentVersion)
+{
+    HTTPClient http;
+    http.begin(url);
+    return handleUpdate(http, currentVersion, true);
+}
+
+HTTPUpdateResult ESP32HTTPUpdate::update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
+        bool https, const String& httpsCertificate, bool reboot)
+{
+    rebootOnUpdate(reboot);
+    if (httpsCertificate.length() == 0) {
+        return update(host, port, uri, currentVersion);
+    } else {
+        return update(host, port, uri, currentVersion, httpsCertificate);
+    }
+}
+
+HTTPUpdateResult ESP32HTTPUpdate::update(const String& host, uint16_t port, const String& uri,
+        const String& currentVersion)
+{
+    HTTPClient http;
+    http.begin(host, port, uri);
+    return handleUpdate(http, currentVersion, false);
+}
+HTTPUpdateResult ESP32HTTPUpdate::update(const String& host, uint16_t port, const String& url,
+        const String& currentVersion, const String& httpsCertificate)
+{
+    HTTPClient http;
+    const char * cacert = strdup(httpsCertificate.c_str());
+    http.begin(host, port, url, cacert);
+    return handleUpdate(http, currentVersion, false);
+
+}
+
+/**
+ * return error code as int
+ * @return int error code
+ */
+int ESP32HTTPUpdate::getLastError(void)
+{
+    return _lastError;
+}
+
+/**
+ * return error code as String
+ * @return String error
+ */
+String ESP32HTTPUpdate::getLastErrorString(void)
+{
+
+    if(_lastError == 0) {
+        return String(); // no error
+    }
+
+    // error from Update class
+    if(_lastError > 0) {
+        StreamString error;
+        Update.printError(error);
+        error.trim(); // remove line ending
+        return String(F("Update error: ")) + error;
+    }
+
+    // error from http client
+    if(_lastError > -100) {
+        return String(F("HTTP error: ")) + HTTPClient::errorToString(_lastError);
+    }
+
+    switch(_lastError) {
+    case HTTP_UE_TOO_LESS_SPACE:
+        return F("To less space");
+    case HTTP_UE_SERVER_NOT_REPORT_SIZE:
+        return F("Server not Report Size");
+    case HTTP_UE_SERVER_FILE_NOT_FOUND:
+        return F("File not Found (404)");
+    case HTTP_UE_SERVER_FORBIDDEN:
+        return F("Forbidden (403)");
+    case HTTP_UE_SERVER_WRONG_HTTP_CODE:
+        return F("Wrong HTTP code");
+    case HTTP_UE_SERVER_FAULTY_MD5:
+        return F("Faulty MD5");
+    case HTTP_UE_BIN_VERIFY_HEADER_FAILED:
+        return F("Verify bin header failed");
+    case HTTP_UE_BIN_FOR_WRONG_FLASH:
+        return F("bin for wrong flash size");
+    }
+
+    return String();
+}
+
+
+/**
+ *
+ * @param http HTTPClient *
+ * @param currentVersion const char *
+ * @return HTTPUpdateResult
+ */
+HTTPUpdateResult ESP32HTTPUpdate::handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs)
+{
+
+    HTTPUpdateResult ret = HTTP_UPDATE_FAILED;
+
+    // use HTTP/1.0 for update since the update handler not support any transfer Encoding
+    http.useHTTP10(true);
+    http.setTimeout(30000); // allow time to download on slower networks
+    http.setUserAgent(F("ESP32-http-Update"));
+    http.addHeader(F("x-ESP32-STA-MAC"), WiFi.macAddress());
+    http.addHeader(F("x-ESP32-AP-MAC"), WiFi.softAPmacAddress());
+    // http.addHeader(F("x-ESP32-free-space"), String(ESP.getFreeSketchSpace()));
+    // http.addHeader(F("x-ESP32-sketch-size"), String(ESP.getSketchSize()));
+    // http.addHeader(F("x-ESP32-sketch-md5"), String(ESP.getSketchMD5()));
+    // http.addHeader(F("x-ESP32-chip-size"), String(ESP.getFlashChipRealSize()));
+    http.addHeader(F("x-ESP32-sdk-version"), ESP.getSdkVersion());
+
+    if(spiffs) {
+        http.addHeader(F("x-ESP32-mode"), F("spiffs"));
+    } else {
+        http.addHeader(F("x-ESP32-mode"), F("sketch"));
+    }
+
+    if(currentVersion && currentVersion[0] != 0x00) {
+        http.addHeader(F("x-ESP32-version"), currentVersion);
+    }
+
+    const char * headerkeys[] = { "x-MD5" };
+    size_t headerkeyssize = sizeof(headerkeys) / sizeof(char*);
+
+    // track these headers
+    http.collectHeaders(headerkeys, headerkeyssize);
+
+
+    int code = http.GET();
+    int len = http.getSize();
+
+    if(code <= 0) {
+        DEBUG_HTTP_UPDATE("[httpUpdate] HTTP error: %s\n", http.errorToString(code).c_str());
+        _lastError = code;
+        http.end();
+        return HTTP_UPDATE_FAILED;
+    }
+
+
+    DEBUG_HTTP_UPDATE("[httpUpdate] Header read fin.\n");
+    DEBUG_HTTP_UPDATE("[httpUpdate] Server header:\n");
+    DEBUG_HTTP_UPDATE("[httpUpdate]  - code: %d\n", code);
+    DEBUG_HTTP_UPDATE("[httpUpdate]  - len: %d\n", len);
+
+    if(http.hasHeader("x-MD5")) {
+        DEBUG_HTTP_UPDATE("[httpUpdate]  - MD5: %s\n", http.header("x-MD5").c_str());
+    }
+
+    if(currentVersion && currentVersion[0] != 0x00) {
+        DEBUG_HTTP_UPDATE("[httpUpdate]  - current version: %s\n", currentVersion.c_str() );
+    }
+
+    switch(code) {
+    case HTTP_CODE_OK:  ///< OK (Start Update)
+        if(len > 0) {
+            bool startUpdate = true;
+            if(spiffs) {
+                size_t spiffsSize = ((size_t) SPIFFS.totalBytes() - (size_t) SPIFFS.usedBytes());
+                if(len > (int) spiffsSize) {
+                    DEBUG_HTTP_UPDATE("[httpUpdate] spiffsSize to low (%d) needed: %d\n", spiffsSize, len);
+                    startUpdate = false;
+                }
+            } else {
+                //if(len > (int) ESP.getFreeSketchSpace()) {
+                //    DEBUG_HTTP_UPDATE("[httpUpdate] FreeSketchSpace to low (%d) needed: %d\n", ESP.getFreeSketchSpace(), len);
+                //    startUpdate = false;
+                //}
+            }
+
+            if(!startUpdate) {
+                _lastError = HTTP_UE_TOO_LESS_SPACE;
+                ret = HTTP_UPDATE_FAILED;
+            } else {
+
+                WiFiClient * tcp = http.getStreamPtr();
+
+                //WiFiUDP::stopAll();
+                //WiFiClient::stopAllExcept(tcp);
+
+                delay(100);
+
+                int command;
+
+                if(spiffs) {
+                    command = U_SPIFFS;
+                    DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate spiffs...\n");
+                } else {
+                    command = U_FLASH;
+                    DEBUG_HTTP_UPDATE("[httpUpdate] runUpdate flash...\n");
+                }
+
+                if(!spiffs) {
+
+                    /*
+                    uint8_t buf[4];
+                    if(tcp->peekBytes(&buf[0], 4) != 4) {
+                        DEBUG_HTTP_UPDATE("[httpUpdate] peekBytes magic header failed\n");
+                        _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
+                        http.end();
+                        return HTTP_UPDATE_FAILED;
+                    }
+
+                    // check for valid first magic byte
+                    if(buf[0] != 0xE9) {
+                        DEBUG_HTTP_UPDATE("[httpUpdate] magic header not starts with 0xE9\n");
+                        _lastError = HTTP_UE_BIN_VERIFY_HEADER_FAILED;
+                        http.end();
+                        return HTTP_UPDATE_FAILED;
+
+                    }
+                    */
+
+                    // uint32_t bin_flash_size = ESP.magicFlashChipSize((buf[3] & 0xf0) >> 4);
+
+                    /*
+                    // check if new bin fits to SPI flash
+                    if(bin_flash_size > ESP.getFlashChipRealSize()) {
+                        DEBUG_HTTP_UPDATE("[httpUpdate] magic header, new bin not fits SPI Flash\n");
+                        _lastError = HTTP_UE_BIN_FOR_WRONG_FLASH;
+                        http.end();
+                        return HTTP_UPDATE_FAILED;
+                    }
+                    */
+                }
+
+                if(runUpdate(*tcp, len, http.header("x-MD5"), command)) {
+                    ret = HTTP_UPDATE_OK;
+                    DEBUG_HTTP_UPDATE("[httpUpdate] Update ok\n");
+                    http.end();
+
+                    if(_rebootOnUpdate && !spiffs) {
+                        ESP.restart();
+                    }
+
+                } else {
+                    ret = HTTP_UPDATE_FAILED;
+                    DEBUG_HTTP_UPDATE("[httpUpdate] Update failed\n");
+                }
+            }
+        } else {
+            _lastError = HTTP_UE_SERVER_NOT_REPORT_SIZE;
+            ret = HTTP_UPDATE_FAILED;
+            DEBUG_HTTP_UPDATE("[httpUpdate] Content-Length is 0 or not set by Server?!\n");
+        }
+        break;
+    case HTTP_CODE_NOT_MODIFIED:
+        ///< Not Modified (No updates)
+        ret = HTTP_UPDATE_NO_UPDATES;
+        break;
+    case HTTP_CODE_NOT_FOUND:
+        _lastError = HTTP_UE_SERVER_FILE_NOT_FOUND;
+        ret = HTTP_UPDATE_FAILED;
+        break;
+    case HTTP_CODE_FORBIDDEN:
+        _lastError = HTTP_UE_SERVER_FORBIDDEN;
+        ret = HTTP_UPDATE_FAILED;
+        break;
+    default:
+        _lastError = HTTP_UE_SERVER_WRONG_HTTP_CODE;
+        ret = HTTP_UPDATE_FAILED;
+        DEBUG_HTTP_UPDATE("[httpUpdate] HTTP Code is (%d)\n", code);
+        //http.writeToStream(&Serial1);
+        break;
+    }
+
+    http.end();
+    return ret;
+}
+
+/**
+ * write Update to flash
+ * @param in Stream&
+ * @param size uint32_t
+ * @param md5 String
+ * @return true if Update ok
+ */
+bool ESP32HTTPUpdate::runUpdate(Stream& in, uint32_t size, String md5, int command)
+{
+
+    StreamString error;
+
+    if(!Update.begin(size, command)) {
+        _lastError = Update.getError();
+        Update.printError(error);
+        error.trim(); // remove line ending
+        DEBUG_HTTP_UPDATE("[httpUpdate] Update.begin failed! (%s)\n", error.c_str());
+        return false;
+    }
+
+    if(md5.length()) {
+        if(!Update.setMD5(md5.c_str())) {
+            _lastError = HTTP_UE_SERVER_FAULTY_MD5;
+            DEBUG_HTTP_UPDATE("[httpUpdate] Update.setMD5 failed! (%s)\n", md5.c_str());
+            return false;
+        }
+    }
+
+    if(Update.writeStream(in) != size) {
+        _lastError = Update.getError();
+        Update.printError(error);
+        error.trim(); // remove line ending
+        DEBUG_HTTP_UPDATE("[httpUpdate] Update.writeStream failed! (%s)\n", error.c_str());
+        return false;
+    }
+
+    if(!Update.end()) {
+        _lastError = Update.getError();
+        Update.printError(error);
+        error.trim(); // remove line ending
+        DEBUG_HTTP_UPDATE("[httpUpdate] Update.end failed! (%s)\n", error.c_str());
+        return false;
+    }
+
+    return true;
+}
+
+#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
+ESP32HTTPUpdate ESPhttpUpdate;
+#endif
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/src/ESP32httpUpdate.h b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/src/ESP32httpUpdate.h
new file mode 100644
index 0000000000000000000000000000000000000000..31d45e9a3847be08f47474910d44c11c2e5970ac
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/esp32-http-update/src/ESP32httpUpdate.h
@@ -0,0 +1,116 @@
+/**
+ *
+ * @file ESP32HTTPUpdate.h
+ * @date 27.11.2017
+ * @author Matej Sychra
+ *
+ * Copyright (c) 2017 Matej Sychra. All rights reserved.
+ * This file is part of the ESP32 Http Updater.
+ *
+ * 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 ESP32HTTPUPDATE_H_
+#define ESP32HTTPUPDATE_H_
+
+#include <Arduino.h>
+#include <WiFi.h>
+#include <WiFiClient.h>
+#include <WiFiUdp.h>
+#include <HTTPClient.h>
+#include <Update.h>
+
+#include "FS.h"
+#include "SPIFFS.h"
+
+#ifdef DEBUG_ESP_HTTP_UPDATE
+#ifdef DEBUG_ESP_PORT
+#define DEBUG_HTTP_UPDATE(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ )
+#endif
+#endif
+
+#ifndef DEBUG_HTTP_UPDATE
+#define DEBUG_HTTP_UPDATE(...)
+#endif
+
+/// note we use HTTP client errors too so we start at 100
+#define HTTP_UE_TOO_LESS_SPACE              (-100)
+#define HTTP_UE_SERVER_NOT_REPORT_SIZE      (-101)
+#define HTTP_UE_SERVER_FILE_NOT_FOUND       (-102)
+#define HTTP_UE_SERVER_FORBIDDEN            (-103)
+#define HTTP_UE_SERVER_WRONG_HTTP_CODE      (-104)
+#define HTTP_UE_SERVER_FAULTY_MD5           (-105)
+#define HTTP_UE_BIN_VERIFY_HEADER_FAILED    (-106)
+#define HTTP_UE_BIN_FOR_WRONG_FLASH         (-107)
+
+enum HTTPUpdateResult {
+    HTTP_UPDATE_FAILED,
+    HTTP_UPDATE_NO_UPDATES,
+    HTTP_UPDATE_OK
+};
+
+typedef HTTPUpdateResult t_httpUpdate_return; // backward compatibility
+
+class ESP32HTTPUpdate
+{
+public:
+    ESP32HTTPUpdate(void);
+    ~ESP32HTTPUpdate(void);
+
+    void rebootOnUpdate(bool reboot)
+    {
+        _rebootOnUpdate = reboot;
+    }
+
+    // This function is deprecated, use rebootOnUpdate and the next one instead
+    t_httpUpdate_return update(const String& url, const String& currentVersion,
+                               const String& httpsCertificate, bool reboot) __attribute__((deprecated));
+    t_httpUpdate_return update(const String& url, const String& currentVersion = "");
+    t_httpUpdate_return update(const String& url, const String& currentVersion,
+                               const String& httpsCertificate);
+
+    // This function is deprecated, use one of the overloads below along with rebootOnUpdate
+    t_httpUpdate_return update(const String& host, uint16_t port, const String& uri, const String& currentVersion,
+                               bool https, const String& httpsCertificate, bool reboot) __attribute__((deprecated));
+
+    t_httpUpdate_return update(const String& host, uint16_t port, const String& uri = "/",
+                               const String& currentVersion = "");
+    t_httpUpdate_return update(const String& host, uint16_t port, const String& url,
+                               const String& currentVersion, const String& httpsCertificate);
+
+    // This function is deprecated, use rebootOnUpdate and the next one instead
+    t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion,
+                                     const String& httpsCertificate, bool reboot) __attribute__((deprecated));
+    t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion = "");
+    t_httpUpdate_return updateSpiffs(const String& url, const String& currentVersion, const String& httpsCertificate);
+
+
+    int getLastError(void);
+    String getLastErrorString(void);
+
+protected:
+    t_httpUpdate_return handleUpdate(HTTPClient& http, const String& currentVersion, bool spiffs = false);
+    bool runUpdate(Stream& in, uint32_t size, String md5, int command = U_FLASH);
+
+    int _lastError;
+    bool _rebootOnUpdate = true;
+};
+
+#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_HTTPUPDATE)
+extern ESP32HTTPUpdate ESPhttpUpdate;
+#endif
+
+#endif /* ESP32HTTPUPDATE_H_ */
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/LICENSE b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..444856b043f361d1ad31663de955a3b2792b5b0d
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/LICENSE
@@ -0,0 +1,7 @@
+Copyright (C) 2013 Adam Rudd
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/README.markdown b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/README.markdown
new file mode 100644
index 0000000000000000000000000000000000000000..9311ce7a90b43d9d962100d3ece4c83f440b5f90
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/README.markdown
@@ -0,0 +1,4 @@
+Introduction
+------------
+
+This library provides methods for encoding binary into base64 strings and the reverse operation.
\ No newline at end of file
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/examples/base64/base64.ino b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/examples/base64/base64.ino
new file mode 100644
index 0000000000000000000000000000000000000000..45af03d0c550b3b9880f5dcfd454191a433244c4
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/examples/base64/base64.ino
@@ -0,0 +1,61 @@
+#include <gBase64.h>
+#include <ESP.h>
+
+/*
+ Base64 Encode/Decode example
+ 
+ Encodes the text "Hello world" to "SGVsbG8gd29ybGQA" and decodes "Zm9vYmFy" to "foobar"
+
+ Created 29 April 2015
+ by Nathan Friedly - http://nfriedly.com/
+ 
+ This example code is in the public domain.
+
+ */
+
+
+void setup()
+{
+  // start serial port at 9600 bps:
+  Serial.begin(9600);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for Leonardo only
+  }
+  
+  Serial.println("Base64 example");
+  
+  
+  
+  // encoding
+  char input[] = "Hello world";
+  int inputLen = sizeof(input);
+  
+  int encodedLen = base64_enc_len(inputLen);
+  char encoded[encodedLen];
+  
+  Serial.print(input); Serial.print(" = ");
+  
+  // note input is consumed in this step: it will be empty afterwards
+  base64_encode(encoded, input, inputLen); 
+  
+  Serial.println(encoded);
+  
+  
+  
+  // decoding
+  char input2[] = "Zm9vYmFy";
+  int input2Len = sizeof(input2);
+  
+  int decodedLen = base64_dec_len(input2, input2Len);
+  char decoded[decodedLen];
+  
+  base64_decode(decoded, input2, input2Len);
+  
+  Serial.print(input2); Serial.print(" = "); Serial.println(decoded);
+}
+
+
+void loop()
+{
+  
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/gBase64.cpp b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/gBase64.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e125de4e30c2603cbc85e1b685c03e786453281c
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/gBase64.cpp
@@ -0,0 +1,132 @@
+#include "gBase64.h"
+//#include <avr/pgmspace.h>
+
+const char b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+		"abcdefghijklmnopqrstuvwxyz"
+		"0123456789+/";
+
+/* 'Private' declarations */
+inline void a3_to_a4(unsigned char * a4, unsigned char * a3);
+inline void a4_to_a3(unsigned char * a3, unsigned char * a4);
+inline unsigned char b64_lookup(char c);
+
+int base64_encode(char *output, char *input, int inputLen) {
+	int i = 0, j = 0;
+	int encLen = 0;
+	unsigned char a3[3];
+	unsigned char a4[4];
+
+	while(inputLen--) {
+		a3[i++] = *(input++);
+		if(i == 3) {
+			a3_to_a4(a4, a3);
+
+			for(i = 0; i < 4; i++) {
+				output[encLen++] = b64_alphabet[a4[i]];
+			}
+
+			i = 0;
+		}
+	}
+
+	if(i) {
+		for(j = i; j < 3; j++) {
+			a3[j] = '\0';
+		}
+
+		a3_to_a4(a4, a3);
+
+		for(j = 0; j < i + 1; j++) {
+			output[encLen++] = b64_alphabet[a4[j]];
+		}
+
+		while((i++ < 3)) {
+			output[encLen++] = '=';
+		}
+	}
+	output[encLen] = '\0';
+	return encLen;
+}
+
+int base64_decode(char * output, char * input, int inputLen) {
+	int i = 0, j = 0;
+	int decLen = 0;
+	unsigned char a3[3];
+	unsigned char a4[4];
+
+
+	while (inputLen--) {
+		if(*input == '=') {
+			break;
+		}
+
+		a4[i++] = *(input++);
+		if (i == 4) {
+			for (i = 0; i <4; i++) {
+				a4[i] = b64_lookup(a4[i]);
+			}
+
+			a4_to_a3(a3,a4);
+
+			for (i = 0; i < 3; i++) {
+				output[decLen++] = a3[i];
+			}
+			i = 0;
+		}
+	}
+
+	if (i) {
+		for (j = i; j < 4; j++) {
+			a4[j] = '\0';
+		}
+
+		for (j = 0; j <4; j++) {
+			a4[j] = b64_lookup(a4[j]);
+		}
+
+		a4_to_a3(a3,a4);
+
+		for (j = 0; j < i - 1; j++) {
+			output[decLen++] = a3[j];
+		}
+	}
+	output[decLen] = '\0';
+	return decLen;
+}
+
+int base64_enc_len(int plainLen) {
+	int n = plainLen;
+	return (n + 2 - ((n + 2) % 3)) / 3 * 4;
+}
+
+int base64_dec_len(char * input, int inputLen) {
+	int i = 0;
+	int numEq = 0;
+	for(i = inputLen - 1; input[i] == '='; i--) {
+		numEq++;
+	}
+
+	return ((6 * inputLen) / 8) - numEq;
+}
+
+inline void a3_to_a4(unsigned char * a4, unsigned char * a3) {
+	a4[0] = (a3[0] & 0xfc) >> 2;
+	a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
+	a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
+	a4[3] = (a3[2] & 0x3f);
+}
+
+inline void a4_to_a3(unsigned char * a3, unsigned char * a4) {
+	a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
+	a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
+	a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
+}
+
+inline unsigned char b64_lookup(char c) {
+	if(c >='A' && c <='Z') return c - 'A';
+	if(c >='a' && c <='z') return c - 71;
+	if(c >='0' && c <='9') return c + 4;
+	if(c == '+') return 62;
+	if(c == '/') return 63;
+	return -1;
+}
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/gBase64.h b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/gBase64.h
new file mode 100644
index 0000000000000000000000000000000000000000..35a537b33098dbfe2a13824c33c106d0bdfab98e
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/gBase64.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2013 Adam Rudd.
+ * See LICENSE for more information
+ */
+#ifndef _BASE64_H
+#define _BASE64_H
+
+/* b64_alphabet:
+ * 		Description: Base64 alphabet table, a mapping between integers
+ * 					 and base64 digits
+ * 		Notes: This is an extern here but is defined in Base64.c
+ */
+extern const char b64_alphabet[];
+
+/* base64_encode:
+ * 		Description:
+ * 			Encode a string of characters as base64
+ * 		Parameters:
+ * 			output: the output buffer for the encoding, stores the encoded string
+ * 			input: the input buffer for the encoding, stores the binary to be encoded
+ * 			inputLen: the length of the input buffer, in bytes
+ * 		Return value:
+ * 			Returns the length of the encoded string
+ * 		Requirements:
+ * 			1. output must not be null or empty
+ * 			2. input must not be null
+ * 			3. inputLen must be greater than or equal to 0
+ */
+int base64_encode(char *output, char *input, int inputLen);
+
+/* base64_decode:
+ * 		Description:
+ * 			Decode a base64 encoded string into bytes
+ * 		Parameters:
+ * 			output: the output buffer for the decoding,
+ * 					stores the decoded binary
+ * 			input: the input buffer for the decoding,
+ * 				   stores the base64 string to be decoded
+ * 			inputLen: the length of the input buffer, in bytes
+ * 		Return value:
+ * 			Returns the length of the decoded string
+ * 		Requirements:
+ * 			1. output must not be null or empty
+ * 			2. input must not be null
+ * 			3. inputLen must be greater than or equal to 0
+ */
+int base64_decode(char *output, char *input, int inputLen);
+
+/* base64_enc_len:
+ * 		Description:
+ * 			Returns the length of a base64 encoded string whose decoded
+ * 			form is inputLen bytes long
+ * 		Parameters:
+ * 			inputLen: the length of the decoded string
+ * 		Return value:
+ * 			The length of a base64 encoded string whose decoded form
+ * 			is inputLen bytes long
+ * 		Requirements:
+ * 			None
+ */
+int base64_enc_len(int inputLen);
+
+/* base64_dec_len:
+ * 		Description:
+ * 			Returns the length of the decoded form of a
+ * 			base64 encoded string
+ * 		Parameters:
+ * 			input: the base64 encoded string to be measured
+ * 			inputLen: the length of the base64 encoded string
+ * 		Return value:
+ * 			Returns the length of the decoded form of a
+ * 			base64 encoded string
+ * 		Requirements:
+ * 			1. input must not be null
+ * 			2. input must be greater than or equal to zero
+ */
+int base64_dec_len(char *input, int inputLen);
+
+#endif // _BASE64_H
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/keywords (1).txt b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/keywords (1).txt
new file mode 100644
index 0000000000000000000000000000000000000000..970fda0a1a2498a879292bd7ef0fe96e51b293a1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/keywords (1).txt	
@@ -0,0 +1,24 @@
+#######################################
+# Syntax Coloring Map For Base64
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+# I WANT BASE64.h HIGHLIGHTED, DAMMIT!
+Base64		KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+base64_encode 	KEYWORD2
+base64_decode 	KEYWORD2
+base64_enc_len 	KEYWORD2
+base64_dec_len 	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/keywords.txt b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/keywords.txt
new file mode 100644
index 0000000000000000000000000000000000000000..970fda0a1a2498a879292bd7ef0fe96e51b293a1
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/keywords.txt
@@ -0,0 +1,24 @@
+#######################################
+# Syntax Coloring Map For Base64
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+# I WANT BASE64.h HIGHLIGHTED, DAMMIT!
+Base64		KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+base64_encode 	KEYWORD2
+base64_decode 	KEYWORD2
+base64_enc_len 	KEYWORD2
+base64_dec_len 	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
diff --git a/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/library.properties b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/library.properties
new file mode 100644
index 0000000000000000000000000000000000000000..93d42b7c2f9e558ed98d90c78690fa0e1bde0ebb
--- /dev/null
+++ b/ESP-1ch-Gateway-v5.0-master/libraries/gBase64/library.properties
@@ -0,0 +1,9 @@
+name=base64
+version=1.0.0
+author=Adam Rudd
+maintainer=Adam Rudd
+sentence=A base64 library for the arduino platform, written in C
+paragraph=
+category=Data Processing
+url=https://github.com/adamvr/arduino-base64
+architectures=*
diff --git a/test2.txt b/test2.txt
index 896266208f3cfc73db7a0191ec2bd9bd45889a17..3b004880b0225b835e404d3dcff7e1fd1c66eafb 100644
--- a/test2.txt
+++ b/test2.txt
@@ -1 +1,2 @@
-whatever
\ No newline at end of file
+whatever
+Sep
\ No newline at end of file