SocketAndHttp-Teensy41-Server.ino 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. /*
  2. Teensy41 Websockets Server and Http Server (using NativeEthernet).
  3. Combining the Teensy41-Server-Multiple-Clients example with the NativeEthernet
  4. WebServer example (https://github.com/vjmuzik/NativeEthernet/blob/master/examples/WebServer/WebServer.ino).
  5. This sketch:
  6. 1. Connects to a ethernet network
  7. 2. Starts a websocket server on port 3000
  8. 3. Starts a http server at the default port 80
  9. 4. Waits for both http and websockets connections
  10. 5. Once a http client connects, it serves an html document, once a socket
  11. client wants to connect, it checks whether a free slot is available and
  12. accepts it accordingly
  13. 5. If the socket client is accepted it sends a welcome message and echoes any
  14. messages from the client
  15. 6. Goes back to step 4
  16. Note:
  17. Make sure you share your computer's internet connection with the Teensy
  18. via ethernet.
  19. Libraries:
  20. To use this sketch install
  21. * TeensyID library (https://github.com/sstaub/TeensyID)
  22. * NativeEthernet (https://github.com/vjmuzik/NativeEthernet)
  23. Hardware:
  24. For this sketch you need a Teensy 4.1 board and the Teensy 4.1 Ethernet Kit
  25. (https://www.pjrc.com/store/ethernet_kit.html).
  26. */
  27. #include <NativeEthernet.h>
  28. #include <ArduinoWebsockets.h>
  29. #include <TeensyID.h>
  30. using namespace websockets;
  31. // We will set the MAC address at the beginning of `setup()` using TeensyID's
  32. // `teensyMac` helper.
  33. byte mac[6];
  34. // Enter websockets server port.
  35. const uint16_t websocketsPort = 3000;
  36. // Define how many clients we accpet simultaneously.
  37. const byte maxSocketClients = 4;
  38. WebsocketsClient socketClients[maxSocketClients];
  39. WebsocketsServer socketServer;
  40. EthernetServer httpServer;
  41. void setup() {
  42. // Set the MAC address.
  43. teensyMAC(mac);
  44. // Start Serial and wait until it is ready.
  45. Serial.begin(9600);
  46. while (!Serial) {}
  47. // Connect to ethernet.
  48. if (Ethernet.begin(mac)) {
  49. Serial.println("Ethernet connected");
  50. } else {
  51. Serial.println("Ethernet failed");
  52. }
  53. // Start websockets server.
  54. socketServer.listen(websocketsPort);
  55. if (!socketServer.available()) {
  56. Serial.println("Websockets Server not available!");
  57. }
  58. // Start http server.
  59. httpServer.begin(80);
  60. Serial.print("Visit http://");
  61. Serial.print(Ethernet.localIP());
  62. Serial.println(" in the browser to connect.");
  63. }
  64. int8_t getFreeSocketClientIndex() {
  65. // If a client in our list is not available, it's connection is closed and we
  66. // can use it for a new client.
  67. for (byte i = 0; i < maxSocketClients; i++) {
  68. if (!socketClients[i].available()) return i;
  69. }
  70. return -1;
  71. }
  72. void handleMessage(WebsocketsClient &client, WebsocketsMessage message) {
  73. auto data = message.data();
  74. // Log message
  75. Serial.print("Got Message: ");
  76. Serial.println(data);
  77. // Echo message
  78. client.send("Echo: " + data);
  79. }
  80. void handleEvent(WebsocketsClient &client, WebsocketsEvent event, String data) {
  81. if (event == WebsocketsEvent::ConnectionClosed) {
  82. Serial.println("Connection closed");
  83. }
  84. }
  85. void listenForSocketClients() {
  86. if (socketServer.poll()) {
  87. int8_t freeIndex = getFreeSocketClientIndex();
  88. if (freeIndex >= 0) {
  89. WebsocketsClient newClient = socketServer.accept();
  90. Serial.printf("Accepted new websockets client at index %d\n", freeIndex);
  91. newClient.onMessage(handleMessage);
  92. newClient.onEvent(handleEvent);
  93. newClient.send("Hello from Teensy");
  94. socketClients[freeIndex] = newClient;
  95. }
  96. }
  97. }
  98. void pollSocketClients() {
  99. for (byte i = 0; i < maxSocketClients; i++) {
  100. socketClients[i].poll();
  101. }
  102. }
  103. void sendHttpReply(EthernetClient &client) {
  104. // Send a website that connects to the websocket server and allows to
  105. // communicate with the teensy.
  106. const char* header =
  107. "HTTP/1.1 200 OK\r\n"
  108. "Content-Type: text/html\r\n"
  109. "Connection: close\r\n"
  110. "\r\n";
  111. const char* document =
  112. "<!DOCTYPE html>\n"
  113. "<title>Teensy 4.1 Websockets</title>\n"
  114. "<meta charset='UTF-8'>\n"
  115. "<style>\n"
  116. " body {\n"
  117. " display: grid;\n"
  118. " grid-template: min-content auto / auto min-content;\n"
  119. " grid-gap: 1em;\n"
  120. " margin: 0;\n"
  121. " padding: 1em;\n"
  122. " height: 100vh;\n"
  123. " box-sizing: border-box;\n"
  124. " }\n"
  125. " #output {\n"
  126. " grid-column-start: span 2;\n"
  127. " overflow-y: scroll;\n"
  128. " padding: 0.1em;\n"
  129. " border: 1px solid;\n"
  130. " font-family: monospace;\n"
  131. " }\n"
  132. "</style>\n"
  133. "<input type='text' id='message' placeholder='Send a message and Teensy will echo it back!'>\n"
  134. "<button id='send-message'>send</button>\n"
  135. "<div id='output'></div>\n"
  136. "<script>\n"
  137. " const url = `ws://${window.location.host}:3000`\n"
  138. " const ws = new WebSocket(url)\n"
  139. " let connected = false\n"
  140. " const sendMessage = document.querySelector('#send-message')\n"
  141. " const message = document.querySelector('#message')\n"
  142. " const output = document.querySelector('#output')\n"
  143. " function log(message, color = 'black') {\n"
  144. " const el = document.createElement('div')\n"
  145. " el.innerHTML = message\n"
  146. " el.style.color = color\n"
  147. " output.append(el)\n"
  148. " output.scrollTop = output.scrollHeight\n"
  149. " }\n"
  150. " ws.addEventListener('open', () => {\n"
  151. " connected = true\n"
  152. " log('(✔️) Open', 'green')\n"
  153. " })\n"
  154. " ws.addEventListener('close', () => {\n"
  155. " connected = false\n"
  156. " log('(❌) Close', 'red')\n"
  157. " })\n"
  158. " ws.addEventListener('message', ({ data }) =>\n"
  159. " log(`(💌) ${data}`)\n"
  160. " )\n"
  161. " sendMessage.addEventListener('click', () => {\n"
  162. " connected && ws.send(message.value)\n"
  163. " })\n"
  164. " message.addEventListener('keyup', ({ keyCode }) => {\n"
  165. " connected && keyCode === 13 && ws.send(message.value)\n"
  166. " })\n"
  167. " log(`(📡) Connecting to ${url} ...`, 'blue')\n"
  168. "</script>\n";
  169. client.write(header);
  170. client.write(document);
  171. }
  172. void listenForHttpClients() {
  173. // Listen for incoming http clients.
  174. EthernetClient client = httpServer.available();
  175. if (client) {
  176. Serial.println("Http client connected!");
  177. // An http request ends with a blank line.
  178. bool currentLineIsBlank = true;
  179. while (client.connected()) {
  180. if (client.available()) {
  181. char c = client.read();
  182. if (c == '\n' && currentLineIsBlank) {
  183. // If we've gotten to the end of the line (received a newline
  184. // character) and the line is blank, the http request has ended,
  185. // so we can send a reply.
  186. sendHttpReply(client);
  187. break;
  188. } else if (c == '\n') {
  189. // Starting a new line.
  190. currentLineIsBlank = true;
  191. } else if (c != '\r') {
  192. // Read a character on the current line.
  193. currentLineIsBlank = false;
  194. }
  195. }
  196. }
  197. // The NativeEthernet's WebServer example adds a small delay here. For me it
  198. // seems to work without the delay. Uncomment to following line if you have
  199. // issues connecting to the website in the browser.
  200. // delay(1);
  201. // Close the connection.
  202. client.stop();
  203. }
  204. }
  205. void loop() {
  206. listenForSocketClients();
  207. pollSocketClients();
  208. listenForHttpClients();
  209. }