1+ #ifndef LED_BUILTIN_LED
2+ #define BUILTIN_LED 15
3+ #endif
4+ #define BOOT_BUTTON 0
5+
6+ #if defined(ESP8266)
7+ #include < ESP8266mDNS.h>
8+ #elif defined(ESP32)
9+ #include < ESPmDNS.h>
10+ #endif
11+ #include < FS.h>
12+ #include < LittleFS.h>
13+ #include < AsyncFsWebServer.h> // https://github.com/cotestatnt/async-esp-fs-webserver
14+
15+ #define FILESYSTEM LittleFS
16+
17+ char const * hostname = " fsbrowser" ;
18+ AsyncFsWebServer server (80 , FILESYSTEM, hostname);
19+ long unsigned int timer = 0 ; // used to determine when to send websocket packets
20+
21+ // type declarations for websocket packet
22+
23+ // data type
24+ typedef struct trig_t {
25+ float sin;
26+ float cos;
27+ float tan;
28+ uint16_t angle; // storing angle (x1000) as an integer, just to be different!
29+ uint16_t X; // column on chart
30+ };
31+
32+ // union of data type and a byte array for websockets
33+ typedef union trig_u {
34+ trig_t data;
35+ uint8_t byteArray[sizeof (trig_t )];
36+ };
37+
38+ trig_u trig; // union to store trig data to be transmitted
39+ uint16_t X=0 ; // chart column
40+
41+ // HTML of homepage:
42+ static const char homepage[] PROGMEM = R"EOF(
43+ <!DOCTYPE html>
44+ <html>
45+
46+ <body onload="onBodyLoad()">
47+ <canvas id="chart" width="500" height="202"></canvas>
48+ <span>Angle (radians): </span><span id="angle"></span>
49+ <script type="text/javascript">
50+
51+ var ws = null; // reference to websocket
52+ var v16; // 16-bit view of incoming data
53+ var v32; // 32-bit view of incoming data
54+
55+ var ctxChart = null; // reference to chart canvas 2d context
56+ var angle; // angle (radians)
57+ var sin = 0; // sin of angle
58+ var cos = 1; // cos of angle
59+ var tan = 0; // tan of angle
60+
61+ // Handles incoming messages
62+ function rxMessage(msg) {
63+ // console.log(msg);
64+ v16 = new Uint16Array(msg);
65+ v32 = new Float32Array(msg);
66+ angle = v16[6] / 1000; // (1000x) angle is stored as an int in the 7th pair of bytes in the websocket packet
67+ var X = v16[7]; // current column is stored as an int in the 8th pair of bytes in the websocket packet
68+ document.getElementById("angle").innerHTML = angle;
69+ // Draw a line from previous sin point to current sin point
70+ ctxChart.strokeStyle = "red";
71+ ctxChart.beginPath();
72+ ctxChart.moveTo(X-1, 100 - 100*sin);
73+ sin = v32[0]; // sin is stored as a float in the first four bytes of the websocket packet
74+ ctxChart.lineTo(X, 100 - 100*sin);
75+ ctxChart.stroke();
76+ // Draw a line from previous cos point to current sin point
77+ ctxChart.strokeStyle = "green";
78+ ctxChart.beginPath();
79+ ctxChart.moveTo(X-1, 100 - 100*cos);
80+ cos = v32[1];
81+ ctxChart.lineTo(X, 100 - 100*cos);
82+ ctxChart.stroke();
83+ // Draw a line from previous tan point to current tan point
84+ ctxChart.strokeStyle = "blue";
85+ ctxChart.beginPath();
86+ ctxChart.moveTo(X-1, 100 - 100*tan);
87+ tan = v32[2];
88+ ctxChart.lineTo(X, 100 - 100*tan);
89+ ctxChart.stroke();
90+ // increment X and wrap if necessary
91+ if (++X >= 500) {
92+ X = 0;
93+ ctxChart.clearRect(0,0,500,202);
94+ }
95+ }
96+
97+ // Configure and start WebSocket client
98+ function startSocket() {
99+ ws = new WebSocket('ws://' + document.location.host + '/ws', ['arduino']);
100+ ws.binaryType = "arraybuffer";
101+ ws.onopen = function (e) {
102+ console.log("WebSocket client connected to " + 'ws://' + document.location.host + '/ws');
103+ };
104+ ws.onclose = function (e) {
105+ addMessage("WebSocket client disconnected");
106+ };
107+ ws.onerror = function (e) {
108+ console.log("ws error", e);
109+ addMessage("Error");
110+ };
111+ ws.onmessage = function (e) {
112+ rxMessage(e.data)
113+ };
114+ }
115+
116+ // When page is fully loaded start connection
117+ function onBodyLoad() {
118+ startSocket();
119+ ctxChart = document.getElementById("chart").getContext("2d");
120+ }
121+ </script>
122+ </body>
123+
124+ </html>
125+ )EOF" ;
126+
127+ // In this example a custom websocket event handler is used instead default
128+ void onWsEvent (AsyncWebSocket* server, AsyncWebSocketClient* client, AwsEventType type, void * arg, uint8_t * data, size_t len) {
129+ switch (type) {
130+ case WS_EVT_CONNECT:
131+ client->printf (" {\" Websocket connected\" : true, \" clients\" : %u}" , client->id ());
132+ Serial.printf (" Websocket client %u connected\n " , client->id ());
133+ break ;
134+
135+ case WS_EVT_DISCONNECT:
136+ Serial.printf (" Websocket client %u connected\n " , client->id ());
137+ break ;
138+
139+ case WS_EVT_DATA:
140+ {
141+ AwsFrameInfo* info = (AwsFrameInfo*)arg;
142+ if (info->opcode == WS_TEXT) {
143+ char msg[len+1 ];
144+ msg[len] = ' \0 ' ;
145+ memcpy (msg, data, len);
146+ Serial.printf (" Received message \" %s\"\n " , msg);
147+ }
148+ }
149+ break ;
150+
151+ default :
152+ break ;
153+ }
154+ }
155+
156+ // ////////////////////////////// Filesystem /////////////////////////////////////////
157+ void listDir (fs::FS &fs, const char * dirname, uint8_t levels){
158+ Serial.printf (" \n Listing directory: %s\n " , dirname);
159+ File root = fs.open (dirname, " r" );
160+ if (!root){
161+ Serial.println (" - failed to open directory" );
162+ return ;
163+ }
164+ if (!root.isDirectory ()){
165+ Serial.println (" - not a directory" );
166+ return ;
167+ }
168+ File file = root.openNextFile ();
169+ while (file){
170+ if (file.isDirectory ()){
171+ if (levels){
172+ #ifdef ESP32
173+ listDir (fs, file.path (), levels - 1 );
174+ #elif defined(ESP8266)
175+ listDir (fs, file.fullName (), levels - 1 );
176+ #endif
177+ }
178+ } else {
179+ Serial.printf (" |__ FILE: %s (%d bytes)\n " ,file.name (), file.size ());
180+ }
181+ file = root.openNextFile ();
182+ }
183+ }
184+
185+ bool startFilesystem () {
186+ if (FILESYSTEM.begin ()) {
187+ listDir (LittleFS, " /" , 1 );
188+ return true ;
189+ } else {
190+ Serial.println (" ERROR on mounting filesystem. It will be reformatted!" );
191+ FILESYSTEM.format ();
192+ ESP.restart ();
193+ }
194+ return false ;
195+ }
196+
197+ void setup () {
198+ pinMode (BUILTIN_LED, OUTPUT);
199+ digitalWrite (BUILTIN_LED, HIGH);
200+
201+ Serial.begin (115200 );
202+ while (!Serial) {
203+ if (millis () > 3000 ) break ; // 3 second timeout to start Serial
204+ }
205+
206+ digitalWrite (BUILTIN_LED, LOW);
207+ if (Serial) { // flash LED if Serial is available
208+ delay (200 );
209+ digitalWrite (BUILTIN_LED, HIGH);
210+ delay (200 );
211+ digitalWrite (BUILTIN_LED, LOW);
212+ }
213+
214+ // FILESYSTEM INIT
215+ startFilesystem ();
216+
217+ // Try to connect to stored SSID, start AP if fails after timeout
218+ IPAddress myIP = server.startWiFi (15000 , " ESP_AP" , " 123456789" );
219+
220+ // Add custom page handlers
221+ server.on (" /" , HTTP_GET, [](AsyncWebServerRequest *request){
222+ request->send (200 , " text/html" , homepage);
223+ });
224+
225+ // Enable ACE FS file web editor and add FS info callback function
226+ server.enableFsCodeEditor ();
227+
228+ #ifdef ESP32
229+ server.setFsInfoCallback ([](fsInfo_t* fsInfo) {
230+ fsInfo->fsName = " LittleFS" ;
231+ fsInfo->totalBytes = LittleFS.totalBytes ();
232+ fsInfo->usedBytes = LittleFS.usedBytes ();
233+ });
234+ #endif
235+
236+ // Init with custom WebSocket event handler and start server
237+ server.init (onWsEvent);
238+
239+ Serial.print (F (" ESP Web Server started on IP Address: " ));
240+ Serial.println (WiFi.localIP ());
241+ Serial.println (F (
242+ " This is \" BinaryWebSocket.ino\" example.\n "
243+ " Open /setup page to configure optional parameters.\n "
244+ " Open /edit page to view, edit or upload example or your custom webserver source files."
245+ ));
246+
247+ // Set hostname
248+ #ifdef ESP8266
249+ WiFi.hostname (hostname);
250+ #elif defined(ESP32)
251+ WiFi.setHostname (hostname);
252+ #endif
253+
254+ // Start MDSN responder
255+ if (WiFi.status () == WL_CONNECTED) {
256+ if (MDNS.begin (hostname)) {
257+ Serial.println (F (" MDNS responder started." ));
258+ Serial.printf (" You should be able to connect with address http://%s.local/\n " , hostname);
259+ // Add service to MDNS-SD
260+ MDNS.addService (" http" , " tcp" , 80 );
261+ }
262+ }
263+ }
264+
265+ void loop () {
266+
267+ // send websocket every 50ms
268+ if (millis () > 50 * timer) {
269+ timer++;
270+ trig.data .angle = 1000.0 * 2 * PI * X/500 ; // angle (x1000) is stored as a 16-bit integer, just to be different!
271+ trig.data .sin = sin (trig.data .angle /1000.0 );
272+ trig.data .cos = cos (trig.data .angle /1000.0 );
273+ trig.data .tan = tan (trig.data .angle /1000.0 );
274+ trig.data .X = X;
275+ if (++X >= 500 ) X = 0 ;
276+ server.wsBroadcastBinary (trig.byteArray , sizeof (trig.byteArray ));
277+ }
278+ }
0 commit comments