diff --git a/lib/readme.txt b/lib/readme.txt
deleted file mode 100644
index 3f9e008a3dcf3bab372049af66357ff3619218fd..0000000000000000000000000000000000000000
--- a/lib/readme.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-
-This directory is intended for the project specific (private) libraries.
-PlatformIO will compile them to static libraries and link to executable file.
-
-The source code of each library should be placed in separate directory, like
-"lib/private_lib/[here are source files]".
-
-For example, see how can be organized `Foo` and `Bar` libraries:
-
-|--lib
-|  |--Bar
-|  |  |--docs
-|  |  |--examples
-|  |  |--src
-|  |     |- Bar.c
-|  |     |- Bar.h
-|  |--Foo
-|  |  |- Foo.c
-|  |  |- Foo.h
-|  |- readme.txt --> THIS FILE
-|- platformio.ini
-|--src
-   |- main.c
-
-Then in `src/main.c` you should use:
-
-#include <Foo.h>
-#include <Bar.h>
-
-// rest H/C/CPP code
-
-PlatformIO will find your libraries automatically, configure preprocessor's
-include paths and build them.
-
-More information about PlatformIO Library Dependency Finder
-- http://docs.platformio.org/en/stable/librarymanager/ldf.html
diff --git a/libraries/LiquidCrystal_I2C_ID576/.library.json b/libraries/LiquidCrystal_I2C_ID576/.library.json
new file mode 100644
index 0000000000000000000000000000000000000000..c6d47a9743096281699ad6d5a571e1c897f6313a
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/.library.json
@@ -0,0 +1,45 @@
+{
+    "name": "LiquidCrystal_I2C", 
+    "version": "1.1.4", 
+    "keywords": [
+        "display"
+    ], 
+    "description": "A library for I2C LCD displays.", 
+    "frameworks": [
+        "arduino"
+    ], 
+    "platforms": [
+        "atmelavr"
+    ], 
+    "authors": [
+        {
+            "email": null, 
+            "url": null, 
+            "maintainer": false, 
+            "name": "Frank de Brabander"
+        }, 
+        {
+            "email": "marcolivier.schwartz@gmail.com", 
+            "url": null, 
+            "maintainer": true, 
+            "name": "Marco Schwartz"
+        }
+    ], 
+    "repository": {
+        "type": "git", 
+        "url": "https://github.com/marcoschwartz/LiquidCrystal_I2C"
+    }, 
+    "homepage": null, 
+    "export": {
+        "include": null, 
+        "exclude": [
+            "extras", 
+            "docs", 
+            "tests", 
+            "test", 
+            "*.doxyfile", 
+            "*.pdf"
+        ]
+    }, 
+    "id": 576
+}
\ No newline at end of file
diff --git a/libraries/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.cpp b/libraries/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9dea08c59edc6163651275385bb4d86e2d1c6911
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.cpp
@@ -0,0 +1,332 @@
+// Based on the work by DFRobot
+
+#include "LiquidCrystal_I2C.h"
+#include <inttypes.h>
+#if defined(ARDUINO) && ARDUINO >= 100
+
+#include "Arduino.h"
+
+#define printIIC(args)	Wire.write(args)
+inline size_t LiquidCrystal_I2C::write(uint8_t value) {
+	send(value, Rs);
+	return 1;
+}
+
+#else
+#include "WProgram.h"
+
+#define printIIC(args)	Wire.send(args)
+inline void LiquidCrystal_I2C::write(uint8_t value) {
+	send(value, Rs);
+}
+
+#endif
+#include "Wire.h"
+
+
+
+// When the display powers up, it is configured as follows:
+//
+// 1. Display clear
+// 2. Function set: 
+//    DL = 1; 8-bit interface data 
+//    N = 0; 1-line display 
+//    F = 0; 5x8 dot character font 
+// 3. Display on/off control: 
+//    D = 0; Display off 
+//    C = 0; Cursor off 
+//    B = 0; Blinking off 
+// 4. Entry mode set: 
+//    I/D = 1; Increment by 1
+//    S = 0; No shift 
+//
+// Note, however, that resetting the Arduino doesn't reset the LCD, so we
+// can't assume that its in that state when a sketch starts (and the
+// LiquidCrystal constructor is called).
+
+LiquidCrystal_I2C::LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows)
+{
+  _Addr = lcd_Addr;
+  _cols = lcd_cols;
+  _rows = lcd_rows;
+  _backlightval = LCD_NOBACKLIGHT;
+}
+
+void LiquidCrystal_I2C::oled_init(){
+  _oled = true;
+	init_priv();
+}
+
+void LiquidCrystal_I2C::init(){
+	init_priv();
+}
+
+void LiquidCrystal_I2C::init_priv()
+{
+	Wire.begin();
+	_displayfunction = LCD_4BITMODE | LCD_1LINE | LCD_5x8DOTS;
+	begin(_cols, _rows);  
+}
+
+void LiquidCrystal_I2C::begin(uint8_t cols, uint8_t lines, uint8_t dotsize) {
+	if (lines > 1) {
+		_displayfunction |= LCD_2LINE;
+	}
+	_numlines = lines;
+
+	// for some 1 line displays you can select a 10 pixel high font
+	if ((dotsize != 0) && (lines == 1)) {
+		_displayfunction |= LCD_5x10DOTS;
+	}
+
+	// SEE PAGE 45/46 FOR INITIALIZATION SPECIFICATION!
+	// according to datasheet, we need at least 40ms after power rises above 2.7V
+	// before sending commands. Arduino can turn on way befer 4.5V so we'll wait 50
+	delay(50); 
+  
+	// Now we pull both RS and R/W low to begin commands
+	expanderWrite(_backlightval);	// reset expanderand turn backlight off (Bit 8 =1)
+	delay(1000);
+
+  	//put the LCD into 4 bit mode
+	// this is according to the hitachi HD44780 datasheet
+	// figure 24, pg 46
+	
+	  // we start in 8bit mode, try to set 4 bit mode
+   write4bits(0x03 << 4);
+   delayMicroseconds(4500); // wait min 4.1ms
+   
+   // second try
+   write4bits(0x03 << 4);
+   delayMicroseconds(4500); // wait min 4.1ms
+   
+   // third go!
+   write4bits(0x03 << 4); 
+   delayMicroseconds(150);
+   
+   // finally, set to 4-bit interface
+   write4bits(0x02 << 4); 
+
+
+	// set # lines, font size, etc.
+	command(LCD_FUNCTIONSET | _displayfunction);  
+	
+	// turn the display on with no cursor or blinking default
+	_displaycontrol = LCD_DISPLAYON | LCD_CURSOROFF | LCD_BLINKOFF;
+	display();
+	
+	// clear it off
+	clear();
+	
+	// Initialize to default text direction (for roman languages)
+	_displaymode = LCD_ENTRYLEFT | LCD_ENTRYSHIFTDECREMENT;
+	
+	// set the entry mode
+	command(LCD_ENTRYMODESET | _displaymode);
+	
+	home();
+  
+}
+
+/********** high level commands, for the user! */
+void LiquidCrystal_I2C::clear(){
+	command(LCD_CLEARDISPLAY);// clear display, set cursor position to zero
+	delayMicroseconds(2000);  // this command takes a long time!
+  if (_oled) setCursor(0,0);
+}
+
+void LiquidCrystal_I2C::home(){
+	command(LCD_RETURNHOME);  // set cursor position to zero
+	delayMicroseconds(2000);  // this command takes a long time!
+}
+
+void LiquidCrystal_I2C::setCursor(uint8_t col, uint8_t row){
+	int row_offsets[] = { 0x00, 0x40, 0x14, 0x54 };
+	if ( row > _numlines ) {
+		row = _numlines-1;    // we count rows starting w/0
+	}
+	command(LCD_SETDDRAMADDR | (col + row_offsets[row]));
+}
+
+// Turn the display on/off (quickly)
+void LiquidCrystal_I2C::noDisplay() {
+	_displaycontrol &= ~LCD_DISPLAYON;
+	command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+void LiquidCrystal_I2C::display() {
+	_displaycontrol |= LCD_DISPLAYON;
+	command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+// Turns the underline cursor on/off
+void LiquidCrystal_I2C::noCursor() {
+	_displaycontrol &= ~LCD_CURSORON;
+	command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+void LiquidCrystal_I2C::cursor() {
+	_displaycontrol |= LCD_CURSORON;
+	command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+// Turn on and off the blinking cursor
+void LiquidCrystal_I2C::noBlink() {
+	_displaycontrol &= ~LCD_BLINKON;
+	command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+void LiquidCrystal_I2C::blink() {
+	_displaycontrol |= LCD_BLINKON;
+	command(LCD_DISPLAYCONTROL | _displaycontrol);
+}
+
+// These commands scroll the display without changing the RAM
+void LiquidCrystal_I2C::scrollDisplayLeft(void) {
+	command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVELEFT);
+}
+void LiquidCrystal_I2C::scrollDisplayRight(void) {
+	command(LCD_CURSORSHIFT | LCD_DISPLAYMOVE | LCD_MOVERIGHT);
+}
+
+// This is for text that flows Left to Right
+void LiquidCrystal_I2C::leftToRight(void) {
+	_displaymode |= LCD_ENTRYLEFT;
+	command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// This is for text that flows Right to Left
+void LiquidCrystal_I2C::rightToLeft(void) {
+	_displaymode &= ~LCD_ENTRYLEFT;
+	command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// This will 'right justify' text from the cursor
+void LiquidCrystal_I2C::autoscroll(void) {
+	_displaymode |= LCD_ENTRYSHIFTINCREMENT;
+	command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// This will 'left justify' text from the cursor
+void LiquidCrystal_I2C::noAutoscroll(void) {
+	_displaymode &= ~LCD_ENTRYSHIFTINCREMENT;
+	command(LCD_ENTRYMODESET | _displaymode);
+}
+
+// Allows us to fill the first 8 CGRAM locations
+// with custom characters
+void LiquidCrystal_I2C::createChar(uint8_t location, uint8_t charmap[]) {
+	location &= 0x7; // we only have 8 locations 0-7
+	command(LCD_SETCGRAMADDR | (location << 3));
+	for (int i=0; i<8; i++) {
+		write(charmap[i]);
+	}
+}
+
+//createChar with PROGMEM input
+void LiquidCrystal_I2C::createChar(uint8_t location, const char *charmap) {
+	location &= 0x7; // we only have 8 locations 0-7
+	command(LCD_SETCGRAMADDR | (location << 3));
+	for (int i=0; i<8; i++) {
+	    	write(pgm_read_byte_near(charmap++));
+	}
+}
+
+// Turn the (optional) backlight off/on
+void LiquidCrystal_I2C::noBacklight(void) {
+	_backlightval=LCD_NOBACKLIGHT;
+	expanderWrite(0);
+}
+
+void LiquidCrystal_I2C::backlight(void) {
+	_backlightval=LCD_BACKLIGHT;
+	expanderWrite(0);
+}
+
+
+
+/*********** mid level commands, for sending data/cmds */
+
+inline void LiquidCrystal_I2C::command(uint8_t value) {
+	send(value, 0);
+}
+
+
+/************ low level data pushing commands **********/
+
+// write either command or data
+void LiquidCrystal_I2C::send(uint8_t value, uint8_t mode) {
+	uint8_t highnib=value&0xf0;
+	uint8_t lownib=(value<<4)&0xf0;
+       write4bits((highnib)|mode);
+	write4bits((lownib)|mode); 
+}
+
+void LiquidCrystal_I2C::write4bits(uint8_t value) {
+	expanderWrite(value);
+	pulseEnable(value);
+}
+
+void LiquidCrystal_I2C::expanderWrite(uint8_t _data){                                        
+	Wire.beginTransmission(_Addr);
+	printIIC((int)(_data) | _backlightval);
+	Wire.endTransmission();   
+}
+
+void LiquidCrystal_I2C::pulseEnable(uint8_t _data){
+	expanderWrite(_data | En);	// En high
+	delayMicroseconds(1);		// enable pulse must be >450ns
+	
+	expanderWrite(_data & ~En);	// En low
+	delayMicroseconds(50);		// commands need > 37us to settle
+} 
+
+
+// Alias functions
+
+void LiquidCrystal_I2C::cursor_on(){
+	cursor();
+}
+
+void LiquidCrystal_I2C::cursor_off(){
+	noCursor();
+}
+
+void LiquidCrystal_I2C::blink_on(){
+	blink();
+}
+
+void LiquidCrystal_I2C::blink_off(){
+	noBlink();
+}
+
+void LiquidCrystal_I2C::load_custom_character(uint8_t char_num, uint8_t *rows){
+		createChar(char_num, rows);
+}
+
+void LiquidCrystal_I2C::setBacklight(uint8_t new_val){
+	if(new_val){
+		backlight();		// turn backlight on
+	}else{
+		noBacklight();		// turn backlight off
+	}
+}
+
+void LiquidCrystal_I2C::printstr(const char c[]){
+	//This function is not identical to the function used for "real" I2C displays
+	//it's here so the user sketch doesn't have to be changed 
+	print(c);
+}
+
+
+// unsupported API functions
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-parameter"
+void LiquidCrystal_I2C::off(){}
+void LiquidCrystal_I2C::on(){}
+void LiquidCrystal_I2C::setDelay (int cmdDelay,int charDelay) {}
+uint8_t LiquidCrystal_I2C::status(){return 0;}
+uint8_t LiquidCrystal_I2C::keypad (){return 0;}
+uint8_t LiquidCrystal_I2C::init_bargraph(uint8_t graphtype){return 0;}
+void LiquidCrystal_I2C::draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len,  uint8_t pixel_col_end){}
+void LiquidCrystal_I2C::draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len,  uint8_t pixel_row_end){}
+void LiquidCrystal_I2C::setContrast(uint8_t new_val){}
+#pragma GCC diagnostic pop
+	
diff --git a/libraries/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.h b/libraries/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.h
new file mode 100644
index 0000000000000000000000000000000000000000..f7c7d132c3f53fc27b0917a3df3fa22deff0a63d
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/LiquidCrystal_I2C.h
@@ -0,0 +1,131 @@
+//YWROBOT
+#ifndef LiquidCrystal_I2C_h
+#define LiquidCrystal_I2C_h
+
+#include <inttypes.h>
+#include "Print.h" 
+#include <Wire.h>
+
+// commands
+#define LCD_CLEARDISPLAY 0x01
+#define LCD_RETURNHOME 0x02
+#define LCD_ENTRYMODESET 0x04
+#define LCD_DISPLAYCONTROL 0x08
+#define LCD_CURSORSHIFT 0x10
+#define LCD_FUNCTIONSET 0x20
+#define LCD_SETCGRAMADDR 0x40
+#define LCD_SETDDRAMADDR 0x80
+
+// flags for display entry mode
+#define LCD_ENTRYRIGHT 0x00
+#define LCD_ENTRYLEFT 0x02
+#define LCD_ENTRYSHIFTINCREMENT 0x01
+#define LCD_ENTRYSHIFTDECREMENT 0x00
+
+// flags for display on/off control
+#define LCD_DISPLAYON 0x04
+#define LCD_DISPLAYOFF 0x00
+#define LCD_CURSORON 0x02
+#define LCD_CURSOROFF 0x00
+#define LCD_BLINKON 0x01
+#define LCD_BLINKOFF 0x00
+
+// flags for display/cursor shift
+#define LCD_DISPLAYMOVE 0x08
+#define LCD_CURSORMOVE 0x00
+#define LCD_MOVERIGHT 0x04
+#define LCD_MOVELEFT 0x00
+
+// flags for function set
+#define LCD_8BITMODE 0x10
+#define LCD_4BITMODE 0x00
+#define LCD_2LINE 0x08
+#define LCD_1LINE 0x00
+#define LCD_5x10DOTS 0x04
+#define LCD_5x8DOTS 0x00
+
+// flags for backlight control
+#define LCD_BACKLIGHT 0x08
+#define LCD_NOBACKLIGHT 0x00
+
+#define En B00000100  // Enable bit
+#define Rw B00000010  // Read/Write bit
+#define Rs B00000001  // Register select bit
+
+class LiquidCrystal_I2C : public Print {
+public:
+  LiquidCrystal_I2C(uint8_t lcd_Addr,uint8_t lcd_cols,uint8_t lcd_rows);
+  void begin(uint8_t cols, uint8_t rows, uint8_t charsize = LCD_5x8DOTS );
+  void clear();
+  void home();
+  void noDisplay();
+  void display();
+  void noBlink();
+  void blink();
+  void noCursor();
+  void cursor();
+  void scrollDisplayLeft();
+  void scrollDisplayRight();
+  void printLeft();
+  void printRight();
+  void leftToRight();
+  void rightToLeft();
+  void shiftIncrement();
+  void shiftDecrement();
+  void noBacklight();
+  void backlight();
+  void autoscroll();
+  void noAutoscroll(); 
+  void createChar(uint8_t, uint8_t[]);
+  void createChar(uint8_t location, const char *charmap);
+  // Example: 	const char bell[8] PROGMEM = {B00100,B01110,B01110,B01110,B11111,B00000,B00100,B00000};
+  
+  void setCursor(uint8_t, uint8_t); 
+#if defined(ARDUINO) && ARDUINO >= 100
+  virtual size_t write(uint8_t);
+#else
+  virtual void write(uint8_t);
+#endif
+  void command(uint8_t);
+  void init();
+  void oled_init();
+
+////compatibility API function aliases
+void blink_on();						// alias for blink()
+void blink_off();       					// alias for noBlink()
+void cursor_on();      	 					// alias for cursor()
+void cursor_off();      					// alias for noCursor()
+void setBacklight(uint8_t new_val);				// alias for backlight() and nobacklight()
+void load_custom_character(uint8_t char_num, uint8_t *rows);	// alias for createChar()
+void printstr(const char[]);
+
+////Unsupported API functions (not implemented in this library)
+uint8_t status();
+void setContrast(uint8_t new_val);
+uint8_t keypad();
+void setDelay(int,int);
+void on();
+void off();
+uint8_t init_bargraph(uint8_t graphtype);
+void draw_horizontal_graph(uint8_t row, uint8_t column, uint8_t len,  uint8_t pixel_col_end);
+void draw_vertical_graph(uint8_t row, uint8_t column, uint8_t len,  uint8_t pixel_col_end);
+	 
+
+private:
+  void init_priv();
+  void send(uint8_t, uint8_t);
+  void write4bits(uint8_t);
+  void expanderWrite(uint8_t);
+  void pulseEnable(uint8_t);
+  uint8_t _Addr;
+  uint8_t _displayfunction;
+  uint8_t _displaycontrol;
+  uint8_t _displaymode;
+  uint8_t _numlines;
+  bool _oled = false;
+  uint8_t _cols;
+  uint8_t _rows;
+  uint8_t _backlightval;
+};
+
+#endif
diff --git a/libraries/LiquidCrystal_I2C_ID576/README.md b/libraries/LiquidCrystal_I2C_ID576/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..a097904ce0e51345d44788a4557485cffbc1db94
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/README.md
@@ -0,0 +1,5 @@
+# LiquidCrystal_I2C
+
+LiquidCrystal Arduino library for the DFRobot I2C LCD displays
+
+**This library is no longer actively maintained, I only put it here so everyone can access it via the Arduino library manger. If you would like to take the role of the maintainer/owner of the library, please send me a message!**
diff --git a/libraries/LiquidCrystal_I2C_ID576/examples/CustomChars/CustomChars.pde b/libraries/LiquidCrystal_I2C_ID576/examples/CustomChars/CustomChars.pde
new file mode 100644
index 0000000000000000000000000000000000000000..e34100039c7f7f18733791c01f1a296977954251
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/examples/CustomChars/CustomChars.pde
@@ -0,0 +1,70 @@
+//YWROBOT
+//Compatible with the Arduino IDE 1.0
+//Library version:1.1
+#include <Wire.h>
+#include <LiquidCrystal_I2C.h>
+
+#if defined(ARDUINO) && ARDUINO >= 100
+#define printByte(args)  write(args);
+#else
+#define printByte(args)  print(args,BYTE);
+#endif
+
+uint8_t bell[8]  = {0x4,0xe,0xe,0xe,0x1f,0x0,0x4};
+uint8_t note[8]  = {0x2,0x3,0x2,0xe,0x1e,0xc,0x0};
+uint8_t clock[8] = {0x0,0xe,0x15,0x17,0x11,0xe,0x0};
+uint8_t heart[8] = {0x0,0xa,0x1f,0x1f,0xe,0x4,0x0};
+uint8_t duck[8]  = {0x0,0xc,0x1d,0xf,0xf,0x6,0x0};
+uint8_t check[8] = {0x0,0x1,0x3,0x16,0x1c,0x8,0x0};
+uint8_t cross[8] = {0x0,0x1b,0xe,0x4,0xe,0x1b,0x0};
+uint8_t retarrow[8] = {	0x1,0x1,0x5,0x9,0x1f,0x8,0x4};
+  
+LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
+
+void setup()
+{
+  lcd.init();                      // initialize the lcd 
+  lcd.backlight();
+  
+  lcd.createChar(0, bell);
+  lcd.createChar(1, note);
+  lcd.createChar(2, clock);
+  lcd.createChar(3, heart);
+  lcd.createChar(4, duck);
+  lcd.createChar(5, check);
+  lcd.createChar(6, cross);
+  lcd.createChar(7, retarrow);
+  lcd.home();
+  
+  lcd.print("Hello world...");
+  lcd.setCursor(0, 1);
+  lcd.print(" i ");
+  lcd.printByte(3);
+  lcd.print(" arduinos!");
+  delay(5000);
+  displayKeyCodes();
+  
+}
+
+// display all keycodes
+void displayKeyCodes(void) {
+  uint8_t i = 0;
+  while (1) {
+    lcd.clear();
+    lcd.print("Codes 0x"); lcd.print(i, HEX);
+    lcd.print("-0x"); lcd.print(i+15, HEX);
+    lcd.setCursor(0, 1);
+    for (int j=0; j<16; j++) {
+      lcd.printByte(i+j);
+    }
+    i+=16;
+    
+    delay(4000);
+  }
+}
+
+void loop()
+{
+
+}
+
diff --git a/libraries/LiquidCrystal_I2C_ID576/examples/HelloWorld/HelloWorld.pde b/libraries/LiquidCrystal_I2C_ID576/examples/HelloWorld/HelloWorld.pde
new file mode 100644
index 0000000000000000000000000000000000000000..234d115d22228ae1d736e976ba08de376dee339d
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/examples/HelloWorld/HelloWorld.pde
@@ -0,0 +1,26 @@
+//YWROBOT
+//Compatible with the Arduino IDE 1.0
+//Library version:1.1
+#include <LiquidCrystal_I2C.h>
+
+LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
+
+void setup()
+{
+  lcd.init();                      // initialize the lcd 
+  // Print a message to the LCD.
+  lcd.backlight();
+  lcd.setCursor(3,0);
+  lcd.print("Hello, world!");
+  lcd.setCursor(2,1);
+  lcd.print("Ywrobot Arduino!");
+   lcd.setCursor(0,2);
+  lcd.print("Arduino LCM IIC 2004");
+   lcd.setCursor(2,3);
+  lcd.print("Power By Ec-yuan!");
+}
+
+
+void loop()
+{
+}
diff --git a/libraries/LiquidCrystal_I2C_ID576/examples/SerialDisplay/SerialDisplay.pde b/libraries/LiquidCrystal_I2C_ID576/examples/SerialDisplay/SerialDisplay.pde
new file mode 100644
index 0000000000000000000000000000000000000000..1fca4e140a856254d0df6955228c6ad910e4ea8b
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/examples/SerialDisplay/SerialDisplay.pde
@@ -0,0 +1,34 @@
+/*
+ * Displays text sent over the serial port (e.g. from the Serial Monitor) on
+ * an attached LCD.
+ * YWROBOT
+ *Compatible with the Arduino IDE 1.0
+ *Library version:1.1
+ */
+#include <Wire.h> 
+#include <LiquidCrystal_I2C.h>
+
+LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
+
+void setup()
+{
+  lcd.init();                      // initialize the lcd 
+  lcd.backlight();
+  Serial.begin(9600);
+}
+
+void loop()
+{
+  // when characters arrive over the serial port...
+  if (Serial.available()) {
+    // wait a bit for the entire message to arrive
+    delay(100);
+    // clear the screen
+    lcd.clear();
+    // read all the available characters
+    while (Serial.available() > 0) {
+      // display each character to the LCD
+      lcd.write(Serial.read());
+    }
+  }
+}
diff --git a/libraries/LiquidCrystal_I2C_ID576/keywords.txt b/libraries/LiquidCrystal_I2C_ID576/keywords.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8c450a9e94cbf435ce7cbdbe7f5bc63c6a5701d0
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/keywords.txt
@@ -0,0 +1,46 @@
+###########################################
+# Syntax Coloring Map For LiquidCrystal_I2C
+###########################################
+
+###########################################
+# Datatypes (KEYWORD1)
+###########################################
+
+LiquidCrystal_I2C	KEYWORD1
+
+###########################################
+# Methods and Functions (KEYWORD2)
+###########################################
+init	KEYWORD2
+begin	KEYWORD2
+clear	KEYWORD2
+home	KEYWORD2
+noDisplay	KEYWORD2
+display	KEYWORD2
+noBlink	KEYWORD2
+blink	KEYWORD2
+noCursor	KEYWORD2
+cursor	KEYWORD2
+scrollDisplayLeft	KEYWORD2
+scrollDisplayRight	KEYWORD2
+leftToRight	KEYWORD2
+rightToLeft	KEYWORD2
+shiftIncrement	KEYWORD2
+shiftDecrement	KEYWORD2
+noBacklight	KEYWORD2
+backlight	KEYWORD2
+autoscroll	KEYWORD2
+noAutoscroll	KEYWORD2
+createChar	KEYWORD2
+setCursor	KEYWORD2
+print	KEYWORD2
+blink_on	KEYWORD2
+blink_off	KEYWORD2
+cursor_on	KEYWORD2
+cursor_off	KEYWORD2
+setBacklight	KEYWORD2
+load_custom_character	KEYWORD2
+printstr	KEYWORD2
+###########################################
+# Constants (LITERAL1)
+###########################################
diff --git a/libraries/LiquidCrystal_I2C_ID576/library.json b/libraries/LiquidCrystal_I2C_ID576/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..47109208ec5bb80baf91d76f21bae6a9ec8de0f7
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/library.json
@@ -0,0 +1,16 @@
+{
+  "name": "LiquidCrystal_I2C",
+  "keywords": "LCD, liquidcrystal, I2C",
+  "description": "A library for DFRobot I2C LCD displays",
+  "repository":
+  {
+    "type": "git",
+    "url": "https://github.com/marcoschwartz/LiquidCrystal_I2C.git"
+  },
+  "frameworks": "arduino",
+  "platforms":
+  [
+    "atmelavr",
+    "espressif8266"
+  ]
+}
diff --git a/libraries/LiquidCrystal_I2C_ID576/library.properties b/libraries/LiquidCrystal_I2C_ID576/library.properties
new file mode 100644
index 0000000000000000000000000000000000000000..976481071bc6ad6ffcbcaaaf25875ab0c48f63a5
--- /dev/null
+++ b/libraries/LiquidCrystal_I2C_ID576/library.properties
@@ -0,0 +1,9 @@
+name=LiquidCrystal_I2C
+version=1.1.4
+author=Frank de Brabander
+maintainer=Marco Schwartz <marcolivier.schwartz@gmail.com>
+sentence=A library for I2C LCD displays.
+paragraph= The library allows to control I2C displays with functions extremely similar to LiquidCrystal library. THIS LIBRARY MIGHT NOT BE COMPATIBLE WITH EXISTING SKETCHES.
+category=Display
+url=https://github.com/marcoschwartz/LiquidCrystal_I2C
+architectures=avr
diff --git a/libraries/SimpleTimer_ID419/.gitignore b/libraries/SimpleTimer_ID419/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/libraries/SimpleTimer_ID419/.library.json b/libraries/SimpleTimer_ID419/.library.json
new file mode 100644
index 0000000000000000000000000000000000000000..31befe58edaa6a63b80128777de117896de2d79d
--- /dev/null
+++ b/libraries/SimpleTimer_ID419/.library.json
@@ -0,0 +1,27 @@
+{
+    "name": "SimpleTimer", 
+    "keywords": [
+        "timer"
+    ], 
+    "description": "SimpleTimer Library for Arduino", 
+    "repository": {
+        "type": "git", 
+        "url": "https://github.com/jfturcot/SimpleTimer.git"
+    }, 
+    "frameworks": [
+        "arduino"
+    ], 
+    "platforms": [
+        "atmelavr"
+    ], 
+    "version": "b30890b8f7", 
+    "authors": [
+        {
+            "email": "jf.turcot@gmail.com", 
+            "url": "https://github.com/jfturcot", 
+            "maintainer": false, 
+            "name": "Jean-Francois Turcot"
+        }
+    ], 
+    "id": 419
+}
\ No newline at end of file
diff --git a/libraries/SimpleTimer_ID419/README b/libraries/SimpleTimer_ID419/README
new file mode 100644
index 0000000000000000000000000000000000000000..c224b02c6befa80884a0fb7b55bcdb61fcf6cfde
--- /dev/null
+++ b/libraries/SimpleTimer_ID419/README
@@ -0,0 +1,2 @@
+Visit this page for more information:
+http://playground.arduino.cc/Code/SimpleTimer
\ No newline at end of file
diff --git a/libraries/SimpleTimer_ID419/SimpleTimer.cpp b/libraries/SimpleTimer_ID419/SimpleTimer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5e8cb533b07bccfb531fb22c953e2aaa3a5b6539
--- /dev/null
+++ b/libraries/SimpleTimer_ID419/SimpleTimer.cpp
@@ -0,0 +1,241 @@
+/*
+ * SimpleTimer.cpp
+ *
+ * SimpleTimer - A timer library for Arduino.
+ * Author: mromani@ottotecnica.com
+ * Copyright (c) 2010 OTTOTECNICA Italy
+ *
+ * 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 "SimpleTimer.h"
+
+
+// Select time function:
+//static inline unsigned long elapsed() { return micros(); }
+static inline unsigned long elapsed() { return millis(); }
+
+
+SimpleTimer::SimpleTimer() {
+    unsigned long current_millis = elapsed();
+
+    for (int i = 0; i < MAX_TIMERS; i++) {
+        enabled[i] = false;
+        callbacks[i] = 0;                   // if the callback pointer is zero, the slot is free, i.e. doesn't "contain" any timer
+        prev_millis[i] = current_millis;
+        numRuns[i] = 0;
+    }
+
+    numTimers = 0;
+}
+
+
+void SimpleTimer::run() {
+    int i;
+    unsigned long current_millis;
+
+    // get current time
+    current_millis = elapsed();
+
+    for (i = 0; i < MAX_TIMERS; i++) {
+
+        toBeCalled[i] = DEFCALL_DONTRUN;
+
+        // no callback == no timer, i.e. jump over empty slots
+        if (callbacks[i]) {
+
+            // is it time to process this timer ?
+            // see http://arduino.cc/forum/index.php/topic,124048.msg932592.html#msg932592
+
+            if (current_millis - prev_millis[i] >= delays[i]) {
+
+                // update time
+                //prev_millis[i] = current_millis;
+                prev_millis[i] += delays[i];
+
+                // check if the timer callback has to be executed
+                if (enabled[i]) {
+
+                    // "run forever" timers must always be executed
+                    if (maxNumRuns[i] == RUN_FOREVER) {
+                        toBeCalled[i] = DEFCALL_RUNONLY;
+                    }
+                    // other timers get executed the specified number of times
+                    else if (numRuns[i] < maxNumRuns[i]) {
+                        toBeCalled[i] = DEFCALL_RUNONLY;
+                        numRuns[i]++;
+
+                        // after the last run, delete the timer
+                        if (numRuns[i] >= maxNumRuns[i]) {
+                            toBeCalled[i] = DEFCALL_RUNANDDEL;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    for (i = 0; i < MAX_TIMERS; i++) {
+        switch(toBeCalled[i]) {
+            case DEFCALL_DONTRUN:
+                break;
+
+            case DEFCALL_RUNONLY:
+                (*callbacks[i])();
+                break;
+
+            case DEFCALL_RUNANDDEL:
+                (*callbacks[i])();
+                deleteTimer(i);
+                break;
+        }
+    }
+}
+
+
+// find the first available slot
+// return -1 if none found
+int SimpleTimer::findFirstFreeSlot() {
+    int i;
+
+    // all slots are used
+    if (numTimers >= MAX_TIMERS) {
+        return -1;
+    }
+
+    // return the first slot with no callback (i.e. free)
+    for (i = 0; i < MAX_TIMERS; i++) {
+        if (callbacks[i] == 0) {
+            return i;
+        }
+    }
+
+    // no free slots found
+    return -1;
+}
+
+
+int SimpleTimer::setTimer(long d, timer_callback f, int n) {
+    int freeTimer;
+
+    freeTimer = findFirstFreeSlot();
+    if (freeTimer < 0) {
+        return -1;
+    }
+
+    if (f == NULL) {
+        return -1;
+    }
+
+    delays[freeTimer] = d;
+    callbacks[freeTimer] = f;
+    maxNumRuns[freeTimer] = n;
+    enabled[freeTimer] = true;
+    prev_millis[freeTimer] = elapsed();
+
+    numTimers++;
+
+    return freeTimer;
+}
+
+
+int SimpleTimer::setInterval(long d, timer_callback f) {
+    return setTimer(d, f, RUN_FOREVER);
+}
+
+
+int SimpleTimer::setTimeout(long d, timer_callback f) {
+    return setTimer(d, f, RUN_ONCE);
+}
+
+
+void SimpleTimer::deleteTimer(int timerId) {
+    if (timerId >= MAX_TIMERS) {
+        return;
+    }
+
+    // nothing to delete if no timers are in use
+    if (numTimers == 0) {
+        return;
+    }
+
+    // don't decrease the number of timers if the
+    // specified slot is already empty
+    if (callbacks[timerId] != NULL) {
+        callbacks[timerId] = 0;
+        enabled[timerId] = false;
+        toBeCalled[timerId] = DEFCALL_DONTRUN;
+        delays[timerId] = 0;
+        numRuns[timerId] = 0;
+
+        // update number of timers
+        numTimers--;
+    }
+}
+
+
+// function contributed by code@rowansimms.com
+void SimpleTimer::restartTimer(int numTimer) {
+    if (numTimer >= MAX_TIMERS) {
+        return;
+    }
+
+    prev_millis[numTimer] = elapsed();
+}
+
+
+boolean SimpleTimer::isEnabled(int numTimer) {
+    if (numTimer >= MAX_TIMERS) {
+        return false;
+    }
+
+    return enabled[numTimer];
+}
+
+
+void SimpleTimer::enable(int numTimer) {
+    if (numTimer >= MAX_TIMERS) {
+        return;
+    }
+
+    enabled[numTimer] = true;
+}
+
+
+void SimpleTimer::disable(int numTimer) {
+    if (numTimer >= MAX_TIMERS) {
+        return;
+    }
+
+    enabled[numTimer] = false;
+}
+
+
+void SimpleTimer::toggle(int numTimer) {
+    if (numTimer >= MAX_TIMERS) {
+        return;
+    }
+
+    enabled[numTimer] = !enabled[numTimer];
+}
+
+
+int SimpleTimer::getNumTimers() {
+    return numTimers;
+}
\ No newline at end of file
diff --git a/libraries/SimpleTimer_ID419/SimpleTimer.h b/libraries/SimpleTimer_ID419/SimpleTimer.h
new file mode 100644
index 0000000000000000000000000000000000000000..79f32e5eb5f4bcdfb21aee3fbfdf635c974b1498
--- /dev/null
+++ b/libraries/SimpleTimer_ID419/SimpleTimer.h
@@ -0,0 +1,124 @@
+/*
+ * SimpleTimer.h
+ *
+ * SimpleTimer - A timer library for Arduino.
+ * Author: mromani@ottotecnica.com
+ * Copyright (c) 2010 OTTOTECNICA Italy
+ *
+ * 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 SIMPLETIMER_H
+#define SIMPLETIMER_H
+
+#if defined(ARDUINO) && ARDUINO >= 100
+#include <Arduino.h>
+#else
+#include <WProgram.h>
+#endif
+
+typedef void (*timer_callback)(void);
+
+class SimpleTimer {
+
+public:
+    // maximum number of timers
+    const static int MAX_TIMERS = 10;
+
+    // setTimer() constants
+    const static int RUN_FOREVER = 0;
+    const static int RUN_ONCE = 1;
+
+    // constructor
+    SimpleTimer();
+
+    // this function must be called inside loop()
+    void run();
+
+    // call function f every d milliseconds
+    int setInterval(long d, timer_callback f);
+
+    // call function f once after d milliseconds
+    int setTimeout(long d, timer_callback f);
+
+    // call function f every d milliseconds for n times
+    int setTimer(long d, timer_callback f, int n);
+
+    // destroy the specified timer
+    void deleteTimer(int numTimer);
+
+    // restart the specified timer
+    void restartTimer(int numTimer);
+
+    // returns true if the specified timer is enabled
+    boolean isEnabled(int numTimer);
+
+    // enables the specified timer
+    void enable(int numTimer);
+
+    // disables the specified timer
+    void disable(int numTimer);
+
+    // enables the specified timer if it's currently disabled,
+    // and vice-versa
+    void toggle(int numTimer);
+
+    // returns the number of used timers
+    int getNumTimers();
+
+    // returns the number of available timers
+    int getNumAvailableTimers() { return MAX_TIMERS - numTimers; };
+
+private:
+    // deferred call constants
+    const static int DEFCALL_DONTRUN = 0;       // don't call the callback function
+    const static int DEFCALL_RUNONLY = 1;       // call the callback function but don't delete the timer
+    const static int DEFCALL_RUNANDDEL = 2;      // call the callback function and delete the timer
+
+    // find the first available slot
+    int findFirstFreeSlot();
+
+    // value returned by the millis() function
+    // in the previous run() call
+    unsigned long prev_millis[MAX_TIMERS];
+
+    // pointers to the callback functions
+    timer_callback callbacks[MAX_TIMERS];
+
+    // delay values
+    long delays[MAX_TIMERS];
+
+    // number of runs to be executed for each timer
+    int maxNumRuns[MAX_TIMERS];
+
+    // number of executed runs for each timer
+    int numRuns[MAX_TIMERS];
+
+    // which timers are enabled
+    boolean enabled[MAX_TIMERS];
+
+    // deferred function call (sort of) - N.B.: this array is only used in run()
+    int toBeCalled[MAX_TIMERS];
+
+    // actual number of timers in use
+    int numTimers;
+};
+
+#endif
\ No newline at end of file
diff --git a/libraries/SimpleTimer_ID419/library.json b/libraries/SimpleTimer_ID419/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..ca1195605fd3dcd726746573aa6301f40b31bfa3
--- /dev/null
+++ b/libraries/SimpleTimer_ID419/library.json
@@ -0,0 +1,12 @@
+{
+  "name": "SimpleTimer",
+  "keywords": "timer",
+  "description": "SimpleTimer Library for Arduino",
+  "repository":
+  {
+     "type": "git",
+     "url": "https://github.com/jfturcot/SimpleTimer.git"
+  },
+  "frameworks": "arduino",
+  "platforms": "atmelavr"
+ }
diff --git a/libraries/WebSockets_ID549/.gitignore b/libraries/WebSockets_ID549/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..44b2c85f8e0b0fb21a878023f464238af92bf457
--- /dev/null
+++ b/libraries/WebSockets_ID549/.gitignore
@@ -0,0 +1,29 @@
+# Compiled Object files
+*.slo
+*.lo
+*.o
+*.obj
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+*.dll
+
+# Fortran module files
+*.mod
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+*.lib
+
+# Executables
+*.exe
+*.out
+*.app
+/tests/webSocketServer/node_modules
diff --git a/libraries/WebSockets_ID549/.library.json b/libraries/WebSockets_ID549/.library.json
new file mode 100644
index 0000000000000000000000000000000000000000..0279d6f1dd35c914ce8a32636b63e3172797d712
--- /dev/null
+++ b/libraries/WebSockets_ID549/.library.json
@@ -0,0 +1,40 @@
+{
+    "name": "WebSockets", 
+    "description": "WebSocket Server and Client for Arduino based on RFC6455", 
+    "keywords": [
+        "wifi", 
+        "http", 
+        "web", 
+        "server", 
+        "client", 
+        "websocket"
+    ], 
+    "authors": [
+        {
+            "email": null, 
+            "url": "https://github.com/Links2004", 
+            "maintainer": true, 
+            "name": "Markus Sattler"
+        }
+    ], 
+    "repository": {
+        "type": "git", 
+        "url": "https://github.com/Links2004/arduinoWebSockets.git"
+    }, 
+    "version": "2.1.3", 
+    "license": "LGPL-2.1", 
+    "export": {
+        "exclude": [
+            "tests"
+        ]
+    }, 
+    "frameworks": [
+        "arduino"
+    ], 
+    "platforms": [
+        "atmelavr", 
+        "espressif8266", 
+        "espressif32"
+    ], 
+    "id": 549
+}
\ No newline at end of file
diff --git a/libraries/WebSockets_ID549/.travis.yml b/libraries/WebSockets_ID549/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..3493a8c0c5cb5985bd29881363862ed0eab762e2
--- /dev/null
+++ b/libraries/WebSockets_ID549/.travis.yml
@@ -0,0 +1,42 @@
+sudo: false
+language: bash
+os:
+  - linux
+env:
+  matrix:
+    - CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80" IDE_VERSION=1.6.5
+    - CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,eesz=1M,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.5
+    - CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,eesz=1M,FlashMode=qio,FlashFreq=80" IDE_VERSION=1.8.7
+    - CPU="esp8266" BOARD="esp8266com:esp8266:generic:xtal=80,dbg=Serial1" IDE_VERSION=1.6.5
+    - CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.6.5
+    - CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.5
+    - CPU="esp32" BOARD="espressif:esp32:esp32:FlashFreq=80" IDE_VERSION=1.8.7
+
+script:
+  - /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
+  - sleep 3
+  - export DISPLAY=:1.0
+  - wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz
+  - tar xf arduino-$IDE_VERSION-linux64.tar.xz
+  - mv arduino-$IDE_VERSION $HOME/arduino_ide
+  - export PATH="$HOME/arduino_ide:$PATH"
+  - which arduino
+  - mkdir -p $HOME/Arduino/libraries
+  - cp -r $TRAVIS_BUILD_DIR $HOME/Arduino/libraries/arduinoWebSockets
+  - source $TRAVIS_BUILD_DIR/travis/common.sh
+  - get_core $CPU
+  - cd $TRAVIS_BUILD_DIR
+  - arduino --board $BOARD --save-prefs
+  - arduino --get-pref sketchbook.path
+  - build_sketches arduino $HOME/Arduino/libraries/arduinoWebSockets/examples/$CPU $CPU
+
+notifications:
+  email:
+    on_success: change
+    on_failure: change
+  webhooks:
+    urls:
+      - https://webhooks.gitter.im/e/1aa78fbe15080b0c2e37
+    on_success: change  # options: [always|never|change] default: always
+    on_failure: always  # options: [always|never|change] default: always
+    on_start: false     # default: false
diff --git a/libraries/WebSockets_ID549/LICENSE b/libraries/WebSockets_ID549/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..f166cc57b2783565bc48e8999103c572fca4c0e4
--- /dev/null
+++ b/libraries/WebSockets_ID549/LICENSE
@@ -0,0 +1,502 @@
+                  GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+ 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+                  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+                            NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
\ No newline at end of file
diff --git a/libraries/WebSockets_ID549/README.md b/libraries/WebSockets_ID549/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..81b410723de0452cd75c6dbef8b1b1446e1a1e45
--- /dev/null
+++ b/libraries/WebSockets_ID549/README.md
@@ -0,0 +1,98 @@
+WebSocket Server and Client for Arduino [![Build Status](https://travis-ci.org/Links2004/arduinoWebSockets.svg?branch=master)](https://travis-ci.org/Links2004/arduinoWebSockets)
+===========================================
+
+a WebSocket Server and Client for Arduino based on RFC6455.
+
+
+##### Supported features of RFC6455 #####
+ - text frame
+ - binary frame
+ - connection close
+ - ping
+ - pong
+ - continuation frame
+
+##### Limitations #####
+ - max input length is limited to the ram size and the ```WEBSOCKETS_MAX_DATA_SIZE``` define
+ - max output length has no limit (the hardware is the limit)
+ - Client send big frames with mask 0x00000000 (on AVR all frames)
+ - continuation frame reassembly need to be handled in the application code
+
+ ##### Limitations for Async #####
+ - Functions called from within the context of the websocket event might not honor `yield()` and/or `delay()`.  See [this issue](https://github.com/Links2004/arduinoWebSockets/issues/58#issuecomment-192376395) for more info and a potential workaround.
+ - wss / SSL is not possible.
+
+##### Supported Hardware #####
+ - ESP8266 [Arduino for ESP8266](https://github.com/esp8266/Arduino/)
+ - ESP32 [Arduino for ESP32](https://github.com/espressif/arduino-esp32)
+ - ESP31B
+ - Particle with STM32 ARM Cortex M3
+ - ATmega328 with Ethernet Shield (ATmega branch)
+ - ATmega328 with enc28j60 (ATmega branch)
+ - ATmega2560 with Ethernet Shield (ATmega branch)
+ - ATmega2560 with enc28j60 (ATmega branch)
+
+###### Note: ######
+
+  version 2.0 and up is not compatible with AVR/ATmega, check ATmega branch.
+
+  Arduino for AVR not supports std namespace of c++.
+
+### wss / SSL ###
+ supported for:
+ - wss client on the ESP8266
+ - wss / SSL is not natively supported in WebSocketsServer however it is possible to achieve secure websockets
+   by running the device behind an SSL proxy. See [Nginx](examples/Nginx/esp8266.ssl.reverse.proxy.conf) for a
+   sample Nginx server configuration file to enable this.
+
+### ESP Async TCP ###
+
+This libary can run in Async TCP mode on the ESP.
+
+The mode can be activated in the ```WebSockets.h``` (see WEBSOCKETS_NETWORK_TYPE define).
+
+[ESPAsyncTCP](https://github.com/me-no-dev/ESPAsyncTCP) libary is required.
+
+
+### High Level Client API ###
+
+ - `begin` : Initiate connection sequence to the websocket host.
+```
+void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
+void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
+ ```
+ - `onEvent`: Callback to handle for websocket events
+
+ ```
+ void onEvent(WebSocketClientEvent cbEvent);
+ ```
+
+ - `WebSocketClientEvent`: Handler for websocket events
+ ```
+ void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length)
+ ```
+Where `WStype_t type` is defined as:
+  ```
+  typedef enum {
+      WStype_ERROR,
+      WStype_DISCONNECTED,
+      WStype_CONNECTED,
+      WStype_TEXT,
+      WStype_BIN,
+  	WStype_FRAGMENT_TEXT_START,
+  	WStype_FRAGMENT_BIN_START,
+  	WStype_FRAGMENT,
+  	WStype_FRAGMENT_FIN,
+  } WStype_t;
+  ```
+
+### Issues ###
+Submit issues to: https://github.com/Links2004/arduinoWebSockets/issues
+
+[![Join the chat at https://gitter.im/Links2004/arduinoWebSockets](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/Links2004/arduinoWebSockets?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
+
+### License and credits ###
+
+The library is licensed under [LGPLv2.1](https://github.com/Links2004/arduinoWebSockets/blob/master/LICENSE)
+
+[libb64](http://libb64.sourceforge.net/) written by Chris Venter. It is distributed under Public Domain see [LICENSE](https://github.com/Links2004/arduinoWebSockets/blob/master/src/libb64/LICENSE).
diff --git a/libraries/WebSockets_ID549/examples/Nginx/esp8266.ssl.reverse.proxy.conf b/libraries/WebSockets_ID549/examples/Nginx/esp8266.ssl.reverse.proxy.conf
new file mode 100644
index 0000000000000000000000000000000000000000..ec5aa89fa5a08d951baab74f63782bc223b806ab
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/Nginx/esp8266.ssl.reverse.proxy.conf
@@ -0,0 +1,83 @@
+# ESP8266 nginx SSL reverse proxy configuration file (tested and working on nginx v1.10.0)
+
+# proxy cache location
+proxy_cache_path /opt/etc/nginx/cache levels=1:2 keys_zone=ESP8266_cache:10m max_size=10g inactive=5m use_temp_path=off;
+
+# webserver proxy
+server {
+
+    # general server parameters
+    listen                      50080;
+    server_name                 myDomain.net;
+    access_log                  /opt/var/log/nginx/myDomain.net.access.log;       
+
+    # SSL configuration
+    ssl                         on;
+    ssl_certificate             /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem;
+    ssl_certificate_key         /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem;
+    ssl_session_cache           builtin:1000  shared:SSL:10m;
+    ssl_protocols               TLSv1 TLSv1.1 TLSv1.2;
+    ssl_ciphers                 HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
+    ssl_prefer_server_ciphers   on;
+    
+    location / {
+
+      # proxy caching configuration
+      proxy_cache             ESP8266_cache;
+      proxy_cache_revalidate  on;
+      proxy_cache_min_uses    1;
+      proxy_cache_use_stale   off;
+      proxy_cache_lock        on;
+      # proxy_cache_bypass      $http_cache_control;      
+      # include the sessionId cookie value as part of the cache key - keeps the cache per user
+      # proxy_cache_key         $proxy_host$request_uri$cookie_sessionId;
+
+      # header pass through configuration
+      proxy_set_header        Host $host;      
+      proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
+      proxy_set_header        X-Forwarded-Proto $scheme;      
+
+      # ESP8266 custom headers which identify to the device that it's running through an SSL proxy     
+      proxy_set_header        X-SSL On;
+      proxy_set_header        X-SSL-WebserverPort 50080;
+      proxy_set_header        X-SSL-WebsocketPort 50081;
+
+      # extra debug headers      
+      add_header              X-Proxy-Cache $upstream_cache_status;
+      add_header              X-Forwarded-For $proxy_add_x_forwarded_for;
+
+      # actual proxying configuration
+      proxy_ssl_session_reuse on;
+      # target the IP address of the device with proxy_pass
+      proxy_pass              http://192.168.0.20;
+      proxy_read_timeout      90;
+    }
+ }
+
+# websocket proxy
+server {
+
+    # general server parameters
+    listen                      50081;
+    server_name                 myDomain.net;
+    access_log                  /opt/var/log/nginx/myDomain.net.wss.access.log;
+
+    # SSL configuration
+    ssl                         on;
+    ssl_certificate             /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/fullchain.pem;
+    ssl_certificate_key         /usr/builtin/etc/certificate/lets-encrypt/myDomain.net/privkey.pem;
+    ssl_session_cache           builtin:1000  shared:SSL:10m;
+    ssl_protocols               TLSv1 TLSv1.1 TLSv1.2;
+    ssl_ciphers                 HIGH:!aNULL:!eNULL:!EXPORT:!CAMELLIA:!DES:!MD5:!PSK:!RC4;
+    ssl_prefer_server_ciphers   on;
+    
+    location / {     
+
+      # websocket upgrade tunnel configuration
+      proxy_pass                    http://192.168.0.20:81;
+      proxy_http_version            1.1;
+      proxy_set_header Upgrade      $http_upgrade;
+      proxy_set_header Connection   "Upgrade";
+      proxy_read_timeout            86400;
+    }
+ }
diff --git a/libraries/WebSockets_ID549/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino b/libraries/WebSockets_ID549/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino
new file mode 100644
index 0000000000000000000000000000000000000000..943d89310cb0648de063a1e1eb1f314bd475f755
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino
@@ -0,0 +1,84 @@
+/*
+ * WebSocketClientAVR.ino
+ *
+ *  Created on: 10.12.2015
+ *
+ */
+
+#include <Arduino.h>
+
+#include <SPI.h>
+#include <Ethernet.h>
+
+#include <WebSocketsClient.h>
+
+
+
+// Enter a MAC address for your controller below.
+// Newer Ethernet shields have a MAC address printed on a sticker on the shield
+byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
+
+// Set the static IP address to use if the DHCP fails to assign
+IPAddress ip(192, 168, 0, 177);
+
+WebSocketsClient webSocket;
+
+
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+
+    switch(type) {
+        case WStype_DISCONNECTED:
+            Serial.println("[WSc] Disconnected!\n");
+            break;
+        case WStype_CONNECTED:
+            {
+                Serial.print("[WSc] Connected to url: ");
+                Serial.println((char *)payload);
+                // send message to server when Connected
+                webSocket.sendTXT("Connected");
+            }
+            break;
+        case WStype_TEXT:
+            Serial.print("[WSc] get text: ");
+            Serial.println((char *)payload);
+            // send message to server
+            // webSocket.sendTXT("message here");
+            break;
+        case WStype_BIN:
+            Serial.print("[WSc] get binary length: ");
+            Serial.println(length);
+           // hexdump(payload, length);
+
+            // send data to server
+            // webSocket.sendBIN(payload, length);
+            break;
+    }
+
+}
+
+void setup()
+{
+    // Open serial communications and wait for port to open:
+    Serial.begin(115200);
+    while (!Serial) {}
+
+    // start the Ethernet connection:
+    if (Ethernet.begin(mac) == 0) {
+      Serial.println("Failed to configure Ethernet using DHCP");
+      // no point in carrying on, so do nothing forevermore:
+      // try to congifure using IP address instead of DHCP:
+      Ethernet.begin(mac, ip);
+    }
+
+    webSocket.begin("192.168.0.123", 8011);
+    webSocket.onEvent(webSocketEvent);
+
+}
+
+
+void loop()
+{
+    webSocket.loop();
+}
diff --git a/libraries/WebSockets_ID549/examples/esp32/WebSocketClient/WebSocketClient.ino b/libraries/WebSockets_ID549/examples/esp32/WebSocketClient/WebSocketClient.ino
new file mode 100644
index 0000000000000000000000000000000000000000..00c70e13b08423b3ebfc186c8004fdd1702cad57
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp32/WebSocketClient/WebSocketClient.ino
@@ -0,0 +1,110 @@
+/*
+ * WebSocketClient.ino
+ *
+ *  Created on: 24.05.2015
+ *
+ */
+
+#include <Arduino.h>
+
+#include <WiFi.h>
+#include <WiFiMulti.h>
+#include <WiFiClientSecure.h>
+
+#include <WebSocketsClient.h>
+
+
+WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+#define USE_SERIAL Serial1
+
+void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
+	const uint8_t* src = (const uint8_t*) mem;
+	USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
+	for(uint32_t i = 0; i < len; i++) {
+		if(i % cols == 0) {
+			USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
+		}
+		USE_SERIAL.printf("%02X ", *src);
+		src++;
+	}
+	USE_SERIAL.printf("\n");
+}
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+	switch(type) {
+		case WStype_DISCONNECTED:
+			USE_SERIAL.printf("[WSc] Disconnected!\n");
+			break;
+		case WStype_CONNECTED:
+			USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
+
+			// send message to server when Connected
+			webSocket.sendTXT("Connected");
+			break;
+		case WStype_TEXT:
+			USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+			// send message to server
+			// webSocket.sendTXT("message here");
+			break;
+		case WStype_BIN:
+			USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+			hexdump(payload, length);
+
+			// send data to server
+			// webSocket.sendBIN(payload, length);
+			break;
+		case WStype_ERROR:			
+		case WStype_FRAGMENT_TEXT_START:
+		case WStype_FRAGMENT_BIN_START:
+		case WStype_FRAGMENT:
+		case WStype_FRAGMENT_FIN:
+			break;
+	}
+
+}
+
+void setup() {
+	// USE_SERIAL.begin(921600);
+	USE_SERIAL.begin(115200);
+
+	//Serial.setDebugOutput(true);
+	USE_SERIAL.setDebugOutput(true);
+
+	USE_SERIAL.println();
+	USE_SERIAL.println();
+	USE_SERIAL.println();
+
+	for(uint8_t t = 4; t > 0; t--) {
+		USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+		USE_SERIAL.flush();
+		delay(1000);
+	}
+
+	WiFiMulti.addAP("SSID", "passpasspass");
+
+	//WiFi.disconnect();
+	while(WiFiMulti.run() != WL_CONNECTED) {
+		delay(100);
+	}
+
+	// server address, port and URL
+	webSocket.begin("192.168.0.123", 81, "/");
+
+	// event handler
+	webSocket.onEvent(webSocketEvent);
+
+	// use HTTP Basic Authorization this is optional remove if not needed
+	webSocket.setAuthorization("user", "Password");
+
+	// try ever 5000 again if connection has failed
+	webSocket.setReconnectInterval(5000);
+
+}
+
+void loop() {
+	webSocket.loop();
+}
diff --git a/libraries/WebSockets_ID549/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino b/libraries/WebSockets_ID549/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino
new file mode 100644
index 0000000000000000000000000000000000000000..7d59f63efb37803ce82cd9ff4edcee77a8ddbb1b
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino
@@ -0,0 +1,106 @@
+/*
+ * WebSocketClientSSL.ino
+ *
+ *  Created on: 10.12.2015
+ *
+ *  note SSL is only possible with the ESP8266
+ *
+ */
+
+#include <Arduino.h>
+
+#include <WiFi.h>
+#include <WiFiMulti.h>
+#include <WiFiClientSecure.h>
+
+#include <WebSocketsClient.h>
+
+
+WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+#define USE_SERIAL Serial1
+
+void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
+	const uint8_t* src = (const uint8_t*) mem;
+	USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
+	for(uint32_t i = 0; i < len; i++) {
+		if(i % cols == 0) {
+			USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
+		}
+		USE_SERIAL.printf("%02X ", *src);
+		src++;
+	}
+	USE_SERIAL.printf("\n");
+}
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+
+    switch(type) {
+        case WStype_DISCONNECTED:
+            USE_SERIAL.printf("[WSc] Disconnected!\n");
+            break;
+        case WStype_CONNECTED:
+            {
+                USE_SERIAL.printf("[WSc] Connected to url: %s\n",  payload);
+
+			    // send message to server when Connected
+				webSocket.sendTXT("Connected");
+            }
+            break;
+        case WStype_TEXT:
+            USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+			// send message to server
+			// webSocket.sendTXT("message here");
+            break;
+        case WStype_BIN:
+            USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+            hexdump(payload, length);
+
+            // send data to server
+            // webSocket.sendBIN(payload, length);
+            break;
+		case WStype_ERROR:			
+		case WStype_FRAGMENT_TEXT_START:
+		case WStype_FRAGMENT_BIN_START:
+		case WStype_FRAGMENT:
+		case WStype_FRAGMENT_FIN:
+			break;
+    }
+
+}
+
+void setup() {
+    // USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //Serial.setDebugOutput(true);
+    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+      for(uint8_t t = 4; t > 0; t--) {
+          USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+          USE_SERIAL.flush();
+          delay(1000);
+      }
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    //WiFi.disconnect();
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    webSocket.beginSSL("192.168.0.123", 81);
+    webSocket.onEvent(webSocketEvent);
+
+}
+
+void loop() {
+    webSocket.loop();
+}
diff --git a/libraries/WebSockets_ID549/examples/esp32/WebSocketServer/WebSocketServer.ino b/libraries/WebSockets_ID549/examples/esp32/WebSocketServer/WebSocketServer.ino
new file mode 100644
index 0000000000000000000000000000000000000000..87866edd93ef71783406bfa902c2640b25569349
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp32/WebSocketServer/WebSocketServer.ino
@@ -0,0 +1,104 @@
+/*
+ * WebSocketServer.ino
+ *
+ *  Created on: 22.05.2015
+ *
+ */
+
+#include <Arduino.h>
+
+#include <WiFi.h>
+#include <WiFiMulti.h>
+#include <WiFiClientSecure.h>
+
+#include <WebSocketsServer.h>
+
+WiFiMulti WiFiMulti;
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+#define USE_SERIAL Serial1
+
+void hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {
+	const uint8_t* src = (const uint8_t*) mem;
+	USE_SERIAL.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
+	for(uint32_t i = 0; i < len; i++) {
+		if(i % cols == 0) {
+			USE_SERIAL.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
+		}
+		USE_SERIAL.printf("%02X ", *src);
+		src++;
+	}
+	USE_SERIAL.printf("\n");
+}
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+    switch(type) {
+        case WStype_DISCONNECTED:
+            USE_SERIAL.printf("[%u] Disconnected!\n", num);
+            break;
+        case WStype_CONNECTED:
+            {
+                IPAddress ip = webSocket.remoteIP(num);
+                USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+
+				// send message to client
+				webSocket.sendTXT(num, "Connected");
+            }
+            break;
+        case WStype_TEXT:
+            USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+
+            // send message to client
+            // webSocket.sendTXT(num, "message here");
+
+            // send data to all connected clients
+            // webSocket.broadcastTXT("message here");
+            break;
+        case WStype_BIN:
+            USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
+            hexdump(payload, length);
+
+            // send message to client
+            // webSocket.sendBIN(num, payload, length);
+            break;
+		case WStype_ERROR:			
+		case WStype_FRAGMENT_TEXT_START:
+		case WStype_FRAGMENT_BIN_START:
+		case WStype_FRAGMENT:
+		case WStype_FRAGMENT_FIN:
+			break;
+    }
+
+}
+
+void setup() {
+    // USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //Serial.setDebugOutput(true);
+    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+    for(uint8_t t = 4; t > 0; t--) {
+        USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+        USE_SERIAL.flush();
+        delay(1000);
+    }
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    webSocket.begin();
+    webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+    webSocket.loop();
+}
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketClient/WebSocketClient.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClient/WebSocketClient.ino
new file mode 100644
index 0000000000000000000000000000000000000000..86fc2413df9b11cb937063b909e679adc867a2a6
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClient/WebSocketClient.ino
@@ -0,0 +1,98 @@
+/*
+ * WebSocketClient.ino
+ *
+ *  Created on: 24.05.2015
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+
+#include <WebSocketsClient.h>
+
+#include <Hash.h>
+
+ESP8266WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+#define USE_SERIAL Serial1
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+	switch(type) {
+		case WStype_DISCONNECTED:
+			USE_SERIAL.printf("[WSc] Disconnected!\n");
+			break;
+		case WStype_CONNECTED: {
+			USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
+
+			// send message to server when Connected
+			webSocket.sendTXT("Connected");
+		}
+			break;
+		case WStype_TEXT:
+			USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+			// send message to server
+			// webSocket.sendTXT("message here");
+			break;
+		case WStype_BIN:
+			USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+			hexdump(payload, length);
+
+			// send data to server
+			// webSocket.sendBIN(payload, length);
+			break;
+	}
+
+}
+
+void setup() {
+	// USE_SERIAL.begin(921600);
+	USE_SERIAL.begin(115200);
+
+	//Serial.setDebugOutput(true);
+	USE_SERIAL.setDebugOutput(true);
+
+	USE_SERIAL.println();
+	USE_SERIAL.println();
+	USE_SERIAL.println();
+
+	for(uint8_t t = 4; t > 0; t--) {
+		USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+		USE_SERIAL.flush();
+		delay(1000);
+	}
+
+	WiFiMulti.addAP("SSID", "passpasspass");
+
+	//WiFi.disconnect();
+	while(WiFiMulti.run() != WL_CONNECTED) {
+		delay(100);
+	}
+
+	// server address, port and URL
+	webSocket.begin("192.168.0.123", 81, "/");
+
+	// event handler
+	webSocket.onEvent(webSocketEvent);
+
+	// use HTTP Basic Authorization this is optional remove if not needed
+	webSocket.setAuthorization("user", "Password");
+
+	// try ever 5000 again if connection has failed
+	webSocket.setReconnectInterval(5000);
+  
+  // start heartbeat (optional)
+  // ping server every 15000 ms
+  // expect pong from server within 3000 ms
+  // consider connection disconnected if pong is not received 2 times
+  webSocket.enableHeartbeat(15000, 3000, 2);
+
+}
+
+void loop() {
+	webSocket.loop();
+}
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino
new file mode 100644
index 0000000000000000000000000000000000000000..62e860b605c29844a91c7fac833f23a4e8dcceb8
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino
@@ -0,0 +1,88 @@
+/*
+ * WebSocketClientSSL.ino
+ *
+ *  Created on: 10.12.2015
+ *
+ *  note SSL is only possible with the ESP8266
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+
+#include <WebSocketsClient.h>
+
+#include <Hash.h>
+
+ESP8266WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+
+#define USE_SERIAL Serial1
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+
+    switch(type) {
+        case WStype_DISCONNECTED:
+            USE_SERIAL.printf("[WSc] Disconnected!\n");
+            break;
+        case WStype_CONNECTED:
+            {
+                USE_SERIAL.printf("[WSc] Connected to url: %s\n",  payload);
+				
+			    // send message to server when Connected
+				webSocket.sendTXT("Connected");
+            }
+            break;
+        case WStype_TEXT:
+            USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+			// send message to server
+			// webSocket.sendTXT("message here");
+            break;
+        case WStype_BIN:
+            USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+            hexdump(payload, length);
+
+            // send data to server
+            // webSocket.sendBIN(payload, length);
+            break;
+    }
+
+}
+
+void setup() {
+    // USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //Serial.setDebugOutput(true);
+    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+      for(uint8_t t = 4; t > 0; t--) {
+          USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+          USE_SERIAL.flush();
+          delay(1000);
+      }
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    //WiFi.disconnect();
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    webSocket.beginSSL("192.168.0.123", 81);
+    webSocket.onEvent(webSocketEvent);
+
+}
+
+void loop() {
+    webSocket.loop();
+}
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino
new file mode 100644
index 0000000000000000000000000000000000000000..39dce87a6904559dd0f9aecd33a13d4911f47809
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino
@@ -0,0 +1,113 @@
+/*
+ * WebSocketClientSocketIO.ino
+ *
+ *  Created on: 06.06.2016
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+
+#include <WebSocketsClient.h>
+
+#include <Hash.h>
+
+ESP8266WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+
+#define USE_SERIAL Serial1
+
+#define MESSAGE_INTERVAL 30000
+#define HEARTBEAT_INTERVAL 25000
+
+uint64_t messageTimestamp = 0;
+uint64_t heartbeatTimestamp = 0;
+bool isConnected = false;
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+
+    switch(type) {
+        case WStype_DISCONNECTED:
+            USE_SERIAL.printf("[WSc] Disconnected!\n");
+            isConnected = false;
+            break;
+        case WStype_CONNECTED:
+            {
+                USE_SERIAL.printf("[WSc] Connected to url: %s\n",  payload);
+                isConnected = true;
+
+			    // send message to server when Connected
+                // socket.io upgrade confirmation message (required)
+				webSocket.sendTXT("5");
+            }
+            break;
+        case WStype_TEXT:
+            USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+			// send message to server
+			// webSocket.sendTXT("message here");
+            break;
+        case WStype_BIN:
+            USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+            hexdump(payload, length);
+
+            // send data to server
+            // webSocket.sendBIN(payload, length);
+            break;
+    }
+
+}
+
+void setup() {
+    // USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //Serial.setDebugOutput(true);
+    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+      for(uint8_t t = 4; t > 0; t--) {
+          USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+          USE_SERIAL.flush();
+          delay(1000);
+      }
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    //WiFi.disconnect();
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    webSocket.beginSocketIO("192.168.0.123", 81);
+    //webSocket.setAuthorization("user", "Password"); // HTTP Basic Authorization
+    webSocket.onEvent(webSocketEvent);
+
+}
+
+void loop() {
+    webSocket.loop();
+
+    if(isConnected) {
+
+        uint64_t now = millis();
+
+        if(now - messageTimestamp > MESSAGE_INTERVAL) {
+            messageTimestamp = now;
+            // example socket.io message with type "messageType" and JSON payload
+            webSocket.sendTXT("42[\"messageType\",{\"greeting\":\"hello\"}]");
+        }
+        if((now - heartbeatTimestamp) > HEARTBEAT_INTERVAL) {
+            heartbeatTimestamp = now;
+            // socket.io heartbeat message
+            webSocket.sendTXT("2");
+        }
+    }
+}
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino
new file mode 100644
index 0000000000000000000000000000000000000000..a0eb011fdf6ff57f975c9553f05c7f70370f950f
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino
@@ -0,0 +1,149 @@
+/*
+    WebSocketClientStomp.ino
+
+    Example for connecting and maintining a connection with a STOMP websocket connection.
+    In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html).
+
+    Created on: 25.09.2017
+    Author: Martin Becker <mgbckr>, Contact: becker@informatik.uni-wuerzburg.de
+*/
+
+// PRE
+
+#define USE_SERIAL Serial
+
+
+// LIBRARIES
+
+#include <Arduino.h>
+#include <Hash.h>
+
+#include <ESP8266WiFi.h>
+#include <WebSocketsClient.h>
+
+
+// SETTINGS
+
+const char* wlan_ssid             = "yourssid";
+const char* wlan_password         = "somepassword";
+
+const char* ws_host               = "the.host.net";
+const int   ws_port               = 80;
+
+// URL for STOMP endpoint.
+// For the default config of Spring's STOMP support, the default URL is "/socketentry/websocket".
+const char* stompUrl            = "/socketentry/websocket"; // don't forget the leading "/" !!!
+
+
+// VARIABLES
+
+WebSocketsClient webSocket;
+
+
+// FUNCTIONS
+
+/**
+ * STOMP messages need to be NULL-terminated (i.e., \0 or \u0000).
+ * However, when we send a String or a char[] array without specifying 
+ * a length, the size of the message payload is derived by strlen() internally,
+ * thus dropping any NULL values appended to the "msg"-String.
+ * 
+ * To solve this, we first convert the String to a NULL terminated char[] array
+ * via "c_str" and set the length of the payload to include the NULL value.
+ */
+void sendMessage(String & msg) {
+    webSocket.sendTXT(msg.c_str(), msg.length() + 1);
+}
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+    switch (type) {
+        case WStype_DISCONNECTED:
+            USE_SERIAL.printf("[WSc] Disconnected!\n");
+            break;
+        case WStype_CONNECTED:
+            {
+                USE_SERIAL.printf("[WSc] Connected to url: %s\n",  payload);
+                
+                String msg = "CONNECT\r\naccept-version:1.1,1.0\r\nheart-beat:10000,10000\r\n\r\n";
+                sendMessage(msg);
+            }
+            break;
+        case WStype_TEXT:
+            {
+                // #####################
+                // handle STOMP protocol
+                // #####################
+
+                String text = (char*) payload;
+                USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+                if (text.startsWith("CONNECTED")) {
+
+                    // subscribe to some channels
+
+                    String msg = "SUBSCRIBE\nid:sub-0\ndestination:/user/queue/messages\n\n";
+                    sendMessage(msg);
+                    delay(1000);
+
+                    // and send a message
+
+                    msg = "SEND\ndestination:/app/message\n\n{\"user\":\"esp\",\"message\":\"Hello!\"}";
+                    sendMessage(msg);
+                    delay(1000);
+                    
+                } else {
+
+                    // do something with messages
+                    
+                }
+
+                break;
+            }
+        case WStype_BIN:
+            USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+            hexdump(payload, length);
+
+            // send data to server
+            // webSocket.sendBIN(payload, length);
+            break;
+    }
+
+}
+
+void setup() {
+
+    // setup serial
+
+    // USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+
+
+    // connect to WiFi
+
+    USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ...");
+    WiFi.mode(WIFI_STA);
+    WiFi.begin(wlan_ssid, wlan_password);
+
+    while (WiFi.status() != WL_CONNECTED) {
+        delay(500);
+        USE_SERIAL.print(".");
+    }
+    USE_SERIAL.println(" success.");
+    USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP());
+
+
+    // connect to websocket
+    webSocket.begin(ws_host, ws_port, stompUrl);
+    webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
+    //    webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
+    webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+    webSocket.loop();
+}
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino
new file mode 100644
index 0000000000000000000000000000000000000000..cb0c45be3ec9320dd5669be89052bd0b3f2a9273
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino
@@ -0,0 +1,150 @@
+/*
+    WebSocketClientStompOverSockJs.ino
+
+    Example for connecting and maintining a connection with a SockJS+STOMP websocket connection.
+    In this example, we connect to a Spring application (see https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html).
+
+    Created on: 18.07.2017
+    Author: Martin Becker <mgbckr>, Contact: becker@informatik.uni-wuerzburg.de
+*/
+
+// PRE
+
+#define USE_SERIAL Serial
+
+
+// LIBRARIES
+
+#include <Arduino.h>
+#include <Hash.h>
+
+#include <ESP8266WiFi.h>
+#include <WebSocketsClient.h>
+
+
+// SETTINGS
+
+const char* wlan_ssid             = "yourssid";
+const char* wlan_password         = "somepassword";
+
+const char* ws_host               = "the.host.net";
+const int   ws_port               = 80;
+
+// base URL for SockJS (websocket) connection
+// The complete URL will look something like this(cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36):
+// ws://<ws_host>:<ws_port>/<ws_baseurl>/<3digits>/<randomstring>/websocket
+// For the default config of Spring's SockJS/STOMP support, the default base URL is "/socketentry/".
+const char* ws_baseurl            = "/socketentry/"; // don't forget leading and trailing "/" !!!
+
+
+// VARIABLES
+
+WebSocketsClient webSocket;
+
+
+// FUNCTIONS
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+    switch (type) {
+        case WStype_DISCONNECTED:
+            USE_SERIAL.printf("[WSc] Disconnected!\n");
+            break;
+        case WStype_CONNECTED:
+            {
+                USE_SERIAL.printf("[WSc] Connected to url: %s\n",  payload);
+            }
+            break;
+        case WStype_TEXT:
+            {
+                // #####################
+                // handle SockJs+STOMP protocol
+                // #####################
+
+                String text = (char*) payload;
+
+                USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+                if (payload[0] == 'h') {
+
+                    USE_SERIAL.println("Heartbeat!");
+
+                } else if (payload[0] == 'o') {
+
+                    // on open connection
+                    char *msg = "[\"CONNECT\\naccept-version:1.1,1.0\\nheart-beat:10000,10000\\n\\n\\u0000\"]";
+                    webSocket.sendTXT(msg);
+
+                } else if (text.startsWith("a[\"CONNECTED")) {
+
+                    // subscribe to some channels
+
+                    char *msg = "[\"SUBSCRIBE\\nid:sub-0\\ndestination:/user/queue/messages\\n\\n\\u0000\"]";
+                    webSocket.sendTXT(msg);
+                    delay(1000);
+
+                    // and send a message
+
+                    msg = "[\"SEND\\ndestination:/app/message\\n\\n{\\\"user\\\":\\\"esp\\\",\\\"message\\\":\\\"Hello!\\\"}\\u0000\"]";
+                    webSocket.sendTXT(msg);
+                    delay(1000);
+                }
+
+                break;
+            }
+        case WStype_BIN:
+            USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+            hexdump(payload, length);
+
+            // send data to server
+            // webSocket.sendBIN(payload, length);
+            break;
+    }
+
+}
+
+void setup() {
+
+    // setup serial
+
+    // USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+
+
+    // connect to WiFi
+
+    USE_SERIAL.print("Logging into WLAN: "); Serial.print(wlan_ssid); Serial.print(" ...");
+    WiFi.mode(WIFI_STA);
+    WiFi.begin(wlan_ssid, wlan_password);
+
+    while (WiFi.status() != WL_CONNECTED) {
+        delay(500);
+        USE_SERIAL.print(".");
+    }
+    USE_SERIAL.println(" success.");
+    USE_SERIAL.print("IP: "); USE_SERIAL.println(WiFi.localIP());
+
+
+    // #####################
+    // create socket url according to SockJS protocol (cf. http://sockjs.github.io/sockjs-protocol/sockjs-protocol-0.3.3.html#section-36)
+    // #####################
+    String socketUrl = ws_baseurl;
+    socketUrl += random(0, 999);
+    socketUrl += "/";
+    socketUrl += random(0, 999999); // should be a random string, but this works (see )
+    socketUrl += "/websocket";
+
+    // connect to websocket
+    webSocket.begin(ws_host, ws_port, socketUrl);
+    webSocket.setExtraHeaders(); // remove "Origin: file://" header because it breaks the connection with Spring's default websocket config
+    //    webSocket.setExtraHeaders("foo: I am so funny\r\nbar: not"); // some headers, in case you feel funny
+    webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+    webSocket.loop();
+}
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketServer/WebSocketServer.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServer/WebSocketServer.ino
new file mode 100644
index 0000000000000000000000000000000000000000..5e266a23c10f5b761516cc4d2ed8747d78736360
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServer/WebSocketServer.ino
@@ -0,0 +1,86 @@
+/*
+ * WebSocketServer.ino
+ *
+ *  Created on: 22.05.2015
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+#include <WebSocketsServer.h>
+#include <Hash.h>
+
+ESP8266WiFiMulti WiFiMulti;
+
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+#define USE_SERIAL Serial1
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+    switch(type) {
+        case WStype_DISCONNECTED:
+            USE_SERIAL.printf("[%u] Disconnected!\n", num);
+            break;
+        case WStype_CONNECTED:
+            {
+                IPAddress ip = webSocket.remoteIP(num);
+                USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+				
+				// send message to client
+				webSocket.sendTXT(num, "Connected");
+            }
+            break;
+        case WStype_TEXT:
+            USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+
+            // send message to client
+            // webSocket.sendTXT(num, "message here");
+
+            // send data to all connected clients
+            // webSocket.broadcastTXT("message here");
+            break;
+        case WStype_BIN:
+            USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
+            hexdump(payload, length);
+
+            // send message to client
+            // webSocket.sendBIN(num, payload, length);
+            break;
+    }
+
+}
+
+void setup() {
+    // USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //Serial.setDebugOutput(true);
+    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+    for(uint8_t t = 4; t > 0; t--) {
+        USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+        USE_SERIAL.flush();
+        delay(1000);
+    }
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    webSocket.begin();
+    webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+    webSocket.loop();
+}
+
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino
new file mode 100644
index 0000000000000000000000000000000000000000..5fed1a959c896fc58de5cf618cb80017294dea40
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino
@@ -0,0 +1,132 @@
+/*
+ * WebSocketServerAllFunctionsDemo.ino
+ *
+ *  Created on: 10.05.2018
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+#include <WebSocketsServer.h>
+#include <ESP8266WebServer.h>
+#include <ESP8266mDNS.h>
+#include <Hash.h>
+
+#define LED_RED     15
+#define LED_GREEN   12
+#define LED_BLUE    13
+
+#define USE_SERIAL Serial
+
+ESP8266WiFiMulti WiFiMulti;
+
+ESP8266WebServer server(80);
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+    switch(type) {
+        case WStype_DISCONNECTED:
+            USE_SERIAL.printf("[%u] Disconnected!\n", num);
+            break;
+        case WStype_CONNECTED: {
+            IPAddress ip = webSocket.remoteIP(num);
+            USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+
+            // send message to client
+            webSocket.sendTXT(num, "Connected");
+        }
+            break;
+        case WStype_TEXT:
+            USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+
+            if(payload[0] == '#') {
+                // we get RGB data
+
+                // decode rgb data
+                uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
+
+                analogWrite(LED_RED, ((rgb >> 16) & 0xFF));
+                analogWrite(LED_GREEN, ((rgb >> 8) & 0xFF));
+                analogWrite(LED_BLUE, ((rgb >> 0) & 0xFF));
+            }
+
+            break;
+    }
+
+}
+
+void setup() {
+    //USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+    for(uint8_t t = 4; t > 0; t--) {
+        USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+        USE_SERIAL.flush();
+        delay(1000);
+    }
+
+    pinMode(LED_RED, OUTPUT);
+    pinMode(LED_GREEN, OUTPUT);
+    pinMode(LED_BLUE, OUTPUT);
+
+    digitalWrite(LED_RED, 1);
+    digitalWrite(LED_GREEN, 1);
+    digitalWrite(LED_BLUE, 1);
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    // start webSocket server
+    webSocket.begin();
+    webSocket.onEvent(webSocketEvent);
+
+    if(MDNS.begin("esp8266")) {
+        USE_SERIAL.println("MDNS responder started");
+    }
+
+    // handle index
+    server.on("/", []() {
+        // send index.html
+        server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () {  connection.send('Connect ' + new Date()); }; connection.onerror = function (error) {    console.log('WebSocket Error ', error);};connection.onmessage = function (e) {  console.log('Server: ', e.data);};function sendRGB() {  var r = parseInt(document.getElementById('r').value).toString(16);  var g = parseInt(document.getElementById('g').value).toString(16);  var b = parseInt(document.getElementById('b').value).toString(16);  if(r.length < 2) { r = '0' + r; }   if(g.length < 2) { g = '0' + g; }   if(b.length < 2) { b = '0' + b; }   var rgb = '#'+r+g+b;    console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/></body></html>");
+    });
+
+    server.begin();
+
+    // Add service to MDNS
+    MDNS.addService("http", "tcp", 80);
+    MDNS.addService("ws", "tcp", 81);
+
+    digitalWrite(LED_RED, 0);
+    digitalWrite(LED_GREEN, 0);
+    digitalWrite(LED_BLUE, 0);
+
+}
+
+unsigned long last_10sec = 0;
+unsigned int counter = 0;
+
+void loop() {
+    unsigned long t = millis();
+    webSocket.loop();
+    server.handleClient();
+
+    if((t - last_10sec) > 10 * 1000) {
+        counter++;
+        bool ping = (counter % 2);
+        int i = webSocket.connectedClients(ping);
+        USE_SERIAL.printf("%d Connected websocket clients ping: %d\n", i, ping);
+        last_10sec = millis();
+    }
+}
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino
new file mode 100644
index 0000000000000000000000000000000000000000..62bef310576048432458e286abff64bffb34ccc8
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino
@@ -0,0 +1,94 @@
+/*
+ * WebSocketServer.ino
+ *
+ *  Created on: 22.05.2015
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+#include <WebSocketsServer.h>
+#include <Hash.h>
+
+ESP8266WiFiMulti WiFiMulti;
+
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+#define USE_SERIAL Serial
+
+String fragmentBuffer = "";
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+	switch(type) {
+		case WStype_DISCONNECTED:
+			USE_SERIAL.printf("[%u] Disconnected!\n", num);
+			break;
+		case WStype_CONNECTED: {
+			IPAddress ip = webSocket.remoteIP(num);
+			USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+
+			// send message to client
+			webSocket.sendTXT(num, "Connected");
+		}
+			break;
+		case WStype_TEXT:
+			USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+			break;
+		case WStype_BIN:
+			USE_SERIAL.printf("[%u] get binary length: %u\n", num, length);
+			hexdump(payload, length);
+			break;
+
+		// Fragmentation / continuation opcode handling
+		// case WStype_FRAGMENT_BIN_START:
+		case WStype_FRAGMENT_TEXT_START:
+			fragmentBuffer = (char*)payload;
+			USE_SERIAL.printf("[%u] get start start of Textfragment: %s\n", num, payload);
+			break;
+		case WStype_FRAGMENT:
+			fragmentBuffer += (char*)payload;
+			USE_SERIAL.printf("[%u] get Textfragment : %s\n", num, payload);
+			break;
+		case WStype_FRAGMENT_FIN:
+			fragmentBuffer += (char*)payload;
+			USE_SERIAL.printf("[%u] get end of Textfragment: %s\n", num, payload);
+			USE_SERIAL.printf("[%u] full frame: %s\n", num, fragmentBuffer.c_str());
+			break;
+	}
+
+}
+
+void setup() {
+	// USE_SERIAL.begin(921600);
+	USE_SERIAL.begin(115200);
+
+	//Serial.setDebugOutput(true);
+	USE_SERIAL.setDebugOutput(true);
+
+	USE_SERIAL.println();
+	USE_SERIAL.println();
+	USE_SERIAL.println();
+
+	for(uint8_t t = 4; t > 0; t--) {
+		USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+		USE_SERIAL.flush();
+		delay(1000);
+	}
+
+	WiFiMulti.addAP("SSID", "passpasspass");
+
+	while(WiFiMulti.run() != WL_CONNECTED) {
+		delay(100);
+	}
+
+	webSocket.begin();
+	webSocket.onEvent(webSocketEvent);
+}
+
+void loop() {
+	webSocket.loop();
+}
+
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino
new file mode 100644
index 0000000000000000000000000000000000000000..da58fa355f8a423bac4c7cfa288fdde2655cc7a8
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino
@@ -0,0 +1,86 @@
+/*
+ * WebSocketServerHttpHeaderValidation.ino
+ *
+ *  Created on: 08.06.2016
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+#include <WebSocketsServer.h>
+#include <Hash.h>
+
+ESP8266WiFiMulti WiFiMulti;
+
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+#define USE_SERIAL Serial1
+
+const unsigned long int validSessionId = 12345; //some arbitrary value to act as a valid sessionId
+
+/*
+ * Returns a bool value as an indicator to describe whether a user is allowed to initiate a websocket upgrade
+ * based on the value of a cookie. This function expects the rawCookieHeaderValue to look like this "sessionId=<someSessionIdNumberValue>|"
+ */
+bool isCookieValid(String rawCookieHeaderValue) {
+
+	if (rawCookieHeaderValue.indexOf("sessionId") != -1) {
+		String sessionIdStr = rawCookieHeaderValue.substring(rawCookieHeaderValue.indexOf("sessionId=") + 10, rawCookieHeaderValue.indexOf("|"));
+		unsigned long int sessionId = strtoul(sessionIdStr.c_str(), NULL, 10);
+		return sessionId == validSessionId;
+	}
+	return false;
+}
+
+/*
+ * The WebSocketServerHttpHeaderValFunc delegate passed to webSocket.onValidateHttpHeader
+ */
+bool validateHttpHeader(String headerName, String headerValue) {
+
+	//assume a true response for any headers not handled by this validator
+	bool valid = true;
+
+	if(headerName.equalsIgnoreCase("Cookie")) {
+		//if the header passed is the Cookie header, validate it according to the rules in 'isCookieValid' function
+		valid = isCookieValid(headerValue);
+	}
+
+	return valid;
+}
+
+void setup() {
+    // USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //Serial.setDebugOutput(true);
+    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+    for(uint8_t t = 4; t > 0; t--) {
+        USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+        USE_SERIAL.flush();
+        delay(1000);
+    }
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    //connecting clients must supply a valid session cookie at websocket upgrade handshake negotiation time
+    const char * headerkeys[] = { "Cookie" };
+    size_t headerKeyCount = sizeof(headerkeys) / sizeof(char*);
+    webSocket.onValidateHttpHeader(validateHttpHeader, headerkeys, headerKeyCount);
+    webSocket.begin();
+}
+
+void loop() {
+    webSocket.loop();
+}
+
diff --git a/libraries/WebSockets_ID549/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino
new file mode 100644
index 0000000000000000000000000000000000000000..7915409ab6c05815b26939d0768e956e392ae7fa
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino
@@ -0,0 +1,121 @@
+/*
+ * WebSocketServer_LEDcontrol.ino
+ *
+ *  Created on: 26.11.2015
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+#include <WebSocketsServer.h>
+#include <ESP8266WebServer.h>
+#include <ESP8266mDNS.h>
+#include <Hash.h>
+
+#define LED_RED     15
+#define LED_GREEN   12
+#define LED_BLUE    13
+
+#define USE_SERIAL Serial
+
+
+ESP8266WiFiMulti WiFiMulti;
+
+ESP8266WebServer server(80);
+WebSocketsServer webSocket = WebSocketsServer(81);
+
+void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+
+    switch(type) {
+        case WStype_DISCONNECTED:
+            USE_SERIAL.printf("[%u] Disconnected!\n", num);
+            break;
+        case WStype_CONNECTED: {
+            IPAddress ip = webSocket.remoteIP(num);
+            USE_SERIAL.printf("[%u] Connected from %d.%d.%d.%d url: %s\n", num, ip[0], ip[1], ip[2], ip[3], payload);
+
+            // send message to client
+            webSocket.sendTXT(num, "Connected");
+        }
+            break;
+        case WStype_TEXT:
+            USE_SERIAL.printf("[%u] get Text: %s\n", num, payload);
+
+            if(payload[0] == '#') {
+                // we get RGB data
+
+                // decode rgb data
+                uint32_t rgb = (uint32_t) strtol((const char *) &payload[1], NULL, 16);
+
+                analogWrite(LED_RED,    ((rgb >> 16) & 0xFF));
+                analogWrite(LED_GREEN,  ((rgb >> 8) & 0xFF));
+                analogWrite(LED_BLUE,   ((rgb >> 0) & 0xFF));
+            }
+
+            break;
+    }
+
+}
+
+void setup() {
+    //USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+    for(uint8_t t = 4; t > 0; t--) {
+        USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+        USE_SERIAL.flush();
+        delay(1000);
+    }
+
+    pinMode(LED_RED, OUTPUT);
+    pinMode(LED_GREEN, OUTPUT);
+    pinMode(LED_BLUE, OUTPUT);
+
+    digitalWrite(LED_RED, 1);
+    digitalWrite(LED_GREEN, 1);
+    digitalWrite(LED_BLUE, 1);
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    // start webSocket server
+    webSocket.begin();
+    webSocket.onEvent(webSocketEvent);
+
+    if(MDNS.begin("esp8266")) {
+        USE_SERIAL.println("MDNS responder started");
+    }
+
+    // handle index
+    server.on("/", []() {
+        // send index.html
+        server.send(200, "text/html", "<html><head><script>var connection = new WebSocket('ws://'+location.hostname+':81/', ['arduino']);connection.onopen = function () {  connection.send('Connect ' + new Date()); }; connection.onerror = function (error) {    console.log('WebSocket Error ', error);};connection.onmessage = function (e) {  console.log('Server: ', e.data);};function sendRGB() {  var r = parseInt(document.getElementById('r').value).toString(16);  var g = parseInt(document.getElementById('g').value).toString(16);  var b = parseInt(document.getElementById('b').value).toString(16);  if(r.length < 2) { r = '0' + r; }   if(g.length < 2) { g = '0' + g; }   if(b.length < 2) { b = '0' + b; }   var rgb = '#'+r+g+b;    console.log('RGB: ' + rgb); connection.send(rgb); }</script></head><body>LED Control:<br/><br/>R: <input id=\"r\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>G: <input id=\"g\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/>B: <input id=\"b\" type=\"range\" min=\"0\" max=\"255\" step=\"1\" oninput=\"sendRGB();\" /><br/></body></html>");
+    });
+
+    server.begin();
+
+    // Add service to MDNS
+    MDNS.addService("http", "tcp", 80);
+    MDNS.addService("ws", "tcp", 81);
+
+    digitalWrite(LED_RED, 0);
+    digitalWrite(LED_GREEN, 0);
+    digitalWrite(LED_BLUE, 0);
+
+}
+
+void loop() {
+    webSocket.loop();
+    server.handleClient();
+}
diff --git a/libraries/WebSockets_ID549/examples/particle/ParticleWebSocketClient/application.cpp b/libraries/WebSockets_ID549/examples/particle/ParticleWebSocketClient/application.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..461228f39b72ad2dd7d18a0c36041b24bc98863d
--- /dev/null
+++ b/libraries/WebSockets_ID549/examples/particle/ParticleWebSocketClient/application.cpp
@@ -0,0 +1,46 @@
+/* To compile using make CLI, create a folder under \firmware\user\applications and copy application.cpp there.
+*  Then, copy src files under particleWebSocket folder.
+*/
+
+#include "application.h"
+#include "particleWebSocket/WebSocketsClient.h"
+
+WebSocketsClient webSocket;
+
+void webSocketEvent(WStype_t type, uint8_t* payload, size_t length)
+{
+	switch (type)
+	{
+	case WStype_DISCONNECTED:
+		Serial.printlnf("[WSc] Disconnected!");
+		break;
+	case WStype_CONNECTED:
+		Serial.printlnf("[WSc] Connected to URL: %s", payload);
+		webSocket.sendTXT("Connected\r\n");
+		break;
+	case WStype_TEXT:
+		Serial.printlnf("[WSc] get text: %s", payload);
+		break;
+	case WStype_BIN:
+		Serial.printlnf("[WSc] get binary length: %u", length);
+		break;
+	}
+}
+
+void setup()
+{
+	Serial.begin(9600);
+	
+	WiFi.setCredentials("[SSID]", "[PASSWORD]", WPA2, WLAN_CIPHER_AES_TKIP);
+	WiFi.connect();
+	    
+	webSocket.begin("192.168.1.153", 85, "/ClientService/?variable=Test1212");
+	webSocket.onEvent(webSocketEvent);
+}
+
+void loop()
+{
+	webSocket.sendTXT("Hello world!");
+	delay(500);
+	webSocket.loop();
+}
diff --git a/libraries/WebSockets_ID549/library.json b/libraries/WebSockets_ID549/library.json
new file mode 100644
index 0000000000000000000000000000000000000000..978e584a451624481ffdfb48cdb3de0a7922a343
--- /dev/null
+++ b/libraries/WebSockets_ID549/library.json
@@ -0,0 +1,25 @@
+{
+    "name": "WebSockets",
+    "description": "WebSocket Server and Client for Arduino based on RFC6455",
+    "keywords": "wifi, http, web, server, client, websocket",
+    "authors": [
+        {
+            "name": "Markus Sattler",
+            "url": "https://github.com/Links2004",
+            "maintainer": true
+        }
+    ],
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/Links2004/arduinoWebSockets.git"
+    },
+    "version": "2.1.3",
+    "license": "LGPL-2.1",
+    "export": {
+        "exclude": [
+            "tests"
+        ]
+    },
+    "frameworks": "arduino",
+    "platforms": "atmelavr, espressif8266, espressif32"
+}
diff --git a/libraries/WebSockets_ID549/library.properties b/libraries/WebSockets_ID549/library.properties
new file mode 100644
index 0000000000000000000000000000000000000000..a30fdbf65be64f76827421ee8557383556729167
--- /dev/null
+++ b/libraries/WebSockets_ID549/library.properties
@@ -0,0 +1,9 @@
+name=WebSockets
+version=2.1.3
+author=Markus Sattler
+maintainer=Markus Sattler
+sentence=WebSockets for Arduino (Server + Client)
+paragraph=use 2.x.x for ESP and 1.3 for AVR
+category=Communication
+url=https://github.com/Links2004/arduinoWebSockets
+architectures=*
diff --git a/libraries/WebSockets_ID549/src/WebSockets.cpp b/libraries/WebSockets_ID549/src/WebSockets.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fc4a6ba9a0a95d96b58267604b48434b9f3bd58a
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/WebSockets.cpp
@@ -0,0 +1,699 @@
+/**
+ * @file WebSockets.cpp
+ * @date 20.05.2015
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the WebSockets for Arduino.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "WebSockets.h"
+
+#ifdef ESP8266
+#include <core_esp8266_features.h>
+#endif
+
+extern "C" {
+#ifdef CORE_HAS_LIBB64
+#include <libb64/cencode.h>
+#else
+#include "libb64/cencode_inc.h"
+#endif
+}
+
+#ifdef ESP8266
+#include <Hash.h>
+#elif defined(ESP32)
+#include <hwcrypto/sha.h>
+#else
+
+extern "C" {
+#include "libsha1/libsha1.h"
+}
+
+#endif
+
+
+/**
+ *
+ * @param client WSclient_t *  ptr to the client struct
+ * @param code uint16_t see RFC
+ * @param reason ptr to the disconnect reason message
+ * @param reasonLen length of the disconnect reason message
+ */
+void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) {
+    DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] clientDisconnect code: %u\n", client->num, code);
+    if(client->status == WSC_CONNECTED && code) {
+        if(reason) {
+            sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen);
+        } else {
+            uint8_t buffer[2];
+            buffer[0] = ((code >> 8) & 0xFF);
+            buffer[1] = (code & 0xFF);
+            sendFrame(client, WSop_close, &buffer[0], 2);
+        }
+    }
+    clientDisconnect(client);
+}
+
+/**
+ *
+ * @param client WSclient_t *   ptr to the client struct
+ * @param opcode WSopcode_t
+ * @param payload uint8_t *     ptr to the payload
+ * @param length size_t         length of the payload
+ * @param fin bool              can be used to send data in more then one frame (set fin on the last frame)
+ * @param headerToPayload bool  set true if the payload has reserved 14 Byte at the beginning to dynamically add the Header (payload neet to be in RAM!)
+ * @return true if ok
+ */
+bool WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin, bool headerToPayload) {
+
+    if(client->tcp && !client->tcp->connected()) {
+        DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not Connected!?\n", client->num);
+        return false;
+    }
+
+    if(client->status != WSC_CONNECTED) {
+        DEBUG_WEBSOCKETS("[WS][%d][sendFrame] not in WSC_CONNECTED state!?\n", client->num);
+        return false;
+    }
+
+    DEBUG_WEBSOCKETS("[WS][%d][sendFrame] ------- send message frame -------\n", client->num);
+    DEBUG_WEBSOCKETS("[WS][%d][sendFrame] fin: %u opCode: %u mask: %u length: %u headerToPayload: %u\n", client->num, fin, opcode, client->cIsClient, length, headerToPayload);
+
+    if(opcode == WSop_text) {
+        DEBUG_WEBSOCKETS("[WS][%d][sendFrame] text: %s\n", client->num, (payload + (headerToPayload ? 14 : 0)));
+    }
+
+    uint8_t maskKey[4] = { 0x00, 0x00, 0x00, 0x00 };
+    uint8_t buffer[WEBSOCKETS_MAX_HEADER_SIZE] = { 0 };
+
+    uint8_t headerSize;
+    uint8_t * headerPtr;
+    uint8_t * payloadPtr = payload;
+    bool useInternBuffer = false;
+    bool ret = true;
+
+    // calculate header Size
+    if(length < 126) {
+        headerSize = 2;
+    } else if(length < 0xFFFF) {
+        headerSize = 4;
+    } else {
+        headerSize = 10;
+    }
+
+    if(client->cIsClient) {
+        headerSize += 4;
+    }
+
+#ifdef WEBSOCKETS_USE_BIG_MEM
+    // only for ESP since AVR has less HEAP
+    // try to send data in one TCP package (only if some free Heap is there)
+    if(!headerToPayload && ((length > 0) && (length < 1400)) && (GET_FREE_HEAP > 6000)) {
+        DEBUG_WEBSOCKETS("[WS][%d][sendFrame] pack to one TCP package...\n", client->num);
+        uint8_t * dataPtr = (uint8_t *) malloc(length + WEBSOCKETS_MAX_HEADER_SIZE);
+        if(dataPtr) {
+            memcpy((dataPtr + WEBSOCKETS_MAX_HEADER_SIZE), payload, length);
+            headerToPayload = true;
+            useInternBuffer = true;
+            payloadPtr = dataPtr;
+        }
+    }
+#endif
+
+    // set Header Pointer
+    if(headerToPayload) {
+        // calculate offset in payload
+        headerPtr = (payloadPtr + (WEBSOCKETS_MAX_HEADER_SIZE - headerSize));
+    } else {
+        headerPtr = &buffer[0];
+    }
+
+    // create header
+
+    // byte 0
+    *headerPtr = 0x00;
+    if(fin) {
+        *headerPtr |= bit(7);    ///< set Fin
+    }
+    *headerPtr |= opcode;        ///< set opcode
+    headerPtr++;
+
+    // byte 1
+    *headerPtr = 0x00;
+    if(client->cIsClient) {
+        *headerPtr |= bit(7);    ///< set mask
+    }
+
+    if(length < 126) {
+        *headerPtr |= length;
+        headerPtr++;
+    } else if(length < 0xFFFF) {
+        *headerPtr |= 126;
+        headerPtr++;
+        *headerPtr = ((length >> 8) & 0xFF);
+        headerPtr++;
+        *headerPtr = (length & 0xFF);
+        headerPtr++;
+    } else {
+        // Normally we never get here (to less memory)
+        *headerPtr |= 127;
+        headerPtr++;
+        *headerPtr = 0x00;
+        headerPtr++;
+        *headerPtr = 0x00;
+        headerPtr++;
+        *headerPtr = 0x00;
+        headerPtr++;
+        *headerPtr = 0x00;
+        headerPtr++;
+        *headerPtr = ((length >> 24) & 0xFF);
+        headerPtr++;
+        *headerPtr = ((length >> 16) & 0xFF);
+        headerPtr++;
+        *headerPtr = ((length >> 8) & 0xFF);
+        headerPtr++;
+        *headerPtr = (length & 0xFF);
+        headerPtr++;
+    }
+
+    if(client->cIsClient) {
+        if(useInternBuffer) {
+            // if we use a Intern Buffer we can modify the data
+            // by this fact its possible the do the masking
+            for(uint8_t x = 0; x < sizeof(maskKey); x++) {
+                maskKey[x] = random(0xFF);
+                *headerPtr = maskKey[x];
+                headerPtr++;
+            }
+
+            uint8_t * dataMaskPtr;
+
+            if(headerToPayload) {
+                dataMaskPtr = (payloadPtr + WEBSOCKETS_MAX_HEADER_SIZE);
+            } else {
+                dataMaskPtr = payloadPtr;
+            }
+
+            for(size_t x = 0; x < length; x++) {
+                dataMaskPtr[x] = (dataMaskPtr[x] ^ maskKey[x % 4]);
+            }
+
+        } else {
+            *headerPtr = maskKey[0];
+            headerPtr++;
+            *headerPtr = maskKey[1];
+            headerPtr++;
+            *headerPtr = maskKey[2];
+            headerPtr++;
+            *headerPtr = maskKey[3];
+            headerPtr++;
+        }
+    }
+
+#ifndef NODEBUG_WEBSOCKETS
+    unsigned long start = micros();
+#endif
+
+    if(headerToPayload) {
+        // header has be added to payload
+        // payload is forced to reserved 14 Byte but we may not need all based on the length and mask settings
+        // offset in payload is calculatetd 14 - headerSize
+        if(write(client, &payloadPtr[(WEBSOCKETS_MAX_HEADER_SIZE - headerSize)], (length + headerSize)) != (length + headerSize)) {
+            ret = false;
+        }
+    } else {
+        // send header
+        if(write(client, &buffer[0], headerSize) != headerSize) {
+            ret = false;
+        }
+
+        if(payloadPtr && length > 0) {
+            // send payload
+            if(write(client, &payloadPtr[0], length) != length) {
+                ret = false;
+            }
+        }
+    }
+
+    DEBUG_WEBSOCKETS("[WS][%d][sendFrame] sending Frame Done (%luus).\n", client->num, (micros() - start));
+
+#ifdef WEBSOCKETS_USE_BIG_MEM
+    if(useInternBuffer && payloadPtr) {
+        free(payloadPtr);
+    }
+#endif
+
+    return ret;
+}
+
+/**
+ * callen when HTTP header is done
+ * @param client WSclient_t *  ptr to the client struct
+ */
+void WebSockets::headerDone(WSclient_t * client) {
+    client->status = WSC_CONNECTED;
+    client->cWsRXsize = 0;
+    DEBUG_WEBSOCKETS("[WS][%d][headerDone] Header Handling Done.\n", client->num);
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+    client->cHttpLine = "";
+    handleWebsocket(client);
+#endif
+}
+
+/**
+ * handle the WebSocket stream
+ * @param client WSclient_t *  ptr to the client struct
+ */
+void WebSockets::handleWebsocket(WSclient_t * client) {
+    if(client->cWsRXsize == 0) {
+        handleWebsocketCb(client);
+    }
+}
+
+/**
+ * wait for
+ * @param client
+ * @param size
+ */
+bool WebSockets::handleWebsocketWaitFor(WSclient_t * client, size_t size) {
+    if(!client->tcp || !client->tcp->connected()) {
+        return false;
+    }
+
+    if(size > WEBSOCKETS_MAX_HEADER_SIZE) {
+        DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d too big!\n", client->num, size);
+        return false;
+    }
+
+    if(client->cWsRXsize >= size) {
+        return true;
+    }
+
+    DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor] size: %d cWsRXsize: %d\n", client->num, size, client->cWsRXsize);
+    readCb(client, &client->cWsHeader[client->cWsRXsize], (size - client->cWsRXsize), std::bind([](WebSockets * server, size_t size, WSclient_t * client, bool ok) {
+        DEBUG_WEBSOCKETS("[WS][%d][handleWebsocketWaitFor][readCb] size: %d ok: %d\n", client->num, size, ok);
+        if(ok) {
+            client->cWsRXsize = size;
+            server->handleWebsocketCb(client);
+        } else {
+            DEBUG_WEBSOCKETS("[WS][%d][readCb] failed.\n", client->num);
+            client->cWsRXsize = 0;
+            // timeout or error
+            server->clientDisconnect(client, 1002);
+        }
+    }, this, size, std::placeholders::_1, std::placeholders::_2));
+    return false;
+}
+
+void WebSockets::handleWebsocketCb(WSclient_t * client) {
+
+    if(!client->tcp || !client->tcp->connected()) {
+        return;
+    }
+
+    uint8_t * buffer = client->cWsHeader;
+
+    WSMessageHeader_t * header = &client->cWsHeaderDecode;
+    uint8_t * payload = NULL;
+
+    uint8_t headerLen = 2;
+
+    if(!handleWebsocketWaitFor(client, headerLen)) {
+        return;
+    }
+
+    // split first 2 bytes in the data
+    header->fin = ((*buffer >> 7) & 0x01);
+    header->rsv1 = ((*buffer >> 6) & 0x01);
+    header->rsv2 = ((*buffer >> 5) & 0x01);
+    header->rsv3 = ((*buffer >> 4) & 0x01);
+    header->opCode = (WSopcode_t) (*buffer & 0x0F);
+    buffer++;
+
+    header->mask = ((*buffer >> 7) & 0x01);
+    header->payloadLen = (WSopcode_t) (*buffer & 0x7F);
+    buffer++;
+
+    if(header->payloadLen == 126) {
+        headerLen += 2;
+        if(!handleWebsocketWaitFor(client, headerLen)) {
+            return;
+        }
+        header->payloadLen = buffer[0] << 8 | buffer[1];
+        buffer += 2;
+    } else if(header->payloadLen == 127) {
+        headerLen += 8;
+        // read 64bit integer as length
+        if(!handleWebsocketWaitFor(client, headerLen)) {
+            return;
+        }
+
+        if(buffer[0] != 0 || buffer[1] != 0 || buffer[2] != 0 || buffer[3] != 0) {
+            // really too big!
+            header->payloadLen = 0xFFFFFFFF;
+        } else {
+            header->payloadLen = buffer[4] << 24 | buffer[5] << 16 | buffer[6] << 8 | buffer[7];
+        }
+        buffer += 8;
+    }
+
+    DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ------- read massage frame -------\n", client->num);
+    DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] fin: %u rsv1: %u rsv2: %u rsv3 %u  opCode: %u\n", client->num, header->fin, header->rsv1, header->rsv2, header->rsv3, header->opCode);
+    DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] mask: %u payloadLen: %u\n", client->num, header->mask, header->payloadLen);
+
+    if(header->payloadLen > WEBSOCKETS_MAX_DATA_SIZE) {
+        DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] payload too big! (%u)\n", client->num, header->payloadLen);
+        clientDisconnect(client, 1009);
+        return;
+    }
+
+    if(header->mask) {
+        headerLen += 4;
+        if(!handleWebsocketWaitFor(client, headerLen)) {
+            return;
+        }
+        header->maskKey = buffer;
+        buffer += 4;
+    }
+
+    if(header->payloadLen > 0) {
+        // if text data we need one more
+        payload = (uint8_t *) malloc(header->payloadLen + 1);
+
+        if(!payload) {
+            DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] to less memory to handle payload %d!\n", client->num, header->payloadLen);
+            clientDisconnect(client, 1011);
+            return;
+        }
+        readCb(client, payload, header->payloadLen, std::bind(&WebSockets::handleWebsocketPayloadCb, this, std::placeholders::_1, std::placeholders::_2, payload));
+    } else {
+        handleWebsocketPayloadCb(client, true, NULL);
+    }
+}
+
+void WebSockets::handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload) {
+
+    WSMessageHeader_t * header = &client->cWsHeaderDecode;
+    if(ok) {
+        if(header->payloadLen > 0) {
+            payload[header->payloadLen] = 0x00;
+
+            if(header->mask) {
+                //decode XOR
+                for(size_t i = 0; i < header->payloadLen; i++) {
+                    payload[i] = (payload[i] ^ header->maskKey[i % 4]);
+                }
+            }
+        }
+
+        switch(header->opCode) {
+            case WSop_text:
+                DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] text: %s\n", client->num, payload);
+                // no break here!
+            case WSop_binary:
+            case WSop_continuation:
+                messageReceived(client, header->opCode, payload, header->payloadLen, header->fin);
+                break;
+            case WSop_ping:
+                // send pong back
+                DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] ping received (%s)\n", client->num, payload ? (const char*)payload : "");
+                sendFrame(client, WSop_pong, payload, header->payloadLen);
+                break;
+            case WSop_pong:
+                DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get pong (%s)\n", client->num, payload ? (const char*)payload : "");
+                client->pongReceived = true;
+                break;
+            case WSop_close: {
+                #ifndef NODEBUG_WEBSOCKETS
+                    uint16_t reasonCode = 1000;
+                    if(header->payloadLen >= 2) {
+                        reasonCode = payload[0] << 8 | payload[1];
+                    }
+                #endif
+                DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] get ask for close. Code: %d", client->num, reasonCode);
+                if(header->payloadLen > 2) {
+                    DEBUG_WEBSOCKETS(" (%s)\n", (payload + 2));
+                } else {
+                    DEBUG_WEBSOCKETS("\n");
+                }
+                clientDisconnect(client, 1000);
+            }
+            	break;
+            default:
+                clientDisconnect(client, 1002);
+                break;
+        }
+
+        if(payload) {
+            free(payload);
+        }
+
+        // reset input
+        client->cWsRXsize = 0;
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+        //register callback for next message
+        handleWebsocketWaitFor(client, 2);
+#endif
+
+    } else {
+        DEBUG_WEBSOCKETS("[WS][%d][handleWebsocket] missing data!\n", client->num);
+        free(payload);
+        clientDisconnect(client, 1002);
+    }
+}
+
+/**
+ * generate the key for Sec-WebSocket-Accept
+ * @param clientKey String
+ * @return String Accept Key
+ */
+String WebSockets::acceptKey(String & clientKey) {
+    uint8_t sha1HashBin[20] = { 0 };
+#ifdef ESP8266
+    sha1(clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]);
+#elif defined(ESP32)
+    String data = clientKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+    esp_sha(SHA1, (unsigned char*)data.c_str(), data.length(), &sha1HashBin[0]);
+#else
+    clientKey += "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
+    SHA1_CTX ctx;
+    SHA1Init(&ctx);
+    SHA1Update(&ctx, (const unsigned char*)clientKey.c_str(), clientKey.length());
+    SHA1Final(&sha1HashBin[0], &ctx);
+#endif
+
+    String key = base64_encode(sha1HashBin, 20);
+    key.trim();
+
+    return key;
+}
+
+/**
+ * base64_encode
+ * @param data uint8_t *
+ * @param length size_t
+ * @return base64 encoded String
+ */
+String WebSockets::base64_encode(uint8_t * data, size_t length) {
+    size_t size = ((length * 1.6f) + 1);
+    char * buffer = (char *) malloc(size);
+    if(buffer) {
+        base64_encodestate _state;
+        base64_init_encodestate(&_state);
+        int len = base64_encode_block((const char *) &data[0], length, &buffer[0], &_state);
+        len = base64_encode_blockend((buffer + len), &_state);
+
+        String base64 = String(buffer);
+        free(buffer);
+        return base64;
+    }
+    return String("-FAIL-");
+}
+
+/**
+ * read x byte from tcp or get timeout
+ * @param client WSclient_t *
+ * @param out  uint8_t * data buffer
+ * @param n size_t byte count
+ * @return true if ok
+ */
+bool WebSockets::readCb(WSclient_t * client, uint8_t * out, size_t n, WSreadWaitCb cb) {
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+    if(!client->tcp || !client->tcp->connected()) {
+        return false;
+    }
+
+    client->tcp->readBytes(out, n, std::bind([](WSclient_t * client, bool ok, WSreadWaitCb cb) {
+        if(cb) {
+            cb(client, ok);
+        }
+    }, client, std::placeholders::_1, cb));
+
+#else
+    unsigned long t = millis();
+    size_t len;
+    DEBUG_WEBSOCKETS("[readCb] n: %zu t: %lu\n", n, t);
+    while(n > 0) {
+        if(client->tcp == NULL) {
+            DEBUG_WEBSOCKETS("[readCb] tcp is null!\n");
+            if(cb) {
+                cb(client, false);
+            }
+            return false;
+        }
+
+        if(!client->tcp->connected()) {
+            DEBUG_WEBSOCKETS("[readCb] not connected!\n");
+            if(cb) {
+                cb(client, false);
+            }
+            return false;
+        }
+
+        if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
+            DEBUG_WEBSOCKETS("[readCb] receive TIMEOUT! %lu\n", (millis() - t));
+            if(cb) {
+                cb(client, false);
+            }
+            return false;
+        }
+
+        if(!client->tcp->available()) {
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+            delay(0);
+#endif
+            continue;
+        }
+
+        len = client->tcp->read((uint8_t*) out, n);
+        if(len) {
+            t = millis();
+            out += len;
+            n -= len;
+            //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
+        } else {
+            //DEBUG_WEBSOCKETS("Receive %d left %d!\n", len, n);
+        }
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+        delay(0);
+#endif
+    }
+    if(cb) {
+        cb(client, true);
+    }
+#endif
+    return true;
+}
+
+/**
+ * write x byte to tcp or get timeout
+ * @param client WSclient_t *
+ * @param out  uint8_t * data buffer
+ * @param n size_t byte count
+ * @return bytes send
+ */
+size_t WebSockets::write(WSclient_t * client, uint8_t *out, size_t n) {
+	if(out == NULL) return 0;
+	if(client == NULL) return 0;
+	unsigned long t = millis();
+	size_t len = 0;
+	size_t total = 0;
+	DEBUG_WEBSOCKETS("[write] n: %zu t: %lu\n", n, t);
+	while(n > 0) {
+		if(client->tcp == NULL) {
+			DEBUG_WEBSOCKETS("[write] tcp is null!\n");
+			break;
+		}
+
+		if(!client->tcp->connected()) {
+			DEBUG_WEBSOCKETS("[write] not connected!\n");
+			break;
+		}
+
+		if((millis() - t) > WEBSOCKETS_TCP_TIMEOUT) {
+			DEBUG_WEBSOCKETS("[write] write TIMEOUT! %lu\n", (millis() - t));
+			break;
+		}
+
+		len = client->tcp->write((const uint8_t*)out, n);
+		if(len) {
+			t = millis();
+			out += len;
+			n -= len;
+			total += len;
+			//DEBUG_WEBSOCKETS("write %d left %d!\n", len, n);
+		} else {
+			//DEBUG_WEBSOCKETS("write %d failed left %d!\n", len, n);
+		}
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+		delay(0);
+#endif
+	}
+	return total;
+}
+
+size_t WebSockets::write(WSclient_t * client, const char *out) {
+	if(client == NULL) return 0;
+	if(out == NULL) return 0;
+	return write(client, (uint8_t*)out, strlen(out));
+}
+
+/**
+ * enable ping/pong heartbeat process
+ * @param client WSclient_t *
+ * @param pingInterval uint32_t how often ping will be sent
+ * @param pongTimeout uint32_t millis after which pong should timout if not received
+ * @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
+ */
+void WebSockets::enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount){
+    if(client == NULL) return;
+    client->pingInterval = pingInterval;
+    client->pongTimeout = pongTimeout;
+    client->disconnectTimeoutCount = disconnectTimeoutCount;
+
+}
+
+/**
+ * handle ping/pong heartbeat timeout process
+ * @param client WSclient_t *
+ */ 
+void WebSockets::handleHBTimeout(WSclient_t * client){
+    if (client->pingInterval) { // if heartbeat is enabled
+        uint32_t pi = millis() - client->lastPing;
+
+        if (client->pongReceived) {
+            client->pongTimeoutCount = 0;
+        } else {
+            if (pi > client->pongTimeout){ // pong not received in time
+                client->pongTimeoutCount++;
+                client->lastPing = millis() - client->pingInterval - 500; // force ping on the next run 
+
+                DEBUG_WEBSOCKETS("[HBtimeout] pong TIMEOUT! lp=%d millis=%d pi=%d count=%d\n", client->lastPing, millis(), pi, client->pongTimeoutCount);
+
+                if (client->disconnectTimeoutCount && client->pongTimeoutCount >= client->disconnectTimeoutCount){
+                    DEBUG_WEBSOCKETS("[HBtimeout] count=%d, DISCONNECTING\n", client->pongTimeoutCount);
+                    clientDisconnect(client);
+                }
+            } 
+        }
+
+    }
+        
+}
\ No newline at end of file
diff --git a/libraries/WebSockets_ID549/src/WebSockets.h b/libraries/WebSockets_ID549/src/WebSockets.h
new file mode 100644
index 0000000000000000000000000000000000000000..39a1e0bd79a7f19fef59910d68d7c7ec70ce2d4c
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/WebSockets.h
@@ -0,0 +1,322 @@
+/**
+ * @file WebSockets.h
+ * @date 20.05.2015
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the WebSockets for Arduino.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef WEBSOCKETS_H_
+#define WEBSOCKETS_H_
+
+#ifdef STM32_DEVICE
+#include <application.h>
+#define bit(b) (1UL << (b)) // Taken directly from Arduino.h
+#else
+#include <Arduino.h>
+#include <IPAddress.h>
+#endif
+
+#ifdef ARDUINO_ARCH_AVR
+#error Version 2.x.x currently does not support Arduino with AVR since there is no support for std namespace of c++.
+#error Use Version 1.x.x. (ATmega branch)
+#else
+#include <functional>
+#endif
+
+
+#ifndef NODEBUG_WEBSOCKETS
+#ifdef DEBUG_ESP_PORT
+#define DEBUG_WEBSOCKETS(...) DEBUG_ESP_PORT.printf( __VA_ARGS__ )
+#else
+//#define DEBUG_WEBSOCKETS(...) os_printf( __VA_ARGS__ )
+#endif
+#endif
+
+
+#ifndef DEBUG_WEBSOCKETS
+#define DEBUG_WEBSOCKETS(...)
+#define NODEBUG_WEBSOCKETS
+#endif
+
+#if defined(ESP8266) || defined(ESP32)
+
+#define WEBSOCKETS_MAX_DATA_SIZE  (15*1024)
+#define WEBSOCKETS_USE_BIG_MEM
+#define GET_FREE_HEAP ESP.getFreeHeap()
+// moves all Header strings to Flash (~300 Byte)
+//#define WEBSOCKETS_SAVE_RAM
+
+#elif defined(STM32_DEVICE)
+
+#define WEBSOCKETS_MAX_DATA_SIZE  (15*1024)
+#define WEBSOCKETS_USE_BIG_MEM
+#define GET_FREE_HEAP System.freeMemory()
+
+#else
+
+//atmega328p has only 2KB ram!
+#define WEBSOCKETS_MAX_DATA_SIZE  (1024)
+// moves all Header strings to Flash
+#define WEBSOCKETS_SAVE_RAM
+
+#endif
+
+
+#define WEBSOCKETS_TCP_TIMEOUT    (2000)
+
+#define NETWORK_ESP8266_ASYNC   (0)
+#define NETWORK_ESP8266         (1)
+#define NETWORK_W5100           (2)
+#define NETWORK_ENC28J60        (3)
+#define NETWORK_ESP32           (4)
+
+// max size of the WS Message Header
+#define WEBSOCKETS_MAX_HEADER_SIZE  (14)
+
+#if !defined(WEBSOCKETS_NETWORK_TYPE)
+// select Network type based
+#if defined(ESP8266) || defined(ESP31B)
+#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266
+//#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP8266_ASYNC
+//#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
+
+#elif defined(ESP32)
+#define WEBSOCKETS_NETWORK_TYPE NETWORK_ESP32
+
+#else
+#define WEBSOCKETS_NETWORK_TYPE NETWORK_W5100
+
+#endif
+#endif
+
+// Includes and defined based on Network Type
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+
+// Note:
+//   No SSL/WSS support for client in Async mode
+//   TLS lib need a sync interface!
+
+
+#if defined(ESP8266)
+#include <ESP8266WiFi.h>
+#elif defined(ESP32)
+#include <WiFi.h>
+#include <WiFiClientSecure.h>
+#elif defined(ESP31B)
+#include <ESP31BWiFi.h>
+#else
+#error "network type ESP8266 ASYNC only possible on the ESP mcu!"
+#endif
+
+#include <ESPAsyncTCP.h>
+#include <ESPAsyncTCPbuffer.h>
+#define WEBSOCKETS_NETWORK_CLASS AsyncTCPbuffer
+#define WEBSOCKETS_NETWORK_SERVER_CLASS AsyncServer
+
+#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+
+#if !defined(ESP8266) && !defined(ESP31B)
+#error "network type ESP8266 only possible on the ESP mcu!"
+#endif
+
+#ifdef ESP8266
+#include <ESP8266WiFi.h>
+#else
+#include <ESP31BWiFi.h>
+#endif
+#define WEBSOCKETS_NETWORK_CLASS WiFiClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
+
+#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_W5100)
+
+#ifdef STM32_DEVICE
+#define WEBSOCKETS_NETWORK_CLASS TCPClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS TCPServer
+#else
+#include <Ethernet.h>
+#include <SPI.h>
+#define WEBSOCKETS_NETWORK_CLASS EthernetClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS EthernetServer
+#endif
+
+#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ENC28J60)
+
+#include <UIPEthernet.h>
+#define WEBSOCKETS_NETWORK_CLASS UIPClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS UIPServer
+
+#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+
+#include <WiFi.h>
+#include <WiFiClientSecure.h>
+#define WEBSOCKETS_NETWORK_CLASS WiFiClient
+#define WEBSOCKETS_NETWORK_SERVER_CLASS WiFiServer
+
+#else
+#error "no network type selected!"
+#endif
+
+// moves all Header strings to Flash (~300 Byte)
+#ifdef WEBSOCKETS_SAVE_RAM
+#define WEBSOCKETS_STRING(var)  F(var)
+#else
+#define WEBSOCKETS_STRING(var)  var
+#endif
+
+typedef enum {
+    WSC_NOT_CONNECTED,
+    WSC_HEADER,
+    WSC_CONNECTED
+} WSclientsStatus_t;
+
+typedef enum {
+    WStype_ERROR,
+    WStype_DISCONNECTED,
+    WStype_CONNECTED,
+    WStype_TEXT,
+    WStype_BIN,
+	WStype_FRAGMENT_TEXT_START,
+	WStype_FRAGMENT_BIN_START,
+	WStype_FRAGMENT,
+	WStype_FRAGMENT_FIN,
+} WStype_t;
+
+typedef enum {
+    WSop_continuation = 0x00, ///< %x0 denotes a continuation frame
+    WSop_text = 0x01,         ///< %x1 denotes a text frame
+    WSop_binary = 0x02,       ///< %x2 denotes a binary frame
+                              ///< %x3-7 are reserved for further non-control frames
+    WSop_close = 0x08,        ///< %x8 denotes a connection close
+    WSop_ping = 0x09,         ///< %x9 denotes a ping
+    WSop_pong = 0x0A          ///< %xA denotes a pong
+                              ///< %xB-F are reserved for further control frames
+} WSopcode_t;
+
+typedef struct {
+
+        bool fin;
+        bool rsv1;
+        bool rsv2;
+        bool rsv3;
+
+        WSopcode_t opCode;
+        bool mask;
+
+        size_t payloadLen;
+
+        uint8_t * maskKey;
+} WSMessageHeader_t;
+
+typedef struct {
+        uint8_t num; ///< connection number
+
+        WSclientsStatus_t status;
+
+        WEBSOCKETS_NETWORK_CLASS * tcp;
+
+        bool isSocketIO;    ///< client for socket.io server
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+        bool isSSL;             ///< run in ssl mode
+        WiFiClientSecure * ssl;
+#endif
+
+        String cUrl;        ///< http url
+        uint16_t cCode;     ///< http code
+
+        bool cIsClient = false;     ///< will be used for masking
+        bool cIsUpgrade;    ///< Connection == Upgrade
+        bool cIsWebsocket;  ///< Upgrade == websocket
+
+        String cSessionId;  ///< client Set-Cookie (session id)
+        String cKey;        ///< client Sec-WebSocket-Key
+        String cAccept;     ///< client Sec-WebSocket-Accept
+        String cProtocol;   ///< client Sec-WebSocket-Protocol
+        String cExtensions; ///< client Sec-WebSocket-Extensions
+        uint16_t cVersion;  ///< client Sec-WebSocket-Version
+
+        uint8_t cWsRXsize;  ///< State of the RX
+        uint8_t cWsHeader[WEBSOCKETS_MAX_HEADER_SIZE]; ///< RX WS Message buffer
+        WSMessageHeader_t cWsHeaderDecode;
+
+        String base64Authorization; ///< Base64 encoded Auth request
+        String plainAuthorization; ///< Base64 encoded Auth request
+
+        String extraHeaders;
+
+        bool cHttpHeadersValid; ///< non-websocket http header validity indicator
+        size_t cMandatoryHeadersCount; ///< non-websocket mandatory http headers present count
+
+        bool pongReceived;
+        uint32_t pingInterval;  // how often ping will be sent, 0 means "heartbeat is not active"
+        uint32_t lastPing;      // millis when last pong has been received
+        uint32_t pongTimeout;   // interval in millis after which pong is considered to timeout
+        uint8_t disconnectTimeoutCount;  // after how many subsequent pong timeouts discconnect will happen, 0 means "do not disconnect"
+        uint8_t pongTimeoutCount;   // current pong timeout count     
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+        String cHttpLine;   ///< HTTP header lines
+#endif
+
+} WSclient_t;
+
+
+
+class WebSockets {
+    protected:
+#ifdef __AVR__
+        typedef void (*WSreadWaitCb)(WSclient_t * client, bool ok);
+#else
+        typedef std::function<void(WSclient_t * client, bool ok)> WSreadWaitCb;
+#endif
+
+        virtual void clientDisconnect(WSclient_t * client) = 0;
+        virtual bool clientIsConnected(WSclient_t * client) = 0;
+
+        virtual void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) = 0;
+
+        void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0);
+        bool sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0, bool fin = true, bool headerToPayload = false);
+
+        void headerDone(WSclient_t * client);
+
+        void handleWebsocket(WSclient_t * client);
+
+        bool handleWebsocketWaitFor(WSclient_t * client, size_t size);
+        void handleWebsocketCb(WSclient_t * client);
+        void handleWebsocketPayloadCb(WSclient_t * client, bool ok, uint8_t * payload);
+
+        String acceptKey(String & clientKey);
+        String base64_encode(uint8_t * data, size_t length);
+
+        bool readCb(WSclient_t * client, uint8_t *out, size_t n, WSreadWaitCb cb);
+        virtual size_t write(WSclient_t * client, uint8_t *out, size_t n);
+        size_t write(WSclient_t * client, const char *out);
+
+        void enableHeartbeat(WSclient_t * client, uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
+        void handleHBTimeout(WSclient_t * client);
+
+
+};
+
+#ifndef UNUSED
+#define UNUSED(var) (void)(var)
+#endif
+#endif /* WEBSOCKETS_H_ */
diff --git a/libraries/WebSockets_ID549/src/WebSocketsClient.cpp b/libraries/WebSockets_ID549/src/WebSocketsClient.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2d96358819ceddf0e841a0740a9d2af43744ecf2
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/WebSocketsClient.cpp
@@ -0,0 +1,809 @@
+/**
+ * @file WebSocketsClient.cpp
+ * @date 20.05.2015
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the WebSockets for Arduino.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "WebSockets.h"
+#include "WebSocketsClient.h"
+
+WebSocketsClient::WebSocketsClient() {
+    _cbEvent = NULL;
+    _client.num = 0;
+    _client.cIsClient = true;
+    _client.extraHeaders = WEBSOCKETS_STRING("Origin: file://");
+}
+
+WebSocketsClient::~WebSocketsClient() {
+    disconnect();
+}
+
+/**
+ * calles to init the Websockets server
+ */
+void WebSocketsClient::begin(const char *host, uint16_t port, const char * url, const char * protocol) {
+    _host = host;
+    _port = port;
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+    _fingerprint = "";
+#endif
+
+    _client.num = 0;
+    _client.status = WSC_NOT_CONNECTED;
+    _client.tcp = NULL;
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+    _client.isSSL = false;
+    _client.ssl = NULL;
+#endif
+    _client.cUrl = url;
+    _client.cCode = 0;
+    _client.cIsUpgrade = false;
+    _client.cIsWebsocket = true;
+    _client.cKey = "";
+    _client.cAccept = "";
+    _client.cProtocol = protocol;
+    _client.cExtensions = "";
+    _client.cVersion = 0;
+    _client.base64Authorization = "";
+    _client.plainAuthorization = "";
+    _client.isSocketIO = false;
+
+    _client.lastPing = 0;
+    _client.pongReceived = false;
+    _client.pongTimeoutCount = 0;    
+
+#ifdef ESP8266
+    randomSeed(RANDOM_REG32);
+#else
+    // todo find better seed
+    randomSeed(millis());
+#endif
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+    asyncConnect();
+#endif
+
+    _lastConnectionFail = 0;
+    _reconnectInterval = 500;
+}
+
+void WebSocketsClient::begin(String host, uint16_t port, String url, String protocol) {
+    begin(host.c_str(), port, url.c_str(), protocol.c_str());
+}
+
+void WebSocketsClient::begin(IPAddress host, uint16_t port, const char * url, const char * protocol) {
+    return begin(host.toString().c_str(), port, url, protocol);
+}
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+void WebSocketsClient::beginSSL(const char *host, uint16_t port, const char * url, const char * fingerprint, const char * protocol) {
+    begin(host, port, url, protocol);
+    _client.isSSL = true;
+    _fingerprint = fingerprint;
+}
+
+void WebSocketsClient::beginSSL(String host, uint16_t port, String url, String fingerprint, String protocol) {
+    beginSSL(host.c_str(), port, url.c_str(), fingerprint.c_str(), protocol.c_str());
+}
+#endif
+
+void WebSocketsClient::beginSocketIO(const char *host, uint16_t port, const char * url, const char * protocol) {
+    begin(host, port, url, protocol);
+    _client.isSocketIO = true;
+}
+
+void WebSocketsClient::beginSocketIO(String host, uint16_t port, String url, String protocol) {
+    beginSocketIO(host.c_str(), port, url.c_str(), protocol.c_str());
+}
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+void WebSocketsClient::beginSocketIOSSL(const char *host, uint16_t port, const char * url, const char * protocol) {
+    begin(host, port, url, protocol);
+    _client.isSocketIO = true;
+    _client.isSSL = true;
+    _fingerprint = "";
+}
+
+void WebSocketsClient::beginSocketIOSSL(String host, uint16_t port, String url, String protocol) {
+    beginSocketIOSSL(host.c_str(), port, url.c_str(), protocol.c_str());
+}
+#endif
+
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+/**
+ * called in arduino loop
+ */
+void WebSocketsClient::loop(void) {
+    if(!clientIsConnected(&_client)) {
+        // do not flood the server
+        if((millis() - _lastConnectionFail) < _reconnectInterval) {
+            return;
+        }
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+        if(_client.isSSL) {
+            DEBUG_WEBSOCKETS("[WS-Client] connect wss...\n");
+            if(_client.ssl) {
+                delete _client.ssl;
+                _client.ssl = NULL;
+                _client.tcp = NULL;
+            }
+            _client.ssl = new WiFiClientSecure();
+            _client.tcp = _client.ssl;
+        } else {
+            DEBUG_WEBSOCKETS("[WS-Client] connect ws...\n");
+            if(_client.tcp) {
+                delete _client.tcp;
+                _client.tcp = NULL;
+            }
+            _client.tcp = new WiFiClient();
+        }
+#else
+        _client.tcp = new WEBSOCKETS_NETWORK_CLASS();
+#endif
+
+        if(!_client.tcp) {
+            DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
+            return;
+        }
+
+        if(_client.tcp->connect(_host.c_str(), _port)) {
+            connectedCb();
+            _lastConnectionFail = 0;
+        } else {
+            connectFailedCb();
+            _lastConnectionFail = millis();
+
+        }
+    } else {
+        handleClientData();
+
+        if (_client.status == WSC_CONNECTED){
+            handleHBPing();
+            handleHBTimeout(&_client);
+        }
+        
+    }
+}
+#endif
+
+/**
+ * set callback function
+ * @param cbEvent WebSocketServerEvent
+ */
+void WebSocketsClient::onEvent(WebSocketClientEvent cbEvent) {
+    _cbEvent = cbEvent;
+}
+
+/**
+ * send text data to client
+ * @param num uint8_t client id
+ * @param payload uint8_t *
+ * @param length size_t
+ * @param headerToPayload bool  (see sendFrame for more details)
+ * @return true if ok
+ */
+bool WebSocketsClient::sendTXT(uint8_t * payload, size_t length, bool headerToPayload) {
+    if(length == 0) {
+        length = strlen((const char *) payload);
+    }
+    if(clientIsConnected(&_client)) {
+        return sendFrame(&_client, WSop_text, payload, length, true, headerToPayload);
+    }
+    return false;
+}
+
+bool WebSocketsClient::sendTXT(const uint8_t * payload, size_t length) {
+    return sendTXT((uint8_t *) payload, length);
+}
+
+bool WebSocketsClient::sendTXT(char * payload, size_t length, bool headerToPayload) {
+    return sendTXT((uint8_t *) payload, length, headerToPayload);
+}
+
+bool WebSocketsClient::sendTXT(const char * payload, size_t length) {
+    return sendTXT((uint8_t *) payload, length);
+}
+
+bool WebSocketsClient::sendTXT(String & payload) {
+    return sendTXT((uint8_t *) payload.c_str(), payload.length());
+}
+
+/**
+ * send binary data to client
+ * @param num uint8_t client id
+ * @param payload uint8_t *
+ * @param length size_t
+ * @param headerToPayload bool  (see sendFrame for more details)
+ * @return true if ok
+ */
+bool WebSocketsClient::sendBIN(uint8_t * payload, size_t length, bool headerToPayload) {
+    if(clientIsConnected(&_client)) {
+        return sendFrame(&_client, WSop_binary, payload, length, true, headerToPayload);
+    }
+    return false;
+}
+
+bool WebSocketsClient::sendBIN(const uint8_t * payload, size_t length) {
+    return sendBIN((uint8_t *) payload, length);
+}
+
+/**
+ * sends a WS ping to Server
+ * @param payload uint8_t *
+ * @param length size_t
+ * @return true if ping is send out
+ */
+bool WebSocketsClient::sendPing(uint8_t * payload, size_t length) {
+    if(clientIsConnected(&_client)) {
+        bool sent = sendFrame(&_client, WSop_ping, payload, length);
+        if (sent) 
+            _client.lastPing = millis();
+        return sent;
+    }
+    return false;
+}
+
+bool WebSocketsClient::sendPing(String & payload) {
+    return sendPing((uint8_t *) payload.c_str(), payload.length());
+}
+
+/**
+ * disconnect one client
+ * @param num uint8_t client id
+ */
+void WebSocketsClient::disconnect(void) {
+    if(clientIsConnected(&_client)) {
+        WebSockets::clientDisconnect(&_client, 1000);
+    }
+}
+
+/**
+ * set the Authorizatio for the http request
+ * @param user const char *
+ * @param password const char *
+ */
+void WebSocketsClient::setAuthorization(const char * user, const char * password) {
+    if(user && password) {
+        String auth = user;
+        auth += ":";
+        auth += password;
+        _client.base64Authorization = base64_encode((uint8_t *) auth.c_str(), auth.length());
+    }
+}
+
+/**
+ * set the Authorizatio for the http request
+ * @param auth const char * base64
+ */
+void WebSocketsClient::setAuthorization(const char * auth) {
+    if(auth) {
+        //_client.base64Authorization = auth;
+        _client.plainAuthorization = auth;
+    }
+}
+
+/**
+ * set extra headers for the http request;
+ * separate headers by "\r\n"
+ * @param extraHeaders const char * extraHeaders
+ */
+void WebSocketsClient::setExtraHeaders(const char * extraHeaders) {
+    _client.extraHeaders = extraHeaders;
+}
+
+/**
+ * set the reconnect Interval
+ * how long to wait after a connection initiate failed
+ * @param time in ms
+ */
+void WebSocketsClient::setReconnectInterval(unsigned long time) {
+    _reconnectInterval = time;
+}
+
+//#################################################################################
+//#################################################################################
+//#################################################################################
+
+/**
+ *
+ * @param client WSclient_t *  ptr to the client struct
+ * @param opcode WSopcode_t
+ * @param payload  uint8_t *
+ * @param length size_t
+ */
+void WebSocketsClient::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
+    WStype_t type = WStype_ERROR;
+
+    UNUSED(client);
+
+    switch(opcode) {
+        case WSop_text:
+            type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
+            break;
+        case WSop_binary:
+            type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
+            break;
+        case WSop_continuation:
+            type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
+            break;
+        case WSop_close:
+        case WSop_ping:
+        case WSop_pong:
+        default:
+            break;
+    }
+
+    runCbEvent(type, payload, length);
+
+}
+
+/**
+ * Disconnect an client
+ * @param client WSclient_t *  ptr to the client struct
+ */
+void WebSocketsClient::clientDisconnect(WSclient_t * client) {
+
+    bool event = false;
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+    if(client->isSSL && client->ssl) {
+        if(client->ssl->connected()) {
+            client->ssl->flush();
+            client->ssl->stop();
+        }
+        event = true;
+        delete client->ssl;
+        client->ssl = NULL;
+        client->tcp = NULL;
+    }
+#endif
+
+    if(client->tcp) {
+        if(client->tcp->connected()) {
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+            client->tcp->flush();
+#endif
+            client->tcp->stop();
+        }
+        event = true;
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+        client->status = WSC_NOT_CONNECTED;
+#else
+        delete client->tcp;
+#endif
+        client->tcp = NULL;
+    }
+
+    client->cCode = 0;
+    client->cKey = "";
+    client->cAccept = "";
+    client->cVersion = 0;
+    client->cIsUpgrade = false;
+    client->cIsWebsocket = false;
+    client->cSessionId = "";
+
+    client->status = WSC_NOT_CONNECTED;
+
+    DEBUG_WEBSOCKETS("[WS-Client] client disconnected.\n");
+    if(event) {
+        runCbEvent(WStype_DISCONNECTED, NULL, 0);
+    }
+}
+
+/**
+ * get client state
+ * @param client WSclient_t *  ptr to the client struct
+ * @return true = conneted
+ */
+bool WebSocketsClient::clientIsConnected(WSclient_t * client) {
+
+    if(!client->tcp) {
+        return false;
+    }
+
+    if(client->tcp->connected()) {
+        if(client->status != WSC_NOT_CONNECTED) {
+            return true;
+        }
+    } else {
+        // client lost
+        if(client->status != WSC_NOT_CONNECTED) {
+            DEBUG_WEBSOCKETS("[WS-Client] connection lost.\n");
+            // do cleanup
+            clientDisconnect(client);
+        }
+    }
+
+    if(client->tcp) {
+        // do cleanup
+        clientDisconnect(client);
+    }
+
+    return false;
+}
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+/**
+ * Handel incomming data from Client
+ */
+void WebSocketsClient::handleClientData(void) {
+    int len = _client.tcp->available();
+    if(len > 0) {
+        switch(_client.status) {
+            case WSC_HEADER: {
+                String headerLine = _client.tcp->readStringUntil('\n');
+                handleHeader(&_client, &headerLine);
+            }
+                break;
+            case WSC_CONNECTED:
+                WebSockets::handleWebsocket(&_client);
+                break;
+            default:
+                WebSockets::clientDisconnect(&_client, 1002);
+                break;
+        }
+    }
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+    delay(0);
+#endif
+}
+#endif
+
+/**
+ * send the WebSocket header to Server
+ * @param client WSclient_t *  ptr to the client struct
+ */
+void WebSocketsClient::sendHeader(WSclient_t * client) {
+
+    static const char * NEW_LINE = "\r\n";
+
+    DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header...\n");
+
+    uint8_t randomKey[16] = { 0 };
+
+    for(uint8_t i = 0; i < sizeof(randomKey); i++) {
+        randomKey[i] = random(0xFF);
+    }
+
+    client->cKey = base64_encode(&randomKey[0], 16);
+
+#ifndef NODEBUG_WEBSOCKETS
+    unsigned long start = micros();
+#endif
+
+    String handshake;
+    bool ws_header = true;
+    String url = client->cUrl;
+
+    if(client->isSocketIO) {
+        if(client->cSessionId.length() == 0) {
+            url += WEBSOCKETS_STRING("&transport=polling");
+            ws_header = false;
+        } else {
+            url += WEBSOCKETS_STRING("&transport=websocket&sid=");
+            url += client->cSessionId;
+        }
+    }
+
+    handshake = WEBSOCKETS_STRING("GET ");
+    handshake += url + WEBSOCKETS_STRING(" HTTP/1.1\r\n"
+            "Host: ");
+    handshake += _host + ":" + _port + NEW_LINE;
+
+    if(ws_header) {
+        handshake += WEBSOCKETS_STRING("Connection: Upgrade\r\n"
+                "Upgrade: websocket\r\n"
+                "Sec-WebSocket-Version: 13\r\n"
+                "Sec-WebSocket-Key: ");
+        handshake += client->cKey + NEW_LINE;
+
+        if(client->cProtocol.length() > 0) {
+            handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
+            handshake += client->cProtocol + NEW_LINE;
+        }
+
+        if(client->cExtensions.length() > 0) {
+            handshake += WEBSOCKETS_STRING("Sec-WebSocket-Extensions: ");
+            handshake += client->cExtensions + NEW_LINE;
+        }
+    } else {
+        handshake += WEBSOCKETS_STRING("Connection: keep-alive\r\n");
+    }
+
+    // add extra headers; by default this includes "Origin: file://"
+    if(client->extraHeaders) {
+        handshake += client->extraHeaders + NEW_LINE;
+    }
+
+    handshake += WEBSOCKETS_STRING("User-Agent: arduino-WebSocket-Client\r\n");
+
+    if(client->base64Authorization.length() > 0) {
+        handshake += WEBSOCKETS_STRING("Authorization: Basic ");
+        handshake += client->base64Authorization + NEW_LINE;
+    }
+
+    if(client->plainAuthorization.length() > 0) {
+        handshake += WEBSOCKETS_STRING("Authorization: ");
+        handshake += client->plainAuthorization + NEW_LINE;
+    }
+
+    handshake += NEW_LINE;
+
+    DEBUG_WEBSOCKETS("[WS-Client][sendHeader] handshake %s", (uint8_t* )handshake.c_str());
+    write(client, (uint8_t*) handshake.c_str(), handshake.length());
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+    client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
+#endif
+
+    DEBUG_WEBSOCKETS("[WS-Client][sendHeader] sending header... Done (%luus).\n", (micros() - start));
+
+}
+
+/**
+ * handle the WebSocket header reading
+ * @param client WSclient_t *  ptr to the client struct
+ */
+void WebSocketsClient::handleHeader(WSclient_t * client, String * headerLine) {
+
+    headerLine->trim(); // remove \r
+
+    if(headerLine->length() > 0) {
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader] RX: %s\n", headerLine->c_str());
+
+        if(headerLine->startsWith(WEBSOCKETS_STRING("HTTP/1."))) {
+            // "HTTP/1.1 101 Switching Protocols"
+            client->cCode = headerLine->substring(9, headerLine->indexOf(' ', 9)).toInt();
+        } else if(headerLine->indexOf(':')) {
+            String headerName = headerLine->substring(0, headerLine->indexOf(':'));
+            String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
+
+            // remove space in the beginning  (RFC2616)
+            if(headerValue[0] == ' ') {
+                headerValue.remove(0, 1);
+            }
+
+            if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
+                if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("upgrade"))) {
+                    client->cIsUpgrade = true;
+                }
+            } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
+                if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
+                    client->cIsWebsocket = true;
+                }
+            } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Accept"))) {
+                client->cAccept = headerValue;
+                client->cAccept.trim(); // see rfc6455
+            } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
+                client->cProtocol = headerValue;
+            } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
+                client->cExtensions = headerValue;
+            } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
+                client->cVersion = headerValue.toInt();
+            } else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Set-Cookie"))) {
+                if(headerValue.indexOf(WEBSOCKETS_STRING("HttpOnly")) > -1) {
+                    client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1, headerValue.indexOf(";"));
+                } else {
+                    client->cSessionId = headerValue.substring(headerValue.indexOf('=') + 1);
+                }
+            }
+        } else {
+            DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
+        }
+
+        (*headerLine) = "";
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+        client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsClient::handleHeader, this, client, &(client->cHttpLine)));
+#endif
+
+    } else {
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header read fin.\n");
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Client settings:\n");
+
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cURL: %s\n", client->cUrl.c_str());
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cKey: %s\n", client->cKey.c_str());
+
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Server header:\n");
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cCode: %d\n", client->cCode);
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cIsUpgrade: %d\n", client->cIsUpgrade);
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cIsWebsocket: %d\n", client->cIsWebsocket);
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cAccept: %s\n", client->cAccept.c_str());
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cProtocol: %s\n", client->cProtocol.c_str());
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cExtensions: %s\n", client->cExtensions.c_str());
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cVersion: %d\n", client->cVersion);
+        DEBUG_WEBSOCKETS("[WS-Client][handleHeader]  - cSessionId: %s\n", client->cSessionId.c_str());
+
+        bool ok = (client->cIsUpgrade && client->cIsWebsocket);
+
+        if(ok) {
+            switch(client->cCode) {
+                case 101:  ///< Switching Protocols
+
+                    break;
+                case 200:
+                    if(client->isSocketIO) {
+                        break;
+                    }
+                case 403: ///< Forbidden
+                    // todo handle login
+                default:   ///< Server dont unterstand requrst
+                    ok = false;
+                    DEBUG_WEBSOCKETS("[WS-Client][handleHeader] serverCode is not 101 (%d)\n", client->cCode);
+                    clientDisconnect(client);
+                    _lastConnectionFail = millis();
+                    break;
+            }
+        }
+
+        if(ok) {
+
+            if(client->cAccept.length() == 0) {
+                ok = false;
+            } else {
+                // generate Sec-WebSocket-Accept key for check
+                String sKey = acceptKey(client->cKey);
+                if(sKey != client->cAccept) {
+                    DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Sec-WebSocket-Accept is wrong\n");
+                    ok = false;
+                }
+            }
+        }
+
+        if(ok) {
+
+            DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Websocket connection init done.\n");
+            headerDone(client);
+
+            runCbEvent(WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length());
+
+        } else if(clientIsConnected(client) && client->isSocketIO && client->cSessionId.length() > 0) {
+            sendHeader(client);
+        } else {
+            DEBUG_WEBSOCKETS("[WS-Client][handleHeader] no Websocket connection close.\n");
+            _lastConnectionFail = millis();
+            if(clientIsConnected(client)) {
+                write(client, "This is a webSocket client!");
+            }
+            clientDisconnect(client);
+        }
+    }
+}
+
+void WebSocketsClient::connectedCb() {
+
+    DEBUG_WEBSOCKETS("[WS-Client] connected to %s:%u.\n", _host.c_str(), _port);
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+    _client.tcp->onDisconnect(std::bind([](WebSocketsClient * c, AsyncTCPbuffer * obj, WSclient_t * client) -> bool {
+                        DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
+                        client->status = WSC_NOT_CONNECTED;
+                        client->tcp = NULL;
+
+                        // reconnect
+                        c->asyncConnect();
+
+                        return true;
+                    }, this, std::placeholders::_1, &_client));
+#endif
+
+    _client.status = WSC_HEADER;
+
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+    // set Timeout for readBytesUntil and readStringUntil
+    _client.tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
+#endif
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+    _client.tcp->setNoDelay(true);
+
+    if(_client.isSSL && _fingerprint.length()) {
+        if(!_client.ssl->verify(_fingerprint.c_str(), _host.c_str())) {
+            DEBUG_WEBSOCKETS("[WS-Client] certificate mismatch\n");
+            WebSockets::clientDisconnect(&_client, 1000);
+            return;
+        }
+    }
+#endif
+
+    // send Header to Server
+    sendHeader(&_client);
+
+}
+
+void WebSocketsClient::connectFailedCb() {
+    DEBUG_WEBSOCKETS("[WS-Client] connection to %s:%u Failed\n", _host.c_str(), _port);
+}
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+
+void WebSocketsClient::asyncConnect() {
+
+    DEBUG_WEBSOCKETS("[WS-Client] asyncConnect...\n");
+
+    AsyncClient * tcpclient = new AsyncClient();
+
+    if(!tcpclient) {
+        DEBUG_WEBSOCKETS("[WS-Client] creating AsyncClient class failed!\n");
+        return;
+    }
+
+    tcpclient->onDisconnect([](void *obj, AsyncClient* c) {
+                c->free();
+                delete c;
+            });
+
+    tcpclient->onConnect(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) {
+                        ws->_client.tcp = new AsyncTCPbuffer(tcp);
+                        if(!ws->_client.tcp) {
+                            DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!\n");
+                            ws->connectFailedCb();
+                            return;
+                        }
+                        ws->connectedCb();
+                    }, this, std::placeholders::_2));
+
+    tcpclient->onError(std::bind([](WebSocketsClient * ws , AsyncClient * tcp) {
+                        ws->connectFailedCb();
+
+                        // reconnect
+                        ws->asyncConnect();
+                    }, this, std::placeholders::_2));
+
+    if(!tcpclient->connect(_host.c_str(), _port)) {
+        connectFailedCb();
+        delete tcpclient;
+    }
+
+}
+
+#endif
+
+/**
+ * send heartbeat ping to server in set intervals
+ */ 
+void WebSocketsClient::handleHBPing(){
+    if (_client.pingInterval == 0) return;
+    uint32_t pi = millis() - _client.lastPing;
+    if (pi > _client.pingInterval){
+        DEBUG_WEBSOCKETS("[WS-Client] sending HB ping\n");
+        if (sendPing()) {
+            _client.lastPing = millis();
+            _client.pongReceived = false;
+        }
+    }
+
+}
+
+/**
+ * enable ping/pong heartbeat process
+ * @param pingInterval uint32_t how often ping will be sent
+ * @param pongTimeout uint32_t millis after which pong should timout if not received
+ * @param disconnectTimeoutCount uint8_t how many timeouts before disconnect, 0=> do not disconnect
+ */
+void WebSocketsClient::enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount){
+    WebSockets::enableHeartbeat(&_client, pingInterval, pongTimeout, disconnectTimeoutCount);
+}
+
+/**
+ * disable ping/pong heartbeat process
+ */
+void WebSocketsClient::disableHeartbeat(){
+    _client.pingInterval = 0;
+}
\ No newline at end of file
diff --git a/libraries/WebSockets_ID549/src/WebSocketsClient.h b/libraries/WebSockets_ID549/src/WebSocketsClient.h
new file mode 100644
index 0000000000000000000000000000000000000000..47fe8c813ef97b2033ae73665ce1fdc3589ea829
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/WebSocketsClient.h
@@ -0,0 +1,141 @@
+/**
+ * @file WebSocketsClient.h
+ * @date 20.05.2015
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the WebSockets for Arduino.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef WEBSOCKETSCLIENT_H_
+#define WEBSOCKETSCLIENT_H_
+
+#include "WebSockets.h"
+
+class WebSocketsClient: private WebSockets {
+    public:
+#ifdef __AVR__
+        typedef void (*WebSocketClientEvent)(WStype_t type, uint8_t * payload, size_t length);
+#else
+        typedef std::function<void (WStype_t type, uint8_t * payload, size_t length)> WebSocketClientEvent;
+#endif
+
+
+        WebSocketsClient(void);
+        virtual ~WebSocketsClient(void);
+
+        void begin(const char *host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
+        void begin(String host, uint16_t port, String url = "/", String protocol = "arduino");
+        void begin(IPAddress host, uint16_t port, const char * url = "/", const char * protocol = "arduino");
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+        void beginSSL(const char *host, uint16_t port, const char * url = "/", const char * = "", const char * protocol = "arduino");
+        void beginSSL(String host, uint16_t port, String url = "/", String fingerprint = "", String protocol = "arduino");
+#endif
+
+        void beginSocketIO(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
+        void beginSocketIO(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+        void beginSocketIOSSL(const char *host, uint16_t port, const char * url = "/socket.io/?EIO=3", const char * protocol = "arduino");
+        void beginSocketIOSSL(String host, uint16_t port, String url = "/socket.io/?EIO=3", String protocol = "arduino");
+#endif
+
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+        void loop(void);
+#else
+        // Async interface not need a loop call
+        void loop(void) __attribute__ ((deprecated)) {}
+#endif
+
+        void onEvent(WebSocketClientEvent cbEvent);
+
+        bool sendTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
+        bool sendTXT(const uint8_t * payload, size_t length = 0);
+        bool sendTXT(char * payload, size_t length = 0, bool headerToPayload = false);
+        bool sendTXT(const char * payload, size_t length = 0);
+        bool sendTXT(String & payload);
+
+        bool sendBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
+        bool sendBIN(const uint8_t * payload, size_t length);
+
+        bool sendPing(uint8_t * payload = NULL, size_t length = 0);
+        bool sendPing(String & payload);
+
+        void disconnect(void);
+
+        void setAuthorization(const char * user, const char * password);
+        void setAuthorization(const char * auth);
+	
+        void setExtraHeaders(const char * extraHeaders = NULL);
+
+        void setReconnectInterval(unsigned long time);
+
+        void enableHeartbeat(uint32_t pingInterval, uint32_t pongTimeout, uint8_t disconnectTimeoutCount);
+        void disableHeartbeat();        
+
+    protected:
+        String _host;
+        uint16_t _port;
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+        String _fingerprint;
+#endif
+        WSclient_t _client;
+
+        WebSocketClientEvent _cbEvent;
+
+        unsigned long _lastConnectionFail;
+        unsigned long _reconnectInterval;
+
+        void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
+
+        void clientDisconnect(WSclient_t * client);
+        bool clientIsConnected(WSclient_t * client);
+
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+        void handleClientData(void);
+#endif
+
+        void sendHeader(WSclient_t * client);
+        void handleHeader(WSclient_t * client, String * headerLine);
+
+        void connectedCb();
+        void connectFailedCb();
+
+        void handleHBPing(); // send ping in specified intervals
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+        void asyncConnect();
+#endif
+
+        /**
+         * called for sending a Event to the app
+         * @param type WStype_t
+         * @param payload uint8_t *
+         * @param length size_t
+         */
+        virtual void runCbEvent(WStype_t type, uint8_t * payload, size_t length) {
+            if(_cbEvent) {
+                _cbEvent(type, payload, length);
+            }
+        }
+
+};
+
+#endif /* WEBSOCKETSCLIENT_H_ */
diff --git a/libraries/WebSockets_ID549/src/WebSocketsServer.cpp b/libraries/WebSockets_ID549/src/WebSocketsServer.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e1752f2cfd5b27c93e1c5ccca821aa4e465cd37e
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/WebSocketsServer.cpp
@@ -0,0 +1,873 @@
+/**
+ * @file WebSocketsServer.cpp
+ * @date 20.05.2015
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the WebSockets for Arduino.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "WebSockets.h"
+#include "WebSocketsServer.h"
+
+WebSocketsServer::WebSocketsServer(uint16_t port, String origin, String protocol) {
+    _port = port;
+    _origin = origin;
+    _protocol = protocol;
+    _runnning = false;
+
+    _server = new WEBSOCKETS_NETWORK_SERVER_CLASS(port);
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+    _server->onClient([](void *s, AsyncClient* c){
+        ((WebSocketsServer*)s)->newClient(new AsyncTCPbuffer(c));
+    }, this);
+#endif
+
+    _cbEvent = NULL;
+
+    _httpHeaderValidationFunc = NULL;
+    _mandatoryHttpHeaders = NULL;
+    _mandatoryHttpHeaderCount = 0;
+
+    memset(&_clients[0], 0x00, (sizeof(WSclient_t) * WEBSOCKETS_SERVER_CLIENT_MAX));
+}
+
+
+WebSocketsServer::~WebSocketsServer() {
+    // disconnect all clients
+	close();
+
+    if (_mandatoryHttpHeaders)
+        delete[] _mandatoryHttpHeaders;
+
+    _mandatoryHttpHeaderCount = 0;
+}
+
+/**
+ * called to initialize the Websocket server
+ */
+void WebSocketsServer::begin(void) {
+    WSclient_t * client;
+
+    // init client storage
+    for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
+        client = &_clients[i];
+
+        client->num = i;
+        client->status = WSC_NOT_CONNECTED;
+        client->tcp = NULL;
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+        client->isSSL = false;
+        client->ssl = NULL;
+#endif
+        client->cUrl = "";
+        client->cCode = 0;
+        client->cKey = "";
+        client->cProtocol = "";
+        client->cVersion = 0;
+        client->cIsUpgrade = false;
+        client->cIsWebsocket = false;
+
+        client->base64Authorization = "";
+
+        client->cWsRXsize = 0;
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+        client->cHttpLine = "";
+#endif
+    }
+
+#ifdef ESP8266
+    randomSeed(RANDOM_REG32);
+#elif defined(ESP32)
+    #define DR_REG_RNG_BASE 0x3ff75144
+    randomSeed(READ_PERI_REG(DR_REG_RNG_BASE));
+#else
+    // TODO find better seed
+    randomSeed(millis());
+#endif
+
+    _runnning = true;
+    _server->begin();
+
+    DEBUG_WEBSOCKETS("[WS-Server] Server Started.\n");
+}
+
+void WebSocketsServer::close(void) {
+	_runnning = false;
+	 disconnect();
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+    _server->close();
+#elif (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+    _server->end();
+#else
+    // TODO how to close server?
+#endif
+
+}
+
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+/**
+ * called in arduino loop
+ */
+void WebSocketsServer::loop(void) {
+	if(_runnning) {
+		handleNewClients();
+		handleClientData();
+	}
+}
+#endif
+
+/**
+ * set callback function
+ * @param cbEvent WebSocketServerEvent
+ */
+void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) {
+    _cbEvent = cbEvent;
+}
+
+/*
+ * Sets the custom http header validator function
+ * @param httpHeaderValidationFunc WebSocketServerHttpHeaderValFunc ///< pointer to the custom http header validation function
+ * @param mandatoryHttpHeaders[] const char* ///< the array of named http headers considered to be mandatory / must be present in order for websocket upgrade to succeed
+ * @param mandatoryHttpHeaderCount size_t ///< the number of items in the mandatoryHttpHeaders array
+ */
+void WebSocketsServer::onValidateHttpHeader(
+	WebSocketServerHttpHeaderValFunc validationFunc,
+	const char* mandatoryHttpHeaders[],
+	size_t mandatoryHttpHeaderCount)
+{
+	_httpHeaderValidationFunc = validationFunc;
+
+	if (_mandatoryHttpHeaders)
+		delete[] _mandatoryHttpHeaders;
+
+	_mandatoryHttpHeaderCount = mandatoryHttpHeaderCount;
+	_mandatoryHttpHeaders = new String[_mandatoryHttpHeaderCount];
+
+	for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
+		_mandatoryHttpHeaders[i] = mandatoryHttpHeaders[i];
+	}
+}
+
+/*
+ * send text data to client
+ * @param num uint8_t client id
+ * @param payload uint8_t *
+ * @param length size_t
+ * @param headerToPayload bool  (see sendFrame for more details)
+ * @return true if ok
+ */
+bool WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
+    if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
+        return false;
+    }
+    if(length == 0) {
+        length = strlen((const char *) payload);
+    }
+    WSclient_t * client = &_clients[num];
+    if(clientIsConnected(client)) {
+        return sendFrame(client, WSop_text, payload, length, true, headerToPayload);
+    }
+    return false;
+}
+
+bool WebSocketsServer::sendTXT(uint8_t num, const uint8_t * payload, size_t length) {
+    return sendTXT(num, (uint8_t *) payload, length);
+}
+
+bool WebSocketsServer::sendTXT(uint8_t num, char * payload, size_t length, bool headerToPayload) {
+    return sendTXT(num, (uint8_t *) payload, length, headerToPayload);
+}
+
+bool WebSocketsServer::sendTXT(uint8_t num, const char * payload, size_t length) {
+    return sendTXT(num, (uint8_t *) payload, length);
+}
+
+bool WebSocketsServer::sendTXT(uint8_t num, String & payload) {
+    return sendTXT(num, (uint8_t *) payload.c_str(), payload.length());
+}
+
+/**
+ * send text data to client all
+ * @param payload uint8_t *
+ * @param length size_t
+ * @param headerToPayload bool  (see sendFrame for more details)
+ * @return true if ok
+ */
+bool WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length, bool headerToPayload) {
+    WSclient_t * client;
+    bool ret = true;
+    if(length == 0) {
+        length = strlen((const char *) payload);
+    }
+
+    for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
+        client = &_clients[i];
+        if(clientIsConnected(client)) {
+            if(!sendFrame(client, WSop_text, payload, length, true, headerToPayload)) {
+                ret = false;
+            }
+        }
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+        delay(0);
+#endif
+    }
+    return ret;
+}
+
+bool WebSocketsServer::broadcastTXT(const uint8_t * payload, size_t length) {
+    return broadcastTXT((uint8_t *) payload, length);
+}
+
+bool WebSocketsServer::broadcastTXT(char * payload, size_t length, bool headerToPayload) {
+    return broadcastTXT((uint8_t *) payload, length, headerToPayload);
+}
+
+bool WebSocketsServer::broadcastTXT(const char * payload, size_t length) {
+    return broadcastTXT((uint8_t *) payload, length);
+}
+
+bool WebSocketsServer::broadcastTXT(String & payload) {
+    return broadcastTXT((uint8_t *) payload.c_str(), payload.length());
+}
+
+/**
+ * send binary data to client
+ * @param num uint8_t client id
+ * @param payload uint8_t *
+ * @param length size_t
+ * @param headerToPayload bool  (see sendFrame for more details)
+ * @return true if ok
+ */
+bool WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload) {
+    if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
+        return false;
+    }
+    WSclient_t * client = &_clients[num];
+    if(clientIsConnected(client)) {
+        return sendFrame(client, WSop_binary, payload, length, true, headerToPayload);
+    }
+    return false;
+}
+
+bool WebSocketsServer::sendBIN(uint8_t num, const uint8_t * payload, size_t length) {
+    return sendBIN(num, (uint8_t *) payload, length);
+}
+
+/**
+ * send binary data to client all
+ * @param payload uint8_t *
+ * @param length size_t
+ * @param headerToPayload bool  (see sendFrame for more details)
+ * @return true if ok
+ */
+bool WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload) {
+    WSclient_t * client;
+    bool ret = true;
+    for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
+        client = &_clients[i];
+        if(clientIsConnected(client)) {
+            if(!sendFrame(client, WSop_binary, payload, length, true, headerToPayload)) {
+                ret = false;
+            }
+        }
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+        delay(0);
+#endif
+    }
+    return ret;
+}
+
+bool WebSocketsServer::broadcastBIN(const uint8_t * payload, size_t length) {
+    return broadcastBIN((uint8_t *) payload, length);
+}
+
+
+/**
+ * sends a WS ping to Client
+ * @param num uint8_t client id
+ * @param payload uint8_t *
+ * @param length size_t
+ * @return true if ping is send out
+ */
+bool WebSocketsServer::sendPing(uint8_t num, uint8_t * payload, size_t length) {
+    if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
+        return false;
+    }
+    WSclient_t * client = &_clients[num];
+    if(clientIsConnected(client)) {
+        return sendFrame(client, WSop_ping, payload, length);
+    }
+    return false;
+}
+
+bool WebSocketsServer::sendPing(uint8_t num, String & payload) {
+    return sendPing(num, (uint8_t *) payload.c_str(), payload.length());
+}
+
+/**
+ *  sends a WS ping to all Client
+ * @param payload uint8_t *
+ * @param length size_t
+ * @return true if ping is send out
+ */
+bool WebSocketsServer::broadcastPing(uint8_t * payload, size_t length) {
+    WSclient_t * client;
+    bool ret = true;
+    for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
+        client = &_clients[i];
+        if(clientIsConnected(client)) {
+            if(!sendFrame(client, WSop_ping, payload, length)) {
+                ret = false;
+            }
+        }
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+        delay(0);
+#endif
+    }
+    return ret;
+}
+
+bool WebSocketsServer::broadcastPing(String & payload) {
+    return broadcastPing((uint8_t *) payload.c_str(), payload.length());
+}
+
+
+/**
+ * disconnect all clients
+ */
+void WebSocketsServer::disconnect(void) {
+    WSclient_t * client;
+    for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
+        client = &_clients[i];
+        if(clientIsConnected(client)) {
+            WebSockets::clientDisconnect(client, 1000);
+        }
+    }
+}
+
+/**
+ * disconnect one client
+ * @param num uint8_t client id
+ */
+void WebSocketsServer::disconnect(uint8_t num) {
+    if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) {
+        return;
+    }
+    WSclient_t * client = &_clients[num];
+    if(clientIsConnected(client)) {
+        WebSockets::clientDisconnect(client, 1000);
+    }
+}
+
+
+/*
+ * set the Authorization for the http request
+ * @param user const char *
+ * @param password const char *
+ */
+void WebSocketsServer::setAuthorization(const char * user, const char * password) {
+    if(user && password) {
+        String auth = user;
+        auth += ":";
+        auth += password;
+        _base64Authorization = base64_encode((uint8_t *)auth.c_str(), auth.length());
+    }
+}
+
+/**
+ * set the Authorizatio for the http request
+ * @param auth const char * base64
+ */
+void WebSocketsServer::setAuthorization(const char * auth) {
+    if(auth) {
+        _base64Authorization = auth;
+    }
+}
+
+/**
+ * count the connected clients (optional ping them)
+ * @param ping bool ping the connected clients
+ */
+int WebSocketsServer::connectedClients(bool ping) {
+    WSclient_t * client;
+    int count = 0;
+	for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
+		client = &_clients[i];
+		if(client->status == WSC_CONNECTED) {
+			if(ping != true || sendPing(i)) {
+				count++;
+			}
+		}
+	}
+    return count;
+}
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+/**
+ * get an IP for a client
+ * @param num uint8_t client id
+ * @return IPAddress
+ */
+IPAddress WebSocketsServer::remoteIP(uint8_t num) {
+    if(num < WEBSOCKETS_SERVER_CLIENT_MAX) {
+        WSclient_t * client = &_clients[num];
+        if(clientIsConnected(client)) {
+            return client->tcp->remoteIP();
+        }
+    }
+
+    return IPAddress();
+}
+#endif
+
+//#################################################################################
+//#################################################################################
+//#################################################################################
+
+/**
+ * handle new client connection
+ * @param client
+ */
+bool WebSocketsServer::newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient) {
+    WSclient_t * client;
+    // search free list entry for client
+    for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
+        client = &_clients[i];
+
+        // state is not connected or tcp connection is lost
+        if(!clientIsConnected(client)) {
+
+            client->tcp = TCPclient;
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+            client->isSSL = false;
+            client->tcp->setNoDelay(true);
+#endif
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+            // set Timeout for readBytesUntil and readStringUntil
+            client->tcp->setTimeout(WEBSOCKETS_TCP_TIMEOUT);
+#endif
+            client->status = WSC_HEADER;
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+            IPAddress ip = client->tcp->remoteIP();
+            DEBUG_WEBSOCKETS("[WS-Server][%d] new client from %d.%d.%d.%d\n", client->num, ip[0], ip[1], ip[2], ip[3]);
+#else
+            DEBUG_WEBSOCKETS("[WS-Server][%d] new client\n", client->num);
+#endif
+
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+            client->tcp->onDisconnect(std::bind([](WebSocketsServer * server, AsyncTCPbuffer * obj, WSclient_t * client) -> bool {
+                DEBUG_WEBSOCKETS("[WS-Server][%d] Disconnect client\n", client->num);
+
+                AsyncTCPbuffer ** sl = &server->_clients[client->num].tcp;
+                if(*sl == obj) {
+                    client->status = WSC_NOT_CONNECTED;
+                    *sl = NULL;
+                }
+                return true;
+            },  this, std::placeholders::_1, client));
+
+
+            client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine)));
+#endif
+
+            return true;
+            break;
+        }
+    }
+    return false;
+}
+
+/**
+ *
+ * @param client WSclient_t *  ptr to the client struct
+ * @param opcode WSopcode_t
+ * @param payload  uint8_t *
+ * @param length size_t
+ */
+void WebSocketsServer::messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin) {
+    WStype_t type = WStype_ERROR;
+
+    switch(opcode) {
+        case WSop_text:
+            type = fin ? WStype_TEXT : WStype_FRAGMENT_TEXT_START;
+            break;
+        case WSop_binary:
+            type = fin ? WStype_BIN : WStype_FRAGMENT_BIN_START;
+            break;
+        case WSop_continuation:
+            type = fin ? WStype_FRAGMENT_FIN : WStype_FRAGMENT;
+            break;
+        case WSop_close:
+        case WSop_ping:
+        case WSop_pong:
+        default:
+            break;
+    }
+
+    runCbEvent(client->num, type, payload, length);
+
+}
+
+/**
+ * Disconnect an client
+ * @param client WSclient_t *  ptr to the client struct
+ */
+void WebSocketsServer::clientDisconnect(WSclient_t * client) {
+
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+    if(client->isSSL && client->ssl) {
+        if(client->ssl->connected()) {
+            client->ssl->flush();
+            client->ssl->stop();
+        }
+        delete client->ssl;
+        client->ssl = NULL;
+        client->tcp = NULL;
+    }
+#endif
+
+    if(client->tcp) {
+        if(client->tcp->connected()) {
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+            client->tcp->flush();
+#endif
+            client->tcp->stop();
+        }
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+        client->status = WSC_NOT_CONNECTED;
+#else
+        delete client->tcp;
+#endif
+        client->tcp = NULL;
+    }
+
+    client->cUrl = "";
+    client->cKey = "";
+    client->cProtocol = "";
+    client->cVersion = 0;
+    client->cIsUpgrade = false;
+    client->cIsWebsocket = false;
+
+    client->cWsRXsize = 0;
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+    client->cHttpLine = "";
+#endif
+
+    client->status = WSC_NOT_CONNECTED;
+
+    DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num);
+
+    runCbEvent(client->num, WStype_DISCONNECTED, NULL, 0);
+
+}
+
+/**
+ * get client state
+ * @param client WSclient_t *  ptr to the client struct
+ * @return true = connected
+ */
+bool WebSocketsServer::clientIsConnected(WSclient_t * client) {
+
+    if(!client->tcp) {
+        return false;
+    }
+
+    if(client->tcp->connected()) {
+        if(client->status != WSC_NOT_CONNECTED) {
+            return true;
+        }
+    } else {
+        // client lost
+        if(client->status != WSC_NOT_CONNECTED) {
+            DEBUG_WEBSOCKETS("[WS-Server][%d] client connection lost.\n", client->num);
+            // do cleanup
+            clientDisconnect(client);
+        }
+    }
+
+    if(client->tcp) {
+        // do cleanup
+        DEBUG_WEBSOCKETS("[WS-Server][%d] client list cleanup.\n", client->num);
+        clientDisconnect(client);
+    }
+
+    return false;
+}
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+/**
+ * Handle incoming Connection Request
+ */
+void WebSocketsServer::handleNewClients(void) {
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+    while(_server->hasClient()) {
+#endif
+        bool ok = false;
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+        // store new connection
+        WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available());
+#else
+        WEBSOCKETS_NETWORK_CLASS * tcpClient = new WEBSOCKETS_NETWORK_CLASS(_server->available());
+#endif
+
+        if(!tcpClient) {
+            DEBUG_WEBSOCKETS("[WS-Client] creating Network class failed!");
+            return;
+        }
+
+        ok = newClient(tcpClient);
+
+        if(!ok) {
+            // no free space to handle client
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+            IPAddress ip = tcpClient->remoteIP();
+            DEBUG_WEBSOCKETS("[WS-Server] no free space new client from %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
+#else
+            DEBUG_WEBSOCKETS("[WS-Server] no free space new client\n");
+#endif
+            tcpClient->stop();
+        }
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+        delay(0);
+    }
+#endif
+
+}
+
+
+/**
+ * Handel incomming data from Client
+ */
+void WebSocketsServer::handleClientData(void) {
+
+    WSclient_t * client;
+    for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) {
+        client = &_clients[i];
+        if(clientIsConnected(client)) {
+            int len = client->tcp->available();
+            if(len > 0) {
+                //DEBUG_WEBSOCKETS("[WS-Server][%d][handleClientData] len: %d\n", client->num, len);
+                switch(client->status) {
+                    case WSC_HEADER:
+                    {
+                        String headerLine = client->tcp->readStringUntil('\n');
+                        handleHeader(client, &headerLine);
+                    }
+                        break;
+                    case WSC_CONNECTED:
+                        WebSockets::handleWebsocket(client);
+                        break;
+                    default:
+                        WebSockets::clientDisconnect(client, 1002);
+                        break;
+                }
+            }
+        }
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266)
+        delay(0);
+#endif
+    }
+}
+#endif
+
+/*
+ * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
+ * @param headerName String ///< the name of the header being checked
+ */
+bool WebSocketsServer::hasMandatoryHeader(String headerName) {
+	for (size_t i = 0; i < _mandatoryHttpHeaderCount; i++) {
+		if (_mandatoryHttpHeaders[i].equalsIgnoreCase(headerName))
+			return true;
+	}
+	return false;
+}
+
+
+/**
+ * handles http header reading for WebSocket upgrade
+ * @param client WSclient_t * ///< pointer to the client struct
+ * @param headerLine String ///< the header being read / processed
+ */
+void WebSocketsServer::handleHeader(WSclient_t * client, String * headerLine) {
+
+	static const char * NEW_LINE = "\r\n";
+
+	headerLine->trim(); // remove \r
+
+	if(headerLine->length() > 0) {
+		DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] RX: %s\n", client->num, headerLine->c_str());
+
+		// websocket requests always start with GET see rfc6455
+		if(headerLine->startsWith("GET ")) {
+
+			// cut URL out
+			client->cUrl = headerLine->substring(4, headerLine->indexOf(' ', 4));
+
+			//reset non-websocket http header validation state for this client
+			client->cHttpHeadersValid = true;
+			client->cMandatoryHeadersCount = 0;
+
+		} else if(headerLine->indexOf(':')) {
+			String headerName = headerLine->substring(0, headerLine->indexOf(':'));
+			String headerValue = headerLine->substring(headerLine->indexOf(':') + 1);
+
+			// remove space in the beginning (RFC2616)
+			if(headerValue[0] == ' ') {
+				headerValue.remove(0, 1);
+			}
+
+			if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Connection"))) {
+				headerValue.toLowerCase();
+				if(headerValue.indexOf(WEBSOCKETS_STRING("upgrade")) >= 0) {
+					client->cIsUpgrade = true;
+				}
+			} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Upgrade"))) {
+				if(headerValue.equalsIgnoreCase(WEBSOCKETS_STRING("websocket"))) {
+					client->cIsWebsocket = true;
+				}
+			} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Version"))) {
+				client->cVersion = headerValue.toInt();
+			} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Key"))) {
+				client->cKey = headerValue;
+				client->cKey.trim(); // see rfc6455
+			} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Protocol"))) {
+				client->cProtocol = headerValue;
+			} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Sec-WebSocket-Extensions"))) {
+				client->cExtensions = headerValue;
+			} else if(headerName.equalsIgnoreCase(WEBSOCKETS_STRING("Authorization"))) {
+				client->base64Authorization = headerValue;
+			} else {
+				client->cHttpHeadersValid &= execHttpHeaderValidation(headerName, headerValue);
+				if(_mandatoryHttpHeaderCount > 0 && hasMandatoryHeader(headerName)) {
+					client->cMandatoryHeadersCount++;
+				}
+			}
+
+		} else {
+			DEBUG_WEBSOCKETS("[WS-Client][handleHeader] Header error (%s)\n", headerLine->c_str());
+		}
+
+        (*headerLine) = "";
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC)
+        client->tcp->readStringUntil('\n', &(client->cHttpLine), std::bind(&WebSocketsServer::handleHeader, this, client, &(client->cHttpLine)));
+#endif
+    } else {
+
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num);
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cURL: %s\n", client->num, client->cUrl.c_str());
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cIsUpgrade: %d\n", client->num, client->cIsUpgrade);
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cIsWebsocket: %d\n", client->num, client->cIsWebsocket);
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cKey: %s\n", client->num, client->cKey.c_str());
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cProtocol: %s\n", client->num, client->cProtocol.c_str());
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cExtensions: %s\n", client->num, client->cExtensions.c_str());
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cVersion: %d\n", client->num, client->cVersion);
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - base64Authorization: %s\n", client->num, client->base64Authorization.c_str());
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cHttpHeadersValid: %d\n", client->num, client->cHttpHeadersValid);
+        DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - cMandatoryHeadersCount: %d\n", client->num, client->cMandatoryHeadersCount);
+
+        bool ok = (client->cIsUpgrade && client->cIsWebsocket);
+
+		if(ok) {
+			if(client->cUrl.length() == 0) {
+				ok = false;
+			}
+			if(client->cKey.length() == 0) {
+                ok = false;
+            }
+            if(client->cVersion != 13) {
+                ok = false;
+            }
+            if(!client->cHttpHeadersValid) {
+            	ok = false;
+            }
+            if (client->cMandatoryHeadersCount != _mandatoryHttpHeaderCount) {
+            	ok = false;
+            }
+        }
+
+        if(_base64Authorization.length() > 0) {
+			String auth = WEBSOCKETS_STRING("Basic ");
+			auth += _base64Authorization;
+			if(auth != client->base64Authorization) {
+				DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] HTTP Authorization failed!\n", client->num);
+				handleAuthorizationFailed(client);
+				return;
+			}
+        }
+
+        if(ok) {
+
+            DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incoming.\n", client->num);
+
+            // generate Sec-WebSocket-Accept key
+            String sKey = acceptKey(client->cKey);
+
+            DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader]  - sKey: %s\n", client->num, sKey.c_str());
+
+            client->status = WSC_CONNECTED;
+
+            String handshake = WEBSOCKETS_STRING("HTTP/1.1 101 Switching Protocols\r\n"
+                    "Server: arduino-WebSocketsServer\r\n"
+                    "Upgrade: websocket\r\n"
+                    "Connection: Upgrade\r\n"
+                    "Sec-WebSocket-Version: 13\r\n"
+                    "Sec-WebSocket-Accept: ");
+			handshake += sKey + NEW_LINE;
+
+            if(_origin.length() > 0) {
+                handshake += WEBSOCKETS_STRING("Access-Control-Allow-Origin: ");
+                handshake +=_origin + NEW_LINE;
+            }
+
+            if(client->cProtocol.length() > 0) {
+            	handshake += WEBSOCKETS_STRING("Sec-WebSocket-Protocol: ");
+            	handshake +=_protocol + NEW_LINE;
+            }
+
+            // header end
+            handshake += NEW_LINE;
+
+            DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] handshake %s",  client->num, (uint8_t*)handshake.c_str());
+
+            write(client, (uint8_t*)handshake.c_str(), handshake.length());
+
+            headerDone(client);
+
+            // send ping
+            WebSockets::sendFrame(client, WSop_ping);
+
+            runCbEvent(client->num, WStype_CONNECTED, (uint8_t *) client->cUrl.c_str(), client->cUrl.length());
+
+        } else {
+            handleNonWebsocketConnection(client);
+        }
+    }
+}
+
+
+
diff --git a/libraries/WebSockets_ID549/src/WebSocketsServer.h b/libraries/WebSockets_ID549/src/WebSocketsServer.h
new file mode 100644
index 0000000000000000000000000000000000000000..e010488a1294805ed7ea7a30ae610f3912792b1b
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/WebSocketsServer.h
@@ -0,0 +1,212 @@
+/**
+ * @file WebSocketsServer.h
+ * @date 20.05.2015
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the WebSockets for Arduino.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef WEBSOCKETSSERVER_H_
+#define WEBSOCKETSSERVER_H_
+
+#include "WebSockets.h"
+
+#ifndef WEBSOCKETS_SERVER_CLIENT_MAX
+#define WEBSOCKETS_SERVER_CLIENT_MAX  (5)
+#endif
+
+
+
+
+class WebSocketsServer: protected WebSockets {
+public:
+
+#ifdef __AVR__
+        typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length);
+        typedef bool (*WebSocketServerHttpHeaderValFunc)(String headerName, String headerValue);
+#else
+        typedef std::function<void (uint8_t num, WStype_t type, uint8_t * payload, size_t length)> WebSocketServerEvent;
+        typedef std::function<bool (String headerName, String headerValue)> WebSocketServerHttpHeaderValFunc;
+#endif
+
+        WebSocketsServer(uint16_t port, String origin = "", String protocol = "arduino");
+        virtual ~WebSocketsServer(void);
+
+        void begin(void);
+        void close(void);
+
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+        void loop(void);
+#else
+        // Async interface not need a loop call
+        void loop(void) __attribute__ ((deprecated)) {}
+#endif
+
+        void onEvent(WebSocketServerEvent cbEvent);
+        void onValidateHttpHeader(
+			WebSocketServerHttpHeaderValFunc validationFunc,
+			const char* mandatoryHttpHeaders[],
+			size_t mandatoryHttpHeaderCount);
+
+
+        bool sendTXT(uint8_t num, uint8_t * payload, size_t length = 0, bool headerToPayload = false);
+        bool sendTXT(uint8_t num, const uint8_t * payload, size_t length = 0);
+        bool sendTXT(uint8_t num, char * payload, size_t length = 0, bool headerToPayload = false);
+        bool sendTXT(uint8_t num, const char * payload, size_t length = 0);
+        bool sendTXT(uint8_t num, String & payload);
+
+        bool broadcastTXT(uint8_t * payload, size_t length = 0, bool headerToPayload = false);
+        bool broadcastTXT(const uint8_t * payload, size_t length = 0);
+        bool broadcastTXT(char * payload, size_t length = 0, bool headerToPayload = false);
+        bool broadcastTXT(const char * payload, size_t length = 0);
+        bool broadcastTXT(String & payload);
+
+        bool sendBIN(uint8_t num, uint8_t * payload, size_t length, bool headerToPayload = false);
+        bool sendBIN(uint8_t num, const uint8_t * payload, size_t length);
+
+        bool broadcastBIN(uint8_t * payload, size_t length, bool headerToPayload = false);
+        bool broadcastBIN(const uint8_t * payload, size_t length);
+
+        bool sendPing(uint8_t num, uint8_t * payload = NULL, size_t length = 0);
+        bool sendPing(uint8_t num, String & payload);
+
+        bool broadcastPing(uint8_t * payload = NULL, size_t length = 0);
+        bool broadcastPing(String & payload);
+
+        void disconnect(void);
+        void disconnect(uint8_t num);
+
+        void setAuthorization(const char * user, const char * password);
+        void setAuthorization(const char * auth);
+
+        int connectedClients(bool ping = false);
+
+#if (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP8266_ASYNC) || (WEBSOCKETS_NETWORK_TYPE == NETWORK_ESP32)
+        IPAddress remoteIP(uint8_t num);
+#endif
+
+protected:
+        uint16_t _port;
+        String _origin;
+        String _protocol;
+        String _base64Authorization; ///< Base64 encoded Auth request
+        String * _mandatoryHttpHeaders;
+        size_t _mandatoryHttpHeaderCount;
+
+        WEBSOCKETS_NETWORK_SERVER_CLASS * _server;
+
+        WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX];
+
+        WebSocketServerEvent _cbEvent;
+        WebSocketServerHttpHeaderValFunc _httpHeaderValidationFunc;
+
+        bool _runnning;
+
+        bool newClient(WEBSOCKETS_NETWORK_CLASS * TCPclient);
+
+        void messageReceived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length, bool fin);
+
+        void clientDisconnect(WSclient_t * client);
+        bool clientIsConnected(WSclient_t * client);
+
+#if (WEBSOCKETS_NETWORK_TYPE != NETWORK_ESP8266_ASYNC)
+        void handleNewClients(void);
+        void handleClientData(void);
+#endif
+
+        void handleHeader(WSclient_t * client, String * headerLine);
+
+        /**
+         * called if a non Websocket connection is coming in.
+         * Note: can be override
+         * @param client WSclient_t *  ptr to the client struct
+         */
+        virtual void handleNonWebsocketConnection(WSclient_t * client) {
+            DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num);
+            client->tcp->write("HTTP/1.1 400 Bad Request\r\n"
+                    "Server: arduino-WebSocket-Server\r\n"
+                    "Content-Type: text/plain\r\n"
+                    "Content-Length: 32\r\n"
+                    "Connection: close\r\n"
+                    "Sec-WebSocket-Version: 13\r\n"
+                    "\r\n"
+                    "This is a Websocket server only!");
+            clientDisconnect(client);
+        }
+
+        /**
+         * called if a non Authorization connection is coming in.
+         * Note: can be override
+         * @param client WSclient_t *  ptr to the client struct
+         */
+        virtual void handleAuthorizationFailed(WSclient_t *client) {
+        	 client->tcp->write("HTTP/1.1 401 Unauthorized\r\n"
+                    "Server: arduino-WebSocket-Server\r\n"
+                    "Content-Type: text/plain\r\n"
+                    "Content-Length: 45\r\n"
+                    "Connection: close\r\n"
+                    "Sec-WebSocket-Version: 13\r\n"
+                    "WWW-Authenticate: Basic realm=\"WebSocket Server\""
+                    "\r\n"
+                    "This Websocket server requires Authorization!");
+            clientDisconnect(client);
+        }
+
+        /**
+         * called for sending a Event to the app
+         * @param num uint8_t
+         * @param type WStype_t
+         * @param payload uint8_t *
+         * @param length size_t
+         */
+        virtual void runCbEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
+            if(_cbEvent) {
+                _cbEvent(num, type, payload, length);
+            }
+        }
+
+        /*
+         * Called at client socket connect handshake negotiation time for each http header that is not
+         * a websocket specific http header (not Connection, Upgrade, Sec-WebSocket-*)
+         * If the custom httpHeaderValidationFunc returns false for any headerName / headerValue passed, the
+         * socket negotiation is considered invalid and the upgrade to websockets request is denied / rejected
+         * This mechanism can be used to enable custom authentication schemes e.g. test the value
+         * of a session cookie to determine if a user is logged on / authenticated
+         */
+        virtual bool execHttpHeaderValidation(String headerName, String headerValue) {
+        	if(_httpHeaderValidationFunc) {
+        		//return the value of the custom http header validation function
+        		return _httpHeaderValidationFunc(headerName, headerValue);
+        	}
+        	//no custom http header validation so just assume all is good
+        	return true;
+        }
+
+private:
+        /*
+         * returns an indicator whether the given named header exists in the configured _mandatoryHttpHeaders collection
+         * @param headerName String ///< the name of the header being checked
+         */
+        bool hasMandatoryHeader(String headerName);
+
+};
+
+
+
+#endif /* WEBSOCKETSSERVER_H_ */
diff --git a/libraries/WebSockets_ID549/src/libb64/AUTHORS b/libraries/WebSockets_ID549/src/libb64/AUTHORS
new file mode 100644
index 0000000000000000000000000000000000000000..af68737569ae5e67bae720ffc9e46273e9befc8a
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/libb64/AUTHORS
@@ -0,0 +1,7 @@
+libb64: Base64 Encoding/Decoding Routines
+======================================
+
+Authors:
+-------
+
+Chris Venter	chris.venter@gmail.com	http://rocketpod.blogspot.com
diff --git a/libraries/WebSockets_ID549/src/libb64/LICENSE b/libraries/WebSockets_ID549/src/libb64/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..a6b56069e7fd1ad439c36b32342ccb5483a72fdb
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/libb64/LICENSE
@@ -0,0 +1,29 @@
+Copyright-Only Dedication (based on United States law) 
+or Public Domain Certification
+
+The person or persons who have associated work with this document (the
+"Dedicator" or "Certifier") hereby either (a) certifies that, to the best of
+his knowledge, the work of authorship identified is in the public domain of the
+country from which the work is published, or (b) hereby dedicates whatever
+copyright the dedicators holds in the work of authorship identified below (the
+"Work") to the public domain. A certifier, moreover, dedicates any copyright
+interest he may have in the associated work, and for these purposes, is
+described as a "dedicator" below.
+
+A certifier has taken reasonable steps to verify the copyright status of this
+work. Certifier recognizes that his good faith efforts may not shield him from
+liability if in fact the work certified is not in the public domain.
+
+Dedicator makes this dedication for the benefit of the public at large and to
+the detriment of the Dedicator's heirs and successors. Dedicator intends this
+dedication to be an overt act of relinquishment in perpetuity of all present
+and future rights under copyright law, whether vested or contingent, in the
+Work. Dedicator understands that such relinquishment of all rights includes
+the relinquishment of all rights to enforce (by lawsuit or otherwise) those
+copyrights in the Work.
+
+Dedicator recognizes that, once placed in the public domain, the Work may be
+freely reproduced, distributed, transmitted, used, modified, built upon, or
+otherwise exploited by anyone for any purpose, commercial or non-commercial,
+and in any way, including by methods that have not yet been invented or
+conceived.
\ No newline at end of file
diff --git a/libraries/WebSockets_ID549/src/libb64/cdecode.c b/libraries/WebSockets_ID549/src/libb64/cdecode.c
new file mode 100644
index 0000000000000000000000000000000000000000..e135da2490f6cc484b1f3798339cf97665b9463a
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/libb64/cdecode.c
@@ -0,0 +1,98 @@
+/*
+cdecoder.c - c source to a base64 decoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifdef ESP8266
+#include <core_esp8266_features.h>
+#endif
+
+#if defined(ESP32)
+#define CORE_HAS_LIBB64
+#endif
+
+#ifndef CORE_HAS_LIBB64
+#include "cdecode_inc.h"
+
+int base64_decode_value(char value_in)
+{
+	static const char decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51};
+	static const char decoding_size = sizeof(decoding);
+	value_in -= 43;
+	if (value_in < 0 || value_in > decoding_size) return -1;
+	return decoding[(int)value_in];
+}
+
+void base64_init_decodestate(base64_decodestate* state_in)
+{
+	state_in->step = step_a;
+	state_in->plainchar = 0;
+}
+
+int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in)
+{
+	const char* codechar = code_in;
+	char* plainchar = plaintext_out;
+	char fragment;
+
+	*plainchar = state_in->plainchar;
+
+	switch (state_in->step)
+	{
+		while (1)
+		{
+	case step_a:
+			do {
+				if (codechar == code_in+length_in)
+				{
+					state_in->step = step_a;
+					state_in->plainchar = *plainchar;
+					return plainchar - plaintext_out;
+				}
+				fragment = (char)base64_decode_value(*codechar++);
+			} while (fragment < 0);
+			*plainchar    = (fragment & 0x03f) << 2;
+	case step_b:
+			do {
+				if (codechar == code_in+length_in)
+				{
+					state_in->step = step_b;
+					state_in->plainchar = *plainchar;
+					return plainchar - plaintext_out;
+				}
+				fragment = (char)base64_decode_value(*codechar++);
+			} while (fragment < 0);
+			*plainchar++ |= (fragment & 0x030) >> 4;
+			*plainchar    = (fragment & 0x00f) << 4;
+	case step_c:
+			do {
+				if (codechar == code_in+length_in)
+				{
+					state_in->step = step_c;
+					state_in->plainchar = *plainchar;
+					return plainchar - plaintext_out;
+				}
+				fragment = (char)base64_decode_value(*codechar++);
+			} while (fragment < 0);
+			*plainchar++ |= (fragment & 0x03c) >> 2;
+			*plainchar    = (fragment & 0x003) << 6;
+	case step_d:
+			do {
+				if (codechar == code_in+length_in)
+				{
+					state_in->step = step_d;
+					state_in->plainchar = *plainchar;
+					return plainchar - plaintext_out;
+				}
+				fragment = (char)base64_decode_value(*codechar++);
+			} while (fragment < 0);
+			*plainchar++   |= (fragment & 0x03f);
+		}
+	}
+	/* control should not reach here */
+	return plainchar - plaintext_out;
+}
+
+#endif
diff --git a/libraries/WebSockets_ID549/src/libb64/cdecode_inc.h b/libraries/WebSockets_ID549/src/libb64/cdecode_inc.h
new file mode 100644
index 0000000000000000000000000000000000000000..d0d7f489d73b1fe312ba1c4254c0563c7cf1fa76
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/libb64/cdecode_inc.h
@@ -0,0 +1,28 @@
+/*
+cdecode.h - c header for a base64 decoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CDECODE_H
+#define BASE64_CDECODE_H
+
+typedef enum
+{
+	step_a, step_b, step_c, step_d
+} base64_decodestep;
+
+typedef struct
+{
+	base64_decodestep step;
+	char plainchar;
+} base64_decodestate;
+
+void base64_init_decodestate(base64_decodestate* state_in);
+
+int base64_decode_value(char value_in);
+
+int base64_decode_block(const char* code_in, const int length_in, char* plaintext_out, base64_decodestate* state_in);
+
+#endif /* BASE64_CDECODE_H */
diff --git a/libraries/WebSockets_ID549/src/libb64/cencode.c b/libraries/WebSockets_ID549/src/libb64/cencode.c
new file mode 100644
index 0000000000000000000000000000000000000000..afe1463c62f8ae1c0df02cf33eb8dc649e4d8259
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/libb64/cencode.c
@@ -0,0 +1,119 @@
+/*
+cencoder.c - c source to a base64 encoding algorithm implementation
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifdef ESP8266
+#include <core_esp8266_features.h>
+#endif
+
+#if defined(ESP32)
+#define CORE_HAS_LIBB64
+#endif
+
+#ifndef CORE_HAS_LIBB64
+#include "cencode_inc.h"
+
+const int CHARS_PER_LINE = 72;
+
+void base64_init_encodestate(base64_encodestate* state_in)
+{
+	state_in->step = step_A;
+	state_in->result = 0;
+	state_in->stepcount = 0;
+}
+
+char base64_encode_value(char value_in)
+{
+	static const char* encoding = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+	if (value_in > 63) return '=';
+	return encoding[(int)value_in];
+}
+
+int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in)
+{
+	const char* plainchar = plaintext_in;
+	const char* const plaintextend = plaintext_in + length_in;
+	char* codechar = code_out;
+	char result;
+	char fragment;
+
+	result = state_in->result;
+
+	switch (state_in->step)
+	{
+		while (1)
+		{
+	case step_A:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_A;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result = (fragment & 0x0fc) >> 2;
+			*codechar++ = base64_encode_value(result);
+			result = (fragment & 0x003) << 4;
+	case step_B:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_B;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result |= (fragment & 0x0f0) >> 4;
+			*codechar++ = base64_encode_value(result);
+			result = (fragment & 0x00f) << 2;
+	case step_C:
+			if (plainchar == plaintextend)
+			{
+				state_in->result = result;
+				state_in->step = step_C;
+				return codechar - code_out;
+			}
+			fragment = *plainchar++;
+			result |= (fragment & 0x0c0) >> 6;
+			*codechar++ = base64_encode_value(result);
+			result  = (fragment & 0x03f) >> 0;
+			*codechar++ = base64_encode_value(result);
+
+			++(state_in->stepcount);
+			if (state_in->stepcount == CHARS_PER_LINE/4)
+			{
+				*codechar++ = '\n';
+				state_in->stepcount = 0;
+			}
+		}
+	}
+	/* control should not reach here */
+	return codechar - code_out;
+}
+
+int base64_encode_blockend(char* code_out, base64_encodestate* state_in)
+{
+	char* codechar = code_out;
+
+	switch (state_in->step)
+	{
+	case step_B:
+		*codechar++ = base64_encode_value(state_in->result);
+		*codechar++ = '=';
+		*codechar++ = '=';
+		break;
+	case step_C:
+		*codechar++ = base64_encode_value(state_in->result);
+		*codechar++ = '=';
+		break;
+	case step_A:
+		break;
+	}
+	*codechar++ = 0x00;
+
+	return codechar - code_out;
+}
+
+#endif
diff --git a/libraries/WebSockets_ID549/src/libb64/cencode_inc.h b/libraries/WebSockets_ID549/src/libb64/cencode_inc.h
new file mode 100644
index 0000000000000000000000000000000000000000..c1e3464af3bb7798c0f4b05dba25f84a58fa7490
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/libb64/cencode_inc.h
@@ -0,0 +1,31 @@
+/*
+cencode.h - c header for a base64 encoding algorithm
+
+This is part of the libb64 project, and has been placed in the public domain.
+For details, see http://sourceforge.net/projects/libb64
+*/
+
+#ifndef BASE64_CENCODE_H
+#define BASE64_CENCODE_H
+
+typedef enum
+{
+	step_A, step_B, step_C
+} base64_encodestep;
+
+typedef struct
+{
+	base64_encodestep step;
+	char result;
+	int stepcount;
+} base64_encodestate;
+
+void base64_init_encodestate(base64_encodestate* state_in);
+
+char base64_encode_value(char value_in);
+
+int base64_encode_block(const char* plaintext_in, int length_in, char* code_out, base64_encodestate* state_in);
+
+int base64_encode_blockend(char* code_out, base64_encodestate* state_in);
+
+#endif /* BASE64_CENCODE_H */
diff --git a/libraries/WebSockets_ID549/src/libsha1/libsha1.c b/libraries/WebSockets_ID549/src/libsha1/libsha1.c
new file mode 100644
index 0000000000000000000000000000000000000000..fcf01c531406e34a9191dd09e76ee3922b896eae
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/libsha1/libsha1.c
@@ -0,0 +1,202 @@
+/* from valgrind tests */
+
+/* ================ sha1.c ================ */
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+
+Test Vectors (from FIPS PUB 180-1)
+"abc"
+  A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D
+"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
+  84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1
+A million repetitions of "a"
+  34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F
+*/
+
+/* #define LITTLE_ENDIAN * This should be #define'd already, if true. */
+/* #define SHA1HANDSOFF * Copies data before messing with it. */
+
+#if !defined(ESP8266) && !defined(ESP32)
+
+#define SHA1HANDSOFF
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+
+#include "libsha1.h"
+
+
+#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits))))
+
+/* blk0() and blk() perform the initial expand. */
+/* I got the idea of expanding during the round function from SSLeay */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define blk0(i) (block->l[i] = (rol(block->l[i],24)&0xFF00FF00) \
+    |(rol(block->l[i],8)&0x00FF00FF))
+#elif BYTE_ORDER == BIG_ENDIAN
+#define blk0(i) block->l[i]
+#else
+#error "Endianness not defined!"
+#endif
+#define blk(i) (block->l[i&15] = rol(block->l[(i+13)&15]^block->l[(i+8)&15] \
+    ^block->l[(i+2)&15]^block->l[i&15],1))
+
+/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */
+#define R0(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk0(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R1(v,w,x,y,z,i) z+=((w&(x^y))^y)+blk(i)+0x5A827999+rol(v,5);w=rol(w,30);
+#define R2(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0x6ED9EBA1+rol(v,5);w=rol(w,30);
+#define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30);
+#define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30);
+
+
+/* Hash a single 512-bit block. This is the core of the algorithm. */
+
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64])
+{
+    uint32_t a, b, c, d, e;
+    typedef union {
+        unsigned char c[64];
+        uint32_t l[16];
+    } CHAR64LONG16;
+#ifdef SHA1HANDSOFF
+    CHAR64LONG16 block[1];  /* use array to appear as a pointer */
+    memcpy(block, buffer, 64);
+#else
+    /* The following had better never be used because it causes the
+     * pointer-to-const buffer to be cast into a pointer to non-const.
+     * And the result is written through.  I threw a "const" in, hoping
+     * this will cause a diagnostic.
+     */
+    CHAR64LONG16* block = (const CHAR64LONG16*)buffer;
+#endif
+    /* Copy context->state[] to working vars */
+    a = state[0];
+    b = state[1];
+    c = state[2];
+    d = state[3];
+    e = state[4];
+    /* 4 rounds of 20 operations each. Loop unrolled. */
+    R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3);
+    R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7);
+    R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11);
+    R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15);
+    R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19);
+    R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23);
+    R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27);
+    R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31);
+    R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35);
+    R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39);
+    R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43);
+    R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47);
+    R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51);
+    R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55);
+    R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59);
+    R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63);
+    R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67);
+    R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71);
+    R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75);
+    R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79);
+    /* Add the working vars back into context.state[] */
+    state[0] += a;
+    state[1] += b;
+    state[2] += c;
+    state[3] += d;
+    state[4] += e;
+    /* Wipe variables */
+    a = b = c = d = e = 0;
+#ifdef SHA1HANDSOFF
+    memset(block, '\0', sizeof(block));
+#endif
+}
+
+
+/* SHA1Init - Initialize new context */
+
+void SHA1Init(SHA1_CTX* context)
+{
+    /* SHA1 initialization constants */
+    context->state[0] = 0x67452301;
+    context->state[1] = 0xEFCDAB89;
+    context->state[2] = 0x98BADCFE;
+    context->state[3] = 0x10325476;
+    context->state[4] = 0xC3D2E1F0;
+    context->count[0] = context->count[1] = 0;
+}
+
+
+/* Run your data through this. */
+
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len)
+{
+    uint32_t i, j;
+
+    j = context->count[0];
+    if ((context->count[0] += len << 3) < j)
+        context->count[1]++;
+    context->count[1] += (len>>29);
+    j = (j >> 3) & 63;
+    if ((j + len) > 63) {
+        memcpy(&context->buffer[j], data, (i = 64-j));
+        SHA1Transform(context->state, context->buffer);
+        for ( ; i + 63 < len; i += 64) {
+            SHA1Transform(context->state, &data[i]);
+        }
+        j = 0;
+    }
+    else i = 0;
+    memcpy(&context->buffer[j], &data[i], len - i);
+}
+
+
+/* Add padding and return the message digest. */
+
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context)
+{
+    unsigned i;
+    unsigned char finalcount[8];
+    unsigned char c;
+
+#if 0	/* untested "improvement" by DHR */
+    /* Convert context->count to a sequence of bytes
+     * in finalcount.  Second element first, but
+     * big-endian order within element.
+     * But we do it all backwards.
+     */
+    unsigned char *fcp = &finalcount[8];
+
+    for (i = 0; i < 2; i++)
+       {
+        uint32_t t = context->count[i];
+        int j;
+
+        for (j = 0; j < 4; t >>= 8, j++)
+	          *--fcp = (unsigned char) t;
+    }
+#else
+    for (i = 0; i < 8; i++) {
+        finalcount[i] = (unsigned char)((context->count[(i >= 4 ? 0 : 1)]
+         >> ((3-(i & 3)) * 8) ) & 255);  /* Endian independent */
+    }
+#endif
+    c = 0200;
+    SHA1Update(context, &c, 1);
+    while ((context->count[0] & 504) != 448) {
+	c = 0000;
+        SHA1Update(context, &c, 1);
+    }
+    SHA1Update(context, finalcount, 8);  /* Should cause a SHA1Transform() */
+    for (i = 0; i < 20; i++) {
+        digest[i] = (unsigned char)
+         ((context->state[i>>2] >> ((3-(i & 3)) * 8) ) & 255);
+    }
+    /* Wipe variables */
+    memset(context, '\0', sizeof(*context));
+    memset(&finalcount, '\0', sizeof(finalcount));
+}
+/* ================ end of sha1.c ================ */
+
+
+#endif
diff --git a/libraries/WebSockets_ID549/src/libsha1/libsha1.h b/libraries/WebSockets_ID549/src/libsha1/libsha1.h
new file mode 100644
index 0000000000000000000000000000000000000000..40afa61c043f6b54a05db389304e609a45ce3c64
--- /dev/null
+++ b/libraries/WebSockets_ID549/src/libsha1/libsha1.h
@@ -0,0 +1,21 @@
+/* ================ sha1.h ================ */
+/*
+SHA-1 in C
+By Steve Reid <steve@edmweb.com>
+100% Public Domain
+*/
+
+#if !defined(ESP8266) && !defined(ESP32)
+
+typedef struct {
+    uint32_t state[5];
+    uint32_t count[2];
+    unsigned char buffer[64];
+} SHA1_CTX;
+
+void SHA1Transform(uint32_t state[5], const unsigned char buffer[64]);
+void SHA1Init(SHA1_CTX* context);
+void SHA1Update(SHA1_CTX* context, const unsigned char* data, uint32_t len);
+void SHA1Final(unsigned char digest[20], SHA1_CTX* context);
+
+#endif
diff --git a/libraries/WebSockets_ID549/travis/common.sh b/libraries/WebSockets_ID549/travis/common.sh
new file mode 100644
index 0000000000000000000000000000000000000000..be959faf7f14dacf1a72cd3e777606af97e7a792
--- /dev/null
+++ b/libraries/WebSockets_ID549/travis/common.sh
@@ -0,0 +1,53 @@
+#!/bin/bash
+
+function build_sketches()
+{
+    local arduino=$1
+    local srcpath=$2
+    local platform=$3
+    local sketches=$(find $srcpath -name *.ino)
+    for sketch in $sketches; do
+        local sketchdir=$(dirname $sketch)
+        if [[ -f "$sketchdir/.$platform.skip" ]]; then
+            echo -e "\n\n ------------ Skipping $sketch ------------ \n\n";
+            continue
+        fi
+        echo -e "\n\n ------------ Building $sketch ------------ \n\n";
+        $arduino --verify $sketch;
+        local result=$?
+        if [ $result -ne 0 ]; then
+            echo "Build failed ($sketch) build verbose..."
+            $arduino --verify --verbose --preserve-temp-files $sketch
+            result=$?
+        fi
+        if [ $result -ne 0 ]; then
+            echo "Build failed ($1) $sketch"
+            return $result
+        fi
+    done
+}
+
+
+function get_core()
+{
+    echo Setup core for $1
+
+    cd $HOME/arduino_ide/hardware
+
+    if [ "$1" = "esp8266" ] ; then
+        mkdir esp8266com
+        cd esp8266com
+        git clone https://github.com/esp8266/Arduino.git esp8266
+        cd esp8266/tools
+        python get.py
+    fi
+
+    if [ "$1" = "esp32" ] ; then
+        mkdir espressif
+        cd espressif
+        git clone https://github.com/espressif/arduino-esp32.git esp32
+        cd esp32/tools
+        python get.py
+    fi
+
+}
diff --git a/lib/mcp4652/mcp4652.cpp b/libraries/mcp4652/mcp4652.cpp
similarity index 100%
rename from lib/mcp4652/mcp4652.cpp
rename to libraries/mcp4652/mcp4652.cpp
diff --git a/lib/mcp4652/mcp4652.h b/libraries/mcp4652/mcp4652.h
similarity index 100%
rename from lib/mcp4652/mcp4652.h
rename to libraries/mcp4652/mcp4652.h
diff --git a/libraries/readme.txt b/libraries/readme.txt
new file mode 100644
index 0000000000000000000000000000000000000000..96ce674fe0ca3be65c6d8872a3b109caaad73f07
--- /dev/null
+++ b/libraries/readme.txt
@@ -0,0 +1 @@
+For information on installing libraries, see: http://www.arduino.cc/en/Guide/Libraries
diff --git a/platformio.ini b/platformio.ini
deleted file mode 100644
index e7a518cf46209579d53e38f7e43596cce66b0289..0000000000000000000000000000000000000000
--- a/platformio.ini
+++ /dev/null
@@ -1,31 +0,0 @@
-#
-# Project Configuration File
-#
-# A detailed documentation with the EXAMPLES is located here:
-# http://docs.platformio.org/en/latest/projectconf.html
-#
-
-# A sign `#` at the beginning of the line indicates a comment
-# Comment lines are ignored.
-
-# Simple and base environment
-# [env:mybaseenv]
-# platform = %INSTALLED_PLATFORM_NAME_HERE%
-# framework =
-# board =
-#
-# Automatic targets - enable auto-uploading
-# targets = upload
-
-[env:esp12e]
-platform = espressif
-#platform = espressif8266_stage
-framework = arduino
-board = esp12e
-upload_speed = 921600
-board_f_cpu = 160000000L
-build_flags = -Wl,-Tesp8266.flash.4m.ld
-lib_deps = WebSockets
-  Simpletimer
-  LiquidCrystal_I2C
-  
diff --git a/src/smartpower2.ino b/smartpower2.ino
similarity index 100%
rename from src/smartpower2.ino
rename to smartpower2.ino