Browse Source

esp模块连接手柄
Signed-off-by: Caner

Caner 4 years ago
parent
commit
7631ca89a1
97 changed files with 13555 additions and 0 deletions
  1. BIN
      8266-01S.png
  2. BIN
      ESP连接手柄图.png
  3. 674 0
      Esp32-Client/ArduinoWebsockets/LICENSE
  4. 326 0
      Esp32-Client/ArduinoWebsockets/README.md
  5. 1 0
      Esp32-Client/ArduinoWebsockets/_config.yml
  6. 70 0
      Esp32-Client/ArduinoWebsockets/examples/Esp32-Client/Esp32-Client.ino
  7. 67 0
      Esp32-Client/ArduinoWebsockets/examples/Esp32-Server/Esp32-Server.ino
  8. 70 0
      Esp32-Client/ArduinoWebsockets/examples/Esp8266-Client/Esp8266-Client.ino
  9. 67 0
      Esp32-Client/ArduinoWebsockets/examples/Esp8266-Server/Esp8266-Server.ino
  10. 82 0
      Esp32-Client/ArduinoWebsockets/examples/Minimal-Esp32-Client/Minimal-Esp32-Client.ino
  11. 82 0
      Esp32-Client/ArduinoWebsockets/examples/Minimal-Esp8266-Client/Minimal-Esp8266-Client.ino
  12. 122 0
      Esp32-Client/ArduinoWebsockets/examples/MultipleClients-Teensy41-Server/MultipleClients-Teensy41-Server.ino
  13. 115 0
      Esp32-Client/ArduinoWebsockets/examples/Secured-Esp32-Client/Secured-Esp32-Client.ino
  14. 88 0
      Esp32-Client/ArduinoWebsockets/examples/Secured-Esp8266-Client/Secured-Esp8266-Client.ino
  15. 187 0
      Esp32-Client/ArduinoWebsockets/examples/SecuredTwoWay-Esp8266-Client/SecuredTwoWay-Esp8266-Client.ino
  16. 243 0
      Esp32-Client/ArduinoWebsockets/examples/SocketAndHttp-Teensy41-Server/SocketAndHttp-Teensy41-Server.ino
  17. 78 0
      Esp32-Client/ArduinoWebsockets/examples/Teensy41-Client/Teens41-Client.ino
  18. 86 0
      Esp32-Client/ArduinoWebsockets/examples/Teensy41-Server/Teens41-Server.ino
  19. 82 0
      Esp32-Client/ArduinoWebsockets/keywords.txt
  20. 9 0
      Esp32-Client/ArduinoWebsockets/library.properties
  21. 8 0
      Esp32-Client/ArduinoWebsockets/src/ArduinoWebsockets.h
  22. 58 0
      Esp32-Client/ArduinoWebsockets/src/crypto.cpp
  23. 14 0
      Esp32-Client/ArduinoWebsockets/src/message.cpp
  24. 127 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/client.hpp
  25. 89 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/data_frame.hpp
  26. 91 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/websockets_endpoint.hpp
  27. 53 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/ws_common.hpp
  28. 126 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/wscrypto/base64.hpp
  29. 11 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/wscrypto/crypto.hpp
  30. 261 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/wscrypto/sha1.hpp
  31. 186 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/message.hpp
  32. 81 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/esp32/esp32_tcp.hpp
  33. 91 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/esp8266/esp8266_tcp.hpp
  34. 84 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/generic_esp/generic_esp_clients.hpp
  35. 34 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/linux/linux_tcp_client.hpp
  36. 31 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/linux/linux_tcp_server.hpp
  37. 17 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/tcp_client.hpp
  38. 14 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/tcp_server.hpp
  39. 14 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/tcp_socket.hpp
  40. 98 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/teensy41/teensy41_tcp_client.hpp
  41. 57 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/teensy41/teensy41_tcp_server.hpp
  42. 45 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/windows/win_tcp_client.hpp
  43. 38 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/windows/win_tcp_server.hpp
  44. 28 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/server.hpp
  45. 4 0
      Esp32-Client/ArduinoWebsockets/src/tiny_websockets/ws_config_defs.hpp
  46. 644 0
      Esp32-Client/ArduinoWebsockets/src/websockets_client.cpp
  47. 468 0
      Esp32-Client/ArduinoWebsockets/src/websockets_endpoint.cpp
  48. 93 0
      Esp32-Client/ArduinoWebsockets/src/websockets_server.cpp
  49. 17 0
      Esp32-Client/ArduinoWebsockets/src/ws_common.cpp
  50. 155 0
      Esp32-Client/Esp32-Client.ino
  51. 512 0
      PS2X_lib/PS2X_lib.cpp
  52. 251 0
      PS2X_lib/PS2X_lib.h
  53. 150 0
      PS2X_lib/examples/PS2XMouse/PS2XMouse.ino
  54. 189 0
      PS2X_lib/examples/PS2X_Example/PS2X_Example.ino
  55. 78 0
      PS2X_lib/keywords.txt
  56. 138 0
      linkServer_3/linkServer_3.ino
  57. 66 0
      socket/socket.ino
  58. 252 0
      wifi-socket/ADWebSockets/SocketIOclient.cpp
  59. 101 0
      wifi-socket/ADWebSockets/SocketIOclient.h
  60. 757 0
      wifi-socket/ADWebSockets/WebSockets.cpp
  61. 367 0
      wifi-socket/ADWebSockets/WebSockets.h
  62. 80 0
      wifi-socket/ADWebSockets/WebSockets4WebServer.h
  63. 973 0
      wifi-socket/ADWebSockets/WebSocketsClient.cpp
  64. 169 0
      wifi-socket/ADWebSockets/WebSocketsClient.h
  65. 948 0
      wifi-socket/ADWebSockets/WebSocketsServer.cpp
  66. 243 0
      wifi-socket/ADWebSockets/WebSocketsServer.h
  67. 36 0
      wifi-socket/ADWebSockets/WebSocketsVersion.h
  68. 83 0
      wifi-socket/ADWebSockets/examples/Nginx/esp8266.ssl.reverse.proxy.conf
  69. 84 0
      wifi-socket/ADWebSockets/examples/avr/WebSocketClientAVR/WebSocketClientAVR.ino
  70. 110 0
      wifi-socket/ADWebSockets/examples/esp32/WebSocketClient/WebSocketClient.ino
  71. 106 0
      wifi-socket/ADWebSockets/examples/esp32/WebSocketClientSSL/WebSocketClientSSL.ino
  72. 104 0
      wifi-socket/ADWebSockets/examples/esp32/WebSocketServer/WebSocketServer.ino
  73. 106 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino
  74. 88 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketClientSSL/WebSocketClientSSL.ino
  75. 103 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketClientSSLWithCA/WebSocketClientSSLWithCA.ino
  76. 128 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino
  77. 165 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketClientSocketIOack/WebSocketClientSocketIOack.ino
  78. 149 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketClientStomp/WebSocketClientStomp.ino
  79. 150 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketClientStompOverSockJs/WebSocketClientStompOverSockJs.ino
  80. 86 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketServer/WebSocketServer.ino
  81. 132 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketServerAllFunctionsDemo/WebSocketServerAllFunctionsDemo.ino
  82. 94 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketServerFragmentation/WebSocketServerFragmentation.ino
  83. 103 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketServerHooked/WebSocketServerHooked.ino
  84. 20 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketServerHooked/emu
  85. 45 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketServerHooked/ws-testclient.py
  86. 86 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketServerHttpHeaderValidation/WebSocketServerHttpHeaderValidation.ino
  87. 121 0
      wifi-socket/ADWebSockets/examples/esp8266/WebSocketServer_LEDcontrol/WebSocketServer_LEDcontrol.ino
  88. 46 0
      wifi-socket/ADWebSockets/examples/particle/ParticleWebSocketClient/application.cpp
  89. 7 0
      wifi-socket/ADWebSockets/libb64/AUTHORS
  90. 29 0
      wifi-socket/ADWebSockets/libb64/LICENSE
  91. 98 0
      wifi-socket/ADWebSockets/libb64/cdecode.c
  92. 28 0
      wifi-socket/ADWebSockets/libb64/cdecode_inc.h
  93. 119 0
      wifi-socket/ADWebSockets/libb64/cencode.c
  94. 31 0
      wifi-socket/ADWebSockets/libb64/cencode_inc.h
  95. 202 0
      wifi-socket/ADWebSockets/libsha1/libsha1.c
  96. 21 0
      wifi-socket/ADWebSockets/libsha1/libsha1.h
  97. 115 0
      wifi-socket/wifi-socket.ino

BIN
8266-01S.png


BIN
ESP连接手柄图.png


+ 674 - 0
Esp32-Client/ArduinoWebsockets/LICENSE

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.

+ 326 - 0
Esp32-Client/ArduinoWebsockets/README.md

