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