@@ -0,0 +1,326 @@
+[![arduino-library-badge](https://www.ardu-badge.com/badge/ArduinoWebsockets.svg?)](https://www.ardu-badge.com/ArduinoWebsockets)  [![Build Status](https://travis-ci.org/gilmaimon/ArduinoWebsockets.svg?branch=master)](https://travis-ci.org/gilmaimon/ArduinoWebsockets)
+
+# Arduino Websockets
+
+A library for writing modern websockets applications with Arduino (ESP8266 and ESP32). This project is based on my project [TinyWebsockets](https://github.com/gilmaimon/TinyWebsockets).
+
+The library provides simple and easy interface for websockets work (Client and Server). See the [basic-usage](#Basic-Usage) guide and the [examples](#Full-Examples).
+
+### Please check out the [TinyWebsockets Wiki](https://github.com/gilmaimon/TinyWebsockets/wiki) for many more details!
+
+## Getting Started
+This section should help you get started with the library. If you have any questions feel free to open an issue.
+
+### Prerequisites
+Currently (version 0.5.*) the library only works with `ESP8266`, `ESP32` and `Teensy 4.1`.
+
+### Installing
+
+You can install the library from the Arduino IDE or using a release ZIP file from the [Github release page](https://github.com/gilmaimon/ArduinoWebsockets/releases).
+Detailed instructions can be found [here](https://www.ardu-badge.com/ArduinoWebsockets).
+
+## Basic Usage
+
+### Client
+
+Creating a client and connecting to a server:
+```c++
+WebsocketsClient client;
+client.connect("ws://your-server-ip:port/uri");
+```
+
+Sending a message:
+```c++
+client.send("Hello Server!");
+```
+
+Waiting for messages:
+```c++
+client.onMessage([](WebsocketsMessage msg){
+    Serial.prinln("Got Message: " + msg.data());
+});
+```
+
+In order to keep receiving messages, you should:
+```c++
+void loop() {
+    client.poll();
+}
+```
+### Server
+
+Creating a server and listening for connections:
+```c++
+WebsocketsServer server;
+server.listen(8080);
+```
+
+Accepting connections:
+```c++
+WebsocketsClient client = server.accept();
+// handle client as described before :)
+```
+
+## Full Examples
+
+### Client
+
+```c++
+#include <ArduinoWebsockets.h>
+#include <ESP8266WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+const char* websockets_server = "www.myserver.com:8080"; //server adress and port
+
+using namespace websockets;
+
+void onMessageCallback(WebsocketsMessage message) {
+    Serial.print("Got Message: ");
+    Serial.println(message.data());
+}
+
+void onEventsCallback(WebsocketsEvent event, String data) {
+    if(event == WebsocketsEvent::ConnectionOpened) {
+        Serial.println("Connnection Opened");
+    } else if(event == WebsocketsEvent::ConnectionClosed) {
+        Serial.println("Connnection Closed");
+    } else if(event == WebsocketsEvent::GotPing) {
+        Serial.println("Got a Ping!");
+    } else if(event == WebsocketsEvent::GotPong) {
+        Serial.println("Got a Pong!");
+    }
+}
+
+WebsocketsClient client;
+void setup() {
+    Serial.begin(115200);
+    // Connect to wifi
+    WiFi.begin(ssid, password);
+
+    // Wait some time to connect to wifi
+    for(int i = 0; i < 10 && WiFi.status() != WL_CONNECTED; i++) {
+        Serial.print(".");
+        delay(1000);
+    }
+
+    // Setup Callbacks
+    client.onMessage(onMessageCallback);
+    client.onEvent(onEventsCallback);
+    
+    // Connect to server
+    client.connect(websockets_server);
+
+    // Send a message
+    client.send("Hi Server!");
+    // Send a ping
+    client.ping();
+}
+
+void loop() {
+    client.poll();
+}
+```
+***Note:** for ESP32 you only need to change to code that connects to WiFi (replace `#include <ESP8266WiFi.h>` with `#include <WiFi.h>`), everything else stays the same.*
+
+### Server
+```c++
+#include <ArduinoWebsockets.h>
+#include <ESP8266WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+
+using namespace websockets;
+
+WebsocketsServer server;
+void setup() {
+  Serial.begin(115200);
+  // Connect to wifi
+  WiFi.begin(ssid, password);
+
+  // Wait some time to connect to wifi
+  for(int i = 0; i < 15 && WiFi.status() != WL_CONNECTED; i++) {
+      Serial.print(".");
+      delay(1000);
+  }
+  
+  Serial.println("");
+  Serial.println("WiFi connected");
+  Serial.println("IP address: ");
+  Serial.println(WiFi.localIP());   //You can get IP address assigned to ESP
+
+  server.listen(80);
+  Serial.print("Is server live? ");
+  Serial.println(server.available());
+}
+
+void loop() {
+  auto client = server.accept();
+  if(client.available()) {
+    auto msg = client.readBlocking();
+
+    // log
+    Serial.print("Got Message: ");
+    Serial.println(msg.data());
+
+    // return echo
+    client.send("Echo: " + msg.data());
+
+    // close the connection
+    client.close();
+  }
+  
+  delay(1000);
+}
+```
+***Note:** for ESP32 you only need to change to code that connects to WiFi (replace `#include <ESP8266WiFi.h>` with `#include <WiFi.h>`), everything else stays the same.*
+
+## Binary Data
+
+For binary data it is recommended to use `msg.rawData()` which returns a `std::string`, or `msg.c_str()` which returns a `const char*`. 
+The reason is that `msg.data()` returns an Arduino `String`, which is great for Serial printing and very basic memory handling but bad for most binary usages.
+
+See [issue #32](https://github.com/gilmaimon/ArduinoWebsockets/issues/32) for further information.
+
+## SSL and WSS Support
+
+No matter what board you are using, in order to use WSS (websockets over SSL) you need to use
+```c++
+client.connect("wss://your-secured-server-ip:port/uri");
+```
+
+The next sections describe board-specific code for using WSS with the library.
+
+### ESP8266
+With the esp8266 there are multiple ways for using WSS. By default, `ArduinoWebsockets` does not validate the certificate chain. This can be set explicitly using:
+```c++
+client.setInsecure();
+```
+
+You can also use a `SSL Fingerprint` to validate the SSL connection, for example:
+```c++
+const char ssl_fingerprint[] PROGMEM = "D5 07 4D 79 B2 D2 53 D7 74 E6 1B 46 C5 86 4E FE AD 00 F1 98";
+
+client.setFingerprint(ssl_fingerprint);
+```
+
+or you could use the `setKnownKey()` method to specify the public key of a certificate in order to validate the server you are connecting to.
+```
+PublicKey *publicKey = new PublicKey(public_key);
+client.setKnownKey(publicKey);
+```
+or you can specify the Certificate Authority (CA) using `setTrustAnchors` method, as follows:
+
+```
+X509List *serverTrustedCA = new X509List(ca_cert);
+client.setTrustAnchors(serverTrustedCA);
+```
+
+For client-side certificate validation, you can use RSA or EC certificates, using the method `setClientRSACert` or `setClientECCert` .
+
+### ESP32
+With the esp32 you could either provide the full certificate, or provide no certificate. An example for setting CA Certificate:
+```c++
+const char ssl_ca_cert[] PROGMEM = \
+    "-----BEGIN CERTIFICATE-----\n" \
+    "MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \
+    "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \
+    "DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \
+    "SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \
+    "GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
+    "AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \
+    "q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \
+    "SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \
+    "Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \
+    "a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \
+    "/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \
+    "AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \
+    "CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \
+    "bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \
+    "c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \
+    "VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \
+    "ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \
+    "MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \
+    "Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \
+    "AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \
+    "uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \
+    "wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \
+    "X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \
+    "PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \
+    "KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \
+    "-----END CERTIFICATE-----\n";
+
+client.setCACert(ssl_ca_cert);
+```
+
+### TEENSY 4.1
+Currently WSS is not implemented.
+
+## Contributing
+Contributions are welcomed! Please open issues if you have troubles while using the library or any queshtions on how to get started. Pull requests are welcomed, please open an issue first.
+
+## Contributors
+Thanks for everyone who reported a bug, suggested a feature and contributed to the development of this library.
+<table>
+  <tr>
+    <td align="center"><a href="https://github.com/arnoson"><img src="https://github.com/arnoson.png" width="100px;" alt="arnoson"/><br /><sub><b>⭐️ arnoson</b></sub></a><br /></td>
+    <td align="center"><a href="https://github.com/ramdor"><img src="https://github.com/ramdor.png" width="100px;" alt="ramdor"/><br /><sub><b>⭐️ ramdor</b></sub></a><br /></td>
+    <td align="center"><a href="https://github.com/xgarb"><img src="https://github.com/xgarb.png" width="100px;" alt="xgarb"/><br /><sub><b>⭐️ xgarb</b></sub></a><br /></td>
+    <td align="center"><a href="https://github.com/matsujirushi"><img src="https://github.com/matsujirushi.png" width="100px;" alt="matsujirushi"/><br /><sub><b>matsujirushi</b></sub></a><br /></td>
+    <td align="center"><a href="https://github.com/bastienvans"><img src="https://github.com/bastienvans.png" width="100px;" alt="bastienvans"/><br /><sub><b>bastienvans</b></sub></a><br /></td>
+    <td align="center"><a href="https://github.com/johneakin"><img src="https://github.com/johneakin.png" width="100px;" alt="johneakin"/><br /><sub><b>johneakin</b></sub></a><br /></td>
+    <td align="center"><a href="https://github.com/lalten"><img src="https://github.com/lalten.png" width="100px;" alt="lalten"/><br /><sub><b>lalten</b></sub></a><br /></td>
+  </tr>
+  <tr>
+      <td align="center"><a href="https://github.com/adelin-mcbsoft"><img src="https://github.com/adelin-mcbsoft.png" width="100px;" alt="adelin-mcbsoft"/><br /><sub><b>⭐️ adelin-mcbsoft</b></sub></a><br /></td>
+      <td align="center"><a href="https://github.com/Jonty"><img src="https://github.com/Jonty.png" width="100px;" alt="Jonty"/><br /><sub><b>⭐️ Jonty</b></sub></a><br /></td>
+      <td align="center"><a href="https://github.com/Nufflee"><img src="https://github.com/Nufflee.png" width="100px;" alt="Nufflee"/><br /><sub><b>Nufflee</b></sub></a><br /></td>
+      <td align="center"><a href="https://github.com/mmcArg"><img src="https://github.com/mmcArg.png" width="100px;" alt="mmcArg"/><br /><sub><b>mmcArg</b></sub></a><br /></td>
+      <td align="center"><a href="https://github.com/JohnInWI"><img src="https://github.com/JohnInWI.png" width="100px;" alt="JohnInWI"/><br /><sub><b>JohnInWI</b></sub></a><br /></td>
+      <td align="center"><a href="https://github.com/logdog2709"><img src="https://github.com/logdog2709.png" width="100px;" alt="logdog2709"/><br /><sub><b>logdog2709</b></sub></a><br /></td>
+       <td align="center"><a href="https://github.com/elC0mpa"><img src="https://github.com/elC0mpa.png" width="100px;" alt="elC0mpa"/><br /><sub><b>elC0mpa</b></sub></a><br /></td>
+ </tr>
+    
+ <tr>
+      <td align="center"><a href="https://github.com/oofnik"><img src="https://github.com/oofnik.png" width="100px;" alt="oofnik"/><br /><sub><b>⭐️ oofnik</b></sub></a><br /></td>
+          <td align="center"><a href="https://github.com/zastrixarundell"><img src="https://github.com/zastrixarundell.png" width="100px;" alt="zastrixarundell"/><br /><sub><b>⭐️ zastrixarundell</b></sub></a><br /></td>
+        <td align="center"><a href="https://github.com/elielmarcos"><img src="https://github.com/elielmarcos.png" width="100px;" alt="elielmarcos"/><br /><sub><b>elielmarcos</b></sub></a><br /></td>
+
+ </tr>
+ 
+</table>
+
+## Change Log
+- **14/02/2019 (v0.1.1)** - Initial commits and support for ESP32 and ESP8266 Websocket Clients.
+- **16/02/2019 (v0.1.2)** - Added support for events (Pings, Pongs) and more internal improvements (events handling according to [RFC-6455](https://tools.ietf.org/html/rfc6455))
+- **20/02/2019 (v0.1.3)** - Users now dont have to specify TCP client types (ESP8266/ESP32) they are selected automatically.
+- **21/02/2019 (v0.1.5)** - Bug Fixes. Client now exposes a single string connect interface.
+- **24/02/2019 (v0.2.0)** - User-facing interface is now done with Arduino's `String` class. Merged more changes (mainly optimizations) from TinyWebsockets.
+- **25/02/2019 (v0.2.1)** - A tiny patch. Fixed missing user-facing strings for client interface. 
+- **07/03/2019 (v0.3.0)** - A version update. Now supports a websockets server, better support for fragmented messages and streams. bug fixes and more optimized networking implementations. 
+- **08/03/2019 (v0.3.1)** - Small patch. Merged changes from TinyWebsockets - interface changes to callbacks (partial callbacks without WebsocketsClient& as first parameter).
+- **12/03/2019 (v0.3.2)** - Fixed a bug with behaviour of WebsokcetsClient (copy c'tor and assignment operator). Added close codes from TinyWebsockets. Thank you [@ramdor](https://github.com/gilmaimon/ArduinoWebsockets/issues/2)
+- **13/03/2019 (v0.3.3)** - Fixed a bug in the esp8266 networking impl. Thank you [@ramdor](https://github.com/gilmaimon/ArduinoWebsockets/issues/2)
+- **14/03/2019 (v0.3.4)** - changed underling tcp impl for esp8266 and esp32 to use `setNoDelay(true)` instead of sync communication. This makes communication faster and more relaiable than default. Thank you @ramdor for pointing out these methods.
+- **06/04/2019 (v0.3.5)** - added very basic support for WSS in esp8266 (no support for fingerprint/ca or any kind of chain validation). 
+- **22/04/2019 (v0.4.0)** - Added WSS support for both esp8266 and esp32. E328266 can use `client.setInsecure()` (does not validate certificate chain) or `client.setFingerprint(fingerprint)` in order to use WSS. With ESP32 there is `client.setCACert(certificate)` that can be used. (Usage is same as the built in `WiFiClientSecure`).
+- **18/05/2019 (v0.4.1)** - Patch! Addressed an error with some servers. The bugs where first noted in [issue #9](https://github.com/gilmaimon/ArduinoWebsockets/issues/9). Bug was that some servers will drop connections that don't use masking, and TinyWebsockets does not use masking by default. TinyWebsockets changed that and this library merged the changes.
+- **24/05/2019 (v0.4.2)** - Patch! Adressed masking issues - server to client messages would get masked but RFC forbbids it. (changes merged from TinyWebsockets)
+- **07/06/2019 (v0.4.3)** - Patch! Fixed a bug on some clients (mainly FireFox websokcets impl). Thank you @xgarb ([related issue](https://github.com/gilmaimon/ArduinoWebsockets/issues/11))
+- **09/06/2019 (v0.4.4)** - Patch! Fixed an issue with `close` event callback not called in some cases (sudden disconnect, for example). Thank you @adelin-mcbsoft for pointing out the issue ([related issue](https://github.com/gilmaimon/ArduinoWebsockets/issues/14))
+- **14/06/2019 (v0.4.5)** - Patch! Fixed a memory leak and an unnecessary use of heap memory in case of masking messages. This was discoverd thanks to [issue #16](https://github.com/gilmaimon/ArduinoWebsockets/issues/16). Thank you [xgarb](https://github.com/xgarb)!
+- **13/07/2019 (v0.4.6)** - Very small update. Changed readme to document esp32's secured client behvior ([As discussed in issue #18](https://github.com/gilmaimon/ArduinoWebsockets/issues/18)). Also esp32's version of WebsocketsClient now has a `setInsecure` method. Thank you [adelin-mcbsoft](https://github.com/adelin-mcbsoft)!
+- **25/07/2019 (v0.4.7)** - Bugfix. Fixed issues with receving large messages (unchecked reads) which was pointed out in [in issue #21](https://github.com/gilmaimon/ArduinoWebsockets/issues/21)). Thank you [Jonty](https://github.com/Jonty)!
+- **26/07/2019 (v0.4.8)** - Feature. Added an `addHeader` method as suggested [in issue #22](https://github.com/gilmaimon/ArduinoWebsockets/issues/21)). Thank you [mmcArg](https://github.com/mmcArg)!
+- **01/08/2019 (v0.4.9)** - Patch - Bugfix. Worked around a bug where connecting to unavailable endpoints would not return false (this is a bug with the `WiFiClient` library itself). Added some missing keywords. Thank you [Nufflee](https://github.com/Nufflee) for pointing out the [issue](https://github.com/gilmaimon/ArduinoWebsockets/issues/25)!
+- **10/08/2019 (v0.4.10)** - Patch - Bugfix. Fixed a bug (and general in-stability) caused from unchecked and unsafe read operations on sockets. Also improved memory usage and management. Thank you [Jonty](https://github.com/Jonty) for openning and helping with the [issue](https://github.com/gilmaimon/ArduinoWebsockets/issues/26)!
+- **14/09/2019 (v0.4.11)** - Bugfixes - masking settings used to not get copied when using assignment between `WebsocketClient` instances. Also handshake validation is now case insensitive. Thank you [logdog2709](https://github.com/logdog2709) for pointing out the [issue](https://github.com/gilmaimon/ArduinoWebsockets/issues/34).
+- **12/10/2019 (v0.4.12)** - Patch - Messages are now sent as a single TCP buffer instead of separate messages. Thank you [elC0mpa](https://github.com/elC0mpa) for posting the [issue](https://github.com/gilmaimon/ArduinoWebsockets/issues/44).
+- **19/10/2019 (v0.4.13)** - Patch - added `yield` calls in order to prevent software-watchdog resets on esp8266 (on long messages). Thank you [elC0mpa](https://github.com/elC0mpa) for documenting and helping with the [issue](https://github.com/gilmaimon/ArduinoWebsockets/issues/43).
+- **22/11/2019 (v0.4.14)** - Added `rawData` and `c_str` as acccessors in `WebsocketsMessage` so now the raw data can be acccessed which should solve issue #32 and not break any existing sketch.
+- **24/02/20 (v0.4.15)** - Added `Origin` and `User-Agent` headers to requests sent by the library, this seems to be required by some servers. Thank you [imesut](https://github.com/imesut) for pointing out the issue.
+- **21/04/20 (v0.4.16)** - Merged pull request by @oofnik which added 2 way SSL auth for ESP32 and ES8266. Thank you very [oofnik](https://github.com/oofnik) for the contribuation.
+- **25/04/20 (v0.4.17)** - Merged pull request by Luka Bodroža (@zastrixarundell) which fixed [issue #69](https://github.com/gilmaimon/ArduinoWebsockets/issues/69) - default headers (like Origin, Host) are now customizable via the `addHeader` method. Thank you [zastrixarundell](https://github.com/zastrixarundell) for the contribution.
+- **23/07/20 (v0.4.18)** - Merged pull request by Adelin U (@adelin-mcbsoft) which fixed [issue #84](https://github.com/gilmaimon/ArduinoWebsockets/issues/84) - SSL bug-fixing, implemented public-key certificate validation & EC Certificates for client-side. Thank you Adelin!
+- **28/11/20 (v0.5.0)** - Support for Teensy 4.1 added by the awesome [@arnoson](https://github.com/arnoson). Supporting plaintext client/server communication and providing new and useful examples. Thank you arnoson!

+ 1 - 0
Esp32-Client/ArduinoWebsockets/_config.yml

@@ -0,0 +1 @@
+theme: jekyll-theme-cayman

+ 70 - 0
Esp32-Client/ArduinoWebsockets/examples/Esp32-Client/Esp32-Client.ino

@@ -0,0 +1,70 @@
+/*
+	Esp32 Websockets Client
+
+	This sketch:
+        1. Connects to a WiFi network
+        2. Connects to a Websockets server
+        3. Sends the websockets server a message ("Hello Server")
+        4. Prints all incoming messages while the connection is open
+
+	Hardware:
+        For this sketch you only need an ESP32 board.
+
+	Created 15/02/2019
+	By Gil Maimon
+	https://github.com/gilmaimon/ArduinoWebsockets
+
+*/
+
+#include <ArduinoWebsockets.h>
+#include <WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+const char* websockets_server_host = "serverip_or_name"; //Enter server adress
+const uint16_t websockets_server_port = 8080; // Enter server port
+
+using namespace websockets;
+
+WebsocketsClient client;
+void setup() {
+    Serial.begin(115200);
+    // Connect to wifi
+    WiFi.begin(ssid, password);
+
+    // Wait some time to connect to wifi
+    for(int i = 0; i < 10 && WiFi.status() != WL_CONNECTED; i++) {
+        Serial.print(".");
+        delay(1000);
+    }
+
+    // Check if connected to wifi
+    if(WiFi.status() != WL_CONNECTED) {
+        Serial.println("No Wifi!");
+        return;
+    }
+
+    Serial.println("Connected to Wifi, Connecting to server.");
+    // try to connect to Websockets server
+    bool connected = client.connect(websockets_server_host, websockets_server_port, "/");
+    if(connected) {
+        Serial.println("Connected!");
+        client.send("Hello Server");
+    } else {
+        Serial.println("Not Connected!");
+    }
+    
+    // run callback when messages are received
+    client.onMessage([&](WebsocketsMessage message){
+        Serial.print("Got Message: ");
+        Serial.println(message.data());
+    });
+}
+
+void loop() {
+    // let the websockets client check for incoming messages
+    if(client.available()) {
+        client.poll();
+    }
+    delay(500);
+}

+ 67 - 0
Esp32-Client/ArduinoWebsockets/examples/Esp32-Server/Esp32-Server.ino

@@ -0,0 +1,67 @@
+/*
+  Minimal Esp32 Websockets Server
+
+  This sketch:
+        1. Connects to a WiFi network
+        2. Starts a websocket server on port 80
+        3. Waits for connections
+        4. Once a client connects, it wait for a message from the client
+        5. Sends an "echo" message to the client
+        6. closes the connection and goes back to step 3
+
+  Hardware:
+        For this sketch you only need an ESP32 board.
+
+  Created 15/02/2019
+  By Gil Maimon
+  https://github.com/gilmaimon/ArduinoWebsockets
+*/
+
+#include <ArduinoWebsockets.h>
+#include <WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+
+using namespace websockets;
+
+WebsocketsServer server;
+void setup() {
+  Serial.begin(115200);
+  // Connect to wifi
+  WiFi.begin(ssid, password);
+
+  // Wait some time to connect to wifi
+  for(int i = 0; i < 15 && WiFi.status() != WL_CONNECTED; i++) {
+      Serial.print(".");
+      delay(1000);
+  }
+  
+  Serial.println("");
+  Serial.println("WiFi connected");
+  Serial.println("IP address: ");
+  Serial.println(WiFi.localIP());   //You can get IP address assigned to ESP
+
+  server.listen(80);
+  Serial.print("Is server live? ");
+  Serial.println(server.available());
+}
+
+void loop() {
+  WebsocketsClient client = server.accept();
+  if(client.available()) {
+    WebsocketsMessage msg = client.readBlocking();
+
+    // log
+    Serial.print("Got Message: ");
+    Serial.println(msg.data());
+
+    // return echo
+    client.send("Echo: " + msg.data());
+
+    // close the connection
+    client.close();
+  }
+  
+  delay(1000);
+}

+ 70 - 0
Esp32-Client/ArduinoWebsockets/examples/Esp8266-Client/Esp8266-Client.ino

@@ -0,0 +1,70 @@
+/*
+	Esp8266 Websockets Client
+
+	This sketch:
+        1. Connects to a WiFi network
+        2. Connects to a Websockets server
+        3. Sends the websockets server a message ("Hello Server")
+        4. Prints all incoming messages while the connection is open
+
+	Hardware:
+        For this sketch you only need an ESP8266 board.
+
+	Created 15/02/2019
+	By Gil Maimon
+	https://github.com/gilmaimon/ArduinoWebsockets
+
+*/
+
+#include <ArduinoWebsockets.h>
+#include <ESP8266WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+const char* websockets_server_host = "serverip_or_name"; //Enter server adress
+const uint16_t websockets_server_port = 8080; // Enter server port
+
+using namespace websockets;
+
+WebsocketsClient client;
+void setup() {
+    Serial.begin(115200);
+    // Connect to wifi
+    WiFi.begin(ssid, password);
+
+    // Wait some time to connect to wifi
+    for(int i = 0; i < 10 && WiFi.status() != WL_CONNECTED; i++) {
+        Serial.print(".");
+        delay(1000);
+    }
+
+    // Check if connected to wifi
+    if(WiFi.status() != WL_CONNECTED) {
+        Serial.println("No Wifi!");
+        return;
+    }
+
+    Serial.println("Connected to Wifi, Connecting to server.");
+    // try to connect to Websockets server
+    bool connected = client.connect(websockets_server_host, websockets_server_port, "/");
+    if(connected) {
+        Serial.println("Connecetd!");
+        client.send("Hello Server");
+    } else {
+        Serial.println("Not Connected!");
+    }
+    
+    // run callback when messages are received
+    client.onMessage([&](WebsocketsMessage message) {
+        Serial.print("Got Message: ");
+        Serial.println(message.data());
+    });
+}
+
+void loop() {
+    // let the websockets client check for incoming messages
+    if(client.available()) {
+        client.poll();
+    }
+    delay(500);
+}

+ 67 - 0
Esp32-Client/ArduinoWebsockets/examples/Esp8266-Server/Esp8266-Server.ino

@@ -0,0 +1,67 @@
+/*
+  Minimal Esp8266 Websockets Server
+
+  This sketch:
+        1. Connects to a WiFi network
+        2. Starts a websocket server on port 80
+        3. Waits for connections
+        4. Once a client connects, it wait for a message from the client
+        5. Sends an "echo" message to the client
+        6. closes the connection and goes back to step 3
+
+  Hardware:
+        For this sketch you only need an ESP8266 board.
+
+  Created 15/02/2019
+  By Gil Maimon
+  https://github.com/gilmaimon/ArduinoWebsockets
+*/
+
+#include <ArduinoWebsockets.h>
+#include <ESP8266WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+
+using namespace websockets;
+
+WebsocketsServer server;
+void setup() {
+  Serial.begin(115200);
+  // Connect to wifi
+  WiFi.begin(ssid, password);
+
+  // Wait some time to connect to wifi
+  for(int i = 0; i < 15 && WiFi.status() != WL_CONNECTED; i++) {
+      Serial.print(".");
+      delay(1000);
+  }
+  
+  Serial.println("");
+  Serial.println("WiFi connected");
+  Serial.println("IP address: ");
+  Serial.println(WiFi.localIP());   //You can get IP address assigned to ESP
+
+  server.listen(80);
+  Serial.print("Is server live? ");
+  Serial.println(server.available());
+}
+
+void loop() {
+  WebsocketsClient client = server.accept();
+  if(client.available()) {
+    WebsocketsMessage msg = client.readBlocking();
+
+    // log
+    Serial.print("Got Message: ");
+    Serial.println(msg.data());
+
+    // return echo
+    client.send("Echo: " + msg.data());
+
+    // close the connection
+    client.close();
+  }
+  
+  delay(1000);
+}

+ 82 - 0
Esp32-Client/ArduinoWebsockets/examples/Minimal-Esp32-Client/Minimal-Esp32-Client.ino

@@ -0,0 +1,82 @@
+/*
+	Minimal Esp32 Websockets Client
+
+	This sketch:
+        1. Connects to a WiFi network
+        2. Connects to a Websockets server
+        3. Sends the websockets server a message ("Hello Server")
+        4. Sends the websocket server a "ping"
+        5. Prints all incoming messages while the connection is open
+
+    NOTE:
+    The sketch dosen't check or indicate about errors while connecting to 
+    WiFi or to the websockets server. For full example you might want 
+    to try the example named "Esp32-Client".
+
+	Hardware:
+        For this sketch you only need an ESP8266 board.
+
+	Created 15/02/2019
+	By Gil Maimon
+	https://github.com/gilmaimon/ArduinoWebsockets
+
+*/
+
+#include <ArduinoWebsockets.h>
+#include <WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+const char* websockets_server_host = "serverip_or_name"; //Enter server adress
+const uint16_t websockets_server_port = 8080; // Enter server port
+
+using namespace websockets;
+
+void onMessageCallback(WebsocketsMessage message) {
+    Serial.print("Got Message: ");
+    Serial.println(message.data());
+}
+
+void onEventsCallback(WebsocketsEvent event, String data) {
+    if(event == WebsocketsEvent::ConnectionOpened) {
+        Serial.println("Connnection Opened");
+    } else if(event == WebsocketsEvent::ConnectionClosed) {
+        Serial.println("Connnection Closed");
+    } else if(event == WebsocketsEvent::GotPing) {
+        Serial.println("Got a Ping!");
+    } else if(event == WebsocketsEvent::GotPong) {
+        Serial.println("Got a Pong!");
+    }
+}
+
+WebsocketsClient client;
+void setup() {
+    Serial.begin(115200);
+    // Connect to wifi
+    WiFi.begin(ssid, password);
+
+    // Wait some time to connect to wifi
+    for(int i = 0; i < 10 && WiFi.status() != WL_CONNECTED; i++) {
+        Serial.print(".");
+        delay(1000);
+    }
+
+    // run callback when messages are received
+    client.onMessage(onMessageCallback);
+    
+    // run callback when events are occuring
+    client.onEvent(onEventsCallback);
+
+    // Connect to server
+    client.connect(websockets_server_host, websockets_server_port, "/");
+
+    // Send a message
+    client.send("Hello Server");
+
+    // Send a ping
+    client.ping();
+}
+
+void loop() {
+    client.poll();
+}

+ 82 - 0
Esp32-Client/ArduinoWebsockets/examples/Minimal-Esp8266-Client/Minimal-Esp8266-Client.ino

@@ -0,0 +1,82 @@
+/*
+	Minimal Esp8266 Websockets Client
+
+	This sketch:
+        1. Connects to a WiFi network
+        2. Connects to a Websockets server
+        3. Sends the websockets server a message ("Hello Server")
+        4. Sends the websocket server a "ping"
+        5. Prints all incoming messages while the connection is open
+
+    NOTE:
+    The sketch dosen't check or indicate about errors while connecting to 
+    WiFi or to the websockets server. For full example you might want 
+    to try the example named "Esp8266-Client".
+
+	Hardware:
+        For this sketch you only need an ESP8266 board.
+
+	Created 15/02/2019
+	By Gil Maimon
+	https://github.com/gilmaimon/ArduinoWebsockets
+
+*/
+
+#include <ArduinoWebsockets.h>
+#include <ESP8266WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+const char* websockets_server_host = "serverip_or_name"; //Enter server adress
+const uint16_t websockets_server_port = 8080; // Enter server port
+
+using namespace websockets;
+
+void onMessageCallback(WebsocketsMessage message) {
+    Serial.print("Got Message: ");
+    Serial.println(message.data());
+}
+
+void onEventsCallback(WebsocketsEvent event, String data) {
+    if(event == WebsocketsEvent::ConnectionOpened) {
+        Serial.println("Connnection Opened");
+    } else if(event == WebsocketsEvent::ConnectionClosed) {
+        Serial.println("Connnection Closed");
+    } else if(event == WebsocketsEvent::GotPing) {
+        Serial.println("Got a Ping!");
+    } else if(event == WebsocketsEvent::GotPong) {
+        Serial.println("Got a Pong!");
+    }
+}
+
+WebsocketsClient client;
+void setup() {
+    Serial.begin(115200);
+    // Connect to wifi
+    WiFi.begin(ssid, password);
+
+    // Wait some time to connect to wifi
+    for(int i = 0; i < 10 && WiFi.status() != WL_CONNECTED; i++) {
+        Serial.print(".");
+        delay(1000);
+    }
+
+    // run callback when messages are received
+    client.onMessage(onMessageCallback);
+    
+    // run callback when events are occuring
+    client.onEvent(onEventsCallback);
+
+    // Connect to server
+    client.connect(websockets_server_host, websockets_server_port, "/");
+
+    // Send a message
+    client.send("Hello Server");
+
+    // Send a ping
+    client.ping();
+}
+
+void loop() {
+    client.poll();
+}

+ 122 - 0
Esp32-Client/ArduinoWebsockets/examples/MultipleClients-Teensy41-Server/MultipleClients-Teensy41-Server.ino

@@ -0,0 +1,122 @@
+/*
+  Teensy41 Websockets Server and Http Server (using NativeEthernet).
+  Combining the Teensy41-Server example with the NativeEthernet WebServer
+  example (https://github.com/vjmuzik/NativeEthernet/blob/master/examples/WebServer/WebServer.ino).
+
+  This sketch:
+  1. Connects to a ethernet network
+  2. Starts a websocket server on port 80
+  3. Waits for connections
+  4. As soon as a client wants to establish a connection, it checks whether a
+     free slot is available and accepts it accordingly
+  5. If the client is accepted it sends a welcome message and echoes any 
+     messages from the client
+  6. Goes back to step 3
+
+  Note:
+  Make sure you share your computer's internet connection with the Teensy
+  via ethernet.
+
+  Libraries:
+  To use this sketch install
+  * TeensyID library (https://github.com/sstaub/TeensyID)
+  * NativeEthernet (https://github.com/vjmuzik/NativeEthernet)
+
+  Hardware:
+  For this sketch you need a Teensy 4.1 board and the Teensy 4.1 Ethernet Kit
+  (https://www.pjrc.com/store/ethernet_kit.html).
+*/
+
+#include <NativeEthernet.h>
+#include <ArduinoWebsockets.h>
+#include <TeensyID.h>
+
+using namespace websockets;
+
+// We will set the MAC address at the beginning of `setup()` using TeensyID's
+// `teensyMac` helper.
+byte mac[6];
+
+// Enter websockets server port.
+const uint16_t port = 80;
+
+// Define how many clients we accpet simultaneously.
+const byte maxClients = 4;
+
+WebsocketsClient clients[maxClients];
+WebsocketsServer server;
+
+void setup() {
+  // Set the MAC address.
+  teensyMAC(mac);
+
+  // Connect to ethernet.
+  if (Ethernet.begin(mac)) {
+    Serial.println("Ethernet connected");
+  } else {
+    Serial.println("Ethernet failed");
+  }
+
+  // Start websockets server.
+  server.listen(port);
+  if (server.available()) {
+    Serial.print("Server available at ws://");
+    Serial.print(Ethernet.localIP());
+    // Also log any non default port.
+    if (port != 80) Serial.printf(":%d", port);
+    Serial.println();
+  } else {
+    Serial.println("Server not available!");
+  }  
+}
+
+void handleMessage(WebsocketsClient &client, WebsocketsMessage message) {
+  auto data = message.data();
+
+  // Log message
+  Serial.print("Got Message: ");
+  Serial.println(data);
+
+  // Echo message
+  client.send("Echo: " + data);
+}
+
+void handleEvent(WebsocketsClient &client, WebsocketsEvent event, String data) {
+  if (event == WebsocketsEvent::ConnectionClosed) {
+    Serial.println("Connection closed");
+  }
+}
+
+int8_t getFreeClientIndex() {
+  // If a client in our list is not available, it's connection is closed and we
+  // can use it for a new client.  
+  for (byte i = 0; i < maxClients; i++) {
+    if (!clients[i].available()) return i;
+  }
+  return -1;
+}
+
+void listenForClients() {
+  if (server.poll()) {
+    int8_t freeIndex = getFreeClientIndex();
+    if (freeIndex >= 0) {
+      WebsocketsClient newClient = server.accept();
+      Serial.printf("Accepted new websockets client at index %d\n", freeIndex);
+      newClient.onMessage(handleMessage);
+      newClient.onEvent(handleEvent);
+      newClient.send("Hello from Teensy");
+      clients[freeIndex] = newClient;
+    }
+  }
+}
+
+void pollClients() {
+  for (byte i = 0; i < maxClients; i++) {
+    clients[i].poll();
+  }
+}
+
+void loop() {
+  listenForClients();
+  pollClients();
+}

+ 115 - 0
Esp32-Client/ArduinoWebsockets/examples/Secured-Esp32-Client/Secured-Esp32-Client.ino

@@ -0,0 +1,115 @@
+/*
+	Secured Esp32 Websockets Client
+
+	This sketch:
+        1. Connects to a WiFi network
+        2. Connects to a Websockets server (using WSS)
+        3. Sends the websockets server a message ("Hello Server")
+        4. Sends the websocket server a "ping"
+        5. Prints all incoming messages while the connection is open
+
+    NOTE:
+    The sketch dosen't check or indicate about errors while connecting to 
+    WiFi or to the websockets server. For full example you might want 
+    to try the example named "Esp32-Client" (And use the ssl methods).
+
+	Hardware:
+        For this sketch you only need an Esp32 board.
+
+	Created 15/02/2019
+	By Gil Maimon
+	https://github.com/gilmaimon/ArduinoWebsockets
+
+*/
+
+#include <ArduinoWebsockets.h>
+#include <WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+
+const char* websockets_connection_string = "wss://echo.websocket.org/"; //Enter server adress
+
+// This certificate was updated 20.04.2019
+const char echo_org_ssl_ca_cert[] PROGMEM = \
+    "-----BEGIN CERTIFICATE-----\n" \
+    "MIIEkjCCA3qgAwIBAgIQCgFBQgAAAVOFc2oLheynCDANBgkqhkiG9w0BAQsFADA/\n" \
+    "MSQwIgYDVQQKExtEaWdpdGFsIFNpZ25hdHVyZSBUcnVzdCBDby4xFzAVBgNVBAMT\n" \
+    "DkRTVCBSb290IENBIFgzMB4XDTE2MDMxNzE2NDA0NloXDTIxMDMxNzE2NDA0Nlow\n" \
+    "SjELMAkGA1UEBhMCVVMxFjAUBgNVBAoTDUxldCdzIEVuY3J5cHQxIzAhBgNVBAMT\n" \
+    "GkxldCdzIEVuY3J5cHQgQXV0aG9yaXR5IFgzMIIBIjANBgkqhkiG9w0BAQEFAAOC\n" \
+    "AQ8AMIIBCgKCAQEAnNMM8FrlLke3cl03g7NoYzDq1zUmGSXhvb418XCSL7e4S0EF\n" \
+    "q6meNQhY7LEqxGiHC6PjdeTm86dicbp5gWAf15Gan/PQeGdxyGkOlZHP/uaZ6WA8\n" \
+    "SMx+yk13EiSdRxta67nsHjcAHJyse6cF6s5K671B5TaYucv9bTyWaN8jKkKQDIZ0\n" \
+    "Z8h/pZq4UmEUEz9l6YKHy9v6Dlb2honzhT+Xhq+w3Brvaw2VFn3EK6BlspkENnWA\n" \
+    "a6xK8xuQSXgvopZPKiAlKQTGdMDQMc2PMTiVFrqoM7hD8bEfwzB/onkxEz0tNvjj\n" \
+    "/PIzark5McWvxI0NHWQWM6r6hCm21AvA2H3DkwIDAQABo4IBfTCCAXkwEgYDVR0T\n" \
+    "AQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwfwYIKwYBBQUHAQEEczBxMDIG\n" \
+    "CCsGAQUFBzABhiZodHRwOi8vaXNyZy50cnVzdGlkLm9jc3AuaWRlbnRydXN0LmNv\n" \
+    "bTA7BggrBgEFBQcwAoYvaHR0cDovL2FwcHMuaWRlbnRydXN0LmNvbS9yb290cy9k\n" \
+    "c3Ryb290Y2F4My5wN2MwHwYDVR0jBBgwFoAUxKexpHsscfrb4UuQdf/EFWCFiRAw\n" \
+    "VAYDVR0gBE0wSzAIBgZngQwBAgEwPwYLKwYBBAGC3xMBAQEwMDAuBggrBgEFBQcC\n" \
+    "ARYiaHR0cDovL2Nwcy5yb290LXgxLmxldHNlbmNyeXB0Lm9yZzA8BgNVHR8ENTAz\n" \
+    "MDGgL6AthitodHRwOi8vY3JsLmlkZW50cnVzdC5jb20vRFNUUk9PVENBWDNDUkwu\n" \
+    "Y3JsMB0GA1UdDgQWBBSoSmpjBH3duubRObemRWXv86jsoTANBgkqhkiG9w0BAQsF\n" \
+    "AAOCAQEA3TPXEfNjWDjdGBX7CVW+dla5cEilaUcne8IkCJLxWh9KEik3JHRRHGJo\n" \
+    "uM2VcGfl96S8TihRzZvoroed6ti6WqEBmtzw3Wodatg+VyOeph4EYpr/1wXKtx8/\n" \
+    "wApIvJSwtmVi4MFU5aMqrSDE6ea73Mj2tcMyo5jMd6jmeWUHK8so/joWUoHOUgwu\n" \
+    "X4Po1QYz+3dszkDqMp4fklxBwXRsW10KXzPMTZ+sOPAveyxindmjkW8lGy+QsRlG\n" \
+    "PfZ+G6Z6h7mjem0Y+iWlkYcV4PIWL1iwBi8saCbGS5jN2p8M+X+Q7UNKEkROb3N6\n" \
+    "KOqkqm57TH2H3eDJAkSnh6/DNFu0Qg==\n" \
+    "-----END CERTIFICATE-----\n";
+
+using namespace websockets;
+
+void onMessageCallback(WebsocketsMessage message) {
+    Serial.print("Got Message: ");
+    Serial.println(message.data());
+}
+
+void onEventsCallback(WebsocketsEvent event, String data) {
+    if(event == WebsocketsEvent::ConnectionOpened) {
+        Serial.println("Connnection Opened");
+    } else if(event == WebsocketsEvent::ConnectionClosed) {
+        Serial.println("Connnection Closed");
+    } else if(event == WebsocketsEvent::GotPing) {
+        Serial.println("Got a Ping!");
+    } else if(event == WebsocketsEvent::GotPong) {
+        Serial.println("Got a Pong!");
+    }
+}
+
+WebsocketsClient client;
+void setup() {
+    Serial.begin(115200);
+    // Connect to wifi
+    WiFi.begin(ssid, password);
+
+    // Wait some time to connect to wifi
+    for(int i = 0; i < 10 && WiFi.status() != WL_CONNECTED; i++) {
+        Serial.print(".");
+        delay(1000);
+    }
+
+    // run callback when messages are received
+    client.onMessage(onMessageCallback);
+    
+    // run callback when events are occuring
+    client.onEvent(onEventsCallback);
+
+    // Before connecting, set the ssl fingerprint of the server
+    client.setCACert(echo_org_ssl_ca_cert);
+
+    // Connect to server
+    client.connect(websockets_connection_string);
+
+    // Send a message
+    client.send("Hello Server");
+
+    // Send a ping
+    client.ping();
+}
+
+void loop() {
+    client.poll();
+}

+ 88 - 0
Esp32-Client/ArduinoWebsockets/examples/Secured-Esp8266-Client/Secured-Esp8266-Client.ino

@@ -0,0 +1,88 @@
+/*
+	Secured Esp8266 Websockets Client
+
+	This sketch:
+        1. Connects to a WiFi network
+        2. Connects to a Websockets server (using WSS)
+        3. Sends the websockets server a message ("Hello Server")
+        4. Sends the websocket server a "ping"
+        5. Prints all incoming messages while the connection is open
+
+    NOTE:
+    The sketch dosen't check or indicate about errors while connecting to 
+    WiFi or to the websockets server. For full example you might want 
+    to try the example named "Esp8266-Client" (And use the ssl methods).
+
+	Hardware:
+        For this sketch you only need an ESP8266 board.
+
+	Created 15/02/2019
+	By Gil Maimon
+	https://github.com/gilmaimon/ArduinoWebsockets
+
+*/
+
+#include <ArduinoWebsockets.h>
+#include <ESP8266WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+
+const char* websockets_connection_string = "wss://echo.websocket.org/"; //Enter server adress
+
+// This fingerprint was updated 20.04.2019
+const char echo_org_ssl_fingerprint[] PROGMEM = "E0 E7 13 AE F4 38 0F 7F 22 39 5C 32 B3 16 EC BB 95 F3 0B 5B";
+
+using namespace websockets;
+
+void onMessageCallback(WebsocketsMessage message) {
+    Serial.print("Got Message: ");
+    Serial.println(message.data());
+}
+
+void onEventsCallback(WebsocketsEvent event, String data) {
+    if(event == WebsocketsEvent::ConnectionOpened) {
+        Serial.println("Connnection Opened");
+    } else if(event == WebsocketsEvent::ConnectionClosed) {
+        Serial.println("Connnection Closed");
+    } else if(event == WebsocketsEvent::GotPing) {
+        Serial.println("Got a Ping!");
+    } else if(event == WebsocketsEvent::GotPong) {
+        Serial.println("Got a Pong!");
+    }
+}
+
+WebsocketsClient client;
+void setup() {
+    Serial.begin(115200);
+    // Connect to wifi
+    WiFi.begin(ssid, password);
+
+    // Wait some time to connect to wifi
+    for(int i = 0; i < 10 && WiFi.status() != WL_CONNECTED; i++) {
+        Serial.print(".");
+        delay(1000);
+    }
+
+    // run callback when messages are received
+    client.onMessage(onMessageCallback);
+    
+    // run callback when events are occuring
+    client.onEvent(onEventsCallback);
+
+    // Before connecting, set the ssl fingerprint of the server
+    client.setFingerprint(echo_org_ssl_fingerprint);
+
+    // Connect to server
+    client.connect(websockets_connection_string);
+
+    // Send a message
+    client.send("Hello Server");
+
+    // Send a ping
+    client.ping();
+}
+
+void loop() {
+    client.poll();
+}

+ 187 - 0
Esp32-Client/ArduinoWebsockets/examples/SecuredTwoWay-Esp8266-Client/SecuredTwoWay-Esp8266-Client.ino

@@ -0,0 +1,187 @@
+/*
+	Secured Esp8266 Websockets Client
+
+	This sketch:
+        1. Connects to a WiFi network
+        2. Connects to a Websockets server (using WSS)
+        3. Sends the websockets server a message ("Hello Server")
+        4. Sends the websocket server a "ping"
+        5. Prints all incoming messages while the connection is open
+
+    NOTE:
+    The sketch dosen't check or indicate about errors while connecting to 
+    WiFi or to the websockets server. For full example you might want 
+    to try the example named "Esp8266-Client" (And use the ssl methods).
+
+	Hardware:
+        For this sketch you only need an ESP8266 board.
+
+	Created 15/02/2019
+	By Gil Maimon
+	https://github.com/gilmaimon/ArduinoWebsockets
+
+*/
+
+#include <ArduinoWebsockets.h>
+#include <ESP8266WiFi.h>
+
+const char* ssid = "ssid"; //Enter SSID
+const char* password = "password"; //Enter Password
+
+const char* websockets_connection_string = "wss://echo.websocket.org/"; //Enter server adress
+
+/* NTP Time Servers */
+const char *ntp1 = "time.windows.com";
+const char *ntp2 = "pool.ntp.org";
+time_t now;
+
+// The hardcoded certificate authority for this example.
+// Don't use it on your own apps!!!!!
+const char ca_cert[] PROGMEM = R"EOF(
+-----BEGIN CERTIFICATE-----
+MIIC1TCCAb2gAwIBAgIJAMPt1Ms37+hLMA0GCSqGSIb3DQEBCwUAMCExCzAJBgNV
+BAYTAlVTMRIwEAYDVQQDDAkxMjcuMC4wLjMwHhcNMTgwMzE0MDQyMTU0WhcNMjkw
+NTMxMDQyMTU0WjAhMQswCQYDVQQGEwJVUzESMBAGA1UEAwwJMTI3LjAuMC4zMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxsa4qU/tlzN4YTcnn/I/ffsi
+jOPc8QRcwClKzasIZNFEye4uThl+LGZWFIFb8X8Dc+xmmBaWlPJbqtphgFKStpar
+DdduHSW1ud6Y1FVKxljo3UwCMrYm76Q/jNzXJvGs6Z1MDNsVZzGJaoqit2H2Hkvk
+y+7kk3YbEDlcyVsLOw0zCKL4cd2DSNDyhIZxWo2a8Qn5IdjWAYtsTnW6MvLk/ya4
+abNeRfSZwi+r37rqi9CIs++NpL5ynqkKKEMrbeLactWgHbWrZeaMyLpuUEL2GF+w
+MRaAwaj7ERwT5gFJRqYwj6bbfIdx5PC7h7ucbyp272MbrDa6WNBCMwQO222t4wID
+AQABoxAwDjAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCmXfrC42nW
+IpL3JDkB8YlB2QUvD9JdMp98xxo33+xE69Gov0e6984F1Gluao0p6sS7KF+q3YLS
+4hjnzuGzF9GJMimIB7NMQ20yXKfKpmKJ7YugMaKTDWDhHn5679mKVbLSQxHCUMEe
+tEnMT93/UaDbWBjV6zu876q5vjPMYgDHODqO295ySaA71UkijaCn6UwKUT49286T
+V9ZtzgabNGHXfklHgUPWoShyze+G3g29I1BR0qABoJI63zaNu8ua42v5g1RldxsW
+X8yKI14mFOGxuvcygG8L2xxysW7Zq+9g+O7gW0Pm6RDYnUQmIwY83h1KFCtYCJdS
+2PgozwkkUNyP
+-----END CERTIFICATE-----
+)EOF";
+
+// The client's private key which must be kept secret
+const char client_private_key[] PROGMEM = R"EOF(
+-----BEGIN RSA PRIVATE KEY-----
+MIIEowIBAAKCAQEAsRNVTvqP++YUh8NrbXwE83xVsDqcB3F76xcXNKFDERfVd2P/
+LvyDovCcoQtT0UCRgPcxRp894EuPH/Ru6Z2Lu85sV//i7ce27tc2WRFSfuhlRxHP
+LJWHxTl1CEfXp/owkECQ4MB3pw6Ekc16iTEPiezTG+T+mQ/BkiIwcIK6CMlpR9DI
+eYUTqv0f9NrUfAjdBrqlEO2gpgFvLFrkDEU2ntAIc4aPOP7yDOym/xzfy6TiG8Wo
+7nlh6M97xTZGfbEPCH9rZDjo5istym1HzF5P+COq+OTSPscjFGXoi978o6hZwa7i
+zxorg4h5a5lGnshRu2Gl+Ybfa14OwnIrv/yCswIDAQABAoIBAHxwgbsHCriTcEoY
+Yx6F0VTrQ6ydA5mXfuYvS/eIfIE+pp1IgMScYEXZobjrJPQg1CA1l0NyFSHS97oV
+JPy34sMQxcLx6KABgeVHCMJ/EeJtnv7a3SUP0GIhhsVS95Lsl8RIG4hWub+EzFVK
+eZqAB9N9wr4Pp3wZPodbz37B38rb1QPyMFmQOLlHjKTOmoxsXhL2ot+R3+aLYSur
+oPO1kQo7/d0UAZoy8h9OQN4a2EXvawh4O2EvFGbc5X/yXwAdEQ4NPp9VZhkNIRkV
++XZ3FcIqEVOploKtRF/tVBTz3g61/lFz21L9PMmV5y8tvSafr2SpJugGVmp2rrVQ
+VNyGlIECgYEA10JSI5gmeCU3zK6kvOfBp54hY/5dDrSUpjKkMxpmm7WZQ6Il/k7A
+hMcLeMzHiriT7WhRIXF8AOr2MoEkHkH3DhVNN4ccieVZx2SE5P5mVkItZGLrrpfU
+dysR/ARAI1HYegGUiKacZtf9SrRavU0m7fOVOiYwbFRhjyX+MyuteYkCgYEA0pbz
+4ZosetScP68uZx1sGlTfkcqLl7i15DHk3gnj6jKlfhvC2MjeLMhNDtKeUAuY7rLQ
+guZ0CCghWAv0Glh5eYdfIiPhgqFfX4P5F3Om4zQHVPYj8xHfHG4ZP7dKQTndrO1Q
+fLdGDTQLVXabAUSp2YGrijC8J9idSW1pYClvF1sCgYEAjkDn41nzYkbGP1/Swnwu
+AEWCL4Czoro32jVxScxSrugt5wJLNWp508VukWBTJhugtq3Pn9hNaJXeKbYqVkyl
+pgrxwpZph7+nuxt0r5hnrO2C7eppcjIoWLB/7BorAKxf8REGReBFT7nBTBMwPBW2
+el4U6h6+tXh2GJG1Eb/1nnECgYAydVb0THOx7rWNkNUGggc/++why61M6kYy6j2T
+cj05BW+f2tkCBoctpcTI83BZb53yO8g4RS2yMqNirGKN2XspwmTqEjzbhv0KLt4F
+X4GyWOoU0nFksXiLIFpOaQWSwWG7KJWrfGJ9kWXR0Xxsfl5QLoDCuNCsn3t4d43T
+K7phlwKBgHDzF+50+/Wez3YHCy2a/HgSbHCpLQjkknvgwkOh1z7YitYBUm72HP8Z
+Ge6b4wEfNuBdlZll/y9BQQOZJLFvJTE5t51X9klrkGrOb+Ftwr7eI/H5xgcadI52
+tPYglR5fjuRF/wnt3oX9JlQ2RtSbs+3naXH8JoherHaqNn8UpH0t
+-----END RSA PRIVATE KEY-----
+)EOF";
+
+// The clint's public certificate which must be shared
+const char client_cert[] PROGMEM = R"EOF(
+-----BEGIN CERTIFICATE-----
+MIIDTzCCAjcCCQDPXvMRYOpeuDANBgkqhkiG9w0BAQsFADCBpjESMBAGA1UEAwwJ
+MTI3LjAuMC4xMQswCQYDVQQGEwJVUzElMCMGA1UECgwcTXkgT3duIENlcnRpZmlj
+YXRlIEF1dGhvcml0eTEUMBIGA1UECAwLQXJkdWlub0xhbmQxFTATBgNVBAcMDEFy
+ZHVpbm9WaWxsZTEVMBMGA1UECgwMRVNQODI2NlVzZXJzMRgwFgYDVQQLDA9FU1A4
+MjY2LUFyZHVpbm8wHhcNMTgwMzE0MDQwMDAwWhcNMjkwMjI0MDQwMDAwWjAsMRYw
+FAYDVQQKDA1NeSBTZXJ2ZXIgT3JnMRIwEAYDVQQDDAkxMjcuMC4wLjMwggEiMA0G
+CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCxE1VO+o/75hSHw2ttfATzfFWwOpwH
+cXvrFxc0oUMRF9V3Y/8u/IOi8JyhC1PRQJGA9zFGnz3gS48f9G7pnYu7zmxX/+Lt
+x7bu1zZZEVJ+6GVHEc8slYfFOXUIR9en+jCQQJDgwHenDoSRzXqJMQ+J7NMb5P6Z
+D8GSIjBwgroIyWlH0Mh5hROq/R/02tR8CN0GuqUQ7aCmAW8sWuQMRTae0Ahzho84
+/vIM7Kb/HN/LpOIbxajueWHoz3vFNkZ9sQ8If2tkOOjmKy3KbUfMXk/4I6r45NI+
+xyMUZeiL3vyjqFnBruLPGiuDiHlrmUaeyFG7YaX5ht9rXg7Cciu//IKzAgMBAAEw
+DQYJKoZIhvcNAQELBQADggEBAEnG+FNyNCOkBvzHiUpHHpScxZqM2f+XDcewJgeS
+L6HkYEDIZZDNnd5gduSvkHpdJtWgsvJ7dJZL40w7Ba5sxpZHPIgKJGl9hzMkG+aA
+z5GMkjys9h2xpQZx9KL3q7G6A+C0bll7ODZlwBtY07CFMykT4Mp2oMRrQKRucMSV
+AB1mKujLAnMRKJ3NM89RQJH4GYiRps9y/HvM5lh7EIK/J0/nEZeJxY5hJngskPKb
+oPPdmkR97kaQnll4KNsC3owVlHVU2fMftgYkgQLzyeWgzcNa39AF3B6JlcOzNyQY
+seoK24dHmt6tWmn/sbxX7Aa6TL/4mVlFoOgcaTJyVaY/BrY=
+-----END CERTIFICATE-----
+)EOF";
+
+using namespace websockets;
+
+void onMessageCallback(WebsocketsMessage message) {
+    Serial.print("Got Message: ");
+    Serial.println(message.data());
+}
+
+void onEventsCallback(WebsocketsEvent event, String data) {
+    if(event == WebsocketsEvent::ConnectionOpened) {
+        Serial.println("Connnection Opened");
+    } else if(event == WebsocketsEvent::ConnectionClosed) {
+        Serial.println("Connnection Closed");
+    } else if(event == WebsocketsEvent::GotPing) {
+        Serial.println("Got a Ping!");
+    } else if(event == WebsocketsEvent::GotPong) {
+        Serial.println("Got a Pong!");
+    }
+}
+
+WebsocketsClient client;
+void setup() {
+    Serial.begin(115200);
+    // Connect to wifi
+    WiFi.begin(ssid, password);
+
+    Serial.println("Connecting to WiFi...");
+
+    // Wait until we are connected to WiFi
+    while(WiFi.status() != WL_CONNECTED) {
+      Serial.print(".");
+      delay(1000);
+    }
+
+    Serial.println("Successfully connected to WiFi, setting time... ");
+
+    // We configure ESP8266's time, as we need it to validate the certificates
+    configTime(2 * 3600, 1, ntp1, ntp2);
+    while(now < 2 * 3600) {
+        Serial.print(".");
+        delay(500);
+        now = time(nullptr);
+    }
+    Serial.println("");
+    Serial.println("Time set, connecting to server...");
+
+    // run callback when messages are received
+    client.onMessage(onMessageCallback);
+    
+    // run callback when events are occuring
+    client.onEvent(onEventsCallback);
+
+    // Before connecting, set the ssl certificates and key of the server
+    X509List cert(ca_cert);
+    client.setTrustAnchors(&cert);
+
+    X509List *serverCertList = new X509List(client_cert);
+    PrivateKey *serverPrivKey = new PrivateKey(client_private_key);
+    client.setClientRSACert(serverCertList, serverPrivKey);
+    
+    // Connect to server
+    client.connect(websockets_connection_string);
+
+    // Send a message
+    client.send("Hello Server");
+
+    // Send a ping
+    client.ping();
+}
+
+void loop() {
+    client.poll();
+}

+ 243 - 0
Esp32-Client/ArduinoWebsockets/examples/SocketAndHttp-Teensy41-Server/SocketAndHttp-Teensy41-Server.ino

@@ -0,0 +1,243 @@
+/*
+  Teensy41 Websockets Server and Http Server (using NativeEthernet).
+  Combining the Teensy41-Server-Multiple-Clients example with the NativeEthernet
+  WebServer example (https://github.com/vjmuzik/NativeEthernet/blob/master/examples/WebServer/WebServer.ino).
+
+  This sketch:
+  1. Connects to a ethernet network
+  2. Starts a websocket server on port 3000
+  3. Starts a http server at the default port 80
+  4. Waits for both http and websockets connections
+  5. Once a http client connects, it serves an html document, once a socket
+     client wants to connect, it checks whether a free slot is available and
+     accepts it accordingly
+  5. If the socket client is accepted it sends a welcome message and echoes any
+     messages from the client
+  6. Goes back to step 4
+
+  Note:
+  Make sure you share your computer's internet connection with the Teensy
+  via ethernet.
+
+  Libraries:
+  To use this sketch install
+  * TeensyID library (https://github.com/sstaub/TeensyID)
+  * NativeEthernet (https://github.com/vjmuzik/NativeEthernet)
+
+  Hardware:
+  For this sketch you need a Teensy 4.1 board and the Teensy 4.1 Ethernet Kit
+  (https://www.pjrc.com/store/ethernet_kit.html).
+*/
+
+#include <NativeEthernet.h>
+#include <ArduinoWebsockets.h>
+#include <TeensyID.h>
+
+using namespace websockets;
+
+// We will set the MAC address at the beginning of `setup()` using TeensyID's
+// `teensyMac` helper.
+byte mac[6];
+
+// Enter websockets server port.
+const uint16_t websocketsPort = 3000;
+
+// Define how many clients we accpet simultaneously.
+const byte maxSocketClients = 4;
+
+WebsocketsClient socketClients[maxSocketClients];
+WebsocketsServer socketServer;
+EthernetServer httpServer;
+
+void setup() {
+  // Set the MAC address.
+  teensyMAC(mac);
+
+  // Start Serial and wait until it is ready.
+  Serial.begin(9600);
+  while (!Serial) {}
+
+  // Connect to ethernet.
+  if (Ethernet.begin(mac)) {
+    Serial.println("Ethernet connected");
+  } else {
+    Serial.println("Ethernet failed");
+  }
+
+  // Start websockets server.
+  socketServer.listen(websocketsPort);
+  if (!socketServer.available()) {
+    Serial.println("Websockets Server not available!");
+  }
+
+  // Start http server.
+  httpServer.begin(80);
+  Serial.print("Visit http://");
+  Serial.print(Ethernet.localIP());
+  Serial.println(" in the browser to connect.");
+}
+
+int8_t getFreeSocketClientIndex() {
+  // If a client in our list is not available, it's connection is closed and we
+  // can use it for a new client.  
+  for (byte i = 0; i < maxSocketClients; i++) {
+    if (!socketClients[i].available()) return i;
+  }
+  return -1;
+}
+
+void handleMessage(WebsocketsClient &client, WebsocketsMessage message) {
+  auto data = message.data();
+
+  // Log message
+  Serial.print("Got Message: ");
+  Serial.println(data);
+
+  // Echo message
+  client.send("Echo: " + data);
+}
+
+void handleEvent(WebsocketsClient &client, WebsocketsEvent event, String data) {
+  if (event == WebsocketsEvent::ConnectionClosed) {
+    Serial.println("Connection closed");
+  }
+}
+
+void listenForSocketClients() {
+  if (socketServer.poll()) {
+    int8_t freeIndex = getFreeSocketClientIndex();
+    if (freeIndex >= 0) {
+      WebsocketsClient newClient = socketServer.accept();
+      Serial.printf("Accepted new websockets client at index %d\n", freeIndex);
+      newClient.onMessage(handleMessage);
+      newClient.onEvent(handleEvent);
+      newClient.send("Hello from Teensy");
+      socketClients[freeIndex] = newClient;
+    }
+  }
+}
+
+void pollSocketClients() {
+  for (byte i = 0; i < maxSocketClients; i++) {
+    socketClients[i].poll();
+  }
+}
+
+void sendHttpReply(EthernetClient &client) {
+  // Send a website that connects to the websocket server and allows to
+  // communicate with the teensy.
+
+  const char* header = 
+    "HTTP/1.1 200 OK\r\n"
+    "Content-Type: text/html\r\n"
+    "Connection: close\r\n"
+    "\r\n";
+
+  const char* document = 
+    "<!DOCTYPE html>\n"
+    "<title>Teensy 4.1 Websockets</title>\n"
+    "<meta charset='UTF-8'>\n"
+    "<style>\n"
+    "  body {\n"
+    "    display: grid;\n"
+    "    grid-template: min-content auto / auto min-content;\n"
+    "    grid-gap: 1em;\n"
+    "    margin: 0;\n"
+    "    padding: 1em;\n"
+    "    height: 100vh;\n"
+    "    box-sizing: border-box;\n"
+    "  }\n"
+    "  #output {\n"
+    "    grid-column-start: span 2;\n"
+    "    overflow-y: scroll;\n"
+    "    padding: 0.1em;\n"
+    "    border: 1px solid;\n"
+    "    font-family: monospace;\n"
+    "  }\n"
+    "</style>\n"
+    "<input type='text' id='message' placeholder='Send a message and Teensy will echo it back!'>\n"
+    "<button id='send-message'>send</button>\n"
+    "<div id='output'></div>\n"
+    "<script>\n"
+    "  const url = `ws://${window.location.host}:3000`\n"
+    "  const ws = new WebSocket(url)\n"
+    "  let connected = false\n"
+    "  const sendMessage = document.querySelector('#send-message')\n"
+    "  const message = document.querySelector('#message')\n"
+    "  const output = document.querySelector('#output')\n"
+    "  function log(message, color = 'black') {\n"
+    "    const el = document.createElement('div')\n"
+    "    el.innerHTML = message\n"
+    "    el.style.color = color\n"
+    "    output.append(el)\n"
+    "    output.scrollTop = output.scrollHeight\n"
+    "  }\n"
+    "  ws.addEventListener('open', () => {\n"
+    "    connected = true\n"
+    "    log('(✔️) Open', 'green')\n"
+    "  })\n"
+    "  ws.addEventListener('close', () => {\n"
+    "    connected = false\n"
+    "    log('(❌) Close', 'red')\n"
+    "  })\n"
+    "  ws.addEventListener('message', ({ data }) =>\n"
+    "    log(`(💌) ${data}`)\n"
+    "  )\n"
+    "  sendMessage.addEventListener('click', () => {\n"
+    "    connected && ws.send(message.value)\n"
+    "  })\n"
+    "  message.addEventListener('keyup', ({ keyCode }) => {\n"
+    "     connected && keyCode === 13 && ws.send(message.value)\n"
+    "  })\n"
+    "  log(`(📡) Connecting to ${url} ...`, 'blue')\n"
+    "</script>\n";
+
+  client.write(header);
+  client.write(document);  
+}
+
+void listenForHttpClients() {
+  // Listen for incoming http clients.
+  EthernetClient client = httpServer.available();
+
+  if (client) {
+    Serial.println("Http client connected!");
+
+    // An http request ends with a blank line.
+    bool currentLineIsBlank = true;
+
+    while (client.connected()) {
+      if (client.available()) {
+        char c = client.read();
+
+        if (c == '\n' && currentLineIsBlank) {
+          // If we've gotten to the end of the line (received a newline
+          // character) and the line is blank, the http request has ended,
+          // so we can send a reply.
+          sendHttpReply(client);
+          break;
+        } else if (c == '\n') {
+          // Starting a new line.
+          currentLineIsBlank = true;
+        } else if (c != '\r') {
+          // Read a character on the current line.
+          currentLineIsBlank = false;
+        }
+      }
+    }
+
+    // The NativeEthernet's WebServer example adds a small delay here. For me it
+    // seems to work without the delay. Uncomment to following line if you have
+    // issues connecting to the website in the browser.
+    // delay(1);
+
+    // Close the connection.
+    client.stop();
+  }
+}
+
+void loop() {
+  listenForSocketClients();
+  pollSocketClients();
+  listenForHttpClients();
+}

+ 78 - 0
Esp32-Client/ArduinoWebsockets/examples/Teensy41-Client/Teens41-Client.ino

@@ -0,0 +1,78 @@
+/*
+  Teensy41 Websockets Client (using NativeEthernet)
+
+  This sketch:
+  1. Connects to a ethernet network
+  2. Connects to a websockets server at port 80
+  3. Sends the websockets server a message ("Hello Server")
+  4. Prints all incoming messages while the connection is open
+
+  Note:
+  Make sure you share your computer's internet connection with the Teensy
+  via ethernet.
+
+  Libraries:
+  To use this sketch install
+  * TeensyID library (https://github.com/sstaub/TeensyID)
+  * NativeEthernet (https://github.com/vjmuzik/NativeEthernet)
+
+  Hardware:
+  For this sketch you need a Teensy 4.1 board and the Teensy 4.1 Ethernet Kit
+  (https://www.pjrc.com/store/ethernet_kit.html).
+*/
+
+#include <NativeEthernet.h>
+#include <ArduinoWebsockets.h>
+#include <TeensyID.h>
+#include <SPI.h>
+
+using namespace websockets;
+WebsocketsClient client;
+
+// We will set the MAC address at the beginning of `setup()` using TeensyID's
+// `teensyMac` helper.
+byte mac[6];
+
+// Enter websockets url.
+// Note: wss:// currently not working.
+const char* url  = "ws://echo.websocket.org";
+
+void setup() {
+  // Set the MAC address.
+  teensyMAC(mac);
+
+  // Start Serial and wait until it is ready.
+  Serial.begin(9600);
+  while (!Serial) {}
+
+  // Connect to ethernet.
+  if (Ethernet.begin(mac)) {
+    Serial.print("Ethernet connected (");
+    Serial.print(Ethernet.localIP());
+    Serial.println(")");
+  } else {
+    Serial.println("Ethernet failed");
+  }
+
+  // Connect to websocket server.
+  if (client.connect(url)) {
+    Serial.printf("Connected to server %s\n", url);
+    // Send welcome message.
+    client.send("Hello Server");
+  } else {
+    Serial.println("Couldn't connect to server!");
+  }
+
+  // Run callback when messages are received.
+  client.onMessage([&](WebsocketsMessage message) {
+    Serial.print("Got Message: ");
+    Serial.println(message.data());
+  });	
+}
+
+void loop() {
+  // Check for incoming messages.
+  if (client.available()) {
+    client.poll();
+  }
+}

+ 86 - 0
Esp32-Client/ArduinoWebsockets/examples/Teensy41-Server/Teens41-Server.ino

@@ -0,0 +1,86 @@
+/*
+  Teensy41 Websockets Server (using NativeEthernet)
+
+  This sketch:
+  1. Connects to a ethernet network
+  2. Starts a websocket server on port 80
+  3. Waits for connections
+  4. Once a client connects, it wait for a message from the client
+  5. Echoes the message to the client
+  6. Closes the connection and goes back to step 3
+
+  Note:
+  Make sure you share your computer's internet connection with the Teensy
+  via ethernet.
+
+  Libraries:
+  To use this sketch install
+  * TeensyID library (https://github.com/sstaub/TeensyID)
+  * NativeEthernet (https://github.com/vjmuzik/NativeEthernet)
+
+  Hardware:
+  For this sketch you need a Teensy 4.1 board and the Teensy 4.1 Ethernet Kit
+  (https://www.pjrc.com/store/ethernet_kit.html).
+*/
+
+#include <NativeEthernet.h>
+#include <ArduinoWebsockets.h>
+#include <TeensyID.h>
+
+using namespace websockets;
+WebsocketsServer server;
+
+// We will set the MAC address at the beginning of `setup()` using TeensyID's
+// `teensyMac` helper.
+byte mac[6];
+
+// Enter websockets server port.
+const uint16_t port = 80;
+
+void setup() {
+  // Set the MAC address.
+  teensyMAC(mac);
+
+  // Start Serial and wait until it is ready.
+  Serial.begin(9600);
+  while (!Serial) {}
+
+  // Connect to ethernet.
+  if (Ethernet.begin(mac)) {
+    Serial.println("Ethernet connected");
+  } else {
+    Serial.println("Ethernet failed");
+  }
+
+  // Start websockets server.
+  server.listen(port);
+  if (server.available()) {
+    Serial.print("Server available at ws://");
+    Serial.print(Ethernet.localIP());
+    // Also log any non default port.
+    if (port != 80) Serial.printf(":%d", port);
+    Serial.println();
+  } else {
+    Serial.println("Server not available!");
+  }
+}
+
+void loop() {
+  WebsocketsClient client = server.accept();
+  
+  if (client.available()) {
+    Serial.println("Client connected");
+
+    // Read message from client and log it.
+    WebsocketsMessage msg = client.readBlocking();
+    Serial.print("Got Message: ");
+    Serial.println(msg.data());
+
+    // Echo the message.
+    client.send("Echo: " + msg.data());
+
+    // Close the connection.
+    client.close();
+    Serial.println("Client closed");
+  }
+}

+ 82 - 0
Esp32-Client/ArduinoWebsockets/keywords.txt

@@ -0,0 +1,82 @@
+websockets	KEYWORD1
+network	KEYWORD1
+WebsocketsClient	KEYWORD1
+WebsocketsServer	KEYWORD1
+
+connect	KEYWORD2
+send	KEYWORD2
+sendBinary	KEYWORD2
+onMessage	KEYWORD2
+onEvent	KEYWORD2
+available	KEYWORD2
+poll	KEYWORD2
+ping	KEYWORD2
+pong	KEYWORD2
+close	KEYWORD2
+setCACert	KEYWORD2
+setFingerprint	KEYWORD2
+setInsecure	KEYWORD2
+readBlocking	KEYWORD2
+addHeader	KEYWORD2
+
+setFragmentsPolicy	KEYWORD2
+getFragmentsPolicy	KEYWORD2
+getCloseReason	KEYWORD2
+
+
+# Server
+listen	KEYWORD2
+poll	KEYWORD2
+accept	KEYWORD2
+
+# Message
+isEmpty	KEYWORD2
+isText	KEYWORD2
+isBinary	KEYWORD2
+isPing	KEYWORD2
+isPong	KEYWORD2
+isClose	KEYWORD2
+isComplete	KEYWORD2
+isPartial	KEYWORD2
+isFirst	KEYWORD2
+isContinuation	KEYWORD2
+isLast	KEYWORD2
+
+WebsocketsMessage	KEYWORD1
+data	KEYWORD2
+type	KEYWORD2
+rawData	KEYWORD2
+c_str	KEYWORD2
+
+WebsocketsEvent	KEYWORD1
+ConnectionOpened	LITERAL1
+ConnectionClosed	LITERAL1
+GotPing	LITERAL1
+GotPong	LITERAL1
+
+WSString	KEYWORD1
+MessageType	KEYWORD1
+Text	LITERAL1
+Binary	LITERAL1
+Close	LITERAL1
+Ping	LITERAL1
+Pong	LITERAL1
+
+
+CloseReason	KEYWORD1
+CloseReason_None	LITERAL1
+CloseReason_NormalClosure	LITERAL1
+CloseReason_GoingAway	LITERAL1
+CloseReason_ProtocolError	LITERAL1
+CloseReason_UnsupportedData	LITERAL1
+CloseReason_NoStatusRcvd	LITERAL1
+CloseReason_AbnormalClosure	LITERAL1
+CloseReason_InvalidPayloadData	LITERAL1
+CloseReason_PolicyViolation	LITERAL1
+CloseReason_MessageTooBig	LITERAL1
+CloseReason_InternalServerError	LITERAL1
+
+
+FragmentsPolicy	KEYWORD1
+FragmentsPolicy_Aggregate	LITERAL1
+FragmentsPolicy_Notify	LITERAL1

+ 9 - 0
Esp32-Client/ArduinoWebsockets/library.properties

@@ -0,0 +1,9 @@
+name=ArduinoWebsockets
+version=0.5.0
+author=Gil Maimon <mail.gilmaimon@gmail.com>
+maintainer=Gil Maimon <mail.gilmaimon@gmail.com>
+sentence=A library for writing modern Websockets applications with Arduino.
+paragraph=Featuring modern callbacks (supports lambdas) and a minimal interface. Contains a websockets Client and Server. Supports all features of the RFC (pings, pongs, binary and text data, error codes) and WSS (Websockets over SSL).
+category=Communication
+url=https://github.com/gilmaimon/ArduinoWebsockets
+includes=ArduinoWebsockets.h

+ 8 - 0
Esp32-Client/ArduinoWebsockets/src/ArduinoWebsockets.h

@@ -0,0 +1,8 @@
+#ifndef _WEBSOCKETS_CLIENT_H
+#define _WEBSOCKETS_CLIENT_H
+
+#include "tiny_websockets/message.hpp"
+#include "tiny_websockets/client.hpp"
+#include "tiny_websockets/server.hpp"
+
+#endif //_WEBSOCKETS_CLIENT_H

+ 58 - 0
Esp32-Client/ArduinoWebsockets/src/crypto.cpp

@@ -0,0 +1,58 @@
+#include <tiny_websockets/internals/wscrypto/crypto.hpp>
+#include <tiny_websockets/internals/wscrypto/base64.hpp>
+#include <tiny_websockets/internals/wscrypto/sha1.hpp>
+
+#ifndef _WS_CONFIG_NO_TRUE_RANDOMNESS
+#include <time.h>
+#endif
+
+namespace websockets { namespace crypto {
+  WSString base64Encode(WSString data) {
+    return internals::base64_encode(reinterpret_cast<const uint8_t*>(data.c_str()), data.size());
+  }
+  WSString base64Encode(uint8_t* data, size_t len) {
+    return internals::base64_encode(reinterpret_cast<const uint8_t*>(data), len);
+  }
+  
+  WSString base64Decode(WSString data) {
+    return internals::base64_decode(data);
+  }
+
+  WSString websocketsHandshakeEncodeKey(WSString key) {
+      char base64[30];
+      internals::sha1(key.c_str())
+        .add("258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
+        .finalize()
+        .print_base64(base64);
+      
+      return WSString(base64);
+  }
+
+#ifdef _WS_CONFIG_NO_TRUE_RANDOMNESS
+  WSString randomBytes(size_t len) {
+    WSString result;
+    result.reserve(len);
+
+    for(size_t i = 0; i < len; i++) {
+      result += "0123456789abcdef"[i % 16];
+    }
+
+    return result;
+  }
+#else
+  WSString randomBytes(size_t len) {
+    static int onlyOnce = [](){
+      srand(time(NULL));
+      return 0;
+    }();
+
+    WSString result;
+    result.reserve(len);
+
+    for(size_t i = onlyOnce; i < len; i++) {
+      result += "0123456789abcdefABCDEFGHIJKLMNOPQRSTUVEXYZ"[rand() % 42];
+    }
+    return result;
+  }
+#endif
+}} // websockets::crypto

+ 14 - 0
Esp32-Client/ArduinoWebsockets/src/message.cpp

@@ -0,0 +1,14 @@
+#include <tiny_websockets/message.hpp>
+
+namespace websockets { 
+  MessageType messageTypeFromOpcode(uint8_t opcode) {
+      switch(opcode) {
+          case internals::ContentType::Binary: return MessageType::Binary;
+          case internals::ContentType::Text: return MessageType::Text;
+          case internals::ContentType::Ping: return MessageType::Ping;
+          case internals::ContentType::Pong: return MessageType::Pong;
+          case internals::ContentType::Close: return MessageType::Close;
+          default: return MessageType::Empty;
+      }
+  }
+}

+ 127 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/client.hpp

@@ -0,0 +1,127 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+#include <tiny_websockets/internals/data_frame.hpp>
+#include <tiny_websockets/internals/websockets_endpoint.hpp>
+#include <tiny_websockets/message.hpp>
+#include <memory>
+#include <functional>
+#include <vector>
+
+namespace websockets {
+  enum class WebsocketsEvent {
+    ConnectionOpened,
+    ConnectionClosed,
+    GotPing, GotPong
+  };
+
+  class WebsocketsClient;
+    typedef std::function<void(WebsocketsClient&, WebsocketsMessage)> MessageCallback;
+    typedef std::function<void(WebsocketsMessage)> PartialMessageCallback;
+    
+    typedef std::function<void(WebsocketsClient&, WebsocketsEvent, WSInterfaceString)> EventCallback;
+    typedef std::function<void(WebsocketsEvent, WSInterfaceString)> PartialEventCallback;
+
+  class WebsocketsClient {
+  public:
+    WebsocketsClient();
+    WebsocketsClient(std::shared_ptr<network::TcpClient> client);
+    
+    WebsocketsClient(const WebsocketsClient& other);
+    WebsocketsClient(const WebsocketsClient&& other);
+    
+    WebsocketsClient& operator=(const WebsocketsClient& other);
+    WebsocketsClient& operator=(const WebsocketsClient&& other);
+
+    void addHeader(const WSInterfaceString key, const WSInterfaceString value);
+
+    bool connect(const WSInterfaceString url);
+    bool connect(const WSInterfaceString host, const int port, const WSInterfaceString path);
+    
+    void onMessage(const MessageCallback callback);
+    void onMessage(const PartialMessageCallback callback);
+
+    void onEvent(const EventCallback callback);
+    void onEvent(const PartialEventCallback callback);
+
+    bool poll();
+    bool available(const bool activeTest = false);
+
+    bool send(const WSInterfaceString&& data);
+    bool send(const WSInterfaceString& data);
+    bool send(const char* data);
+    bool send(const char* data, const size_t len);
+
+    bool sendBinary(const WSInterfaceString data);
+    bool sendBinary(const char* data, const size_t len);
+
+    // stream messages
+    bool stream(const WSInterfaceString data = "");
+    bool streamBinary(const WSInterfaceString data = "");
+    bool end(const WSInterfaceString data = "");
+    
+    void setFragmentsPolicy(const FragmentsPolicy newPolicy);
+    FragmentsPolicy getFragmentsPolicy() const;
+    
+    WebsocketsMessage readBlocking();
+
+    bool ping(const WSInterfaceString data = "");
+    bool pong(const WSInterfaceString data = "");
+
+    void close(const CloseReason reason = CloseReason_NormalClosure);
+    CloseReason getCloseReason() const;
+
+    void setUseMasking(bool useMasking) {
+      _endpoint.setUseMasking(useMasking);
+    }
+
+    void setInsecure();
+  #ifdef ESP8266
+    void setFingerprint(const char* fingerprint);
+    void setClientRSACert(const X509List *cert, const PrivateKey *sk);
+	void setClientECCert(const X509List *cert, const PrivateKey *sk);
+    void setTrustAnchors(const X509List *ta);
+	void setKnownKey(const PublicKey *pk);
+  #elif defined(ESP32)
+    void setCACert(const char* ca_cert);
+    void setCertificate(const char* client_ca);
+    void setPrivateKey(const char* private_key);
+  #endif
+
+    virtual ~WebsocketsClient();
+
+  private:
+    std::shared_ptr<network::TcpClient> _client;
+    std::vector<std::pair<WSString, WSString>> _customHeaders;
+    internals::WebsocketsEndpoint _endpoint;
+    bool _connectionOpen;
+    MessageCallback _messagesCallback;
+    EventCallback _eventsCallback;
+    enum SendMode {
+      SendMode_Normal,
+      SendMode_Streaming
+    } _sendMode;
+
+
+  #ifdef ESP8266
+    const char* _optional_ssl_fingerprint = nullptr;
+    const X509List* _optional_ssl_trust_anchors = nullptr;
+	const PublicKey* _optional_ssl_known_key = nullptr;
+    const X509List* _optional_ssl_rsa_cert = nullptr;
+    const PrivateKey* _optional_ssl_rsa_private_key = nullptr;
+	const X509List* _optional_ssl_ec_cert = nullptr;
+    const PrivateKey* _optional_ssl_ec_private_key = nullptr;
+  #elif defined(ESP32)
+    const char* _optional_ssl_ca_cert = nullptr;
+    const char* _optional_ssl_client_ca = nullptr;
+    const char* _optional_ssl_private_key = nullptr;
+  #endif
+
+    void _handlePing(WebsocketsMessage);
+    void _handlePong(WebsocketsMessage);
+    void _handleClose(WebsocketsMessage);
+
+    void upgradeToSecuredConnection();
+  };
+}

+ 89 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/data_frame.hpp

@@ -0,0 +1,89 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+
+namespace websockets { namespace internals {
+  enum ContentType {
+    // None as error value
+    None = -1,
+    // Default value for empty messages
+    Continuation = 0x0,
+
+    // Data opcdoes
+    Text = 0x1,
+    Binary = 0x2,
+
+    // Control opcodes
+    Close = 0x8,
+    Ping = 0x9,
+    Pong = 0xA
+  };
+
+  struct WebsocketsFrame {
+    uint8_t fin : 1;
+    uint8_t opcode : 4;
+    uint8_t mask : 1;
+    uint8_t mask_buf[4];
+    uint64_t payload_length;
+    WSString payload;
+
+    bool isControlFrame() {
+      return fin && (opcode == 0x8 || opcode == 0x9 || opcode == 0xA);
+    }
+
+    bool isEmpty() {
+      return (fin == 0) && (opcode == 0) && (payload_length == 0);
+    }
+
+    bool isBeginningOfFragmentsStream() const {
+      return (fin == 0) && (opcode != 0);
+    }
+
+    bool isContinuesFragment() const {
+      return (fin == 0) && (opcode == 0);
+    }
+
+    bool isEndOfFragmentsStream() const {
+      return (fin == 1) && (opcode == 0);
+    }
+
+    bool isNormalUnfragmentedMessage() const {
+      return (fin == 1) && (opcode != 0);
+    }
+  };
+
+  template <class HeaderTy> HeaderTy MakeHeader(size_t len, uint8_t opcode, bool fin, bool mask) {
+    HeaderTy header;
+    header.fin = fin;
+    header.flags = 0;
+    header.opcode = opcode;
+    header.mask = mask? 1: 0;
+
+    // set payload
+    if(len < 126) {
+      header.payload = len;
+    } else if(len < 65536) {
+      header.payload = 126;
+    } else {
+      header.payload = 127;
+    }
+
+    return header;
+  }
+
+  struct Header {
+    uint8_t opcode : 4;
+    uint8_t flags : 3;
+    uint8_t fin : 1;
+    uint8_t payload : 7;
+    uint8_t mask : 1;
+  };
+
+  struct HeaderWithExtended16 : Header {
+    uint16_t extendedPayload;
+  };
+
+  struct HeaderWithExtended64 : Header {
+    uint64_t extendedPayload;
+  };
+}} // websockets::internals

+ 91 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/websockets_endpoint.hpp

@@ -0,0 +1,91 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+#include <tiny_websockets/internals/data_frame.hpp>
+#include <tiny_websockets/message.hpp>
+#include <memory>
+
+#define __TINY_WS_INTERNAL_DEFAULT_MASK "\00\00\00\00"
+
+namespace websockets {     
+    enum FragmentsPolicy {
+        FragmentsPolicy_Aggregate,
+        FragmentsPolicy_Notify
+    };
+
+    enum CloseReason {
+        CloseReason_None                =       -1,
+        CloseReason_NormalClosure       =       1000,
+        CloseReason_GoingAway           =       1001,
+        CloseReason_ProtocolError       =       1002,
+        CloseReason_UnsupportedData     =       1003,
+        CloseReason_NoStatusRcvd        =       1005,
+        CloseReason_AbnormalClosure     =       1006,
+        CloseReason_InvalidPayloadData  =       1007,
+        CloseReason_PolicyViolation     =       1008,
+        CloseReason_MessageTooBig       =       1009,
+        CloseReason_InternalServerError =       1011,
+    };
+
+    CloseReason GetCloseReason(uint16_t reasonCode);
+    
+    namespace internals {
+    
+    class WebsocketsEndpoint {
+    public:
+        WebsocketsEndpoint(std::shared_ptr<network::TcpClient> socket, FragmentsPolicy fragmentsPolicy = FragmentsPolicy_Aggregate);
+
+        WebsocketsEndpoint(const WebsocketsEndpoint& other);
+        WebsocketsEndpoint(const WebsocketsEndpoint&& other);
+        
+        WebsocketsEndpoint& operator=(const WebsocketsEndpoint& other);
+        WebsocketsEndpoint& operator=(const WebsocketsEndpoint&& other);
+
+        void setInternalSocket(std::shared_ptr<network::TcpClient> socket);
+
+        bool poll();
+        WebsocketsMessage recv();
+        bool send(const char* data, const size_t len, const uint8_t opcode, const bool fin, const bool mask, const char* maskingKey = __TINY_WS_INTERNAL_DEFAULT_MASK);    
+        bool send(const WSString& data, const uint8_t opcode, const bool fin, const bool mask, const char* maskingKey = __TINY_WS_INTERNAL_DEFAULT_MASK);
+        
+        bool send(const char* data, const size_t len, const uint8_t opcode, const bool fin);    
+        bool send(const WSString& data, const uint8_t opcode, const bool fin);
+        
+        bool ping(const WSString& msg);
+        bool ping(const WSString&& msg);
+
+        bool pong(const WSString& msg);
+        bool pong(const WSString&& msg);
+
+        void close(const CloseReason reason = CloseReason_NormalClosure);
+        CloseReason getCloseReason() const;
+
+        void setFragmentsPolicy(const FragmentsPolicy newPolicy);
+        FragmentsPolicy getFragmentsPolicy() const;
+
+        void setUseMasking(bool useMasking) {
+            _useMasking = useMasking;
+        }
+
+        virtual ~WebsocketsEndpoint();
+    private:
+        std::shared_ptr<network::TcpClient> _client;
+        FragmentsPolicy _fragmentsPolicy;
+        enum RecvMode {
+            RecvMode_Normal,
+            RecvMode_Streaming
+        } _recvMode;
+        WebsocketsMessage::StreamBuilder _streamBuilder;
+        CloseReason _closeReason;
+        bool _useMasking = true;
+
+        WebsocketsFrame _recv();
+        void handleMessageInternally(WebsocketsMessage& msg);
+
+        WebsocketsMessage handleFrameInStreamingMode(WebsocketsFrame& frame);
+        WebsocketsMessage handleFrameInStandardMode(WebsocketsFrame& frame);
+
+        std::string getHeader(uint64_t len, uint8_t opcode, bool fin, bool mask);
+    };
+}} // websockets::internals

+ 53 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/ws_common.hpp

@@ -0,0 +1,53 @@
+#pragma once
+
+#include <tiny_websockets/ws_config_defs.hpp>
+#include <string>
+#include <Arduino.h>
+
+namespace websockets {
+    typedef std::string WSString;
+    typedef String WSInterfaceString;
+
+    namespace internals {
+        WSString fromInterfaceString(const WSInterfaceString& str);
+        WSString fromInterfaceString(const WSInterfaceString&& str);
+        WSInterfaceString fromInternalString(const WSString& str);
+        WSInterfaceString fromInternalString(const WSString&& str);
+    }
+}
+
+#ifdef ESP8266
+    #define PLATFORM_DOES_NOT_SUPPORT_BLOCKING_READ
+
+    #include <tiny_websockets/network/esp8266/esp8266_tcp.hpp>
+    #define WSDefaultTcpClient websockets::network::Esp8266TcpClient
+    #define WSDefaultTcpServer websockets::network::Esp8266TcpServer
+
+    #ifndef _WS_CONFIG_NO_SSL
+        // OpenSSL Dependent
+        #define WSDefaultSecuredTcpClient websockets::network::SecuredEsp8266TcpClient
+    #endif //_WS_CONFIG_NO_SSL
+
+#elif defined(ESP32)
+
+    #define PLATFORM_DOES_NOT_SUPPORT_BLOCKING_READ
+
+    #include <tiny_websockets/network/esp32/esp32_tcp.hpp>
+    #define WSDefaultTcpClient websockets::network::Esp32TcpClient
+    #define WSDefaultTcpServer websockets::network::Esp32TcpServer
+
+    #ifndef _WS_CONFIG_NO_SSL
+        // OpenSSL Dependent
+        #define WSDefaultSecuredTcpClient websockets::network::SecuredEsp32TcpClient
+    #endif //_WS_CONFIG_NO_SSL
+
+#elif defined(ARDUINO_TEENSY41)
+    #define PLATFORM_DOES_NOT_SUPPORT_BLOCKING_READ
+    #define _WS_CONFIG_NO_SSL
+
+    #include <tiny_websockets/network/teensy41/teensy41_tcp_client.hpp>
+    #include <tiny_websockets/network/teensy41/teensy41_tcp_server.hpp>
+
+    #define WSDefaultTcpClient websockets::network::Teensy41TcpClient
+    #define WSDefaultTcpServer websockets::network::Teensy41TcpServer    
+#endif

+ 126 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/wscrypto/base64.hpp

@@ -0,0 +1,126 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+
+namespace websockets { namespace crypto { namespace internals {
+/* 
+   base64.cpp and base64.hpp
+
+   Copyright (C) 2004-2008 René Nyffenegger
+
+   This source code is provided 'as-is', without any express or implied
+   warranty. In no event will the author be held liable for any damages
+   arising from the use of this software.
+
+   Permission is granted to anyone to use this software for any purpose,
+   including commercial applications, and to alter it and redistribute it
+   freely, subject to the following restrictions:
+
+   1. The origin of this source code must not be misrepresented; you must not
+      claim that you wrote the original source code. If you use this source code
+      in a product, an acknowledgment in the product documentation would be
+      appreciated but is not required.
+
+   2. Altered source versions must be plainly marked as such, and must not be
+      misrepresented as being the original source code.
+
+   3. This notice may not be removed or altered from any source distribution.
+
+   René Nyffenegger rene.nyffenegger@adp-gmbh.ch
+
+*/
+
+static const WSString base64_chars = 
+             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+             "abcdefghijklmnopqrstuvwxyz"
+             "0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+  return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+WSString base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+  WSString ret;
+  int i = 0;
+  int j = 0;
+  unsigned char char_array_3[3];
+  unsigned char char_array_4[4];
+
+  while (in_len--) {
+    char_array_3[i++] = *(bytes_to_encode++);
+    if (i == 3) {
+      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+      char_array_4[3] = char_array_3[2] & 0x3f;
+
+      for(i = 0; (i <4) ; i++)
+        ret += base64_chars[char_array_4[i]];
+      i = 0;
+    }
+  }
+
+  if (i)
+  {
+    for(j = i; j < 3; j++)
+      char_array_3[j] = '\0';
+
+    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+    char_array_4[3] = char_array_3[2] & 0x3f;
+
+    for (j = 0; (j < i + 1); j++)
+      ret += base64_chars[char_array_4[j]];
+
+    while((i++ < 3))
+      ret += '=';
+
+  }
+
+  return ret;
+
+}
+WSString base64_decode(WSString const& encoded_string) {
+  int in_len = encoded_string.size();
+  int i = 0;
+  int j = 0;
+  int in_ = 0;
+  unsigned char char_array_4[4], char_array_3[3];
+  WSString ret;
+
+  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+    char_array_4[i++] = encoded_string[in_]; in_++;
+    if (i ==4) {
+      for (i = 0; i <4; i++)
+        char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+      for (i = 0; (i < 3); i++)
+        ret += char_array_3[i];
+      i = 0;
+    }
+  }
+
+  if (i) {
+    for (j = i; j <4; j++)
+      char_array_4[j] = 0;
+
+    for (j = 0; j <4; j++)
+      char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+  }
+
+  return ret;
+}
+
+}}} // websockets::crypto::internals

+ 11 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/wscrypto/crypto.hpp

@@ -0,0 +1,11 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+
+namespace websockets { namespace crypto {
+  WSString base64Encode(WSString data);
+  WSString base64Encode(uint8_t* data, size_t len);
+  WSString base64Decode(WSString data);
+  WSString websocketsHandshakeEncodeKey(WSString key);
+  WSString randomBytes(size_t len);
+}} // websockets::crypto

+ 261 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/internals/wscrypto/sha1.hpp

@@ -0,0 +1,261 @@
+#pragma once
+
+#include <stdint.h>
+#include <string.h>
+
+/*
+    CREDIT: this implementation is from https://github.com/983/SHA1
+*/
+namespace websockets { namespace crypto { namespace internals {
+#define SHA1_HEX_SIZE (40 + 1)
+#define SHA1_BASE64_SIZE (28 + 1)
+
+class sha1 {
+private:
+    void add_byte_dont_count_bits(uint8_t x){
+        buf[i++] = x;
+
+        if (i >= sizeof(buf)){
+            i = 0;
+            process_block(buf);
+        }
+    }
+
+    static uint32_t rol32(uint32_t x, uint32_t n){
+        return (x << n) | (x >> (32 - n));
+    }
+
+    static uint32_t make_word(const uint8_t *p){
+        return
+            static_cast<uint32_t>(p[0] << 3*8) |
+            static_cast<uint32_t>(p[1] << 2*8) |
+            static_cast<uint32_t>(p[2] << 1*8) |
+            static_cast<uint32_t>(p[3] << 0*8);
+    }
+
+    void process_block(const uint8_t *ptr){
+        const uint32_t c0 = 0x5a827999;
+        const uint32_t c1 = 0x6ed9eba1;
+        const uint32_t c2 = 0x8f1bbcdc;
+        const uint32_t c3 = 0xca62c1d6;
+
+        uint32_t a = state[0];
+        uint32_t b = state[1];
+        uint32_t c = state[2];
+        uint32_t d = state[3];
+        uint32_t e = state[4];
+
+        uint32_t w[16];
+
+        for (int _i = 0; _i < 16; _i++) w[_i] = make_word(ptr + _i*4);
+
+#define SHA1_LOAD(i) w[i&15] = rol32(w[(i+13)&15] ^ w[(i+8)&15] ^ w[(i+2)&15] ^ w[i&15], 1);
+#define SHA1_ROUND_0(v,u,x,y,z,i)              z += ((u & (x ^ y)) ^ y) + w[i&15] + c0 + rol32(v, 5); u = rol32(u, 30);
+#define SHA1_ROUND_1(v,u,x,y,z,i) SHA1_LOAD(i) z += ((u & (x ^ y)) ^ y) + w[i&15] + c0 + rol32(v, 5); u = rol32(u, 30);
+#define SHA1_ROUND_2(v,u,x,y,z,i) SHA1_LOAD(i) z += (u ^ x ^ y) + w[i&15] + c1 + rol32(v, 5); u = rol32(u, 30);
+#define SHA1_ROUND_3(v,u,x,y,z,i) SHA1_LOAD(i) z += (((u | x) & y) | (u & x)) + w[i&15] + c2 + rol32(v, 5); u = rol32(u, 30);
+#define SHA1_ROUND_4(v,u,x,y,z,i) SHA1_LOAD(i) z += (u ^ x ^ y) + w[i&15] + c3 + rol32(v, 5); u = rol32(u, 30);
+
+        SHA1_ROUND_0(a, b, c, d, e,  0);
+        SHA1_ROUND_0(e, a, b, c, d,  1);
+        SHA1_ROUND_0(d, e, a, b, c,  2);
+        SHA1_ROUND_0(c, d, e, a, b,  3);
+        SHA1_ROUND_0(b, c, d, e, a,  4);
+        SHA1_ROUND_0(a, b, c, d, e,  5);
+        SHA1_ROUND_0(e, a, b, c, d,  6);
+        SHA1_ROUND_0(d, e, a, b, c,  7);
+        SHA1_ROUND_0(c, d, e, a, b,  8);
+        SHA1_ROUND_0(b, c, d, e, a,  9);
+        SHA1_ROUND_0(a, b, c, d, e, 10);
+        SHA1_ROUND_0(e, a, b, c, d, 11);
+        SHA1_ROUND_0(d, e, a, b, c, 12);
+        SHA1_ROUND_0(c, d, e, a, b, 13);
+        SHA1_ROUND_0(b, c, d, e, a, 14);
+        SHA1_ROUND_0(a, b, c, d, e, 15);
+        SHA1_ROUND_1(e, a, b, c, d, 16);
+        SHA1_ROUND_1(d, e, a, b, c, 17);
+        SHA1_ROUND_1(c, d, e, a, b, 18);
+        SHA1_ROUND_1(b, c, d, e, a, 19);
+        SHA1_ROUND_2(a, b, c, d, e, 20);
+        SHA1_ROUND_2(e, a, b, c, d, 21);
+        SHA1_ROUND_2(d, e, a, b, c, 22);
+        SHA1_ROUND_2(c, d, e, a, b, 23);
+        SHA1_ROUND_2(b, c, d, e, a, 24);
+        SHA1_ROUND_2(a, b, c, d, e, 25);
+        SHA1_ROUND_2(e, a, b, c, d, 26);
+        SHA1_ROUND_2(d, e, a, b, c, 27);
+        SHA1_ROUND_2(c, d, e, a, b, 28);
+        SHA1_ROUND_2(b, c, d, e, a, 29);
+        SHA1_ROUND_2(a, b, c, d, e, 30);
+        SHA1_ROUND_2(e, a, b, c, d, 31);
+        SHA1_ROUND_2(d, e, a, b, c, 32);
+        SHA1_ROUND_2(c, d, e, a, b, 33);
+        SHA1_ROUND_2(b, c, d, e, a, 34);
+        SHA1_ROUND_2(a, b, c, d, e, 35);
+        SHA1_ROUND_2(e, a, b, c, d, 36);
+        SHA1_ROUND_2(d, e, a, b, c, 37);
+        SHA1_ROUND_2(c, d, e, a, b, 38);
+        SHA1_ROUND_2(b, c, d, e, a, 39);
+        SHA1_ROUND_3(a, b, c, d, e, 40);
+        SHA1_ROUND_3(e, a, b, c, d, 41);
+        SHA1_ROUND_3(d, e, a, b, c, 42);
+        SHA1_ROUND_3(c, d, e, a, b, 43);
+        SHA1_ROUND_3(b, c, d, e, a, 44);
+        SHA1_ROUND_3(a, b, c, d, e, 45);
+        SHA1_ROUND_3(e, a, b, c, d, 46);
+        SHA1_ROUND_3(d, e, a, b, c, 47);
+        SHA1_ROUND_3(c, d, e, a, b, 48);
+        SHA1_ROUND_3(b, c, d, e, a, 49);
+        SHA1_ROUND_3(a, b, c, d, e, 50);
+        SHA1_ROUND_3(e, a, b, c, d, 51);
+        SHA1_ROUND_3(d, e, a, b, c, 52);
+        SHA1_ROUND_3(c, d, e, a, b, 53);
+        SHA1_ROUND_3(b, c, d, e, a, 54);
+        SHA1_ROUND_3(a, b, c, d, e, 55);
+        SHA1_ROUND_3(e, a, b, c, d, 56);
+        SHA1_ROUND_3(d, e, a, b, c, 57);
+        SHA1_ROUND_3(c, d, e, a, b, 58);
+        SHA1_ROUND_3(b, c, d, e, a, 59);
+        SHA1_ROUND_4(a, b, c, d, e, 60);
+        SHA1_ROUND_4(e, a, b, c, d, 61);
+        SHA1_ROUND_4(d, e, a, b, c, 62);
+        SHA1_ROUND_4(c, d, e, a, b, 63);
+        SHA1_ROUND_4(b, c, d, e, a, 64);
+        SHA1_ROUND_4(a, b, c, d, e, 65);
+        SHA1_ROUND_4(e, a, b, c, d, 66);
+        SHA1_ROUND_4(d, e, a, b, c, 67);
+        SHA1_ROUND_4(c, d, e, a, b, 68);
+        SHA1_ROUND_4(b, c, d, e, a, 69);
+        SHA1_ROUND_4(a, b, c, d, e, 70);
+        SHA1_ROUND_4(e, a, b, c, d, 71);
+        SHA1_ROUND_4(d, e, a, b, c, 72);
+        SHA1_ROUND_4(c, d, e, a, b, 73);
+        SHA1_ROUND_4(b, c, d, e, a, 74);
+        SHA1_ROUND_4(a, b, c, d, e, 75);
+        SHA1_ROUND_4(e, a, b, c, d, 76);
+        SHA1_ROUND_4(d, e, a, b, c, 77);
+        SHA1_ROUND_4(c, d, e, a, b, 78);
+        SHA1_ROUND_4(b, c, d, e, a, 79);
+
+#undef SHA1_LOAD
+#undef SHA1_ROUND_0
+#undef SHA1_ROUND_1
+#undef SHA1_ROUND_2
+#undef SHA1_ROUND_3
+#undef SHA1_ROUND_4
+
+        state[0] += a;
+        state[1] += b;
+        state[2] += c;
+        state[3] += d;
+        state[4] += e;
+    }
+
+public:
+
+    uint32_t state[5];
+    uint8_t buf[64];
+    uint32_t i;
+    uint64_t n_bits;
+
+    sha1(const char *text = NULL): i(0), n_bits(0){
+        state[0] = 0x67452301;
+        state[1] = 0xEFCDAB89;
+        state[2] = 0x98BADCFE;
+        state[3] = 0x10325476;
+        state[4] = 0xC3D2E1F0;
+        add(text);
+    }
+
+    sha1& add(uint8_t x){
+        add_byte_dont_count_bits(x);
+        n_bits += 8;
+        return *this;
+    }
+
+    sha1& add(char c){
+        return add( static_cast<uint8_t>(c) );
+    }
+
+    sha1& add(const void *data, uint32_t n){
+        if (!data) return *this;
+
+        const uint8_t *ptr = reinterpret_cast<const uint8_t*>(data);
+
+        // fill up block if not full
+        for (; n && i % sizeof(buf); n--) add(*ptr++);
+
+        // process full blocks
+        for (; n >= sizeof(buf); n -= sizeof(buf)){
+            process_block(ptr);
+            ptr += sizeof(buf);
+            n_bits += sizeof(buf) * 8;
+        }
+
+        // process remaining part of block
+        for (; n; n--) add(*ptr++);
+
+        return *this;
+    }
+
+    sha1& add(const char *text){
+        return add(text, strlen(text));
+    }
+
+    sha1& finalize(){
+        // hashed text ends with 0x80, some padding 0x00 and the length in bits
+        add_byte_dont_count_bits(0x80);
+        while (i % 64 != 56) add_byte_dont_count_bits(0x00);
+        for (int j = 7; j >= 0; j--) add_byte_dont_count_bits(n_bits >> j * 8);
+
+        return *this;
+    }
+
+    const sha1& print_hex(
+        char *hex,
+        bool zero_terminate = true,
+        const char *alphabet = "0123456789abcdef"
+    ) const {
+        // print hex
+        int k = 0;
+        for (int _i = 0; _i < 5; _i++){
+            for (int j = 7; j >= 0; j--){
+                hex[k++] = alphabet[(state[_i] >> j * 4) & 0xf];
+            }
+        }
+        if (zero_terminate) hex[k] = '\0';
+        return *this;
+    }
+
+    const sha1& print_base64(char *base64, bool zero_terminate = true) const {
+        static const uint8_t *table = reinterpret_cast<const uint8_t*>(
+            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+            "abcdefghijklmnopqrstuvwxyz"
+            "0123456789"
+            "+/"
+        );
+
+        uint32_t triples[7] = {
+            ((state[0] & 0xffffff00) >> 1*8),
+            ((state[0] & 0x000000ff) << 2*8) | ((state[1] & 0xffff0000) >> 2*8),
+            ((state[1] & 0x0000ffff) << 1*8) | ((state[2] & 0xff000000) >> 3*8),
+            ((state[2] & 0x00ffffff) << 0*8),
+            ((state[3] & 0xffffff00) >> 1*8),
+            ((state[3] & 0x000000ff) << 2*8) | ((state[4] & 0xffff0000) >> 2*8),
+            ((state[4] & 0x0000ffff) << 1*8),
+        };
+
+        for (int _i = 0; _i < 7; _i++){
+            uint32_t x = triples[_i];
+            base64[_i*4 + 0] = table[(x >> 3*6) % 64];
+            base64[_i*4 + 1] = table[(x >> 2*6) % 64];
+            base64[_i*4 + 2] = table[(x >> 1*6) % 64];
+            base64[_i*4 + 3] = table[(x >> 0*6) % 64];
+        }
+
+        base64[SHA1_BASE64_SIZE - 2] = '=';
+        if (zero_terminate) base64[SHA1_BASE64_SIZE - 1] = '\0';
+        return *this;
+    }
+};
+}}} // websockets::crypto::internals

+ 186 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/message.hpp

@@ -0,0 +1,186 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/internals/data_frame.hpp>
+
+namespace websockets {
+    enum class MessageType {
+        Empty,
+        Text, Binary,
+        Ping, Pong, Close
+    };
+
+    MessageType messageTypeFromOpcode(uint8_t opcode);
+
+    enum class MessageRole {
+        Complete, First, Continuation, Last 
+    };
+
+    // The class the user will interact with as a message
+    // This message can be partial (so practically this is a Frame and not a message)
+    struct WebsocketsMessage {
+        WebsocketsMessage(MessageType msgType, const WSString& msgData, MessageRole msgRole = MessageRole::Complete) : _type(msgType), _length(msgData.size()), _data(msgData), _role(msgRole) {}
+        WebsocketsMessage() : WebsocketsMessage(MessageType::Empty, "", MessageRole::Complete) {}
+
+        static WebsocketsMessage CreateFromFrame(internals::WebsocketsFrame frame, MessageType overrideType = MessageType::Empty) {
+            auto type = overrideType;
+            if(type == MessageType::Empty) {
+                type = messageTypeFromOpcode(frame.opcode);
+            }
+
+            // deduce role
+            MessageRole msgRole = MessageRole::Complete;
+            if(frame.isNormalUnfragmentedMessage()) {
+                msgRole = MessageRole::Complete;
+            } else if(frame.isBeginningOfFragmentsStream()) {
+                msgRole = MessageRole::First;
+            } else if(frame.isContinuesFragment()) {
+                msgRole = MessageRole::Continuation;
+            } else if(frame.isEndOfFragmentsStream()) {
+                msgRole = MessageRole::Last;
+            }
+
+            return WebsocketsMessage(type, std::move(frame.payload), msgRole);
+        }
+        
+        // for validation
+        bool isEmpty() const { return this->_type == MessageType::Empty; }
+
+        // Type Helper Functions
+        MessageType type() const { return this->_type; }
+
+        bool isText() const { return this->_type == MessageType::Text; }
+        bool isBinary() const { return this->_type == MessageType::Binary; }
+        
+        bool isPing() const { return this->_type == MessageType::Ping; }
+        bool isPong() const { return this->_type == MessageType::Pong; }
+        
+        bool isClose() const { return this->_type == MessageType::Close; }
+
+        
+        // Role Helper Function
+        MessageRole role() const { return this->_role; }
+
+        bool isComplete() const { return this->_role == MessageRole::Complete; }
+        bool isPartial() const { return this->_role != MessageRole::Complete; }
+        bool isFirst() const { return this->_role == MessageRole::First; }
+        bool isContinuation() const { return this->_role == MessageRole::Continuation; }
+        bool isLast() const { return this->_role == MessageRole::Last; }
+
+
+        WSInterfaceString data() const { return internals::fromInternalString(this->_data); }
+        const WSString& rawData() const { return this->_data; }
+        const char* c_str() const { return this->_data.c_str(); }
+
+        uint32_t length() const { return this->_length; }
+
+        class StreamBuilder {
+        public:
+            StreamBuilder(bool dummyMode = false) : _dummyMode(dummyMode), _empty(true) {}
+
+            void first(const internals::WebsocketsFrame& frame) {
+                if(this->_empty == false) {
+                    badFragment();
+                    return;
+                }
+
+                this->_empty = false;
+                if(frame.isBeginningOfFragmentsStream()) {
+                    this->_isComplete = false;
+                    this->_didErrored = false;
+
+                    if(this->_dummyMode == false) {
+                        this->_content = std::move(frame.payload);
+                    }
+
+                    this->_type = messageTypeFromOpcode(frame.opcode);
+                    if(this->_type == MessageType::Empty) {
+                        badFragment();
+                    }
+                } else {
+                    this->_didErrored = true;
+                }
+            }
+
+            void append(const internals::WebsocketsFrame& frame) {
+                if(isErrored()) return;
+                if(isEmpty() || isComplete()) {
+                    badFragment();
+                    return;
+                }
+
+                if(frame.isContinuesFragment()) {
+                    if(this->_dummyMode == false) {
+                        this->_content += std::move(frame.payload);
+                    }
+                } else {
+                    badFragment();
+                }
+            }
+
+            void end(const internals::WebsocketsFrame& frame) {
+                if(isErrored()) return;
+                if(isEmpty() || isComplete()) {
+                    badFragment();
+                    return;
+                }
+
+                if(frame.isEndOfFragmentsStream()) {
+                    if(this->_dummyMode == false) {
+                        this->_content += std::move(frame.payload);
+                    }
+                    this->_isComplete = true;
+                } else {
+                    badFragment();
+                }
+            }
+
+            void badFragment() {
+                this->_didErrored = true;
+                this->_isComplete = false;
+            }
+
+            bool isErrored() {
+                return this->_didErrored;
+            }
+
+            bool isOk() {
+                return !this->_didErrored;
+            }
+
+            bool isComplete() {
+                return this->_isComplete;
+            }
+
+            bool isEmpty() {
+                return this->_empty;
+            }
+            
+            MessageType type() {
+                return this->_type;
+            }
+
+            WebsocketsMessage build() {
+                return WebsocketsMessage(
+                    this->_type, 
+                    std::move(this->_content),
+                    MessageRole::Complete
+                );
+            }
+
+        private:
+            bool _dummyMode;
+            bool _empty;
+            bool _isComplete = false;
+            WSString _content;
+            MessageType _type;
+            bool _didErrored;
+        };
+
+    private:
+        const MessageType _type;
+        const uint32_t _length;
+        const WSString _data;
+        const MessageRole _role;
+    };
+}

+ 81 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/esp32/esp32_tcp.hpp

@@ -0,0 +1,81 @@
+#pragma once
+
+#ifdef ESP32 
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+#include <tiny_websockets/network/tcp_server.hpp>
+#include <tiny_websockets/network/generic_esp/generic_esp_clients.hpp>
+
+#include <WiFi.h>
+#include <HTTPClient.h>
+
+namespace websockets { namespace network {
+  typedef GenericEspTcpClient<WiFiClient> Esp32TcpClient;
+  
+  class SecuredEsp32TcpClient : public GenericEspTcpClient<WiFiClientSecure> {
+  public:
+    void setCACert(const char* ca_cert) {
+      this->client.setCACert(ca_cert);
+    }
+
+    void setCertificate(const char* client_ca) {
+      this->client.setCertificate(client_ca);
+    }
+    
+    void setPrivateKey(const char* private_key) {
+      this->client.setPrivateKey(private_key);
+    }    
+  };
+
+
+  class Esp32TcpServer : public TcpServer {
+  public:
+    Esp32TcpServer() {}
+    bool poll() override {
+      yield();
+      return server.hasClient();
+    }
+
+    bool listen(const uint16_t port) override {
+      yield();
+      server = WiFiServer(port);
+      server.begin(port);
+      return available();
+    }
+    
+    TcpClient* accept() override {
+      while(available()) {
+        auto client = server.available();
+        if(client) {
+          return new Esp32TcpClient{client};
+        }
+      }
+      return new Esp32TcpClient;
+    }
+
+    bool available() override {
+      yield();
+      return static_cast<bool>(server);
+    }
+    
+    void close() override {
+      yield();
+      server.close();
+    }
+
+    virtual ~Esp32TcpServer() {
+      if(available()) close();
+    }
+
+  protected:
+    int getSocket() const override {
+      return -1; // Not Implemented
+    }
+  
+  private:
+    WiFiServer server;
+  };
+}} // websockets::network
+
+#endif // #ifdef ESP32 

+ 91 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/esp8266/esp8266_tcp.hpp

@@ -0,0 +1,91 @@
+#pragma once
+
+#ifdef ESP8266 
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+#include <tiny_websockets/network/tcp_server.hpp>
+#include <tiny_websockets/network/generic_esp/generic_esp_clients.hpp>
+
+#include <ESP8266WiFi.h>
+
+namespace websockets { namespace network {
+  typedef GenericEspTcpClient<WiFiClient> Esp8266TcpClient;
+  
+  class SecuredEsp8266TcpClient : public GenericEspTcpClient<WiFiClientSecure> {
+  public:
+    void setInsecure() {
+      this->client.setInsecure();
+    }
+
+    void setFingerprint(const char* fingerprint) {
+      this->client.setFingerprint(fingerprint);
+    }
+
+	void setKnownKey(const PublicKey *pk) {
+		this->client.setKnownKey(pk);
+	}
+
+    void setTrustAnchors(const X509List *ta){
+      this->client.setTrustAnchors(ta);
+	}
+
+    void setClientRSACert(const X509List *cert, const PrivateKey *sk) {
+      this->client.setClientRSACert(cert, sk);
+	}
+
+	void setClientECCert(const X509List *cert, const PrivateKey *sk) {
+      this->client.setClientECCert(cert, sk, 0xFFFF, 0);
+	}
+  };
+
+  #define DUMMY_PORT 0
+
+  class Esp8266TcpServer : public TcpServer {
+  public:
+    Esp8266TcpServer() : server(DUMMY_PORT) {}
+    bool poll() override {
+      yield();
+      return server.hasClient();
+    }
+
+    bool listen(const uint16_t port) override {
+      yield();
+      server.begin(port);
+      return available();
+    }
+    
+    TcpClient* accept() override {
+      while(available()) {
+        yield();
+        auto client = server.available();
+        if(client) return new Esp8266TcpClient{client};
+      }
+      return new Esp8266TcpClient;
+    }
+
+    bool available() override {
+      yield();
+      return server.status() != CLOSED;
+    }
+    
+    void close() override {
+      yield();
+      server.close();
+    }
+
+    virtual ~Esp8266TcpServer() {
+      if(available()) close();
+    }
+  
+  protected:
+    int getSocket() const override {
+      return -1;
+    }
+
+  private:
+    WiFiServer server;
+  };
+}} // websockets::network
+
+#endif // #ifdef ESP8266 

+ 84 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/generic_esp/generic_esp_clients.hpp

@@ -0,0 +1,84 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+
+namespace websockets { namespace network {
+  template <class WifiClientImpl> 
+  class GenericEspTcpClient : public TcpClient {
+  public:
+    GenericEspTcpClient(WifiClientImpl c) : client(c) {
+      client.setNoDelay(true);
+    }
+    
+    GenericEspTcpClient() {}
+
+    bool connect(const WSString& host, const int port) {
+      yield();
+      auto didConnect = client.connect(host.c_str(), port);
+      client.setNoDelay(true);
+      return didConnect;
+    }
+
+    bool poll() {
+      yield();
+      return client.available();
+    }
+
+    bool available() override {
+      return client.connected();
+    }
+
+    void send(const WSString& data) override {
+      yield();
+      client.write(reinterpret_cast<uint8_t*>(const_cast<char*>(data.c_str())), data.size());
+      yield();
+    }
+
+    void send(const WSString&& data) override {
+      yield();
+      client.write(reinterpret_cast<uint8_t*>(const_cast<char*>(data.c_str())), data.size());
+      yield();
+    }
+
+    void send(const uint8_t* data, const uint32_t len) override {
+      yield();
+      client.write(data, len);
+      yield();
+    }
+    
+    WSString readLine() override {
+      WSString line = "";
+
+      int ch = -1;
+      while( ch != '\n' && available()) {
+        ch = client.read();
+        if (ch < 0) continue;
+        line += (char) ch;
+      }
+
+      return line;
+    }
+
+    uint32_t read(uint8_t* buffer, const uint32_t len) override {
+      yield();
+      return client.read(buffer, len);
+    }
+
+    void close() override {
+      yield();
+      client.stop();
+    }
+
+    virtual ~GenericEspTcpClient() {
+      client.stop();
+    }
+
+  protected:
+    WifiClientImpl client;
+
+    int getSocket() const override {
+      return -1;
+    }    
+  };
+}} // websockets::network

+ 34 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/linux/linux_tcp_client.hpp

@@ -0,0 +1,34 @@
+#pragma once
+
+#ifdef __linux__ 
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+#include <tiny_websockets/network/tcp_socket.hpp>
+
+#define INVALID_SOCKET -1
+
+namespace websockets { namespace network {
+  class LinuxTcpClient : public TcpClient {
+    public:
+        LinuxTcpClient(int socket = INVALID_SOCKET);
+        bool connect(const WSString& host, int port) override;
+        bool poll() override;
+        bool available() override;
+        void send(const WSString& data) override;
+        void send(const WSString&& data) override;
+        void send(const uint8_t* data, const uint32_t len) override;
+        WSString readLine() override;
+        void read(uint8_t* buffer, const uint32_t len) override;
+        void close() override;
+        virtual ~LinuxTcpClient();
+
+    protected:
+        virtual int getSocket() const override { return _socket; }
+
+    private:
+        int _socket;
+    };
+}} // websockets::network
+
+#endif // #ifdef __linux__ 

+ 31 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/linux/linux_tcp_server.hpp

@@ -0,0 +1,31 @@
+#pragma once
+
+#ifdef __linux__ 
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_server.hpp>
+#include <tiny_websockets/network/linux/linux_tcp_client.hpp>
+
+#define DEFAULT_BACKLOG_SIZE 5
+
+namespace websockets { namespace network {
+  class LinuxTcpServer : public TcpServer {
+    public:
+        LinuxTcpServer(size_t backlog = DEFAULT_BACKLOG_SIZE) : _num_backlog(backlog) {}
+        bool listen(const uint16_t port) override;
+        bool poll() override;
+        TcpClient* accept() override;
+        bool available() override;
+        void close() override;
+        virtual ~LinuxTcpServer();
+
+    protected:
+        virtual int getSocket() const override { return _socket; }
+    
+    private:
+        int _socket;
+        size_t _num_backlog;
+    };
+}} // websockets::network
+
+#endif // #ifdef __linux__ 

+ 17 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/tcp_client.hpp

@@ -0,0 +1,17 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_socket.hpp>
+
+namespace websockets { namespace network {
+  struct TcpClient : public TcpSocket {
+    virtual bool poll() = 0;
+    virtual void send(const WSString& data) = 0;
+    virtual void send(const WSString&& data) = 0;
+    virtual void send(const uint8_t* data, const uint32_t len) = 0;
+    virtual WSString readLine() = 0;
+    virtual uint32_t read(uint8_t* buffer, const uint32_t len) = 0;
+    virtual bool connect(const WSString& host, int port) = 0;
+    virtual ~TcpClient() {}
+  };
+}} // websockets::network

+ 14 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/tcp_server.hpp

@@ -0,0 +1,14 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+#include <tiny_websockets/network/tcp_server.hpp>
+
+namespace websockets { namespace network {
+  struct TcpServer : public TcpSocket {
+    virtual bool poll() = 0;
+    virtual bool listen(const uint16_t port) = 0;
+    virtual TcpClient* accept() = 0;
+    virtual ~TcpServer() {}
+  };
+}} // websockets::network

+ 14 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/tcp_socket.hpp

@@ -0,0 +1,14 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+
+namespace websockets { namespace network {
+    struct TcpSocket {
+    public:
+        virtual bool available() = 0;
+        virtual void close() = 0;
+        virtual ~TcpSocket() {}
+    protected:
+        virtual int getSocket() const = 0;
+    };
+}} // websockets::network

+ 98 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/teensy41/teensy41_tcp_client.hpp

@@ -0,0 +1,98 @@
+#pragma once
+
+#ifdef ARDUINO_TEENSY41
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+#include <tiny_websockets/network/tcp_server.hpp>
+
+#include <NativeEthernet.h>
+
+namespace websockets { namespace network {
+  class Teensy41TcpClient : public TcpClient {
+  public:
+    Teensy41TcpClient(EthernetClient c) : client(c) {}
+
+    Teensy41TcpClient() {}
+
+    bool connect(const WSString& host, const int port) {
+      yield();
+      const char* hostStr = host.c_str();
+      // Teensy's NativeEthernet library doesn't accept a char buffer
+      // as an IP (it will try to resolve it). So we have to convert
+      // it if necessary.
+      IPAddress ip;
+      return (ip.fromString(hostStr)
+        ? client.connect(ip, port)
+        : client.connect(hostStr, port)
+      );
+    }
+
+    bool poll() {
+      yield();
+      return client.available();
+    }
+
+    bool available() override {
+      return client.connected();
+    }
+
+    void send(const WSString& data) override {
+      yield();
+      client.write(reinterpret_cast<uint8_t*>(const_cast<char*>(data.c_str())), data.size());
+      yield();
+    }
+
+    void send(const WSString&& data) override {
+      yield();
+      client.write(reinterpret_cast<uint8_t*>(const_cast<char*>(data.c_str())), data.size());
+      yield();
+    }
+
+    void send(const uint8_t* data, const uint32_t len) override {
+      yield();
+      client.write(data, len);
+      yield();
+    }
+
+    WSString readLine() override {
+      WSString line = "";
+
+      int ch = -1;
+      while( ch != '\n' && available()) {
+        // It is important to call `client.available()`. Otherwise no data can be read.
+        if (client.available()) {
+          ch = client.read();
+          if (ch >= 0) {
+            line += (char) ch;
+          }
+        }
+      }
+
+      return line;
+    }
+
+    uint32_t read(uint8_t* buffer, const uint32_t len) override {
+      yield();
+      return client.read(buffer, len);
+    }
+
+    void close() override {
+      yield();
+      client.stop();
+    }
+
+    virtual ~Teensy41TcpClient() {
+      client.stop();
+    }
+
+  protected:
+    EthernetClient client;
+
+    int getSocket() const override {
+      return -1;
+    }    
+  };  
+}} // websockets::network
+
+#endif // #ifdef ARDUINO_TEENSY41 

+ 57 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/teensy41/teensy41_tcp_server.hpp

@@ -0,0 +1,57 @@
+#pragma once
+
+#ifdef ARDUINO_TEENSY41 
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+#include <tiny_websockets/network/tcp_server.hpp>
+#include <tiny_websockets/network/teensy41/teensy41_tcp_client.hpp>
+
+#include <NativeEthernet.h>
+
+namespace websockets { namespace network {
+  class Teensy41TcpServer : public TcpServer {
+  public:
+    Teensy41TcpServer() {}
+
+    bool poll() override {
+      yield();
+      return server.available();
+    }
+
+    bool listen(const uint16_t port) override {
+      yield();
+      server = EthernetServer(port);
+      server.begin(port);
+      return available();
+    }
+    
+    TcpClient* accept() override {
+      auto client = server.accept();
+      return new Teensy41TcpClient(client);
+    }
+
+    bool available() override {
+      yield();
+      return static_cast<bool>(server);
+    }
+    
+    void close() override {
+      // Not Implemented
+    }
+
+    virtual ~Teensy41TcpServer() {
+      // Not Implemented
+    }
+
+  protected:
+    int getSocket() const override {
+      return -1; // Not Implemented
+    }
+  
+  private:
+    EthernetServer server;
+  };
+}} // websockets::network
+
+#endif // #ifdef ARDUINO_TEENSY41 

+ 45 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/windows/win_tcp_client.hpp

@@ -0,0 +1,45 @@
+#pragma once
+
+#ifdef _WIN32 
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+
+#define WIN32_LEAN_AND_MEAN
+
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x501
+
+#include <winsock2.h>
+#include <windows.h>
+#include <ws2tcpip.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <iostream>
+
+namespace websockets { namespace network {
+    class WinTcpClient : public TcpClient {
+    public:
+        WinTcpClient(const SOCKET s = INVALID_SOCKET);
+        bool connect(const WSString& host, const int port) override;
+        bool poll() override;
+        bool available() override;
+        void send(const WSString& data) override;
+        void send(const WSString&& data) override;
+        void send(const uint8_t* data, const uint32_t len) override;
+        WSString readLine() override;
+        void read(uint8_t* buffer, const uint32_t len) override;
+        void close() override;
+        virtual ~WinTcpClient();
+
+    protected:
+        int getSocket() const override;
+    
+    private:
+        SOCKET socket;
+    };
+}} // websockets::network
+
+
+#endif // #ifdef _WIN32 

+ 38 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/network/windows/win_tcp_server.hpp

@@ -0,0 +1,38 @@
+#pragma once
+
+#ifdef _WIN32 
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_server.hpp>
+
+#define WIN32_LEAN_AND_MEAN
+
+#undef _WIN32_WINNT
+#define _WIN32_WINNT 0x501
+
+#include <winsock2.h>
+#include <windows.h>
+#include <ws2tcpip.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+namespace websockets { namespace network {
+    class WinTcpServer : public TcpServer {
+    public:
+        bool listen(uint16_t port) override;
+        TcpClient* accept() override;
+        bool available() override;
+        bool poll() override;
+        void close() override;
+        virtual ~WinTcpServer();
+
+    protected:
+        int getSocket() const override;
+
+    private:
+        SOCKET socket;
+    };
+}} // websockets::network
+
+
+#endif // #ifdef _WIN32 

+ 28 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/server.hpp

@@ -0,0 +1,28 @@
+#pragma once
+
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/client.hpp>
+#include <functional>
+
+namespace websockets {
+  class WebsocketsServer {
+  public:
+    WebsocketsServer(network::TcpServer* server = new WSDefaultTcpServer);
+    
+    WebsocketsServer(const WebsocketsServer& other) = delete;
+    WebsocketsServer(const WebsocketsServer&& other) = delete;
+    
+    WebsocketsServer& operator=(const WebsocketsServer& other) = delete;
+    WebsocketsServer& operator=(const WebsocketsServer&& other) = delete;
+
+    bool available();
+    void listen(uint16_t port);
+    bool poll();
+    WebsocketsClient accept();
+
+    virtual ~WebsocketsServer();
+
+  private:
+    network::TcpServer* _server;
+  };
+}

+ 4 - 0
Esp32-Client/ArduinoWebsockets/src/tiny_websockets/ws_config_defs.hpp

@@ -0,0 +1,4 @@
+#pragma once
+
+#define _WS_CONFIG_NO_TRUE_RANDOMNESS
+#define _WS_BUFFER_SIZE 512

+ 644 - 0
Esp32-Client/ArduinoWebsockets/src/websockets_client.cpp

@@ -0,0 +1,644 @@
+#include <tiny_websockets/internals/ws_common.hpp>
+#include <tiny_websockets/network/tcp_client.hpp>
+#include <tiny_websockets/message.hpp>
+#include <tiny_websockets/client.hpp>
+#include <tiny_websockets/internals/wscrypto/crypto.hpp>
+
+namespace websockets {
+    WebsocketsClient::WebsocketsClient() : WebsocketsClient(std::make_shared<WSDefaultTcpClient>()) {
+        // Empty
+    }
+
+    WebsocketsClient::WebsocketsClient(std::shared_ptr<network::TcpClient> client) :
+        _client(client),
+        _endpoint(client),
+        _connectionOpen(client->available()),
+        _messagesCallback([](WebsocketsClient&, WebsocketsMessage){}),
+        _eventsCallback([](WebsocketsClient&, WebsocketsEvent, WSInterfaceString){}),
+        _sendMode(SendMode_Normal) {
+        // Empty
+    }
+
+    WebsocketsClient::WebsocketsClient(const WebsocketsClient& other) :
+        _client(other._client),
+        _endpoint(other._endpoint),
+        _connectionOpen(other._client->available()),
+        _messagesCallback(other._messagesCallback),
+        _eventsCallback(other._eventsCallback),
+        _sendMode(other._sendMode) {
+
+        // delete other's client
+        const_cast<WebsocketsClient&>(other)._client = nullptr;
+        const_cast<WebsocketsClient&>(other)._connectionOpen = false;
+    }
+
+    WebsocketsClient::WebsocketsClient(const WebsocketsClient&& other) :
+        _client(other._client),
+        _endpoint(other._endpoint),
+        _connectionOpen(other._client->available()),
+        _messagesCallback(other._messagesCallback),
+        _eventsCallback(other._eventsCallback),
+        _sendMode(other._sendMode) {
+
+        // delete other's client
+        const_cast<WebsocketsClient&>(other)._client = nullptr;
+        const_cast<WebsocketsClient&>(other)._connectionOpen = false;
+    }
+
+    WebsocketsClient& WebsocketsClient::operator=(const WebsocketsClient& other) {
+        // call endpoint's copy operator
+        _endpoint = other._endpoint;
+
+        // get callbacks and data from other
+        this->_client = other._client;
+        this->_messagesCallback = other._messagesCallback;
+        this->_eventsCallback = other._eventsCallback;
+        this->_connectionOpen = other._connectionOpen;
+        this->_sendMode = other._sendMode;
+
+        // delete other's client
+        const_cast<WebsocketsClient&>(other)._client = nullptr;
+        const_cast<WebsocketsClient&>(other)._connectionOpen = false;
+        return *this;
+    }
+
+    WebsocketsClient& WebsocketsClient::operator=(const WebsocketsClient&& other) {
+        // call endpoint's copy operator
+        _endpoint = other._endpoint;
+
+        // get callbacks and data from other
+        this->_client = other._client;
+        this->_messagesCallback = other._messagesCallback;
+        this->_eventsCallback = other._eventsCallback;
+        this->_connectionOpen = other._connectionOpen;
+        this->_sendMode = other._sendMode;
+
+        // delete other's client
+        const_cast<WebsocketsClient&>(other)._client = nullptr;
+        const_cast<WebsocketsClient&>(other)._connectionOpen = false;
+        return *this;
+    }
+
+    struct HandshakeRequestResult {
+        WSString requestStr;
+        WSString expectedAcceptKey;
+    };
+
+    bool shouldAddDefaultHeader(const std::string& keyWord, const std::vector<std::pair<WSString, WSString>>& customHeaders) {
+        for (const auto& header : customHeaders) {
+            if(!keyWord.compare(header.first)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    HandshakeRequestResult generateHandshake(const WSString& host, const WSString& uri,
+                                             const std::vector<std::pair<WSString, WSString>>& customHeaders) {
+
+        WSString key = crypto::base64Encode(crypto::randomBytes(16));
+
+        WSString handshake = "GET " + uri + " HTTP/1.1\r\n";
+        handshake += "Host: " + host + "\r\n";
+        handshake += "Sec-WebSocket-Key: " + key + "\r\n";
+
+        for (const auto& header: customHeaders) {
+            handshake += header.first + ": " + header.second + "\r\n";
+        }
+
+        if (shouldAddDefaultHeader("Upgrade", customHeaders)) {
+            handshake += "Upgrade: websocket\r\n";
+        }
+
+        if (shouldAddDefaultHeader("Connection", customHeaders)) {
+            handshake += "Connection: Upgrade\r\n";
+        }
+
+        if (shouldAddDefaultHeader("Sec-WebSocket-Version", customHeaders)) {
+            handshake += "Sec-WebSocket-Version: 13\r\n";
+        }
+
+        if (shouldAddDefaultHeader("User-Agent", customHeaders)) {
+            handshake += "User-Agent: TinyWebsockets Client\r\n";
+        }
+
+        if (shouldAddDefaultHeader("Origin", customHeaders)) {
+            handshake += "Origin: https://github.com/gilmaimon/TinyWebsockets\r\n";
+        }
+
+        handshake += "\r\n";
+
+        HandshakeRequestResult result;
+        result.requestStr = handshake;
+#ifndef _WS_CONFIG_SKIP_HANDSHAKE_ACCEPT_VALIDATION
+        result.expectedAcceptKey = crypto::websocketsHandshakeEncodeKey(key);
+#endif
+        return std::move(result);
+    }
+
+    bool isWhitespace(char ch) {
+        return ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n';
+    }
+
+    struct HandshakeResponseResult {
+        bool isSuccess;
+        WSString serverAccept;
+    };
+
+    bool isCaseInsensetiveEqual(const WSString lhs, const WSString rhs) {
+      if (lhs.size() != rhs.size()) return false;
+
+      for (size_t i = 0; i < lhs.size(); i++) {
+        char leftLowerCaseChar = lhs[i] >= 'A' && lhs[i] <= 'Z' ? lhs[i] - 'A' + 'a' : lhs[i];
+        char righerLowerCaseChar = rhs[i] >= 'A' && rhs[i] <= 'Z' ? rhs[i] - 'A' + 'a' : rhs[i];
+        if (leftLowerCaseChar != righerLowerCaseChar) return false;
+      }
+
+      return true;
+    }
+
+    HandshakeResponseResult parseHandshakeResponse(std::vector<WSString> responseHeaders) {
+        bool didUpgradeToWebsockets = false, isConnectionUpgraded = false;
+        WSString serverAccept = "";
+        for(WSString header : responseHeaders) {
+            auto colonIndex = header.find_first_of(':');
+
+            WSString key = header.substr(0, colonIndex);
+            WSString value = header.substr(colonIndex + 2); // +2 (ignore space and ':')
+
+            if(isCaseInsensetiveEqual(key, "upgrade")) {
+                didUpgradeToWebsockets = isCaseInsensetiveEqual(value, "Websocket");
+            } else if(isCaseInsensetiveEqual(key, "connection")) {
+                isConnectionUpgraded = isCaseInsensetiveEqual(value, "upgrade");
+            } else if(isCaseInsensetiveEqual(key, "Sec-WebSocket-Accept")) {
+                serverAccept = value;
+            }
+        }
+
+        HandshakeResponseResult result;
+        result.isSuccess = serverAccept != "" && didUpgradeToWebsockets && isConnectionUpgraded;
+        result.serverAccept = serverAccept;
+        return result;
+    }
+
+    bool doestStartsWith(WSString str, WSString prefix) {
+        if(str.size() < prefix.size()) return false;
+        for(size_t i = 0; i < prefix.size(); i++) {
+            if(str[i] != prefix[i]) return false;
+        }
+
+        return true;
+    }
+
+    void WebsocketsClient::upgradeToSecuredConnection() {
+    #ifndef _WS_CONFIG_NO_SSL
+        auto client = new WSDefaultSecuredTcpClient;
+
+    #ifdef ESP8266
+        if(
+				this->_optional_ssl_fingerprint
+			|| 	(this->_optional_ssl_rsa_cert && this->_optional_ssl_rsa_private_key)
+			|| 	(this->_optional_ssl_ec_cert && this->_optional_ssl_ec_private_key)
+			|| 	this->_optional_ssl_trust_anchors
+			|| 	this->_optional_ssl_known_key
+		) {
+            if(this->_optional_ssl_fingerprint) {
+                client->setFingerprint(this->_optional_ssl_fingerprint);
+            }
+            if(this->_optional_ssl_trust_anchors) {
+                client->setTrustAnchors(this->_optional_ssl_trust_anchors);
+            }
+			if(this->_optional_ssl_known_key) {
+				client->setKnownKey(this->_optional_ssl_known_key);
+			}
+            if(this->_optional_ssl_rsa_cert && this->_optional_ssl_rsa_private_key) {
+                client->setClientRSACert(this->_optional_ssl_rsa_cert, this->_optional_ssl_rsa_private_key);
+            }
+			if(this->_optional_ssl_ec_cert && this->_optional_ssl_ec_private_key) {
+                client->setClientECCert(this->_optional_ssl_ec_cert, this->_optional_ssl_ec_private_key);
+            }
+        } else {
+            client->setInsecure();
+        }
+    #elif defined(ESP32)
+        if(this->_optional_ssl_ca_cert) {
+            client->setCACert(this->_optional_ssl_ca_cert);
+        }
+        if(this->_optional_ssl_client_ca) {
+            client->setCertificate(this->_optional_ssl_client_ca);
+        }
+        if(this->_optional_ssl_private_key) {
+            client->setPrivateKey(this->_optional_ssl_private_key);
+        }
+    #endif
+
+        this->_client = std::shared_ptr<WSDefaultSecuredTcpClient>(client);
+        this->_endpoint.setInternalSocket(this->_client);
+    #endif //_WS_CONFIG_NO_SSL
+    }
+
+    void WebsocketsClient::addHeader(const WSInterfaceString key, const WSInterfaceString value) {
+        _customHeaders.push_back({internals::fromInterfaceString(key), internals::fromInterfaceString(value)});
+    }
+
+    bool WebsocketsClient::connect(WSInterfaceString _url) {
+        WSString url = internals::fromInterfaceString(_url);
+        WSString protocol = "";
+        int defaultPort = 0;
+
+        if(doestStartsWith(url, "http://")) {
+            defaultPort = 80;
+            protocol = "http";
+            url = url.substr(7); //strlen("http://") == 7
+        } else if(doestStartsWith(url, "ws://")) {
+            defaultPort = 80;
+            protocol = "ws";
+            url = url.substr(5); //strlen("ws://") == 5
+        }
+
+    #ifndef _WS_CONFIG_NO_SSL
+        else if(doestStartsWith(url, "wss://")) {
+            defaultPort = 443;
+            protocol = "wss";
+            url = url.substr(6); //strlen("wss://") == 6
+
+            upgradeToSecuredConnection();
+        } else if(doestStartsWith(url, "https://")) {
+            defaultPort = 443;
+            protocol = "https";
+            url = url.substr(8); //strlen("https://") == 8
+
+            upgradeToSecuredConnection();
+        }
+    #endif
+
+        else {
+            return false;
+            // Not supported
+        }
+
+        auto uriBeg = url.find_first_of('/');
+        std::string host = url, uri = "/";
+
+        if(static_cast<int>(uriBeg) != -1) {
+            uri = url.substr(uriBeg);
+            host = url.substr(0, uriBeg);
+        }
+
+        auto portIdx = host.find_first_of(':');
+        int port = defaultPort;
+        if(static_cast<int>(portIdx) != -1) {
+            auto onlyHost = host.substr(0, portIdx);
+            ++portIdx;
+            port = 0;
+            while(portIdx < host.size() && host[portIdx] >= '0' && host[portIdx] <= '9') {
+                port = port * 10 + (host[portIdx] - '0');
+                ++portIdx;
+            }
+
+            host = onlyHost;
+        }
+
+        return this->connect(
+            internals::fromInternalString(host),
+            port,
+            internals::fromInternalString(uri)
+        );
+    }
+
+    bool WebsocketsClient::connect(WSInterfaceString host, int port, WSInterfaceString path) {
+        this->_connectionOpen = this->_client->connect(internals::fromInterfaceString(host), port);
+        if (!this->_connectionOpen) return false;
+
+        auto handshake = generateHandshake(internals::fromInterfaceString(host), internals::fromInterfaceString(path), _customHeaders);
+        this->_client->send(handshake.requestStr);
+
+        // This check is needed because of an ESP32 lib bug that wont signal that the connection had
+        // failed in `->connect` (called above), sometimes the disconnect will only be noticed here (after a `send`)
+        if(!available()) {
+            return false;
+        }
+
+        auto head = this->_client->readLine();
+        if(!doestStartsWith(head, "HTTP/1.1 101")) {
+            close(CloseReason_ProtocolError);
+            return false;
+        }
+
+        std::vector<WSString> serverResponseHeaders;
+        WSString line = "";
+        while (true) {
+            line = this->_client->readLine();
+
+            if (line.size() < 2) {
+                close(CloseReason_ProtocolError);
+                return false;
+            }
+
+            if (line == "\r\n") break;
+            // remove /r/n from line end
+            line = line.substr(0, line.size() - 2);
+
+            serverResponseHeaders.push_back(line);
+        }
+        auto parsedResponse = parseHandshakeResponse(serverResponseHeaders);
+
+#ifdef _WS_CONFIG_SKIP_HANDSHAKE_ACCEPT_VALIDATION
+        bool serverAcceptMismatch = false;
+#else
+        bool serverAcceptMismatch = parsedResponse.serverAccept != handshake.expectedAcceptKey;
+#endif
+        if(parsedResponse.isSuccess == false || serverAcceptMismatch) {
+            close(CloseReason_ProtocolError);
+            return false;
+        }
+
+        this->_eventsCallback(*this, WebsocketsEvent::ConnectionOpened, {});
+        return true;
+    }
+
+    void WebsocketsClient::onMessage(MessageCallback callback) {
+        this->_messagesCallback = callback;
+    }
+
+    void WebsocketsClient::onMessage(PartialMessageCallback callback) {
+        this->_messagesCallback = [callback](WebsocketsClient&, WebsocketsMessage msg) {
+            callback(msg);
+        };
+    }
+
+    void WebsocketsClient::onEvent(EventCallback callback) {
+        this->_eventsCallback = callback;
+    }
+
+    void WebsocketsClient::onEvent(PartialEventCallback callback) {
+        this->_eventsCallback = [callback](WebsocketsClient&, WebsocketsEvent event, WSInterfaceString data) {
+            callback(event, data);
+        };
+    }
+
+    bool WebsocketsClient::poll() {
+        bool messageReceived = false;
+        while(available() && _endpoint.poll()) {
+            auto msg = _endpoint.recv();
+            if(msg.isEmpty()) {
+                continue;
+            }
+            messageReceived = true;
+
+            if(msg.isBinary() || msg.isText()) {
+                this->_messagesCallback(*this, std::move(msg));
+            } else if(msg.isContinuation()) {
+                // continuation messages will only be returned when policy is appropriate
+                this->_messagesCallback(*this, std::move(msg));
+            } else if(msg.isPing()) {
+                _handlePing(std::move(msg));
+            } else if(msg.isPong()) {
+                _handlePong(std::move(msg));
+            } else if(msg.isClose()) {
+                this->_connectionOpen = false;
+                _handleClose(std::move(msg));
+            }
+        }
+
+        return messageReceived;
+    }
+
+    WebsocketsMessage WebsocketsClient::readBlocking() {
+        while(available()) {
+#ifdef PLATFORM_DOES_NOT_SUPPORT_BLOCKING_READ
+            while(available() && _endpoint.poll() == false) continue;
+#endif
+            auto msg = _endpoint.recv();
+            if(!msg.isEmpty()) return msg;
+        }
+        return {};
+    }
+
+    bool WebsocketsClient::send(const WSInterfaceString& data) {
+        auto str = internals::fromInterfaceString(data);
+        return this->send(str.c_str(), str.size());
+    }
+
+    bool WebsocketsClient::send(const WSInterfaceString&& data) {
+        auto str = internals::fromInterfaceString(data);
+        return this->send(str.c_str(), str.size());
+    }
+
+    bool WebsocketsClient::send(const char* data) {
+        return this->send(data, strlen(data));
+    }
+
+    bool WebsocketsClient::send(const char* data, const size_t len) {
+        if(available()) {
+            // if in normal mode
+            if(this->_sendMode == SendMode_Normal) {
+                // send a normal message
+                return _endpoint.send(
+                    data,
+                    len,
+                    internals::ContentType::Text,
+                    true
+                );
+            }
+            // if in streaming mode
+            else if(this->_sendMode == SendMode_Streaming) {
+                // send a continue frame
+                return _endpoint.send(
+                    data,
+                    len,
+                    internals::ContentType::Continuation,
+                    false
+                );
+            }
+        }
+        return false;
+    }
+
+    bool WebsocketsClient::sendBinary(WSInterfaceString data) {
+        auto str = internals::fromInterfaceString(data);
+        return this->sendBinary(str.c_str(), str.size());
+    }
+
+    bool WebsocketsClient::sendBinary(const char* data, const size_t len) {
+        if(available()) {
+            // if in normal mode
+            if(this->_sendMode == SendMode_Normal) {
+                // send a normal message
+                return _endpoint.send(
+                    data,
+                    len,
+                    internals::ContentType::Binary,
+                    true
+                );
+            }
+            // if in streaming mode
+            else if(this->_sendMode == SendMode_Streaming) {
+                // send a continue frame
+                return _endpoint.send(
+                    data,
+                    len,
+                    internals::ContentType::Continuation,
+                    false
+                );
+            }
+        }
+        return false;
+    }
+
+    bool WebsocketsClient::stream(const WSInterfaceString data) {
+        if(available() && this->_sendMode == SendMode_Normal) {
+            this->_sendMode = SendMode_Streaming;
+            return _endpoint.send(
+                internals::fromInterfaceString(data),
+                internals::ContentType::Text,
+                false
+            );
+        }
+        return false;
+    }
+
+
+    bool WebsocketsClient::streamBinary(const WSInterfaceString data) {
+        if(available() && this->_sendMode == SendMode_Normal) {
+            this->_sendMode = SendMode_Streaming;
+            return _endpoint.send(
+                internals::fromInterfaceString(data),
+                internals::ContentType::Binary,
+                false
+            );
+        }
+        return false;
+    }
+
+    bool WebsocketsClient::end(const WSInterfaceString data) {
+        if(available() && this->_sendMode == SendMode_Streaming) {
+            this->_sendMode = SendMode_Normal;
+            return _endpoint.send(
+                internals::fromInterfaceString(data),
+                internals::ContentType::Continuation,
+                true
+            );
+        }
+        return false;
+    }
+
+    void WebsocketsClient::setFragmentsPolicy(const FragmentsPolicy newPolicy) {
+        _endpoint.setFragmentsPolicy(newPolicy);
+    }
+
+    bool WebsocketsClient::available(const bool activeTest) {
+        if(activeTest)  {
+            _endpoint.ping("");
+        }
+
+        bool updatedConnectionOpen = this->_connectionOpen && this->_client && this->_client->available();
+
+        if(updatedConnectionOpen != this->_connectionOpen) {
+            _endpoint.close(CloseReason_AbnormalClosure);
+            this->_eventsCallback(*this, WebsocketsEvent::ConnectionClosed, "");
+        }
+
+        this->_connectionOpen = updatedConnectionOpen;
+        return this->_connectionOpen;
+    }
+
+    bool WebsocketsClient::ping(const WSInterfaceString data) {
+        if(available()) {
+            return _endpoint.ping(internals::fromInterfaceString(data));
+        }
+        return false;
+    }
+
+    bool WebsocketsClient::pong(const WSInterfaceString data) {
+        if(available()) {
+            return _endpoint.pong(internals::fromInterfaceString(data));
+        }
+        return false;
+    }
+
+    void WebsocketsClient::close(const CloseReason reason) {
+        if(available()) {
+            this->_connectionOpen = false;
+            _endpoint.close(reason);
+            _handleClose({});
+        }
+    }
+
+    CloseReason WebsocketsClient::getCloseReason() const {
+        return _endpoint.getCloseReason();
+    }
+
+    void WebsocketsClient::_handlePing(const WebsocketsMessage message) {
+        this->_eventsCallback(*this, WebsocketsEvent::GotPing, message.data());
+    }
+
+    void WebsocketsClient::_handlePong(const WebsocketsMessage message) {
+        this->_eventsCallback(*this, WebsocketsEvent::GotPong, message.data());
+    }
+
+    void WebsocketsClient::_handleClose(const WebsocketsMessage message) {
+        this->_eventsCallback(*this, WebsocketsEvent::ConnectionClosed, message.data());
+    }
+
+
+#ifdef ESP8266
+    void WebsocketsClient::setFingerprint(const char* fingerprint) {
+        this->_optional_ssl_fingerprint = fingerprint;
+    }
+
+    void WebsocketsClient::setInsecure() {
+        this->_optional_ssl_fingerprint = nullptr;
+    	this->_optional_ssl_rsa_cert = nullptr;
+    	this->_optional_ssl_rsa_private_key = nullptr;
+		this->_optional_ssl_ec_cert = nullptr;
+    	this->_optional_ssl_ec_private_key = nullptr;
+    	this->_optional_ssl_trust_anchors = nullptr;
+		this->_optional_ssl_known_key = nullptr;
+    }
+
+    void WebsocketsClient::setClientRSACert(const X509List *cert, const PrivateKey *sk) {
+    	this->_optional_ssl_rsa_cert = cert;
+    	this->_optional_ssl_rsa_private_key = sk;
+	}
+
+	void WebsocketsClient::setClientECCert(const X509List *cert, const PrivateKey *sk) {
+    	this->_optional_ssl_ec_cert = cert;
+    	this->_optional_ssl_ec_private_key = sk;
+	}
+
+    void WebsocketsClient::setTrustAnchors(const X509List *ta){
+    	this->_optional_ssl_trust_anchors = ta;
+	}
+
+	void WebsocketsClient::setKnownKey(const PublicKey *pk) {
+		this->_optional_ssl_known_key = pk;
+	}
+
+#elif defined(ESP32)
+    void WebsocketsClient::setCACert(const char* ca_cert) {
+        this->_optional_ssl_ca_cert = ca_cert;
+    }
+
+    void WebsocketsClient::setCertificate(const char* client_ca) {
+        this->_optional_ssl_client_ca = client_ca;
+    }
+
+    void WebsocketsClient::setPrivateKey(const char* private_key) {
+        this->_optional_ssl_private_key = private_key;
+    }
+
+    void WebsocketsClient::setInsecure() {
+        this->_optional_ssl_ca_cert = nullptr;
+        this->_optional_ssl_client_ca = nullptr;
+        this->_optional_ssl_private_key = nullptr;
+    }
+#endif
+
+    WebsocketsClient::~WebsocketsClient() {
+        if(available()) {
+            this->close(CloseReason_GoingAway);
+        }
+    }
+}

+ 468 - 0
Esp32-Client/ArduinoWebsockets/src/websockets_endpoint.cpp

@@ -0,0 +1,468 @@
+#include <tiny_websockets/internals/websockets_endpoint.hpp>
+
+namespace websockets { 
+
+    CloseReason GetCloseReason(uint16_t reasonCode) {
+        switch(reasonCode) {
+            case CloseReason_NormalClosure: 
+                return CloseReason_NormalClosure;
+            
+            case CloseReason_GoingAway: 
+                return CloseReason_GoingAway;
+            
+            case CloseReason_ProtocolError: 
+                return CloseReason_ProtocolError;
+            
+            case CloseReason_UnsupportedData: 
+                return CloseReason_UnsupportedData;
+            
+            case CloseReason_AbnormalClosure: 
+                return CloseReason_AbnormalClosure;
+            
+            case CloseReason_InvalidPayloadData: 
+                return CloseReason_InvalidPayloadData;
+            
+            case CloseReason_PolicyViolation: 
+                return CloseReason_PolicyViolation;
+            
+            case CloseReason_MessageTooBig: 
+                return CloseReason_MessageTooBig;
+            
+            case CloseReason_NoStatusRcvd: 
+                return CloseReason_NoStatusRcvd;
+            
+            case CloseReason_InternalServerError: 
+                return CloseReason_InternalServerError;
+
+            default: return CloseReason_None;
+        }
+    }    
+
+namespace internals {
+
+    uint32_t swapEndianess(uint32_t num) {
+        uint32_t highest = (num >> 24);
+        uint32_t second = (num << 8) >> 24;
+        uint32_t third = (num << 16) >> 24;
+        uint32_t lowest = (num << 24) >> 24;
+
+        return highest | (second << 8) | (third << 16) | (lowest << 24);
+    }
+
+    uint64_t swapEndianess(uint64_t num) {
+        uint32_t upper = (num >> 32);
+        uint32_t lower = (num << 32) >> 32;
+    
+        upper = swapEndianess(upper);
+        lower = swapEndianess(lower);
+    
+        uint64_t upperLong = upper;
+        uint64_t lowerLong = lower;
+        
+        return upperLong | (lowerLong << 32);
+    }
+
+    WebsocketsEndpoint::WebsocketsEndpoint(std::shared_ptr<network::TcpClient> client, FragmentsPolicy fragmentsPolicy) : 
+        _client(client),
+        _fragmentsPolicy(fragmentsPolicy),
+        _recvMode(RecvMode_Normal),
+        _streamBuilder(fragmentsPolicy == FragmentsPolicy_Notify? true: false),
+        _closeReason(CloseReason_None) {
+        // Empty
+    }
+
+    WebsocketsEndpoint::WebsocketsEndpoint(const WebsocketsEndpoint& other) : 
+        _client(other._client), 
+        _fragmentsPolicy(other._fragmentsPolicy), 
+        _recvMode(other._recvMode), 
+        _streamBuilder(other._streamBuilder), 
+        _closeReason(other._closeReason),
+        _useMasking(other._useMasking) {
+
+        const_cast<WebsocketsEndpoint&>(other)._client = nullptr;
+    }
+    WebsocketsEndpoint::WebsocketsEndpoint(const WebsocketsEndpoint&& other) : 
+        _client(other._client), 
+        _fragmentsPolicy(other._fragmentsPolicy), 
+        _recvMode(other._recvMode), 
+        _streamBuilder(other._streamBuilder), 
+        _closeReason(other._closeReason),
+        _useMasking(other._useMasking) {
+
+        const_cast<WebsocketsEndpoint&>(other)._client = nullptr;
+    }
+
+    WebsocketsEndpoint& WebsocketsEndpoint::operator=(const WebsocketsEndpoint& other) {
+        this->_client = other._client;
+        this->_fragmentsPolicy = other._fragmentsPolicy;
+        this->_recvMode = other._recvMode;
+        this->_streamBuilder = other._streamBuilder;
+        this->_closeReason = other._closeReason;
+        this->_useMasking = other._useMasking;
+
+        const_cast<WebsocketsEndpoint&>(other)._client = nullptr;
+
+        return *this;
+    }
+    WebsocketsEndpoint& WebsocketsEndpoint::operator=(const WebsocketsEndpoint&& other) {
+        this->_client = other._client;
+        this->_fragmentsPolicy = other._fragmentsPolicy;
+        this->_recvMode = other._recvMode;
+        this->_streamBuilder = other._streamBuilder;
+        this->_closeReason = other._closeReason;
+        this->_useMasking = other._useMasking;
+
+        const_cast<WebsocketsEndpoint&>(other)._client = nullptr;
+
+        return *this;
+    }
+
+    void WebsocketsEndpoint::setInternalSocket(std::shared_ptr<network::TcpClient> socket) {
+        this->_client = socket;
+    }
+
+    bool WebsocketsEndpoint::poll() {
+        return this->_client->poll();
+    }
+
+    uint32_t readUntilSuccessfullOrError(network::TcpClient& socket, uint8_t* buffer, const uint32_t len) {
+        auto numRead = socket.read(buffer, len);
+        while(numRead == static_cast<uint32_t>(-1) && socket.available()) {
+            numRead = socket.read(buffer, len);
+        }
+        return numRead;
+    }
+
+    Header readHeaderFromSocket(network::TcpClient& socket) {
+        Header header;
+        header.payload = 0;
+        readUntilSuccessfullOrError(socket, reinterpret_cast<uint8_t*>(&header), 2);
+        return header;
+    }
+
+    uint64_t readExtendedPayloadLength(network::TcpClient& socket, const Header& header) {
+        uint64_t extendedPayload = header.payload;
+        // in case of extended payload length
+        if (header.payload == 126) {
+            // read next 16 bits as payload length
+            uint16_t tmp = 0;
+            readUntilSuccessfullOrError(socket, reinterpret_cast<uint8_t*>(&tmp), 2);
+            tmp = (tmp << 8) | (tmp >> 8);
+            extendedPayload = tmp;
+        }
+        else if (header.payload == 127) {
+            uint64_t tmp = 0;
+            readUntilSuccessfullOrError(socket, reinterpret_cast<uint8_t*>(&tmp), 8);
+            extendedPayload = swapEndianess(tmp);
+        }
+
+        return extendedPayload;
+    }
+
+    void readMaskingKey(network::TcpClient& socket, uint8_t* outputBuffer) {
+        readUntilSuccessfullOrError(socket, reinterpret_cast<uint8_t*>(outputBuffer), 4);
+    }
+
+    WSString readData(network::TcpClient& socket, uint64_t extendedPayload) {
+        const uint64_t BUFFER_SIZE = _WS_BUFFER_SIZE;
+
+        WSString data(extendedPayload, '\0');
+        uint8_t buffer[BUFFER_SIZE];
+        uint64_t done_reading = 0;
+        while (done_reading < extendedPayload && socket.available()) {
+            uint64_t to_read = extendedPayload - done_reading >= BUFFER_SIZE ? BUFFER_SIZE : extendedPayload - done_reading;
+            uint32_t numReceived = readUntilSuccessfullOrError(socket, buffer, to_read);
+
+            // On failed reads, skip
+            if(!socket.available()) break;
+
+            for (uint64_t i = 0; i < numReceived; i++) {
+                data[done_reading + i] = static_cast<char>(buffer[i]);
+            }
+
+            done_reading += numReceived;
+        }
+        return std::move(data);
+    }
+
+    void remaskData(WSString& data, const uint8_t* const maskingKey, uint64_t payloadLength) {
+      for (uint64_t i = 0; i < payloadLength; i++) {
+        data[i] = data[i] ^ maskingKey[i % 4];
+      }
+    }
+
+    WebsocketsFrame WebsocketsEndpoint::_recv() {
+        auto header = readHeaderFromSocket(*this->_client);
+        if(!_client->available()) return WebsocketsFrame(); // In case of faliure
+
+        uint64_t payloadLength = readExtendedPayloadLength(*this->_client, header);
+        if(!_client->available()) return WebsocketsFrame(); // In case of faliure
+
+#ifdef _WS_CONFIG_MAX_MESSAGE_SIZE
+        if(payloadLength > _WS_CONFIG_MAX_MESSAGE_SIZE) {
+            return WebsocketsFrame();
+        }
+#endif
+
+        uint8_t maskingKey[4];
+        // if masking is set
+        if (header.mask) {
+            readMaskingKey(*this->_client, maskingKey);
+            if(!_client->available()) return WebsocketsFrame(); // In case of faliure
+        }
+
+        WebsocketsFrame frame;
+        // read the message's payload (data) according to the read length
+        frame.payload = readData(*this->_client, payloadLength);
+        if(!_client->available()) return WebsocketsFrame(); // In case of faliure
+
+        // if masking is set un-mask the message
+        if (header.mask) {
+            remaskData(frame.payload, maskingKey, payloadLength);
+        }
+
+        // Construct frame from data and header that was read
+        frame.fin = header.fin;
+        frame.mask = header.mask;
+
+        frame.mask_buf[0] = maskingKey[0];
+        frame.mask_buf[1] = maskingKey[1];
+        frame.mask_buf[2] = maskingKey[2];
+        frame.mask_buf[3] = maskingKey[3];
+
+        frame.opcode = header.opcode;
+        frame.payload_length = payloadLength;
+
+        return std::move(frame);
+    }
+
+    WebsocketsMessage WebsocketsEndpoint::handleFrameInStreamingMode(WebsocketsFrame& frame) {
+        if(frame.isControlFrame()) {
+            auto msg = WebsocketsMessage::CreateFromFrame(std::move(frame));
+            this->handleMessageInternally(msg);
+            return std::move(msg);
+        }
+        else if(frame.isBeginningOfFragmentsStream()) {
+            this->_recvMode = RecvMode_Streaming;
+
+            if(this->_streamBuilder.isEmpty()) {
+                this->_streamBuilder.first(frame);
+                // if policy is set to notify, return the frame to the user
+                if(this->_fragmentsPolicy == FragmentsPolicy_Notify) {
+                    return WebsocketsMessage(this->_streamBuilder.type(), std::move(frame.payload), MessageRole::First);
+                }
+                else return {};
+            }
+        }
+        else if(frame.isContinuesFragment()) {
+            this->_streamBuilder.append(frame);
+            if(this->_streamBuilder.isOk()) {
+                // if policy is set to notify, return the frame to the user
+                if(this->_fragmentsPolicy == FragmentsPolicy_Notify) {
+                    return WebsocketsMessage(this->_streamBuilder.type(), std::move(frame.payload), MessageRole::Continuation);
+                }
+                else return {};
+            }
+        }
+        else if(frame.isEndOfFragmentsStream()) {
+            this->_recvMode = RecvMode_Normal;
+            this->_streamBuilder.end(frame);
+            if(this->_streamBuilder.isOk()) {
+                // if policy is set to notify, return the frame to the user
+                if(this->_fragmentsPolicy == FragmentsPolicy_Aggregate) {
+                    auto completeMessage = this->_streamBuilder.build();
+                    this->_streamBuilder = WebsocketsMessage::StreamBuilder(false);
+                    this->handleMessageInternally(completeMessage);
+                    return completeMessage;
+                }
+                else { // in case of notify policy
+                    auto messageType = this->_streamBuilder.type();
+                    this->_streamBuilder = WebsocketsMessage::StreamBuilder(true);
+                    return WebsocketsMessage(messageType, std::move(frame.payload), MessageRole::Last);
+                }                
+            }
+        } 
+        
+        // Error
+        close(CloseReason_ProtocolError);
+        return {};
+    }
+
+    WebsocketsMessage WebsocketsEndpoint::handleFrameInStandardMode(WebsocketsFrame& frame) {
+        // Normal (unfragmented) frames are handled as a complete message 
+        if(frame.isNormalUnfragmentedMessage() || frame.isControlFrame()) {
+            auto msg = WebsocketsMessage::CreateFromFrame(std::move(frame));
+            this->handleMessageInternally(msg);
+            return std::move(msg);
+        } 
+        else if(frame.isBeginningOfFragmentsStream()) {
+            return handleFrameInStreamingMode(frame);
+        }
+
+        // This is an error. a bad combination of opcodes and fin flag arrived.
+        close(CloseReason_ProtocolError);
+        return {};
+    }
+
+    WebsocketsMessage WebsocketsEndpoint::recv() {        
+        auto frame = _recv();
+        if (frame.isEmpty()) {
+            return {};
+        }
+
+        if(this->_recvMode == RecvMode_Normal) {
+            return handleFrameInStandardMode(frame);
+        } 
+        else /* this->_recvMode == RecvMode_Streaming */ {
+            return handleFrameInStreamingMode(frame);
+        }
+    }
+
+    void WebsocketsEndpoint::handleMessageInternally(WebsocketsMessage& msg) {
+        if(msg.isPing()) {
+            pong(internals::fromInterfaceString(msg.data()));
+        } else if(msg.isClose()) {
+            // is there a reason field
+            if(internals::fromInterfaceString(msg.data()).size() >= 2) {
+                uint16_t reason = *(reinterpret_cast<const uint16_t*>(msg.data().c_str()));
+                reason = reason >> 8 | reason << 8;
+                this->_closeReason = GetCloseReason(reason);
+            } else {
+                this->_closeReason = CloseReason_GoingAway;
+            }
+            close(this->_closeReason);
+        }
+    }
+
+    bool WebsocketsEndpoint::send(const char* data, const size_t len, const uint8_t opcode, const bool fin) {
+        return this->send(data, len, opcode, fin, this->_useMasking);
+    }
+
+    bool WebsocketsEndpoint::send(const WSString& data, const uint8_t opcode, const bool fin) {
+        return this->send(data, opcode, fin, this->_useMasking);
+    }
+
+    bool WebsocketsEndpoint::send(const WSString& data, const uint8_t opcode, const bool fin, const bool mask, const char* maskingKey) { 
+        return send(data.c_str(), data.size(), opcode, fin, mask, maskingKey);
+    }
+
+    std::string WebsocketsEndpoint::getHeader(uint64_t len, uint8_t opcode, bool fin, bool mask) {
+      std::string header_data;
+      
+        if(len < 126) {
+            auto header = MakeHeader<Header>(len, opcode, fin, mask);
+            header_data = std::string(reinterpret_cast<char*>(&header), 2 + 0);
+        } else if(len < 65536) {
+            auto header = MakeHeader<HeaderWithExtended16>(len, opcode, fin, mask);
+            header.extendedPayload = (len << 8) | (len >> 8);
+            header_data = std::string(reinterpret_cast<char*>(&header), 2 + 2);
+        } else {
+            auto header = MakeHeader<HeaderWithExtended64>(len, opcode, fin, mask);
+            // header.extendedPayload = swapEndianess(len);
+            header.extendedPayload = swapEndianess(len);
+
+            header_data = std::string(reinterpret_cast<char*>(&header), 2);
+            header_data += std::string(reinterpret_cast<char*>(&header.extendedPayload), 8);
+        }
+
+        return header_data;
+    }
+
+    void remaskData(WSString& data, const char* const maskingKey, size_t first, size_t len) {
+      for (size_t i = first; i < first + len; i++) {
+        data[i] = data[i] ^ maskingKey[i % 4];
+      }
+    }
+
+    bool WebsocketsEndpoint::send(const char* data, const size_t len, const uint8_t opcode, const bool fin, const bool mask, const char* maskingKey) {
+
+#ifdef _WS_CONFIG_MAX_MESSAGE_SIZE
+        if(len > _WS_CONFIG_MAX_MESSAGE_SIZE) {
+            return false;
+        }
+#endif
+        // send the header
+        std::string message_data = getHeader(len, opcode, fin, mask);
+
+        if (mask) {
+          message_data += std::string(maskingKey, 4);
+        }
+
+        size_t data_start = message_data.size();
+        message_data += std::string(data, len);
+
+        if (mask && memcmp(maskingKey, __TINY_WS_INTERNAL_DEFAULT_MASK, 4) != 0) {
+          remaskData(message_data, maskingKey, data_start, len);
+        }
+
+        this->_client->send(reinterpret_cast<const uint8_t*>(message_data.c_str()), message_data.size());
+        return true; // TODO dont assume success
+    }
+
+    void WebsocketsEndpoint::close(CloseReason reason) {
+        this->_closeReason = reason;
+        
+        if(!this->_client->available()) return;
+
+        if(reason == CloseReason_None) {
+            send(nullptr, 0, internals::ContentType::Close, true, this->_useMasking);
+        } else {
+            uint16_t reasonNum = static_cast<uint16_t>(reason);
+            reasonNum = (reasonNum >> 8) | (reasonNum << 8);
+            send(reinterpret_cast<const char*>(&reasonNum), 2, internals::ContentType::Close, true, this->_useMasking);
+        }
+        this->_client->close();
+    }
+
+    CloseReason WebsocketsEndpoint::getCloseReason() const {
+        return _closeReason;
+    }
+
+    bool WebsocketsEndpoint::ping(const WSString& msg) {
+        // Ping data must be shorter than 125 bytes
+        if(msg.size() > 125) {
+            return false;
+        }
+        else {
+            return send(msg, ContentType::Ping, true, this->_useMasking);
+        }
+    }
+    bool WebsocketsEndpoint::ping(const WSString&& msg) {
+        // Ping data must be shorter than 125 bytes
+        if(msg.size() > 125) {
+            return false;
+        }
+        else {
+            return send(msg, ContentType::Ping, true, this->_useMasking);
+        }
+    }
+
+    bool WebsocketsEndpoint::pong(const WSString& msg) {
+        // Pong data must be shorter than 125 bytes
+        if(msg.size() > 125)  {
+            return false;
+        }
+        else {
+            return this->send(msg, ContentType::Pong, true, this->_useMasking);
+        }
+    }
+    bool WebsocketsEndpoint::pong(const WSString&& msg) {
+        // Pong data must be shorter than 125 bytes
+        if(msg.size() > 125)  {
+            return false;
+        }
+        else {
+            return this->send(msg, ContentType::Pong, true, this->_useMasking);
+        }
+    }
+
+    void WebsocketsEndpoint::setFragmentsPolicy(FragmentsPolicy newPolicy) {
+        this->_fragmentsPolicy = newPolicy;
+    }
+
+    FragmentsPolicy WebsocketsEndpoint::getFragmentsPolicy() const {
+        return this->_fragmentsPolicy;
+    }
+
+    WebsocketsEndpoint::~WebsocketsEndpoint() {}
+}} // websockets::internals

+ 93 - 0
Esp32-Client/ArduinoWebsockets/src/websockets_server.cpp

@@ -0,0 +1,93 @@
+#include <tiny_websockets/server.hpp>
+#include <tiny_websockets/internals/wscrypto/crypto.hpp>
+#include <memory>
+#include <map>
+
+namespace websockets {
+    WebsocketsServer::WebsocketsServer(network::TcpServer* server) : _server(server) {}
+
+    bool WebsocketsServer::available() {
+        return this->_server->available();
+    }
+
+    void WebsocketsServer::listen(uint16_t port) {
+        this->_server->listen(port);
+    }
+
+    bool WebsocketsServer::poll() {
+        return this->_server->poll();
+    }
+
+    struct ParsedHandshakeParams {
+        WSString head;
+        std::map<WSString, WSString> headers;
+    };
+
+    ParsedHandshakeParams recvHandshakeRequest(network::TcpClient& client) {
+        ParsedHandshakeParams result;
+
+        result.head = client.readLine();
+
+        WSString line = client.readLine();
+        do {
+            WSString key = "", value = "";
+            size_t idx = 0;
+
+            // read key
+            while(idx < line.size() && line[idx] != ':') {
+                key += line[idx];
+                idx++;
+            }
+
+            // skip key and whitespace
+            idx++;
+            while(idx < line.size() && (line[idx] == ' ' || line[idx] == '\t')) idx++;
+
+            // read value (until \r\n)
+            while(idx < line.size() && line[idx] != '\r') {
+                value += line[idx];
+                idx++;
+            }
+
+            // store header
+            result.headers[key] = value;
+
+            line = client.readLine();
+        } while(client.available() && line != "\r\n");
+
+        return result;
+    }
+
+    WebsocketsClient WebsocketsServer::accept() {
+        std::shared_ptr<network::TcpClient> tcpClient(_server->accept());
+        if(tcpClient->available() == false) return {};
+        
+        auto params = recvHandshakeRequest(*tcpClient);
+        
+        if(params.headers["Connection"].find("Upgrade") == std::string::npos) return {};
+        if(params.headers["Upgrade"] != "websocket") return {}; 
+        if(params.headers["Sec-WebSocket-Version"] != "13") return {}; 
+        if(params.headers["Sec-WebSocket-Key"] == "") return {};
+        
+        auto serverAccept = crypto::websocketsHandshakeEncodeKey(
+            params.headers["Sec-WebSocket-Key"]
+        );
+
+        tcpClient->send("HTTP/1.1 101 Switching Protocols\r\n");
+        tcpClient->send("Connection: Upgrade\r\n");
+        tcpClient->send("Upgrade: websocket\r\n");
+        tcpClient->send("Sec-WebSocket-Version: 13\r\n");
+        tcpClient->send("Sec-WebSocket-Accept: " + serverAccept + "\r\n");
+        tcpClient->send("\r\n");
+        
+        WebsocketsClient wsClient(tcpClient);
+        // Don't use masking from server to client (according to RFC)
+        wsClient.setUseMasking(false);
+        return wsClient;
+    }
+
+    WebsocketsServer::~WebsocketsServer() {
+        this->_server->close();
+    }
+
+} //websockets

+ 17 - 0
Esp32-Client/ArduinoWebsockets/src/ws_common.cpp

@@ -0,0 +1,17 @@
+#include <tiny_websockets/internals/ws_common.hpp>
+
+namespace websockets { namespace internals {
+    WSString fromInterfaceString(const WSInterfaceString& str) {
+        return str.c_str();
+    }
+    WSString fromInterfaceString(const WSInterfaceString&& str) {
+        return str.c_str();
+    }
+
+    WSInterfaceString fromInternalString(const WSString& str) {
+        return str.c_str();
+    }
+    WSInterfaceString fromInternalString(const WSString&& str) {
+        return str.c_str();
+    }
+}}

+ 155 - 0
Esp32-Client/Esp32-Client.ino

@@ -0,0 +1,155 @@
+//库
+#include <ESP8266WiFi.h>
+#include <ArduinoWebsockets.h>
+#include <PS2X_lib.h>
+
+//基础配置wifi+socket+PS2
+const char* ssid = "iPhone";
+const char* password = "12345678";
+const char* websockets_server_host = "caner.top";
+const uint16_t websockets_server_port = 49700;
+int PlyStnRStickUpDn = 0;  //读取PS2右侧摇杆向上/向下的值.
+int PlyStnRStickLtRt = 0;  //读取PS2右侧摇杆向左/向右的值.
+int PlyStnLStickUpDn = 0; //读取PS2左侧摇杆向上/向下的值.
+int PlyStnLStickLtRt = 0; //读取PS2左侧摇杆向左/向右的值.
+int error = 4; //控制器配置错误
+byte type = 0; //控制器类型
+//实例化
+using namespace websockets;
+WebsocketsClient client;
+PS2X ps2x;
+
+//定义宏
+#define PS2_DAT        1  //14    
+#define PS2_CMD        3  //15
+#define PS2_SEL        12  //16
+#define PS2_CLK        15  //17
+
+//重置
+void(*resetFunc) (void) = 0;
+
+//WIFI连接
+void WifiConnect() {
+  WiFi.begin(ssid, password);
+  while (WiFi.status() != WL_CONNECTED) {
+    delay(500);
+    Serial.print(".");
+  }
+  Serial.println("");
+  Serial.print("WiFi connected IP:");
+  Serial.println(WiFi.localIP());
+}
+
+//socket连接
+void SocketConnect() {
+  //  连接socekt
+  bool connected = client.connect(websockets_server_host, websockets_server_port, "/");
+  if (connected) {
+    Serial.println("Server Connected!");
+  } else {
+    Serial.println("Server Not Connected!");
+  }
+  //  监听socekt发送过来的信息
+  client.onMessage([](WebsocketsMessage msg) {
+    Serial.println("Got Message: " + msg.data());
+  });
+}
+
+//控制器初始化
+void ControllerInt() {
+  delay(1000);
+  type = ps2x.readType();
+  switch (type) {
+    case 0:
+      Serial.println("发现未知控制器类型");
+      break;
+    case 1:
+      Serial.println("dualshock3手柄控制器发现");
+      break;
+    case 2:
+      Serial.println("GuitarHero控制器发现");
+      break;
+    case 3:
+      Serial.println("发现索尼无线DualShock控制器");
+      break;
+  }
+  //setup pins and settings:  GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error
+  //  error = ps2x.config_gamepad(PS2_CLK, PS2_CMD, PS2_SEL, PS2_DAT, false, false);
+  if (error == 0) {
+    Serial.println("找到控制器,配置成功");
+  } else if (error == 1) {
+    Serial.println("没有找到控制器,检查接线,请参阅readme.txt来启用调试");
+  } else if (error == 2) {
+    Serial.println("找到了控制器,但不接受命令,请参阅readme.txt来启用调试");
+  } else if (error == 3) {
+    Serial.println("控制器拒绝进入压力模式,可能不支持");
+  } else {
+    Serial.println("未知错误");
+  }
+}
+
+//监测PS2按钮并发送信息
+void Ps2OnButton() {
+  //  拿到值
+  PlyStnLStickUpDn = ps2x.Analog(PSS_LY); //左侧摇杆上/下
+  PlyStnLStickLtRt = ps2x.Analog(PSS_LX); //左侧摇杆左/右
+  PlyStnRStickUpDn = ps2x.Analog(PSS_RY); //右侧摇杆上/下
+  PlyStnRStickLtRt = ps2x.Analog(PSS_RX); //右侧摇杆左/右
+  //  读值
+  ps2x.read_gamepad();
+  //  发送值
+  if (ps2x.ButtonPressed(YELLOW_FRET)) {
+    client.send("1");
+  } else if (ps2x.ButtonPressed(RED_FRET)) {
+    client.send("2");
+  } else if (ps2x.ButtonPressed(BLUE_FRET)) {
+    client.send("3");
+  } else if (ps2x.ButtonPressed(ORANGE_FRET)) {
+    client.send("3");
+  } else if (ps2x.Button(UP_STRUM)) {
+    client.send("UP");
+  } else if (ps2x.Button(DOWN_STRUM)) {
+    client.send("DOWN");
+  } else if (ps2x.Button(RIGHT_STRUM)) {
+    client.send("RIGHT");
+  } else if (ps2x.Button(LEFT_STRUM)) {
+    client.send("LEFT");
+  } else if (ps2x.Button(PSB_START)) {
+    client.send("START");
+  } else if (ps2x.Button(PSB_SELECT)) {
+    client.send("SELECT");
+  } else if (ps2x.Button(PSB_L2)) {
+    client.send("L2");
+  } else if (ps2x.Button(PSB_R2)) {
+    client.send("R2");
+  } else if (ps2x.Button(PSB_L1)) {
+    client.send("K-" + String(PlyStnLStickUpDn));
+  } else if (ps2x.Button(PSB_R1)) {
+    client.send("W-" + String(PlyStnLStickUpDn));
+  } else {
+    client.send("Other operating");
+  }
+}
+
+
+
+void setup() {
+  Serial.begin(115200);
+  //  连接wifi
+  WifiConnect();
+  //  等待3S
+  delay(3000);
+  //  连接socket
+  SocketConnect();
+  //  连接PS2
+  ControllerInt();  
+}
+
+void loop() {
+  // let the websockets client check for incoming messages
+  if (client.available()) {
+    client.poll();
+    //    Ps2OnButton();
+  }
+  delay(1000);
+}

+ 512 - 0
PS2X_lib/PS2X_lib.cpp

@@ -0,0 +1,512 @@
+#include "PS2X_lib.h"
+#include <math.h>
+#include <stdio.h>
+#include <stdint.h>
+#ifdef __AVR__
+#include <avr/io.h>
+#endif
+#if ARDUINO > 22
+  #include "Arduino.h"
+#else
+  #include "WProgram.h"
+  #include "pins_arduino.h"
+#endif
+
+static byte enter_config[]={0x01,0x43,0x00,0x01,0x00};
+static byte set_mode[]={0x01,0x44,0x00,0x01,0x03,0x00,0x00,0x00,0x00};
+static byte set_bytes_large[]={0x01,0x4F,0x00,0xFF,0xFF,0x03,0x00,0x00,0x00};
+static byte exit_config[]={0x01,0x43,0x00,0x00,0x5A,0x5A,0x5A,0x5A,0x5A};
+static byte enable_rumble[]={0x01,0x4D,0x00,0x00,0x01};
+static byte type_read[]={0x01,0x45,0x00,0x5A,0x5A,0x5A,0x5A,0x5A,0x5A};
+
+/****************************************************************************************/
+boolean PS2X::NewButtonState() {
+  return ((last_buttons ^ buttons) > 0);
+}
+
+/****************************************************************************************/
+boolean PS2X::NewButtonState(unsigned int button) {
+  return (((last_buttons ^ buttons) & button) > 0);
+}
+
+/****************************************************************************************/
+boolean PS2X::ButtonPressed(unsigned int button) {
+  return(NewButtonState(button) & Button(button));
+}
+
+/****************************************************************************************/
+boolean PS2X::ButtonReleased(unsigned int button) {
+  return((NewButtonState(button)) & ((~last_buttons & button) > 0));
+}
+
+/****************************************************************************************/
+boolean PS2X::Button(uint16_t button) {
+  return ((~buttons & button) > 0);
+}
+
+/****************************************************************************************/
+unsigned int PS2X::ButtonDataByte() {
+   return (~buttons);
+}
+
+/****************************************************************************************/
+byte PS2X::Analog(byte button) {
+   return PS2data[button];
+}
+
+/****************************************************************************************/
+unsigned char PS2X::_gamepad_shiftinout (char byte) {
+   unsigned char tmp = 0;
+   for(unsigned char i=0;i<8;i++) {
+      if(CHK(byte,i)) CMD_SET();
+      else CMD_CLR();
+	  
+      CLK_CLR();
+      delayMicroseconds(CTRL_CLK);
+
+      //if(DAT_CHK()) SET(tmp,i);
+      if(DAT_CHK()) bitSet(tmp,i);
+
+      CLK_SET();
+#if CTRL_CLK_HIGH
+      delayMicroseconds(CTRL_CLK_HIGH);
+#endif
+   }
+   CMD_SET();
+   delayMicroseconds(CTRL_BYTE_DELAY);
+   return tmp;
+}
+
+/****************************************************************************************/
+void PS2X::read_gamepad() {
+   read_gamepad(false, 0x00);
+}
+
+/****************************************************************************************/
+boolean PS2X::read_gamepad(boolean motor1, byte motor2) {
+   double temp = millis() - last_read;
+
+   if (temp > 1500) //waited to long
+      reconfig_gamepad();
+
+   if(temp < read_delay)  //waited too short
+      delay(read_delay - temp);
+
+   if(motor2 != 0x00)
+      motor2 = map(motor2,0,255,0x40,0xFF); //noting below 40 will make it spin
+
+   byte dword[9] = {0x01,0x42,0,motor1,motor2,0,0,0,0};
+   byte dword2[12] = {0,0,0,0,0,0,0,0,0,0,0,0};
+
+   // Try a few times to get valid data...
+   for (byte RetryCnt = 0; RetryCnt < 5; RetryCnt++) {
+      CMD_SET();
+      CLK_SET();
+      ATT_CLR(); // low enable joystick
+
+      delayMicroseconds(CTRL_BYTE_DELAY);
+      //Send the command to send button and joystick data;
+      for (int i = 0; i<9; i++) {
+         PS2data[i] = _gamepad_shiftinout(dword[i]);
+      }
+
+      if(PS2data[1] == 0x79) {  //if controller is in full data return mode, get the rest of data
+         for (int i = 0; i<12; i++) {
+            PS2data[i+9] = _gamepad_shiftinout(dword2[i]);
+         }
+      }
+
+      ATT_SET(); // HI disable joystick
+      // Check to see if we received valid data or not.  
+	  // We should be in analog mode for our data to be valid (analog == 0x7_)
+      if ((PS2data[1] & 0xf0) == 0x70)
+         break;
+
+      // If we got to here, we are not in analog mode, try to recover...
+      reconfig_gamepad(); // try to get back into Analog mode.
+      delay(read_delay);
+   }
+
+   // If we get here and still not in analog mode (=0x7_), try increasing the read_delay...
+   if ((PS2data[1] & 0xf0) != 0x70) {
+      if (read_delay < 10)
+         read_delay++;   // see if this helps out...
+   }
+
+#ifdef PS2X_COM_DEBUG
+   Serial.print("OUT:IN ");
+   for(int i=0; i<9; i++){
+      Serial.print(dword[i], HEX);
+      Serial.print(":");
+      Serial.print(PS2data[i], HEX);
+      Serial.print(" ");
+   }
+   for (int i = 0; i<12; i++) {
+      Serial.print(dword2[i], HEX);
+      Serial.print(":");
+      Serial.print(PS2data[i+9], HEX);
+      Serial.print(" ");
+   }
+   Serial.println("");
+#endif
+
+   last_buttons = buttons; //store the previous buttons states
+
+#if defined(__AVR__)
+   buttons = *(uint16_t*)(PS2data+3);   //store as one value for multiple functions
+#else
+   buttons =  (uint16_t)(PS2data[4] << 8) + PS2data[3];   //store as one value for multiple functions
+#endif
+   last_read = millis();
+   return ((PS2data[1] & 0xf0) == 0x70);  // 1 = OK = analog mode - 0 = NOK
+}
+
+/****************************************************************************************/
+byte PS2X::config_gamepad(uint8_t clk, uint8_t cmd, uint8_t att, uint8_t dat) {
+   return config_gamepad(clk, cmd, att, dat, false, false);
+}
+
+/****************************************************************************************/
+byte PS2X::config_gamepad(uint8_t clk, uint8_t cmd, uint8_t att, uint8_t dat, bool pressures, bool rumble) {
+
+  byte temp[sizeof(type_read)];
+
+#ifdef __AVR__
+  _clk_mask = digitalPinToBitMask(clk);
+  _clk_oreg = portOutputRegister(digitalPinToPort(clk));
+  _cmd_mask = digitalPinToBitMask(cmd);
+  _cmd_oreg = portOutputRegister(digitalPinToPort(cmd));
+  _att_mask = digitalPinToBitMask(att);
+  _att_oreg = portOutputRegister(digitalPinToPort(att));
+  _dat_mask = digitalPinToBitMask(dat);
+  _dat_ireg = portInputRegister(digitalPinToPort(dat));
+#else
+#ifdef ESP8266
+  _clk_pin = clk;
+  _cmd_pin = cmd;
+  _att_pin = att;
+  _dat_pin = dat;
+#else
+  uint32_t            lport;                   // Port number for this pin
+  _clk_mask = digitalPinToBitMask(clk);
+  lport = digitalPinToPort(clk);
+  _clk_lport_set = portOutputRegister(lport) + 2;
+  _clk_lport_clr = portOutputRegister(lport) + 1;
+
+  _cmd_mask = digitalPinToBitMask(cmd);
+  lport = digitalPinToPort(cmd);
+  _cmd_lport_set = portOutputRegister(lport) + 2;
+  _cmd_lport_clr = portOutputRegister(lport) + 1;
+
+  _att_mask = digitalPinToBitMask(att);
+  lport = digitalPinToPort(att);
+  _att_lport_set = portOutputRegister(lport) + 2;
+  _att_lport_clr = portOutputRegister(lport) + 1;
+
+  _dat_mask = digitalPinToBitMask(dat);
+  _dat_lport = portInputRegister(digitalPinToPort(dat));
+#endif
+#endif
+
+  pinMode(clk, OUTPUT); //configure ports
+  pinMode(att, OUTPUT);
+  pinMode(cmd, OUTPUT);
+#ifdef ESP8266
+  pinMode(dat, INPUT_PULLUP); // enable pull-up
+#else
+  pinMode(dat, INPUT);
+#endif
+
+#if defined(__AVR__)
+  digitalWrite(dat, HIGH); //enable pull-up
+#endif
+
+  CMD_SET(); // SET(*_cmd_oreg,_cmd_mask);
+  CLK_SET();
+
+  //new error checking. First, read gamepad a few times to see if it's talking
+  read_gamepad();
+  read_gamepad();
+
+  //see if it talked - see if mode came back. 
+  //If still anything but 41, 73 or 79, then it's not talking
+  if(PS2data[1] != 0x41 && PS2data[1] != 0x42 && PS2data[1] != 0x73 && PS2data[1] != 0x79){ 
+#ifdef PS2X_DEBUG
+    Serial.println("Controller mode not matched or no controller found");
+    Serial.print("Expected 0x41, 0x42, 0x73 or 0x79, but got ");
+    Serial.println(PS2data[1], HEX);
+#endif
+    return 1; //return error code 1
+  }
+
+  //try setting mode, increasing delays if need be.
+  read_delay = 1;
+
+  for(int y = 0; y <= 10; y++) {
+    sendCommandString(enter_config, sizeof(enter_config)); //start config run
+
+    //read type
+    delayMicroseconds(CTRL_BYTE_DELAY);
+
+    CMD_SET();
+    CLK_SET();
+    ATT_CLR(); // low enable joystick
+
+    delayMicroseconds(CTRL_BYTE_DELAY);
+
+    for (int i = 0; i<9; i++) {
+      temp[i] = _gamepad_shiftinout(type_read[i]);
+    }
+
+    ATT_SET(); // HI disable joystick
+
+    controller_type = temp[3];
+
+    sendCommandString(set_mode, sizeof(set_mode));
+    if(rumble){ sendCommandString(enable_rumble, sizeof(enable_rumble)); en_Rumble = true; }
+    if(pressures){ sendCommandString(set_bytes_large, sizeof(set_bytes_large)); en_Pressures = true; }
+    sendCommandString(exit_config, sizeof(exit_config));
+
+    read_gamepad();
+
+    if(pressures){
+      if(PS2data[1] == 0x79)
+        break;
+      if(PS2data[1] == 0x73)
+        return 3;
+    }
+
+    if(PS2data[1] == 0x73)
+      break;
+
+    if(y == 10){
+#ifdef PS2X_DEBUG
+      Serial.println("Controller not accepting commands");
+      Serial.print("mode still set at");
+      Serial.println(PS2data[1], HEX);
+#endif
+      return 2; //exit function with error
+    }
+    read_delay += 1; //add 1ms to read_delay
+  }
+  return 0; //no error if here
+}
+
+/****************************************************************************************/
+void PS2X::sendCommandString(byte string[], byte len) {
+#ifdef PS2X_COM_DEBUG
+  byte temp[len];
+  ATT_CLR(); // low enable joystick
+  delayMicroseconds(CTRL_BYTE_DELAY);
+
+  for (int y=0; y < len; y++)
+    temp[y] = _gamepad_shiftinout(string[y]);
+
+  ATT_SET(); //high disable joystick
+  delay(read_delay); //wait a few
+
+  Serial.println("OUT:IN Configure");
+  for(int i=0; i<len; i++) {
+    Serial.print(string[i], HEX);
+    Serial.print(":");
+    Serial.print(temp[i], HEX);
+    Serial.print(" ");
+  }
+  Serial.println("");
+#else
+  ATT_CLR(); // low enable joystick
+  delayMicroseconds(CTRL_BYTE_DELAY);
+  for (int y=0; y < len; y++)
+    _gamepad_shiftinout(string[y]);
+  ATT_SET(); //high disable joystick
+  delay(read_delay);                  //wait a few
+#endif
+}
+
+/****************************************************************************************/
+byte PS2X::readType() {
+/*
+  byte temp[sizeof(type_read)];
+
+  sendCommandString(enter_config, sizeof(enter_config));
+
+  delayMicroseconds(CTRL_BYTE_DELAY);
+
+  CMD_SET();
+  CLK_SET();
+  ATT_CLR(); // low enable joystick
+
+  delayMicroseconds(CTRL_BYTE_DELAY);
+
+  for (int i = 0; i<9; i++) {
+    temp[i] = _gamepad_shiftinout(type_read[i]);
+  }
+
+  sendCommandString(exit_config, sizeof(exit_config));
+
+  if(temp[3] == 0x03)
+    return 1;
+  else if(temp[3] == 0x01)
+    return 2;
+
+  return 0;
+*/
+  Serial.print("Controller_type: ");
+  Serial.println(controller_type, HEX);
+  if(controller_type == 0x03)
+    return 1;
+  else if(controller_type == 0x01 && PS2data[1] == 0x42)
+	return 4;
+  else if(controller_type == 0x01 && PS2data[1] != 0x42)
+    return 2;
+  else if(controller_type == 0x0C)  
+    return 3;  //2.4G Wireless Dual Shock PS2 Game Controller
+	
+  return 0;
+}
+
+/****************************************************************************************/
+void PS2X::enableRumble() {
+  sendCommandString(enter_config, sizeof(enter_config));
+  sendCommandString(enable_rumble, sizeof(enable_rumble));
+  sendCommandString(exit_config, sizeof(exit_config));
+  en_Rumble = true;
+}
+
+/****************************************************************************************/
+bool PS2X::enablePressures() {
+  sendCommandString(enter_config, sizeof(enter_config));
+  sendCommandString(set_bytes_large, sizeof(set_bytes_large));
+  sendCommandString(exit_config, sizeof(exit_config));
+
+  read_gamepad();
+  read_gamepad();
+
+  if(PS2data[1] != 0x79)
+    return false;
+
+  en_Pressures = true;
+    return true;
+}
+
+/****************************************************************************************/
+void PS2X::reconfig_gamepad(){
+  sendCommandString(enter_config, sizeof(enter_config));
+  sendCommandString(set_mode, sizeof(set_mode));
+  if (en_Rumble)
+    sendCommandString(enable_rumble, sizeof(enable_rumble));
+  if (en_Pressures)
+    sendCommandString(set_bytes_large, sizeof(set_bytes_large));
+  sendCommandString(exit_config, sizeof(exit_config));
+}
+
+/****************************************************************************************/
+#ifdef __AVR__
+inline void  PS2X::CLK_SET(void) {
+  register uint8_t old_sreg = SREG;
+  cli();
+  *_clk_oreg |= _clk_mask;
+  SREG = old_sreg;
+}
+
+inline void  PS2X::CLK_CLR(void) {
+  register uint8_t old_sreg = SREG;
+  cli();
+  *_clk_oreg &= ~_clk_mask;
+  SREG = old_sreg;
+}
+
+inline void  PS2X::CMD_SET(void) {
+  register uint8_t old_sreg = SREG;
+  cli();
+  *_cmd_oreg |= _cmd_mask; // SET(*_cmd_oreg,_cmd_mask);
+  SREG = old_sreg;
+}
+
+inline void  PS2X::CMD_CLR(void) {
+  register uint8_t old_sreg = SREG;
+  cli();
+  *_cmd_oreg &= ~_cmd_mask; // SET(*_cmd_oreg,_cmd_mask);
+  SREG = old_sreg;
+}
+
+inline void  PS2X::ATT_SET(void) {
+  register uint8_t old_sreg = SREG;
+  cli();
+  *_att_oreg |= _att_mask ;
+  SREG = old_sreg;
+}
+
+inline void PS2X::ATT_CLR(void) {
+  register uint8_t old_sreg = SREG;
+  cli();
+  *_att_oreg &= ~_att_mask;
+  SREG = old_sreg;
+}
+
+inline bool PS2X::DAT_CHK(void) {
+  return (*_dat_ireg & _dat_mask) ? true : false;
+}
+
+#else
+#ifdef ESP8266
+// Let's just use digitalWrite() on ESP8266.
+inline void  PS2X::CLK_SET(void) {
+  digitalWrite(_clk_pin, HIGH);
+}
+
+inline void  PS2X::CLK_CLR(void) {
+  digitalWrite(_clk_pin, LOW);
+}
+
+inline void  PS2X::CMD_SET(void) {
+  digitalWrite(_cmd_pin, HIGH);
+}
+
+inline void  PS2X::CMD_CLR(void) {
+  digitalWrite(_cmd_pin, LOW);
+}
+
+inline void  PS2X::ATT_SET(void) {
+  digitalWrite(_att_pin, HIGH);
+}
+
+inline void PS2X::ATT_CLR(void) {
+  digitalWrite(_att_pin, LOW);
+}
+
+inline bool PS2X::DAT_CHK(void) {
+  return digitalRead(_dat_pin) ? true : false;
+}
+#else
+// On pic32, use the set/clr registers to make them atomic...
+inline void  PS2X::CLK_SET(void) {
+  *_clk_lport_set |= _clk_mask;
+}
+
+inline void  PS2X::CLK_CLR(void) {
+  *_clk_lport_clr |= _clk_mask;
+}
+
+inline void  PS2X::CMD_SET(void) {
+  *_cmd_lport_set |= _cmd_mask;
+}
+
+inline void  PS2X::CMD_CLR(void) {
+  *_cmd_lport_clr |= _cmd_mask;
+}
+
+inline void  PS2X::ATT_SET(void) {
+  *_att_lport_set |= _att_mask;
+}
+
+inline void PS2X::ATT_CLR(void) {
+  *_att_lport_clr |= _att_mask;
+}
+
+inline bool PS2X::DAT_CHK(void) {
+  return (*_dat_lport & _dat_mask) ? true : false;
+}
+
+#endif
+#endif

+ 251 - 0
PS2X_lib/PS2X_lib.h

@@ -0,0 +1,251 @@
+/******************************************************************
+*  Super amazing PS2 controller Arduino Library v1.8
+*		details and example sketch: 
+*			http://www.billporter.info/?p=240
+*
+*    Original code by Shutter on Arduino Forums
+*
+*    Revamped, made into lib by and supporting continued development:
+*              Bill Porter
+*              www.billporter.info
+*
+*	 Contributers:
+*		Eric Wetzel (thewetzel@gmail.com)
+*		Kurt Eckhardt
+*
+*  Lib version history
+*    0.1 made into library, added analog stick support. 
+*    0.2 fixed config_gamepad miss-spelling
+*        added new functions:
+*          NewButtonState();
+*          NewButtonState(unsigned int);
+*          ButtonPressed(unsigned int);
+*          ButtonReleased(unsigned int);
+*        removed 'PS' from beginning of ever function
+*    1.0 found and fixed bug that wasn't configuring controller
+*        added ability to define pins
+*        added time checking to reconfigure controller if not polled enough
+*        Analog sticks and pressures all through 'ps2x.Analog()' function
+*        added:
+*          enableRumble();
+*          enablePressures();
+*    1.1  
+*        added some debug stuff for end user. Reports if no controller found
+*        added auto-increasing sentence delay to see if it helps compatibility.
+*    1.2
+*        found bad math by Shutter for original clock. Was running at 50kHz, not the required 500kHz. 
+*        fixed some of the debug reporting. 
+*	1.3 
+*	    Changed clock back to 50kHz. CuriousInventor says it's suppose to be 500kHz, but doesn't seem to work for everybody. 
+*	1.4
+*		Removed redundant functions.
+*		Fixed mode check to include two other possible modes the controller could be in.
+*       Added debug code enabled by compiler directives. See below to enable debug mode.
+*		Added button definitions for shapes as well as colors.
+*	1.41
+*		Some simple bug fixes
+*		Added Keywords.txt file
+*	1.5
+*		Added proper Guitar Hero compatibility
+*		Fixed issue with DEBUG mode, had to send serial at once instead of in bits
+*	1.6
+*		Changed config_gamepad() call to include rumble and pressures options
+*			This was to fix controllers that will only go into config mode once
+*			Old methods should still work for backwards compatibility 
+*    1.7
+*		Integrated Kurt's fixes for the interrupts messing with servo signals
+*		Reorganized directory so examples show up in Arduino IDE menu
+*    1.8
+*		Added Arduino 1.0 compatibility. 
+*    1.9
+*       Kurt - Added detection and recovery from dropping from analog mode, plus
+*       integrated Chipkit (pic32mx...) support
+*
+*
+*
+*This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or(at your option) any later version.
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+<http://www.gnu.org/licenses/>
+*  
+******************************************************************/
+
+// $$$$$$$$$$$$ DEBUG ENABLE SECTION $$$$$$$$$$$$$$$$
+// to debug ps2 controller, uncomment these two lines to print out debug to uart
+//#define PS2X_DEBUG
+//#define PS2X_COM_DEBUG
+
+#ifndef PS2X_lib_h
+  #define PS2X_lib_h
+
+#if ARDUINO > 22
+  #include "Arduino.h"
+#else
+  #include "WProgram.h"
+#endif
+
+#include <math.h>
+#include <stdio.h>
+#include <stdint.h>
+#ifdef __AVR__
+  // AVR
+  #include <avr/io.h>
+  #define CTRL_CLK        4
+  #define CTRL_BYTE_DELAY 3
+#else
+#ifdef ESP8266
+  #define CTRL_CLK        5
+  #define CTRL_CLK_HIGH   5
+  #define CTRL_BYTE_DELAY 18
+#else
+  // Pic32...
+  #include <pins_arduino.h>
+  #define CTRL_CLK        5
+  #define CTRL_CLK_HIGH   5
+  #define CTRL_BYTE_DELAY 4
+#endif 
+#endif
+
+//These are our button constants
+#define PSB_SELECT      0x0001
+#define PSB_L3          0x0002
+#define PSB_R3          0x0004
+#define PSB_START       0x0008
+#define PSB_PAD_UP      0x0010
+#define PSB_PAD_RIGHT   0x0020
+#define PSB_PAD_DOWN    0x0040
+#define PSB_PAD_LEFT    0x0080
+#define PSB_L2          0x0100
+#define PSB_R2          0x0200
+#define PSB_L1          0x0400
+#define PSB_R1          0x0800
+#define PSB_GREEN       0x1000
+#define PSB_RED         0x2000
+#define PSB_BLUE        0x4000
+#define PSB_PINK        0x8000
+#define PSB_TRIANGLE    0x1000
+#define PSB_CIRCLE      0x2000
+#define PSB_CROSS       0x4000
+#define PSB_SQUARE      0x8000
+
+//Guitar  button constants
+#define UP_STRUM		0x0010
+#define DOWN_STRUM		0x0040
+#define LEFT_STRUM		0x0080
+#define RIGHT_STRUM		0x0020
+#define STAR_POWER		0x0100
+#define GREEN_FRET		0x0200
+#define YELLOW_FRET		0x1000
+#define RED_FRET		0x2000
+#define BLUE_FRET		0x4000
+#define ORANGE_FRET		0x8000
+#define WHAMMY_BAR		8
+
+//These are stick values
+#define PSS_RX 5
+#define PSS_RY 6
+#define PSS_LX 7
+#define PSS_LY 8
+
+//These are analog buttons
+#define PSAB_PAD_RIGHT    9
+#define PSAB_PAD_UP      11
+#define PSAB_PAD_DOWN    12
+#define PSAB_PAD_LEFT    10
+#define PSAB_L2          19
+#define PSAB_R2          20
+#define PSAB_L1          17
+#define PSAB_R1          18
+#define PSAB_GREEN       13
+#define PSAB_RED         14
+#define PSAB_BLUE        15
+#define PSAB_PINK        16
+#define PSAB_TRIANGLE    13
+#define PSAB_CIRCLE      14
+#define PSAB_CROSS       15
+#define PSAB_SQUARE      16
+
+#define SET(x,y) (x|=(1<<y))
+#define CLR(x,y) (x&=(~(1<<y)))
+#define CHK(x,y) (x & (1<<y))
+#define TOG(x,y) (x^=(1<<y))
+
+class PS2X {
+  public:
+    boolean Button(uint16_t);                //will be TRUE if button is being pressed
+    unsigned int ButtonDataByte();
+    boolean NewButtonState();
+    boolean NewButtonState(unsigned int);    //will be TRUE if button was JUST pressed OR released
+    boolean ButtonPressed(unsigned int);     //will be TRUE if button was JUST pressed
+    boolean ButtonReleased(unsigned int);    //will be TRUE if button was JUST released
+    void read_gamepad();
+    boolean  read_gamepad(boolean, byte);
+    byte readType();
+    byte config_gamepad(uint8_t, uint8_t, uint8_t, uint8_t);
+    byte config_gamepad(uint8_t, uint8_t, uint8_t, uint8_t, bool, bool);
+    void enableRumble();
+    bool enablePressures();
+    byte Analog(byte);
+    void reconfig_gamepad();
+
+  private:
+    inline void CLK_SET(void);
+    inline void CLK_CLR(void);
+    inline void CMD_SET(void);
+    inline void CMD_CLR(void);
+    inline void ATT_SET(void);
+    inline void ATT_CLR(void);
+    inline bool DAT_CHK(void);
+    
+    unsigned char _gamepad_shiftinout (char);
+    unsigned char PS2data[21];
+    void sendCommandString(byte*, byte);
+    unsigned char i;
+    unsigned int last_buttons;
+    unsigned int buttons;
+	
+    #ifdef __AVR__
+      uint8_t maskToBitNum(uint8_t);
+      uint8_t _clk_mask; 
+      volatile uint8_t *_clk_oreg;
+      uint8_t _cmd_mask; 
+      volatile uint8_t *_cmd_oreg;
+      uint8_t _att_mask; 
+      volatile uint8_t *_att_oreg;
+      uint8_t _dat_mask; 
+      volatile uint8_t *_dat_ireg;
+    #else
+    #ifdef ESP8266
+      int _clk_pin;
+      int _cmd_pin;
+      int _att_pin;
+      int _dat_pin;
+    #else
+      uint8_t maskToBitNum(uint8_t);
+      uint16_t _clk_mask; 
+      volatile uint32_t *_clk_lport_set;
+      volatile uint32_t *_clk_lport_clr;
+      uint16_t _cmd_mask; 
+      volatile uint32_t *_cmd_lport_set;
+      volatile uint32_t *_cmd_lport_clr;
+      uint16_t _att_mask; 
+      volatile uint32_t *_att_lport_set;
+      volatile uint32_t *_att_lport_clr;
+      uint16_t _dat_mask; 
+      volatile uint32_t *_dat_lport;
+    #endif
+    #endif
+	
+    unsigned long last_read;
+    byte read_delay;
+    byte controller_type;
+    boolean en_Rumble;
+    boolean en_Pressures;
+};
+
+#endif
+
+
+

+ 150 - 0
PS2X_lib/examples/PS2XMouse/PS2XMouse.ino

@@ -0,0 +1,150 @@
+#include <PS2X_lib.h>
+
+PS2X ps2x; // create PS2 Controller Class
+
+int error = 0; 
+byte type = 0;
+const int ledPin = 11;         // Mouse control LED (11 on Teensy 2.0, 13 on Arduino Leonardo)
+
+// parameters for reading the joystick:
+int range = 12;               // output range of X or Y movement
+int responseDelay = 5;        // response delay of the mouse, in ms
+int threshold = range/4;      // resting threshold
+int center = range/2;         // resting position value
+
+boolean mouseIsActive = false;    // whether or not to control the mouse
+int lastSwitchState = LOW;        // previous switch state
+
+void setup(){
+ Serial.begin(57600);
+  
+ error = ps2x.config_gamepad(15,14,13,12, true, true);   //setup pins and settings:  GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error
+ 
+ if(error == 0){
+   Serial.println("Found Controller, configured successful");
+ }
+   
+  else if(error == 1)
+   Serial.println("No controller found, check wiring, see readme.txt to enable debug. visit www.billporter.info for troubleshooting tips");
+   
+  else if(error == 2)
+   Serial.println("Controller found but not accepting commands. see readme.txt to enable debug. Visit www.billporter.info for troubleshooting tips");
+   
+  else if(error == 3)
+   Serial.println("Controller refusing to enter Pressures mode, may not support it. ");
+   
+   type = ps2x.readType(); 
+     switch(type) {
+       case 0:
+        Serial.println("Unknown Controller type");
+       break;
+       case 1:
+        Serial.println("DualShock Controller Found");
+       break;
+       case 2:
+         Serial.println("GuitarHero Controller Found");
+       break;
+     }
+  
+ // take control of the mouse:
+  Mouse.begin();
+  Keyboard.begin();
+}
+
+void loop()
+{
+   
+ if(error == 1) //skip loop if no controller found
+  return; 
+  
+  ps2x.read_gamepad(false, 0);          //read controller and set large motor to spin at 'vibrate' speed
+    
+  // read the switch:
+  int switchState = ps2x.ButtonPressed(PSB_RED);
+  // if it's changed and it's high, toggle the mouse state:
+  if (switchState != lastSwitchState) {
+    if (switchState == HIGH) {
+      mouseIsActive = !mouseIsActive;
+      // turn on LED to indicate mouse state:
+      digitalWrite(ledPin, mouseIsActive);
+    } 
+  }
+  // save switch state for next comparison:
+  lastSwitchState = switchState;
+
+  // read and scale the two axes:
+  int xReading = readAxis(PSS_LX);
+  int yReading = readAxis(PSS_LY);
+
+  // if the mouse control state is active, move the mouse:
+  if (mouseIsActive) {
+    Mouse.move(xReading, yReading, 0);
+  }  
+
+  // read the mouse button and click or not click:
+  // if the mouse button is pressed:
+  if (ps2x.ButtonPressed(PSB_BLUE)) {
+    // if the mouse is not pressed, press it:
+    if (!Mouse.isPressed(MOUSE_LEFT)) {
+      Mouse.press(MOUSE_LEFT); 
+    }
+  } 
+  // else the mouse button is not pressed:
+  else {
+    // if the mouse is pressed, release it:
+    if (Mouse.isPressed(MOUSE_LEFT)) {
+      Mouse.release(MOUSE_LEFT); 
+    }
+  }
+  
+  if (ps2x.Button(PSB_PAD_UP)) {
+    Keyboard.press(KEY_UP_ARROW); 
+  } else {
+    Keyboard.release(KEY_UP_ARROW); 
+  }
+  
+  if (ps2x.Button(PSB_PAD_DOWN)) {
+    Keyboard.press(KEY_DOWN_ARROW); 
+  } else {
+    Keyboard.release(KEY_DOWN_ARROW); 
+  }
+  
+  if (ps2x.Button(PSB_PAD_RIGHT)) {
+    Keyboard.press(KEY_RIGHT_ARROW); 
+  } else {
+    Keyboard.release(KEY_RIGHT_ARROW); 
+  }
+  
+  if (ps2x.Button(PSB_PAD_LEFT)) {
+    Keyboard.press(KEY_LEFT_ARROW); 
+  } else {
+    Keyboard.release(KEY_LEFT_ARROW); 
+  }
+ 
+ delay(5);
+     
+}
+
+/*
+  reads an axis (0 or 1 for x or y) and scales the 
+ analog input range to a range from 0 to <range>
+ */
+
+int readAxis(int thisAxis) { 
+  // read the analog input:
+  int reading = ps2x.Analog(thisAxis);
+
+  // map the reading from the analog input range to the output range:
+  reading = map(reading, 0, 255, 0, range);
+
+  // if the output reading is outside from the
+  // rest position threshold,  use it:
+  int distance = reading - center;
+
+  if (abs(distance) < threshold) {
+    distance = 0;
+  } 
+
+  // return the distance for this axis:
+  return distance;
+}

+ 189 - 0
PS2X_lib/examples/PS2X_Example/PS2X_Example.ino

@@ -0,0 +1,189 @@
+#include <PS2X_lib.h>  //for v1.6
+
+/******************************************************************
+ * set pins connected to PS2 controller:
+ *   - 1e column: original 
+ *   - 2e colmun: Stef?
+ * replace pin numbers by the ones you use
+ ******************************************************************/
+#define PS2_DAT        13  //14    
+#define PS2_CMD        11  //15
+#define PS2_SEL        10  //16
+#define PS2_CLK        12  //17
+
+/******************************************************************
+ * select modes of PS2 controller:
+ *   - pressures = analog reading of push-butttons 
+ *   - rumble    = motor rumbling
+ * uncomment 1 of the lines for each mode selection
+ ******************************************************************/
+//#define pressures   true
+#define pressures   false
+//#define rumble      true
+#define rumble      false
+
+PS2X ps2x; // create PS2 Controller Class
+
+//right now, the library does NOT support hot pluggable controllers, meaning 
+//you must always either restart your Arduino after you connect the controller, 
+//or call config_gamepad(pins) again after connecting the controller.
+
+int error = 0;
+byte type = 0;
+byte vibrate = 0;
+
+void setup(){
+ 
+  Serial.begin(57600);
+  
+  delay(300);  //added delay to give wireless ps2 module some time to startup, before configuring it
+   
+  //CHANGES for v1.6 HERE!!! **************PAY ATTENTION*************
+  
+  //setup pins and settings: GamePad(clock, command, attention, data, Pressures?, Rumble?) check for error
+  error = ps2x.config_gamepad(PS2_CLK, PS2_CMD, PS2_SEL, PS2_DAT, pressures, rumble);
+  
+  if(error == 0){
+    Serial.print("Found Controller, configured successful ");
+    Serial.print("pressures = ");
+	if (pressures)
+	  Serial.println("true ");
+	else
+	  Serial.println("false");
+	Serial.print("rumble = ");
+	if (rumble)
+	  Serial.println("true)");
+	else
+	  Serial.println("false");
+    Serial.println("Try out all the buttons, X will vibrate the controller, faster as you press harder;");
+    Serial.println("holding L1 or R1 will print out the analog stick values.");
+    Serial.println("Note: Go to www.billporter.info for updates and to report bugs.");
+  }  
+  else if(error == 1)
+    Serial.println("No controller found, check wiring, see readme.txt to enable debug. visit www.billporter.info for troubleshooting tips");
+   
+  else if(error == 2)
+    Serial.println("Controller found but not accepting commands. see readme.txt to enable debug. Visit www.billporter.info for troubleshooting tips");
+
+  else if(error == 3)
+    Serial.println("Controller refusing to enter Pressures mode, may not support it. ");
+  
+//  Serial.print(ps2x.Analog(1), HEX);
+  
+  type = ps2x.readType(); 
+  switch(type) {
+    case 0:
+      Serial.print("Unknown Controller type found ");
+      break;
+    case 1:
+      Serial.print("DualShock Controller found ");
+      break;
+    case 2:
+      Serial.print("GuitarHero Controller found ");
+      break;
+	case 3:
+      Serial.print("Wireless Sony DualShock Controller found ");
+      break;
+   }
+}
+
+void loop() {
+  /* You must Read Gamepad to get new values and set vibration values
+     ps2x.read_gamepad(small motor on/off, larger motor strenght from 0-255)
+     if you don't enable the rumble, use ps2x.read_gamepad(); with no values
+     You should call this at least once a second
+   */  
+  if(error == 1) //skip loop if no controller found
+    return; 
+  
+  if(type == 2){ //Guitar Hero Controller
+    ps2x.read_gamepad();          //read controller 
+   
+    if(ps2x.ButtonPressed(GREEN_FRET))
+      Serial.println("Green Fret Pressed");
+    if(ps2x.ButtonPressed(RED_FRET))
+      Serial.println("Red Fret Pressed");
+    if(ps2x.ButtonPressed(YELLOW_FRET))
+      Serial.println("Yellow Fret Pressed");
+    if(ps2x.ButtonPressed(BLUE_FRET))
+      Serial.println("Blue Fret Pressed");
+    if(ps2x.ButtonPressed(ORANGE_FRET))
+      Serial.println("Orange Fret Pressed"); 
+
+    if(ps2x.ButtonPressed(STAR_POWER))
+      Serial.println("Star Power Command");
+    
+    if(ps2x.Button(UP_STRUM))          //will be TRUE as long as button is pressed
+      Serial.println("Up Strum");
+    if(ps2x.Button(DOWN_STRUM))
+      Serial.println("DOWN Strum");
+ 
+    if(ps2x.Button(PSB_START))         //will be TRUE as long as button is pressed
+      Serial.println("Start is being held");
+    if(ps2x.Button(PSB_SELECT))
+      Serial.println("Select is being held");
+    
+    if(ps2x.Button(ORANGE_FRET)) {     // print stick value IF TRUE
+      Serial.print("Wammy Bar Position:");
+      Serial.println(ps2x.Analog(WHAMMY_BAR), DEC); 
+    } 
+  }
+  else { //DualShock Controller
+    ps2x.read_gamepad(false, vibrate); //read controller and set large motor to spin at 'vibrate' speed
+    
+    if(ps2x.Button(PSB_START))         //will be TRUE as long as button is pressed
+      Serial.println("Start is being held");
+    if(ps2x.Button(PSB_SELECT))
+      Serial.println("Select is being held");      
+
+    if(ps2x.Button(PSB_PAD_UP)) {      //will be TRUE as long as button is pressed
+      Serial.print("Up held this hard: ");
+      Serial.println(ps2x.Analog(PSAB_PAD_UP), DEC);
+    }
+    if(ps2x.Button(PSB_PAD_RIGHT)){
+      Serial.print("Right held this hard: ");
+      Serial.println(ps2x.Analog(PSAB_PAD_RIGHT), DEC);
+    }
+    if(ps2x.Button(PSB_PAD_LEFT)){
+      Serial.print("LEFT held this hard: ");
+      Serial.println(ps2x.Analog(PSAB_PAD_LEFT), DEC);
+    }
+    if(ps2x.Button(PSB_PAD_DOWN)){
+      Serial.print("DOWN held this hard: ");
+      Serial.println(ps2x.Analog(PSAB_PAD_DOWN), DEC);
+    }   
+
+    vibrate = ps2x.Analog(PSAB_CROSS);  //this will set the large motor vibrate speed based on how hard you press the blue (X) button
+    if (ps2x.NewButtonState()) {        //will be TRUE if any button changes state (on to off, or off to on)
+      if(ps2x.Button(PSB_L3))
+        Serial.println("L3 pressed");
+      if(ps2x.Button(PSB_R3))
+        Serial.println("R3 pressed");
+      if(ps2x.Button(PSB_L2))
+        Serial.println("L2 pressed");
+      if(ps2x.Button(PSB_R2))
+        Serial.println("R2 pressed");
+      if(ps2x.Button(PSB_TRIANGLE))
+        Serial.println("Triangle pressed");        
+    }
+
+    if(ps2x.ButtonPressed(PSB_CIRCLE))               //will be TRUE if button was JUST pressed
+      Serial.println("Circle just pressed");
+    if(ps2x.NewButtonState(PSB_CROSS))               //will be TRUE if button was JUST pressed OR released
+      Serial.println("X just changed");
+    if(ps2x.ButtonReleased(PSB_SQUARE))              //will be TRUE if button was JUST released
+      Serial.println("Square just released");     
+
+    if(ps2x.Button(PSB_L1) || ps2x.Button(PSB_R1)) { //print stick values if either is TRUE
+      Serial.print("Stick Values:");
+      Serial.print(ps2x.Analog(PSS_LY), DEC); //Left stick, Y axis. Other options: LX, RY, RX  
+      Serial.print(",");
+      Serial.print(ps2x.Analog(PSS_LX), DEC); 
+      Serial.print(",");
+      Serial.print(ps2x.Analog(PSS_RY), DEC); 
+      Serial.print(",");
+      Serial.println(ps2x.Analog(PSS_RX), DEC); 
+    }     
+  }
+  delay(50);  
+}

+ 78 - 0
PS2X_lib/keywords.txt

@@ -0,0 +1,78 @@
+#######################################
+# Syntax Coloring Map PS2X
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+PS2X	KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+Button	KEYWORD2
+ButtonDataByte	KEYWORD2
+NewButtonState	KEYWORD2
+ButtonPressed	KEYWORD2
+ButtonReleased	KEYWORD2
+read_gamepad	KEYWORD2
+config_gamepad	KEYWORD2
+enableRumble	KEYWORD2
+enablePressures	KEYWORD2
+Analog	KEYWORD2
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+PSB_SELECT	LITERAL1
+PSB_L3	LITERAL1
+PSB_R3	LITERAL1
+PSB_START	LITERAL1
+PSB_PAD_UP	LITERAL1
+PSB_PAD_RIGHT	LITERAL1
+PSB_PAD_DOWN	LITERAL1
+PSB_PAD_LEFT	LITERAL1
+PSB_L2	LITERAL1
+PSB_R2	LITERAL1
+PSB_L1	LITERAL1
+PSB_R1	LITERAL1
+PSB_GREEN	LITERAL1
+PSB_RED	LITERAL1
+PSB_BLUE	LITERAL1
+PSB_PINK	LITERAL1
+PSB_TRIANGLE	LITERAL1
+PSB_CIRCLE	LITERAL1
+PSB_CROSS	LITERAL1
+PSB_SQUARE	LITERAL1
+PSS_RX	LITERAL1
+PSS_RY	LITERAL1
+PSS_LX	LITERAL1
+PSS_LY	LITERAL1
+
+PSAB_PAD_RIGHT	LITERAL1
+PSAB_PAD_UP	LITERAL1
+PSAB_PAD_DOWN	LITERAL1
+PSAB_PAD_LEFT	LITERAL1
+PSAB_L2	LITERAL1
+PSAB_R2	LITERAL1
+PSAB_L1	LITERAL1
+PSAB_R1	LITERAL1
+PSAB_GREEN	LITERAL1
+PSAB_RED	LITERAL1
+PSAB_BLUE	LITERAL1
+PSAB_PINK	LITERAL1
+PSAB_TRIANGLE	LITERAL1
+PSAB_CIRCLE	LITERAL1
+PSAB_CROSS	LITERAL1
+PSAB_SQUARE	LITERAL1
+
+GREEN_FRET LITERAL1
+RED_FRET LITERAL1
+YELLOW_FRET LITERAL1
+BLUE_FRET LITERAL1
+ORANGE_FRET LITERAL1
+STAR_POWER LITERAL1
+UP_STRUM LITERAL1
+DOWN_STRUM LITERAL1
+WHAMMY_BAR LITERAL1

+ 138 - 0
linkServer_3/linkServer_3.ino

@@ -0,0 +1,138 @@
+#include <ESP8266WiFi.h>
+#include <WebSocketClient.h>
+
+#include <PS2X_lib.h>  //for v1.6
+#define PS2_DAT        1  //14    
+#define PS2_CMD        3  //15
+#define PS2_SEL        12  //16
+#define PS2_CLK        15  //17
+
+PS2X ps2x;
+int error = 0;
+byte type = 0;
+byte vibrate = 0;
+
+int PlyStnRStickUpDn = 0;  //读取PS2右侧摇杆向上/向下的值.
+int PlyStnRStickLtRt = 0;  //读取PS2右侧摇杆向左/向右的值. 
+int PlyStnLStickUpDn = 0; //读取PS2左侧摇杆向上/向下的值.
+int PlyStnLStickLtRt = 0; //读取PS2左侧摇杆向左/向右的值.
+
+/******************************************************************/
+const char* ssid     = "无线信号";
+const char* password = "无线密码";
+char path[] = "/wss";
+char host[] = "www.xxx.com";  // 服务器端域名地址
+
+WebSocketClient webSocketClient;
+WiFiClient client;
+
+void(*resetFunc) (void) = 0;
+
+void setup(void) {
+  Serial.begin(115200);
+  ps2x.config_gamepad(PS2_CLK, PS2_CMD, PS2_SEL, PS2_DAT, false, false);
+  delay(5000);
+  WiFi.begin(ssid, password);
+  while (WiFi.status() != WL_CONNECTED) {
+    delay(500);
+    Serial.println("loading...");
+  }
+  delay(5000);
+  
+  if (client.connect(host, 80)) {
+    Serial.println("Wss-Udp-ok!");
+  } else {
+    Serial.println("Wss-Udp-No!");
+    while(1) {
+    }
+  }
+
+  webSocketClient.path = path;
+  webSocketClient.host = host;
+  if (webSocketClient.handshake(client)) {
+    Serial.println("Handshake Ok");
+  } else {
+    Serial.println("Handshake No.");
+    while(1) {
+    }  
+  }
+  
+}
+
+void loop() {
+
+  String data;
+  
+  PlyStnLStickUpDn = ps2x.Analog(PSS_LY); //左侧摇杆上/下
+  PlyStnLStickLtRt = ps2x.Analog(PSS_LX); //左侧摇杆左/右
+  PlyStnRStickUpDn = ps2x.Analog(PSS_RY); //右侧摇杆上/下
+  PlyStnRStickLtRt = ps2x.Analog(PSS_RX); //右侧摇杆左/右
+  
+  ps2x.read_gamepad();
+  
+  if(ps2x.ButtonPressed(RED_FRET)){
+    webSocketClient.sendData("2");
+  }
+  if(ps2x.ButtonPressed(YELLOW_FRET)){
+    webSocketClient.sendData("1");
+  }
+  if(ps2x.ButtonPressed(BLUE_FRET)){
+    webSocketClient.sendData("3");
+  }
+  if(ps2x.ButtonPressed(ORANGE_FRET)){
+    webSocketClient.sendData("4");
+  }
+  if(ps2x.Button(UP_STRUM)){
+    webSocketClient.sendData("UP");
+  }
+  if(ps2x.Button(DOWN_STRUM)){
+    webSocketClient.sendData("DOWN");
+  }
+  
+  if(ps2x.Button(RIGHT_STRUM)){
+    webSocketClient.sendData("RIGHT");
+  }
+  if(ps2x.Button(LEFT_STRUM)){
+    webSocketClient.sendData("LEFT");
+  }
+  
+  if(ps2x.Button(PSB_START)){ 
+    webSocketClient.sendData("START");
+  }
+  if(ps2x.Button(PSB_SELECT)){
+    webSocketClient.sendData("SELECT");
+  }
+
+  if(ps2x.Button(PSB_L2)){
+    webSocketClient.sendData("L2");
+  }
+  if(ps2x.Button(PSB_R2)){
+    webSocketClient.sendData("R2");
+  }
+  if(ps2x.Button(PSB_L1)) {
+    webSocketClient.sendData("K-" + String(PlyStnLStickUpDn));
+  }
+  if(ps2x.Button(PSB_R1)) {
+    webSocketClient.sendData("W-" + String(PlyStnRStickUpDn));
+  }
+
+    
+  if (client.connected()) {
+    webSocketClient.getData(data);
+    if (data.length() > 0) {
+      if(data == "server-ok"){
+        webSocketClient.sendData("Hello!");
+      }
+      if(data == "username or password"){
+        webSocketClient.sendData("admin:123456");
+      }
+      if(data == "reset"){
+          Serial.println("EPS-Reset ...");
+          resetFunc();
+      }
+    }
+  }else{
+    resetFunc();
+  }
+  delay(50);
+}

+ 66 - 0
socket/socket.ino

@@ -0,0 +1,66 @@
+#include <WiFi.h>
+
+//网络配置
+const char* ssid = "cdjs_work";
+const char* password = "cdjs654321aA";
+
+//sockt服务器地址
+const char* host = "81.70.80.219";
+const uint16_t port = 49700;
+volatile uint16_t state = 0;
+WiFiClient client;
+
+// WIFI连接
+void netConnect(){
+  WiFi.mode(WIFI_STA);
+  WiFi.begin(ssid, password);
+  while (WiFi.status() != WL_CONNECTED) {
+    delay(500);
+    Serial.println("WIFI connection...");
+  }
+  Serial.println("WiFi connected IP:");
+  Serial.println(WiFi.localIP());    
+}
+
+//socekt 连接
+void soConnect(){
+  //  判断是否有网
+  if(WiFi.status() == WL_CONNECTED){
+    //若未连接到服务端,则客户端进行连接。
+    if(client.connected()){
+        //增加状态      
+        if(state == 0){
+          Serial.println("socket connected!");
+          state = 1;
+        }
+        //  发送      
+        delay(2000);
+        Serial.println("发送:test");
+        client.println("test");
+    }else{
+        //  判断状态
+        if(state == 1){
+          Serial.println("socket restart connection...");
+          state = 0;
+        }
+        //  连接服务器
+        Serial.println("socket connection..."); 
+        client.connect(host, port);
+        delay(1000);
+    }
+  }else{
+    //  重新连接网络
+    netConnect();
+  }
+}
+
+void setup() {
+  // put your setup code here, to run once:
+  Serial.begin(115200);
+  netConnect();
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+  soConnect();
+}

+ 252 - 0
wifi-socket/ADWebSockets/SocketIOclient.cpp

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

+ 101 - 0
wifi-socket/ADWebSockets/SocketIOclient.h

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

+ 757 - 0
wifi-socket/ADWebSockets/WebSockets.cpp

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

+ 367 - 0
wifi-socket/ADWebSockets/WebSockets.h

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

+ 80 - 0
wifi-socket/ADWebSockets/WebSockets4WebServer.h

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

+ 973 - 0
wifi-socket/ADWebSockets/WebSocketsClient.cpp

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

+ 169 - 0
wifi-socket/ADWebSockets/WebSocketsClient.h

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

+ 948 - 0
wifi-socket/ADWebSockets/WebSocketsServer.cpp

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

+ 243 - 0
wifi-socket/ADWebSockets/WebSocketsServer.h

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

+ 36 - 0
wifi-socket/ADWebSockets/WebSocketsVersion.h

@@ -0,0 +1,36 @@
+/**
+ * @file WebSocketsVersion.h
+ * @date 08.03.2021
+ * @author Markus Sattler
+ *
+ * Copyright (c) 2015 Markus Sattler. All rights reserved.
+ * This file is part of the WebSockets for Arduino.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#ifndef WEBSOCKETSVERSION_H_
+#define WEBSOCKETSVERSION_H_
+
+#define WEBSOCKETS_VERSION "2.3.6"
+
+#define WEBSOCKETS_VERSION_MAJOR 2
+#define WEBSOCKETS_VERSION_MINOR 3
+#define WEBSOCKETS_VERSION_PATCH 6
+
+#define WEBSOCKETS_VERSION_INT 2003006
+
+#endif /* WEBSOCKETSVERSION_H_ */

+ 83 - 0
wifi-socket/ADWebSockets/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;
+    }
+ }

+ 84 - 0
wifi-socket/ADWebSockets/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();
+}

+ 110 - 0
wifi-socket/ADWebSockets/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();
+}

+ 106 - 0
wifi-socket/ADWebSockets/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();
+}

+ 104 - 0
wifi-socket/ADWebSockets/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();
+}

+ 106 - 0
wifi-socket/ADWebSockets/examples/esp8266/WebSocketClient/WebSocketClient.ino

@@ -0,0 +1,106 @@
+/*
+ * WebSocketClient.ino
+ *
+ *  Created on: 24.05.2015
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+
+#include <WebSocketsClient.h>
+
+#include <Hash.h>
+
+ESP8266WiFiMulti WiFiMulti;
+WebSocketsClient webSocket;
+
+#define USE_SERIAL Serial1
+
+void webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+
+	switch(type) {
+		case WStype_DISCONNECTED:
+			USE_SERIAL.printf("[WSc] Disconnected!\n");
+			break;
+		case WStype_CONNECTED: {
+			USE_SERIAL.printf("[WSc] Connected to url: %s\n", payload);
+
+			// send message to server when Connected
+			webSocket.sendTXT("Connected");
+		}
+			break;
+		case WStype_TEXT:
+			USE_SERIAL.printf("[WSc] get text: %s\n", payload);
+
+			// send message to server
+			// webSocket.sendTXT("message here");
+			break;
+		case WStype_BIN:
+			USE_SERIAL.printf("[WSc] get binary length: %u\n", length);
+			hexdump(payload, length);
+
+			// send data to server
+			// webSocket.sendBIN(payload, length);
+			break;
+        case WStype_PING:
+            // pong will be send automatically
+            USE_SERIAL.printf("[WSc] get ping\n");
+            break;
+        case WStype_PONG:
+            // answer to a ping we send
+            USE_SERIAL.printf("[WSc] get pong\n");
+            break;
+    }
+
+}
+
+void setup() {
+	// USE_SERIAL.begin(921600);
+	USE_SERIAL.begin(115200);
+
+	//Serial.setDebugOutput(true);
+	USE_SERIAL.setDebugOutput(true);
+
+	USE_SERIAL.println();
+	USE_SERIAL.println();
+	USE_SERIAL.println();
+
+	for(uint8_t t = 4; t > 0; t--) {
+		USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+		USE_SERIAL.flush();
+		delay(1000);
+	}
+
+	WiFiMulti.addAP("SSID", "passpasspass");
+
+	//WiFi.disconnect();
+	while(WiFiMulti.run() != WL_CONNECTED) {
+		delay(100);
+	}
+
+	// server address, port and URL
+	webSocket.begin("192.168.0.123", 81, "/");
+
+	// event handler
+	webSocket.onEvent(webSocketEvent);
+
+	// use HTTP Basic Authorization this is optional remove if not needed
+	webSocket.setAuthorization("user", "Password");
+
+	// try ever 5000 again if connection has failed
+	webSocket.setReconnectInterval(5000);
+  
+  // start heartbeat (optional)
+  // ping server every 15000 ms
+  // expect pong from server within 3000 ms
+  // consider connection disconnected if pong is not received 2 times
+  webSocket.enableHeartbeat(15000, 3000, 2);
+
+}
+
+void loop() {
+	webSocket.loop();
+}

+ 88 - 0
wifi-socket/ADWebSockets/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();
+}

+ 103 - 0
wifi-socket/ADWebSockets/examples/esp8266/WebSocketClientSSLWithCA/WebSocketClientSSLWithCA.ino

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

+ 128 - 0
wifi-socket/ADWebSockets/examples/esp8266/WebSocketClientSocketIO/WebSocketClientSocketIO.ino

@@ -0,0 +1,128 @@
+/*
+ * WebSocketClientSocketIO.ino
+ *
+ *  Created on: 06.06.2016
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+
+#include <ArduinoJson.h>
+
+#include <WebSocketsClient.h>
+#include <SocketIOclient.h>
+
+#include <Hash.h>
+
+ESP8266WiFiMulti WiFiMulti;
+SocketIOclient socketIO;
+
+#define USE_SERIAL Serial1
+
+void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
+    switch(type) {
+        case sIOtype_DISCONNECT:
+            USE_SERIAL.printf("[IOc] Disconnected!\n");
+            break;
+        case sIOtype_CONNECT:
+            USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload);
+
+            // join default namespace (no auto join in Socket.IO V3)
+            socketIO.send(sIOtype_CONNECT, "/");
+            break;
+        case sIOtype_EVENT:
+            USE_SERIAL.printf("[IOc] get event: %s\n", payload);
+            break;
+        case sIOtype_ACK:
+            USE_SERIAL.printf("[IOc] get ack: %u\n", length);
+            hexdump(payload, length);
+            break;
+        case sIOtype_ERROR:
+            USE_SERIAL.printf("[IOc] get error: %u\n", length);
+            hexdump(payload, length);
+            break;
+        case sIOtype_BINARY_EVENT:
+            USE_SERIAL.printf("[IOc] get binary: %u\n", length);
+            hexdump(payload, length);
+            break;
+        case sIOtype_BINARY_ACK:
+            USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
+            hexdump(payload, length);
+            break;
+    }
+}
+
+void setup() {
+    // USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //Serial.setDebugOutput(true);
+    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+      for(uint8_t t = 4; t > 0; t--) {
+          USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+          USE_SERIAL.flush();
+          delay(1000);
+      }
+
+    // disable AP
+    if(WiFi.getMode() & WIFI_AP) {
+        WiFi.softAPdisconnect(true);
+    }
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    //WiFi.disconnect();
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    String ip = WiFi.localIP().toString();
+    USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
+
+    // server address, port and URL
+    socketIO.begin("10.11.100.100", 8880);
+
+    // event handler
+    socketIO.onEvent(socketIOEvent);
+}
+
+unsigned long messageTimestamp = 0;
+void loop() {
+    socketIO.loop();
+
+    uint64_t now = millis();
+
+    if(now - messageTimestamp > 2000) {
+        messageTimestamp = now;
+
+        // creat JSON message for Socket.IO (event)
+        DynamicJsonDocument doc(1024);
+        JsonArray array = doc.to<JsonArray>();
+        
+        // add evnet name
+        // Hint: socket.on('event_name', ....
+        array.add("event_name");
+
+        // add payload (parameters) for the event
+        JsonObject param1 = array.createNestedObject();
+        param1["now"] = (uint32_t) now;
+
+        // JSON to String (serializion)
+        String output;
+        serializeJson(doc, output);
+
+        // Send event        
+        socketIO.sendEVENT(output);
+
+        // Print JSON for debugging
+        USE_SERIAL.println(output);
+    }
+}

+ 165 - 0
wifi-socket/ADWebSockets/examples/esp8266/WebSocketClientSocketIOack/WebSocketClientSocketIOack.ino

@@ -0,0 +1,165 @@
+/*
+ * WebSocketClientSocketIOack.ino
+ *
+ *  Created on: 20.07.2019
+ *
+ */
+
+#include <Arduino.h>
+
+#include <ESP8266WiFi.h>
+#include <ESP8266WiFiMulti.h>
+
+#include <ArduinoJson.h>
+
+#include <WebSocketsClient.h>
+#include <SocketIOclient.h>
+
+#include <Hash.h>
+
+ESP8266WiFiMulti WiFiMulti;
+SocketIOclient socketIO;
+
+#define USE_SERIAL Serial
+
+
+void socketIOEvent(socketIOmessageType_t type, uint8_t * payload, size_t length) {
+    switch(type) {
+        case sIOtype_DISCONNECT:
+            USE_SERIAL.printf("[IOc] Disconnected!\n");
+            break;
+        case sIOtype_CONNECT:
+            USE_SERIAL.printf("[IOc] Connected to url: %s\n", payload);
+
+            // join default namespace (no auto join in Socket.IO V3)
+            socketIO.send(sIOtype_CONNECT, "/");
+            break;
+        case sIOtype_EVENT:
+        {
+            char * sptr = NULL;
+            int id = strtol((char *)payload, &sptr, 10);
+            USE_SERIAL.printf("[IOc] get event: %s id: %d\n", payload, id);
+            if(id) {
+                payload = (uint8_t *)sptr;
+            }
+            DynamicJsonDocument doc(1024);
+            DeserializationError error = deserializeJson(doc, payload, length);
+            if(error) {
+                USE_SERIAL.print(F("deserializeJson() failed: "));
+                USE_SERIAL.println(error.c_str());
+                return;
+            }
+            
+            String eventName = doc[0];
+            USE_SERIAL.printf("[IOc] event name: %s\n", eventName.c_str());
+
+            // Message Includes a ID for a ACK (callback)
+            if(id) {
+                // creat JSON message for Socket.IO (ack)
+                DynamicJsonDocument docOut(1024);
+                JsonArray array = docOut.to<JsonArray>();
+                
+                // add payload (parameters) for the ack (callback function)
+                JsonObject param1 = array.createNestedObject();
+                param1["now"] = millis();
+
+                // JSON to String (serializion)
+                String output;
+                output += id;
+                serializeJson(docOut, output);
+
+                // Send event        
+                socketIO.send(sIOtype_ACK, output);
+            }
+        }
+            break;
+        case sIOtype_ACK:
+            USE_SERIAL.printf("[IOc] get ack: %u\n", length);
+            hexdump(payload, length);
+            break;
+        case sIOtype_ERROR:
+            USE_SERIAL.printf("[IOc] get error: %u\n", length);
+            hexdump(payload, length);
+            break;
+        case sIOtype_BINARY_EVENT:
+            USE_SERIAL.printf("[IOc] get binary: %u\n", length);
+            hexdump(payload, length);
+            break;
+        case sIOtype_BINARY_ACK:
+            USE_SERIAL.printf("[IOc] get binary ack: %u\n", length);
+            hexdump(payload, length);
+            break;
+    }
+}
+
+void setup() {
+    //USE_SERIAL.begin(921600);
+    USE_SERIAL.begin(115200);
+
+    //Serial.setDebugOutput(true);
+    USE_SERIAL.setDebugOutput(true);
+
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+    USE_SERIAL.println();
+
+      for(uint8_t t = 4; t > 0; t--) {
+          USE_SERIAL.printf("[SETUP] BOOT WAIT %d...\n", t);
+          USE_SERIAL.flush();
+          delay(1000);
+      }
+
+    // disable AP
+    if(WiFi.getMode() & WIFI_AP) {
+        WiFi.softAPdisconnect(true);
+    }
+
+    WiFiMulti.addAP("SSID", "passpasspass");
+
+    //WiFi.disconnect();
+    while(WiFiMulti.run() != WL_CONNECTED) {
+        delay(100);
+    }
+
+    String ip = WiFi.localIP().toString();
+    USE_SERIAL.printf("[SETUP] WiFi Connected %s\n", ip.c_str());
+
+    // server address, port and URL
+    socketIO.begin("10.11.100.100", 8880);
+
+    // event handler
+    socketIO.onEvent(socketIOEvent);
+}
+
+unsigned long messageTimestamp = 0;
+void loop() {
+    socketIO.loop();
+
+    uint64_t now = millis();
+
+    if(now - messageTimestamp > 2000) {
+        messageTimestamp = now;
+
+        // creat JSON message for Socket.IO (event)
+        DynamicJsonDocument doc(1024);
+        JsonArray array = doc.to<JsonArray>();
+        
+        // add evnet name
+        // Hint: socket.on('event_name', ....
+        array.add("event_name");
+
+        // add payload (parameters) for the event
+        JsonObject param1 = array.createNestedObject();
+        param1["now"] = (uint32_t) now;
+
+        // JSON to String (serializion)
+        String output;
+        serializeJson(doc, output);
+
+        // Send event        
+        socketIO.sendEVENT(output);
+
+        // Print JSON for debugging
+        USE_SERIAL.println(output);
+    }
+}

+ 149 - 0
wifi-socket/ADWebSockets/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();
+}

+ 150 - 0
wifi-socket/ADWebSockets/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();
+}

+ 86 - 0
wifi-socket/ADWebSockets/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();
+}
+

+ 132 - 0
wifi-socket/ADWebSockets/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();
+    }
+}

+ 94 - 0
wifi-socket/ADWebSockets/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();
+}
+

+ 103 - 0
wifi-socket/ADWebSockets/examples/esp8266/WebSocketServerHooked/WebSocketServerHooked.ino

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

+ 20 - 0
wifi-socket/ADWebSockets/examples/esp8266/WebSocketServerHooked/emu

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

+ 45 - 0
wifi-socket/ADWebSockets/examples/esp8266/WebSocketServerHooked/ws-testclient.py

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

+ 86 - 0
wifi-socket/ADWebSockets/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();
+}
+

+ 121 - 0
wifi-socket/ADWebSockets/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();
+}

+ 46 - 0
wifi-socket/ADWebSockets/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();
+}

+ 7 - 0
wifi-socket/ADWebSockets/libb64/AUTHORS

@@ -0,0 +1,7 @@
+libb64: Base64 Encoding/Decoding Routines
+======================================
+
+Authors:
+-------
+
+Chris Venter	chris.venter@gmail.com	http://rocketpod.blogspot.com

+ 29 - 0
wifi-socket/ADWebSockets/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.

+ 98 - 0
wifi-socket/ADWebSockets/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

+ 28 - 0
wifi-socket/ADWebSockets/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 */

+ 119 - 0
wifi-socket/ADWebSockets/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

+ 31 - 0
wifi-socket/ADWebSockets/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 */

+ 202 - 0
wifi-socket/ADWebSockets/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

+ 21 - 0
wifi-socket/ADWebSockets/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

+ 115 - 0
wifi-socket/wifi-socket.ino

@@ -0,0 +1,115 @@
+#include <PS2X_lib.h>
+
+#include <ESP8266WiFi.h>
+#include <WebSocketsClient.h>
+
+// 定义wifi账户|密码
+#define AP_SSID "iPhone"
+#define AP_PSW  "12345678"
+
+// 服务器端域名地址
+char path[] = "/";
+char host[] = "caner.top";
+int PlyStnRStickUpDn = 0;  //读取PS2右侧摇杆向上/向下的值.
+int PlyStnRStickLtRt = 0;  //读取PS2右侧摇杆向左/向右的值.
+int PlyStnLStickUpDn = 0; //读取PS2左侧摇杆向上/向下的值.
+int PlyStnLStickLtRt = 0; //读取PS2左侧摇杆向左/向右的值.
+//socket方法
+WebSocketsClient webSocket;
+
+PS2X ps2x;
+//定义宏
+#define PS2_DAT        1  //14    
+#define PS2_CMD        3  //15
+#define PS2_SEL        12  //16
+#define PS2_CLK        15  //17
+//socket自定义监听
+void SocketEvent(WStype_t type, uint8_t * payload, size_t length) {
+  switch (type) {
+    case WStype_DISCONNECTED:
+      Serial.printf("[WSc] Disconnected!\n");
+      break;
+    case WStype_CONNECTED:
+      Serial.printf("[WSc] Connected to url: %s\n", payload);
+      break;
+    case WStype_TEXT:
+      Serial.printf("[WSc] get text: %s\n", payload);
+      break;
+  }
+};
+//监测PS2按钮并发送信息
+void Ps2OnButton() {
+  //  拿到值
+  PlyStnLStickUpDn = ps2x.Analog(PSS_LY); //左侧摇杆上/下
+  PlyStnLStickLtRt = ps2x.Analog(PSS_LX); //左侧摇杆左/右
+  PlyStnRStickUpDn = ps2x.Analog(PSS_RY); //右侧摇杆上/下
+  PlyStnRStickLtRt = ps2x.Analog(PSS_RX); //右侧摇杆左/右
+  //  读值
+  ps2x.read_gamepad();
+  //  发送值
+  if (ps2x.ButtonPressed(YELLOW_FRET)) {
+    webSocket.sendTXT("1");
+  } else if (ps2x.ButtonPressed(RED_FRET)) {
+    webSocket.sendTXT("2");
+  } else if (ps2x.ButtonPressed(BLUE_FRET)) {
+    webSocket.sendTXT("3");
+  } else if (ps2x.ButtonPressed(ORANGE_FRET)) {
+    webSocket.sendTXT("3");
+  } else if (ps2x.Button(UP_STRUM)) {
+    webSocket.sendTXT("UP");
+  } else if (ps2x.Button(DOWN_STRUM)) {
+    webSocket.sendTXT("DOWN");
+  } else if (ps2x.Button(RIGHT_STRUM)) {
+    webSocket.sendTXT("RIGHT");
+  } else if (ps2x.Button(LEFT_STRUM)) {
+    webSocket.sendTXT("LEFT");
+  } else if (ps2x.Button(PSB_START)) {
+    webSocket.sendTXT("START");
+  } else if (ps2x.Button(PSB_SELECT)) {
+    webSocket.sendTXT("SELECT");
+  } else if (ps2x.Button(PSB_L2)) {
+    webSocket.sendTXT("L2");
+  } else if (ps2x.Button(PSB_R2)) {
+    webSocket.sendTXT("R2");
+  } else if (ps2x.Button(PSB_L1)) {
+    webSocket.sendTXT("K-" + String(PlyStnLStickUpDn));
+  } else if (ps2x.Button(PSB_R1)) {
+    webSocket.sendTXT("W-" + String(PlyStnLStickUpDn));
+  } else {
+    webSocket.sendTXT("Other operating");
+  }
+};
+void setup() {
+  Serial.begin(115200);
+  WiFi.begin(AP_SSID, AP_PSW);
+  while (WiFi.status() != WL_CONNECTED) {
+    delay(500);
+    Serial.print(".");
+  }
+  Serial.println("");
+  Serial.println("WiFi connected");
+  Serial.println("IP address: ");
+  Serial.println(WiFi.localIP());
+  delay(5000);
+  //  socket连接
+  webSocket.begin(host, 49700, path);
+
+  // event handler
+  webSocket.onEvent(SocketEvent);
+
+  // 设置连接认证
+  //  webSocket.setAuthorization("user", "Password");
+
+  // 设置重连时间ms
+  webSocket.setReconnectInterval(5000);
+
+  //  设置心跳ms
+  //  webSocket.enableHeartbeat(150000, 3000, 2);
+  ps2x.config_gamepad(PS2_CLK, PS2_CMD, PS2_SEL, PS2_DAT, false, false);
+}
+
+void loop() {
+  // put your main code here, to run repeatedly:
+  webSocket.loop();
+  Ps2OnButton();
+}