Compare commits

...
349 Commits
Author SHA1 Message Date
Sergio R. Caprile 32559f15ea fix Github editor line ending creativity 2023-10-12 10:56:38 -03:00
Sergio R. Caprile b10eef1e32 Update version in mongoose.h 2023-10-12 10:44:58 -03:00
Sergio R. Caprile 7c4911598c Update version.h 2023-10-12 10:27:55 -03:00
Sergio R. Caprile efacb20bdc Merge pull request #2423 from cesanta/same
rename same54
2023-10-11 17:36:09 -03:00
Sergio R. Caprile 2e6188205f rename same54 2023-10-11 16:58:21 -03:00
Sergio R. Caprile 83fec2cf45 cleanup 2023-10-11 16:39:24 -03:00
Sergio R. Caprile 9be36eb9cb Merge pull request #2422 from cesanta/tcp
Fix tcp example for 2-way TLS
2023-10-11 16:34:12 -03:00
Sergio R. Caprile a51802ecaf Fix tcp example for 2-way TLS 2023-10-11 16:25:24 -03:00
Sergio R. Caprile b1c220e610 Remove mg_mkpipe() leftovers 2023-10-10 18:16:02 -03:00
Sergey Lyubka 36f6b805e3 Merge pull request #2421 from cesanta/h723
Add h723 example
2023-10-10 19:19:51 +01:00
Sergey Lyubka 64efad9e46 Merge pull request #2419 from cesanta/rt1020
Add FreeRTOS example for RT1020
2023-10-10 07:57:05 +01:00
Sergio R. Caprile 8a8d5ce151 Add FreeRTOS example for RT1020 2023-10-09 17:03:58 -03:00
Sergey Lyubka 907417f9ec Merge pull request #2418 from cesanta/rt1020
Add TRNG
2023-10-09 17:24:39 +01:00
Sergio R. Caprile 0965177e72 Add TRNG 2023-10-09 13:03:52 -03:00
cpq 7d07ca8427 Add drivers 2023-10-09 09:36:43 +01:00
Sergey Lyubka d18b2b390a Merge pull request #2415 from cesanta/json
Add mg_json_next() for iterating objects/arrays
2023-10-09 07:55:44 +01:00
Sergey Lyubka fefe8b1c15 Merge pull request #2416 from cesanta/log
Add MG_ENABLE_CUSTOM_LOG
2023-10-09 07:55:31 +01:00
cpq ea2ff010a9 Add MG_ENABLE_CUSTOM_LOG 2023-10-08 17:09:14 +01:00
cpq e357cb3185 Bump up max DNS responses 10->15 2023-10-08 15:30:18 +01:00
cpq 293ee2162c Add mg_json_next() for iterating objects/arrays 2023-10-07 22:40:51 +01:00
cpq c29ee769bc Fix comment 2023-10-07 17:58:20 +01:00
Sergey Lyubka 82399f1561 Merge pull request #2414 from cesanta/rt1020
Rework  RT1020 driver
2023-10-07 11:21:29 +01:00
Sergio R. Caprile 9255ceaf8a Rework RT1020 driver
Add web server in example
Add to tests
2023-10-06 16:56:00 -03:00
cpq bb9c7c0eb3 Handle ECOS case in accept_conn() 2023-10-06 15:58:26 +01:00
Sergey Lyubka 8ec327f9c7 Merge pull request #2407 from cesanta/rt1020
Add RT1020-EVK baremetal example
2023-10-05 14:19:00 +01:00
Sergio R. Caprile c9b982eaff add update script to Makefile 2023-10-04 16:31:50 -03:00
Sergio R. Caprile 248e7b6e29 Add RT1020-EVK baremetal example 2023-10-04 15:18:57 -03:00
Sergey Lyubka cb1ae312a1 Merge pull request #2404 from cesanta/g031
Add nucleo-g031 baremetal example
2023-10-04 15:39:19 +01:00
cpq aec25cf547 Add h723 example 2023-10-04 11:11:56 +01:00
cpq 4db3fc2515 dd nucleo-g031 baremetal example 2023-10-03 13:55:47 +01:00
Sergey Lyubka 79d6017c56 Merge pull request #2402 from cesanta/bcast
send broadcasts in MIP
2023-10-03 13:53:58 +01:00
Sergio R. Caprile d4c122b17a send broadcasts in MIP 2023-10-02 13:37:27 -03:00
cpq 57fd93eeda Fix ring color 2023-10-01 23:20:42 +01:00
cpq 19cac05747 Enhance components 2023-10-01 23:08:10 +01:00
Sergey Lyubka 5ebede5096 Merge pull request #2395 from cesanta/scopeid
Fix #2192 - honor addr%scopeid ipv6 notation
2023-09-29 15:52:40 +01:00
cpq 46ecb07fc8 Fix #2192 - honor addr%scopeid ipv6 notation 2023-09-29 15:49:47 +01:00
cpq a628a05efb Restore pub. domain notice 2023-09-29 13:09:29 +01:00
cpq fc626bb57f Update pakced fs 2023-09-28 16:27:58 +01:00
cpq 1291839182 Update packed fs 2023-09-28 15:09:34 +01:00
Sergey Lyubka 736e7a2cf9 Merge pull request #2394 from cesanta/chunks
Get rid of MG_EV_HTTP_CHUNK
2023-09-26 20:45:40 +01:00
cpq 7884a2f3c7 Get rid of MG_EV_HTTP_CHUNK 2023-09-26 20:45:24 +01:00
Sergey Lyubka dde699ffda Merge pull request #2393 from cesanta/ota-api-refactoring
OTA API refactoring: renamed files and defines
2023-09-26 14:15:49 +01:00
robert fa54a22aed OTA API refactoring: renamed files and defines 2023-09-26 08:33:39 -04:00
cpq 822b0c011a Add sys_flash.c for common mg_flash_{load,save} 2023-09-25 07:55:35 +01:00
cpq a0f8a197c9 Split sys.h API over MG_SYS_* 2023-09-25 07:33:38 +01:00
Sergey Lyubka 7e75e51332 Merge pull request #2392 from cesanta/flash2
Commonise flash-based OTA. Add h7 support.
2023-09-25 05:21:52 +01:00
cpq b37efbe891 Commonise flash-based OTA. Add h7 support. 2023-09-24 21:55:49 +01:00
cpq 6e648c90b0 Added OTA to the feature list 2023-09-24 21:18:02 +01:00
cpq 377d6acc74 Fix leak in unit test 2023-09-22 08:12:09 +01:00
Sergey Lyubka f552d15fe4 Merge pull request #2390 from cesanta/tickets
Restore tickets support
2023-09-21 19:08:08 +01:00
cpq d1204e8a66 Better chunk length calculation 2023-09-21 19:04:32 +01:00
cpq 9ec48e41f7 Restore tickets support 2023-09-21 18:43:33 +01:00
Sergey Lyubka 9055a83b4f Merge pull request #2388 from cesanta/dns
dns.c slight refactor, no functional changes
2023-09-21 13:38:23 +01:00
cpq dd047b2613 dns.c slight refactor, no functional changes 2023-09-21 13:38:06 +01:00
Sergey Lyubka b7a32069b1 Merge pull request #2389 from cesanta/ipv6
Address discussions/2305 - bind to v6 only / v6 and v4
2023-09-21 13:37:29 +01:00
cpq 00d475bc7e Address https://github.com/cesanta/mongoose/discussions/2305 - bind to v6 only / v6 and v4 2023-09-21 12:35:47 +01:00
Sergio R. Caprile 33ab44430c Merge pull request #2386 from cesanta/timer
Revert to the old TLS API, but keep certs as mg_str for DER
2023-09-19 12:17:56 -03:00
Sergio R. Caprile b355da32b9 Add GA OpenSSL tests back 2023-09-19 12:08:02 -03:00
cpq 3f0366a514 Revert to the old TLS API, but keep certs as mg_str for DER 2023-09-19 11:15:23 +01:00
cpq 7ecbbc517d Using mg_bzero to cleanup memory in mg_close_conn() 2023-09-17 22:25:36 +01:00
cpq a04c94444e Add rt1020 baremetal sketch 2023-09-17 10:41:04 +01:00
Sergio R. Caprile 42b9ac1145 update 2023-09-12 18:30:23 -03:00
Sergio R. Caprile 7be4925c70 Merge pull request #2384 from cesanta/p_open
Do not use CLOEXEC on Windows
2023-09-12 18:28:39 -03:00
Sergio R. Caprile 7b19568683 Do not use CLOEXEC on Windows 2023-09-12 17:11:55 -03:00
Sergio R. Caprile 8f56779855 Merge pull request #2383 from cesanta/poll
Optimize test coverage
2023-09-12 16:41:17 -03:00
Sergio R. Caprile fdfad8462c Optimize test coverage 2023-09-12 16:28:10 -03:00
Sergio R. Caprile 3773ef45e7 Optimize test coverage 2023-09-12 16:22:54 -03:00
Sergio R. Caprile c0ca3cacb4 Fixing test.yml 2023-09-12 15:05:29 -03:00
Sergio R. Caprile bed0630698 Fixing test.yml 2023-09-12 09:13:38 -03:00
Sergey Lyubka 53baa18836 Merge pull request #2380 from cesanta/ip-fragmentation-2
IP fragmentation send path: added TCP/UDP packet splitting to fit within MTU
2023-09-12 12:51:04 +01:00
Sergey Lyubka 32a2144426 Merge pull request #2382 from cesanta/poll
increase test coverage
2023-09-12 12:50:50 +01:00
robert 264026acde added TCP/UDP packet splitting to fit within MTU 2023-09-12 07:05:13 -04:00
Sergio R. Caprile 43503ef73a increase test coverage 2023-09-11 17:46:25 -03:00
Sergio R. Caprile 7f52717e9d increase test coverage 2023-09-11 17:41:36 -03:00
Sergio R. Caprile bd59185ef1 Merge pull request #2381 from cesanta/tls
add host only validation
2023-09-11 16:38:18 -03:00
Sergio R. Caprile 310ffcd4eb add host only validation 2023-09-11 16:34:24 -03:00
cpq d23664a457 Move network code to net.* 2023-09-08 09:39:57 +01:00
Sergio R. Caprile a1e316ddcf make TCP example work with new TLS API 2023-09-06 18:50:33 -03:00
Sergio R. Caprile e328758fc7 Merge pull request #2379 from cesanta/tls
unifomize TLS examples, stage 1
2023-09-06 16:55:46 -03:00
Sergio R. Caprile dbf49255a3 fix 2023-09-06 16:19:21 -03:00
Sergio R. Caprile 1391bdcf8b remove certs.h 2023-09-06 15:49:02 -03:00
Sergio R. Caprile d8106a8ffe use packed ca.pem 2023-09-06 15:21:16 -03:00
Sergio R. Caprile ae55789c4b erase ca.pem 2023-09-06 15:21:16 -03:00
Sergio R. Caprile ec02ccf5b2 Merge pull request #2377 from cesanta/h5
uniformize examples
2023-09-05 13:43:39 -03:00
Sergio R. Caprile f970cd78d7 uniformize examples 2023-09-05 13:36:46 -03:00
Sergio R. Caprile 8140ea16a7 Merge pull request #2371 from cesanta/cloexec
Fix #2369 - set CLOEXEC on epoll and regular file fds
2023-09-05 10:15:58 -03:00
Sergey Lyubka 2883709756 Merge pull request #2370 from cesanta/ota
Add OTA to the device dashboard. Implement for H5
2023-09-05 13:28:22 +01:00
cpq ca4c2c6c41 Set CLOEXEC on epoll and regular file fds 2023-09-05 06:56:41 +01:00
Sergey Lyubka fbc56f4adf Merge pull request #2367 from cesanta/ip-fragmentation
closing connection if a fragmented packet is received
2023-09-05 06:34:29 +01:00
cpq 2f014237b2 Add OTA to the device dashboard. Implement for H5 2023-09-04 22:07:32 +01:00
robert f33e3a7752 closing connection if a fragmented packet is received 2023-08-30 10:30:51 -04:00
cpq d5b5cec797 Make fuzzer faster on large input/output 2023-08-26 00:52:24 +01:00
Sergey Lyubka f8e1f17651 Merge pull request #2366 from cesanta/h5
Add FreeRTOS example on H5
2023-08-25 21:21:41 +01:00
Sergio R. Caprile be58bfe22b Add FreeRTOS example on H5 2023-08-25 17:15:26 -03:00
Sergey Lyubka 995a63dc39 Merge pull request #2364 from cesanta/braces
Style: always braces around if + else
2023-08-25 15:00:52 +01:00
cpq e8d57ac4e1 Style: always braces around if + else 2023-08-25 14:46:38 +01:00
Sergio R. Caprile eab3a83548 Merge pull request #2362 from cesanta/h5
Update H5 HAL, enable RNG, work at 250MHz, add to tests
2023-08-24 13:35:37 -03:00
Sergio R. Caprile 3fb9e5fd56 Update H5 HAL, enable RNG, work at 250MHz, add to tests 2023-08-24 13:32:37 -03:00
Sergio R. Caprile 41448278cf Merge pull request #2360 from cesanta/h7
Fix sysinit in H7
2023-08-23 14:37:21 -03:00
cpq 48516b43c5 Use device 11 2023-08-23 00:38:57 +01:00
Sergio R. Caprile 80b7be0568 Fix sysinit in H7 2023-08-22 19:58:19 -03:00
Sergey Lyubka 4183552aee Merge pull request #2358 from cesanta/b64
Change base64 API
2023-08-22 14:43:33 +01:00
cpq 527e89754e Change base64 API 2023-08-22 12:11:13 +01:00
robertc2000 30e0f42c97 Merge pull request #2357 from cesanta/tcpip-conn-init
improved tcp connection initiation
2023-08-22 09:30:24 +03:00
Sergio R. Caprile 39b40daf5d cleanup 2023-08-21 19:34:04 -03:00
Sergio R. Caprile 94636f4d30 Merge pull request #2355 from cesanta/mqtt54
Add MQTT example and test for SAME54
2023-08-21 19:27:17 -03:00
Sergio R. Caprile fd53548c5e Add MQTT example and test for SAME54 2023-08-21 19:24:33 -03:00
Sergio R. Caprile 354ea30797 Add MQTT example and test for SAME54 2023-08-21 19:11:50 -03:00
Sergio R. Caprile 11303e0dc1 Merge pull request #2354 from cesanta/tst
Add MQTT test for MIP
2023-08-21 17:26:24 -03:00
Sergio R. Caprile b4c645f703 Add MQTT test for MIP 2023-08-21 17:22:42 -03:00
cpq 3256218fd2 Do uri len check after it is finalised 2023-08-21 17:16:48 +01:00
robert a4f14b0ddf improved tcp connection initiation 2023-08-21 10:22:53 -04:00
Sergio R. Caprile e7459d91f9 Merge pull request #2353 from cesanta/certs
Store certs in certs/. Add mg_unpacked()
2023-08-21 09:52:10 -03:00
cpq b32d1c15f0 Store certs in certs/. Add mg_unpacked() 2023-08-20 23:43:04 +01:00
Sergio R. Caprile 1e0d57a832 Merge pull request #2352 from cesanta/mip
Unify naming: all network stacks are in src/net_...
2023-08-18 16:40:57 -03:00
cpq f3617a1cf3 Add integer overflow protection 2023-08-18 20:33:57 +01:00
cpq 89a9a50b96 Unify naming: all network stacks are in src/net_... 2023-08-18 19:59:15 +01:00
cpq 69d2ba7316 Add example for nucleo-h563zi 2023-08-18 16:06:17 +01:00
robertc2000 3ea5f8dbde Merge pull request #2344 from cesanta/tcp-closure
improved tcp closure
2023-08-18 14:06:41 +03:00
Sergio R. Caprile d56152875d Merge pull request #2350 from cesanta/deprecate
deprecate mg_mkpipe()
2023-08-17 18:32:39 -03:00
Sergio R. Caprile 7459d30a28 deprecate mg_mkpipe() 2023-08-17 18:05:03 -03:00
Sergio R. Caprile 16bf9101aa Merge pull request #2349 from cesanta/isclosing
is_closing -> is_draining
2023-08-17 16:52:51 -03:00
Sergio R. Caprile 2b20af36a7 is_closing -> is_draining 2023-08-17 16:45:10 -03:00
robert f5e87cceec improved tcp closure 2023-08-17 10:34:31 -04:00
Sergey Lyubka 36fcb7ed21 Merge pull request #2347 from cesanta/cl
Fix #2322 - stricter Content-Length check, allow 1*DIGIT only
2023-08-17 14:58:01 +01:00
cpq 9fe7807b02 Fix #2322 - stricter Content-Length check, allow 1*DIGIT only 2023-08-17 12:55:08 +01:00
Sergey Lyubka 88e3017ee5 Merge pull request #2348 from cesanta/dns
Fix #2263 - allow multiple errors in DNS handler
2023-08-17 10:12:16 +01:00
cpq 17f3738192 Fix #2263 - allow multiple errors in DNS handler 2023-08-17 10:11:21 +01:00
Sergey Lyubka 6cfdc39005 Merge pull request #2346 from cesanta/regex
Fix #2345 - json parsing of arrays of objects
2023-08-16 22:10:13 +01:00
cpq 58886a9693 Fix #2345 - json parsing of arrays of objects 2023-08-16 22:09:45 +01:00
Sergey Lyubka 66e022c454 Merge pull request #2343 from cesanta/fuz
Add http serve to fuzzer
2023-08-16 20:46:27 +01:00
cpq ea3968601e Add http serve to fuzzer 2023-08-16 20:18:52 +01:00
Sergio R. Caprile 54be13d1d8 Merge pull request #2342 from cesanta/pio
Fix PIO assembly error
2023-08-14 16:17:37 -03:00
Sergio R. Caprile 22ea825d46 Fix PIO assembly error 2023-08-14 16:15:27 -03:00
Sergio R. Caprile 8c03aa2801 nit 2023-08-10 19:37:47 -03:00
Sergio R. Caprile 5bb952260a Merge pull request #2340 from cesanta/same54
SAME54 Ethernet driver
2023-08-10 19:26:50 -03:00
Sergio R. Caprile face6c44c6 SAME54 Ethernet driver
Add tls_builtin.c
2023-08-10 18:59:23 -03:00
Sergio R. Caprile 66c2bfe822 Merge pull request #2339 from cesanta/revert-2336-foo
Revert "Foo"
2023-08-10 18:16:38 -03:00
Sergio R. Caprile 3721bcc790 Revert "Foo" 2023-08-10 17:48:55 -03:00
Sergio R. Caprile cacab6fe4c Merge pull request #2338 from cesanta/keil
Keil
2023-08-09 17:17:57 -03:00
scaprile 6ad8674cd4 Add F4 Keil examples 2023-08-09 14:23:10 -03:00
novlean 847990ce49 Update README.md
Added URL to https://mongoose.ws/integration/
2023-08-09 17:50:57 +01:00
scaprile 1bfdc1f138 Add hw testing 2023-08-08 17:16:15 -03:00
Sergio R. Caprile 58367fe23e Merge pull request #2337 from cesanta/h7f
add H7 FreeRTOS example
2023-08-08 15:32:29 -03:00
Sergio R. Caprile a8712e2307 add H7 FreeRTOS example 2023-08-08 14:38:41 -03:00
robertc2000 8acfae1111 Merge pull request #2336 from cesanta/foo
Foo
2023-08-08 09:26:48 +03:00
cpq 70fc71c6f7 Changes 2023-08-07 06:18:49 -04:00
Sergio R. Caprile dd5118cab0 Merge pull request #2332 from cesanta/cube
update Cube test Makefile to support testing packaged examples
2023-08-04 17:11:22 -03:00
Sergio R. Caprile 2d75a6ccd4 update 2023-08-04 15:37:57 -03:00
Sergio R. Caprile e004ab1693 Merge pull request #2331 from cesanta/makefile
update Makefile
2023-08-04 14:45:56 -03:00
Sergio R. Caprile acddfc3bf4 update Makefile 2023-08-04 14:32:28 -03:00
cpq 519d4b0286 Changes 2023-08-04 13:53:01 +01:00
cpq 8005a5e687 Changes 2023-08-04 13:53:01 +01:00
cpq f5943fe591 Add tls_builtin.c 2023-08-04 13:53:01 +01:00
cpq cea7fcdb37 AVR build fixes 2023-08-04 13:52:59 +01:00
robert b5213c13dd SAME54 Ethernet driver 2023-08-04 13:48:55 +01:00
Sergio R. Caprile 3a06b4833c Merge pull request #2330 from cesanta/timeout
Timeout
2023-08-03 14:52:54 -03:00
Sergio R. Caprile 5cdf6c1d05 Add SYN timeout 2023-08-03 14:23:14 -03:00
Sergio R. Caprile 5754e5dd59 fix ARP timeout 2023-08-03 13:42:56 -03:00
Sergio R. Caprile e5ab1c454b Merge pull request #2329 from cesanta/arp
Implement ARP resolution for local LANs
2023-08-03 11:02:43 -03:00
cpq 697576b63c Implement ARP resolution for local LANs 2023-08-03 13:48:23 +01:00
Sergio R. Caprile 2db41aef09 Merge pull request #2328 from cesanta/ft
standardize example names
2023-08-02 16:20:05 -03:00
Sergio R. Caprile 7f4ad3cade standardize example names 2023-08-02 16:07:54 -03:00
Sergio R. Caprile 74c4c32082 Merge pull request #2327 from cesanta/rndis
standardize example names
2023-08-02 14:24:07 -03:00
Sergio R. Caprile 30d545443c standardize example names 2023-08-02 14:03:11 -03:00
Sergio R. Caprile dedba52315 Merge pull request #2325 from cesanta/fpu
FP unit in H743 is dp
2023-08-01 17:06:54 -03:00
Sergio R. Caprile 481f7b5cff FP unit in H743 is dp 2023-08-01 17:02:05 -03:00
Sergio R. Caprile 6ce4f4930c Merge pull request #2324 from cesanta/fb
standardize example names
2023-08-01 15:46:58 -03:00
Sergio R. Caprile e33a10db75 standardize example names 2023-08-01 15:24:28 -03:00
Sergio R. Caprile ef17055145 Merge pull request #2323 from cesanta/cube
standardize example names
2023-07-31 18:27:34 -03:00
Sergio R. Caprile 6a2fa10a4e standardize example names 2023-07-31 17:47:56 -03:00
Sergio R. Caprile 6b81a5846d Merge pull request #2320 from cesanta/2308collaterals
fix autogenerated
2023-07-28 17:47:49 -03:00
Sergio R. Caprile b54d086fde fix autogenerated 2023-07-28 17:06:58 -03:00
Sergio R. Caprile 5ee9d22263 momentarily disable MacOS OpenSSL tests 2023-07-26 15:53:59 -03:00
Sergio R. Caprile 7dcb097a5d momentarily disable OpenSSL tests 2023-07-26 15:49:03 -03:00
Sergio R. Caprile 29a30239ab momentarily disable OpenSSL tests 2023-07-26 15:33:30 -03:00
Sergio R. Caprile ed54b60ea3 update to new API 2023-07-25 18:41:41 -03:00
Sergio R. Caprile 199af33b30 update to new TLS API 2023-07-25 14:26:14 -03:00
Sergio R. Caprile adfa1d0e20 fix mongoose.h 2023-07-25 13:22:41 -03:00
Sergio R. Caprile ef25c42e86 fix unit tests for older compilers 2023-07-25 11:55:01 -03:00
Sergio R. Caprile 91a638156c Merge branch 'master' of https://github.com/cesanta/mongoose 2023-07-25 11:37:01 -03:00
Sergio R. Caprile 26ce666938 Merge branch 'master' of https://github.com/cesanta/mongoose 2023-07-25 11:35:29 -03:00
cpq 7548af895b H7 mbedtls build fixed 2023-07-25 15:34:22 +01:00
Sergio R. Caprile 9548437a9e update unit tests for new API 2023-07-25 11:27:01 -03:00
cpq 5e50b7104c Unit test fixed 2023-07-25 15:26:47 +01:00
cpq d824cf7dbd Add mbedtls to H7 baremetal 2023-07-25 15:03:01 +01:00
cpq 0613cc62f4 TLS API refactor 2023-07-25 13:07:28 +01:00
Sergey Lyubka d9cbcb5fa8 Merge pull request #2312 from cesanta/arduino
no Docker, in-place build for tests
2023-07-22 01:09:35 +01:00
Sergio R. Caprile a4f5355161 no Docker, in-place build for tests 2023-07-21 16:45:14 -03:00
Sergey Lyubka a24b6c1909 Merge pull request #2309 from cesanta/arduino
move Arduino tests to its dir
2023-07-21 14:33:00 +01:00
Sergio R. Caprile fd7dc72410 Merge pull request #2310 from cesanta/am
Add arduino/w5500-mqtt example
2023-07-21 10:27:20 -03:00
cpq e4f4dfab3b Add arduino/w5500-mqtt example 2023-07-21 13:54:41 +01:00
Sergio R. Caprile 66010be5a6 move Arduino tests to its dir 2023-07-20 20:22:06 -03:00
Sergio R. Caprile 852c42a8f3 static clue 2023-07-20 16:34:13 -03:00
Sergio R. Caprile 132725de7b Merge pull request #2308 from cesanta/nano
AVR build fixes
2023-07-20 14:12:49 -03:00
cpq 0a761c09e3 AVR build fixes 2023-07-20 16:07:53 +01:00
Sergey Lyubka 09ce6ae1c5 Merge pull request #2307 from cesanta/codeql
add CodeQL analysis
2023-07-20 14:37:18 +01:00
Sergio R. Caprile 85e60ed858 force run 2023-07-19 16:21:14 -03:00
Sergio R. Caprile 86fd18324b codeql 2023-07-19 16:14:44 -03:00
Sergio R. Caprile fdf82e5d6c codeql 2023-07-19 15:10:28 -03:00
Sergio R. Caprile eb0b1580d1 nits 2023-07-19 14:53:29 -03:00
Sergio R. Caprile 2d33c545b8 codeql 2023-07-19 14:53:29 -03:00
Sergio R. Caprile cdaf523aa8 codeql 2023-07-19 14:53:29 -03:00
Sergio R. Caprile 3195f2cec2 codeql 2023-07-19 14:53:29 -03:00
cpq ea89a4afab Revert "Integration page"
This reverts commit 3415b62d2d.
2023-07-19 18:50:27 +01:00
cpq 3415b62d2d Integration page 2023-07-19 16:25:14 +01:00
Sergey Lyubka ee80822564 Merge pull request #2306 from cesanta/nits
Nits
2023-07-19 06:00:32 +01:00
cpq 3c9e218d07 Nits 2023-07-19 05:08:28 +01:00
Sergio R. Caprile 387a602885 Merge pull request #2304 from cesanta/addressing
Addressing
2023-07-18 17:30:35 -03:00
Sergio R. Caprile ef0ff43975 static clue 2023-07-18 17:23:50 -03:00
Sergio R. Caprile ae37407369 static clue 2023-07-18 17:10:05 -03:00
cpq e1c91b5763 Merge branch 'master' of github.com:cesanta/mongoose 2023-07-18 17:47:02 +01:00
cpq c49b472f92 Ignore DHCP for static IP config 2023-07-18 17:46:52 +01:00
Sergey Lyubka 0a6b1b9418 Merge pull request #2297 from cesanta/2251-implement-tcp-retransmit-in-mip
implemented tcp retransmission
2023-07-16 04:53:06 +01:00
Sergio R. Caprile ac40673a9e test for retransmitted segment 2023-07-12 11:22:35 -04:00
cpq 818f99b286 Update examples to adopt to the new iface state 2023-07-12 14:09:30 +01:00
robert 320da5e84c implemented tcp retransmission 2023-07-11 06:20:47 -04:00
Sergey Lyubka 6957c379d5 Merge pull request #2294 from cesanta/hdr
Fix #2292 - fail on stray \r character in headers
2023-07-09 23:08:22 +01:00
cpq 88e880f107 Fix #2292 - fail on stray \r character in headers 2023-07-09 18:18:18 +01:00
cpq bf8c4dfc83 Merge branch 'master' of github.com:cesanta/mongoose 2023-07-07 08:44:33 +01:00
cpq 5dff282132 Fix #2293 - stricter header validity check 2023-07-07 08:44:25 +01:00
Sergio R. Caprile 1148ac0ae4 Merge pull request #2289 from cesanta/acl
update `mg_check_ip_acl`
2023-07-05 18:06:28 -03:00
Sergio R. Caprile 59c46fc594 update 2023-07-05 17:49:18 -03:00
cpq 03a94e3a6a Use \r\n for log messages, for terminal apps 2023-07-05 10:26:27 +01:00
Sergio R. Caprile 5c8e05a4fc bump revno up 2023-07-04 12:18:09 -03:00
Sergio R. Caprile d791ce393b remove old examples 2023-07-03 18:34:18 -03:00
Sergio R. Caprile a452231086 Merge pull request #2285 from cesanta/stm32
Stm32
2023-07-03 18:29:30 -03:00
Sergio R. Caprile a392a19c9c name changes 2023-07-03 18:27:32 -03:00
Sergio R. Caprile a864c04702 uniformize 2023-07-03 16:40:33 -03:00
Sergio R. Caprile 8d2f7296a1 remove old examples 2023-07-03 14:19:28 -03:00
robert 040936eb47 fixed mqtt vc98 warning 2023-07-03 10:49:18 -04:00
Sergey Lyubka 101beec3ae Merge pull request #2284 from cesanta/enhance-mg_remove_double_dots
enhanced path sanitizing
2023-07-03 14:43:32 +01:00
robert 2f1a836bf0 enhanced path sanitizing 2023-07-03 06:14:50 -04:00
cpq 62f5e0a1a9 Add SNTP to the list 2023-07-01 20:28:28 +01:00
cpq cca771e70e Stricter check for test_dns_error() 2023-07-01 20:23:59 +01:00
cpq 0c7e6b548f Standardise margin 2023-06-30 11:53:34 +01:00
robertc2000 8b87e8aa92 Merge pull request #2282 from cesanta/paginator
Make paginator bit more compact
2023-06-30 12:17:35 +03:00
Sergey Lyubka 415bbf2932 Merge pull request #2278 from cesanta/httpheaders
fix header truncation
2023-06-29 22:59:51 +01:00
Sergey Lyubka 48df4f18cd Merge pull request #2283 from cesanta/fuzz
Extend fuzzer, access parsed fields
2023-06-29 22:58:21 +01:00
Sergio R. Caprile dc0f9aca4a access only when parsing is valid 2023-06-29 18:37:10 -03:00
cpq 1b9b8e3f6c Extend fuzzer, access parsed fields 2023-06-29 16:34:15 +01:00
cpq 5d10e9dbee Make paginator bit more compact 2023-06-29 13:33:36 +01:00
Sergio R. Caprile 0adc858c85 add unit test 2023-06-28 11:01:59 -03:00
Sergio R. Caprile eda1e8a402 fix header truncation 2023-06-27 14:56:04 -03:00
Sergio R. Caprile 4b0e34cdf4 Merge pull request #2277 from cesanta/httpcodes
Reinstall extra HTTP codes
2023-06-27 14:53:10 -03:00
Sergio R. Caprile c688687ff8 Reinstall extra HTTP codes
the extra strings are not polluting the binary much
2023-06-27 13:44:44 -03:00
Sergio R. Caprile d56e5bea62 Merge pull request #2274 from cesanta/mqtt-var-length-issue
fixed mqtt variable length header issue
2023-06-27 09:48:20 -03:00
robert aed7b6b37a fixed mqtt variable length header issue 2023-06-27 01:00:45 -04:00
Sergio R. Caprile 8114443e97 Merge pull request #2276 from cesanta/keil
Keil
2023-06-26 18:33:43 -03:00
scaprile 8ba0a33f84 add hw test support 2023-06-26 18:29:52 -03:00
Sergio R. Caprile 661ed61779 add hw test support for Keil 2023-06-26 18:20:38 -03:00
robert 427f95337f event table device-dashboard: fixed wrong api call 2023-06-26 10:31:55 -04:00
Sergey Lyubka fa8490fb4e Merge pull request #2272 from WKJay/master
add RT-Thread support
2023-06-26 14:37:49 +01:00
WKJay 5be1c6381b add RT-Thread support 2023-06-26 16:18:40 +08:00
Sergey Lyubka 6794a89f79 Merge pull request #2270 from cesanta/dhcp
improve DHCP in MIP
2023-06-24 13:09:40 +01:00
robert 8f19ee7b97 event table POST request 404 error 2023-06-23 11:35:42 -04:00
robert c63135ec7f device-dashboard: fixed event table POST request 2023-06-23 08:03:44 -04:00
robert 5c87f2f22c improved device dashboard events table 2023-06-23 07:21:24 -04:00
Sergio R. Caprile 6e6e34c423 improve DHCP in MIP 2023-06-22 18:55:48 -03:00
Sergio R. Caprile bb983fb4b2 update tutorial link 2023-06-22 11:00:37 -03:00
Sergio R. Caprile 285bbf50e9 Merge pull request #2268 from cesanta/cube
add more FreeRTOS+lwIP on Cube examples
2023-06-22 10:59:06 -03:00
Sergio R. Caprile c2ad2e1151 add more FreeRTOS+lwIP on Cube examples 2023-06-21 19:00:10 -03:00
Sergio R. Caprile 46e796ffaa Merge pull request #2267 from cesanta/cube
add more FreeRTOS on Cube examples
2023-06-21 11:34:20 -03:00
Sergio R. Caprile 3bf25d86f2 add tests for new Cube examples 2023-06-21 11:32:10 -03:00
Sergio R. Caprile 2dca4805b0 add more FreeRTOS on Cube examples 2023-06-21 11:21:24 -03:00
Sergio R. Caprile 54aa4678d1 Merge pull request #2265 from cesanta/cube
Cube
2023-06-20 14:48:36 -03:00
Sergio R. Caprile 3ab0e6cade add new Cube examples 2023-06-20 14:35:59 -03:00
Sergio R. Caprile 41e3b23253 rename tutorial 2023-06-20 12:04:38 -03:00
Sergio R. Caprile b00d780a50 Merge pull request #2261 from cesanta/dhcp
fix DHCP xid being ignored
2023-06-19 19:07:22 -03:00
Sergio R. Caprile 71b3e26232 fix DHCP xid being ignored 2023-06-19 18:50:51 -03:00
Sergio R. Caprile 40f28c02b9 Merge pull request #2262 from cesanta/cubetests
Cubetests
2023-06-19 17:13:05 -03:00
Sergio R. Caprile 362b22a673 Add hw test to cube examples 2023-06-19 16:58:37 -03:00
Sergio R. Caprile d6be9a2bba wip 2023-06-19 14:36:49 -03:00
Sergey Lyubka 6bfd113a87 Merge pull request #2260 from cesanta/2259-refactor-event-log-page-for-examplesdevice-dashboard
refactored device-dashboard code
2023-06-19 18:24:15 +01:00
robert b7f86ba743 refactored device-dashboard code 2023-06-19 06:28:00 -04:00
cpq 2bab274056 Fix build error 2023-06-16 15:57:18 +01:00
robertc2000 dd6bffdd0d Merge pull request #2256 from cesanta/2253-implement-event-log-page-for-examplesdevice-dashboard
implemented event log page for the device dashboard
2023-06-16 11:44:49 +03:00
robert bcc044eb92 implemented event log page for the device dashboard 2023-06-16 04:32:45 -04:00
robert a7f1982d03 improved device table style from the wifi-router-dashboard 2023-06-14 09:18:07 -04:00
Sergey Lyubka d013e0fc78 Merge pull request #2254 from cesanta/2249-implement-connected-devices-page-for-wifi-router-dashboard
implemented connected devices page
2023-06-14 11:17:06 +01:00
robert 6f69ad93c1 implemented connected devices page 2023-06-13 03:33:11 -04:00
Sergey Lyubka 7ea2093a91 Merge pull request #2250 from cesanta/2241-optimise-struct-mg_addr
optimized mg_addr structure
2023-06-09 14:54:27 +01:00
robert dd32deb2ad optimized mg_addr structure 2023-06-09 09:19:37 -04:00
Sergey Lyubka ad71fb321c Merge pull request #2247 from cesanta/2243-refacrtor-wifi-router-dashboard-example
refactored dashboards
2023-06-08 16:03:11 +01:00
Sergey Lyubka cc6c28a7ff Merge pull request #2248 from cesanta/F746TLS
Improve performance on F746 with MbedTLS
2023-06-08 16:02:24 +01:00
Sergio R. Caprile c763087462 improve performance 2023-06-08 11:29:51 -03:00
robert 63ff4ddea0 refactored dashboards 2023-06-08 10:25:59 -04:00
Sergey Lyubka 4178fc125d Merge pull request #2245 from cesanta/2235-fix
byte-encoded properties issue fixed
2023-06-08 13:32:23 +01:00
Sergio R. Caprile dcfa7ad657 Merge pull request #2246 from cesanta/tst
Improve MQTT tests resiliency
2023-06-07 19:01:23 -03:00
Sergio R. Caprile c061e60664 Improve MQTT tests resiliency 2023-06-07 18:34:55 -03:00
Ubuntu a0dd3ecd68 byte-encoded properties issue fixed 2023-06-07 10:13:21 -04:00
Sergio R. Caprile 0168a312f2 Merge pull request #2240 from cesanta/dhcp
Renew dhcp lease
2023-06-07 09:39:01 -03:00
cpq 92fe5a5b58 Renew dhcp lease 2023-06-07 13:33:28 +01:00
Sergey Lyubka 7f3d450b32 Merge pull request #2242 from cesanta/FEATURE_Add_tls_context_to_mg_mgr
Adding tls_ctx into mg_mgr for storing TLS context shared by all TLS session
2023-06-07 08:44:53 +01:00
Allan Park 8166339920 Adding tls_ctx into mg_mgr for storing TLS context shared by all TLS sessions 2023-06-07 10:21:46 +03:00
Sergio R. Caprile 57424d4a26 Merge pull request #2239 from cesanta/rlcomments
comment
2023-06-06 18:51:31 -03:00
Sergio R. Caprile 6511b6dd5d comment 2023-06-06 18:48:45 -03:00
Sergio R. Caprile 0625b97246 Merge pull request #2238 from cesanta/f7
Improve driver
2023-06-06 18:46:50 -03:00
Sergio R. Caprile 9f8ce82825 Improve driver 2023-06-06 18:32:11 -03:00
Sergey Lyubka c3c925dc84 Merge pull request #2230 from cesanta/rl
RL: fix accept() not looping on BSD_EWOULDBLOCK
2023-06-06 19:42:17 +01:00
Sergio R. Caprile 36f3703499 Merge pull request #2236 from cesanta/devsys
move dependencies
2023-06-06 14:47:27 -03:00
Sergio R. Caprile 6ae59c3711 move dependencies 2023-06-06 14:35:44 -03:00
Sergio R. Caprile c8f5f85635 Merge pull request #2234 from cesanta/tests
speed up MQTT tests
2023-06-06 10:58:54 -03:00
Sergio R. Caprile 4e5e186784 speed up MQTT tests 2023-06-06 10:44:18 -03:00
cpq 828f7a513c Merge branch 'master' of github.com:cesanta/mongoose 2023-06-06 08:38:18 +01:00
cpq 6085998d57 Proper handling {} in JSON array + unit test 2023-06-06 08:38:08 +01:00
Sergio R. Caprile 71e44d5b65 Merge pull request #2233 from cesanta/disambiguate
disambiguate comments
2023-06-05 18:57:25 -03:00
Sergio R. Caprile e762bbdbf8 disambiguate comments 2023-06-05 18:39:13 -03:00
cpq fe0d2272f6 Export mg_json_unescape() 2023-06-03 09:28:59 +01:00
Sergio R. Caprile e017b88adf Merge pull request #2228 from cesanta/tls
Add TLS=1 option
2023-06-02 14:33:34 -03:00
Sergey Lyubka 5f76d9cf33 Merge pull request #2231 from cesanta/Kfixs
standardize header inclusion
2023-06-02 17:35:56 +01:00
Sergio R. Caprile 85a5bdf4dc standardize header inclusion 2023-06-02 13:27:27 -03:00
Sergio R. Caprile 4abc34ffd1 fix accept() not looping on BSD_EWOULDBLOCK 2023-06-02 11:56:27 -03:00
Sergey Lyubka 250b1d511b Merge pull request #2229 from cesanta/mqttst
MQTT QoS + v5
2023-06-02 10:49:49 +01:00
cpq 59e3b0285a Add TLS=mbedtls option 2023-06-02 09:57:36 +01:00
cpq f961f993c8 FD_SETSIZE fix 2023-06-02 06:57:39 +01:00
cpq adf124c00c Dont check fd against FD_SETSIZE if POLL or EPOLL are used 2023-06-01 22:55:15 +01:00
Sergio R. Caprile f8579131e8 MQTT QoS + v5 2023-05-31 21:35:03 -03:00
Sergio R. Caprile 1dde754724 Merge pull request #2223 from cesanta/devdash
Fix Secure
2023-05-31 09:32:57 -03:00
Sergio R. Caprile 771696fda8 Fix Secure 2023-05-31 09:30:24 -03:00
Sergey Lyubka dd5f469c83 Merge pull request #2224 from cesanta/qos2
fix qos=2 handling
2023-05-31 12:07:10 +01:00
Sergio R. Caprile 6d3fda12cd Merge pull request #2225 from cesanta/Kfixs
Add HTTP_URL
2023-05-30 17:13:05 -03:00
Sergio R. Caprile dc32480563 Add HTTP_URL 2023-05-30 16:02:00 -03:00
Sergio R. Caprile 06ac9e18cb fix qos=2 handling 2023-05-30 14:49:32 -03:00
Sergio R. Caprile 0648943220 Merge pull request #2221 from cesanta/Kfixs
Kfixs
2023-05-29 21:30:41 -03:00
Sergio R. Caprile 7a4a82e872 Add HTTP_URL 2023-05-29 20:59:56 -03:00
Sergio R. Caprile 6c0c05cf7f Add missing headers 2023-05-29 14:34:51 -03:00
Sergio R. Caprile efd2db129f Merge pull request #2219 from cesanta/Kfixs
standardize header inclusion
2023-05-29 11:57:00 -03:00
Sergio R. Caprile f4f276f370 standardize header inclusion 2023-05-29 11:44:59 -03:00
cpq bff1462e9e Add config var size check 2023-05-28 17:06:47 +01:00
Sergio R. Caprile 4d06f33089 fixes 2023-05-26 20:58:54 -03:00
Sergio R. Caprile a433d9ef03 Merge pull request #2217 from cesanta/db
Refactor device-dashboard
2023-05-26 20:53:17 -03:00
Sergio R. Caprile 58606e1702 Refactor device-dashboard 2023-05-26 20:45:08 -03:00
Sergey Lyubka 1ccc9aa72e Merge pull request #2205 from cesanta/arbitrary_content-length
improve negative Content-length handling
2023-05-26 17:16:08 +01:00
cpq 49a9bb5a8b Refactor device-dashboard 2023-05-26 16:20:25 +01:00
Sergey Lyubka 380d6ac5c2 Merge pull request #2215 from cesanta/tlsdash
improve TLS behaviour in dashboard
2023-05-26 08:24:53 +01:00
Sergio R. Caprile 7d9f76df05 improve TLS behaviour in dashboard 2023-05-25 15:23:45 -03:00
Sergey Lyubka 96707acb4e Merge pull request #2214 from cesanta/makelinux
make more distro-friendly
2023-05-24 21:02:46 +01:00
cpq 4e504823c0 Merge branch 'master' of github.com:cesanta/mongoose 2023-05-24 21:02:23 +01:00
cpq 82faa2e9c9 Make UI independent of URI 2023-05-24 21:02:15 +01:00
Sergio R. Caprile 1df9cd8071 make more distro-friendly 2023-05-24 15:37:24 -03:00
Sergio R. Caprile a8d5e4dd28 Merge pull request #2213 from cesanta/nitpicks
nitpicks
2023-05-24 15:20:08 -03:00
Sergio R. Caprile e59ea39a33 nitpicks 2023-05-24 15:17:24 -03:00
Sergio R. Caprile 62d1dc1eea Merge pull request #2212 from cesanta/lwipstack
set a more lwIP-friendly minimum stack in FreeRTOS
2023-05-24 14:46:28 -03:00
Sergio R. Caprile 379af7dec2 set a more lwIP-friendly minimum stack in FreeRTOS 2023-05-24 11:30:01 -03:00
cpq 68a45adac7 Update logo 2023-05-24 13:52:52 +01:00
cpq 9ea8e29cb0 Merge branch 'master' of github.com:cesanta/mongoose 2023-05-24 13:22:18 +01:00
cpq 90092c0762 Make WebUI API calls relative, to work under any URI 2023-05-24 13:22:11 +01:00
Sergio R. Caprile 26699914cc improve negative Content-length handling 2023-05-22 17:40:08 -03:00
1755 changed files with 1012306 additions and 8325 deletions

No files matched your search

+35
View File
@@ -0,0 +1,35 @@
name: "CodeQL Scanning"
# https://github.com/github/codeql-action
on:
push:
paths:
- "Makefile"
- "mongoose.c"
- "mongoose.h"
- test/unit-test
- test/mip-test
env:
IPV6: 0
jobs:
CodeQL-Build:
runs-on: ubuntu-latest
strategy:
fail-fast: false
permissions:
security-events: write
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: Initialize CodeQL
uses: github/codeql-action/init@v2
with:
languages: cpp
- run: |
make test CC=gcc ASAN= ASAN_OPTIONS=
./test/setup_ga_network.sh && make mip_test CC=gcc ASAN= ASAN_OPTIONS=
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v2
+150 -46
View File
@@ -15,11 +15,22 @@ jobs:
matrix: matrix:
cc: [gcc, clang, g++, clang++] cc: [gcc, clang, g++, clang++]
target: [test, mip_test] target: [test, mip_test]
ssl: [MBEDTLS, OPENSSL] ssl: ["", MBEDTLS, OPENSSL]
name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }} select: ["-DMG_ENABLE_POLL=0 -DMG_ENABLE_EPOLL=0", "-DMG_ENABLE_POLL=1 -DMG_ENABLE_EPOLL=0", "-DMG_ENABLE_POLL=0 -DMG_ENABLE_EPOLL=1"]
exclude:
- ssl: MBEDTLS
select: "-DMG_ENABLE_POLL=0 -DMG_ENABLE_EPOLL=0"
- ssl: MBEDTLS
select: "-DMG_ENABLE_POLL=1 -DMG_ENABLE_EPOLL=0"
- ssl: OPENSSL
select: "-DMG_ENABLE_POLL=0 -DMG_ENABLE_EPOLL=0"
- ssl: OPENSSL
select: "-DMG_ENABLE_POLL=1 -DMG_ENABLE_EPOLL=0"
name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
env: env:
CC: ${{ matrix.cc }} CC: ${{ matrix.cc }}
SSL: ${{ matrix.ssl }} SSL: ${{ matrix.ssl }}
TFLAGS: ${{ matrix.select }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
@@ -54,7 +65,21 @@ jobs:
run: make unamalgamated && make valgrind && make mg_prefix run: make unamalgamated && make valgrind && make mg_prefix
macos: macos:
runs-on: macos-latest runs-on: macos-latest
env: { HOMEBREW_NO_AUTO_UPDATE: 1 } strategy:
fail-fast: false
matrix:
ssl: ["", MBEDTLS, OPENSSL]
select: [-DMG_ENABLE_POLL=0, -DMG_ENABLE_POLL=1]
exclude:
- ssl: MBEDTLS
select: -DMG_ENABLE_POLL=0
- ssl: OPENSSL
select: -DMG_ENABLE_POLL=0
name: macos SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
env:
SSL: ${{ matrix.ssl }}
TFLAGS: ${{ matrix.select }} -DNO_SNTP_CHECK
HOMEBREW_NO_AUTO_UPDATE: 1
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
@@ -62,27 +87,28 @@ jobs:
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: brew install jq mbedtls openssl run: brew install jq mbedtls openssl
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make test upload-coverage TFLAGS=-DNO_SNTP_CHECK SSL=OPENSSL ASAN_OPTIONS= OPENSSL=`echo /usr/local/Cellar/openssl*/*` run: make test ASAN_OPTIONS= MBEDTLS=`echo /usr/local/Cellar/mbedtls*/*` OPENSSL=`echo /usr/local/Cellar/openssl*/*`
- if: ${{ env.GO == 1 }}
run: make test SSL=MBEDTLS TFLAGS=-DNO_SNTP_CHECK ASAN_OPTIONS= MBEDTLS=`echo /usr/local/Cellar/mbedtls*/*`
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make mg_prefix run: make mg_prefix
windows: windows:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [vc98, vc17, vc22, mingw, mingw++]
select: [-DMG_ENABLE_POLL=0, -DMG_ENABLE_POLL=1]
exclude:
- target: vc98
select: -DMG_ENABLE_POLL=1
name: windows ${{ matrix.target }} TFLAGS=${{ matrix.select }}
env:
TFLAGS: ${{ matrix.select }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^test|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi - run: if ./test/match_changed_files.sh '^test|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make vc98 vc17 vc22 mingw mingw++ run: make ${{ matrix.target }}
arduino-xiao:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^examples/arduino|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: make arduino-xiao
arm: arm:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -99,8 +125,8 @@ jobs:
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- run: sudo apt -y install libmbedtls-dev libpcap-dev - run: sudo apt -y install libmbedtls-dev libpcap-dev
- run: make clean examples - run: make examples
- run: make clean test MG_ENABLE_POLL=1 - run: make clean
examples_win: examples_win:
runs-on: windows-latest runs-on: windows-latest
steps: steps:
@@ -122,71 +148,145 @@ jobs:
- run: make examples_mac - run: make examples_mac
- run: make clean_examples_mac - run: make clean_examples_mac
test_f7: test_f7:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh "^src|examples/stm32/nucleo-f7" ; then echo GO=1 >> $GITHUB_ENV ; fi - run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-f7.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-f746zg-baremetal test VCON_API_KEY=${{secrets.VCON_API_KEY}} run: make -C examples/stm32/nucleo-f746zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-f746zg-freertos test VCON_API_KEY=${{secrets.VCON_API_KEY}} run: make -C examples/stm32/nucleo-f746zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-f746zg-freertos-tcp test VCON_API_KEY=${{secrets.VCON_API_KEY}} run: make -C examples/stm32/nucleo-f746zg-make-freertos-tcp test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: echo GO=0 >> $GITHUB_ENV ; if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f7.*-cube-'; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
- if: ${{ env.GO == 1 }}
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
- if: ${{ env.GO == 1 }}
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
test_f4: test_f4:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh "^src|examples/stm32/nucleo-f4" ; then echo GO=1 >> $GITHUB_ENV ; fi - run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-f4.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-f429zi-baremetal test VCON_API_KEY=${{secrets.VCON_API_KEY}} run: make -C examples/stm32/nucleo-f429zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-f429zi-freertos test VCON_API_KEY=${{secrets.VCON_API_KEY}} run: make -C examples/stm32/nucleo-f429zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: echo GO=0 >> $GITHUB_ENV ; if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f4.*-cube-'; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
- if: ${{ env.GO == 1 }}
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
- if: ${{ env.GO == 1 }}
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
test_h7: test_h7:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: { fetch-depth: 3 } with: { fetch-depth: 3 }
- run: if ./test/match_changed_files.sh "^src|examples/stm32/nucleo-h7" ; then echo GO=1 >> $GITHUB_ENV ; fi - run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h7.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-h743zi-baremetal test VCON_API_KEY=${{secrets.VCON_API_KEY}} run: make -C examples/stm32/nucleo-h743zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-h743zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: echo GO=0 >> $GITHUB_ENV ; if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h7.*-cube-'; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
- if: ${{ env.GO == 1 }}
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
test_h5:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h5.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
- if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-h563zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
# - if: ${{ env.GO == 1 }}
# WIP run: make -C examples/stm32/nucleo-h563zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_rt1020:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- run: if ./test/match_changed_files.sh "^src|examples/nxp/rt1020.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
- if: ${{ env.GO == 1 }}
run: make -C examples/nxp/rt1020-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }}
run: make -C examples/nxp/rt1020-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_tm4c: test_tm4c:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh "^src|examples/ti/ek-tm4c" ; then echo GO=1 >> $GITHUB_ENV ; fi - run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/ti/ek-tm4c" ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
- run: echo GO=0 > $GITHUB_ENV ; if ./test/match_changed_files.sh "^src|examples/ti/ek-tm4c1294xl-baremetal" ; then echo GO=1 > $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/ti/ek-tm4c1294xl-baremetal test VCON_API_KEY=${{secrets.VCON_API_KEY}} run: make -C examples/ti/ek-tm4c1294xl-baremetal test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: echo GO=0 > $GITHUB_ENV ; if ./test/match_changed_files.sh "^src|examples/ti/ek-tm4c1294xl-freertos" ; then echo GO=1 > $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/ti/ek-tm4c1294xl-freertos test VCON_API_KEY=${{secrets.VCON_API_KEY}} run: make -C examples/ti/ek-tm4c1294xl-freertos test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_same54:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/microchip/same54-xpro" ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
- if: ${{ env.GO == 1 }}
run: make -C examples/microchip/same54-xpro/device-dashboard test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }}
run: make -C examples/microchip/same54-xpro/mqtt-client test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_pico_w5500: test_pico_w5500:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: { fetch-depth: 2 } with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh "^src|examples/rp2040/pico-w5500" ; then echo GO=1 >> $GITHUB_ENV ; fi - run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/rp2040/pico-w5500" ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/rp2040/pico-w5500 test VCON_API_KEY=${{secrets.VCON_API_KEY}} run: make -C examples/rp2040/pico-w5500 test VCON_API_KEY=${{secrets.VCON_API_KEY}}
arduino:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^examples/arduino|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: make -C examples/arduino arduino-xiao
# - if: ${{ env.GO == 1 }}
# run: make -C examples/arduino arduino-nano
- if: ${{ env.GO == 1 }}
run: make -C examples/arduino clean
matrix_examples: matrix_examples:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
@@ -196,18 +296,9 @@ jobs:
- path: esp32/device-dashboard - path: esp32/device-dashboard
- path: esp32/uart-bridge - path: esp32/uart-bridge
- path: esp8266/http-client-server - path: esp8266/http-client-server
- path: stm32/nucleo-h743zi-baremetal - path: microchip/same54-xpro/device-dashboard
- path: stm32/stm32-nucleo-f746z - path: microchip/same54-xpro/mqtt-client
- path: stm32/stm32-nucleo-f746zg - path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
- path: stm32/stm32-nucleo-f429z
- path: stm32/stm32-nucleo-h743z
- path: stm32/nucleo-f429zi-baremetal
- path: stm32/nucleo-f429zi-freertos
- path: stm32/nucleo-f429zi-rndis
- path: stm32/nucleo-f746zg-baremetal
- path: stm32/nucleo-f746zg-freertos
- path: stm32/nucleo-f746zg-freertos-tcp
- path: stm32/nucleo-f746zg-rndis
- path: nxp/nxp-mimxrt1020-azurertos - path: nxp/nxp-mimxrt1020-azurertos
- path: nxp/nxp-frdmk66f-freertos - path: nxp/nxp-frdmk66f-freertos
- path: nxp/nxp-lpcxpresso54s018m-freertos - path: nxp/nxp-lpcxpresso54s018m-freertos
@@ -225,16 +316,29 @@ jobs:
- path: nxp/nxp-lpcxpresso54608-lwip-freertos - path: nxp/nxp-lpcxpresso54608-lwip-freertos
- path: nxp/nxp-lpcxpresso54618-lwip-freertos - path: nxp/nxp-lpcxpresso54618-lwip-freertos
- path: nxp/nxp-lpcxpresso54628-lwip-freertos - path: nxp/nxp-lpcxpresso54628-lwip-freertos
- path: nxp/rt1020-evk-make-baremetal-builtin
- path: nxp/rt1020-evk-make-freertos-builtin
- path: nxp/nxp-twrk65f180m-lwip-freertos - path: nxp/nxp-twrk65f180m-lwip-freertos
- path: nxp/nxp-twrkv58f220m-lwip-freertos - path: nxp/nxp-twrkv58f220m-lwip-freertos
- path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
- path: ti/ti-ek-tm4c1294xl-http-server
- path: ti/ek-tm4c1294xl-baremetal
- path: ti/ek-tm4c1294xl-freertos
- path: rp2040/pico-rmii - path: rp2040/pico-rmii
- path: rp2040/pico-rndis-dashboard - path: rp2040/pico-rndis-dashboard
- path: rp2040/pico-w - path: rp2040/pico-w
- path: rp2040/pico-w5500 - path: rp2040/pico-w5500
- path: stm32/nucleo-f429zi-make-baremetal-builtin
- path: stm32/nucleo-f429zi-make-freertos-builtin
- path: stm32/nucleo-f429zi-make-baremetal-builtin-rndis
- path: stm32/nucleo-f746zg-make-baremetal-builtin
- path: stm32/nucleo-f746zg-make-freertos-builtin
- path: stm32/nucleo-f746zg-make-freertos-tcp
- path: stm32/nucleo-f746zg-make-baremetal-builtin-rndis
- path: stm32/nucleo-g031-make-baremetal-builtin
- path: stm32/nucleo-h563zi-make-baremetal-builtin
- path: stm32/nucleo-h563zi-make-freertos-builtin
- path: stm32/nucleo-h743zi-make-baremetal-builtin
- path: stm32/nucleo-h743zi-make-freertos-builtin
- path: ti/ti-ek-tm4c1294xl-http-server
- path: ti/ek-tm4c1294xl-baremetal
- path: ti/ek-tm4c1294xl-freertos
name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
+21 -39
View File
@@ -1,5 +1,5 @@
SRCS = mongoose.c test/unit_test.c test/packed_fs.c SRCS = mongoose.c test/unit_test.c test/packed_fs.c
HDRS = $(wildcard src/*.h) $(wildcard src/tcpip/*.h) HDRS = $(wildcard src/*.h) $(wildcard src/drivers/*.h)
DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_SSI=1 -DMG_ENABLE_ASSERT=1 DEFS ?= -DMG_MAX_HTTP_HEADERS=7 -DMG_ENABLE_LINES -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_SSI=1 -DMG_ENABLE_ASSERT=1
WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef WARN ?= -pedantic -W -Wall -Werror -Wshadow -Wdouble-promotion -fno-common -Wconversion -Wundef
OPTS ?= -O3 -g3 OPTS ?= -O3 -g3
@@ -7,7 +7,8 @@ INCS ?= -Isrc -I.
SSL ?= SSL ?=
CWD ?= $(realpath $(CURDIR)) CWD ?= $(realpath $(CURDIR))
ENV ?= -e Tmp=. -e WINEDEBUG=-all ENV ?= -e Tmp=. -e WINEDEBUG=-all
DOCKER ?= docker run --platform linux/amd64 --rm $(ENV) -v $(CWD):$(CWD) -w $(CWD) DOCKER_BIN ?= docker
DOCKER ?= $(DOCKER_BIN) run --platform linux/amd64 --rm $(ENV) -v $(CWD):$(CWD) -w $(CWD)
VCFLAGS = /nologo /W3 /O2 /MD /I. $(DEFS) $(TFLAGS) VCFLAGS = /nologo /W3 /O2 /MD /I. $(DEFS) $(TFLAGS)
IPV6 ?= 1 IPV6 ?= 1
ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common
@@ -17,6 +18,8 @@ EXAMPLES_MAC := $(filter-out examples/mip-pcap/ examples/mip-tap/, $(EXAMPLES))
EXAMPLES_WIN := $(dir $(wildcard examples/device-dashboard/Makefile) $(wildcard examples/file-*/Makefile) $(wildcard examples/http-*/Makefile) $(wildcard examples/mqtt-*/Makefile) $(wildcard examples/websocket-*/Makefile) $(wildcard examples/webui-*/Makefile)) EXAMPLES_WIN := $(dir $(wildcard examples/device-dashboard/Makefile) $(wildcard examples/file-*/Makefile) $(wildcard examples/http-*/Makefile) $(wildcard examples/mqtt-*/Makefile) $(wildcard examples/websocket-*/Makefile) $(wildcard examples/webui-*/Makefile))
EXAMPLES_EMBEDDED := $(filter-out $(wildcard examples/zephyr/*/), $(dir $(wildcard examples/*/*/Makefile))) EXAMPLES_EMBEDDED := $(filter-out $(wildcard examples/zephyr/*/), $(dir $(wildcard examples/*/*/Makefile)))
PREFIX ?= /usr/local PREFIX ?= /usr/local
LIBDIR ?= $(PREFIX)/lib
INCLUDEDIR ?= $(PREFIX)/include
VERSION ?= $(shell cut -d'"' -f2 src/version.h) VERSION ?= $(shell cut -d'"' -f2 src/version.h)
COMMON_CFLAGS ?= $(C_WARN) $(WARN) $(INCS) $(DEFS) -DMG_ENABLE_IPV6=$(IPV6) $(TFLAGS) -pthread COMMON_CFLAGS ?= $(C_WARN) $(WARN) $(INCS) $(DEFS) -DMG_ENABLE_IPV6=$(IPV6) $(TFLAGS) -pthread
CFLAGS ?= $(OPTS) $(ASAN) $(COMMON_CFLAGS) CFLAGS ?= $(OPTS) $(ASAN) $(COMMON_CFLAGS)
@@ -34,14 +37,14 @@ endif
ifeq "$(SSL)" "MBEDTLS" ifeq "$(SSL)" "MBEDTLS"
MBEDTLS ?= /usr/local MBEDTLS ?= /usr/local
CFLAGS += -DMG_ENABLE_MBEDTLS=1 -I$(MBEDTLS)/include -I/usr/include CFLAGS += -DMG_TLS=MG_TLS_MBED -I$(MBEDTLS)/include -I/usr/include
LDFLAGS ?= -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509 LDFLAGS += -L$(MBEDTLS)/lib -lmbedtls -lmbedcrypto -lmbedx509
endif endif
ifeq "$(SSL)" "OPENSSL" ifeq "$(SSL)" "OPENSSL"
OPENSSL ?= /usr/local OPENSSL ?= /usr/local
CFLAGS += -DMG_ENABLE_OPENSSL=1 -I$(OPENSSL)/include CFLAGS += -DMG_TLS=MG_TLS_OPENSSL -I$(OPENSSL)/include
LDFLAGS ?= -L$(OPENSSL)/lib -lssl -lcrypto LDFLAGS += -L$(OPENSSL)/lib -lssl -lcrypto
endif endif
all: all:
@@ -72,16 +75,9 @@ examples_win:
clean_examples_win: clean_examples_win:
$(foreach X, $(EXAMPLES_WIN), $(MAKE) -C $(X) clean &) $(foreach X, $(EXAMPLES_WIN), $(MAKE) -C $(X) clean &)
test/packed_fs.c: Makefile src/ssi.h test/fuzz.c test/data/a.txt test/packed_fs.c: Makefile src/ssi.h test/fuzz.c test/data/a.txt test/data/ca.pem
$(CC) $(CFLAGS) test/pack.c -o pack $(CC) $(CFLAGS) test/pack.c -o pack
$(RUN) ./pack Makefile src/ssi.h test/fuzz.c test/data/a.txt test/data/range.txt > $@ $(RUN) ./pack Makefile src/ssi.h test/fuzz.c test/data/a.txt test/data/range.txt test/data/ca.pem > $@
DIR ?= test/data
OUT ?= packed_fs.c
mkfs:
$(CC) $(CFLAGS) test/pack.c -o pack
$(RUN) ./pack -s $(DIR) `find $(DIR) -type f` > $(OUT)
# find $(DIR) -type f | sed -e s,^$(DIR),,g -e s,^/,,g
# Check that all external (exported) symbols have "mg_" prefix # Check that all external (exported) symbols have "mg_" prefix
mg_prefix: mongoose.c mongoose.h mg_prefix: mongoose.c mongoose.h
@@ -93,7 +89,7 @@ musl: WARN += -Wno-sign-conversion
musl: CC = $(DOCKER) mdashnet/cc1 gcc musl: CC = $(DOCKER) mdashnet/cc1 gcc
musl: RUN = $(DOCKER) mdashnet/cc1 musl: RUN = $(DOCKER) mdashnet/cc1
# Make sure we can build from an unamalgamated sources # Make sure we can build from unamalgamated sources.
unamalgamated: $(HDRS) Makefile test/packed_fs.c unamalgamated: $(HDRS) Makefile test/packed_fs.c
$(CC) src/*.c test/packed_fs.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -g -o unit_test $(CC) src/*.c test/packed_fs.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -g -o unit_test
@@ -159,46 +155,32 @@ mingw: Makefile mongoose.h $(SRCS)
$(DOCKER) mdashnet/mingw x86_64-w64-mingw32-gcc $(SRCS) -W -Wall -Werror -I. $(DEFS) -lwsock32 -o $@.exe $(DOCKER) mdashnet/mingw x86_64-w64-mingw32-gcc $(SRCS) -W -Wall -Werror -I. $(DEFS) -lwsock32 -o $@.exe
$(DOCKER) mdashnet/mingw wine64 $@.exe $(DOCKER) mdashnet/mingw wine64 $@.exe
arduino: ENV = -v $(CWD)/arduino:/root
arduino:
curl -sL http://downloads.arduino.cc/arduino-1.8.19-linux64.tar.xz | unxz | tar -xf -
mv arduino-* $@
$(DOCKER) mdashnet/cc2 ./arduino/arduino --pref "boardsmanager.additional.urls=https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json" --save-prefs
$(DOCKER) mdashnet/cc2 ./arduino/arduino --pref "compiler.warning_level=all" --save-prefs
$(DOCKER) mdashnet/cc2 ./arduino/arduino --install-boards Seeeduino:samd
arduino-xiao: ENV = -v $(CWD)/arduino:/root
arduino-xiao: arduino
rm -rf tmp; mkdir tmp
cp examples/arduino/w5500/w5500.ino tmp/tmp.ino
cp mongoose.c mongoose.h examples/arduino/w5500/mongoose_custom.h tmp/
$(DOCKER) mdashnet/cc2 ./arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 tmp/tmp.ino
mingw++: Makefile mongoose.h $(SRCS) mingw++: Makefile mongoose.h $(SRCS)
$(DOCKER) mdashnet/mingw x86_64-w64-mingw32-g++ $(SRCS) -W -Wall -Werror -I. $(DEFS) -lwsock32 -o $@.exe $(DOCKER) mdashnet/mingw x86_64-w64-mingw32-g++ $(SRCS) -W -Wall -Werror -I. $(DEFS) -lwsock32 -o $@.exe
linux-libs: CFLAGS += -fPIC linux-libs: CFLAGS += -fPIC
linux-libs: LDFLAGS += -Wl,-soname,libmongoose.so.$(VERSION)
linux-libs: mongoose.o linux-libs: mongoose.o
$(CC) mongoose.o $(LDFLAGS) -shared -o libmongoose.so.$(VERSION) $(CC) mongoose.o $(LDFLAGS) -shared -o libmongoose.so.$(VERSION)
$(AR) rcs libmongoose.a mongoose.o $(AR) rcs libmongoose.a mongoose.o
install: linux-libs install: linux-libs
install -Dm644 libmongoose.a libmongoose.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib install -Dm644 libmongoose.a libmongoose.so.$(VERSION) $(DESTDIR)$(LIBDIR)
ln -s libmongoose.so.$(VERSION) $(DESTDIR)$(PREFIX)/lib/libmongoose.so ln -s libmongoose.so.$(VERSION) $(DESTDIR)$(LIBDIR)/libmongoose.so
install -Dm644 mongoose.h $(DESTDIR)$(PREFIX)/include/mongoose.h install -Dm644 mongoose.h $(DESTDIR)$(INCLUDEDIR)/mongoose.h
uninstall: uninstall:
rm -rf $(DESTDIR)$(PREFIX)/lib/libmongoose.a $(DESTDIR)$(PREFIX)/lib/libmongoose.so.$(VERSION) $(DESTDIR)$(PREFIX)/include/mongoose.h $(DESTDIR)$(PREFIX)/lib/libmongoose.so rm -rf $(DESTDIR)$(LIBDIR)/libmongoose.a $(DESTDIR)$(LIBDIR)/libmongoose.so.$(VERSION) $(DESTDIR)$(INCLUDEDIR)/mongoose.h $(DESTDIR)$(LIBDIR)/libmongoose.so
mongoose.c: Makefile $(wildcard src/*.c) $(wildcard src/tcpip/*.c) mongoose.c: Makefile $(wildcard src/*.c) $(wildcard src/drivers/*.c)
(cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/tcpip/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ (cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
mongoose.h: $(HDRS) Makefile mongoose.h: $(HDRS) Makefile
(cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_*.h src/config.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/tcpip/tcpip.h src/tcpip/driver_*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@ (cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/device.h src/net_builtin.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
clean: clean_examples clean_embedded clean: clean_examples clean_embedded
rm -rf $(PROG) *.exe *.o *.dSYM *_test* ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* test/packed_fs.c pack arduino tmp rm -rf $(PROG) *.exe *.o *.dSYM *_test* ut fuzzer *.gcov *.gcno *.gcda *.obj *.exe *.ilk *.pdb slow-unit* _CL_* infer-out data.txt crash-* test/packed_fs.c pack
#find examples -maxdepth 3 -name zephyr -prune -o -name Makefile -print | xargs dirname | xargs -n1 make clean -C #find examples -maxdepth 3 -name zephyr -prune -o -name Makefile -print | xargs dirname | xargs -n1 make clean -C
clean_embedded: clean_embedded:
+8 -7
View File
@@ -18,7 +18,7 @@ robust, and easy. Features include:
- works on STM32, NXP, ESP32, NRF52, TI, Microchip, and other - works on STM32, NXP, ESP32, NRF52, TI, Microchip, and other
- write code once - and it'll work everywhere - write code once - and it'll work everywhere
- ideal for the unification of the network infrastructure code across company - ideal for the unification of the network infrastructure code across company
- Built-in protocols: plain TCP/UDP, HTTP, MQTT, Websocket - Built-in protocols: plain TCP/UDP, SNTP, HTTP, MQTT, Websocket
- SSL/TLS support: mbedTLS, OpenSSL or custom (via API) - SSL/TLS support: mbedTLS, OpenSSL or custom (via API)
- Asynchronous DNS resolver - Asynchronous DNS resolver
- Tiny static and run-time footprint - Tiny static and run-time footprint
@@ -26,11 +26,12 @@ robust, and easy. Features include:
- Very easy to integrate: just copy `mongoose.c` and `mongoose.h` files to your source tree. See - Very easy to integrate: just copy `mongoose.c` and `mongoose.h` files to your source tree. See
[exact steps](https://mongoose.ws/documentation/#2-minute-integration-guide) [exact steps](https://mongoose.ws/documentation/#2-minute-integration-guide)
- Works with any network stack with socket API, like LwIP or FreeRTOS-Plus-TCP - Works with any network stack with socket API, like LwIP or FreeRTOS-Plus-TCP
- Provides a built-in TCP/IP stack with drivers for bare metal or RTOS systems - Built-in TCP/IP stack with drivers for bare metal or RTOS systems
- A complete Web device dashboard - Available drivers: STM32 F4, F7, H5, H7; NXP RT1020; TI TM4C; Microchip SAME54; Wiznet W5500
[bare metal example on Nucleo-F429ZI](examples/stm32/nucleo-f429zi-baremetal) - A complete Web device dashboard on bare metal
is only 6 files [Nucleo-F429ZI](examples/stm32/nucleo-f429zi-baremetal) is only 6 files
- For comparison, a CubeIDE generated HTTP example is 400+ files - For comparison, a CubeIDE generated HTTP example is 400+ files
- Built-in firmware updates for STM32H5, STM32H7, and more coming
- Detailed [user guide, API reference and tons of tutorials](https://mongoose.ws/documentation/) available - Detailed [user guide, API reference and tons of tutorials](https://mongoose.ws/documentation/) available
@@ -44,8 +45,8 @@ robust, and easy. Features include:
- See [Case Studies](https://mongoose.ws/case-studies/) from our respected - See [Case Studies](https://mongoose.ws/case-studies/) from our respected
customers like [Schneider Electric](https://mongoose.ws/case-studies/schneider-electric/) (industrial automation), [Broadcom](https://mongoose.ws/case-studies/broadcom/) (semiconductors), [Pilz](https://mongoose.ws/case-studies/pilz/) (industrial automation), and others customers like [Schneider Electric](https://mongoose.ws/case-studies/schneider-electric/) (industrial automation), [Broadcom](https://mongoose.ws/case-studies/broadcom/) (semiconductors), [Pilz](https://mongoose.ws/case-studies/pilz/) (industrial automation), and others
- See [Testimonials](https://mongoose.ws/testimonials/) from engineers that integrated Mongoose in their commercial products - See [Testimonials](https://mongoose.ws/testimonials/) from engineers that integrated Mongoose in their commercial products
- We provide [Evaluation and Commercial licensing](https://mongoose.ws/licensing/), [support](https://mongoose.ws/support/), consultancy and integration - We provide [Evaluation and Commercial licensing](https://mongoose.ws/licensing/), [support](https://mongoose.ws/support/), consultancy and [integration
assistance - don't hesitate to [contact us](https://mongoose.ws/contact/) services](https://mongoose.ws/integration/) - don't hesitate to [contact us](https://mongoose.ws/contact/)
# Security # Security
+28
View File
@@ -0,0 +1,28 @@
all:
example:
true
arduino: ENV = -v $(CWD):/root
arduino:
curl -sL http://downloads.arduino.cc/arduino-1.8.19-linux64.tar.xz | unxz | tar -xf -
mv arduino-* $@
arduino-xiao-board:
./arduino/arduino --pref "boardsmanager.additional.urls=https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json" --save-prefs
./arduino/arduino --pref "compiler.warning_level=all" --save-prefs
./arduino/arduino --install-boards Seeeduino:samd
arduino-xiao: arduino arduino-xiao-board
cd w5500-http && ../arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-http.ino
cd w5500-mqtt && ../arduino/arduino --verbose --verify --board Seeeduino:samd:seeed_XIAO_m0 w5500-mqtt.ino
arduino-nano: arduino
cd w5500-http && ../arduino/arduino --pref compiler.cpp.extra_flags=-DMG_ENABLE_LOG=0 --verbose --verify --board arduino:avr:nano w5500-http.ino
cd w5500-mqtt && ../arduino/arduino --pref compiler.cpp.extra_flags=-DMG_ENABLE_LOG=0 --verbose --verify --board arduino:avr:nano w5500-mqtt.ino
clean:
rm -rf arduino
cleanall: clean #otherwise some parts of xiao-board installation will fail as already installed
rm -rf ~/.arduino15 ~/.oracle_jre_usage
File renamed without changes.
File renamed without changes.
@@ -11,4 +11,5 @@
#define MG_ENABLE_SOCKET 0 #define MG_ENABLE_SOCKET 0
#define MG_ENABLE_TCPIP 1 #define MG_ENABLE_TCPIP 1
#define mkdir(a, b) (-1) #define mkdir(a, b) (-1)
#define MG_IO_SIZE 128
//#define MG_ENABLE_LOG 0 //#define MG_ENABLE_LOG 0
File renamed without changes.
@@ -0,0 +1,15 @@
#pragma once
#include "Arduino.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <time.h>
#define MG_ARCH MG_ARCH_CUSTOM
#define MG_ENABLE_SOCKET 0
#define MG_ENABLE_TCPIP 1
#define mkdir(a, b) (-1)
#define MG_IO_SIZE 512
//#define MG_ENABLE_LOG 0
+58
View File
@@ -0,0 +1,58 @@
#include "net.h"
#define MQTT_SERVER "mqtt://broker.hivemq.com:1883"
struct mg_connection *mqtt_connection;
const char *mqtt_subscribe_topic = "mg/rx";
const char *mqtt_publish_topic = "mg/tx";
struct mg_mgr mgr; // Mongoose event manager
struct mg_tcpip_if mif = {.mac = {2, 0, 1, 2, 3, 5}}; // network interface
void mqtt_publish(const char *message) {
struct mg_mqtt_opts opts = {};
opts.topic = mg_str(mqtt_publish_topic);
opts.message = mg_str(message);
if (mqtt_connection) mg_mqtt_pub(mqtt_connection, &opts);
}
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_MQTT_OPEN) {
MG_INFO(("%lu CONNECTED to %s", c->id, MQTT_SERVER));
struct mg_mqtt_opts opts = {};
opts.topic = mg_str(mqtt_subscribe_topic);
mg_mqtt_sub(c, &opts);
MG_INFO(("%lu SUBSCRIBED to %s", c->id, mqtt_subscribe_topic));
} else if (ev == MG_EV_MQTT_MSG) {
// Received MQTT message
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
MG_INFO(("%lu RECEIVED %.*s <- %.*s", c->id, (int) mm->data.len,
mm->data.ptr, (int) mm->topic.len, mm->topic.ptr));
exec_command(mm->data.ptr, mm->data.len);
} else if (ev == MG_EV_CLOSE) {
MG_INFO(("%lu CLOSED", c->id));
mqtt_connection = NULL;
}
}
static void timer_fn(void *arg) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
struct mg_mqtt_opts opts;
memset(&opts, 0, sizeof(opts));
opts.clean = true;
if (mif.state == MG_TCPIP_STATE_READY && mqtt_connection == NULL) {
mqtt_connection = mg_mqtt_connect(mgr, MQTT_SERVER, &opts, fn, NULL);
}
// MG_INFO(("Eth: %d, MQTT: %p", mif.state, mqtt_connection));
}
void net_init(void) {
// Set Mongoose logging function to a serial print
mg_mgr_init(&mgr);
mif.driver = &mg_tcpip_driver_w5500;
mif.driver_data = &spi;
mg_tcpip_init(&mgr, &mif);
// Start a timer that keeps MQTT connection alive
mg_timer_add(&mgr, 3000, MG_TIMER_REPEAT, timer_fn, &mgr);
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include "mongoose.h"
#ifdef __cplusplus
extern "C" {
#endif
extern struct mg_tcpip_spi spi;
extern struct mg_mgr mgr;
void mqtt_publish(const char *message);
void net_init(void);
void exec_command(const char *req, size_t req_len);
#ifdef __cplusplus
}
#endif
@@ -0,0 +1,51 @@
#include <SPI.h>
#include "net.h"
#define SS_PIN 17 // Slave select pin
struct mg_tcpip_spi spi = {
NULL, // SPI data
[](void *) { digitalWrite(SS_PIN, LOW); }, // Begin transaction
[](void *) { digitalWrite(SS_PIN, HIGH); }, // End transaction
[](void *, uint8_t c) { return SPI.transfer(c); }, // Execute transaction
};
void exec_command(const char *req, size_t req_len) {
char res[100];
if (req_len == 2 && strncmp(req, "on", req_len) == 0) {
digitalWrite(LED_BUILTIN, true);
snprintf(res, sizeof(res), "LED on");
} else if (req_len == 3 && strncmp(req, "off", req_len) == 0) {
digitalWrite(LED_BUILTIN, false);
snprintf(res, sizeof(res), "LED off");
} else {
snprintf(res, sizeof(res), "Unknown command: [%.*s]", (int) req_len, req);
}
Serial.println(res);
mqtt_publish(res);
}
static void process_input(char c) {
static char buf[100];
static size_t len = 0;
char response[100];
if (c != '\n' && c != '\0') buf[len++] = c; // Append to the buffer
if (len >= sizeof(buf)) len = 0; // On overflow, reset
if (c == '\n' && len > 0) {
exec_command(buf, len);
len = 0;
}
}
void setup() {
Serial.begin(115200);
SPI.begin();
pinMode(SS_PIN, OUTPUT);
pinMode(LED_BUILTIN, OUTPUT);
mg_log_set_fn([](char ch, void *) { Serial.print(ch); }, NULL);
net_init();
}
void loop() {
if (Serial.available()) process_input(Serial.read());
mg_mgr_poll(&mgr, 1);
}
+39 -31
View File
@@ -1,47 +1,55 @@
PROG ?= example # Program we are building PROG ?= ./example # Program we are building
PACK ?= ./pack # Packing executable PACK ?= ./pack # Packing executable
DELETE = rm -rf # Command to remove files DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file GZIP ?= gzip # For compressing files in web_root/
SOURCES = main.c mongoose.c net.c packed_fs.c # Source code files OUT ?= -o $(PROG) # Compiler argument for output file
CFLAGS = -W -Wall -Wextra -g -I. # Build options SOURCES = main.c mongoose.c net.c packed_fs.c # Source code files
CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options # Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_PACKED_FS=1 CFLAGS_MONGOOSE += -DMG_ENABLE_PACKED_FS=1
FILES_TO_EMBED ?= $(wildcard web_root/*) ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG = example.exe # Use .exe suffix for the binary
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary
PACK = pack.exe # Packing executable PACK = pack.exe # Packing executable
CC = gcc # Use MinGW gcc compiler CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 # Link against Winsock library CFLAGS += -lws2_32 # Link against Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
OUT ?= -o $(PROG) # Build output GZIP = echo # No gzip on Windows
MAKE += WINDOWS=1 CC=$(CC)
endif endif
all: $(PROG) # Default target. Build and run program # Default target. Build and run program
$(RUN) ./$(PROG) $(ARGS) all: $(PROG)
$(RUN) $(PROG) $(ARGS)
# Before embedding files, gzip them to save space # Build program from sources
packed_fs.c: $(FILES_TO_EMBED) Makefile $(PROG): $(SOURCES)
$(CC) ../../test/pack.c -o $(PACK)
ifeq ($(OS),Windows_NT)
$(PACK) $(FILES_TO_EMBED) > $@
else
rm -rf tmp/web_root && mkdir tmp && cp -r web_root tmp/
find tmp -type f | xargs -n1 gzip
cd tmp && ../pack `find web_root -type f` > ../$@
endif
$(PROG): $(SOURCES) # Build program from sources
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT) $(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
clean: # Cleanup. Delete built program and all build artifacts # Bundle JS libraries (preact, preact-router, ...) into a single file
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM pack tmp mbedtls web_root/bundle.js:
curl -s https://npm.reversehttp.com/preact,preact/hooks,htm/preact,preact-router -o $@
# see https://mongoose.ws/tutorials/tls/#how-to-build for TLS build options # Create optimised CSS. Prerequisite: npm -g i tailwindcss tailwindcss-font-inter
web_root/main.css: web_root/index.html $(wildcard web_root/*.js)
npx tailwindcss -o $@ --minify
mbedtls: # Pull and build mbedTLS library # Generate packed filesystem for serving Web UI
packed_fs.c: $(wildcard web_root/*) $(wildcard certs/*) Makefile web_root/main.css web_root/bundle.js
$(GZIP) web_root/*
$(CC) ../../test/pack.c -o $(PACK)
$(PACK) web_root/* certs/* > $@
$(GZIP) -d web_root/*
mbedtls:
git clone --depth 1 -b v2.28.2 https://github.com/mbed-tls/mbedtls $@ git clone --depth 1 -b v2.28.2 https://github.com/mbed-tls/mbedtls $@
$(MAKE) -C mbedtls/library
ifeq ($(TLS), mbedtls)
CFLAGS += -DMG_TLS=MG_TLS_MBED -Wno-conversion -Imbedtls/include
CFLAGS += -DMBEDTLS_CONFIG_FILE=\"mbedtls_config.h\" mbedtls/library/*.c
$(PROG): mbedtls
endif
# Cleanup. Delete built program and all build artifacts
clean:
$(DELETE) $(PROG) $(PACK) *.o *.obj *.exe *.dSYM mbedtls
+5 -8
View File
@@ -5,13 +5,10 @@ into an embedded device and provide a complete device dashboard with the
following features: following features:
- Authentication: login-protected dashboard - Authentication: login-protected dashboard
- Multiple logins with different permissions (admin and user) - Multiple logins (with possibly different permissions)
- Web UI is fully embedded into the server/firmware binary, and does not - The Web UI can be fully embedded into the firmware binary, then not
need a filesystem to serve it. UI is resilient to FS problems needing a filesystem to serve it; so being resilient to FS problems
- Administrators can change server settings
- All changes are propagated to all connected clients - All changes are propagated to all connected clients
- The device is connected to the external MQTT server
- Logged in clients can send/receive messages via MQTT
## Screenshots ## Screenshots
@@ -21,9 +18,9 @@ This is a login screen that prompts for user/password
## Main dashboard ## Main dashboard
The main dashboard page shows the interactive MQTT console The main dashboard page shows the interactive console
![](screenshots/dashboard.webp) ![](screenshots/dashboard.webp)
See a detailed tutorial at https://mongoose.ws/tutorials/device-dashboard/ <!-- See a detailed tutorial at https://mongoose.ws/tutorials/device-dashboard/ -->
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
@@ -0,0 +1,8 @@
-----BEGIN CERTIFICATE-----
MIIBCTCBsAIJAK9wbIDkHnAoMAoGCCqGSM49BAMCMA0xCzAJBgNVBAYTAklFMB4X
DTIzMDEyOTIxMjEzOFoXDTMzMDEyNjIxMjEzOFowDTELMAkGA1UEBhMCSUUwWTAT
BgcqhkjOPQIBBggqhkjOPQMBBwNCAARzSQS5OHd17lUeNI+6kp9WYu0cxuEIi/JT
jphbCmdJD1cUvhmzM9/phvJT9ka10Z9toZhgnBq0o0xfTQ4jC1vwMAoGCCqGSM49
BAMCA0gAMEUCIQCe0T2E0GOiVe9KwvIEPeX1J1J0T7TNacgR0Ya33HV9VgIgNvdn
aEWiBp1xshs4iz6WbpxrS1IHucrqkZuJLfNZGZI=
-----END CERTIFICATE-----
@@ -0,0 +1,5 @@
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEICBz3HOkQLPBDtdknqC7k1PNsWj6HfhyNB5MenfjmqiooAoGCCqGSM49
AwEHoUQDQgAEc0kEuTh3de5VHjSPupKfVmLtHMbhCIvyU46YWwpnSQ9XFL4ZszPf
6YbyU/ZGtdGfbaGYYJwatKNMX00OIwtb8A==
-----END EC PRIVATE KEY-----
+20 -12
View File
@@ -1,24 +1,32 @@
// Copyright (c) 2020-2022 Cesanta Software Limited // Copyright (c) 2020-2023 Cesanta Software Limited
// All rights reserved // All rights reserved
#include "mongoose.h" #include "mongoose.h"
#include "net.h"
const char *s_listening_url = "http://0.0.0.0:8000"; static int s_sig_num;
const char *s_listening_surl = "https://0.0.0.0:8443"; static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
void device_dashboard_fn(struct mg_connection *, int, void *, void *); s_sig_num = sig_num;
}
int main(void) { int main(void) {
struct mg_mgr mgr; struct mg_mgr mgr;
signal(SIGPIPE, SIG_IGN);
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
mg_log_set(MG_LL_DEBUG); // Set debug log level mg_log_set(MG_LL_DEBUG); // Set debug log level
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
mg_http_listen(&mgr, s_listening_url, device_dashboard_fn, NULL);
MG_INFO(("Listening on %s", s_listening_url)); web_init(&mgr);
#if MG_ENABLE_MBEDTLS || MG_ENABLE_OPENSSL while (s_sig_num == 0) {
mg_http_listen(&mgr, s_listening_surl, device_dashboard_fn, ""); mg_mgr_poll(&mgr, 50);
MG_INFO(("Listening on %s", s_listening_surl)); }
#endif
while (mgr.conns != NULL) mg_mgr_poll(&mgr, 500);
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
MG_INFO(("Exiting on signal %d", s_sig_num));
return 0; return 0;
} }
@@ -0,0 +1,64 @@
/* Workaround for some mbedtls source files using INT_MAX without including limits.h */
#include <limits.h>
#define MBEDTLS_NO_PLATFORM_ENTROPY
//#define MBEDTLS_ENTROPY_HARDWARE_ALT
#define MBEDTLS_SSL_OUT_CONTENT_LEN 2048
#define MBEDTLS_ALLOW_PRIVATE_ACCESS
#define MBEDTLS_HAVE_TIME
#define MBEDTLS_SSL_SESSION_TICKETS
#define MBEDTLS_CIPHER_MODE_CBC
#define MBEDTLS_ECP_DP_SECP192R1_ENABLED
#define MBEDTLS_ECP_DP_SECP224R1_ENABLED
#define MBEDTLS_ECP_DP_SECP256R1_ENABLED
#define MBEDTLS_ECP_DP_SECP384R1_ENABLED
#define MBEDTLS_ECP_DP_SECP521R1_ENABLED
#define MBEDTLS_ECP_DP_SECP192K1_ENABLED
#define MBEDTLS_ECP_DP_SECP224K1_ENABLED
#define MBEDTLS_ECP_DP_SECP256K1_ENABLED
#define MBEDTLS_ECP_DP_BP256R1_ENABLED
#define MBEDTLS_ECP_DP_BP384R1_ENABLED
#define MBEDTLS_ECP_DP_BP512R1_ENABLED
#define MBEDTLS_ECP_DP_CURVE25519_ENABLED
#define MBEDTLS_KEY_EXCHANGE_RSA_ENABLED
#define MBEDTLS_PKCS1_V15
#define MBEDTLS_SHA256_SMALLER
#define MBEDTLS_SSL_SERVER_NAME_INDICATION
#define MBEDTLS_AES_C
#define MBEDTLS_ASN1_PARSE_C
#define MBEDTLS_BIGNUM_C
#define MBEDTLS_CIPHER_C
#define MBEDTLS_CTR_DRBG_C
#define MBEDTLS_ENTROPY_C
#define MBEDTLS_ERROR_C
#define MBEDTLS_MD_C
#define MBEDTLS_MD5_C
#define MBEDTLS_OID_C
#define MBEDTLS_PKCS5_C
#define MBEDTLS_PK_C
#define MBEDTLS_PK_PARSE_C
#define MBEDTLS_PLATFORM_C
#define MBEDTLS_RSA_C
#define MBEDTLS_SHA1_C
#define MBEDTLS_SHA224_C
#define MBEDTLS_SHA256_C
#define MBEDTLS_SHA512_C
#define MBEDTLS_SSL_CLI_C
#define MBEDTLS_SSL_SRV_C
#define MBEDTLS_SSL_TLS_C
#define MBEDTLS_X509_CRT_PARSE_C
#define MBEDTLS_X509_USE_C
#define MBEDTLS_AES_FEWER_TABLES
#define MBEDTLS_PEM_PARSE_C
#define MBEDTLS_BASE64_C
#define MBEDTLS_SSL_TICKET_C
#define MBEDTLS_SSL_PROTO_TLS1_3
#define MBEDTLS_SSL_PROTO_TLS1_2
#define MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
#define MBEDTLS_GCM_C
#define MBEDTLS_ECDH_C
#define MBEDTLS_ECP_C
#define MBEDTLS_ECDSA_C
#define MBEDTLS_ASN1_WRITE_C
+272 -236
View File
@@ -1,279 +1,307 @@
// Copyright (c) 2020-2022 Cesanta Software Limited // Copyright (c) 2023 Cesanta Software Limited
// All rights reserved // All rights reserved
#include "mongoose.h" #include "net.h"
#if !defined(MQTT_SERVER)
#if MG_ENABLE_MBEDTLS || MG_ENABLE_OPENSSL
#define MQTT_SERVER "mqtts://broker.hivemq.com:8883"
#else
#define MQTT_SERVER "mqtt://broker.hivemq.com:1883"
#endif
#endif
#define MQTT_PUBLISH_TOPIC "mg/my_device"
#define MQTT_SUBSCRIBE_TOPIC "mg/#"
// Certificate generation procedure:
// openssl ecparam -name prime256v1 -genkey -noout -out key.pem
// openssl req -new -key key.pem -x509 -nodes -days 3650 -out cert.pem
static const char *s_ssl_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIBCTCBsAIJAK9wbIDkHnAoMAoGCCqGSM49BAMCMA0xCzAJBgNVBAYTAklFMB4X\n"
"DTIzMDEyOTIxMjEzOFoXDTMzMDEyNjIxMjEzOFowDTELMAkGA1UEBhMCSUUwWTAT\n"
"BgcqhkjOPQIBBggqhkjOPQMBBwNCAARzSQS5OHd17lUeNI+6kp9WYu0cxuEIi/JT\n"
"jphbCmdJD1cUvhmzM9/phvJT9ka10Z9toZhgnBq0o0xfTQ4jC1vwMAoGCCqGSM49\n"
"BAMCA0gAMEUCIQCe0T2E0GOiVe9KwvIEPeX1J1J0T7TNacgR0Ya33HV9VgIgNvdn\n"
"aEWiBp1xshs4iz6WbpxrS1IHucrqkZuJLfNZGZI=\n"
"-----END CERTIFICATE-----\n";
static const char *s_ssl_key =
"-----BEGIN EC PRIVATE KEY-----\n"
"MHcCAQEEICBz3HOkQLPBDtdknqC7k1PNsWj6HfhyNB5MenfjmqiooAoGCCqGSM49\n"
"AwEHoUQDQgAEc0kEuTh3de5VHjSPupKfVmLtHMbhCIvyU46YWwpnSQ9XFL4ZszPf\n"
"6YbyU/ZGtdGfbaGYYJwatKNMX00OIwtb8A==\n"
"-----END EC PRIVATE KEY-----\n";
static time_t s_boot_timestamp = 0; // Updated by SNTP
#ifndef DISABLE_ROUTING
static struct mg_connection *s_sntp_conn = NULL; // SNTP connection
#endif
// Define a system time alternative
time_t ourtime(time_t *tp) {
time_t t = s_boot_timestamp + (time_t) (mg_millis() / 1000);
if (tp != NULL) *tp = t;
return t;
}
// Authenticated user. // Authenticated user.
// A user can be authenticated by: // A user can be authenticated by:
// - a name:pass pair // - a name:pass pair, passed in a header Authorization: Basic .....
// - a token // - an access_token, passed in a header Cookie: access_token=....
// When a user is shown a login screen, she enters a user:pass. If successful, // When a user is shown a login screen, she enters a user:pass. If successful,
// a server returns user info which includes token. From that point on, // a server responds with a http-only access_token cookie set.
// client can use token for authentication. Tokens could be refreshed/changed
// on a server side, forcing clients to re-login.
struct user { struct user {
const char *name, *pass, *token; const char *name, *pass, *access_token;
}; };
// This is a configuration structure we're going to show on a dashboard // Settings
static struct config { struct settings {
char *url, *pub, *sub; // MQTT settings bool log_enabled;
} s_config; int log_level;
long brightness;
char *device_name;
};
static struct mg_connection *s_mqtt = NULL; // MQTT connection static struct settings s_settings = {true, 1, 57, NULL};
static bool s_connected = false; // MQTT connection established
// Try to update a single configuration value static const char *s_json_header =
static void update_config(struct mg_str *body, const char *name, char **value) { "Content-Type: application/json\r\n"
char buf[256]; "Cache-Control: no-cache\r\n";
if (mg_http_get_var(body, name, buf, sizeof(buf)) > 0) { static uint64_t s_boot_timestamp = 0; // Updated by SNTP
free(*value);
*value = strdup(buf); // This is for newlib and TLS (mbedTLS)
} uint64_t mg_now(void) {
return mg_millis() + s_boot_timestamp;
} }
// Parse HTTP requests, return authenticated user or NULL int ui_event_next(int no, struct ui_event *e) {
static struct user *getuser(struct mg_http_message *hm) { if (no < 0 || no >= MAX_EVENTS_NO) return 0;
// In production, make passwords strong and tokens randomly generated
// In this example, user list is kept in RAM. In production, it can
// be backed by file, database, or some other method.
static struct user users[] = {
{"admin", "pass0", "admin_token"},
{"user1", "pass1", "user1_token"},
{"user2", "pass2", "user2_token"},
{NULL, NULL, NULL},
};
char user[256], pass[256];
struct user *u;
mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass));
if (user[0] != '\0' && pass[0] != '\0') {
// Both user and password is set, search by user/password
for (u = users; u->name != NULL; u++)
if (strcmp(user, u->name) == 0 && strcmp(pass, u->pass) == 0) return u;
} else if (user[0] == '\0') {
// Only password is set, search by token
for (u = users; u->name != NULL; u++)
if (strcmp(pass, u->token) == 0) return u;
}
return NULL;
}
// Notify all config watchers about the config change srand((unsigned) no);
static void send_notification(struct mg_mgr *mgr, const char *fmt, ...) { e->type = (uint8_t) rand() % 4;
struct mg_connection *c; e->prio = (uint8_t) rand() % 3;
for (c = mgr->conns; c != NULL; c = c->next) { e->timestamp =
if (c->data[0] == 'W') { (unsigned long) ((int64_t) mg_now() - 86400 * 1000 /* one day back */ +
va_list ap; no * 300 * 1000 /* 5 mins between alerts */ +
va_start(ap, fmt); 1000 * (rand() % 300) /* randomize event time */) /
mg_ws_vprintf(c, WEBSOCKET_OP_TEXT, fmt, &ap); 1000UL;
va_end(ap);
}
}
}
// Send simulated metrics data to the dashboard, for chart rendering mg_snprintf(e->text, MAX_EVENT_TEXT_SIZE, "event#%d", no);
static void timer_metrics_fn(void *param) { return no + 1;
send_notification(param, "{%m:%m,%m:[%lu, %d]}", MG_ESC("name"),
MG_ESC("metrics"), MG_ESC("data"),
(unsigned long) ourtime(NULL),
10 + (int) ((double) rand() * 10 / RAND_MAX));
}
#ifndef DISABLE_ROUTING
// MQTT event handler function
static void mqtt_fn(struct mg_connection *c, int ev, void *ev_data, void *fnd) {
if (ev == MG_EV_CONNECT && mg_url_is_ssl(s_config.url)) {
struct mg_tls_opts opts = {.ca = "ca.pem",
.srvname = mg_url_host(s_config.url)};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_MQTT_OPEN) {
s_connected = true;
c->is_hexdumping = 1;
struct mg_mqtt_opts sub_opts;
memset(&sub_opts, 0, sizeof(sub_opts));
sub_opts.topic = mg_str(s_config.sub);
sub_opts.qos = 2;
mg_mqtt_sub(s_mqtt, &sub_opts);
send_notification(c->mgr, "{%m:%m,%m:null}", MG_ESC("name"),
MG_ESC("config"), MG_ESC("data"));
MG_INFO(("MQTT connected, server %s", MQTT_SERVER));
} else if (ev == MG_EV_MQTT_MSG) {
struct mg_mqtt_message *mm = ev_data;
send_notification(
c->mgr, "{%m:%m,%m:{%m: %m, %m: %m, %m: %d}}", MG_ESC("name"),
MG_ESC("message"), MG_ESC("data"), MG_ESC("topic"), mg_print_esc,
(int) mm->topic.len, mm->topic.ptr, MG_ESC("data"), mg_print_esc,
(int) mm->data.len, mm->data.ptr, MG_ESC("qos"), (int) mm->qos);
} else if (ev == MG_EV_MQTT_CMD) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
MG_DEBUG(("%lu cmd %d qos %d", c->id, mm->cmd, mm->qos));
} else if (ev == MG_EV_CLOSE) {
s_mqtt = NULL;
if (s_connected) {
s_connected = false;
send_notification(c->mgr, "{%m:%m,%m:null}", MG_ESC("name"),
MG_ESC("config"), MG_ESC("data"));
}
}
(void) fnd;
}
// Keep MQTT connection open - reconnect if closed
static void timer_mqtt_fn(void *param) {
struct mg_mgr *mgr = (struct mg_mgr *) param;
if (s_mqtt == NULL) {
struct mg_mqtt_opts opts;
memset(&opts, 0, sizeof(opts));
s_mqtt = mg_mqtt_connect(mgr, s_config.url, &opts, mqtt_fn, NULL);
}
} }
// SNTP connection event handler. When we get a response from an SNTP server, // SNTP connection event handler. When we get a response from an SNTP server,
// adjust s_boot_timestamp. We'll get a valid time from that point on // adjust s_boot_timestamp. We'll get a valid time from that point on
static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_SNTP_TIME) { uint64_t *expiration_time = (uint64_t *) c->data;
if (ev == MG_EV_OPEN) {
*expiration_time = mg_millis() + 3000; // Store expiration time in 3s
} else if (ev == MG_EV_SNTP_TIME) {
uint64_t t = *(uint64_t *) ev_data; uint64_t t = *(uint64_t *) ev_data;
s_boot_timestamp = (time_t) ((t - mg_millis()) / 1000); s_boot_timestamp = t - mg_millis();
c->is_closing = 1; c->is_closing = 1;
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_POLL) {
s_sntp_conn = NULL; if (mg_millis() > *expiration_time) c->is_closing = 1;
} }
(void) fn_data; (void) fn_data;
} }
static void timer_sntp_fn(void *param) { // SNTP timer function. Sync up time static void timer_sntp_fn(void *param) { // SNTP timer function. Sync up time
struct mg_mgr *mgr = (struct mg_mgr *) param; mg_sntp_connect(param, "udp://time.google.com:123", sfn, NULL);
if (s_sntp_conn == NULL && s_boot_timestamp == 0) { }
s_sntp_conn = mg_sntp_connect(mgr, NULL, sfn, NULL);
// Parse HTTP requests, return authenticated user or NULL
static struct user *authenticate(struct mg_http_message *hm) {
// In production, make passwords strong and tokens randomly generated
// In this example, user list is kept in RAM. In production, it can
// be backed by file, database, or some other method.
static struct user users[] = {
{"admin", "admin", "admin_token"},
{"user1", "user1", "user1_token"},
{"user2", "user2", "user2_token"},
{NULL, NULL, NULL},
};
char user[64], pass[64];
struct user *u, *result = NULL;
mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass));
MG_VERBOSE(("user [%s] pass [%s]", user, pass));
if (user[0] != '\0' && pass[0] != '\0') {
// Both user and password is set, search by user/password
for (u = users; result == NULL && u->name != NULL; u++)
if (strcmp(user, u->name) == 0 && strcmp(pass, u->pass) == 0) result = u;
} else if (user[0] == '\0') {
// Only password is set, search by token
for (u = users; result == NULL && u->name != NULL; u++)
if (strcmp(pass, u->access_token) == 0) result = u;
}
return result;
}
static void handle_login(struct mg_connection *c, struct user *u) {
char cookie[256];
mg_snprintf(cookie, sizeof(cookie),
"Set-Cookie: access_token=%s; Path=/; "
"%sHttpOnly; SameSite=Lax; Max-Age=%d\r\n",
u->access_token, c->is_tls ? "Secure; " : "", 3600 * 24);
mg_http_reply(c, 200, cookie, "{%m:%m}", MG_ESC("user"), MG_ESC(u->name));
}
static void handle_logout(struct mg_connection *c) {
char cookie[256];
mg_snprintf(cookie, sizeof(cookie),
"Set-Cookie: access_token=; Path=/; "
"Expires=Thu, 01 Jan 1970 00:00:00 UTC; "
"%sHttpOnly; Max-Age=0; \r\n",
c->is_tls ? "Secure; " : "");
mg_http_reply(c, 200, cookie, "true\n");
}
static void handle_debug(struct mg_connection *c, struct mg_http_message *hm) {
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
mg_log_set(level);
mg_http_reply(c, 200, "", "Debug level set to %d\n", level);
}
static size_t print_int_arr(void (*out)(char, void *), void *ptr, va_list *ap) {
size_t len = 0, num = va_arg(*ap, size_t); // Number of items in the array
int *arr = va_arg(*ap, int *); // Array ptr
for (size_t i = 0; i < num; i++) {
len += mg_xprintf(out, ptr, "%s%d", i == 0 ? "" : ",", arr[i]);
}
return len;
}
static void handle_stats_get(struct mg_connection *c) {
int points[] = {21, 22, 22, 19, 18, 20, 23, 23, 22, 22, 22, 23, 22};
mg_http_reply(c, 200, s_json_header, "{%m:%d,%m:%d,%m:[%M]}",
MG_ESC("temperature"), 21, //
MG_ESC("humidity"), 67, //
MG_ESC("points"), print_int_arr,
sizeof(points) / sizeof(points[0]), points);
}
static size_t print_events(void (*out)(char, void *), void *ptr, va_list *ap) {
size_t len = 0;
struct ui_event ev;
int pageno = va_arg(*ap, int);
int no = (pageno - 1) * EVENTS_PER_PAGE;
int end = no + EVENTS_PER_PAGE;
while ((no = ui_event_next(no, &ev)) != 0 && no <= end) {
len += mg_xprintf(out, ptr, "%s{%m:%lu,%m:%d,%m:%d,%m:%m}", //
len == 0 ? "" : ",", //
MG_ESC("time"), ev.timestamp, //
MG_ESC("type"), ev.type, //
MG_ESC("prio"), ev.prio, //
MG_ESC("text"), MG_ESC(ev.text));
}
return len;
}
static void handle_events_get(struct mg_connection *c,
struct mg_http_message *hm) {
int pageno = mg_json_get_long(hm->body, "$.page", 1);
mg_http_reply(c, 200, s_json_header, "{%m:[%M], %m:%d}", MG_ESC("arr"),
print_events, pageno, MG_ESC("totalCount"), MAX_EVENTS_NO);
}
static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
struct settings settings;
memset(&settings, 0, sizeof(settings));
mg_json_get_bool(body, "$.log_enabled", &settings.log_enabled);
settings.log_level = mg_json_get_long(body, "$.log_level", 0);
settings.brightness = mg_json_get_long(body, "$.brightness", 0);
char *s = mg_json_get_str(body, "$.device_name");
if (s && strlen(s) < MAX_DEVICE_NAME) {
free(settings.device_name);
settings.device_name = s;
} else {
free(s);
}
// Save to the device flash
s_settings = settings;
bool ok = true;
mg_http_reply(c, 200, s_json_header,
"{%m:%s,%m:%m}", //
MG_ESC("status"), ok ? "true" : "false", //
MG_ESC("message"), MG_ESC(ok ? "Success" : "Failed"));
}
static void handle_settings_get(struct mg_connection *c) {
mg_http_reply(c, 200, s_json_header, "{%m:%s,%m:%hhu,%m:%hhu,%m:%m}", //
MG_ESC("log_enabled"),
s_settings.log_enabled ? "true" : "false", //
MG_ESC("log_level"), s_settings.log_level, //
MG_ESC("brightness"), s_settings.brightness, //
MG_ESC("device_name"), MG_ESC(s_settings.device_name));
}
static void handle_firmware_upload(struct mg_connection *c,
struct mg_http_message *hm) {
char name[64], offset[20], total[20];
struct mg_str data = hm->body;
long ofs = -1, tot = -1;
name[0] = offset[0] = '\0';
mg_http_get_var(&hm->query, "name", name, sizeof(name));
mg_http_get_var(&hm->query, "offset", offset, sizeof(offset));
mg_http_get_var(&hm->query, "total", total, sizeof(total));
MG_INFO(("File %s, offset %s, len %lu", name, offset, data.len));
if ((ofs = mg_json_get_long(mg_str(offset), "$", -1)) < 0 ||
(tot = mg_json_get_long(mg_str(total), "$", -1)) < 0) {
mg_http_reply(c, 500, "", "offset and total not set\n");
} else if (ofs == 0 && mg_ota_begin((size_t) tot) == false) {
mg_http_reply(c, 500, "", "mg_ota_begin(%ld) failed\n", tot);
} else if (data.len > 0 && mg_ota_write(data.ptr, data.len) == false) {
mg_http_reply(c, 500, "", "mg_ota_write(%lu) @%ld failed\n", data.len, ofs);
mg_ota_end();
} else if (data.len == 0 && mg_ota_end() == false) {
mg_http_reply(c, 500, "", "mg_ota_end() failed\n", tot);
} else {
mg_http_reply(c, 200, s_json_header, "true\n");
if (data.len == 0) {
// Successful mg_ota_end() called, schedule device reboot
mg_timer_add(c->mgr, 500, 0, (void (*)(void *)) mg_device_reset, NULL);
}
} }
} }
#endif static void handle_firmware_commit(struct mg_connection *c) {
mg_http_reply(c, 200, s_json_header, "%s\n",
mg_ota_commit() ? "true" : "false");
}
static void handle_firmware_rollback(struct mg_connection *c) {
mg_http_reply(c, 200, s_json_header, "%s\n",
mg_ota_rollback() ? "true" : "false");
}
static size_t print_status(void (*out)(char, void *), void *ptr, va_list *ap) {
int fw = va_arg(*ap, int);
return mg_xprintf(out, ptr, "{%m:%d,%m:%c%lx%c,%m:%u,%m:%u}",
MG_ESC("status"), mg_ota_status(fw), MG_ESC("crc32"), '"',
mg_ota_crc32(fw), '"', MG_ESC("size"), mg_ota_size(fw),
MG_ESC("timestamp"), mg_ota_timestamp(fw));
}
static void handle_firmware_status(struct mg_connection *c) {
mg_http_reply(c, 200, s_json_header, "[%M,%M]\n", print_status,
MG_FIRMWARE_CURRENT, print_status, MG_FIRMWARE_PREVIOUS);
}
static void handle_device_reset(struct mg_connection *c) {
mg_http_reply(c, 200, s_json_header, "true\n");
mg_timer_add(c->mgr, 500, 0, (void (*)(void *)) mg_device_reset, NULL);
}
// HTTP request handler function // HTTP request handler function
void device_dashboard_fn(struct mg_connection *c, int ev, void *ev_data, static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
void *fn_data) { if (ev == MG_EV_ACCEPT) {
if (ev == MG_EV_OPEN && c->is_listening) { if (fn_data != NULL) { // TLS listener!
mg_timer_add(c->mgr, 1000, MG_TIMER_REPEAT, timer_metrics_fn, c->mgr); struct mg_tls_opts opts = {0};
#ifndef DISABLE_ROUTING opts.cert = mg_unpacked("/certs/server_cert.pem");
mg_timer_add(c->mgr, 1000, MG_TIMER_REPEAT, timer_mqtt_fn, c->mgr); opts.key = mg_unpacked("/certs/server_key.pem");
mg_timer_add(c->mgr, 1000, MG_TIMER_REPEAT, timer_sntp_fn, c->mgr); mg_tls_init(c, &opts);
#endif }
s_config.url = strdup(MQTT_SERVER);
s_config.pub = strdup(MQTT_PUBLISH_TOPIC);
s_config.sub = strdup(MQTT_SUBSCRIBE_TOPIC);
} else if (ev == MG_EV_ACCEPT && fn_data != NULL) {
struct mg_tls_opts opts = {.cert = s_ssl_cert, .certkey = s_ssl_key};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_MSG) { } else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
struct user *u = getuser(hm); struct user *u = authenticate(hm);
// MG_INFO(("%p [%.*s] auth %s", c->fd, (int) hm->uri.len, hm->uri.ptr,
// u ? u->name : "NULL"));
if (mg_http_match_uri(hm, "/api/hi")) {
mg_http_reply(c, 200, "", "hi\n"); // Testing endpoint
} else if (mg_http_match_uri(hm, "/api/debug")) {
int level = mg_json_get_long(hm->body, "$.level", MG_LL_DEBUG);
mg_log_set(level);
mg_http_reply(c, 200, "", "Debug level set to %d\n", level);
} else if (u == NULL && mg_http_match_uri(hm, "/api/#")) {
// All URIs starting with /api/ must be authenticated
mg_http_reply(c, 403, "", "Denied\n");
} else if (mg_http_match_uri(hm, "/api/config/get")) {
#ifdef DISABLE_ROUTING
mg_http_reply(c, 200, NULL, "{%m:%m,%m:%m,%m:%m}\n", MG_ESC("url"),
MG_ESC(s_config.url), MG_ESC("pub"), MG_ESC(s_config.pub),
MG_ESC("sub"), MG_ESC(s_config.sub));
#else
mg_http_reply(c, 200, NULL, "{%m:%m,%m:%m,%m:%m,%m:%s}\n", MG_ESC("url"),
MG_ESC(s_config.url), MG_ESC("pub"), MG_ESC(s_config.pub),
MG_ESC("sub"), MG_ESC(s_config.sub), MG_ESC("connected"),
s_connected ? "true" : "false");
#endif
} else if (mg_http_match_uri(hm, "/api/config/set")) {
// Admins only
if (strcmp(u->name, "admin") == 0) {
update_config(&hm->body, "url", &s_config.url);
update_config(&hm->body, "pub", &s_config.pub);
update_config(&hm->body, "sub", &s_config.sub);
if (s_mqtt) s_mqtt->is_closing = 1; // Ask to disconnect from MQTT
send_notification(c->mgr, "{%m:%m,%m:null}", MG_ESC("name"),
MG_ESC("config"), MG_ESC("data"));
mg_http_reply(c, 200, "", "ok\n");
} else {
mg_http_reply(c, 403, "", "Denied\n");
}
} else if (mg_http_match_uri(hm, "/api/message/send")) {
char buf[256];
if (s_connected &&
mg_http_get_var(&hm->body, "message", buf, sizeof(buf)) > 0) {
struct mg_mqtt_opts pub_opts;
memset(&pub_opts, 0, sizeof(pub_opts));
pub_opts.topic = mg_str(s_config.pub);
pub_opts.message = mg_str(buf);
pub_opts.qos = 2, pub_opts.retain = false;
mg_mqtt_pub(s_mqtt, &pub_opts); if (mg_http_match_uri(hm, "/api/#") && u == NULL) {
} mg_http_reply(c, 403, "", "Not Authorised\n");
mg_http_reply(c, 200, "", "ok\n");
} else if (mg_http_match_uri(hm, "/api/watch")) {
c->data[0] = 'W'; // Mark ourselves as a event listener
mg_ws_upgrade(c, hm, NULL);
} else if (mg_http_match_uri(hm, "/api/login")) { } else if (mg_http_match_uri(hm, "/api/login")) {
mg_http_reply(c, 200, NULL, "{%m:%m,%m:%m}\n", MG_ESC("user"), handle_login(c, u);
MG_ESC(u->name), MG_ESC("token"), MG_ESC(u->token)); } else if (mg_http_match_uri(hm, "/api/logout")) {
handle_logout(c);
} else if (mg_http_match_uri(hm, "/api/debug")) {
handle_debug(c, hm);
} else if (mg_http_match_uri(hm, "/api/stats/get")) {
handle_stats_get(c);
} else if (mg_http_match_uri(hm, "/api/events/get")) {
handle_events_get(c, hm);
} else if (mg_http_match_uri(hm, "/api/settings/get")) {
handle_settings_get(c);
} else if (mg_http_match_uri(hm, "/api/settings/set")) {
handle_settings_set(c, hm->body);
} else if (mg_http_match_uri(hm, "/api/firmware/upload")) {
handle_firmware_upload(c, hm);
} else if (mg_http_match_uri(hm, "/api/firmware/commit")) {
handle_firmware_commit(c);
} else if (mg_http_match_uri(hm, "/api/firmware/rollback")) {
handle_firmware_rollback(c);
} else if (mg_http_match_uri(hm, "/api/firmware/status")) {
handle_firmware_status(c);
} else if (mg_http_match_uri(hm, "/api/device/reset")) {
handle_device_reset(c);
} else { } else {
struct mg_http_serve_opts opts; struct mg_http_serve_opts opts;
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
#if 1 #if MG_ARCH == MG_ARCH_UNIX || MG_ARCH == MG_ARCH_WIN32
opts.root_dir = "/web_root"; opts.root_dir = "web_root"; // On workstations, use filesystem
opts.fs = &mg_fs_packed;
#else #else
opts.root_dir = "web_root"; opts.root_dir = "/web_root"; // On embedded, use packed files
opts.fs = &mg_fs_packed;
#endif #endif
mg_http_serve_dir(c, ev_data, &opts); mg_http_serve_dir(c, ev_data, &opts);
} }
@@ -282,3 +310,11 @@ void device_dashboard_fn(struct mg_connection *c, int ev, void *ev_data,
&c->send.buf[9])); &c->send.buf[9]));
} }
} }
void web_init(struct mg_mgr *mgr) {
s_settings.device_name = strdup("My Device");
mg_http_listen(mgr, HTTP_URL, fn, NULL);
mg_http_listen(mgr, HTTPS_URL, fn, (void *) 1);
mg_timer_add(mgr, 3600 * 1000, MG_TIMER_RUN_NOW | MG_TIMER_REPEAT,
timer_sntp_fn, mgr);
}
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 2023 Cesanta Software Limited
// All rights reserved
#pragma once
#include "mongoose.h"
#if !defined(HTTP_URL)
#define HTTP_URL "http://0.0.0.0:8000"
#endif
#if !defined(HTTPS_URL)
#define HTTPS_URL "https://0.0.0.0:8443"
#endif
#define MAX_DEVICE_NAME 40
#define MAX_EVENTS_NO 400
#define MAX_EVENT_TEXT_SIZE 10
#define EVENTS_PER_PAGE 20
// Event log entry
struct ui_event {
uint8_t type, prio;
unsigned long timestamp;
char text[10];
};
void web_init(struct mg_mgr *mgr);
File diff suppressed because it is too large. Load diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 119 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 16 KiB

@@ -0,0 +1,17 @@
module.exports = {
content: ['./web_root/*.{html,js}'],
xplugins: [ 'tailwindcss', 'xautoprefixer' ],
corePlugins: {outline: false},
theme: {
extend: {},
fontFamily: {
sans:
[
"Inter var, Helvetica, sans-serif", {
fontFeatureSettings: '"cv11", "ss01"',
fontVariationSettings: '"opsz" 32',
}
]
}
}
}
@@ -0,0 +1 @@
var n,t,e,_,r,o,i,u,l,c={},a=[],s=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function f(n,t){for(var e in t)n[e]=t[e];return n}function p(n){var t=n.parentNode;t&&t.removeChild(n)}function h(t,e,_){var r,o,i,u={};for(i in e)"key"==i?r=e[i]:"ref"==i?o=e[i]:u[i]=e[i];if(arguments.length>2&&(u.children=arguments.length>3?n.call(arguments,2):_),"function"==typeof t&&null!=t.defaultProps)for(i in t.defaultProps)void 0===u[i]&&(u[i]=t.defaultProps[i]);return d(t,u,r,o,null)}function d(n,_,r,o,i){var u={type:n,props:_,key:r,ref:o,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==i?++e:i};return null==i&&null!=t.vnode&&t.vnode(u),u}function v(){return{current:null}}function m(n){return n.children}function y(n,t){this.props=n,this.context=t}function g(n,t){if(null==t)return n.__?g(n.__,n.__.__k.indexOf(n)+1):null;for(var e;t<n.__k.length;t++)if(null!=(e=n.__k[t])&&null!=e.__e)return e.__e;return"function"==typeof n.type?g(n):null}function b(n){var t,e;if(null!=(n=n.__)&&null!=n.__c){for(n.__e=n.__c.base=null,t=0;t<n.__k.length;t++)if(null!=(e=n.__k[t])&&null!=e.__e){n.__e=n.__c.base=e.__e;break}return b(n)}}function k(n){(!n.__d&&(n.__d=!0)&&r.push(n)&&!C.__r++||o!==t.debounceRendering)&&((o=t.debounceRendering)||i)(C)}function C(){var n,t,e,_,o,i,l,c;for(r.sort(u);n=r.shift();)n.__d&&(t=r.length,_=void 0,o=void 0,l=(i=(e=n).__v).__e,(c=e.__P)&&(_=[],(o=f({},i)).__v=i.__v+1,T(c,i,o,e.__n,void 0!==c.ownerSVGElement,null!=i.__h?[l]:null,_,null==l?g(i):l,i.__h),R(_,i),i.__e!=l&&b(i)),r.length>t&&r.sort(u));C.__r=0}function x(n,t,e,_,r,o,i,u,l,s){var f,p,h,v,y,b,k,C=_&&_.__k||a,x=C.length;for(e.__k=[],f=0;f<t.length;f++)if(null!=(v=e.__k[f]=null==(v=t[f])||"boolean"==typeof v||"function"==typeof v?null:"string"==typeof v||"number"==typeof v||"bigint"==typeof v?d(null,v,null,null,v):Array.isArray(v)?d(m,{children:v},null,null,null):v.__b>0?d(v.type,v.props,v.key,v.ref?v.ref:null,v.__v):v)){if(v.__=e,v.__b=e.__b+1,null===(h=C[f])||h&&v.key==h.key&&v.type===h.type)C[f]=void 0;else for(p=0;p<x;p++){if((h=C[p])&&v.key==h.key&&v.type===h.type){C[p]=void 0;break}h=null}T(n,v,h=h||c,r,o,i,u,l,s),y=v.__e,(p=v.ref)&&h.ref!=p&&(k||(k=[]),h.ref&&k.push(h.ref,null,v),k.push(p,v.__c||y,v)),null!=y?(null==b&&(b=y),"function"==typeof v.type&&v.__k===h.__k?v.__d=l=E(v,l,n):l=U(n,v,h,C,y,l),"function"==typeof e.type&&(e.__d=l)):l&&h.__e==l&&l.parentNode!=n&&(l=g(h))}for(e.__e=b,f=x;f--;)null!=C[f]&&("function"==typeof e.type&&null!=C[f].__e&&C[f].__e==e.__d&&(e.__d=A(_).nextSibling),W(C[f],C[f]));if(k)for(f=0;f<k.length;f++)M(k[f],k[++f],k[++f])}function E(n,t,e){for(var _,r=n.__k,o=0;r&&o<r.length;o++)(_=r[o])&&(_.__=n,t="function"==typeof _.type?E(_,t,e):U(e,_,_,r,_.__e,t));return t}function H(n,t){return t=t||[],null==n||"boolean"==typeof n||(Array.isArray(n)?n.some((function(n){H(n,t)})):t.push(n)),t}function U(n,t,e,_,r,o){var i,u,l;if(void 0!==t.__d)i=t.__d,t.__d=void 0;else if(null==e||r!=o||null==r.parentNode)n:if(null==o||o.parentNode!==n)n.appendChild(r),i=null;else{for(u=o,l=0;(u=u.nextSibling)&&l<_.length;l+=1)if(u==r)break n;n.insertBefore(r,o),i=o}return void 0!==i?i:r.nextSibling}function A(n){var t,e,_;if(null==n.type||"string"==typeof n.type)return n.__e;if(n.__k)for(t=n.__k.length-1;t>=0;t--)if((e=n.__k[t])&&(_=A(e)))return _;return null}function P(n,t,e,_,r){var o;for(o in e)"children"===o||"key"===o||o in t||N(n,o,null,e[o],_);for(o in t)r&&"function"!=typeof t[o]||"children"===o||"key"===o||"value"===o||"checked"===o||e[o]===t[o]||N(n,o,t[o],e[o],_)}function S(n,t,e){"-"===t[0]?n.setProperty(t,null==e?"":e):n[t]=null==e?"":"number"!=typeof e||s.test(t)?e:e+"px"}function N(n,t,e,_,r){var o;n:if("style"===t)if("string"==typeof e)n.style.cssText=e;else{if("string"==typeof _&&(n.style.cssText=_=""),_)for(t in _)e&&t in e||S(n.style,t,"");if(e)for(t in e)_&&e[t]===_[t]||S(n.style,t,e[t])}else if("o"===t[0]&&"n"===t[1])o=t!==(t=t.replace(/Capture$/,"")),t=t.toLowerCase()in n?t.toLowerCase().slice(2):t.slice(2),n.l||(n.l={}),n.l[t+o]=e,e?_||n.addEventListener(t,o?D:w,o):n.removeEventListener(t,o?D:w,o);else if("dangerouslySetInnerHTML"!==t){if(r)t=t.replace(/xlink(H|:h)/,"h").replace(/sName$/,"s");else if("width"!==t&&"height"!==t&&"href"!==t&&"list"!==t&&"form"!==t&&"tabIndex"!==t&&"download"!==t&&t in n)try{n[t]=null==e?"":e;break n}catch(n){}"function"==typeof e||(null==e||!1===e&&"-"!==t[4]?n.removeAttribute(t):n.setAttribute(t,e))}}function w(n){return this.l[n.type+!1](t.event?t.event(n):n)}function D(n){return this.l[n.type+!0](t.event?t.event(n):n)}function T(n,e,_,r,o,i,u,l,c){var a,s,p,h,d,v,g,b,k,C,E,H,U,A,P,S=e.type;if(void 0!==e.constructor)return null;null!=_.__h&&(c=_.__h,l=e.__e=_.__e,e.__h=null,i=[l]),(a=t.__b)&&a(e);try{n:if("function"==typeof S){if(b=e.props,k=(a=S.contextType)&&r[a.__c],C=a?k?k.props.value:a.__:r,_.__c?g=(s=e.__c=_.__c).__=s.__E:("prototype"in S&&S.prototype.render?e.__c=s=new S(b,C):(e.__c=s=new y(b,C),s.constructor=S,s.rLine truncated
@@ -0,0 +1,349 @@
'use strict';
import { h, render, useState, useEffect, useRef, html, Router } from './bundle.js';
// Helper function that returns a promise that resolves after delay
const Delay = (ms, val) => new Promise(resolve => setTimeout(resolve, ms, val));
export const Icons = {
heart: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"></path></svg>`,
downArrowBox: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9 8.25H7.5a2.25 2.25 0 00-2.25 2.25v9a2.25 2.25 0 002.25 2.25h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25H15M9 12l3 3m0 0l3-3m-3 3V2.25" /> </svg>`,
upArrowBox: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9 8.25H7.5a2.25 2.25 0 00-2.25 2.25v9a2.25 2.25 0 002.25 2.25h9a2.25 2.25 0 002.25-2.25v-9a2.25 2.25 0 00-2.25-2.25H15m0-3l-3-3m0 0l-3 3m3-3V15" /> </svg>`,
cog: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> </svg>`,
settingsH: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M10.5 6h9.75M10.5 6a1.5 1.5 0 11-3 0m3 0a1.5 1.5 0 10-3 0M3.75 6H7.5m3 12h9.75m-9.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-3.75 0H7.5m9-6h3.75m-3.75 0a1.5 1.5 0 01-3 0m3 0a1.5 1.5 0 00-3 0m-9.75 0h9.75" /> </svg>`,
settingsV: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M6 13.5V3.75m0 9.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 3.75V16.5m12-3V3.75m0 9.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 3.75V16.5m-6-9V3.75m0 3.75a1.5 1.5 0 010 3m0-3a1.5 1.5 0 000 3m0 9.75V10.5" /> </svg>`,
scan: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M7.5 3.75H6A2.25 2.25 0 003.75 6v1.5M16.5 3.75H18A2.25 2.25 0 0120.25 6v1.5m0 9V18A2.25 2.25 0 0118 20.25h-1.5m-9 0H6A2.25 2.25 0 013.75 18v-1.5M15 12a3 3 0 11-6 0 3 3 0 016 0z" /> </svg> `,
desktop: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9 17.25v1.007a3 3 0 01-.879 2.122L7.5 21h9l-.621-.621A3 3 0 0115 18.257V17.25m6-12V15a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 15V5.25m18 0A2.25 2.25 0 0018.75 3H5.25A2.25 2.25 0 003 5.25m18 0V12a2.25 2.25 0 01-2.25 2.25H5.25A2.25 2.25 0 013 12V5.25" /> </svg>`,
alert: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0M3.124 7.5A8.969 8.969 0 015.292 3m13.416 0a8.969 8.969 0 012.168 4.5" /> </svg>`,
bell: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0M3.124 7.5A8.969 8.969 0 015.292 3m13.416 0a8.969 8.969 0 012.168 4.5" /> </svg>`,
refresh: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" /> </svg> `,
bars4: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 5.25h16.5m-16.5 4.5h16.5m-16.5 4.5h16.5m-16.5 4.5h16.5" /> </svg>`,
bars3: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 6.75h16.5M3.75 12h16.5m-16.5 5.25h16.5" /> </svg>`,
logout: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M12.75 15l3-3m0 0l-3-3m3 3h-7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg>`,
save: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 3.75V16.5L12 14.25 7.5 16.5V3.75m9 0H18A2.25 2.25 0 0120.25 6v12A2.25 2.25 0 0118 20.25H6A2.25 2.25 0 013.75 18V6A2.25 2.25 0 016 3.75h1.5m9 0h-9" /> </svg>`,
email: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M21.75 6.75v10.5a2.25 2.25 0 01-2.25 2.25h-15a2.25 2.25 0 01-2.25-2.25V6.75m19.5 0A2.25 2.25 0 0019.5 4.5h-15a2.25 2.25 0 00-2.25 2.25m19.5 0v.243a2.25 2.25 0 01-1.07 1.916l-7.5 4.615a2.25 2.25 0 01-2.36 0L3.32 8.91a2.25 2.25 0 01-1.07-1.916V6.75" /> </svg>`,
expand: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 3.75v4.5m0-4.5h4.5m-4.5 0L9 9M3.75 20.25v-4.5m0 4.5h4.5m-4.5 0L9 15M20.25 3.75h-4.5m4.5 0v4.5m0-4.5L15 9m5.25 11.25h-4.5m4.5 0v-4.5m0 4.5L15 15" /> </svg>`,
shrink: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9 9V4.5M9 9H4.5M9 9L3.75 3.75M9 15v4.5M9 15H4.5M9 15l-5.25 5.25M15 9h4.5M15 9V4.5M15 9l5.25-5.25M15 15h4.5M15 15v4.5m0-4.5l5.25 5.25" /> </svg>`,
ok: props => html`<svg class=${props.class} fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" aria-hidden="true"> <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg>`,
fail: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9.75 9.75l4.5 4.5m0-4.5l-4.5 4.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /> </svg>`,
upload: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5m-13.5-9L12 3m0 0l4.5 4.5M12 3v13.5" /> </svg> `,
download: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M3 16.5v2.25A2.25 2.25 0 005.25 21h13.5A2.25 2.25 0 0021 18.75V16.5M16.5 12L12 16.5m0 0L7.5 12m4.5 4.5V3" /> </svg> `,
bolt: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M3.75 13.5l10.5-11.25L12 10.5h8.25L9.75 21.75 12 13.5H3.75z" /> </svg>`,
home: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" /> </svg> `,
link: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M13.19 8.688a4.5 4.5 0 011.242 7.244l-4.5 4.5a4.5 4.5 0 01-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 00-6.364-6.364l-4.5 4.5a4.5 4.5 0 001.242 7.244" /> </svg> `,
shield: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75L11.25 15 15 9.75m-3-7.036A11.959 11.959 0 013.598 6 11.99 11.99 0 003 9.749c0 5.592 3.824 10.29 9 11.623 5.176-1.332 9-6.03 9-11.622 0-1.31-.21-2.571-.598-3.751h-.152c-3.196 0-6.1-1.248-8.25-3.285z" /> </svg> `,
barsdown: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M3 4.5h14.25M3 9h9.75M3 13.5h9.75m4.5-4.5v12m0 0l-3.75-3.75M17.25 21L21 17.25" /> </svg> `,
arrowdown: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m0 0l6.75-6.75M12 19.5l-6.75-6.75" /> </svg> `,
arrowup: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M12 19.5v-15m0 0l-6.75 6.75M12 4.5l6.75 6.75" /> </svg>`,
warn: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" /> </svg>`,
info: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" /> </svg>`,
exclamationTriangle: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M12 9v3.75m-9.303 3.376c-.866 1.5.217 3.374 1.948 3.374h14.71c1.73 0 2.813-1.874 1.948-3.374L13.949 3.378c-.866-1.5-3.032-1.5-3.898 0L2.697 16.126zM12 15.75h.007v.008H12v-.008z" /> </svg>`,
thumbUp: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M6.633 10.5c.806 0 1.533-.446 2.031-1.08a9.041 9.041 0 012.861-2.4c.723-.384 1.35-.956 1.653-1.715a4.498 4.498 0 00.322-1.672V3a.75.75 0 01.75-.75A2.25 2.25 0 0116.5 4.5c0 1.152-.26 2.243-.723 3.218-.266.558.107 1.282.725 1.282h3.126c1.026 0 1.945.694 2.054 1.715.045.422.068.85.068 1.285a11.95 11.95 0 01-2.649 7.521c-.388.482-.987.729-1.605.729H13.48c-.483 0-.964-.078-1.423-.23l-3.114-1.04a4.501 4.501 0 00-1.423-.23H5.904M14.25 9h2.25M5.904 18.75c.083.205.173.405.27.602.197.4-.078.898-.523.898h-.908c-.889 0-1.713-.518-1.972-1.368a12 12 0 01-.521-3.507c0-1.553.295-3.036.831-4.398C3.387 10.203 4.167 9.75 5 9.75h1.053c.472 0 .745.556.5.96a8.958 8.958 0 00-1.302 4.665c0 1.194.232 2.333.654 3.375z" /> </svg>`,
backward: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M21 16.811c0 .864-.933 1.405-1.683.977l-7.108-4.062a1.125 1.125 0 010-1.953l7.108-4.062A1.125 1.125 0 0121 8.688v8.123zM11.25 16.811c0 .864-.933 1.405-1.683.977l-7.108-4.062a1.125 1.125 0 010-1.953L9.567 7.71a1.125 1.125 0 011.683.977v8.123z" /> </svg>`,
chip: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-15 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-1.5h10.5a2.25 2.25 0 002.25-2.25V6.75a2.25 2.25 0 00-2.25-2.25H6.75A2.25 2.25 0 004.5 6.75v10.5a2.25 2.25 0 002.25 2.25zm.75-12h9v9h-9v-9z" /> </svg>`,
camera: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM18.75 10.5h.008v.008h-.008V10.5z" /> </svg>`,
arrows: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5" /> </svg>`,
};
export const tipColors = {
green: 'bg-green-100 text-green-900 ring-green-300',
yellow: 'bg-yellow-100 text-yellow-900 ring-yellow-300',
red: 'bg-red-100 text-red-900 ring-red-300',
};
export function Button({title, onclick, disabled, cls, icon, ref, colors, hovercolor, disabledcolor}) {
const [spin, setSpin] = useState(false);
const cb = function(ev) {
const res = onclick ? onclick() : null;
if (res && typeof (res.catch) === 'function') {
setSpin(true);
res.catch(() => false).then(() => setSpin(false));
}
};
if (!colors) colors = 'bg-blue-600 hover:bg-blue-500 disabled:bg-blue-400';
return html`
<button type="button" class="inline-flex justify-center items-center gap-2 rounded px-2.5 py-1.5 text-sm font-semibold text-white shadow-sm ${colors} ${cls}"
ref=${ref} onclick=${cb} disabled=${disabled || spin} >
${title}
<${spin ? Icons.refresh : icon} class="w-4 ${spin ? 'animate-spin' : ''}" />
<//>`
};
export function Notification({ok, text, close}) {
const closebtn = useRef(null);
const from = 'translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2';
const to = 'translate-y-0 opacity-100 sm:translate-x-0';
const [tr, setTr] = useState(from);
useEffect(function() {
setTr(to);
setTimeout(ev => closebtn && closebtn.current.click && closebtn.current.click(), 1500);
}, []);
const onclose = ev => { setTr(from); setTimeout(close, 300); };
return html`
<div aria-live="assertive" class="z-10 pointer-events-none absolute inset-0 flex items-end px-4 py-6 sm:items-start sm:p-6">
<div class="flex w-full flex-col items-center space-y-4 sm:items-end">
<div class="pointer-events-auto w-full max-w-sm overflow-hidden rounded-lg bg-white shadow-lg ring-1 ring-black ring-opacity-5 transform ease-out duration-300 transition ${tr}">
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<${ok ? Icons.ok : Icons.failed} class="h-6 w-6 ${ok ? 'text-green-400' : 'text-red-400'}" />
<//>
<div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm font-medium text-gray-900">${text}</p>
<p class="hidden mt-1 text-sm text-gray-500">Anyone with a link can now view this file.</p>
<//>
<div class="ml-4 flex flex-shrink-0">
<button type="button" ref=${closebtn} onclick=${onclose} class="inline-flex rounded-md bg-white text-gray-400 hover:text-gray-500 focus:outline-none">
<span class="sr-only">Close</span>
<svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path d="M6.28 5.22a.75.75 0 00-1.06 1.06L8.94 10l-3.72 3.72a.75.75 0 101.06 1.06L10 11.06l3.72 3.72a.75.75 0 101.06-1.06L11.06 10l3.72-3.72a.75.75 0 00-1.06-1.06L10 8.94 6.28 5.22z" />
<//>
<//>
<//>
<//>
<//>
<//>
<//>
<//>`;
};
export function Login({loginFn, logoIcon, title, tipText}) {
const [user, setUser] = useState('');
const [pass, setPass] = useState('');
const onsubmit = function(ev) {
const authhdr = 'Basic ' + btoa(user + ':' + pass);
const headers = {Authorization: authhdr};
return fetch('api/login', {headers}).then(loginFn).finally(r => setPass(''));
};
return html`
<div class="h-full flex items-center justify-center bg-slate-200">
<div class="border rounded bg-white w-96 p-5">
<div class="my-5 py-2 flex items-center justify-center gap-x-4">
<${logoIcon} class="h-12 stroke-cyan-600 stroke-1" />
<h1 class="font-bold text-xl">${title || 'Login'}<//>
<//>
<div class="my-3">
<label class="block text-sm mb-1 dark:text-white">Username</label>
<input type="text" autocomplete="current-user" required
class="font-normal bg-white rounded border border-gray-300 w-full
flex-1 py-0.5 px-2 text-gray-900 placeholder:text-gray-400
focus:outline-none sm:text-sm sm:leading-6 disabled:cursor-not-allowed
disabled:bg-gray-100 disabled:text-gray-500"
oninput=${ev => setUser(ev.target.value)} value=${user} />
<//>
<div class="my-3">
<label class="block text-sm mb-1 dark:text-white">Password</label>
<input type="password" autocomplete="current-password" required
class="font-normal bg-white rounded border border-gray-300 w-full flex-1 py-0.5 px-2 text-gray-900 placeholder:text-gray-400 focus:outline-none sm:text-sm sm:leading-6 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-500"
oninput=${ev => setPass(ev.target.value)}
value=${pass} onchange=${onsubmit} />
<//>
<div class="mt-7">
<${Button} title="Sign In" icon=${Icons.logout} onclick=${onsubmit} cls="flex w-full justify-center" />
<//>
<div class="mt-5 text-slate-400 text-xs">${tipText}<//>
<//>
<//>`;
};
export function Colored({icon, text, colors}) {
colors ||= 'bg-slate-100 text-slate-900';
return html`
<span class="inline-flex items-center gap-1.5 py-0.5">
${icon && html`<${icon} class="w-5 h-5" />`}
<span class="inline-block font-medium rounded-md px-2 py-1 text-xs ring-1 ring-inset ${colors}">${text}<//>
<//>`;
};
export function Stat({title, text, tipText, tipIcon, tipColors, colors}) {
return html`
<div class="flex flex-col bg-white border shadow-sm rounded-xl dark:bg-slate-900 dark:border-gray-800">
<div class="overflow-auto rounded-lg bg-white px-4 py-2 shadow">
<div class="flex items-center gap-x-2">
<p class="text-sm truncate text-gray-500 font-medium"> ${title} </p>
<//>
<div class="mt-1 flex items-center gap-x-2">
<h3 class="text-xl truncate font-semibold tracking-tight ${colors || 'text-gray-800 dark:text-gray-200'}">
${text}
<//>
<span class="flex items-center ${tipText || 'hidden'}">
<${Colored} text=${tipText} icon=${tipIcon} colors=${tipColors} />
<//>
<//>
<//>
<//>`;
};
export function TextValue({value, setfn, disabled, placeholder, type, addonRight, addonLeft, attr, min, max, step}) {
const [bg, setBg] = useState('bg-white');
useEffect(() => { checkval(value); }, []);
const checkval = function(v) {
setBg('bg-white');
if (min && v < min) setBg('bg-red-100 border-red-200');
if (max && v > max) setBg('bg-red-100 border-red-200');
};
const oninput = ev => {
let v = ev.target.value;
if (type == 'number') v = parseInt(v);
setfn(v);
checkval(v);
};
return html`
<div class="flex w-full items-center rounded border shadow-sm ${bg}">
${ addonLeft && html`<span class="inline-flex font-normal truncate py-1 border-r bg-slate-100 items-center border-gray-300 px-2 text-gray-500 text-xs">${addonLeft}<//>` }
<input type=${type || 'text'} disabled=${disabled}
oninput=${oninput} ...${attr}
class="${bg} font-normal text-sm rounded w-full flex-1 py-0.5 px-2 text-gray-700 placeholder:text-gray-400 focus:outline-none disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-500" placeholder=${placeholder} value=${value} />
${ addonRight && html`<span class="inline-flex font-normal truncate py-1 border-l bg-slate-100 items-center border-gray-300 px-2 text-gray-500 text-xs overflow-scroll" style="min-width: 50%;">${addonRight}<//>` }
<//>`;
};
export function SelectValue({value, setfn, options, disabled}) {
const toInt = x => x == parseInt(x) ? parseInt(x) : x;
const onchange = ev => setfn(toInt(ev.target.value));
return html`
<select onchange=${onchange} class="w-full rounded font-normal border py-0.5 px-1 text-gray-600 focus:outline-none text-sm disabled:cursor-not-allowed" disabled=${disabled}>
${options.map(v => html`<option value=${v[0]} selected=${v[0] == value}>${v[1]}<//>`) }
<//>`;
};
export function SwitchValue({value, setfn}) {
const onclick = ev => setfn(!value);
const bg = !!value ? 'bg-blue-600' : 'bg-gray-200';
const tr = !!value ? 'translate-x-5' : 'translate-x-0';
return html`
<button type="button" onclick=${onclick} class="${bg} inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-0 ring-0" role="switch" aria-checked=${!!value}>
<span aria-hidden="true" class="${tr} pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 focus:ring-0 transition duration-200 ease-in-out"></span>
</button>`;
};
export function Setting(props) {
let input = TextValue;
if (props.type == 'switch') input = SwitchValue;
if (props.type == 'select') input = SelectValue;
return html`
<div class=${props.cls || 'grid grid-cols-2 gap-2 my-1'}>
<label class="flex items-center text-sm text-gray-700 mr-2 font-medium">${props.title}<//>
<div class="flex items-center">${h(input, props)}<//>
<//>`;
};
export function Pagination({ totalItems, itemsPerPage, currentPage, setPageFn, colors }) {
const totalPages = Math.ceil(totalItems / itemsPerPage);
const maxPageRange = 2;
const lessThanSymbol = "<";
const greaterThanSymbol = ">";
const whiteSpace = " ";
const itemcls = 'relative inline-flex items-center px-3 py-1 text-sm focus:z-20 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-0 focus-visible:outline-blue-600';
colors ||= 'bg-blue-600';
const PageItem = ({ page, isActive }) => (
html`<a
onClick=${() => setPageFn(page)}
class="${itemcls} ${isActive ? `${colors} text-white` : 'cursor-pointer text-gray-700 ring-1 ring-inset ring-gray-300 hover:bg-gray-50'}"
>
${page}
</a>`
);
return html`
<div class="flex items-center justify-between bg-white px-3 py-2">
<div class="sm:flex sm:flex-1 sm:items-center sm:justify-between space-x-4 whitespace-nowrap select-none">
<p class="text-sm text-slate-500 font-medium">
showing <span class="font-bold text-slate-700">${(currentPage - 1) * itemsPerPage + 1}</span> - <span class="font-medium">${Math.min(currentPage * itemsPerPage, totalItems)}</span> of ${whiteSpace}
<span class="font-bold text-slate-700">${totalItems}</span> results
</p>
<div>
<nav class="isolate inline-flex -space-x-px rounded-md" aria-label="Pagination">
<a
onClick=${() => setPageFn(Math.max(currentPage - 1, 1))}
class="relative inline-flex px-3 items-center text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 ${currentPage != 1 ? 'cursor-pointer' : ''} focus:z-20 focus:outline-offset-0">
${lessThanSymbol}
</a>
<${PageItem} page=${1} isActive=${currentPage === 1} />
${currentPage > maxPageRange + 2 ? html`<span class="${itemcls} ring-1 ring-inset ring-gray-300 text-slate-300">...</span>` : ''}
${Array.from({length: Math.min(totalPages, maxPageRange * 2 + 1)}, (_, i) => Math.max(2, currentPage - maxPageRange) + i).map(page => page > 1 && page < totalPages && html`<${PageItem} page=${page} isActive=${currentPage === page} />`)}
${currentPage < totalPages - (maxPageRange + 1) ? html`<span class="${itemcls} ring-1 ring-inset ring-gray-300 text-slate-300">...</span>` : ''}
${totalPages > 1 ? html`<${PageItem} page=${totalPages} isActive=${currentPage === totalPages} />` : ''}
<a
onClick=${() => setPageFn(Math.min(currentPage + 1, totalPages))}
class="relative inline-flex px-3 items-center text-gray-400 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 ${currentPage != totalPages ? 'cursor-pointer' : ''} focus:z-20 focus:outline-offset-0">
${greaterThanSymbol}
</a>
</nav>
</div>
</div>
</div>`;
};
export function UploadFileButton(props) {
const [upload, setUpload] = useState(null); // Upload promise
const [status, setStatus] = useState(''); // Current upload status
const btn = useRef(null);
const input = useRef(null);
// Send a large file chunk by chunk
const sendFileData = function(url, fileName, fileData, chunkSize) {
return new Promise(function(resolve, reject) {
const finish = ok => {
setUpload(null);
const res = props.onupload ? props.onupload(ok, fileName, fileData.length) : null;
if (res && typeof (res.catch) === 'function') {
res.catch(() => false).then(() => ok ? resolve() : reject());
} else {
ok ? resolve() : reject();
}
};
const sendChunk = function(offset) {
var chunk = fileData.subarray(offset, offset + chunkSize) || '';
var opts = {method: 'POST', body: chunk};
var fullUrl = url + '?offset=' + offset +
'&total=' + fileData.length +
'&name=' + encodeURIComponent(fileName);
var ok;
setStatus('Uploading ' + fileName + ', bytes ' + offset + '..' +
(offset + chunk.length) + ' of ' + fileData.length);
fetch(fullUrl, opts)
.then(function(res) {
if (res.ok && chunk.length > 0) sendChunk(offset + chunk.length);
ok = res.ok;
return res.text();
})
.then(function(text) {
if (!ok) setStatus('Error: ' + text), finish(ok); // Fail
if (chunk.length > 0) return; // More chunks to send
setStatus(x => x + '. Done, resetting device...');
finish(ok); // All chunks sent
});
};
//setFailed(false);
sendChunk(0);
});
};
const onchange = function(ev) {
if (!ev.target.files[0]) return;
let r = new FileReader(), f = ev.target.files[0];
r.readAsArrayBuffer(f);
r.onload = function() {
setUpload(sendFileData(props.url, f.name, new Uint8Array(r.result), 2048));
ev.target.value = '';
ev.preventDefault();
btn && btn.current.base.click();
};
};
const onclick = function(ev) {
let fn; setUpload(x => fn = x);
if (!fn) input.current.click(); // No upload in progress, show file dialog
return fn;
};
return html`
<div class="inline-flex flex-col ${props.class}">
<input class="hidden" type="file" ref=${input} onchange=${onchange} accept=${props.accept} />
<${Button} title=${props.title} icon=${Icons.download} onclick=${onclick} ref=${btn} colors=${props.colors} />
<div class="py-2 text-sm text-slate-400 ${status || 'hidden'}">${status}<//>
<//>`;
};
+1
View File
@@ -0,0 +1 @@
!function(t,n){"object"==typeof exports&&"object"==typeof module?module.exports=n():"function"==typeof define&&define.amd?define([],n):"object"==typeof exports?exports.History=n():t.History=n()}(this,function(){return function(t){function n(o){if(e[o])return e[o].exports;var r=e[o]={exports:{},id:o,loaded:!1};return t[o].call(r.exports,r,r.exports,n),r.loaded=!0,r.exports}var e={};return n.m=t,n.c=e,n.p="",n(0)}([function(t,n,e){"use strict";function o(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0,n.createPath=n.parsePath=n.locationsAreEqual=n.createLocation=n.createMemoryHistory=n.createHashHistory=n.createBrowserHistory=void 0;var r=e(2);Object.defineProperty(n,"createLocation",{enumerable:!0,get:function(){return r.createLocation}}),Object.defineProperty(n,"locationsAreEqual",{enumerable:!0,get:function(){return r.locationsAreEqual}});var i=e(1);Object.defineProperty(n,"parsePath",{enumerable:!0,get:function(){return i.parsePath}}),Object.defineProperty(n,"createPath",{enumerable:!0,get:function(){return i.createPath}});var a=e(7),c=o(a),u=e(8),s=o(u),f=e(9),l=o(f);n.createBrowserHistory=c.default,n.createHashHistory=s.default,n.createMemoryHistory=l.default},function(t,n){"use strict";n.__esModule=!0;n.addLeadingSlash=function(t){return"/"===t.charAt(0)?t:"/"+t},n.stripLeadingSlash=function(t){return"/"===t.charAt(0)?t.substr(1):t},n.stripPrefix=function(t,n){return 0===t.indexOf(n)?t.substr(n.length):t},n.stripTrailingSlash=function(t){return"/"===t.charAt(t.length-1)?t.slice(0,-1):t},n.parsePath=function(t){var n=t||"/",e="",o="",r=n.indexOf("#");r!==-1&&(o=n.substr(r),n=n.substr(0,r));var i=n.indexOf("?");return i!==-1&&(e=n.substr(i),n=n.substr(0,i)),n=decodeURI(n),{pathname:n,search:"?"===e?"":e,hash:"#"===o?"":o}},n.createPath=function(t){var n=t.pathname,e=t.search,o=t.hash,r=encodeURI(n||"/");return e&&"?"!==e&&(r+="?"===e.charAt(0)?e:"?"+e),o&&"#"!==o&&(r+="#"===o.charAt(0)?o:"#"+o),r}},function(t,n,e){"use strict";function o(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0,n.locationsAreEqual=n.createLocation=void 0;var r=Object.assign||function(t){for(var n=1;n<arguments.length;n++){var e=arguments[n];for(var o in e)Object.prototype.hasOwnProperty.call(e,o)&&(t[o]=e[o])}return t},i=e(10),a=o(i),c=e(11),u=o(c),s=e(1);n.createLocation=function(t,n,e,o){var i=void 0;return"string"==typeof t?(i=(0,s.parsePath)(t),i.state=n):(i=r({},t),void 0===i.pathname&&(i.pathname=""),i.search?"?"!==i.search.charAt(0)&&(i.search="?"+i.search):i.search="",i.hash?"#"!==i.hash.charAt(0)&&(i.hash="#"+i.hash):i.hash="",void 0!==n&&void 0===i.state&&(i.state=n)),i.key=e,o&&(i.pathname?"/"!==i.pathname.charAt(0)&&(i.pathname=(0,a.default)(i.pathname,o.pathname)):i.pathname=o.pathname),i},n.locationsAreEqual=function(t,n){return t.pathname===n.pathname&&t.search===n.search&&t.hash===n.hash&&t.key===n.key&&(0,u.default)(t.state,n.state)}},function(t,n,e){"use strict";var o=function(){};t.exports=o},function(t,n,e){"use strict";function o(t){return t&&t.__esModule?t:{default:t}}n.__esModule=!0;var r=e(3),i=(o(r),function(){var t=null,n=function(n){return t=n,function(){t===n&&(t=null)}},e=function(n,e,o,r){if(null!=t){var i="function"==typeof t?t(n,e):t;"string"==typeof i?"function"==typeof o?o(i,r):r(!0):r(i!==!1)}else r(!0)},o=[],r=function(t){var n=!0,e=function(){n&&t.apply(void 0,arguments)};return o.push(e),function(){n=!1,o=o.filter(function(t){return t!==e})}},i=function(){for(var t=arguments.length,n=Array(t),e=0;e<t;e++)n[e]=arguments[e];o.forEach(function(t){return t.apply(void 0,n)})};return{setPrompt:n,confirmTransitionTo:e,appendListener:r,notifyListeners:i}});n.default=i},function(t,n){"use strict";n.__esModule=!0;n.canUseDOM=!("undefined"==typeof window||!window.document||!window.document.createElement),n.addEventListener=function(t,n,e){return t.addEventListener?t.addEventListener(n,e,!1):t.attachEvent("on"+n,e)},n.removeEventListener=function(t,n,e){return t.removeEventListener?t.removeEventListener(n,e,!1):t.detachEvent("on"+n,e)},n.getConfirmation=function(t,n){return n(window.confirm(t))},n.supportsHistory=function(){var t=window.navigator.userAgent;return(t.indexOf("Android 2.")===-1&&t.indexOf("Android 4.0")===-1||t.indexOf("Mobile Safari")===-1||t.indexOf("Chrome")!==-1||t.indexOf("Windows Phone")!==-1)&&(window.history&&"pushState"in window.history)},n.supportsPopStateOnHashChange=function(){return window.navigator.userAgent.indexOf("Trident")===-1},n.supportsGoWithoutReloadUsingHash=function(){return window.navigator.userAgent.indexOf("Firefox")===-1},n.isExtraneousPopstateEvent=function(t){return void 0===t.state&&navigator.userAgent.indexOf("CriOS")===-1}},function(t,n,e){"use strict";var o=function(t,n,e,o,r,i,a,c){if(!t){var u;if(void 0===n)u=new Error("Minified exception occurred; use the non-minified dev environment for the full error message and additional helpful warnings.");else{var s=[e,o,r,i,a,c],f=0;u=new Error(n.replace(/%s/g,function(){return s[f++]})),u.name="InLine truncated
@@ -1,12 +1,15 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en" class="h-full bg-white">
<head> <head>
<title>Device Dashboard</title> <title></title>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="style.css" /> <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke-width='1.5' stroke='currentColor'> <path stroke-linecap='round' stroke-linejoin='round' d='M14.857 17.082a23.848 23.848 0 005.454-1.31A8.967 8.967 0 0118 9.75v-.7V9A6 6 0 006 9v.75a8.967 8.967 0 01-2.312 6.022c1.733.64 3.56 1.085 5.455 1.31m5.714 0a24.255 24.255 0 01-5.714 0m5.714 0a3 3 0 11-5.714 0' /> </svg>" />
<link href="main.css" rel="stylesheet" />
<link href="https://rsms.me/inter/inter.css" rel="stylesheet" />
</head> </head>
<body></body> <body class="h-full"></body>
<script src="history.min.js"></script>
<script type="module" src="main.js"></script> <script type="module" src="main.js"></script>
</html> </html>
@@ -0,0 +1 @@
/*! tailwindcss v3.3.2 | MIT License | https://tailwindcss.com*/*,:after,:before{box-sizing:border-box;border:0 solid #e5e7eb}:after,:before{--tw-content:""}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-family:Inter var,Helvetica,sans-serif;font-feature-settings:"cv11","ss01";font-variation-settings:"opsz" 32}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:initial}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:initial;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:initial}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0}fieldset,legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,::backdrop,:after,:before{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:#3b82f680;--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border-width:0}.pointer-events-none{pointer-events:none}.pointer-events-auto{pointer-events:auto}.fixed{position:fixed}.absolute{position:absolute}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:0}.bottom-0{bottom:0}.left-0{left:0}.right-4{right:1rem}.right-auto{right:auto}.top-0{top:0}.top-4{top:1rem}.isolate{isolation:isolate}.z-10{z-index:10}.z-\[48\]{z-index:48}.z-\[60\]{z-index:60}.col-span-2{grid-column:span 2/span 2}.m-4{margin:1rem}.mx-auto{margin-left:auto;margin-right:auto}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.my-3{margin-top:.75rem;margin-bottom:.75rem}.my-4{margin-top:1rem;margin-bottom:1rem}.my-5{margin-top:1.25rem;margin-bottom:1.25rem}.mb-1{margin-bottom:.25rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-2{margin-right:.5rem}.mr-4{margin-right:1rem}.mt-1{margin-top:.25rem}.mt-3{margin-top:.75rem}.mt-5{margin-top:1.25rem}.mt-7{margin-top:1.75rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.table{display:table}.grid{display:grid}.hidden{display:none}.h-10{height:2.5rem}.h-12{height:3rem}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-0{width:0}.w-11{width:2.75rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-6{width:1.5rem}.w-72{width:18rem}.w-96{width:24rem}.w-full{width:100%}.min-w-full{min-width:100%}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.shrink{flex-shrink:1}.shrink-0{flex-shrink:0}.grow-0{flex-grow:0}.basis-\[30px\]{flex-basis:30px}.border-separate{border-collapse:initialLine truncated
+342 -364
View File
@@ -1,385 +1,363 @@
// NOTE: API calls must start with 'api/' in order to serve the app at any URI
'use strict'; 'use strict';
import {Component, h, html, render, useEffect, useState, useRef} from './preact.min.js'; import { h, render, useState, useEffect, useRef, html, Router } from './bundle.js';
import { Icons, Login, Setting, Button, Stat, tipColors, Colored, Notification, Pagination, UploadFileButton } from './components.js';
var devaddr = "address:port"; const Logo = props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 12.87 12.85"><defs><style>.ll-cls-1{fill:none;stroke:#000;stroke-miterlimit:10;stroke-width:0.5px;}</style></defs><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="ll-cls-1" d="M12.62,1.82V8.91A1.58,1.58,0,0,1,11,10.48H4a1.44,1.44,0,0,1-1-.37A.69.69,0,0,1,2.84,10l-.1-.12a.81.81,0,0,1-.15-.48V5.57a.87.87,0,0,1,.86-.86H4.73V7.28a.86.86,0,0,0,.86.85H9.42a.85.85,0,0,0,.85-.85V3.45A.86.86,0,0,0,10.13,3,.76.76,0,0,0,10,2.84a.29.29,0,0,0-.12-.1,1.49,1.49,0,0,0-1-.37H2.39V1.82A1.57,1.57,0,0,1,4,.25H11A1.57,1.57,0,0,1,12.62,1.82Z"/><path class="ll-cls-1" d="M10.48,10.48V11A1.58,1.58,0,0,1,8.9,12.6H1.82A1.57,1.57,0,0,1,.25,11V3.94A1.57,1.57,0,0,1,1.82,2.37H8.9a1.49,1.49,0,0,1,1,.37l.12.1a.76.76,0,0,1,.11.14.86.86,0,0,1,.14.47V7.28a.85.85,0,0,1-.85.85H8.13V5.57a.86.86,0,0,0-.85-.86H3.45a.87.87,0,0,0-.86.86V9.4a.81.81,0,0,0,.15.48l.1.12a.69.69,0,0,0,.13.11,1.44,1.44,0,0,0,1,.37Z"/></g></g></svg>`;
const MaxMetricsDataPoints = 50; function Header({logout, user, setShowSidebar, showSidebar}) {
// This simple publish/subscribe is used to pass notifications that were
// received from the server, to all child components of the app.
var PubSub = (function() {
var handlers = {}, id = 0;
return {
subscribe: function(fn) {
handlers[id++] = fn;
},
unsubscribe: function(id) {
delete handlers[id];
},
publish: function(data) {
for (var k in handlers) handlers[k](data);
}
};
})();
const Nav = props => html`
<div style="background: #333; padding: 0.5em; color: #fff;">
<div class="container d-flex">
<div style="flex: 1 1 auto; display: flex; align-items: center;">
<b>Your Product</b>
</div>
<div style="display: flex; align-items: center; flex: 0 0 auto; ">
<span>Logged in as:</span>
<span style="padding: 0 0.5em;"><img src="user.png" height="22" /></span>
<span>${props.user}</span>
<a class="btn" onclick=${props.logout}
style="margin-left: 1em; font-size: 0.8em; background: #8aa;">logout</a>
</div>
</div>
</div>`;
const Hero = props => html`
<div class="section">
<div style="margin-top: 1em; background: #eee; padding: 1em; border-radius: 0.5em; color: #777; ">
<h1 style="margin: 0.2em 0;">Interactive Device Dashboard</h1>
<p>
This device dashboard is developed using the modern and compact Preact framework,
in order to fit on very small devices. This is
a <a href="https://mongoose.ws/tutorials/http-server/">hybrid server</a> which
provides both static and dynamic content. Static files, like CSS/JS/HTML
or images, are compiled into the server binary.
This UI uses the REST API implemented by the device, which you can examine
using <code>curl</code> command-line utility:
</p>
<div><code>curl -u admin:pass0 ${devaddr}/api/config/get</code> </div>
<div><code>curl -u admin:pass0 ${devaddr}/api/config/set -d 'pub=mg/topic'</code> </div>
<div><code>curl -u admin:pass0 ${devaddr}/api/message/send -d 'message=hello'</code> </div>
<p>
The device can send notifications to this dashboard at anytime. Notifications
are sent over WebSocket at URI <code>/api/watch</code> as JSON strings: <code>{"name": "..", "data": ...}</code>
<div>Try <code>wscat --auth user1:pass1 --connect ws://${devaddr}/api/watch</code></div>
</p>
</div>
</div>`;
const Login = function(props) {
const [user, setUser] = useState('');
const [pass, setPass] = useState('');
const login = ev =>
fetch(
'/api/login',
{headers: {Authorization: 'Basic ' + btoa(user + ':' + pass)}})
.then(r => r.json())
.then(r => r && props.login(r))
.catch(err => err);
return html` return html`
<div class="rounded border" style="max-width: 480px; margin: 0 auto; margin-top: 5em; background: #eee; "> <div class="bg-white sticky top-0 z-[48] xw-full border-b py-2 ${showSidebar && 'pl-72'} transition-all duration-300 transform">
<div style="padding: 2em; "> <div class="px-2 w-full py-0 my-0 flex items-center">
<h1 style="color: #666;">Device Dashboard Login </h1> <button type="button" onclick=${ev => setShowSidebar(v => !v)} class="text-slate-400">
<div style="margin: 0.5em 0;"> <${Icons.bars3} class="h-6" />
<input type='text' placeholder='Name' style="width: 100%;" <//>
oninput=${ev => setUser(ev.target.value)} value=${user} /> <div class="flex flex-1 gap-x-4 self-stretch lg:gap-x-6">
</div> <div class="relative flex flex-1"><//>
<div style="margin: 0.5em 0;"> <div class="flex items-center gap-x-4 lg:gap-x-6">
<input type="password" placeholder="Password" style="width: 100%;" <span class="text-sm text-slate-400">logged in as: ${user}<//>
oninput=${ev => setPass(ev.target.value)} value=${pass} <div class="hidden lg:block lg:h-4 lg:w-px lg:bg-gray-200" aria-hidden="true"><//>
onchange=${login} /> <${Button} title="Logout" icon=${Icons.logout} onclick=${logout} />
</div> <//>
<div style="margin: 1em 0;"> <//>
<button class="btn" style="width: 100%; background: #8aa;" <//>
disabled=${!user || !pass} onclick=${login}> Login </button> <//>`;
</div>
<div style="color: #777; margin-top: 2em;">
Valid logins: admin:pass0, user1:pass1, user2:pass2
</div>
</div>
</div>`;
}; };
function Sidebar({url, show}) {
const Configuration = function(props) { const NavLink = ({title, icon, href, url}) => html`
const [url, setUrl] = useState(props.config.url || '');
const [pub, setPub] = useState(props.config.pub || '');
const [sub, setSub] = useState(props.config.sub || '');
useEffect(() => {
setUrl(props.config.url);
setPub(props.config.pub);
setSub(props.config.sub);
}, [props.config]);
const update = (name, val) => fetch('/api/config/set', {
method: 'post',
body: `${name}=${encodeURIComponent(val)}`
}).catch(err => err);
const updateurl = ev => update('url', url);
const updatepub = ev => update('pub', pub);
const updatesub = ev => update('sub', sub);
// console.log(props, [url, pub, sub]);
return html`
<div class="section">
<h3 style="background: #c03434; color: #fff; padding: 0.4em;">
Device Configuration</h3>
<div style="margin: 0.5em 0; display: flex;">
<span class="addon nowrap">MQTT server:</span>
<input type="text" style="flex: 1 100%;"
value=${url} onchange=${updateurl}
oninput=${ev => setUrl(ev.target.value)} />
<button class="btn" disabled=${!url} onclick=${updateurl}
style="margin-left: 1em; background: #8aa;">Update</button>
</div>
<div style="margin: 0.5em 0; display: flex; ">
<span class="addon nowrap">Subscribe topic:</span>
<input type="text" style="flex: 1 100%;"
value=${sub} onchange=${updatesub}
oninput=${ev => setSub(ev.target.value)} />
<button class="btn" disabled=${!sub} onclick=${updatesub}
style="margin-left: 1em; background: #8aa;">Update</button>
</div>
<div style="margin: 0.5em 0; display: flex;">
<span class="addon nowrap">Publish topic:</span>
<input type="text" style="flex: 1 100%;"
value=${pub} onchange=${updatepub}
oninput=${ev => setPub(ev.target.value)} />
<button class="btn" disabled=${!pub} onclick=${updatepub}
style="margin-left: 1em; background: #8aa;">Update</button>
</div>
<div> <div>
You can use <a href="http://www.hivemq.com/demos/websocket-client/"> <a href="#${href}" class="${href == url ? 'bg-slate-50 text-blue-600 group' : 'text-gray-700 hover:text-blue-600 hover:bg-gray-50 group'} flex gap-x-3 rounded-md p-2 text-sm leading-6 font-semibold">
HiveMQ Websocket web client</a> to send messages to this console. <${icon} class="w-6 h-6"/>
</div> ${title}
<div class="msg"> <///>
The device keeps a persistent connection to the configured MQTT server. <//>`;
Changes to this configuration are propagated to all dashboards: try return html`
changing them in this dashboard and observe changes in other opened <div class="bg-violet-100 hs-overlay hs-overlay-open:translate-x-0
dashboards. -translate-x-full transition-all duration-300 transform
</div><div class="msg"> fixed top-0 left-0 bottom-0 z-[60] w-72 bg-white border-r
Note: administrators can see this section and can change device border-gray-200 overflow-y-auto scrollbar-y
configuration, whilst users cannot. ${show && 'translate-x-0'} right-auto bottom-0">
</div> <div class="flex flex-col m-4 gap-y-6">
</div>`; <div class="flex h-10 shrink-0 items-center gap-x-4 font-bold text-xl text-slate-500">
<${Logo} class="h-full"/> Your Brand
<//>
<div class="flex flex-1 flex-col">
<${NavLink} title="Dashboard" icon=${Icons.home} href="/" url=${url} />
<${NavLink} title="Settings" icon=${Icons.cog} href="/settings" url=${url} />
<${NavLink} title="Firmware Update" icon=${Icons.download} href="/update" url=${url} />
<${NavLink} title="Events" icon=${Icons.alert} href="/events" url=${url} />
<//>
<//>
<//>`;
}; };
function Events({}) {
const [events, setEvents] = useState([]);
const [page, setPage] = useState(1);
const Message = m => html`<div style="margin: 0.5em 0;"> const refresh = () =>
<span class="qos">qos: ${m.message.qos} </span> fetch('api/events/get', {
<span class="topic">topic: ${m.message.topic} </span> method: 'POST', body: JSON.stringify({page: page}),
<span class="data">data: ${m.message.data}</span> }).then(r => r.json())
</div>`; .then(r => setEvents(r));
const Messages = function(props) { useEffect(refresh, [page]);
const [messages, setMessages] = useState([]);
const [txt, setTxt] = useState('');
useEffect(() => { useEffect(() => {
const id = PubSub.subscribe(function(msg) { setPage(JSON.parse(localStorage.getItem('page')));
if (msg.name == 'message') setMessages(x => x.concat([msg.data])); }, []);
});
return PubSub.unsubscribe(id);
}, []);
const sendmessage = ev => fetch('/api/message/send', {
method: 'post',
body: `message=${encodeURIComponent(txt)}`
}).then(r => setTxt(''));
const routing = "connected" in props.config;
const connstatus = !routing ? 'This device has no MQTT functionality' : props.config.connected ? 'connected' : 'disconnected';
return html`
<div class="section">
<h3 style="background: #30c040; color: #fff; padding: 0.4em;">MQTT messages</h3>
<div>
MQTT server status: <b>${connstatus}</b>
</div>
<div style="height: 10em; overflow: auto; padding: 0.5em; " class="border">
${messages.map(message => h(Message, {message}))}
</div>
<div style="margin: 0.5em 0; display: flex">
<span class="addon nowrap">Publish message:</span>
<input placeholder="type and press enter..." style="flex: 1 100%;"
value=${txt} onchange=${sendmessage} disabled=${!routing}
oninput=${ev => setTxt(ev.target.value)} />
</div>
<div class="msg">
The message gets passed to the device via REST. Then the device sends it to
the MQTT server over MQTT. All MQTT messages on a subscribed topic
received by the device, are propagated to this dashboard via /api/watch.
</div>
</div>`;
};
// Expected arguments:
// data: timeseries, e.g. [ [1654361352, 19], [1654361353, 18], ... ]
// width, height, yticks, xticks, ymin, ymax, xmin, xmax
const SVG = function(props) {
// w
// +---------------------+
// | h1 |
// | +-----------+ |
// | | | | h
// | w1 | | w2 |
// | +-----------+ |
// | h2 |
// +---------------------+
//
let w = props.width, h = props.height, w1 = 30, w2 = 0, h1 = 8, h2 = 18;
let yticks = props.yticks || 4, xticks = props.xticks || 5;
let data = props.data || [];
let ymin = props.ymin || 0;
let ymax = props.ymax || Math.max.apply(null, data.map(p => p[1]));
let xmin = props.xmin || Math.min.apply(null, data.map(p => p[0]));
let xmax = props.xmax || Math.max.apply(null, data.map(p => p[0]));
// Y-axis tick lines and labels
let yta = (new Array(yticks + 1)).fill(0).map((_, i) => i); // indices
let yti = i => h - h2 - (h - h1 - h2) * i / yticks; // index's Y
let ytv = i => (ymax - ymin) * i / yticks;
let ytl = y => html`<line x1=${w1} y1=${y} x2=${w} y2=${y} class="tick"/>`;
let ytt = (y, v) => html`<text x=0 y=${y + 5} class="label">${v}</text>`;
// X-axis tick lines and labels
let datefmt = unix => (new Date(unix * 1000)).toISOString().substr(14, 5);
let xta = (new Array(xticks + 1)).fill(0).map((_, i) => i); // indices
let xti = i => w1 + (w - w1 - w2) * i / xticks; // index's X
let xtv = i => datefmt(xmin + (xmax - xmin) * i / xticks);
let xtl = x => html`<path d="M ${x},${h1} L ${x},${h - h2}" class="tick"/>`;
let xtt = (x, v) =>
html`<text x=${x - 15} y=${h - 2} class="label">${v}</text>`;
// Transform data points array into coordinate
let dx = v => w1 + (v - xmin) / ((xmax - xmin) || 1) * (w - w1 - w2);
let dy = v => h - h2 - (v - ymin) / ((ymax - ymin) || 1) * (h - h1 - h2);
let dd = data.map(p => [Math.round(dx(p[0])), Math.round(dy(p[1]))]);
let ddl = dd.length;
// And plot the data as <path> element
let begin0 = ddl ? `M ${dd[0][0]},${dd[0][1]}` : `M 0,0`;
let begin = `M ${w1},${h - h2}`; // Initial point
let end = ddl ? `L ${dd[ddl - 1][0]},${h - h2}` : `L ${w1},${h - h2}`;
let series = ddl ? dd.map(p => `L ${p[0]} ${p[1]}`) : [];
return html`
<svg viewBox="0 0 ${w} ${h}">
<style>
.axis { stroke: #aaa; fill: none; }
.label { stroke: #aaa; font-size: 13px; }
.tick { stroke: #ccc; fill: none; stroke-dasharray: 5; }
.seriesbg { stroke: none; fill: rgba(200,225,255, 0.25)}
.series { stroke: #25a; fill: none; }
</style>
${yta.map(i => ytl(yti(i)))}
${yta.map(i => ytt(yti(i), ytv(i)))}
${xta.map(i => xtl(xti(i)))}
${data.length ? xta.map(i => xtt(xti(i), xtv(i))) : ''}
<path d="${begin} ${series.join(' ')} ${end}" class="seriesbg" />
<path d="${begin0} ${series.join(' ')}" class="series" />
</svg>`;
};
const Chart = function(props) {
const [data, setData] = useState([]);
useEffect(() => {
const id = PubSub.subscribe(function(msg) {
if (msg.name != 'metrics') return;
setData(x => x.concat([msg.data]).splice(-MaxMetricsDataPoints));
});
return PubSub.unsubscribe(id);
}, []);
let xmax = 0, missing = MaxMetricsDataPoints - data.length;
if (missing > 0) xmax = Math.round(Date.now() / 1000) + missing;
return html`
<div class="section">
<h3 style="background: #ec3; color: #fff; padding: 0.4em;">Data Chart</h3>
<div style="overflow: auto; padding: 0.5em;" class="">
<${SVG} height=240 width=600 ymin=0 ymax=20 xmax=${xmax} data=${data} />
</div>
<div class="msg">
This chart plots live sensor data, sent by the device via /api/watch.
</div>
</div>`;
};
const App = function(props) {
const [user, setUser] = useState('');
const [config, setConfig] = useState({});
const getconfig = () =>
fetch('/api/config/get', {headers: {Authorization: ''}})
.then(r => r.json())
.then(r => setConfig(r))
.catch(err => console.log(err));
// Watch for notifications. As soon as a notification arrives, pass it on
// to all subscribed components
const watch = function() {
var l = window.location, proto = l.protocol.replace('http', 'ws');
var tid, wsURI = proto + '//' + l.host + '/api/watch'
var reconnect = function() {
var ws = new WebSocket(wsURI);
// ws.onopen = () => console.log('ws connected');
ws.onmessage = function(ev) {
try {
var msg = JSON.parse(ev.data);
PubSub.publish(msg);
// if (msg.name != 'metrics') console.log('ws->', msg);
} catch (e) {
console.log('Invalid ws frame:', ev.data); // eslint-disable-line
}
};
ws.onclose = function() {
clearTimeout(tid);
tid = setTimeout(reconnect, 1000);
console.log('ws disconnected');
};
};
reconnect();
};
const login = function(u) {
document.cookie = `access_token=${u.token}; Secure, HttpOnly; SameSite=Lax; path=/; max-age=3600`;
setUser(u.user);
if (location.search.substring(1) == 'nows') {
// If query string is ?nows, then do not connect to websocket. For debug.
} else {
watch(); // Connect to websocket, receive constant graph updates
}
return getconfig();
};
const logout = ev => {
document.cookie = `access_token=; Secure, HttpOnly; SameSite=Lax; path=/; max-age=0`;
setUser('');
};
useEffect(() => { useEffect(() => {
// Called once at init time localStorage.setItem('page', page.toString());
PubSub.subscribe(msg => msg.name == 'config' && getconfig()); }, [page]);
fetch('/api/login', {headers: {Authorization: ''}})
.then(r => r.json())
.then(r => login(r))
.catch(err => setUser(''));
}, []);
if (!user) return html`<${Login} login=${login} />`; const Th = props => html`<th scope="col" class="sticky top-0 z-10 border-b border-slate-300 bg-white bg-opacity-75 py-1.5 px-4 text-left text-sm font-semibold text-slate-900 backdrop-blur backdrop-filter">${props.title}</th>`;
const Td = props => html`<td class="whitespace-nowrap border-b border-slate-200 py-2 px-4 pr-3 text-sm text-slate-900">${props.text}</td>`;
const Prio = ({prio}) => {
const text = ['high', 'medium', 'low'][prio];
const colors = [tipColors.red, tipColors.yellow, tipColors.green][prio];
return html`<${Colored} colors=${colors} text=${text} />`;
};
return html` const Event = ({e}) => html`
<${Nav} user=${user} logout=${logout} /> <tr>
<div class="container"> <${Td} text=${['power', 'hardware', 'tier3', 'tier4'][e.type]} />
<div class="row"> <${Td} text=${html`<${Prio} prio=${e.prio}/>`} />
<div class="col col-6"><${Hero} /></div> <${Td} text=${e.time ? (new Date(e.time * 1000)).toLocaleString() : '1970-01-01'} />
<div class="col col-6"><${Chart} /></div> <${Td} text=${e.text} />
<div class="col col-6"> <//>`;
${user == 'admin' && h(Configuration, {config})}
return html`
<div class="m-4 divide-y divide-gray-200 overflow-auto rounded bg-white">
<div class="font-semibold flex items-center text-gray-600 px-3 justify-between whitespace-nowrap">
<div class="font-semibold flex items-center text-gray-600">
<div class="mr-4">EVENT LOG</div>
</div> </div>
<div class="col col-6"><${Messages} config=${config} /></div> <${Pagination} currentPage=${page} setPageFn=${setPage} totalItems=400 itemsPerPage=20 />
</div> <//>
</div>`; <div class="inline-block min-w-full align-middle" style="max-height: 82vh; overflow: auto;">
<table class="min-w-full border-separate border-spacing-0">
<thead>
<tr>
<${Th} title="Type" />
<${Th} title="Prio" />
<${Th} title="Time" />
<${Th} title="Description" />
</tr>
</thead>
<tbody>
${(events.arr ? events.arr : []).map(e => h(Event, {e}))}
</tbody>
</table>
<//>
<//>`;
};
function Chart({data}) {
const n = data.length /* entries */, w = 20 /* entry width */, ls = 15/* left space */;
const h = 100 /* graph height */, yticks = 5 /* Y axis ticks */, bs = 10 /* bottom space */;
const ymax = 25;
const yt = i => (h - bs) / yticks * (i + 1);
const bh = p => (h - bs) * p / 100; // Bar height
const by = p => (h - bs) - bh(p);
const range = (start, size, step) => Array.from({length: size}, (_, i) => i * (step || 1) + start);
// console.log(ds);
return html`
<div class="my-4 divide-y divide-gray-200 overflow-auto rounded bg-white">
<div class="font-light uppercase flex items-center text-gray-600 px-4 py-2">
Temperature, last 24h
<//>
<div class="relative">
<svg class="bg-yellow-x50 w-full p-4" viewBox="0 0 ${n*w+ls} ${h}">
${range(0, yticks).map(i => html`
<line x1=0 y1=${yt(i)} x2=${ls+n*w} y2=${yt(i)} stroke-width=0.3 class="stroke-slate-300" stroke-dasharray="1,1" />
<text x=0 y=${yt(i)-2} class="text-[6px] fill-slate-400">${ymax-ymax/yticks*(i+1)}<//>
`)}
${range(0, n).map(x => html`
<rect x=${ls+x*w} y=${by(data[x]*100/ymax)} width=12 height=${bh(data[x]*100/ymax)} rx=2 class="fill-cyan-500" />
<text x=${ls+x*w} y=100 class="text-[6px] fill-slate-400">${x*2}:00<//>
`)}
<//>
<//>
<//>`;
};
function DeveloperNote({text, children}) {
return html`
<div class="flex p-4 gap-2">
<div class="text-sm text-slate-500">
<div class="flex items-center">
<${Icons.info} class="self-start basis-[30px] grow-0 shrink-0 text-green-600 mr-2" />
<div class="font-semibold">Developer Note<//>
<//>
${(text || '').split('.').map(v => html` <p class="my-2 ">${v}<//>`)}
${children}
<//>
<//>`;
};
function Main({}) {
const [stats, setStats] = useState(null);
const refresh = () => fetch('api/stats/get').then(r => r.json()).then(r => setStats(r));
useEffect(refresh, []);
if (!stats) return '';
return html`
<div class="p-2">
<div class="p-4 sm:p-2 mx-auto grid grid-cols-2 lg:grid-cols-4 gap-4">
<${Stat} title="Temperature" text="${stats.temperature} °C" tipText="good" tipIcon=${Icons.ok} tipColors=${tipColors.green} />
<${Stat} title="Humidity" text="${stats.humidity} %" tipText="warn" tipIcon=${Icons.warn} tipColors=${tipColors.yellow} />
<div class="bg-white col-span-2 border rounded-md shadow-lg" role="alert">
<${DeveloperNote} text="Stats data is received from the Mongoose backend" />
<//>
<//>
<div class="p-4 sm:p-2 mx-auto grid grid-cols-1 lg:grid-cols-2 gap-4">
<${Chart} data=${stats.points} />
<div class="my-4 hx-24 bg-white border rounded-md shadow-lg" role="alert">
<${DeveloperNote}
text="This chart is an SVG image, generated on the fly from the
data returned by the api/stats/get API call" />
<//>
<//>
<//>`;
};
function FirmwareStatus({title, info, children}) {
const state = ['UNAVAILABLE', 'FIRST_BOOT', 'NOT_COMMITTED', 'COMMITTED'][(info.status || 0) % 4];
const valid = info.status > 0;
return html`
<div class="bg-white py-1 divide-y border rounded">
<div class="font-light uppercase flex items-center text-gray-600 px-4 py-2">
${title}
<//>
<div class="px-4 py-2 relative">
<div class="my-1">Status: ${state}<//>
<div class="my-1">CRC32: ${valid ? info.crc32.toString(16) : 'n/a'}<//>
<div class="my-1">Size: ${valid ? info.size : 'n/a'}<//>
<div class="my-1">Flashed at: ${valid ? new Date(info.timestamp * 1000).toLocaleString() : 'n/a'}<//>
${children}
<//>
<//>`;
};
function FirmwareUpdate({}) {
const [info, setInfo] = useState([{}, {}]);
const refresh = () => fetch('api/firmware/status').then(r => r.json()).then(r => setInfo(r));
useEffect(refresh, []);
const oncommit = ev => fetch('api/firmware/commit')
.then(r => r.json())
.then(refresh);
const onreboot = ev => fetch('api/device/reset')
.then(r => r.json())
.then(r => new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000)));
const onrollback = ev => fetch('api/firmware/rollback')
.then(onreboot);
const onupload = function(ok, name, size) {
if (!ok) return false;
return new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000));
};
const clean = info[0].valid && info[0].golden == 0;
return html`
<div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-2">
<${FirmwareStatus} title="Current firmware image" info=${info[0]}>
<div class="flex flex-wrap gap-2">
<${Button} title="Commit this firmware"
onclick=${oncommit} icon=${Icons.thumbUp} disabled=${clean} />
<${Button} title="Reboot device" onclick=${onreboot} icon=${Icons.refresh} clsx="absolute top-4 right-4" />
<${UploadFileButton}
title="Upload new firmware: choose .bin file:" onupload=${onupload}
url="api/firmware/upload" accept=".bin,.uf2" />
<//>
<//>
<${FirmwareStatus} title="Previous firmware image" info=${info[1]}>
<${Button} title="Rollback to this firmware" onclick=${onrollback}
icon=${Icons.backward} disabled=${info[1].valid == false} />
<//>
<div class="bg-white border shadow-lg">
<${DeveloperNote}>
<div class="my-2">
When a new firmware gets flashed, its status is marked as, "first_boot".
That is an unreliable (uncommitted) firmware. A user may choose
to revert back to the previous committed firmware on the subsequent
boots. Clicking on the "commit" button calls "mg_ota_commit()" function
which commits the firmware.
<//>
<div class="my-2">
This GUI loads a firmware file and sends it chunk by chunk to the
device, passing current chunk offset, total firmware size and a file name:
api/firmware/upload?offset=X&total=Y&name=Z
<//>
<//>
<//>
<div class="bg-white border shadow-lg">
<${DeveloperNote}>
<div>
Firmware update mechanism defines 3 API functions that the target
device must implement: mg_ota_begin(), mg_ota_write() and mg_ota_end()
<//>
<div class="my-2">
RESTful API handlers use ota_xxx() API to save firmware to flash.
The last 0-length chunk triggers ota_end() which performs firmware
update using saved firmware image
<//>
<div class="my-2">
<a class="link text-blue-600 underline"
href="https://mongoose.ws/webinars/">Join our free webinar</a> to
get detailed explanations about possible firmware updates strategies
and implementation demo
<//>
<//>
<//>
<//>`;
};
function Settings({}) {
const [settings, setSettings] = useState(null);
const [saveResult, setSaveResult] = useState(null);
const refresh = () => fetch('api/settings/get')
.then(r => r.json())
.then(r => setSettings(r));
useEffect(refresh, []);
const mksetfn = k => (v => setSettings(x => Object.assign({}, x, {[k]: v})));
const onsave = ev => fetch('api/settings/set', {
method: 'post', body: JSON.stringify(settings)
}).then(r => r.json())
.then(r => setSaveResult(r))
.then(refresh);
if (!settings) return '';
const logOptions = [[0, 'Disable'], [1, 'Error'], [2, 'Info'], [3, 'Debug']];
return html`
<div class="m-4 grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="py-1 divide-y border rounded bg-white flex flex-col">
<div class="font-light uppercase flex items-center text-gray-600 px-4 py-2">
Device Settings
<//>
<div class="py-2 px-5 flex-1 flex flex-col relative">
${saveResult && html`<${Notification} ok=${saveResult.status}
text=${saveResult.message} close=${() => setSaveResult(null)} />`}
<${Setting} title="Enable Logs" value=${settings.log_enabled} setfn=${mksetfn('log_enabled')} type="switch" />
<${Setting} title="Log Level" value=${settings.log_level} setfn=${mksetfn('log_level')} type="select" addonLeft="0-3" disabled=${!settings.log_enabled} options=${logOptions}/>
<${Setting} title="Brightness" value=${settings.brightness} setfn=${mksetfn('brightness')} type="number" addonRight="%" />
<${Setting} title="Device Name" value=${settings.device_name} setfn=${mksetfn('device_name')} type="" />
<div class="mb-1 mt-3 flex place-content-end"><${Button} icon=${Icons.save} onclick=${onsave} title="Save Settings" /><//>
<//>
<//>
<div class="bg-white border rounded-md text-ellipsis overflow-auto" role="alert">
<${DeveloperNote}
text="A variety of controls are pre-defined to ease the development:
toggle button, dropdown select, input field with left and right
addons. Device settings are received by calling
api/settings/get API call, which returns settings JSON object.
Clicking on the save button calls api/settings/set
API call" />
<//>
<//>`;
};
const App = function({}) {
const [loading, setLoading] = useState(true);
const [url, setUrl] = useState('/');
const [user, setUser] = useState('');
const [showSidebar, setShowSidebar] = useState(true);
const logout = () => fetch('api/logout').then(r => setUser(''));
const login = r => !r.ok ? setLoading(false) && setUser(null) : r.json()
.then(r => setUser(r.user))
.finally(r => setLoading(false));
useEffect(() => fetch('api/login').then(login), []);
if (loading) return ''; // Show blank page on initial load
if (!user) return html`<${Login} loginFn=${login} logoIcon=${Logo}
title="Device Dashboard Login"
tipText="To login, use: admin/admin, user1/user1, user2/user2" />`; // If not logged in, show login screen
return html`
<div class="min-h-screen bg-slate-100">
<${Sidebar} url=${url} show=${showSidebar} />
<${Header} logout=${logout} user=${user} showSidebar=${showSidebar} setShowSidebar=${setShowSidebar} />
<div class="${showSidebar && 'pl-72'} transition-all duration-300 transform">
<${Router} onChange=${ev => setUrl(ev.url)} history=${History.createHashHistory()} >
<${Main} default=${true} />
<${Settings} path="settings" />
<${FirmwareUpdate} path="update" />
<${Events} path="events" />
<//>
<//>
<//>`;
}; };
window.onload = () => render(h(App), document.body); window.onload = () => render(h(App), document.body);
-1
View File
@@ -1 +0,0 @@
var e,n,_,t,o,r,u,l={},i=[],c=/acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i;function s(e,n){for(var _ in n)e[_]=n[_];return e}function f(e){var n=e.parentNode;n&&n.removeChild(e)}function a(n,_,t){var o,r,u,l={};for(u in _)"key"==u?o=_[u]:"ref"==u?r=_[u]:l[u]=_[u];if(arguments.length>2&&(l.children=arguments.length>3?e.call(arguments,2):t),"function"==typeof n&&null!=n.defaultProps)for(u in n.defaultProps)void 0===l[u]&&(l[u]=n.defaultProps[u]);return p(n,l,o,r,null)}function p(e,t,o,r,u){var l={type:e,props:t,key:o,ref:r,__k:null,__:null,__b:0,__e:null,__d:void 0,__c:null,__h:null,constructor:void 0,__v:null==u?++_:u};return null!=n.vnode&&n.vnode(l),l}function h(e){return e.children}function d(e,n){this.props=e,this.context=n}function v(e,n){if(null==n)return e.__?v(e.__,e.__.__k.indexOf(e)+1):null;for(var _;n<e.__k.length;n++)if(null!=(_=e.__k[n])&&null!=_.__e)return _.__e;return"function"==typeof e.type?v(e):null}function y(e){var n,_;if(null!=(e=e.__)&&null!=e.__c){for(e.__e=e.__c.base=null,n=0;n<e.__k.length;n++)if(null!=(_=e.__k[n])&&null!=_.__e){e.__e=e.__c.base=_.__e;break}return y(e)}}function m(e){(!e.__d&&(e.__d=!0)&&t.push(e)&&!g.__r++||r!==n.debounceRendering)&&((r=n.debounceRendering)||o)(g)}function g(){for(var e;g.__r=t.length;)e=t.sort(function(e,n){return e.__v.__b-n.__v.__b}),t=[],e.some(function(e){var n,_,t,o,r,u;e.__d&&(r=(o=(n=e).__v).__e,(u=n.__P)&&(_=[],(t=s({},o)).__v=o.__v+1,P(u,o,t,n.__n,void 0!==u.ownerSVGElement,null!=o.__h?[r]:null,_,null==r?v(o):r,o.__h),D(_,o),o.__e!=r&&y(o)))})}function k(e,n,_,t,o,r,u,c,s,f){var a,d,y,m,g,k,x,H=t&&t.__k||i,E=H.length;for(_.__k=[],a=0;a<n.length;a++)if(null!=(m=_.__k[a]=null==(m=n[a])||"boolean"==typeof m?null:"string"==typeof m||"number"==typeof m||"bigint"==typeof m?p(null,m,null,null,m):Array.isArray(m)?p(h,{children:m},null,null,null):m.__b>0?p(m.type,m.props,m.key,null,m.__v):m)){if(m.__=_,m.__b=_.__b+1,null===(y=H[a])||y&&m.key==y.key&&m.type===y.type)H[a]=void 0;else for(d=0;d<E;d++){if((y=H[d])&&m.key==y.key&&m.type===y.type){H[d]=void 0;break}y=null}P(e,m,y=y||l,o,r,u,c,s,f),g=m.__e,(d=m.ref)&&y.ref!=d&&(x||(x=[]),y.ref&&x.push(y.ref,null,m),x.push(d,m.__c||g,m)),null!=g?(null==k&&(k=g),"function"==typeof m.type&&null!=m.__k&&m.__k===y.__k?m.__d=s=b(m,s,e):s=C(e,m,y,H,g,s),f||"option"!==_.type?"function"==typeof _.type&&(_.__d=s):e.value=""):s&&y.__e==s&&s.parentNode!=e&&(s=v(y))}for(_.__e=k,a=E;a--;)null!=H[a]&&("function"==typeof _.type&&null!=H[a].__e&&H[a].__e==_.__d&&(_.__d=v(t,a+1)),U(H[a],H[a]));if(x)for(a=0;a<x.length;a++)T(x[a],x[++a],x[++a])}function b(e,n,_){var t,o;for(t=0;t<e.__k.length;t++)(o=e.__k[t])&&(o.__=e,n="function"==typeof o.type?b(o,n,_):C(_,o,o,e.__k,o.__e,n));return n}function C(e,n,_,t,o,r){var u,l,i;if(void 0!==n.__d)u=n.__d,n.__d=void 0;else if(null==_||o!=r||null==o.parentNode)e:if(null==r||r.parentNode!==e)e.appendChild(o),u=null;else{for(l=r,i=0;(l=l.nextSibling)&&i<t.length;i+=2)if(l==o)break e;e.insertBefore(o,r),u=r}return void 0!==u?u:o.nextSibling}function x(e,n,_){"-"===n[0]?e.setProperty(n,_):e[n]=null==_?"":"number"!=typeof _||c.test(n)?_:_+"px"}function H(e,n,_,t,o){var r;e:if("style"===n)if("string"==typeof _)e.style.cssText=_;else{if("string"==typeof t&&(e.style.cssText=t=""),t)for(n in t)_&&n in _||x(e.style,n,"");if(_)for(n in _)t&&_[n]===t[n]||x(e.style,n,_[n])}else if("o"===n[0]&&"n"===n[1])r=n!==(n=n.replace(/Capture$/,"")),n=n.toLowerCase()in e?n.toLowerCase().slice(2):n.slice(2),e.l||(e.l={}),e.l[n+r]=_,_?t||e.addEventListener(n,r?S:E,r):e.removeEventListener(n,r?S:E,r);else if("dangerouslySetInnerHTML"!==n){if(o)n=n.replace(/xlink[H:h]/,"h").replace(/sName$/,"s");else if("href"!==n&&"list"!==n&&"form"!==n&&"tabIndex"!==n&&"download"!==n&&n in e)try{e[n]=null==_?"":_;break e}catch(e){}"function"==typeof _||(null!=_&&(!1!==_||"a"===n[0]&&"r"===n[1])?e.setAttribute(n,_):e.removeAttribute(n))}}function E(e){this.l[e.type+!1](n.event?n.event(e):e)}function S(e){this.l[e.type+!0](n.event?n.event(e):e)}function P(e,_,t,o,r,u,l,i,c){var f,a,p,v,y,m,g,b,C,x,H,E=_.type;if(void 0!==_.constructor)return null;null!=t.__h&&(c=t.__h,i=_.__e=t.__e,_.__h=null,u=[i]),(f=n.__b)&&f(_);try{e:if("function"==typeof E){if(b=_.props,C=(f=E.contextType)&&o[f.__c],x=f?C?C.props.value:f.__:o,t.__c?g=(a=_.__c=t.__c).__=a.__E:("prototype"in E&&E.prototype.render?_.__c=a=new E(b,x):(_.__c=a=new d(b,x),a.constructor=E,a.render=A),C&&C.sub(a),a.props=b,a.state||(a.state={}),a.context=x,a.__n=o,p=a.__d=!0,a.__h=[]),null==a.__s&&(a.__s=a.state),null!=E.getDerivedStateFromProps&&(a.__s==a.state&&(a.__s=s({},a.__s)),s(a.__s,E.getDerivedStateFromProps(b,a.__s))),v=a.props,y=a.state,p)null==E.getDerivedStateFromProps&&null!=a.componentWillMount&&a.componentWillMount(),null!=a.componentDidMount&&a.__h.push(a.componentDidMount);else{if(null==E.getDerivedStateFromProps&&b!==v&&null!=a.componentWillReceiveProps&&a.componentWillReceiveProps(b,x),!a.__e&&null!=a.shouldComponentUpdate&&!1===a.shouldCompLine truncated
@@ -1,43 +0,0 @@
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; font: 16px sans-serif; }
select, input, label::before, textarea { outline: none; box-shadow:none !important; border: 1px solid #ccc !important; }
code, pre { color: #373; font-family: monospace; font-weight: bolder; font-size: smaller; background: #ddd; padding: 0.1em 0.3em; border-radius: 0.2em; }
textarea, input, .addon { font-size: 15px; border: 1px solid #ccc; padding: 0.5em; }
a, a:visited, a:active { color: #55f; }
.addon { background: #eee; min-width: 9em;}
.btn {
background: #ccc; border-radius: 0.3em; border: 0; color: #fff; cursor: pointer;
display: inline-block; padding: 0.6em 2em; font-weight: bolder;
}
.btn[disabled] { opacity: 0.5; cursor: auto;}
.smooth { transition: all .2s; }
.container { margin: 0 20px; width: auto; }
.d-flex { display: flex; }
.d-none { display: none; }
.border { border: 1px solid #ddd; }
.rounded { border-radius: 0.5em; }
.nowrap { white-space: nowrap; }
.msg { background: #def; border-left: 5px solid #59d; padding: 0.5em; font-size: 90%; margin: 1em 0; }
.section { margin: 0 1em; }
.topic, .data, .qos { padding: 0.2em 0.5em; border-radius: 0.4em; margin-right: 0.5em; }
.qos { background: #efa; }
.topic { background: #fea; }
.data { background: #aef; }
/* Grid */
.row { display: flex; flex-wrap: wrap; }
.col { margin: 0; padding: 0; overflow: auto; }
.col-12 { width: 100%; }
.col-11 { width: 91.66%; }
.col-10 { width: 83.33%; }
.col-9 { width: 75%; }
.col-8 { width: 66.66%; }
.col-7 { width: 58.33%; }
.col-6 { width: 50%; }
.col-5 { width: 41.66%; }
.col-4 { width: 33.33%; }
.col-3 { width: 25%; }
.col-2 { width: 16.66%; }
.col-1 { width: 8.33%; }
@media (min-width: 1310px) { .container { margin: auto; width: 1270px; } }
@media (max-width: 920px) { .row .col { width: 100%; } }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

+58 -696
View File
@@ -2,553 +2,61 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#if defined(__cplusplus)
extern "C" {
#endif
const char *mg_unlist(size_t no);
const char *mg_unpack(const char *, size_t *, time_t *);
#if defined(__cplusplus)
}
#endif
static const unsigned char v1[] = { static const unsigned char v1[] = {
31, 139, 8, 8, 219, 27, 244, 98, 0, 3, 112, 114, // .......b..pr 60, 33, 68, 79, 67, 84, 89, 80, 69, 32, 104, 116, // <!DOCTYPE ht
101, 97, 99, 116, 46, 109, 105, 110, 46, 106, 115, 0, // eact.min.js. 109, 108, 62, 10, 60, 104, 116, 109, 108, 32, 108, 97, // ml>.<html la
157, 91, 123, 119, 219, 182, 146, 255, 127, 63, 69, 164, // .[{w.....?E. 110, 103, 61, 34, 101, 110, 34, 62, 10, 32, 32, 60, // ng="en">. <
211, 163, 18, 43, 68, 177, 147, 182, 187, 75, 5, 213, // ...+D....K.. 104, 101, 97, 100, 62, 10, 32, 32, 32, 32, 60, 109, // head>. <m
105, 29, 183, 238, 109, 234, 230, 38, 105, 123, 123, 85, // i...m..&i{{U 101, 116, 97, 32, 110, 97, 109, 101, 61, 34, 100, 101, // eta name="de
93, 30, 138, 132, 44, 214, 20, 169, 242, 97, 91, 53, // ]...,....a[5 115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 99, // scription" c
245, 221, 247, 55, 3, 128, 164, 108, 185, 247, 236, 158, // ...7...l.... 111, 110, 116, 101, 110, 116, 61, 34, 77, 111, 110, 103, // ontent="Mong
36, 34, 30, 131, 1, 48, 47, 204, 12, 144, 155, 176, // $"...0/..... 111, 111, 115, 101, 32, 69, 109, 98, 101, 100, 100, 101, // oose Embedde
120, 166, 101, 38, 3, 89, 201, 92, 22, 178, 150, 169, // x.e&.Y...... 100, 32, 70, 105, 108, 101, 115, 121, 116, 101, 109, 32, // d Filesytem
186, 223, 203, 68, 205, 23, 50, 82, 47, 194, 40, 169, // ...D..2R/.(. 101, 120, 97, 109, 112, 108, 101, 34, 32, 47, 62, 10, // example" />.
26, 125, 231, 205, 252, 178, 185, 106, 178, 102, 219, 124, // .}.....j.f.| 32, 32, 32, 32, 60, 109, 101, 116, 97, 32, 104, 116, // <meta ht
34, 154, 98, 187, 110, 174, 138, 36, 110, 242, 219, 178, // ".b.n..$n... 116, 112, 45, 101, 113, 117, 105, 118, 61, 34, 67, 111, // tp-equiv="Co
217, 100, 81, 147, 85, 183, 77, 146, 233, 121, 180, 94, // .dQ.U.M..y.^ 110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 34, 32, // ntent-Type"
52, 127, 230, 121, 243, 175, 188, 136, 155, 164, 210, 69, // 4..y.......E 99, 111, 110, 116, 101, 110, 116, 61, 34, 116, 101, 120, // content="tex
248, 34, 153, 174, 234, 44, 170, 146, 60, 123, 86, 122, // ."...,..<{Vz 116, 47, 104, 116, 109, 108, 59, 32, 99, 104, 97, 114, // t/html; char
152, 72, 220, 175, 242, 194, 187, 193, 172, 193, 179, 36, // .H.........$ 115, 101, 116, 61, 85, 84, 70, 45, 56, 34, 62, 10, // set=UTF-8">.
123, 150, 9, 61, 15, 22, 42, 195, 207, 180, 208, 85, // {..=..*....U 32, 32, 32, 32, 60, 109, 101, 116, 97, 32, 104, 116, // <meta ht
93, 100, 207, 244, 190, 29, 177, 242, 180, 184, 39, 216, // ]d........'. 116, 112, 45, 101, 113, 117, 105, 118, 61, 34, 88, 45, // tp-equiv="X-
76, 233, 201, 54, 44, 116, 86, 93, 230, 177, 158, 102, // L..6,tV]...f 85, 65, 45, 67, 111, 109, 112, 97, 116, 105, 98, 108, // UA-Compatibl
163, 81, 54, 41, 244, 38, 191, 209, 103, 235, 36, 141, // .Q6).&..g.$. 101, 34, 32, 99, 111, 110, 116, 101, 110, 116, 61, 34, // e" content="
1, 214, 13, 10, 61, 222, 141, 25, 216, 109, 105, 74, // ....=....miJ 73, 69, 61, 101, 100, 103, 101, 34, 62, 10, 32, 32, // IE=edge">.
243, 214, 52, 103, 32, 134, 215, 122, 55, 84, 170, 158, // ..4g ..z7T.. 32, 32, 60, 109, 101, 116, 97, 32, 110, 97, 109, 101, // <meta name
229, 42, 152, 215, 11, 127, 88, 232, 21, 215, 11, 83, // .*....X....S 61, 34, 118, 105, 101, 119, 112, 111, 114, 116, 34, 32, // ="viewport"
79, 241, 195, 165, 105, 178, 242, 194, 226, 170, 222, 96, // O...i......` 99, 111, 110, 116, 101, 110, 116, 61, 34, 119, 105, 100, // content="wid
230, 114, 146, 234, 236, 170, 90, 127, 249, 114, 52, 242, // .r....Z..r4. 116, 104, 61, 100, 101, 118, 105, 99, 101, 45, 119, 105, // th=device-wi
210, 73, 68, 19, 99, 69, 234, 81, 255, 171, 153, 158, // .ID.cE.Q.... 100, 116, 104, 44, 32, 105, 110, 105, 116, 105, 97, 108, // dth, initial
68, 97, 154, 118, 35, 229, 75, 225, 87, 66, 14, 221, // Da.v#.K.WB.. 45, 115, 99, 97, 108, 101, 61, 49, 34, 62, 10, 32, // -scale=1">.
26, 49, 87, 181, 219, 234, 124, 245, 140, 118, 82, 167, // .1W...|..vR. 32, 32, 32, 32, 32, 32, 32, 60, 116, 105, 116, 108, // <titl
233, 64, 101, 147, 88, 175, 194, 58, 173, 222, 21, 249, // .@e.X..:.... 101, 62, 77, 111, 110, 103, 111, 111, 115, 101, 32, 69, // e>Mongoose E
182, 20, 237, 98, 31, 180, 223, 228, 73, 252, 236, 68, // ...b....I..D 109, 98, 101, 100, 100, 101, 100, 32, 70, 105, 108, 101, // mbedded File
41, 69, 75, 164, 117, 208, 74, 15, 97, 208, 34, 28, // )EK.u.J.a.". 115, 121, 116, 101, 109, 32, 101, 120, 97, 109, 112, 108, // sytem exampl
45, 183, 160, 69, 202, 124, 165, 89, 122, 68, 218, 130, // -..E.|.YzD.. 101, 60, 47, 116, 105, 116, 108, 101, 62, 10, 32, 32, // e</title>.
23, 150, 225, 134, 80, 32, 17, 45, 201, 215, 114, 75, // ....P .-..rK 32, 32, 60, 108, 105, 110, 107, 32, 114, 101, 108, 61, // <link rel=
72, 252, 74, 130, 70, 62, 0, 244, 202, 47, 100, 16, // H.J.F>.../d. 34, 115, 116, 121, 108, 101, 115, 104, 101, 101, 116, 34, // "stylesheet"
92, 251, 132, 1, 5, 247, 93, 250, 39, 248, 213, 174, // ......].'... 32, 104, 114, 101, 102, 61, 34, 115, 116, 121, 108, 101, // href="style
26, 251, 102, 105, 40, 70, 174, 109, 109, 10, 81, 158, // ..fi(F.mm.Q. 46, 99, 115, 115, 34, 32, 47, 62, 10, 32, 32, 60, // .css" />. <
149, 85, 81, 71, 85, 94, 116, 64, 55, 220, 71, 52, // .UQGU^t@7.G4 47, 104, 101, 97, 100, 62, 10, 32, 32, 60, 98, 111, // /head>. <bo
31, 143, 3, 191, 222, 187, 21, 59, 106, 220, 100, 96, // .......;j.d` 100, 121, 62, 60, 47, 98, 111, 100, 121, 62, 10, 32, // dy></body>.
52, 177, 153, 11, 94, 42, 100, 218, 173, 126, 77, 114, // 4...^*d..~Mr 32, 60, 115, 99, 114, 105, 112, 116, 32, 116, 121, 112, // <script typ
225, 196, 165, 229, 68, 215, 31, 27, 73, 171, 214, 73, // ....D...I..I 101, 61, 34, 109, 111, 100, 117, 108, 101, 34, 32, 115, // e="module" s
57, 225, 237, 40, 236, 150, 42, 88, 74, 165, 239, 42, // 9..(..*XJ..* 114, 99, 61, 34, 109, 97, 105, 110, 46, 106, 115, 34, // rc="main.js"
213, 131, 189, 49, 176, 96, 180, 89, 80, 38, 90, 196, // ...1.`.YP&Z. 62, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 60, // ></script>.<
65, 48, 67, 47, 62, 146, 126, 240, 247, 122, 146, 100, // A0C/>.~..z.d 47, 104, 116, 109, 108, 62, 10, 0 // /html>.
177, 190, 251, 145, 228, 114, 124, 42, 120, 11, 211, 86, // .....r|*x..V
158, 167, 217, 107, 205, 64, 70, 14, 166, 217, 120, 44, // ...k.@F...x,
44, 218, 129, 242, 2, 197, 157, 243, 108, 33, 28, 207, // ,.......l!..
9, 165, 118, 211, 113, 197, 82, 225, 136, 160, 232, 9, // ..v.q.R.....
21, 104, 61, 102, 218, 110, 3, 187, 86, 73, 100, 48, // .h=f.n..VId0
237, 230, 211, 60, 95, 59, 23, 85, 34, 163, 124, 84, // ...<_;.U".|T
52, 189, 209, 100, 25, 150, 90, 49, 151, 50, 117, 242, // 4..d..Z1.2u.
255, 220, 192, 253, 35, 132, 102, 47, 203, 66, 135, 215, // ....#.f/.B..
123, 187, 61, 90, 229, 190, 91, 244, 134, 22, 237, 13, // {.=Z..[.....
104, 76, 12, 49, 230, 175, 26, 156, 0, 115, 53, 217, // hL.1.....s5.
214, 37, 49, 120, 52, 26, 92, 161, 185, 24, 143, 155, // .%1x4.......
166, 24, 40, 150, 242, 101, 14, 4, 250, 189, 6, 23, // ..(..e......
138, 36, 187, 2, 140, 231, 21, 71, 123, 154, 38, 23, // .$.....G{.&.
222, 85, 79, 226, 175, 188, 206, 244, 232, 41, 99, 86, // .UO......)cV
149, 219, 169, 208, 40, 151, 121, 81, 121, 14, 222, 72, // ....(.yQy..H
69, 79, 20, 110, 240, 111, 249, 60, 115, 165, 189, 144, // EO.n.o.<s...
21, 217, 72, 141, 97, 27, 221, 27, 214, 242, 194, 233, // ..H.a.......
215, 212, 109, 178, 80, 94, 174, 60, 152, 50, 65, 72, // ..m.P^.<.2AH
232, 71, 75, 175, 86, 132, 242, 29, 237, 36, 32, 124, // .GK.V....$ |
94, 165, 74, 15, 6, 56, 23, 12, 165, 114, 250, 29, // ^.J..8...r..
159, 202, 119, 94, 13, 116, 149, 36, 232, 76, 26, 53, // ..w^.t.$.L.5
2, 81, 234, 73, 126, 155, 233, 226, 195, 207, 223, 158, // .Q.I~.......
167, 154, 108, 141, 52, 156, 161, 97, 235, 217, 188, 88, // ..l.4..a...X
88, 93, 148, 70, 184, 11, 8, 80, 46, 160, 208, 220, // X].F...P....
47, 228, 27, 47, 192, 76, 92, 211, 3, 85, 140, 70, // /../.L...U.F
59, 116, 11, 177, 239, 209, 237, 218, 59, 60, 30, 34, // ;t......;<."
89, 202, 149, 217, 100, 40, 99, 185, 147, 27, 121, 37, // Y...d(c...y%
175, 229, 157, 188, 80, 21, 113, 15, 34, 210, 52, 137, // ....P.q.".4.
60, 87, 23, 142, 184, 68, 117, 214, 30, 218, 95, 8, // <W...Du..._.
49, 11, 95, 103, 174, 47, 60, 16, 177, 13, 75, 206, // 1._g./<...K.
245, 60, 92, 40, 179, 94, 180, 100, 168, 129, 155, 195, // .<.(.^.d....
101, 158, 167, 58, 236, 105, 196, 102, 70, 48, 254, 16, // e..:.i.fF0..
182, 5, 12, 239, 181, 3, 56, 171, 55, 75, 93, 28, // ......8.7K].
182, 45, 147, 171, 36, 171, 250, 227, 183, 60, 47, 54, // .-..$....</6
96, 20, 128, 203, 194, 255, 170, 40, 194, 221, 36, 41, // `......(..$)
249, 235, 109, 4, 192, 214, 242, 222, 25, 25, 127, 179, // ..m.........
239, 129, 179, 85, 245, 55, 36, 16, 95, 158, 0, 112, // ...U.7$._..p
195, 90, 42, 55, 198, 234, 224, 11, 35, 106, 17, 51, // .Z*7....#j.3
207, 253, 141, 96, 83, 67, 53, 21, 112, 227, 146, 247, // ...`SC5.p...
188, 4, 139, 205, 150, 149, 183, 83, 23, 102, 207, 187, // .......S.f..
209, 136, 49, 40, 181, 163, 15, 213, 8, 189, 162, 58, // ..1(.......:
21, 4, 193, 41, 35, 11, 83, 157, 150, 250, 25, 145, // ...)#.S.....
58, 6, 133, 227, 215, 231, 211, 24, 164, 165, 185, 24, // :...........
95, 76, 74, 251, 215, 200, 238, 9, 202, 97, 51, 186, // _LJ......a3.
187, 99, 54, 236, 223, 65, 4, 54, 114, 167, 118, 77, // .c6..A.6r.vM
147, 30, 72, 129, 188, 82, 27, 35, 198, 49, 10, 56, // ..H..R.#.1.8
42, 48, 201, 142, 190, 3, 69, 2, 127, 215, 52, 222, // *0....E...4.
29, 152, 46, 36, 55, 142, 70, 119, 70, 183, 185, 230, // ...$7.FwF...
232, 45, 109, 99, 204, 212, 136, 154, 230, 10, 141, 194, // .-mc........
138, 241, 213, 204, 90, 229, 107, 160, 187, 86, 87, 71, // ....Z.k..VWG
207, 80, 179, 17, 103, 147, 8, 203, 53, 237, 142, 4, // .P..g...5...
142, 54, 135, 239, 108, 195, 246, 165, 84, 75, 111, 131, // .6..l...TKo.
133, 195, 132, 150, 234, 204, 108, 74, 94, 64, 124, 75, // ......lJ^@|K
33, 87, 144, 144, 124, 203, 136, 161, 86, 129, 177, 182, // !W..|...V...
71, 38, 11, 236, 100, 44, 209, 64, 41, 124, 61, 185, // G&..d,.@)|=.
9, 211, 90, 171, 225, 16, 104, 105, 255, 100, 12, 21, // ..Z...hi.d..
74, 101, 207, 101, 129, 1, 198, 152, 82, 221, 120, 59, // Je.e....R.x;
168, 151, 211, 8, 173, 174, 161, 16, 231, 211, 240, 249, // ............
243, 169, 48, 203, 39, 150, 2, 242, 47, 102, 238, 224, // ..0.'.../f..
8, 195, 104, 228, 74, 138, 5, 41, 110, 151, 118, 227, // ..h.J..)n.v.
85, 50, 196, 41, 37, 228, 79, 30, 193, 72, 150, 42, // U2.)%.O..H.*
65, 71, 196, 29, 251, 23, 70, 21, 239, 250, 170, 248, // AG....F.....
209, 187, 35, 200, 187, 249, 120, 220, 126, 122, 182, 96, // ..#...x.~z.`
105, 108, 129, 209, 126, 24, 4, 86, 237, 10, 104, 170, // il..~..V..h.
195, 131, 163, 2, 42, 216, 58, 115, 94, 84, 36, 122, // ....*.:s^T$z
30, 153, 24, 156, 194, 153, 58, 178, 179, 220, 80, 123, // ......:...P{
233, 229, 140, 221, 63, 35, 171, 132, 63, 60, 222, 24, // ....?#..?<..
39, 24, 227, 214, 169, 233, 29, 222, 103, 125, 227, 100, // '.......g}.d
150, 5, 111, 79, 38, 180, 201, 214, 70, 146, 201, 140, // ..oO&...F...
133, 49, 180, 49, 27, 208, 248, 64, 105, 218, 163, 63, // .1.1...@i..?
192, 161, 1, 43, 216, 52, 166, 154, 247, 248, 39, 180, // ...+.4....'.
223, 130, 1, 160, 56, 96, 45, 44, 186, 158, 132, 219, // ....8`-,....
45, 14, 31, 227, 142, 194, 164, 214, 172, 58, 60, 1, // -........:<.
31, 59, 169, 42, 224, 84, 159, 76, 81, 72, 39, 25, // .;.*.T.LQH'.
124, 144, 15, 201, 50, 53, 71, 88, 242, 186, 61, 135, // |...25GX..=.
146, 177, 122, 73, 246, 144, 102, 23, 172, 131, 56, 171, // ..zI..f...8.
52, 188, 142, 82, 23, 213, 215, 26, 136, 180, 71, 59, // 4..R......G;
5, 250, 194, 157, 172, 221, 89, 48, 171, 253, 188, 143, // ......Y0....
187, 163, 211, 157, 99, 220, 240, 57, 200, 14, 155, 122, // ....c..9...z
178, 128, 147, 90, 106, 246, 24, 129, 122, 231, 49, 225, // ...Zj...z.1.
53, 14, 119, 107, 121, 131, 217, 112, 232, 59, 59, 58, // 5.wky..p.;;:
104, 101, 176, 105, 162, 73, 165, 203, 202, 203, 196, 44, // he.i.I.....,
240, 131, 241, 112, 123, 55, 236, 166, 185, 232, 216, 97, // ...p{7.....a
152, 81, 76, 153, 110, 176, 209, 187, 84, 243, 204, 194, // .QL.n...T...
84, 15, 77, 118, 0, 250, 49, 200, 36, 42, 203, 143, // T.Mv..1.$*..
228, 160, 5, 134, 114, 199, 128, 43, 246, 23, 14, 193, // ....r..+....
43, 210, 62, 248, 252, 68, 233, 140, 220, 230, 74, 4, // +.>..D....J.
208, 20, 246, 246, 155, 230, 206, 193, 99, 113, 128, 35, // ........cq.#
225, 8, 58, 208, 64, 0, 99, 64, 59, 199, 20, 248, // ..:.@.c@;...
28, 194, 83, 135, 216, 59, 57, 25, 230, 142, 124, 163, // ..S..;9...|.
209, 48, 51, 229, 211, 133, 128, 11, 2, 250, 227, 104, // .03........h
167, 168, 100, 155, 134, 145, 246, 94, 156, 133, 91, 176, // ..d....^..[.
71, 127, 242, 130, 166, 132, 61, 67, 95, 149, 191, 205, // G.....=C_...
111, 117, 113, 6, 23, 201, 19, 152, 89, 207, 30, 180, // ouq.....Y...
77, 202, 52, 193, 88, 68, 11, 89, 91, 132, 22, 164, // M.4.XD.Y[...
176, 163, 248, 69, 16, 195, 181, 121, 54, 46, 16, 158, // ...E...y6...
200, 96, 86, 53, 13, 4, 47, 142, 207, 111, 32, 139, // .`V5../..o .
111, 147, 178, 210, 240, 5, 192, 202, 98, 246, 193, 63, // o.......b..?
135, 152, 192, 54, 153, 40, 233, 41, 128, 86, 1, 134, // ...6.(.).V..
113, 152, 93, 233, 34, 175, 203, 116, 247, 65, 87, 223, // q.]."..t.AW.
101, 128, 187, 248, 248, 195, 91, 50, 134, 198, 59, 206, // e.....[2..;.
197, 193, 246, 238, 32, 96, 215, 243, 11, 127, 189, 192, // .... `......
6, 215, 67, 209, 245, 148, 151, 225, 134, 183, 93, 14, // ..C.......].
123, 248, 215, 20, 98, 17, 50, 16, 46, 197, 66, 92, // {...b.2...B.
25, 108, 216, 184, 114, 21, 46, 191, 35, 255, 218, 213, // .l..r...#...
99, 248, 54, 105, 30, 198, 182, 206, 220, 210, 162, 42, // c.6i.......*
118, 247, 15, 5, 53, 152, 90, 101, 217, 71, 97, 21, // v...5.Ze.Ga.
113, 120, 176, 63, 102, 66, 65, 72, 235, 185, 66, 136, // qx.?fBAH..B.
6, 167, 3, 214, 249, 97, 216, 99, 106, 209, 50, 213, // .....a.cj.2.
40, 200, 87, 21, 196, 111, 89, 87, 218, 106, 136, 37, // (.W..oYW.j.%
104, 175, 89, 244, 253, 218, 115, 154, 154, 67, 141, 116, // h.Y...s..C.t
110, 92, 246, 241, 224, 116, 225, 101, 19, 77, 28, 152, // n....t.e.M..
217, 47, 57, 241, 253, 144, 245, 195, 145, 81, 39, 255, // ./9......Q'.
118, 20, 29, 203, 189, 176, 93, 38, 50, 50, 90, 183, // v.....]&22Z.
146, 161, 220, 202, 27, 235, 155, 45, 229, 25, 121, 103, // .......-..yg
240, 200, 204, 9, 114, 96, 27, 131, 73, 47, 58, 19, // ....r`..I/:.
189, 24, 108, 106, 232, 68, 254, 220, 26, 180, 138, 76, // ..lj.D.....L
9, 38, 204, 156, 91, 21, 27, 101, 42, 175, 77, 248, // .&..[..e*.M.
80, 171, 121, 130, 131, 222, 91, 177, 153, 93, 194, 172, // P.y...[..]..
145, 146, 77, 153, 89, 108, 2, 30, 115, 227, 156, 229, // ..M.Yl..s...
138, 252, 31, 227, 45, 157, 41, 140, 62, 119, 49, 218, // ....-.).>w1.
71, 242, 73, 70, 163, 124, 190, 34, 183, 0, 135, 145, // G.IF.|."....
90, 205, 206, 102, 103, 6, 214, 28, 185, 62, 117, 33, // Z..fg....>u!
70, 165, 197, 68, 179, 43, 133, 19, 141, 86, 100, 214, // F..D.+...Vd.
26, 145, 255, 172, 66, 252, 156, 251, 222, 16, 163, 170, // ....B.......
156, 230, 29, 66, 134, 206, 71, 163, 243, 73, 219, 2, // ...B..G..I..
142, 82, 208, 48, 51, 67, 67, 149, 233, 91, 176, 113, // .R.03CC..[.q
41, 239, 132, 239, 245, 219, 98, 110, 147, 97, 159, 98, // ).....bn.a.b
234, 28, 117, 51, 94, 125, 37, 228, 217, 104, 116, 54, // ..u3^}%..ht6
41, 235, 165, 23, 18, 156, 137, 60, 151, 40, 149, 85, // )......<.(.U
88, 105, 200, 158, 45, 177, 34, 135, 109, 48, 122, 39, // Xi..-.".m0z'
105, 149, 153, 202, 229, 150, 215, 75, 129, 16, 55, 173, // i......K..7.
217, 119, 50, 98, 78, 117, 248, 20, 30, 127, 149, 69, // .w2bNu.....E
228, 220, 164, 243, 201, 149, 174, 222, 32, 240, 185, 209, // ........ ...
241, 7, 234, 248, 166, 200, 55, 156, 12, 104, 71, 184, // ......7..hG.
33, 109, 3, 71, 25, 92, 132, 117, 42, 77, 163, 124, // !m.G...u*M.|
26, 145, 183, 116, 192, 66, 222, 40, 187, 57, 248, 132, // ...t.B.(.9..
22, 173, 220, 10, 179, 206, 191, 90, 138, 89, 44, 109, // .......Z.Y,m
124, 179, 205, 51, 72, 244, 47, 73, 154, 254, 128, 192, // |..3H./I....
13, 6, 248, 88, 171, 231, 246, 215, 235, 124, 147, 196, // ...X.....|..
237, 8, 80, 200, 248, 142, 71, 250, 69, 123, 128, 252, // ..P...G.E{..
251, 117, 45, 161, 8, 55, 79, 44, 239, 189, 142, 52, // .u-..7O,...4
198, 88, 200, 191, 232, 52, 210, 49, 8, 141, 83, 230, // .X...4.1..S.
112, 149, 235, 188, 78, 227, 51, 55, 232, 167, 109, 204, // p...N.37..m.
60, 24, 156, 42, 245, 84, 175, 163, 52, 208, 53, 77, // <..*.T..4.5M
192, 65, 160, 98, 137, 190, 17, 247, 143, 132, 202, 200, // .A.b........
5, 43, 226, 205, 192, 130, 89, 22, 67, 140, 78, 5, // .+....Y.C.N.
163, 186, 161, 195, 226, 129, 218, 94, 115, 249, 218, 148, // .......^s...
39, 176, 194, 231, 33, 236, 102, 63, 140, 213, 54, 46, // '...!.f?..6.
87, 129, 216, 27, 60, 107, 235, 166, 140, 70, 169, 165, // W...<k...F..
187, 104, 237, 238, 81, 226, 185, 237, 30, 109, 238, 237, // .h..Q....m..
243, 40, 155, 187, 193, 45, 159, 219, 229, 17, 37, 30, // .(...-....%.
195, 122, 108, 242, 40, 122, 61, 84, 174, 227, 52, 179, // .zl.(z=T..4.
166, 170, 176, 166, 74, 58, 154, 181, 36, 163, 239, 59, // ....J:..$..;
248, 172, 43, 229, 116, 220, 115, 114, 239, 164, 190, 157, // ..+.t.sr....
71, 60, 64, 238, 54, 4, 161, 99, 151, 240, 204, 128, // G<@.6..c....
145, 35, 12, 205, 179, 17, 190, 124, 212, 239, 145, 122, // .#.....|...z
109, 157, 3, 202, 221, 31, 178, 112, 11, 57, 177, 222, // m......p.9..
159, 217, 40, 76, 201, 230, 233, 94, 34, 3, 208, 92, // ..(L...^"...
40, 179, 8, 196, 93, 43, 23, 234, 173, 141, 100, 42, // (...]+....d*
181, 162, 24, 112, 182, 178, 214, 180, 13, 110, 87, 146, // ...p.....nW.
194, 253, 195, 8, 248, 66, 204, 46, 252, 249, 197, 226, // .....B......
209, 89, 131, 229, 119, 217, 158, 254, 89, 240, 132, 172, // .Y..w...Y...
200, 43, 43, 153, 231, 76, 36, 134, 53, 100, 215, 32, // .++..L$.5d.
187, 241, 177, 108, 82, 16, 14, 89, 95, 240, 103, 222, // ...lR..Y_.g.
67, 145, 181, 162, 44, 124, 83, 185, 245, 172, 100, 247, // C...,|S...d.
150, 24, 137, 41, 179, 56, 78, 86, 43, 29, 91, 46, // ...).8NV+.[.
119, 254, 129, 153, 128, 215, 235, 69, 134, 226, 3, 85, // w......E...U
11, 27, 62, 105, 149, 216, 29, 13, 6, 145, 172, 231, // ..>i........
117, 155, 249, 75, 196, 194, 174, 156, 164, 71, 155, 51, // u..K.....G.3
184, 239, 2, 188, 161, 38, 113, 79, 189, 17, 37, 48, // .....&qO..%0
241, 65, 60, 163, 197, 163, 84, 17, 128, 248, 120, 100, // .A<...T...xd
2, 174, 237, 116, 199, 83, 74, 54, 161, 76, 154, 216, // ...t.SJ6.L..
237, 160, 157, 159, 109, 195, 190, 159, 180, 185, 245, 250, // ....m.......
41, 155, 214, 49, 40, 217, 49, 88, 131, 118, 70, 140, // )..1(.1X.vF.
227, 246, 236, 221, 89, 231, 64, 110, 16, 163, 176, 215, // ....Y.@n....
125, 67, 46, 183, 218, 113, 222, 106, 112, 226, 84, 180, // }C...q.jp.T.
102, 191, 121, 186, 121, 93, 187, 144, 101, 99, 50, 56, // f.y.y]..ec28
136, 109, 235, 249, 134, 67, 189, 146, 60, 40, 136, 232, // .m...C..<(..
110, 6, 127, 38, 199, 194, 201, 33, 4, 42, 255, 21, // n..&...!.*..
194, 226, 9, 165, 115, 249, 108, 23, 216, 129, 42, 37, // ....s.l...*%
141, 50, 177, 146, 201, 57, 244, 18, 177, 157, 245, 86, // .2...9.....V
59, 231, 155, 196, 121, 196, 57, 245, 73, 4, 232, 74, // ;...y.9.I..J
147, 247, 79, 65, 152, 23, 139, 105, 166, 138, 217, 131, // ..OA...i....
110, 155, 20, 187, 252, 0, 7, 180, 170, 182, 254, 139, // n...........
23, 183, 183, 183, 147, 219, 87, 147, 188, 184, 122, 241, // ......W...z.
242, 228, 228, 228, 5, 237, 19, 234, 226, 31, 31, 232, // ............
237, 100, 12, 69, 24, 141, 98, 23, 209, 201, 136, 100, // .d.E..b....d
181, 191, 176, 53, 126, 99, 196, 69, 196, 107, 168, 95, // ...5~c.E.k._
104, 170, 158, 173, 196, 221, 65, 84, 147, 92, 91, 78, // h.....AT..[N
102, 70, 231, 104, 233, 37, 116, 64, 121, 45, 83, 154, // fF.h.%t@y-S.
38, 21, 147, 39, 188, 113, 56, 8, 241, 147, 125, 131, // &..'.q8...}.
168, 37, 152, 227, 211, 154, 174, 114, 136, 163, 155, 215, // .%.....r....
217, 36, 116, 46, 107, 217, 103, 221, 122, 222, 239, 1, // .$t.k.g.z...
47, 38, 25, 184, 69, 119, 10, 135, 173, 236, 112, 77, // /&..Ew....pM
61, 216, 166, 144, 120, 188, 37, 85, 30, 141, 182, 36, // =...x.%U...$
179, 213, 198, 250, 39, 84, 106, 154, 174, 13, 72, 18, // ....'Tj...H.
183, 60, 193, 36, 105, 171, 106, 219, 13, 134, 7, 142, // .<.$i.j.....
8, 137, 104, 218, 79, 190, 30, 134, 145, 180, 155, 194, // ..h.O.......
94, 222, 56, 107, 69, 242, 137, 88, 220, 94, 230, 112, // ^.8kE..X.^.p
88, 206, 55, 38, 77, 67, 113, 168, 185, 242, 64, 252, // X.7&MCq...@.
86, 44, 160, 159, 29, 130, 76, 228, 20, 118, 56, 111, // V,....L..v8o
180, 13, 109, 51, 0, 2, 217, 211, 200, 135, 76, 2, // ..m3......L.
87, 137, 214, 58, 186, 214, 177, 173, 210, 44, 28, 55, // W..:.....,.7
16, 14, 59, 59, 77, 108, 103, 223, 67, 29, 99, 185, // ..;;Mlg.C.c.
70, 43, 172, 230, 86, 184, 100, 104, 27, 29, 109, 156, // F+..V.dh..m.
30, 182, 150, 24, 118, 56, 147, 143, 50, 145, 27, 31, // ....v8..2...
172, 112, 138, 109, 130, 39, 157, 92, 101, 63, 46, 127, // .p.m.'..e?..
215, 17, 71, 84, 59, 214, 246, 122, 86, 35, 152, 241, // ..GT;..zV#..
43, 147, 9, 163, 60, 16, 244, 55, 58, 84, 225, 141, // +...<..7:T..
234, 52, 184, 203, 62, 145, 50, 146, 161, 100, 85, 158, // .4..>.2..dU.
194, 44, 122, 118, 215, 32, 92, 12, 84, 46, 108, 192, // .,zv. ..T.l.
240, 216, 136, 4, 9, 195, 134, 147, 44, 92, 5, 101, // ........,..e
176, 145, 171, 66, 151, 165, 53, 31, 131, 13, 64, 46, // ...B..5...@.
176, 27, 139, 9, 49, 201, 218, 192, 74, 242, 79, 90, // ....1...J.OZ
66, 30, 155, 193, 246, 81, 234, 146, 167, 176, 117, 139, // B....Q....u.
207, 141, 100, 140, 182, 66, 56, 197, 254, 113, 162, 232, // ..d..B8..q..
163, 181, 211, 108, 113, 143, 93, 175, 204, 180, 199, 225, // ...lq.].....
93, 84, 23, 148, 216, 81, 193, 99, 59, 123, 96, 229, // ]T...Q.c;{`.
127, 114, 8, 237, 45, 35, 223, 188, 76, 234, 108, 99, // .r..-#..L.lc
156, 211, 182, 8, 4, 146, 83, 97, 38, 59, 234, 229, // ......Sa&;..
110, 6, 132, 54, 174, 72, 25, 36, 154, 163, 105, 62, // n..6.H.$..i>
82, 10, 140, 197, 182, 77, 129, 218, 60, 90, 100, 18, // R....M..<Zd.
199, 249, 3, 79, 202, 76, 194, 49, 241, 241, 46, 239, // ...O.L.1....
216, 129, 33, 246, 185, 57, 186, 115, 118, 113, 56, 201, // ..!..9.svq8.
75, 200, 77, 198, 142, 229, 163, 128, 213, 40, 94, 231, // K.M......(^.
78, 70, 10, 152, 138, 28, 194, 60, 26, 253, 228, 229, // NF.....<....
44, 217, 242, 136, 14, 153, 216, 21, 81, 159, 243, 96, // ,.......Q..`
236, 182, 86, 230, 206, 73, 200, 238, 166, 200, 165, 224, // ..V..I......
58, 138, 126, 229, 114, 84, 150, 123, 238, 194, 206, 197, // :.~.rT.{....
90, 102, 225, 45, 248, 15, 94, 223, 64, 144, 220, 79, // Zf.-..^.@..O
105, 127, 230, 196, 165, 62, 178, 216, 216, 198, 177, 124, // i....>.....|
163, 48, 87, 7, 57, 241, 128, 239, 43, 140, 99, 193, // .0W.9...+.c.
151, 222, 239, 160, 46, 129, 242, 6, 208, 175, 28, 29, // ............
130, 117, 53, 244, 214, 134, 47, 243, 0, 17, 89, 77, // .u5.../...YM
121, 240, 180, 187, 132, 169, 30, 93, 194, 240, 232, 217, // y......]....
60, 95, 248, 181, 153, 170, 154, 172, 146, 162, 52, 190, // <_........4.
158, 187, 30, 174, 250, 103, 128, 185, 167, 73, 236, 200, // .....g...I..
28, 227, 106, 162, 213, 193, 64, 74, 252, 189, 241, 146, // ..j...@J....
3, 50, 124, 99, 46, 170, 248, 2, 82, 221, 243, 229, // .2|c....R...
171, 26, 226, 115, 54, 28, 215, 227, 49, 221, 215, 106, // ...s6...1..j
9, 231, 178, 196, 225, 86, 248, 79, 92, 110, 57, 163, // .....V.O.n9.
227, 101, 98, 47, 17, 204, 220, 36, 241, 1, 176, 69, // .eb/...$...E
47, 171, 105, 159, 53, 15, 92, 87, 216, 10, 119, 129, // /.i.5..W..w.
133, 240, 150, 242, 51, 4, 37, 143, 129, 170, 158, 31, // ....3.%.....
239, 48, 238, 13, 228, 209, 136, 72, 245, 151, 210, 93, // .0.....H...]
234, 26, 43, 194, 202, 195, 37, 114, 26, 217, 115, 66, // ..+...%r..sB
8, 96, 177, 213, 203, 131, 177, 129, 187, 91, 156, 186, // .`.......[..
71, 5, 199, 84, 102, 122, 188, 185, 191, 106, 76, 180, // G..Tfz...jL.
229, 252, 92, 208, 187, 17, 150, 48, 103, 252, 50, 129, // .......0g.2.
185, 75, 215, 157, 36, 240, 237, 237, 116, 123, 197, 29, // .K..$...t{..
76, 28, 141, 57, 186, 154, 56, 246, 244, 51, 31, 176, // L..9..8..3..
63, 112, 65, 77, 18, 80, 102, 196, 88, 253, 128, 123, // ?pAM.Pf.X..{
237, 181, 51, 105, 193, 52, 227, 88, 102, 202, 158, 88, // ..3i.4.Xf..X
192, 229, 136, 46, 80, 233, 164, 97, 227, 64, 237, 213, // ....P..a.@..
131, 108, 143, 139, 82, 171, 99, 161, 241, 121, 81, 228, // .l..R.c..yQ.
5, 59, 195, 37, 162, 12, 106, 245, 158, 134, 195, 102, // .;.%..j....f
133, 204, 205, 253, 131, 179, 91, 193, 65, 128, 118, 70, // ......[.A.vF
54, 136, 209, 61, 106, 37, 202, 181, 99, 243, 254, 117, // 6..=j%..c..u
248, 121, 107, 133, 177, 95, 173, 178, 125, 181, 46, 242, // .yk.._..}...
91, 4, 155, 123, 104, 232, 9, 60, 179, 46, 135, 227, // [..{h..<....
22, 169, 14, 137, 100, 238, 229, 3, 27, 9, 177, 76, // ....d......L
112, 26, 197, 149, 72, 121, 89, 80, 104, 236, 204, 181, // p...HyYPh...
250, 174, 96, 146, 36, 29, 196, 209, 235, 39, 142, 147, // ..`.$....'..
149, 54, 65, 29, 226, 200, 78, 64, 65, 20, 116, 150, // .6A...N@A.t.
198, 251, 183, 215, 240, 237, 220, 20, 166, 103, 109, 205, // .........gm.
6, 184, 153, 144, 27, 143, 154, 112, 136, 29, 236, 15, // .......p....
220, 142, 244, 83, 218, 96, 144, 217, 162, 166, 236, 145, // ...S.`......
126, 136, 88, 63, 133, 216, 38, 175, 214, 230, 54, 59, // ~.X?..&...6;
63, 102, 46, 33, 173, 155, 164, 212, 51, 251, 237, 13, // ?f.!....3...
174, 214, 58, 155, 44, 161, 1, 158, 235, 195, 193, 159, // ..:.,.......
167, 55, 26, 113, 172, 15, 150, 124, 76, 54, 58, 175, // .7.q...|L6:.
43, 105, 110, 219, 79, 96, 144, 79, 88, 243, 222, 202, // +in.O`.OX...
75, 249, 139, 124, 143, 150, 239, 104, 214, 31, 77, 206, // K..|...h..M.
80, 254, 108, 2, 114, 249, 71, 27, 181, 201, 175, 141, // P.l.r.G.....
44, 203, 79, 84, 123, 162, 118, 15, 138, 126, 239, 34, // ,.OT{.v..~."
173, 181, 177, 251, 107, 239, 82, 106, 249, 30, 14, 153, // ....k.Rj....
32, 244, 60, 89, 165, 46, 209, 115, 1, 251, 196, 95, // .<Y...s..._
210, 37, 31, 147, 210, 243, 148, 249, 98, 223, 222, 42, // .%......b..*
233, 47, 57, 164, 108, 195, 86, 174, 48, 237, 40, 91, // ./9.l.V.0.([
71, 181, 185, 94, 116, 118, 247, 219, 222, 19, 148, 247, // G..^tv......
234, 84, 254, 233, 37, 90, 246, 179, 179, 127, 30, 220, // .T..%Z......
149, 169, 223, 189, 183, 48, 199, 47, 219, 233, 170, 73, // .....0./...I
69, 143, 82, 38, 124, 229, 201, 209, 171, 154, 7, 179, // E.R&|.......
0, 18, 224, 39, 218, 230, 103, 33, 193, 242, 209, 195, // ...'..g!....
2, 172, 178, 98, 120, 184, 120, 146, 206, 90, 83, 52, // ...bx.x..ZS4
41, 114, 139, 40, 51, 235, 61, 93, 44, 204, 12, 157, // )r.(3.=],...
14, 99, 47, 98, 111, 91, 213, 165, 217, 87, 183, 230, // .c/bo[...W..
191, 25, 130, 246, 87, 252, 74, 76, 7, 153, 81, 154, // ....W.JL..Q.
84, 51, 250, 11, 73, 175, 74, 204, 76, 102, 7, 23, // T3..I.J.Lf..
42, 144, 76, 219, 78, 222, 42, 209, 35, 197, 247, 143, // *.L.N.*.#...
209, 126, 246, 127, 64, 123, 12, 229, 223, 15, 200, 255, // .~..@{......
185, 252, 213, 123, 116, 166, 220, 91, 239, 202, 39, 123, // ...{t..[..'{
49, 239, 95, 99, 254, 195, 177, 230, 189, 250, 66, 126, // 1._c......B~
223, 31, 249, 132, 103, 152, 145, 60, 155, 124, 152, 243, // ....g..<.|..
15, 169, 105, 47, 221, 21, 68, 224, 179, 105, 133, 177, // ..i/..D..i..
34, 83, 216, 205, 244, 107, 255, 116, 54, 59, 255, 175, // "S...k.t6;..
86, 4, 82, 205, 169, 134, 11, 64, 216, 172, 3, 172, // V.R....@....
136, 224, 112, 254, 66, 101, 54, 31, 160, 77, 67, 135, // ..p.Be6..MC.
242, 159, 7, 135, 247, 123, 245, 223, 199, 246, 14, 27, // .....{......
9, 160, 110, 144, 214, 157, 252, 92, 186, 115, 102, 206, // ..n......sf.
126, 37, 52, 193, 174, 236, 127, 68, 119, 66, 69, 116, // ~%4....DwBEt
87, 235, 46, 223, 3, 246, 171, 204, 10, 97, 92, 50, // W........a.2
78, 113, 95, 146, 135, 218, 63, 136, 201, 123, 238, 47, // Nq_...?..{./
52, 211, 78, 61, 235, 82, 191, 209, 203, 250, 234, 103, // 4.N=.R.....g
115, 74, 63, 104, 240, 72, 236, 31, 92, 109, 4, 189, // sJ?h.H...m..
245, 154, 197, 157, 34, 132, 9, 212, 183, 94, 119, 11, // ...."....^w.
108, 4, 229, 242, 241, 129, 194, 154, 254, 168, 245, 192, // l...........
108, 246, 60, 69, 34, 48, 52, 133, 206, 107, 1, 247, // l.<E"04..k..
142, 180, 170, 127, 208, 83, 151, 209, 70, 232, 77, 183, // .....S..F.M.
64, 168, 146, 184, 255, 238, 104, 222, 52, 49, 222, 238, // @.....h.41..
59, 115, 81, 213, 170, 133, 3, 45, 172, 27, 124, 216, // ;sQ....-..|.
90, 247, 91, 97, 19, 237, 185, 23, 136, 251, 126, 171, // Z.[a......~.
77, 57, 153, 247, 104, 156, 242, 97, 3, 186, 103, 243, // M9..h..a..g.
121, 176, 193, 75, 147, 172, 248, 113, 52, 250, 145, 118, // y..K...q4..v
198, 3, 139, 3, 136, 159, 71, 163, 159, 105, 243, 111, // ......G..i.o
173, 153, 204, 148, 119, 105, 35, 13, 154, 145, 222, 88, // ....wi#....X
122, 217, 163, 149, 103, 143, 86, 157, 217, 181, 241, 36, // z...g.V....$
198, 106, 31, 204, 243, 199, 104, 244, 135, 243, 184, 204, // .j....h.....
19, 178, 104, 26, 152, 252, 222, 133, 253, 28, 164, 11, // ..h.........
61, 186, 149, 251, 206, 168, 60, 89, 132, 95, 56, 143, // =.....<Y._8.
80, 232, 63, 106, 93, 86, 95, 101, 201, 38, 36, 204, // P.?j]V_e.&$.
223, 20, 225, 134, 210, 160, 222, 47, 79, 117, 10, 132, // ......./Ou..
29, 143, 31, 101, 245, 157, 184, 40, 213, 97, 97, 143, // ...e...(.aa.
40, 88, 23, 153, 67, 54, 163, 48, 139, 116, 122, 136, // (X..C6.0.tz.
137, 78, 229, 238, 48, 99, 114, 86, 170, 215, 16, 64, // .N..0crV...@
56, 79, 196, 148, 134, 123, 153, 58, 186, 24, 236, 4, // 8O...{.:....
226, 229, 85, 228, 39, 93, 186, 200, 135, 73, 23, 245, // ..U.']...I..
61, 150, 128, 61, 203, 135, 201, 63, 39, 71, 143, 101, // =..=...?'G.e
104, 173, 108, 123, 146, 86, 186, 56, 24, 100, 180, 132, // h.l{.V.8.d..
31, 216, 53, 77, 173, 89, 188, 173, 80, 85, 71, 103, // ..5M.Y..PUGg
209, 230, 20, 245, 116, 203, 79, 82, 184, 86, 230, 170, // ....t.OR.V..
158, 204, 125, 61, 26, 125, 109, 162, 49, 217, 158, 202, // ..}=.}m.1...
7, 60, 255, 100, 52, 250, 228, 33, 207, 233, 210, 221, // .<.d4..!....
242, 155, 53, 195, 113, 190, 191, 169, 167, 115, 155, 140, // ..5.q....s..
42, 215, 199, 220, 147, 163, 4, 239, 156, 132, 162, 111, // *..........o
250, 166, 71, 95, 86, 114, 162, 150, 63, 30, 177, 167, // ..G_Vr..?...
151, 54, 168, 15, 6, 51, 8, 111, 231, 33, 92, 170, // .6...3.o.!..
251, 86, 121, 160, 233, 94, 222, 8, 52, 231, 45, 76, // .Vy..^..4.-L
17, 65, 241, 3, 178, 247, 195, 93, 122, 65, 64, 111, // .A.....]zA@o
161, 251, 105, 220, 228, 0, 237, 177, 231, 195, 179, 140, // ..i.........
31, 132, 238, 105, 137, 209, 161, 247, 219, 75, 79, 76, // ...i.....KOL
233, 114, 27, 122, 238, 98, 134, 66, 157, 34, 184, 207, // .r.z.b.B."..
250, 193, 189, 121, 59, 67, 25, 172, 241, 120, 33, 83, // ...y;C...x!S
78, 101, 193, 254, 99, 92, 163, 234, 217, 169, 255, 146, // Ne..c.......
94, 66, 112, 231, 2, 243, 205, 199, 227, 98, 49, 125, // ^Bp......b1}
165, 232, 209, 73, 69, 184, 83, 255, 51, 91, 57, 93, // ...IE.S.3[9]
40, 147, 143, 154, 132, 101, 153, 92, 101, 30, 53, 53, // (....e..e.55
13, 124, 227, 84, 248, 159, 51, 16, 55, 41, 215, 142, // .|.T..3.7)..
88, 145, 209, 17, 146, 47, 90, 36, 174, 113, 172, 210, // X..../Z$.q..
241, 112, 136, 112, 152, 179, 19, 225, 118, 155, 238, 188, // .p.p....v...
84, 70, 68, 24, 122, 214, 56, 31, 14, 249, 16, 94, // TFD.z.8....^
8, 242, 101, 216, 104, 228, 66, 166, 244, 190, 197, 172, // ..e.h.B.....
253, 165, 143, 77, 20, 207, 95, 98, 251, 156, 146, 83, // ...M.._b...S
57, 142, 114, 11, 153, 182, 153, 34, 132, 159, 244, 14, // 9.r...."....
86, 223, 62, 251, 33, 220, 202, 213, 161, 143, 109, 216, // V.>.!.....m.
15, 231, 22, 225, 143, 241, 161, 219, 51, 136, 146, 154, // ........3...
237, 176, 146, 195, 16, 134, 160, 231, 74, 18, 93, 88, // ........J.]X
167, 169, 242, 88, 109, 146, 160, 4, 68, 65, 93, 127, // ...Xm...DA].
14, 199, 25, 230, 26, 220, 73, 56, 226, 67, 89, 208, // ......I8.CY.
79, 173, 232, 72, 74, 15, 160, 233, 246, 144, 159, 193, // O..HJ.......
0, 95, 174, 242, 238, 237, 197, 191, 126, 43, 255, 243, // ._......~+..
183, 12, 63, 141, 43, 124, 242, 226, 138, 31, 160, 136, // ..?.+|......
89, 109, 54, 141, 224, 0, 81, 22, 229, 226, 29, 138, // Ym6...Q.....
92, 204, 60, 219, 249, 138, 59, 177, 130, 151, 194, 127, // ..<...;.....
105, 32, 134, 147, 201, 132, 82, 121, 57, 84, 196, 225, // i ....Ry9T..
248, 12, 96, 39, 45, 4, 122, 6, 109, 215, 231, 242, // ..`'-.z.m...
68, 194, 71, 192, 12, 213, 151, 234, 115, 122, 159, 155, // D.G.....sz..
55, 205, 0, 166, 145, 88, 95, 145, 163, 99, 1, 43, // 7....X_..c.+
0, 230, 28, 47, 169, 47, 56, 78, 234, 58, 128, 220, // ..././8N.:..
117, 8, 166, 196, 158, 159, 78, 37, 175, 117, 247, 78, // u.....N%.u.N
138, 222, 54, 210, 97, 97, 150, 144, 66, 33, 83, 47, // ..6.aa..B!S/
17, 162, 21, 241, 8, 3, 162, 215, 122, 158, 44, 220, // ........z.,.
152, 8, 99, 16, 242, 163, 101, 14, 167, 135, 7, 206, // ..c...e.....
134, 175, 249, 185, 199, 204, 35, 4, 160, 116, 13, 215, // ......#..t..
88, 189, 18, 126, 62, 86, 25, 75, 52, 64, 158, 63, // X..~>V.K4@.?
183, 219, 31, 126, 105, 129, 29, 131, 0, 167, 178, 113, // ...~i......q
78, 185, 214, 98, 150, 81, 38, 120, 70, 44, 51, 163, // N..b.Q&xF,3.
63, 29, 126, 106, 238, 65, 134, 159, 154, 97, 5, 26, // ?.~j.A...a..
91, 20, 52, 31, 208, 128, 74, 244, 216, 79, 181, 136, // [.4...J..O..
63, 135, 205, 205, 45, 242, 225, 11, 110, 38, 167, 248, // ?...-...n&..
245, 231, 192, 195, 99, 205, 250, 199, 244, 62, 133, 145, // ....c....>..
56, 62, 214, 138, 114, 190, 132, 180, 150, 174, 98, 232, // 8>..r.....b.
249, 18, 212, 172, 168, 3, 28, 27, 62, 27, 218, 69, // ........>..E
253, 86, 181, 165, 172, 45, 21, 7, 171, 123, 105, 232, // .V...-...{i.
208, 206, 49, 28, 180, 164, 192, 74, 63, 147, 118, 22, // ..1....J?.v.
167, 67, 76, 194, 61, 167, 2, 48, 166, 251, 191, 15, // .CL.=..0....
56, 74, 132, 251, 175, 17, 167, 179, 204, 39, 189, 220, // 8J.......'..
155, 168, 49, 20, 83, 125, 183, 205, 139, 234, 62, 124, // ..1.S}....>|
22, 150, 207, 214, 208, 60, 254, 86, 155, 84, 254, 64, // .....<.V.T.@
37, 19, 157, 202, 152, 202, 109, 82, 72, 126, 67, 85, // %.....mRH~CU
115, 79, 99, 51, 74, 242, 91, 106, 130, 91, 201, 81, // sOc3J.[j.[.Q
142, 252, 211, 214, 222, 235, 184, 142, 48, 254, 111, 182, // ........0.o.
126, 14, 239, 36, 170, 228, 247, 182, 250, 54, 220, 225, // ~..$.....6..
252, 182, 141, 127, 111, 199, 172, 228, 63, 108, 249, 187, // ....o...?l..
205, 86, 23, 56, 84, 110, 244, 69, 152, 197, 169, 150, // .V.8Tn.E....
191, 218, 142, 31, 244, 38, 151, 255, 180, 149, 179, 48, // .....&.....0
77, 151, 97, 116, 45, 181, 118, 45, 118, 85, 153, 107, // M.at-.v-vU.k
232, 188, 93, 25, 184, 54, 206, 156, 124, 141, 179, 51, // ..]..6..|..3
14, 139, 221, 126, 250, 31, 255, 11, 22, 19, 178, 116, // ...~.......t
138, 51, 0, 0, 0 // .3..
}; };
static const unsigned char v2[] = { static const unsigned char v2[] = {
31, 139, 8, 8, 219, 27, 244, 98, 0, 3, 109, 97, // .......b..ma
105, 110, 46, 106, 115, 0, 165, 86, 237, 110, 219, 54, // in.js..V.n.6
20, 253, 239, 167, 184, 8, 6, 52, 65, 109, 41, 77, // .......4Am)M
246, 1, 52, 138, 1, 55, 93, 129, 20, 93, 61, 204, // ..4..7]..]=.
233, 218, 254, 43, 37, 210, 22, 91, 138, 84, 249, 97, // ...+%..[.T.a
205, 11, 242, 46, 125, 150, 62, 217, 14, 37, 217, 177, // ....}.>..%..
19, 103, 195, 218, 95, 166, 36, 222, 123, 15, 207, 185, // .g.._.$.{...
247, 208, 143, 130, 19, 228, 188, 149, 133, 127, 116, 54, // ..........t6
144, 85, 109, 172, 167, 107, 42, 135, 84, 250, 74, 13, // .Um..k*.T.J.
201, 10, 205, 133, 165, 27, 154, 91, 83, 209, 163, 36, // .......[S..$
173, 173, 96, 133, 79, 42, 169, 147, 143, 14, 33, 131, // ..`.O*....!.
65, 97, 180, 243, 244, 142, 206, 105, 30, 116, 225, 165, // Aa.....i.t..
209, 116, 120, 68, 215, 131, 1, 33, 218, 7, 171, 219, // .txD...!....
84, 31, 240, 152, 113, 185, 164, 66, 49, 231, 206, 15, // T...q..B1...
16, 229, 153, 212, 194, 30, 140, 241, 5, 223, 202, 147, // ............
245, 39, 39, 218, 44, 7, 227, 201, 179, 233, 155, 43, // .''.,......+
122, 51, 203, 210, 242, 164, 223, 181, 149, 193, 154, 166, // z3..........
143, 189, 155, 89, 141, 126, 217, 124, 193, 183, 122, 220, // ...Y.~.|..z.
45, 54, 111, 136, 46, 132, 99, 168, 79, 51, 51, 247, // -6o...c.O33.
13, 179, 130, 94, 121, 158, 144, 116, 84, 10, 198, 63, // ...^y..tT..?
7, 102, 189, 176, 130, 127, 253, 34, 53, 61, 15, 185, // .f....."5=..
146, 122, 72, 127, 136, 58, 174, 10, 50, 115, 186, 180, // .zH..:..2s..
66, 49, 205, 147, 219, 26, 105, 189, 83, 112, 231, 121, // B1....i.Sp.y
26, 44, 40, 54, 118, 69, 214, 24, 239, 40, 103, 197, // .,(6vE...(g.
39, 242, 134, 78, 142, 143, 127, 28, 82, 83, 10, 77, // '..N....RS.M
191, 25, 189, 48, 6, 82, 188, 21, 57, 205, 132, 93, // ...0.R..9..]
130, 243, 87, 50, 183, 204, 174, 190, 126, 225, 98, 41, // ..W2....~.b)
148, 169, 43, 161, 61, 242, 68, 108, 192, 154, 229, 118, // ..+.=.Dl...v
60, 216, 148, 152, 184, 189, 41, 22, 86, 52, 132, 51, // <.....).V4.3
212, 166, 14, 138, 89, 233, 87, 4, 216, 84, 49, 200, // ....Y.W..T1.
34, 56, 153, 184, 199, 151, 130, 230, 70, 41, 211, 72, // "8......F).H
189, 160, 149, 96, 214, 13, 99, 204, 201, 241, 147, 211, // ...`..c.....
13, 79, 13, 115, 36, 80, 27, 4, 184, 18, 145, 64, // .O.s$P.....@
31, 5, 148, 58, 8, 146, 56, 210, 54, 196, 88, 97, // ...:..8.6.Xa
3, 172, 182, 102, 41, 57, 90, 44, 212, 109, 107, 33, // ...f)9Z,.mk!
208, 128, 142, 37, 83, 1, 105, 138, 0, 94, 42, 97, // ...%S.i..^*a
221, 195, 76, 190, 21, 20, 245, 65, 158, 208, 150, 45, // ..L....A...-
217, 18, 111, 42, 28, 182, 77, 180, 201, 128, 67, 233, // ..o*..M...C.
21, 101, 114, 252, 2, 101, 130, 22, 244, 211, 241, 113, // .er..e.....q
150, 202, 49, 112, 86, 53, 211, 82, 56, 194, 25, 26, // ..1pV5.R8...
161, 84, 252, 173, 4, 151, 161, 106, 161, 186, 138, 225, // .T.....j....
157, 147, 127, 11, 202, 131, 67, 71, 58, 39, 182, 240, // ......CG:'..
16, 184, 44, 194, 134, 58, 244, 134, 138, 107, 52, 65, // ..,..:...k4A
44, 239, 140, 10, 177, 87, 93, 236, 29, 70, 53, 179, // ,....W]..F5.
128, 22, 64, 194, 220, 88, 10, 174, 13, 105, 9, 198, // ..@..X...i..
204, 96, 193, 252, 94, 153, 16, 139, 49, 228, 148, 175, // .`..^...1...
232, 245, 100, 54, 33, 150, 27, 102, 187, 192, 75, 141, // ..d6!..f..K.
78, 212, 44, 214, 96, 138, 102, 53, 43, 4, 205, 124, // N.,.`.f5+..|
251, 28, 195, 182, 96, 198, 221, 57, 52, 138, 194, 204, // ....`..94...
165, 173, 186, 61, 96, 76, 250, 100, 135, 212, 150, 214, // ...=`L.d....
153, 212, 200, 20, 53, 30, 110, 68, 46, 163, 200, 127, // ....5.nD....
129, 44, 14, 44, 81, 85, 144, 206, 3, 128, 71, 229, // .,.,QU....G.
208, 34, 210, 36, 64, 189, 214, 186, 61, 27, 151, 209, // .".$@...=...
59, 242, 224, 5, 137, 42, 23, 60, 70, 186, 245, 76, // ;....*.<F..L
197, 13, 37, 14, 210, 62, 52, 210, 151, 96, 5, 122, // ..%..>4..`.z
17, 96, 1, 163, 198, 148, 99, 119, 95, 227, 150, 170, // .`....cw_...
238, 196, 194, 71, 138, 175, 74, 244, 164, 235, 224, 175, // ...G..J.....
39, 61, 197, 168, 247, 102, 176, 94, 246, 139, 15, 103, // '=...f.^...g
131, 155, 179, 181, 31, 189, 255, 110, 63, 58, 189, 239, // .......n?:..
71, 155, 182, 187, 69, 109, 197, 83, 216, 211, 233, 55, // G...Em.S...7
219, 83, 22, 212, 150, 52, 153, 146, 219, 66, 81, 6, // .S...4...BQ.
85, 172, 152, 159, 31, 148, 222, 215, 238, 105, 154, 86, // U........i.V
125, 251, 36, 141, 75, 15, 198, 123, 154, 41, 75, 217, // }.$.K..{.)K.
78, 134, 17, 136, 189, 213, 166, 193, 62, 215, 53, 93, // N.......>.5]
228, 27, 52, 55, 198, 126, 138, 131, 175, 58, 183, 217, // ..47.~...:..
130, 146, 238, 96, 249, 79, 100, 75, 48, 152, 72, 3, // ...`.OdK0.H.
84, 127, 94, 76, 95, 99, 117, 31, 201, 196, 242, 32, // T.^L_cu....
181, 25, 181, 35, 233, 101, 174, 208, 177, 177, 211, 93, // ...#.e.....]
215, 29, 121, 144, 202, 143, 96, 61, 177, 125, 219, 166, // ..y...`=.}..
153, 94, 77, 40, 212, 156, 121, 225, 182, 51, 117, 30, // .^M(..y..3u.
166, 217, 66, 180, 134, 195, 153, 43, 219, 60, 223, 14, // ..B....+.<..
190, 138, 57, 18, 208, 1, 248, 213, 243, 126, 189, 151, // ..9......~..
74, 152, 5, 32, 142, 12, 28, 230, 210, 92, 209, 239, // J.. ........
138, 97, 52, 108, 245, 29, 165, 123, 13, 71, 198, 37, // .a4l...{.G.%
32, 102, 75, 212, 233, 108, 47, 2, 83, 11, 11, 246, // fK..l/.S...
160, 153, 91, 57, 47, 170, 214, 107, 96, 223, 163, 218, // ..[9/..k`...
52, 208, 181, 146, 133, 53, 177, 155, 45, 76, 29, 198, // 4....5..-L..
120, 23, 217, 255, 128, 182, 128, 42, 33, 143, 168, 210, // x......*!...
162, 243, 136, 180, 250, 232, 192, 208, 203, 253, 200, 54, // ...........6
109, 246, 146, 45, 217, 172, 176, 178, 246, 36, 244, 2, // m..-.....$..
83, 213, 98, 188, 72, 47, 30, 63, 126, 128, 168, 44, // S.b.H/.?~..,
189, 157, 131, 173, 17, 191, 63, 58, 63, 239, 222, 236, // ......?:?...
89, 62, 158, 238, 248, 112, 55, 144, 249, 238, 13, 188, // Y>...p7.....
59, 100, 237, 193, 37, 108, 102, 1, 30, 163, 213, 105, // ;d..%lf....i
184, 164, 47, 77, 192, 17, 209, 139, 240, 29, 156, 24, // ../M........
119, 74, 33, 97, 185, 235, 73, 191, 35, 107, 155, 131, // wJ!a..I.#k..
139, 90, 153, 85, 119, 29, 150, 65, 115, 220, 169, 109, // .Z.Uw..As..m
120, 37, 149, 106, 193, 192, 42, 37, 152, 107, 175, 224, // x%.j..*%.k..
46, 79, 116, 35, 161, 151, 210, 26, 29, 123, 247, 78, // .Ot#.....{.N
218, 7, 88, 248, 87, 163, 155, 212, 245, 142, 213, 161, // ..X.W.......
80, 237, 246, 249, 93, 86, 62, 25, 63, 99, 14, 255, // P...]V>.?c..
96, 126, 93, 235, 244, 66, 42, 209, 55, 17, 23, 21, // `~]..B*.7...
38, 22, 91, 6, 89, 95, 236, 135, 235, 242, 240, 221, // &.[.Y_......
209, 205, 160, 47, 186, 253, 250, 253, 230, 117, 15, 5, // .../.....u..
255, 29, 184, 105, 18, 163, 149, 97, 28, 112, 96, 184, // ...i...a.p`.
231, 227, 254, 207, 227, 97, 121, 8, 140, 71, 67, 226, // .....ay..GC.
112, 255, 120, 232, 36, 55, 124, 117, 116, 54, 248, 7, // p.x.$7|ut6..
71, 33, 75, 134, 124, 10, 0, 0, 0 // G!K.|...
};
static const unsigned char v3[] = {
39, 117, 115, 101, 32, 115, 116, 114, 105, 99, 116, 39, // 'use strict' 39, 117, 115, 101, 32, 115, 116, 114, 105, 99, 116, 39, // 'use strict'
59, 10, 105, 109, 112, 111, 114, 116, 32, 123, 32, 104, // ;.import { h 59, 10, 105, 109, 112, 111, 114, 116, 32, 123, 32, 104, // ;.import { h
44, 32, 104, 116, 109, 108, 44, 32, 114, 101, 110, 100, // , html, rend 44, 32, 104, 116, 109, 108, 44, 32, 114, 101, 110, 100, // , html, rend
@@ -774,7 +282,7 @@ static const unsigned char v3[] = {
112, 41, 44, 32, 100, 111, 99, 117, 109, 101, 110, 116, // p), document 112, 41, 44, 32, 100, 111, 99, 117, 109, 101, 110, 116, // p), document
46, 98, 111, 100, 121, 41, 59, 10, 0 // .body);. 46, 98, 111, 100, 121, 41, 59, 10, 0 // .body);.
}; };
static const unsigned char v4[] = { static const unsigned char v3[] = {
118, 97, 114, 32, 101, 44, 110, 44, 95, 44, 116, 44, // var e,n,_,t, 118, 97, 114, 32, 101, 44, 110, 44, 95, 44, 116, 44, // var e,n,_,t,
111, 44, 114, 44, 117, 44, 108, 61, 123, 125, 44, 105, // o,r,u,l={},i 111, 44, 114, 44, 117, 44, 108, 61, 123, 125, 44, 105, // o,r,u,l={},i
61, 91, 93, 44, 99, 61, 47, 97, 99, 105, 116, 124, // =[],c=/acit| 61, 91, 93, 44, 99, 61, 47, 97, 99, 105, 116, 124, // =[],c=/acit|
@@ -1876,147 +1384,7 @@ static const unsigned char v4[] = {
115, 101, 69, 114, 114, 111, 114, 66, 111, 117, 110, 100, // seErrorBound 115, 101, 69, 114, 114, 111, 114, 66, 111, 117, 110, 100, // seErrorBound
97, 114, 121, 125, 59, 10, 0 // ary};. 97, 114, 121, 125, 59, 10, 0 // ary};.
}; };
static const unsigned char v5[] = { static const unsigned char v4[] = {
60, 33, 68, 79, 67, 84, 89, 80, 69, 32, 104, 116, // <!DOCTYPE ht
109, 108, 62, 10, 60, 104, 116, 109, 108, 32, 108, 97, // ml>.<html la
110, 103, 61, 34, 101, 110, 34, 62, 10, 32, 32, 60, // ng="en">. <
104, 101, 97, 100, 62, 10, 32, 32, 32, 32, 60, 109, // head>. <m
101, 116, 97, 32, 110, 97, 109, 101, 61, 34, 100, 101, // eta name="de
115, 99, 114, 105, 112, 116, 105, 111, 110, 34, 32, 99, // scription" c
111, 110, 116, 101, 110, 116, 61, 34, 77, 111, 110, 103, // ontent="Mong
111, 111, 115, 101, 32, 69, 109, 98, 101, 100, 100, 101, // oose Embedde
100, 32, 70, 105, 108, 101, 115, 121, 116, 101, 109, 32, // d Filesytem
101, 120, 97, 109, 112, 108, 101, 34, 32, 47, 62, 10, // example" />.
32, 32, 32, 32, 60, 109, 101, 116, 97, 32, 104, 116, // <meta ht
116, 112, 45, 101, 113, 117, 105, 118, 61, 34, 67, 111, // tp-equiv="Co
110, 116, 101, 110, 116, 45, 84, 121, 112, 101, 34, 32, // ntent-Type"
99, 111, 110, 116, 101, 110, 116, 61, 34, 116, 101, 120, // content="tex
116, 47, 104, 116, 109, 108, 59, 32, 99, 104, 97, 114, // t/html; char
115, 101, 116, 61, 85, 84, 70, 45, 56, 34, 62, 10, // set=UTF-8">.
32, 32, 32, 32, 60, 109, 101, 116, 97, 32, 104, 116, // <meta ht
116, 112, 45, 101, 113, 117, 105, 118, 61, 34, 88, 45, // tp-equiv="X-
85, 65, 45, 67, 111, 109, 112, 97, 116, 105, 98, 108, // UA-Compatibl
101, 34, 32, 99, 111, 110, 116, 101, 110, 116, 61, 34, // e" content="
73, 69, 61, 101, 100, 103, 101, 34, 62, 10, 32, 32, // IE=edge">.
32, 32, 60, 109, 101, 116, 97, 32, 110, 97, 109, 101, // <meta name
61, 34, 118, 105, 101, 119, 112, 111, 114, 116, 34, 32, // ="viewport"
99, 111, 110, 116, 101, 110, 116, 61, 34, 119, 105, 100, // content="wid
116, 104, 61, 100, 101, 118, 105, 99, 101, 45, 119, 105, // th=device-wi
100, 116, 104, 44, 32, 105, 110, 105, 116, 105, 97, 108, // dth, initial
45, 115, 99, 97, 108, 101, 61, 49, 34, 62, 10, 32, // -scale=1">.
32, 32, 32, 32, 32, 32, 32, 60, 116, 105, 116, 108, // <titl
101, 62, 77, 111, 110, 103, 111, 111, 115, 101, 32, 69, // e>Mongoose E
109, 98, 101, 100, 100, 101, 100, 32, 70, 105, 108, 101, // mbedded File
115, 121, 116, 101, 109, 32, 101, 120, 97, 109, 112, 108, // sytem exampl
101, 60, 47, 116, 105, 116, 108, 101, 62, 10, 32, 32, // e</title>.
32, 32, 60, 108, 105, 110, 107, 32, 114, 101, 108, 61, // <link rel=
34, 115, 116, 121, 108, 101, 115, 104, 101, 101, 116, 34, // "stylesheet"
32, 104, 114, 101, 102, 61, 34, 115, 116, 121, 108, 101, // href="style
46, 99, 115, 115, 34, 32, 47, 62, 10, 32, 32, 60, // .css" />. <
47, 104, 101, 97, 100, 62, 10, 32, 32, 60, 98, 111, // /head>. <bo
100, 121, 62, 60, 47, 98, 111, 100, 121, 62, 10, 32, // dy></body>.
32, 60, 115, 99, 114, 105, 112, 116, 32, 116, 121, 112, // <script typ
101, 61, 34, 109, 111, 100, 117, 108, 101, 34, 32, 115, // e="module" s
114, 99, 61, 34, 109, 97, 105, 110, 46, 106, 115, 34, // rc="main.js"
62, 60, 47, 115, 99, 114, 105, 112, 116, 62, 10, 60, // ></script>.<
47, 104, 116, 109, 108, 62, 10, 0 // /html>.
};
static const unsigned char v6[] = {
31, 139, 8, 8, 219, 27, 244, 98, 0, 3, 105, 110, // .......b..in
100, 101, 120, 46, 104, 116, 109, 108, 0, 141, 145, 77, // dex.html...M
75, 3, 49, 16, 134, 239, 253, 21, 99, 206, 166, 139, // K.1.....c...
55, 193, 205, 130, 212, 22, 60, 136, 30, 90, 208, 99, // 7.....<..Z.c
154, 140, 205, 104, 62, 214, 205, 244, 99, 255, 189, 217, // ...h>...c...
110, 133, 5, 17, 60, 205, 7, 47, 15, 51, 239, 91, // n...<../.3.[
95, 61, 60, 47, 214, 111, 47, 75, 112, 28, 124, 51, // _=</.o/Kp.|3
171, 135, 2, 94, 199, 157, 18, 24, 69, 51, 3, 168, // ...^....E3..
29, 106, 59, 52, 165, 13, 200, 26, 162, 14, 168, 132, // .j;4........
197, 108, 58, 106, 153, 82, 20, 96, 82, 100, 140, 172, // .l:j.R.`Rd..
196, 83, 138, 187, 148, 50, 194, 50, 108, 209, 90, 180, // .S...2.2l.Z.
176, 34, 143, 185, 103, 12, 128, 39, 29, 90, 143, 2, // ."..g..'.Z..
170, 41, 205, 49, 183, 18, 191, 246, 116, 80, 98, 49, // .).1....tPb1
98, 228, 186, 111, 113, 2, 101, 60, 113, 53, 28, 118, // b..oq.e<q5.v
7, 198, 233, 46, 35, 171, 205, 122, 37, 111, 197, 95, // ....#..z%o._
156, 87, 185, 185, 151, 139, 20, 90, 205, 180, 245, 83, // .W.....Z...S
212, 227, 82, 161, 221, 161, 248, 253, 207, 129, 240, 216, // ..R.........
166, 142, 39, 226, 35, 89, 118, 202, 226, 129, 12, 202, // ..'.#Yv.....
243, 112, 13, 20, 137, 73, 123, 153, 141, 246, 168, 110, // .p...I{....n
46, 160, 51, 140, 137, 61, 54, 255, 112, 160, 174, 70, // ..3..=6.p..F
233, 120, 131, 167, 248, 9, 29, 122, 37, 50, 247, 69, // .x.....z%2.E
233, 16, 203, 17, 174, 195, 247, 203, 102, 110, 114, 190, // ........fnr.
152, 86, 87, 63, 97, 212, 219, 100, 251, 166, 174, 206, // .VW?a..d....
101, 152, 199, 52, 128, 139, 117, 74, 132, 100, 247, 195, // e..4..uJ.d..
223, 185, 51, 101, 208, 20, 231, 31, 89, 20, 245, 40, // ..3e....Y..(
42, 49, 87, 99, 220, 223, 14, 156, 225, 97, 255, 1, // *1Wc.....a..
0, 0, 0 // ..
};
static const unsigned char v7[] = {
31, 139, 8, 8, 219, 27, 244, 98, 0, 3, 115, 116, // .......b..st
121, 108, 101, 46, 99, 115, 115, 0, 117, 84, 203, 110, // yle.css.uT.n
219, 48, 16, 188, 251, 43, 88, 4, 5, 218, 32, 82, // .0...+X... R
44, 41, 178, 99, 249, 210, 91, 63, 162, 232, 129, 18, // ,).c..[?....
87, 54, 17, 138, 84, 73, 58, 118, 90, 248, 223, 187, // W6..TI:vZ...
164, 30, 164, 226, 228, 98, 75, 187, 163, 125, 204, 12, // .....bK..}..
121, 79, 254, 145, 90, 93, 18, 195, 255, 114, 121, 168, // yO..Z]...ry.
240, 89, 51, 208, 9, 134, 246, 228, 186, 58, 218, 78, // .Y3......:.N
60, 96, 140, 189, 33, 172, 163, 250, 192, 101, 69, 214, // <`..!....eE.
123, 210, 83, 198, 60, 28, 159, 143, 192, 15, 71, 91, // {.S.<.....G[
145, 108, 189, 254, 186, 39, 173, 146, 238, 121, 211, 95, // .l...'...y._
136, 161, 210, 36, 6, 52, 111, 93, 41, 3, 2, 26, // ...$.4o])...
251, 64, 184, 236, 79, 248, 39, 104, 13, 162, 170, 106, // .@..O.'h...j
104, 149, 134, 7, 98, 225, 98, 169, 6, 138, 109, 212, // h...b.b...m.
201, 10, 46, 161, 34, 82, 73, 216, 15, 195, 29, 41, // ...."RI....)
83, 231, 202, 5, 200, 23, 222, 245, 74, 91, 42, 237, // S.......J[*.
126, 28, 22, 219, 185, 110, 74, 112, 70, 238, 154, 166, // ~....nJpF...
89, 64, 174, 171, 70, 49, 108, 208, 107, 192, 218, 141, // Y@..F1l.k...
18, 10, 241, 119, 197, 182, 24, 70, 77, 90, 218, 113, // ...w...FMZ.q
241, 86, 145, 78, 73, 101, 122, 218, 192, 24, 63, 143, // .V.NIez...?.
91, 213, 74, 96, 139, 49, 136, 36, 225, 92, 166, 163, // [.J`.1.$....
66, 184, 88, 77, 155, 151, 131, 86, 39, 201, 176, 36, // B.XM...V'..$
99, 44, 230, 37, 205, 160, 195, 223, 2, 186, 105, 204, // c,.%......i.
68, 83, 198, 79, 198, 229, 114, 23, 189, 174, 166, 165, // DS.O..r.....
103, 82, 82, 252, 92, 73, 156, 51, 234, 150, 149, 253, // gRR..I.3....
229, 179, 77, 23, 13, 203, 161, 40, 86, 163, 213, 43, // ..M....(V..+
55, 220, 2, 115, 143, 180, 177, 252, 53, 222, 189, 44, // 7..s....5..,
189, 30, 115, 175, 197, 22, 0, 72, 0, 233, 184, 76, // ..s....H...L
206, 156, 217, 99, 69, 118, 88, 21, 193, 181, 69, 232, // ...cEvX...E.
138, 44, 193, 126, 130, 155, 229, 162, 149, 189, 63, 166, // .,.~......?.
190, 109, 139, 125, 155, 147, 54, 238, 173, 87, 92, 90, // .m.}..6..W.Z
228, 16, 75, 50, 110, 122, 65, 81, 2, 46, 157, 238, // ..K2nzAQ....
73, 45, 84, 243, 178, 216, 108, 131, 84, 122, 202, 62, // I-T...l.Tz.>
82, 102, 53, 76, 247, 11, 203, 208, 90, 0, 251, 237, // Rf5L....Z...
44, 132, 66, 114, 251, 230, 89, 9, 61, 233, 201, 42, // ,.Br..Y.=..*
183, 139, 233, 148, 178, 71, 132, 89, 141, 14, 229, 150, // .....G.Y....
43, 52, 53, 74, 74, 210, 220, 120, 102, 26, 108, 67, // +45JJ..xf.lC
113, 20, 29, 155, 158, 228, 107, 39, 196, 72, 139, 175, // q.....k'.H..
229, 176, 44, 105, 5, 92, 16, 56, 175, 225, 222, 199, // ..,i...8....
148, 55, 108, 148, 26, 28, 237, 6, 246, 244, 248, 163, // .7l.........
119, 163, 171, 55, 18, 98, 60, 203, 192, 102, 80, 68, // w..7.b<..fPD
241, 40, 117, 42, 213, 89, 211, 30, 17, 231, 35, 202, // .(u*.Y....#.
157, 120, 255, 186, 46, 46, 234, 1, 157, 57, 188, 87, // .x.......9.W
152, 65, 59, 139, 38, 160, 69, 34, 203, 208, 187, 220, // .A;.&.E"....
177, 91, 79, 69, 102, 220, 185, 35, 62, 81, 226, 29, // .[OEf..#>Q..
238, 219, 24, 60, 217, 220, 155, 41, 208, 149, 141, 51, // ...<...)...3
90, 213, 243, 6, 173, 205, 168, 69, 107, 166, 127, 148, // Z......Ek...
65, 88, 220, 35, 247, 7, 165, 252, 240, 160, 60, 185, // AX.#......<.
232, 80, 51, 209, 131, 236, 35, 212, 149, 30, 106, 45, // .P3...#...j-
253, 219, 210, 208, 245, 125, 178, 133, 33, 233, 70, 121, // .....}..!.Fy
159, 163, 224, 79, 197, 234, 241, 158, 252, 212, 72, 197, // ...O......H.
253, 163, 83, 224, 124, 43, 172, 251, 77, 28, 193, 21, // ..S.|+..M...
153, 105, 70, 139, 127, 126, 63, 170, 87, 208, 173, 192, // .iF..~?.W...
235, 43, 152, 6, 241, 73, 150, 59, 221, 6, 55, 13, // .+...I.;..7.
87, 231, 148, 200, 66, 98, 151, 165, 155, 77, 148, 90, // W...Bb...M.Z
135, 212, 115, 145, 22, 69, 72, 237, 66, 102, 91, 134, // ..s..EH.Bf[.
240, 115, 8, 111, 54, 139, 90, 219, 144, 41, 159, 23, // .s.o6.Z..)..
165, 54, 81, 38, 154, 171, 12, 225, 167, 229, 88, 79, // .6Q&......XO
33, 83, 44, 167, 42, 66, 38, 143, 166, 138, 87, 95, // !S,.*B&...W_
78, 21, 237, 62, 15, 245, 163, 3, 198, 41, 249, 22, // N..>.....)..
221, 74, 89, 145, 225, 105, 252, 142, 224, 15, 15, 235, // .JY..i......
64, 244, 132, 205, 183, 254, 228, 94, 227, 82, 244, 50, // @......^.R.2
95, 112, 249, 84, 201, 201, 61, 74, 185, 212, 5, 63, // _p.T..=J...?
252, 15, 225, 232, 67, 124, 39, 7, 0, 0, 0 // ....C|'...
};
static const unsigned char v8[] = {
42, 32, 123, 32, 98, 111, 120, 45, 115, 105, 122, 105, // * { box-sizi 42, 32, 123, 32, 98, 111, 120, 45, 115, 105, 122, 105, // * { box-sizi
110, 103, 58, 32, 98, 111, 114, 100, 101, 114, 45, 98, // ng: border-b 110, 103, 58, 32, 98, 111, 114, 100, 101, 114, 45, 98, // ng: border-b
111, 120, 59, 32, 125, 10, 104, 116, 109, 108, 44, 32, // ox; }.html, 111, 120, 59, 32, 125, 10, 104, 116, 109, 108, 44, 32, // ox; }.html,
@@ -2178,14 +1546,10 @@ static const struct packed_file {
size_t size; size_t size;
time_t mtime; time_t mtime;
} packed_files[] = { } packed_files[] = {
{"/web_root/preact.min.js.gz", v1, sizeof(v1), 1660165083}, {"/web_root/index.html", v1, sizeof(v1), 1692212839},
{"/web_root/main.js.gz", v2, sizeof(v2), 1660165083}, {"/web_root/main.js", v2, sizeof(v2), 1692212839},
{"/web_root/main.js", v3, sizeof(v3), 1660586478}, {"/web_root/preact.min.js", v3, sizeof(v3), 1692212839},
{"/web_root/preact.min.js", v4, sizeof(v4), 1660586478}, {"/web_root/style.css", v4, sizeof(v4), 1692212839},
{"/web_root/index.html", v5, sizeof(v5), 1660586478},
{"/web_root/index.html.gz", v6, sizeof(v6), 1660165083},
{"/web_root/style.css.gz", v7, sizeof(v7), 1660165083},
{"/web_root/style.css", v8, sizeof(v8), 1660586478},
{NULL, NULL, 0, 0} {NULL, NULL, 0, 0}
}; };
@@ -2193,11 +1557,9 @@ static int scmp(const char *a, const char *b) {
while (*a && (*a == *b)) a++, b++; while (*a && (*a == *b)) a++, b++;
return *(const unsigned char *) a - *(const unsigned char *) b; return *(const unsigned char *) a - *(const unsigned char *) b;
} }
const char *mg_unlist(size_t no);
const char *mg_unlist(size_t no) { const char *mg_unlist(size_t no) {
return packed_files[no].name; return packed_files[no].name;
} }
const char *mg_unpack(const char *path, size_t *size, time_t *mtime);
const char *mg_unpack(const char *name, size_t *size, time_t *mtime) { const char *mg_unpack(const char *name, size_t *size, time_t *mtime) {
const struct packed_file *p; const struct packed_file *p;
for (p = packed_files; p->name != NULL; p++) { for (p = packed_files; p->name != NULL; p++) {
@@ -5,3 +5,6 @@ idf_component_register(SRCS "main.c"
"mongoose.c") "mongoose.c")
component_compile_options(-DMG_ENABLE_LINES) component_compile_options(-DMG_ENABLE_LINES)
component_compile_options(-DMG_ENABLE_PACKED_FS) component_compile_options(-DMG_ENABLE_PACKED_FS)
component_compile_options(-DHTTP_URL="http://0.0.0.0:80")
component_compile_options(-DHTTPS_URL="https://0.0.0.0:443")
component_compile_options(-DMG_TLS=MG_TLS_NONE) # change to 'MG_TLS_MBED' to enable TLS
+8 -6
View File
@@ -2,11 +2,8 @@
// All rights reserved // All rights reserved
#include "esp_spiffs.h" #include "esp_spiffs.h"
//#include "freertos/FreeRTOS.h"
#include "mongoose.h" #include "mongoose.h"
#include "net.h"
const char *s_listening_url = "http://0.0.0.0:80";
void device_dashboard_fn(struct mg_connection *, int, void *, void *);
#define WIFI_SSID "YOUR_WIFI_NETWORK_NAME" // SET THIS! #define WIFI_SSID "YOUR_WIFI_NETWORK_NAME" // SET THIS!
#define WIFI_PASS "YOUR_WIFI_PASSWORD" // SET THIS! #define WIFI_PASS "YOUR_WIFI_PASSWORD" // SET THIS!
@@ -29,7 +26,12 @@ void app_main(void) {
struct mg_mgr mgr; struct mg_mgr mgr;
mg_log_set(MG_LL_DEBUG); // Set log level mg_log_set(MG_LL_DEBUG); // Set log level
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
MG_INFO(("Mongoose v%s on %s", MG_VERSION, s_listening_url)); MG_INFO(("Mongoose version : v%s", MG_VERSION));
mg_http_listen(&mgr, s_listening_url, device_dashboard_fn, NULL); MG_INFO(("Listening on : %s", HTTP_URL));
#if MG_ENABLE_MBEDTLS
MG_INFO(("Listening on : %s", HTTPS_URL));
#endif
web_init(&mgr);
for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop for (;;) mg_mgr_poll(&mgr, 1000); // Infinite event loop
} }
+1
View File
@@ -0,0 +1 @@
../../../device-dashboard/net.h
@@ -27,7 +27,7 @@ static void cb2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
} else if (ev == MG_EV_HTTP_MSG) { } else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = ev_data; // Print HTTP response struct mg_http_message *hm = ev_data; // Print HTTP response
MG_INFO(("Fetched:\n%.*s", (int) hm->message.len, hm->message.ptr)); MG_INFO(("Fetched:\n%.*s", (int) hm->message.len, hm->message.ptr));
c->is_closing = 1; c->is_draining = 1;
} }
} }
+4 -2
View File
@@ -12,11 +12,13 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (mg_http_match_uri(hm, "/upload")) { if (mg_http_match_uri(hm, "/upload")) {
char path[80], name[64]; char path[80], name[64];
mg_http_get_var(&hm->query, "name", name, sizeof(name)); mg_http_get_var(&hm->query, "name", name, sizeof(name));
mg_snprintf(path, sizeof(path), "/tmp/%s", name);
if (name[0] == '\0') { if (name[0] == '\0') {
mg_http_reply(c, 400, "", "%s", "name required"); mg_http_reply(c, 400, "", "%s", "name required");
} else if (!mg_path_is_sane(path)) {
mg_http_reply(c, 400, "", "%s", "invalid path");
} else { } else {
mg_snprintf(path, sizeof(path), "/tmp/%s", name); mg_http_upload(c, hm, &mg_fs_posix, path, 99999);
mg_http_upload(c, hm, &mg_fs_posix, mg_remove_double_dots(path), 99999);
} }
} else { } else {
struct mg_http_serve_opts opts = {.root_dir = "web_root"}; struct mg_http_serve_opts opts = {.root_dir = "web_root"};
+27 -15
View File
@@ -1,8 +1,8 @@
// Copyright (c) 2020 Cesanta Software Limited // Copyright (c) 2020 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// Streaming upload example. Demonstrates how to use MG_EV_HTTP_CHUNK events // Streaming upload example. Demonstrates how to use MG_EV_READ events
// to send large payload in smaller chunks. To test, use curl utility: // to get large payload in smaller chunks. To test, use curl utility:
// //
// curl http://localhost:8000/upload?name=a.txt --data-binary @large_file.txt // curl http://localhost:8000/upload?name=a.txt --data-binary @large_file.txt
@@ -12,21 +12,33 @@
// /upload - Saves the next file chunk // /upload - Saves the next file chunk
// all other URI - serves web_root/ directory // all other URI - serves web_root/ directory
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (ev == MG_EV_READ) {
if (ev == MG_EV_HTTP_CHUNK && mg_http_match_uri(hm, "/upload")) { // Parse the incoming data ourselves. If we can parse the request,
MG_INFO(("Got chunk len %lu", (unsigned long) hm->chunk.len)); // store two size_t variables in the c->data: expected len and recv len.
MG_INFO(("Query string: [%.*s]", (int) hm->query.len, hm->query.ptr)); size_t *data = (size_t *) c->data;
// MG_INFO(("Chunk data:\n%.*s", (int) hm->chunk.len, hm->chunk.ptr)); if (data[0]) { // Already parsed, simply print received data
mg_http_delete_chunk(c, hm); data[1] += c->recv.len;
if (hm->chunk.len == 0) { MG_INFO(("Got chunk len %lu, %lu total", c->recv.len, data[1]));
MG_INFO(("Last chunk received, sending response")); c->recv.len = 0; // And cleanup the receive buffer. Streaming!
mg_http_reply(c, 200, "", "ok (chunked)\n"); if (data[1] >= data[0]) mg_http_reply(c, 200, "", "ok\n");
} else {
struct mg_http_message hm;
int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
if (n < 0) mg_error(c, "Bad response");
if (n > 0) {
if (mg_http_match_uri(&hm, "/upload")) {
MG_INFO(("Got chunk len %lu", c->recv.len - n));
data[0] = hm.body.len;
data[1] = c->recv.len - n;
if (data[1] >= data[0]) mg_http_reply(c, 200, "", "ok\n");
} else {
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, &hm, &opts);
}
}
} }
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, hm, &opts);
} }
(void) fn_data; (void) fn_data, (void) ev_data;
} }
int main(void) { int main(void) {
@@ -19,12 +19,10 @@
of RAM to buffer everything. of RAM to buffer everything.
<br><br> <br><br>
In order to upload large files to a memory-constrained system, use In order to upload large files to a memory-constrained system, use
<code>MG_EV_HTTP_CHUNK</code> on a server side. It fires when <code>MG_EV_READ</code> on a server side. It fires when
a partial HTTP message has been received (or a chunk-encoded chunk). a partial HTTP message has been received. We discourage this method,
Use <code>mg_http_delete_chunk()</code> to release chunk memory. since the client can send chunked-encoded data. Instead, please
When 0-sized chunk is received, that's the end of the message. split the upload into smaller pieces and send sequentially.
Use <code>MG_MAX_RECV_SIZE</code> build constant to limit
maximum chunk size on a server side.
<br><br> <br><br>
In this example, JavaScript code uses "fetch()" browser API. In this example, JavaScript code uses "fetch()" browser API.
Uploaded file is not saved, but rather printed by server side. Uploaded file is not saved, but rather printed by server side.
+10 -3
View File
@@ -1,14 +1,16 @@
PROG ?= example # Program we are building PROG ?= example # Program we are building
PACK ?= ./pack # Packing executable
DELETE = rm -rf # Command to remove files DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files SOURCES = main.c mongoose.c packed_fs.c # Source code files, packed_fs.c contains ca.pem, which contains CA certs for TLS
CFLAGS = -W -Wall -Wextra -g -I. # Build options CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options # Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_PACKED_FS=1
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary PROG ?= example.exe # Use .exe suffix for the binary
PACK = pack.exe # Packing executable
CC = gcc # Use MinGW gcc compiler CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 # Link against Winsock library CFLAGS += -lws2_32 # Link against Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
@@ -23,7 +25,12 @@ $(PROG): $(SOURCES) # Build program from sources
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT) $(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
clean: # Cleanup. Delete built program and all build artifacts clean: # Cleanup. Delete built program and all build artifacts
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM mbedtls $(DELETE) $(PROG) *.o *.obj *.exe *.dSYM mbedtls $(PACK)
# Generate packed filesystem for serving cert
packed_fs.c: $(wildcard certs/*) Makefile
$(CC) ../../test/pack.c -o $(PACK)
$(PACK) $(wildcard certs/*) > $@
# see https://mongoose.ws/tutorials/tls/#how-to-build for TLS build options # see https://mongoose.ws/tutorials/tls/#how-to-build for TLS build options
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
+1
View File
@@ -0,0 +1 @@
../../../test/data/ca.pem
+3 -3
View File
@@ -28,9 +28,9 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
// Connected to server. Extract host name from URL // Connected to server. Extract host name from URL
struct mg_str host = mg_url_host(s_url); struct mg_str host = mg_url_host(s_url);
// If s_url is https://, tell client connection to use TLS
if (mg_url_is_ssl(s_url)) { if (mg_url_is_ssl(s_url)) {
struct mg_tls_opts opts = {.ca = "ca.pem", .srvname = host}; struct mg_tls_opts opts = {.ca = mg_unpacked("/certs/ca.pem"),
.name = mg_url_host(s_url)};
mg_tls_init(c, &opts); mg_tls_init(c, &opts);
} }
@@ -49,7 +49,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
// Response is received. Print it // Response is received. Print it
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
printf("%.*s", (int) hm->message.len, hm->message.ptr); printf("%.*s", (int) hm->message.len, hm->message.ptr);
c->is_closing = 1; // Tell mongoose to close this connection c->is_draining = 1; // Tell mongoose to close this connection
*(bool *) fn_data = true; // Tell event loop to stop *(bool *) fn_data = true; // Tell event loop to stop
} else if (ev == MG_EV_ERROR) { } else if (ev == MG_EV_ERROR) {
*(bool *) fn_data = true; // Error, tell event loop to stop *(bool *) fn_data = true; // Error, tell event loop to stop
File diff suppressed because it is too large. Load diff
+3 -3
View File
@@ -1,13 +1,13 @@
PROG ?= example # Program we are building PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files SOURCES = main.c mongoose.c packed_fs.c # Source code files, packed_fs.c contains ca.pem, which contains CA certs for TLS
CFLAGS = -W -Wall -Wextra -g -I. # Build options CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options # Mongoose build options. See https://mongoose.ws/documentation/#build-options
#CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_PACKED_FS=1
// see tutorial at https://mongoose.ws/tutorials/http-proxy-client/ # See tutorial at https://mongoose.ws/tutorials/http-proxy-client/
ARGS ?= 167.235.63.238:3128 http://info.cern.ch/ # default call arguments ARGS ?= 167.235.63.238:3128 http://info.cern.ch/ # default call arguments
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
+12 -9
View File
@@ -20,15 +20,17 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
} else if (ev == MG_EV_CONNECT) { } else if (ev == MG_EV_CONNECT) {
// Proxy TCP connection established. Send CONNECT request // Proxy TCP connection established. Send CONNECT request
struct mg_str host = mg_url_host(url); struct mg_str host = mg_url_host(url);
if (mg_url_is_ssl(url)) {
struct mg_tls_opts opts = {.ca = mg_unpacked("/certs/ca.pem"),
.name = host};
mg_tls_init(c, &opts);
}
// c->is_hexdumping = 1; // c->is_hexdumping = 1;
mg_printf(c, "CONNECT %.*s:%hu HTTP/1.1\r\nHost: %.*s:%hu\r\n\r\n", mg_printf(c, "CONNECT %.*s:%hu HTTP/1.1\r\nHost: %.*s:%hu\r\n\r\n",
(int) host.len, host.ptr, mg_url_port(url), (int) host.len, (int) host.len, host.ptr, mg_url_port(url), (int) host.len,
host.ptr, mg_url_port(url)); host.ptr, mg_url_port(url));
// If target URL is SSL/TLS, command client connection to use TLS
if (mg_url_is_ssl(url)) {
struct mg_tls_opts opts = {.ca = "ca.pem"};
mg_tls_init(c, &opts);
}
} else if (!connected && ev == MG_EV_READ) { } else if (!connected && ev == MG_EV_READ) {
struct mg_http_message hm; struct mg_http_message hm;
int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm); int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
@@ -40,10 +42,11 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
("Connected to proxy, status: %.*s", (int) hm.uri.len, hm.uri.ptr)); ("Connected to proxy, status: %.*s", (int) hm.uri.len, hm.uri.ptr));
mg_iobuf_del(&c->recv, 0, n); mg_iobuf_del(&c->recv, 0, n);
// Send request to the target server // Send request to the target server
mg_printf(c, "GET %s HTTP/1.0\r\n" mg_printf(c,
"Host: %.*s\r\n" "GET %s HTTP/1.0\r\n"
"\r\n", "Host: %.*s\r\n"
mg_url_uri(url), (int) host.len, host.ptr); "\r\n",
mg_url_uri(url), (int) host.len, host.ptr);
} }
} }
} }
+1
View File
@@ -0,0 +1 @@
../http-client/packed_fs.c
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
+43 -5
View File
@@ -18,17 +18,55 @@ static const char *s_http_addr = "http://0.0.0.0:8000"; // HTTP port
static const char *s_https_addr = "https://0.0.0.0:8443"; // HTTPS port static const char *s_https_addr = "https://0.0.0.0:8443"; // HTTPS port
static const char *s_root_dir = "."; static const char *s_root_dir = ".";
// Self signed certificates
// https://mongoose.ws/documentation/tutorials/tls/#self-signed-certificates
#ifdef TLS_TWOWAY
static const char *s_tls_ca =
"-----BEGIN CERTIFICATE-----\n"
"MIIBqjCCAU+gAwIBAgIUESoOPGqMhf9uarzblVFwzrQweMcwCgYIKoZIzj0EAwIw\n"
"RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50\n"
"YTESMBAGA1UEAwwJVGVzdCBSb290MCAXDTIwMDUwOTIxNTE0NFoYDzIwNTAwNTA5\n"
"MjE1MTQ0WjBEMQswCQYDVQQGEwJJRTEPMA0GA1UEBwwGRHVibGluMRAwDgYDVQQK\n"
"DAdDZXNhbnRhMRIwEAYDVQQDDAlUZXN0IFJvb3QwWTATBgcqhkjOPQIBBggqhkjO\n"
"PQMBBwNCAAQsq9ECZiSW1xI+CVBP8VDuUehVA166sR2YsnJ5J6gbMQ1dUCH/QvLa\n"
"dBdeU7JlQcH8hN5KEbmM9BnZxMor6ussox0wGzAMBgNVHRMEBTADAQH/MAsGA1Ud\n"
"DwQEAwIBrjAKBggqhkjOPQQDAgNJADBGAiEAnHFsAIwGQQyRL81B04dH6d86Iq0l\n"
"fL8OKzndegxOaB0CIQCPwSIwEGFdURDqCC0CY2dnMrUGY5ZXu3hHCojZGS7zvg==\n"
"-----END CERTIFICATE-----\n";
#endif
static const char *s_tls_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIBhzCCASygAwIBAgIUbnMoVd8TtWH1T09dANkK2LU6IUswCgYIKoZIzj0EAwIw\n"
"RDELMAkGA1UEBhMCSUUxDzANBgNVBAcMBkR1YmxpbjEQMA4GA1UECgwHQ2VzYW50\n"
"YTESMBAGA1UEAwwJVGVzdCBSb290MB4XDTIwMDUwOTIxNTE0OVoXDTMwMDUwOTIx\n"
"NTE0OVowETEPMA0GA1UEAwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD\n"
"QgAEkuBGnInDN6l06zVVQ1VcrOvH5FDu9MC6FwJc2e201P8hEpq0Q/SJS2nkbSuW\n"
"H/wBTTBaeXN2uhlBzMUWK790KKMvMC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCA6gw\n"
"EwYDVR0lBAwwCgYIKwYBBQUHAwEwCgYIKoZIzj0EAwIDSQAwRgIhAPo6xx7LjCdZ\n"
"QY133XvLjAgVFrlucOZHONFVQuDXZsjwAiEAzHBNligA08c5U3SySYcnkhurGg50\n"
"BllCI0eYQ9ggp/o=\n"
"-----END CERTIFICATE-----\n";
static const char *s_tls_key =
"-----BEGIN PRIVATE KEY-----\n"
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQglNni0t9Dg9icgG8w\n"
"kbfxWSS+TuNgbtNybIQXcm3NHpmhRANCAASS4EacicM3qXTrNVVDVVys68fkUO70\n"
"wLoXAlzZ7bTU/yESmrRD9IlLaeRtK5Yf/AFNMFp5c3a6GUHMxRYrv3Qo\n"
"-----END PRIVATE KEY-----\n";
// We use the same event handler function for HTTP and HTTPS connections // We use the same event handler function for HTTP and HTTPS connections
// fn_data is NULL for plain HTTP, and non-NULL for HTTPS // fn_data is NULL for plain HTTP, and non-NULL for HTTPS
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_ACCEPT && fn_data != NULL) { if (ev == MG_EV_ACCEPT && fn_data != NULL) {
struct mg_tls_opts opts = { struct mg_tls_opts opts = {
//.ca = "ca.pem", // Uncomment to enable two-way SSL #ifdef TLS_TWOWAY
.cert = "server.pem", // Certificate PEM file .ca = mg_str(s_tls_ca),
.certkey = "server.pem", // This pem contains both cert and key #endif
}; .cert = mg_str(s_tls_cert),
.key = mg_str(s_tls_key)};
mg_tls_init(c, &opts); mg_tls_init(c, &opts);
} else if (ev == MG_EV_HTTP_MSG) { }
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/api/stats")) { if (mg_http_match_uri(hm, "/api/stats")) {
// Print some statistics about currently established connections // Print some statistics about currently established connections
+2 -2
View File
@@ -1,11 +1,11 @@
PROG ?= example # Program we are building PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files SOURCES = main.c mongoose.c packed_fs.c # Source code files, packed_fs.c contains ca.pem, which contains CA certs for TLS
CFLAGS = -W -Wall -Wextra -g -I. # Build options CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options # Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_IO_SIZE=128 -DMG_ENABLE_LINES=1 CFLAGS_MONGOOSE += -DMG_IO_SIZE=128 -DMG_ENABLE_LINES=1 -DMG_ENABLE_PACKED_FS=1
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary PROG ?= example.exe # Use .exe suffix for the binary
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
+5 -4
View File
@@ -10,7 +10,7 @@
#include "mongoose.h" #include "mongoose.h"
static const char *s_backend_url = static const char *s_backend_url =
#if MG_ENABLE_MBEDTLS || MG_ENABLE_OPENSSL #if MG_TLS
"https://cesanta.com"; "https://cesanta.com";
#else #else
"http://info.cern.ch"; "http://info.cern.ch";
@@ -37,7 +37,7 @@ static void forward_request(struct mg_http_message *hm,
} }
static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_connection *c2 = fn_data; struct mg_connection *c2 = (struct mg_connection *) fn_data;
if (ev == MG_EV_READ) { if (ev == MG_EV_READ) {
// All incoming data from the backend, forward to the client // All incoming data from the backend, forward to the client
if (c2 != NULL) mg_send(c2, c->recv.buf, c->recv.len); if (c2 != NULL) mg_send(c2, c->recv.buf, c->recv.len);
@@ -59,12 +59,13 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
mg_error(c, "Cannot create backend connection"); mg_error(c, "Cannot create backend connection");
} else { } else {
if (mg_url_is_ssl(s_backend_url)) { if (mg_url_is_ssl(s_backend_url)) {
struct mg_tls_opts opts = {.ca = "ca.pem"}; struct mg_tls_opts opts = {.ca = mg_unpacked("/certs/ca.pem"),
.name = mg_url_host(s_backend_url)};
mg_tls_init(c2, &opts); mg_tls_init(c2, &opts);
} }
c->fn_data = c2; c->fn_data = c2;
forward_request(hm, c2); forward_request(hm, c2);
c->is_resp = 0; // process further msgs in keep-alive connection c->is_resp = 0; // process further msgs in keep-alive connection
c2->is_hexdumping = 1; c2->is_hexdumping = 1;
} }
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
+1
View File
@@ -0,0 +1 @@
../http-client/packed_fs.c
+2 -2
View File
@@ -1,11 +1,11 @@
PROG ?= example # Program we are building PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files SOURCES = main.c mongoose.c packed_fs.c # Source code files, packed_fs.c contains ca.pem, which contains CA certs for TLS
CFLAGS = -W -Wall -Wextra -g -I. # Build options CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options # Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_PACKED_FS=1
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary PROG ?= example.exe # Use .exe suffix for the binary
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
+40 -29
View File
@@ -1,14 +1,12 @@
// Copyright (c) 2021 Cesanta Software Limited // Copyright (c) 2021 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// Example HTTP client. // Example streaming HTTP client.
// Instead of buffering the whole HTTP response in memory and wait for the // The default HTTP handler waits until the whole HTTP message is buffered in
// MG_EV_HTTP_MSG event, this client catches MG_EV_HTTP_CHUNK events and // c->recv IO buffer. It can be large. This example shows how to receive
// prints them. This is useful to download large content without spooling // a potentially large response in chunks by handling the MG_EV_READ events.
// everything to memory, or catch chunked content.
// //
// You can change `s_url` from the command line by executing: ./example YOUR_URL // You can change `s_url` from the command line by executing: ./example YOUR_URL
//
// To enable SSL/TLS, see https://mongoose.ws/tutorials/tls/#how-to-build // To enable SSL/TLS, see https://mongoose.ws/tutorials/tls/#how-to-build
#include "mongoose.h" #include "mongoose.h"
@@ -21,13 +19,11 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_CONNECT) { if (ev == MG_EV_CONNECT) {
// Connected to server. Extract host name from URL // Connected to server. Extract host name from URL
struct mg_str host = mg_url_host(s_url); struct mg_str host = mg_url_host(s_url);
// If s_url is https://, tell client connection to use TLS
if (mg_url_is_ssl(s_url)) { if (mg_url_is_ssl(s_url)) {
struct mg_tls_opts opts = {.ca = "ca.pem", .srvname = host}; struct mg_tls_opts opts = {.ca = mg_unpacked("/certs/ca.pem"),
.name = host};
mg_tls_init(c, &opts); mg_tls_init(c, &opts);
} }
// Send request // Send request
mg_printf(c, mg_printf(c,
"GET %s HTTP/1.1\r\n" "GET %s HTTP/1.1\r\n"
@@ -36,33 +32,48 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
"Host: %.*s\r\n" "Host: %.*s\r\n"
"\r\n", "\r\n",
mg_url_uri(s_url), (int) host.len, host.ptr); mg_url_uri(s_url), (int) host.len, host.ptr);
} else if (ev == MG_EV_HTTP_CHUNK) { } else if (ev == MG_EV_READ) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; // Parse the incoming data ourselves. If we can parse the request,
fwrite(hm->chunk.ptr, 1, hm->chunk.len, stdout); // store two size_t variables in the c->data: expected len and recv len.
// fprintf(stderr, "c %u\n", (unsigned) hm->chunk.len); size_t *data = (size_t *) c->data;
mg_http_delete_chunk(c, hm); if (data[0]) { // Already parsed, simply print received data
if (hm->chunk.len == 0) *(bool *) fn_data = true; // Last chunk data[1] += c->recv.len;
} else if (ev == MG_EV_HTTP_MSG) { fwrite(c->recv.buf, 1, c->recv.len, stdout);
// Response is received. Print it c->recv.len = 0; // And cleanup the receive buffer. Streming!
struct mg_http_message *hm = (struct mg_http_message *) ev_data; if (data[1] >= data[0]) * (bool *) fn_data = true;
fwrite(hm->body.ptr, 1, hm->body.len, stdout); } else {
c->is_closing = 1; // Tell mongoose to close this connection struct mg_http_message hm;
*(bool *) fn_data = true; // Tell event loop to stop int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
if (n < 0) mg_error(c, "Bad response");
if (n > 0) {
fwrite(c->recv.buf + n, 1, c->recv.len - n, stdout); // Print body
data[0] = n + hm.body.len;
data[1] = c->recv.len;
c->recv.len = 0; // Cleanup receive buffer
if (data[1] >= data[0]) * (bool *) fn_data = true;
}
}
} else if (ev == MG_EV_CLOSE) {
*(bool *) fn_data = true; // tell event loop to stop
} else if (ev == MG_EV_ERROR) { } else if (ev == MG_EV_ERROR) {
*(bool *) fn_data = true; // Error, tell event loop to stop *(bool *) fn_data = true; // Error, tell event loop to stop
} }
(void) ev_data;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
struct mg_mgr mgr; // Event manager struct mg_mgr mgr; // Event manager
bool done = false; // Event handler flips it to true bool done = false; // Event handler flips it to true
const char *log_level = getenv("V"); // Log level const char *log_level = getenv("V"); // Log level
if (log_level == NULL) log_level = "3"; // If not set, set to DEBUG
mg_log_set(atoi(log_level)); // Set to 0 to disable debug log mg_mgr_init(&mgr); // Initialise event manager
if (argc > 1) s_url = argv[1]; // Use URL from command line if (log_level == NULL) log_level = "2"; // If not set, set to DEBUG
mg_mgr_init(&mgr); // Initialise event manager mg_log_set(atoi(log_level)); // Set to 0 to disable debug log
if (argc > 1) s_url = argv[1]; // Use URL from command line
mg_http_connect(&mgr, s_url, fn, &done); // Create client connection mg_http_connect(&mgr, s_url, fn, &done); // Create client connection
while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr); // Free resources mg_mgr_free(&mgr); // Free resources
return 0; return 0;
} }
+1
View File
@@ -0,0 +1 @@
../http-client/packed_fs.c
@@ -0,0 +1,53 @@
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include
CFLAGS += -D__SAME54P20A__ -Icmsis_sam/include
CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=softfp
LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = main.c syscalls.c startup.c
# Mongoose-specific source code files and build options. See https://mongoose.ws/documentation/#build-options
SOURCES += mongoose.c net.c packed_fs.c
CFLAGS += -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_CUSTOM_MILLIS=1 -DMG_ENABLE_LINES=1
CFLAGS += -DMG_ENABLE_DRIVER_SAME54=1 -DMG_ENABLE_CUSTOM_RANDOM=1 -DMG_ENABLE_PACKED_FS=1 $(CFLAGS_EXTRA)
# Example specific build options. See README.md
CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\"
ifeq ($(OS),Windows_NT)
RM = cmd /C del /Q /F
else
RM = rm -rf
endif
build: firmware.bin
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
firmware.elf: cmsis_core cmsis_sam hal.h link.ld Makefile $(SOURCES)
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@
flash: firmware.bin
bossac -p /dev/cu.usb* -w -v -b $<
cmsis_core:
git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
cmsis_sam:
curl -sL https://packs.download.microchip.com/Microchip.SAME54_DFP.3.8.234.pack -o $@.zip
mkdir $@ && cd $@ && unzip ../$@.zip
# Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL
DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/9
update: firmware.bin
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$<
test update: CFLAGS += -DUART_DEBUG=USART1
test: update
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt
grep 'READY, IP:' /tmp/output.txt # Check for network init
clean:
$(RM) firmware.* cmsis_core cmsis_sam *.zip
@@ -0,0 +1,219 @@
// Copyright (c) 2022 Cesanta Software Limited
// SPDX-License-Identifier: MIT
//
// https://ww1.microchip.com/downloads/aemDocuments/documents/MCU32/ProductDocuments/DataSheets/SAM-D5x-E5x-Family-Data-Sheet-DS60001507.pdf
// https://ww1.microchip.com/downloads/en/DeviceDoc/70005321A.pdf
#ifndef LED_PIN
#define LED_PIN PIN('C', 18)
#endif
#ifndef BUTTON_PIN
#define BUTTON_PIN PIN('B', 31)
#endif
#ifndef UART_DEBUG
#define UART_DEBUG USART1
#endif
#pragma once
#include <sam.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define BIT(x) (1UL << (x))
#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK)
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
static inline void spin(volatile uint32_t count) {
while (count--) (void) 0;
}
static inline uint32_t clock_sys_freq(void) {
return 48000000U;
}
enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG };
enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN };
enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE };
enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN };
#define GPIO(N) ((port_group_registers_t *) (PORT_BASE_ADDRESS + 0x80 * (N)))
typedef port_group_registers_t GPIO_TypeDef;
static inline GPIO_TypeDef *gpio_bank(uint16_t pin) {
return GPIO(PINBANK(pin));
}
static inline void gpio_toggle(uint16_t pin) {
gpio_bank(pin)->PORT_OUTTGL = BIT(PINNO(pin));
}
static inline bool gpio_read(uint16_t pin) {
return gpio_bank(pin)->PORT_IN & BIT(PINNO(pin));
}
static inline void gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = gpio_bank(pin);
if (val) {
gpio->PORT_OUTSET = BIT(PINNO(pin));
} else {
gpio->PORT_OUTCLR = BIT(PINNO(pin));
}
}
static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
uint8_t speed, uint8_t pull, uint8_t af) {
(void) type, (void) speed, (void) pull, (void) af;
GPIO_TypeDef *gpio = gpio_bank(pin);
uint32_t mask = BIT(PINNO(pin));
MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_PORT_Msk;
if (mode == GPIO_MODE_INPUT) {
gpio->PORT_DIRCLR = mask;
} else {
gpio->PORT_DIRSET = mask;
}
}
static inline void gpio_input(uint16_t pin) {
gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_NONE, 0);
}
static inline void gpio_output(uint16_t pin) {
gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_NONE, 0);
}
typedef sercom_usart_int_registers_t USART_TypeDef;
#define USART1 ((USART_TypeDef *) SERCOM0_BASE_ADDRESS)
#define USART2 ((USART_TypeDef *) SERCOM1_BASE_ADDRESS)
#define USART3 ((USART_TypeDef *) SERCOM2_BASE_ADDRESS)
static inline bool uart_init(USART_TypeDef *uart, unsigned long baud) {
uint16_t rx = 0, tx = 0; // Pins
uint8_t rx_mux = 0, tx_mux = 0;
if (uart == USART1) {
MCLK_REGS->MCLK_APBAMASK |= MCLK_APBAMASK_SERCOM0_Msk;
GCLK_REGS->GCLK_PCHCTRL[SERCOM0_GCLK_ID_CORE] =
GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN_Msk;
GCLK_REGS->GCLK_PCHCTRL[SERCOM0_GCLK_ID_SLOW] =
GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN_Msk;
tx = PIN('A', 4), rx = PIN('A', 5);
rx_mux = MUX_PA05D_SERCOM0_PAD1, tx_mux = MUX_PA04D_SERCOM0_PAD0;
} else if (uart == USART2) {
MCLK_REGS->MCLK_APBAMASK |= MCLK_APBAMASK_SERCOM1_Msk;
tx = PIN('C', 27), rx = PIN('C', 28);
} else if (uart == USART3) {
MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_SERCOM2_Msk;
tx = PIN('A', 9), rx = PIN('A', 8);
} else {
return false;
}
gpio_bank(rx)->PORT_WRCONFIG =
PORT_WRCONFIG_PMUX(rx_mux) | PORT_WRCONFIG_WRPMUX(1) |
PORT_WRCONFIG_PMUXEN(1) | PORT_WRCONFIG_WRPINCFG(1) | BIT(PINNO(rx));
gpio_bank(tx)->PORT_WRCONFIG =
PORT_WRCONFIG_PMUX(tx_mux) | PORT_WRCONFIG_WRPMUX(1) |
PORT_WRCONFIG_PMUXEN(1) | PORT_WRCONFIG_WRPINCFG(1) | BIT(PINNO(tx));
uart->SERCOM_CTRLA = SERCOM_USART_INT_CTRLA_DORD(1) |
SERCOM_USART_INT_CTRLA_MODE(1 /* INT_CLK */) |
SERCOM_USART_INT_CTRLA_RXPO(1 /* PAD1 */) |
SERCOM_USART_INT_CTRLA_TXPO(0 /* PAD0 */) |
SERCOM_USART_INT_CTRLA_SAMPR(1);
uart->SERCOM_BAUD = (uint16_t) (clock_sys_freq() / (16 * baud));
uart->SERCOM_CTRLB = SERCOM_USART_INT_CTRLB_RXEN(1) |
SERCOM_USART_INT_CTRLB_TXEN(1) |
SERCOM_USART_INT_CTRLB_CHSIZE(0);
while (uart->SERCOM_SYNCBUSY & SERCOM_USART_INT_SYNCBUSY_CTRLB_Msk) spin(1);
uart->SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_ENABLE(1);
while (uart->SERCOM_SYNCBUSY & SERCOM_USART_INT_SYNCBUSY_ENABLE_Msk) spin(1);
return true;
}
static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
while (!(uart->SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_DRE_Msk)) spin(1);
uart->SERCOM_DATA = byte;
}
static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++);
}
static inline bool uart_read_ready(USART_TypeDef *uart) {
return (uart->SERCOM_INTFLAG & SERCOM_USART_EXT_INTFLAG_RXC_Msk);
}
static inline uint8_t uart_read_byte(USART_TypeDef *uart) {
return (uint8_t) (uart->SERCOM_DATA & 255U);
}
static inline void rng_init(void) {
MCLK_REGS->MCLK_APBCMASK |= MCLK_APBCMASK_TRNG_Msk;
TRNG_REGS->TRNG_CTRLA = TRNG_CTRLA_ENABLE_Msk;
}
static inline uint32_t rng_read(void) {
while ((TRNG_REGS->TRNG_INTFLAG & TRNG_INTFLAG_DATARDY_Msk) == 0) spin(1);
return TRNG_REGS->TRNG_DATA;
}
#define UID_BASE_W0 0x008061FC // Word 0 location of the 128-bit chip ID
#define UID_BASE_W1_3 0x00806010 // Words 1-3 location of the 128-bit chip ID
#define UUID(n) ((n >= 0 && n <= 3) ? \
(((uint8_t *) UID_BASE_W0)[n]) : \
(((uint8_t *) UID_BASE_W1_3)[n - 4]))
#define GENERATE_LOCALLY_ADMINISTERED_MAC() \
{ \
2, UUID(0) ^ UUID(1) ^ UUID(2), UUID(3) ^ UUID(4) ^ UUID(5), \
UUID(6) ^ UUID(7) ^ UUID(8), UUID(9) ^ UUID(10) ^ UUID(11), \
UUID(12) ^ UUID(13) ^ UUID(14) ^ UUID(15) \
}
static inline bool timer_expired(volatile uint64_t *t, uint64_t prd,
uint64_t now) {
if (now + prd < *t) *t = 0; // Time wrapped? Reset timer
if (*t == 0) *t = now + prd; // Firt poll? Set expiration
if (*t > now) return false; // Not expired yet, return
*t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time
return true; // Expired, return true
}
static inline void clock_init(void) {
SCB->CPACR |= (15U << 20); // Enable FPU
SysTick_Config(clock_sys_freq() / 1000); // Sys tick every 1ms
}
static inline void gpio_set_irq_handler(uint16_t pin, void (*fn)(void *),
void *arg) {
(void) pin, (void) fn, (void) arg;
}
static inline void ethernet_init(void) {
uint16_t pins[] = {PIN('A', 12), PIN('A', 13), PIN('A', 14), PIN('A', 15),
PIN('A', 17), PIN('A', 18), PIN('A', 19), PIN('C', 11),
PIN('C', 12), PIN('C', 20)};
uint32_t af[] = {MUX_PA12L_GMAC_GRX1, MUX_PA13L_GMAC_GRX0,
MUX_PA14L_GMAC_GTXCK, MUX_PA15L_GMAC_GRXER,
MUX_PA17L_GMAC_GTXEN, MUX_PA18L_GMAC_GTX0,
MUX_PA19L_GMAC_GTX1, MUX_PC11L_GMAC_GMDC,
MUX_PC12L_GMAC_GMDIO, MUX_PC20L_GMAC_GRXDV};
MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_PORT_Msk;
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
int bank = PINBANK(pins[i]), no = PINNO(pins[i]);
PORT_REGS->GROUP[bank].PORT_PINCFG[no] |= PORT_PINCFG_PMUXEN_Msk;
volatile uint8_t *m = &PORT_REGS->GROUP[bank].PORT_PMUX[no / 2], v = m[0];
if (no & 1) {
m[0] = (uint8_t) ((v & ~0xf0) | PORT_PMUX_PMUXO(af[i]));
} else {
m[0] = (uint8_t) ((v & ~0x0f) | PORT_PMUX_PMUXE(af[i]));
}
}
PORT_REGS->GROUP[0].PORT_PINCFG[17] |= PORT_PINCFG_DRVSTR_Msk;
PORT_REGS->GROUP[0].PORT_PINCFG[18] |= PORT_PINCFG_DRVSTR_Msk;
PORT_REGS->GROUP[0].PORT_PINCFG[19] |= PORT_PINCFG_DRVSTR_Msk;
// Reset PHY
uint16_t phy_pin = PIN('C', 21);
gpio_output(phy_pin);
gpio_write(phy_pin, false);
spin(999);
gpio_write(phy_pin, true);
spin(999);
}
@@ -0,0 +1,25 @@
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x00000000, LENGTH = 1024k
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
}
_estack = ORIGIN(sram) + LENGTH(sram);
SECTIONS {
.vectors : { FILL(256) KEEP(*(.vectors)) } > flash
.text : { *(.text*) } > flash
.rodata : { *(.rodata*) } > flash
.data : {
_sdata = .;
*(.first_data)
*(.data SORT(.data.*))
_edata = .;
} > sram AT > flash
_sidata = LOADADDR(.data);
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram
. = ALIGN(8);
_end = .;
}
@@ -0,0 +1,78 @@
// SPDX-FileCopyrightText: 2022-2023 Cesanta Software Limited
// SPDX-License-Identifier: MIT
#include "hal.h"
#include "mongoose.h"
#include "net.h"
#define BLINK_PERIOD_MS 500 // LED blinking period in millis
#define LOG_PERIOD_MS 1000 // Info log period in millis
void SystemInit(void) { // Called automatically by startup code
clock_init();
rng_init();
}
static volatile uint64_t s_ticks; // Milliseconds since boot
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
s_ticks++;
}
uint64_t mg_millis(void) { // Let Mongoose use our uptime function
return s_ticks; // Return number of milliseconds since boot
}
void mg_random(void *buf, size_t len) { // Use on-board RNG
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = rng_read();
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
}
}
static void timer_fn(void *arg) {
struct mg_tcpip_if *ifp = arg; // show network stats
const char *names[] = {"down", "up", "req", "ready"};
MG_INFO(("Ethernet: %s, IP: %M, rx:%u, tx:%u, dr:%u, er:%u",
names[ifp->state], mg_print_ip4, &ifp->ip, ifp->nrecv, ifp->nsent,
ifp->ndrop, ifp->nerr));
}
int main(void) {
gpio_input(BUTTON_PIN);
gpio_output(LED_PIN);
uart_init(UART_DEBUG, 115200);
ethernet_init();
MG_INFO(("Starting, CPU freq %g MHz", (double) clock_sys_freq() / 1000000));
struct mg_mgr mgr; // Initialise
mg_mgr_init(&mgr); // Mongoose event manager
mg_log_set(MG_LL_DEBUG); // Set log level
// Initialise Mongoose network stack
struct mg_tcpip_driver_same54_data driver_data = {.mdc_cr = 5};
struct mg_tcpip_if mif = {.mac = GENERATE_LOCALLY_ADMINISTERED_MAC()/*{2, 3, 4, 5, 6, 7}*/,
// Uncomment below for static configuration:
// .ip = mg_htonl(MG_U32(192, 168, 0, 223)),
// .mask = mg_htonl(MG_U32(255, 255, 255, 0)),
// .gw = mg_htonl(MG_U32(192, 168, 0, 1)),
.driver = &mg_tcpip_driver_same54,
.driver_data = &driver_data};
mg_tcpip_init(&mgr, &mif);
mg_timer_add(&mgr, LOG_PERIOD_MS, MG_TIMER_REPEAT, timer_fn, &mif);
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
MG_INFO(("Initialising application..."));
web_init(&mgr);
MG_INFO(("Starting event loop"));
for (;;) {
mg_mgr_poll(&mgr, 0);
}
return 0;
}
@@ -0,0 +1 @@
../../../../mongoose.c
@@ -0,0 +1 @@
../../../../mongoose.h
@@ -0,0 +1 @@
../../../device-dashboard/net.c
@@ -0,0 +1 @@
../../../device-dashboard/net.h
@@ -0,0 +1 @@
../../../device-dashboard/packed_fs.c
@@ -0,0 +1,332 @@
// SPDX-FileCopyrightText: 2022-2023 Cesanta Software Limited
// SPDX-License-Identifier: MIT
#include "hal.h"
void Reset_Handler(void); // Defined below
void Dummy_Handler(void); // Defined below
void SysTick_Handler(void); // Defined in main.c
void SystemInit(void); // Defined in main.c, called by reset handler
void _estack(void); // Defined in link.ld
#define WEAK_ALIAS __attribute__((weak, alias("Default_Handler")))
WEAK_ALIAS void NMI_Handler(void);
WEAK_ALIAS void HardFault_Handler(void);
WEAK_ALIAS void MemoryManagement_Handler(void);
WEAK_ALIAS void BusFault_Handler(void);
WEAK_ALIAS void UsageFault_Handler(void);
WEAK_ALIAS void SVCall_Handler(void);
WEAK_ALIAS void DebugMonitor_Handler(void);
WEAK_ALIAS void PendSV_Handler(void);
WEAK_ALIAS void SysTick_Handler(void);
WEAK_ALIAS void PM_Handler(void);
WEAK_ALIAS void MCLK_Handler(void);
WEAK_ALIAS void OSCCTRL_XOSC0_Handler(void);
WEAK_ALIAS void OSCCTRL_XOSC1_Handler(void);
WEAK_ALIAS void OSCCTRL_DFLL_Handler(void);
WEAK_ALIAS void OSCCTRL_DPLL0_Handler(void);
WEAK_ALIAS void OSCCTRL_DPLL1_Handler(void);
WEAK_ALIAS void OSC32KCTRL_Handler(void);
WEAK_ALIAS void SUPC_OTHER_Handler(void);
WEAK_ALIAS void SUPC_BODDET_Handler(void);
WEAK_ALIAS void WDT_Handler(void);
WEAK_ALIAS void RTC_Handler(void);
WEAK_ALIAS void EIC_EXTINT_0_Handler(void);
WEAK_ALIAS void EIC_EXTINT_1_Handler(void);
WEAK_ALIAS void EIC_EXTINT_2_Handler(void);
WEAK_ALIAS void EIC_EXTINT_3_Handler(void);
WEAK_ALIAS void EIC_EXTINT_4_Handler(void);
WEAK_ALIAS void EIC_EXTINT_5_Handler(void);
WEAK_ALIAS void EIC_EXTINT_6_Handler(void);
WEAK_ALIAS void EIC_EXTINT_7_Handler(void);
WEAK_ALIAS void EIC_EXTINT_8_Handler(void);
WEAK_ALIAS void EIC_EXTINT_9_Handler(void);
WEAK_ALIAS void EIC_EXTINT_10_Handler(void);
WEAK_ALIAS void EIC_EXTINT_11_Handler(void);
WEAK_ALIAS void EIC_EXTINT_12_Handler(void);
WEAK_ALIAS void EIC_EXTINT_13_Handler(void);
WEAK_ALIAS void EIC_EXTINT_14_Handler(void);
WEAK_ALIAS void EIC_EXTINT_15_Handler(void);
WEAK_ALIAS void FREQM_Handler(void);
WEAK_ALIAS void NVMCTRL_0_Handler(void);
WEAK_ALIAS void NVMCTRL_1_Handler(void);
WEAK_ALIAS void DMAC_0_Handler(void);
WEAK_ALIAS void DMAC_1_Handler(void);
WEAK_ALIAS void DMAC_2_Handler(void);
WEAK_ALIAS void DMAC_3_Handler(void);
WEAK_ALIAS void DMAC_OTHER_Handler(void);
WEAK_ALIAS void EVSYS_0_Handler(void);
WEAK_ALIAS void EVSYS_1_Handler(void);
WEAK_ALIAS void EVSYS_2_Handler(void);
WEAK_ALIAS void EVSYS_3_Handler(void);
WEAK_ALIAS void EVSYS_OTHER_Handler(void);
WEAK_ALIAS void PAC_Handler(void);
WEAK_ALIAS void RAMECC_Handler(void);
WEAK_ALIAS void SERCOM0_0_Handler(void);
WEAK_ALIAS void SERCOM0_1_Handler(void);
WEAK_ALIAS void SERCOM0_2_Handler(void);
WEAK_ALIAS void SERCOM0_OTHER_Handler(void);
WEAK_ALIAS void SERCOM1_0_Handler(void);
WEAK_ALIAS void SERCOM1_1_Handler(void);
WEAK_ALIAS void SERCOM1_2_Handler(void);
WEAK_ALIAS void SERCOM1_OTHER_Handler(void);
WEAK_ALIAS void SERCOM2_0_Handler(void);
WEAK_ALIAS void SERCOM2_1_Handler(void);
WEAK_ALIAS void SERCOM2_2_Handler(void);
WEAK_ALIAS void SERCOM2_OTHER_Handler(void);
WEAK_ALIAS void SERCOM3_0_Handler(void);
WEAK_ALIAS void SERCOM3_1_Handler(void);
WEAK_ALIAS void SERCOM3_2_Handler(void);
WEAK_ALIAS void SERCOM3_OTHER_Handler(void);
WEAK_ALIAS void SERCOM4_0_Handler(void);
WEAK_ALIAS void SERCOM4_1_Handler(void);
WEAK_ALIAS void SERCOM4_2_Handler(void);
WEAK_ALIAS void SERCOM4_OTHER_Handler(void);
WEAK_ALIAS void SERCOM5_0_Handler(void);
WEAK_ALIAS void SERCOM5_1_Handler(void);
WEAK_ALIAS void SERCOM5_2_Handler(void);
WEAK_ALIAS void SERCOM5_OTHER_Handler(void);
WEAK_ALIAS void SERCOM6_0_Handler(void);
WEAK_ALIAS void SERCOM6_1_Handler(void);
WEAK_ALIAS void SERCOM6_2_Handler(void);
WEAK_ALIAS void SERCOM6_OTHER_Handler(void);
WEAK_ALIAS void SERCOM7_0_Handler(void);
WEAK_ALIAS void SERCOM7_1_Handler(void);
WEAK_ALIAS void SERCOM7_2_Handler(void);
WEAK_ALIAS void SERCOM7_OTHER_Handler(void);
WEAK_ALIAS void CAN0_Handler(void);
WEAK_ALIAS void CAN1_Handler(void);
WEAK_ALIAS void USB_OTHER_Handler(void);
WEAK_ALIAS void USB_SOF_HSOF_Handler(void);
WEAK_ALIAS void USB_TRCPT0_Handler(void);
WEAK_ALIAS void USB_TRCPT1_Handler(void);
WEAK_ALIAS void GMAC_Handler(void);
WEAK_ALIAS void TCC0_OTHER_Handler(void);
WEAK_ALIAS void TCC0_MC0_Handler(void);
WEAK_ALIAS void TCC0_MC1_Handler(void);
WEAK_ALIAS void TCC0_MC2_Handler(void);
WEAK_ALIAS void TCC0_MC3_Handler(void);
WEAK_ALIAS void TCC0_MC4_Handler(void);
WEAK_ALIAS void TCC0_MC5_Handler(void);
WEAK_ALIAS void TCC1_OTHER_Handler(void);
WEAK_ALIAS void TCC1_MC0_Handler(void);
WEAK_ALIAS void TCC1_MC1_Handler(void);
WEAK_ALIAS void TCC1_MC2_Handler(void);
WEAK_ALIAS void TCC1_MC3_Handler(void);
WEAK_ALIAS void TCC2_OTHER_Handler(void);
WEAK_ALIAS void TCC2_MC0_Handler(void);
WEAK_ALIAS void TCC2_MC1_Handler(void);
WEAK_ALIAS void TCC2_MC2_Handler(void);
WEAK_ALIAS void TCC3_OTHER_Handler(void);
WEAK_ALIAS void TCC3_MC0_Handler(void);
WEAK_ALIAS void TCC3_MC1_Handler(void);
WEAK_ALIAS void TCC4_OTHER_Handler(void);
WEAK_ALIAS void TCC4_MC0_Handler(void);
WEAK_ALIAS void TCC4_MC1_Handler(void);
WEAK_ALIAS void TC0_Handler(void);
WEAK_ALIAS void TC1_Handler(void);
WEAK_ALIAS void TC2_Handler(void);
WEAK_ALIAS void TC3_Handler(void);
WEAK_ALIAS void TC4_Handler(void);
WEAK_ALIAS void TC5_Handler(void);
WEAK_ALIAS void TC6_Handler(void);
WEAK_ALIAS void TC7_Handler(void);
WEAK_ALIAS void PDEC_OTHER_Handler(void);
WEAK_ALIAS void PDEC_MC0_Handler(void);
WEAK_ALIAS void PDEC_MC1_Handler(void);
WEAK_ALIAS void ADC0_OTHER_Handler(void);
WEAK_ALIAS void ADC0_RESRDY_Handler(void);
WEAK_ALIAS void ADC1_OTHER_Handler(void);
WEAK_ALIAS void ADC1_RESRDY_Handler(void);
WEAK_ALIAS void AC_Handler(void);
WEAK_ALIAS void DAC_OTHER_Handler(void);
WEAK_ALIAS void DAC_EMPTY_0_Handler(void);
WEAK_ALIAS void DAC_EMPTY_1_Handler(void);
WEAK_ALIAS void DAC_RESRDY_0_Handler(void);
WEAK_ALIAS void DAC_RESRDY_1_Handler(void);
WEAK_ALIAS void I2S_Handler(void);
WEAK_ALIAS void PCC_Handler(void);
WEAK_ALIAS void AES_Handler(void);
WEAK_ALIAS void TRNG_Handler(void);
WEAK_ALIAS void ICM_Handler(void);
WEAK_ALIAS void PUKCC_Handler(void);
WEAK_ALIAS void QSPI_Handler(void);
WEAK_ALIAS void SDHC0_Handler(void);
WEAK_ALIAS void SDHC1_Handler(void);
__attribute__((section(".vectors"))) void (*const tab[16 + 138])(void) = {
_estack,
Reset_Handler,
NMI_Handler,
HardFault_Handler,
MemoryManagement_Handler,
BusFault_Handler,
UsageFault_Handler,
NULL,
NULL,
NULL,
NULL,
SVCall_Handler,
DebugMonitor_Handler,
NULL,
PendSV_Handler,
SysTick_Handler,
PM_Handler, // 0 Power
MCLK_Handler, // 1 Main
OSCCTRL_XOSC0_Handler, // 2 Oscillators
OSCCTRL_XOSC1_Handler, // 3 Oscillators
OSCCTRL_DFLL_Handler, // 4 Oscillators
OSCCTRL_DPLL0_Handler, // 5 Oscillators
OSCCTRL_DPLL1_Handler, // 6 Oscillators
OSC32KCTRL_Handler, // 7 32kHz
SUPC_OTHER_Handler, // 8 Supply
SUPC_BODDET_Handler, // 9 Supply
WDT_Handler, // 10 Watchdog
RTC_Handler, // 11 Real-Time
EIC_EXTINT_0_Handler, // 12 External
EIC_EXTINT_1_Handler, // 13 External
EIC_EXTINT_2_Handler, // 14 External
EIC_EXTINT_3_Handler, // 15 External
EIC_EXTINT_4_Handler, // 16 External
EIC_EXTINT_5_Handler, // 17 External
EIC_EXTINT_6_Handler, // 18 External
EIC_EXTINT_7_Handler, // 19 External
EIC_EXTINT_8_Handler, // 20 External
EIC_EXTINT_9_Handler, // 21 External
EIC_EXTINT_10_Handler, // 22 External
EIC_EXTINT_11_Handler, // 23 External
EIC_EXTINT_12_Handler, // 24 External
EIC_EXTINT_13_Handler, // 25 External
EIC_EXTINT_14_Handler, // 26 External
EIC_EXTINT_15_Handler, // 27 External
FREQM_Handler, // 28 Frequency
NVMCTRL_0_Handler, // 29 Non-Volatile
NVMCTRL_1_Handler, // 30 Non-Volatile
DMAC_0_Handler, // 31 Direct
DMAC_1_Handler, // 32 Direct
DMAC_2_Handler, // 33 Direct
DMAC_3_Handler, // 34 Direct
DMAC_OTHER_Handler, // 35 Direct
EVSYS_0_Handler, // 36 Event
EVSYS_1_Handler, // 37 Event
EVSYS_2_Handler, // 38 Event
EVSYS_3_Handler, // 39 Event
EVSYS_OTHER_Handler, // 40 Event
PAC_Handler, // 41 Peripheral
0, // 42 Reserved
0, // 43 Reserved
0, // 44 Reserved
RAMECC_Handler, // 45 RAM
SERCOM0_0_Handler, // 46 Serial
SERCOM0_1_Handler, // 47 Serial
SERCOM0_2_Handler, // 48 Serial
SERCOM0_OTHER_Handler, // 49 Serial
SERCOM1_0_Handler, // 50 Serial
SERCOM1_1_Handler, // 51 Serial
SERCOM1_2_Handler, // 52 Serial
SERCOM1_OTHER_Handler, // 53 Serial
SERCOM2_0_Handler, // 54 Serial
SERCOM2_1_Handler, // 55 Serial
SERCOM2_2_Handler, // 56 Serial
SERCOM2_OTHER_Handler, // 57 Serial
SERCOM3_0_Handler, // 58 Serial
SERCOM3_1_Handler, // 59 Serial
SERCOM3_2_Handler, // 60 Serial
SERCOM3_OTHER_Handler, // 61 Serial
SERCOM4_0_Handler, // 62 Serial
SERCOM4_1_Handler, // 63 Serial
SERCOM4_2_Handler, // 64 Serial
SERCOM4_OTHER_Handler, // 65 Serial
SERCOM5_0_Handler, // 66 Serial
SERCOM5_1_Handler, // 67 Serial
SERCOM5_2_Handler, // 68 Serial
SERCOM5_OTHER_Handler, // 69 Serial
SERCOM6_0_Handler, // 70 Serial
SERCOM6_1_Handler, // 71 Serial
SERCOM6_2_Handler, // 72 Serial
SERCOM6_OTHER_Handler, // 73 Serial
SERCOM7_0_Handler, // 74 Serial
SERCOM7_1_Handler, // 75 Serial
SERCOM7_2_Handler, // 76 Serial
SERCOM7_OTHER_Handler, // 77 Serial
CAN0_Handler, // 78 Control
CAN1_Handler, // 79 Control
USB_OTHER_Handler, // 80 Universal
USB_SOF_HSOF_Handler, // 81 Universal
USB_TRCPT0_Handler, // 82 Universal
USB_TRCPT1_Handler, // 83 Universal
GMAC_Handler, // 84 Ethernet
TCC0_OTHER_Handler, // 85 Timer
TCC0_MC0_Handler, // 86 Timer
TCC0_MC1_Handler, // 87 Timer
TCC0_MC2_Handler, // 88 Timer
TCC0_MC3_Handler, // 89 Timer
TCC0_MC4_Handler, // 90 Timer
TCC0_MC5_Handler, // 91 Timer
TCC1_OTHER_Handler, // 92 Timer
TCC1_MC0_Handler, // 93 Timer
TCC1_MC1_Handler, // 94 Timer
TCC1_MC2_Handler, // 95 Timer
TCC1_MC3_Handler, // 96 Timer
TCC2_OTHER_Handler, // 97 Timer
TCC2_MC0_Handler, // 98 Timer
TCC2_MC1_Handler, // 99 Timer
TCC2_MC2_Handler, // 100 Timer
TCC3_OTHER_Handler, // 101 Timer
TCC3_MC0_Handler, // 102 Timer
TCC3_MC1_Handler, // 103 Timer
TCC4_OTHER_Handler, // 104 Timer
TCC4_MC0_Handler, // 105 Timer
TCC4_MC1_Handler, // 106 Timer
TC0_Handler, // 107 Basic
TC1_Handler, // 108 Basic
TC2_Handler, // 109 Basic
TC3_Handler, // 110 Basic
TC4_Handler, // 111 Basic
TC5_Handler, // 112 Basic
TC6_Handler, // 113 Basic
TC7_Handler, // 114 Basic
PDEC_OTHER_Handler, // 115 Quadrature
PDEC_MC0_Handler, // 116 Quadrature
PDEC_MC1_Handler, // 117 Quadrature
ADC0_OTHER_Handler, // 118 Analog
ADC0_RESRDY_Handler, // 119 Analog
ADC1_OTHER_Handler, // 120 Analog
ADC1_RESRDY_Handler, // 121 Analog
AC_Handler, // 122 Analog
DAC_OTHER_Handler, // 123 Digital-to-Analog
DAC_EMPTY_0_Handler, // 124 Digital-to-Analog
DAC_EMPTY_1_Handler, // 125 Digital-to-Analog
DAC_RESRDY_0_Handler, // 126 Digital-to-Analog
DAC_RESRDY_1_Handler, // 127 Digital-to-Analog
I2S_Handler, // 128 Inter-IC
PCC_Handler, // 129 Parallel
AES_Handler, // 130 Advanced
TRNG_Handler, // 131 True
ICM_Handler, // 132 Integrity
PUKCC_Handler, // 133 PUblic-Key
QSPI_Handler, // 134 Quad
SDHC0_Handler, // 135 SD/MMC
SDHC1_Handler // 136 SD/MMC
};
__attribute__((naked, noreturn)) void Reset_Handler(void) {
// Clear BSS section, and copy data section from flash to RAM
extern long _sbss, _ebss, _sdata, _edata, _sidata;
for (long *src = &_sbss; src < &_ebss; src++) *src = 0;
for (long *src = &_sdata, *dst = &_sidata; src < &_edata;) *src++ = *dst++;
SCB->VTOR = (uint32_t) &tab;
SystemInit();
// Call main()
extern void main(void);
main();
for (;;) (void) 0; // Infinite loop
}
void Default_Handler(void) {
for (;;) (void) 0;
}
@@ -0,0 +1,89 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
#include <sys/stat.h>
#include "hal.h"
int _fstat(int fd, struct stat *st) {
if (fd < 0) return -1;
st->st_mode = S_IFCHR;
return 0;
}
void *_sbrk(int incr) {
extern char _end;
static unsigned char *heap = NULL;
unsigned char *prev_heap;
if (heap == NULL) heap = (unsigned char *) &_end;
prev_heap = heap;
heap += incr;
return prev_heap;
}
int _open(const char *path) {
(void) path;
return -1;
}
int _close(int fd) {
(void) fd;
return -1;
}
int _isatty(int fd) {
(void) fd;
return 1;
}
int _lseek(int fd, int ptr, int dir) {
(void) fd, (void) ptr, (void) dir;
return 0;
}
void _exit(int status) {
(void) status;
for (;;) asm volatile("BKPT #0");
}
void _kill(int pid, int sig) {
(void) pid, (void) sig;
}
int _getpid(void) {
return -1;
}
int _write(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len);
return -1;
}
int _read(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
int _link(const char *a, const char *b) {
(void) a, (void) b;
return -1;
}
int _unlink(const char *a) {
(void) a;
return -1;
}
int _stat(const char *path, struct stat *st) {
(void) path, (void) st;
return -1;
}
int mkdir(const char *path, mode_t mode) {
(void) path, (void) mode;
return -1;
}
void _init(void) {
}
@@ -0,0 +1,54 @@
CFLAGS = -W -Wall -Wextra -Wundef -Wshadow -Wdouble-promotion
CFLAGS += -Wformat-truncation -fno-common -Wconversion
CFLAGS += -g3 -Os -ffunction-sections -fdata-sections
CFLAGS += -I. -Icmsis_core/CMSIS/Core/Include
CFLAGS += -D__SAME54P20A__ -Icmsis_sam/include
CFLAGS += -mcpu=cortex-m4 -mthumb -mfloat-abi=softfp
LDFLAGS ?= -Tlink.ld -nostartfiles -nostdlib --specs nano.specs -lc -lgcc -Wl,--gc-sections -Wl,-Map=$@.map
SOURCES = main.c syscalls.c startup.c
# Mongoose-specific source code files and build options. See https://mongoose.ws/documentation/#build-options
SOURCES += mongoose.c
CFLAGS += -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB -DMG_ENABLE_CUSTOM_MILLIS=1 -DMG_ENABLE_LINES=1
CFLAGS += -DMG_ENABLE_DRIVER_SAME54=1 -DMG_ENABLE_CUSTOM_RANDOM=1 -DMG_ENABLE_PACKED_FS=1 $(CFLAGS_EXTRA)
# Example specific build options. See README.md
CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\"
ifeq ($(OS),Windows_NT)
RM = cmd /C del /Q /F
else
RM = rm -rf
endif
build: firmware.bin
firmware.bin: firmware.elf
arm-none-eabi-objcopy -O binary $< $@
firmware.elf: cmsis_core cmsis_sam hal.h link.ld Makefile $(SOURCES)
arm-none-eabi-gcc $(SOURCES) $(CFLAGS) $(CFLAGS_EXTRA) $(LDFLAGS) -o $@
flash: firmware.bin
bossac -p /dev/cu.usb* -w -v -b $<
cmsis_core:
git clone --depth 1 -b 5.9.0 https://github.com/ARM-software/CMSIS_5 $@
cmsis_sam:
curl -sL https://packs.download.microchip.com/Microchip.SAME54_DFP.3.8.234.pack -o $@.zip
mkdir $@ && cd $@ && unzip ../$@.zip
# Automated test via https://vcon.io/automated-firmware-tests/. Set VCON_API_KEY and update DEVICE_URL
DEVICE_URL ?= https://dash.vcon.io/api/v3/devices/9
update: firmware.bin
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/ota --data-binary @$<
test update: CFLAGS += -DUART_DEBUG=USART1
test: update
curl --fail-with-body -su :$(VCON_API_KEY) $(DEVICE_URL)/tx?t=5 | tee /tmp/output.txt
grep 'READY, IP:' /tmp/output.txt # Check for network init
grep 'RECEIVED hello' /tmp/output.txt # Check for MQTT success
clean:
$(RM) firmware.* cmsis_core cmsis_sam *.zip
@@ -0,0 +1,219 @@
// Copyright (c) 2022 Cesanta Software Limited
// SPDX-License-Identifier: MIT
//
// https://ww1.microchip.com/downloads/aemDocuments/documents/MCU32/ProductDocuments/DataSheets/SAM-D5x-E5x-Family-Data-Sheet-DS60001507.pdf
// https://ww1.microchip.com/downloads/en/DeviceDoc/70005321A.pdf
#ifndef LED_PIN
#define LED_PIN PIN('C', 18)
#endif
#ifndef BUTTON_PIN
#define BUTTON_PIN PIN('B', 31)
#endif
#ifndef UART_DEBUG
#define UART_DEBUG USART1
#endif
#pragma once
#include <sam.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#define BIT(x) (1UL << (x))
#define SETBITS(R, CLEARMASK, SETMASK) (R) = ((R) & ~(CLEARMASK)) | (SETMASK)
#define PIN(bank, num) ((((bank) - 'A') << 8) | (num))
#define PINNO(pin) (pin & 255)
#define PINBANK(pin) (pin >> 8)
static inline void spin(volatile uint32_t count) {
while (count--) (void) 0;
}
static inline uint32_t clock_sys_freq(void) {
return 48000000U;
}
enum { GPIO_MODE_INPUT, GPIO_MODE_OUTPUT, GPIO_MODE_AF, GPIO_MODE_ANALOG };
enum { GPIO_OTYPE_PUSH_PULL, GPIO_OTYPE_OPEN_DRAIN };
enum { GPIO_SPEED_LOW, GPIO_SPEED_MEDIUM, GPIO_SPEED_HIGH, GPIO_SPEED_INSANE };
enum { GPIO_PULL_NONE, GPIO_PULL_UP, GPIO_PULL_DOWN };
#define GPIO(N) ((port_group_registers_t *) (PORT_BASE_ADDRESS + 0x80 * (N)))
typedef port_group_registers_t GPIO_TypeDef;
static inline GPIO_TypeDef *gpio_bank(uint16_t pin) {
return GPIO(PINBANK(pin));
}
static inline void gpio_toggle(uint16_t pin) {
gpio_bank(pin)->PORT_OUTTGL = BIT(PINNO(pin));
}
static inline bool gpio_read(uint16_t pin) {
return gpio_bank(pin)->PORT_IN & BIT(PINNO(pin));
}
static inline void gpio_write(uint16_t pin, bool val) {
GPIO_TypeDef *gpio = gpio_bank(pin);
if (val) {
gpio->PORT_OUTSET = BIT(PINNO(pin));
} else {
gpio->PORT_OUTCLR = BIT(PINNO(pin));
}
}
static inline void gpio_init(uint16_t pin, uint8_t mode, uint8_t type,
uint8_t speed, uint8_t pull, uint8_t af) {
(void) type, (void) speed, (void) pull, (void) af;
GPIO_TypeDef *gpio = gpio_bank(pin);
uint32_t mask = BIT(PINNO(pin));
MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_PORT_Msk;
if (mode == GPIO_MODE_INPUT) {
gpio->PORT_DIRCLR = mask;
} else {
gpio->PORT_DIRSET = mask;
}
}
static inline void gpio_input(uint16_t pin) {
gpio_init(pin, GPIO_MODE_INPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_NONE, 0);
}
static inline void gpio_output(uint16_t pin) {
gpio_init(pin, GPIO_MODE_OUTPUT, GPIO_OTYPE_PUSH_PULL, GPIO_SPEED_HIGH,
GPIO_PULL_NONE, 0);
}
typedef sercom_usart_int_registers_t USART_TypeDef;
#define USART1 ((USART_TypeDef *) SERCOM0_BASE_ADDRESS)
#define USART2 ((USART_TypeDef *) SERCOM1_BASE_ADDRESS)
#define USART3 ((USART_TypeDef *) SERCOM2_BASE_ADDRESS)
static inline bool uart_init(USART_TypeDef *uart, unsigned long baud) {
uint16_t rx = 0, tx = 0; // Pins
uint8_t rx_mux = 0, tx_mux = 0;
if (uart == USART1) {
MCLK_REGS->MCLK_APBAMASK |= MCLK_APBAMASK_SERCOM0_Msk;
GCLK_REGS->GCLK_PCHCTRL[SERCOM0_GCLK_ID_CORE] =
GCLK_PCHCTRL_GEN_GCLK0 | GCLK_PCHCTRL_CHEN_Msk;
GCLK_REGS->GCLK_PCHCTRL[SERCOM0_GCLK_ID_SLOW] =
GCLK_PCHCTRL_GEN_GCLK3 | GCLK_PCHCTRL_CHEN_Msk;
tx = PIN('A', 4), rx = PIN('A', 5);
rx_mux = MUX_PA05D_SERCOM0_PAD1, tx_mux = MUX_PA04D_SERCOM0_PAD0;
} else if (uart == USART2) {
MCLK_REGS->MCLK_APBAMASK |= MCLK_APBAMASK_SERCOM1_Msk;
tx = PIN('C', 27), rx = PIN('C', 28);
} else if (uart == USART3) {
MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_SERCOM2_Msk;
tx = PIN('A', 9), rx = PIN('A', 8);
} else {
return false;
}
gpio_bank(rx)->PORT_WRCONFIG =
PORT_WRCONFIG_PMUX(rx_mux) | PORT_WRCONFIG_WRPMUX(1) |
PORT_WRCONFIG_PMUXEN(1) | PORT_WRCONFIG_WRPINCFG(1) | BIT(PINNO(rx));
gpio_bank(tx)->PORT_WRCONFIG =
PORT_WRCONFIG_PMUX(tx_mux) | PORT_WRCONFIG_WRPMUX(1) |
PORT_WRCONFIG_PMUXEN(1) | PORT_WRCONFIG_WRPINCFG(1) | BIT(PINNO(tx));
uart->SERCOM_CTRLA = SERCOM_USART_INT_CTRLA_DORD(1) |
SERCOM_USART_INT_CTRLA_MODE(1 /* INT_CLK */) |
SERCOM_USART_INT_CTRLA_RXPO(1 /* PAD1 */) |
SERCOM_USART_INT_CTRLA_TXPO(0 /* PAD0 */) |
SERCOM_USART_INT_CTRLA_SAMPR(1);
uart->SERCOM_BAUD = (uint16_t) (clock_sys_freq() / (16 * baud));
uart->SERCOM_CTRLB = SERCOM_USART_INT_CTRLB_RXEN(1) |
SERCOM_USART_INT_CTRLB_TXEN(1) |
SERCOM_USART_INT_CTRLB_CHSIZE(0);
while (uart->SERCOM_SYNCBUSY & SERCOM_USART_INT_SYNCBUSY_CTRLB_Msk) spin(1);
uart->SERCOM_CTRLA |= SERCOM_USART_INT_CTRLA_ENABLE(1);
while (uart->SERCOM_SYNCBUSY & SERCOM_USART_INT_SYNCBUSY_ENABLE_Msk) spin(1);
return true;
}
static inline void uart_write_byte(USART_TypeDef *uart, uint8_t byte) {
while (!(uart->SERCOM_INTFLAG & SERCOM_USART_INT_INTFLAG_DRE_Msk)) spin(1);
uart->SERCOM_DATA = byte;
}
static inline void uart_write_buf(USART_TypeDef *uart, char *buf, size_t len) {
while (len-- > 0) uart_write_byte(uart, *(uint8_t *) buf++);
}
static inline bool uart_read_ready(USART_TypeDef *uart) {
return (uart->SERCOM_INTFLAG & SERCOM_USART_EXT_INTFLAG_RXC_Msk);
}
static inline uint8_t uart_read_byte(USART_TypeDef *uart) {
return (uint8_t) (uart->SERCOM_DATA & 255U);
}
static inline void rng_init(void) {
MCLK_REGS->MCLK_APBCMASK |= MCLK_APBCMASK_TRNG_Msk;
TRNG_REGS->TRNG_CTRLA = TRNG_CTRLA_ENABLE_Msk;
}
static inline uint32_t rng_read(void) {
while ((TRNG_REGS->TRNG_INTFLAG & TRNG_INTFLAG_DATARDY_Msk) == 0) spin(1);
return TRNG_REGS->TRNG_DATA;
}
#define UID_BASE_W0 0x008061FC // Word 0 location of the 128-bit chip ID
#define UID_BASE_W1_3 0x00806010 // Words 1-3 location of the 128-bit chip ID
#define UUID(n) ((n >= 0 && n <= 3) ? \
(((uint8_t *) UID_BASE_W0)[n]) : \
(((uint8_t *) UID_BASE_W1_3)[n - 4]))
#define GENERATE_LOCALLY_ADMINISTERED_MAC() \
{ \
2, UUID(0) ^ UUID(1) ^ UUID(2), UUID(3) ^ UUID(4) ^ UUID(5), \
UUID(6) ^ UUID(7) ^ UUID(8), UUID(9) ^ UUID(10) ^ UUID(11), \
UUID(12) ^ UUID(13) ^ UUID(14) ^ UUID(15) \
}
static inline bool timer_expired(volatile uint64_t *t, uint64_t prd,
uint64_t now) {
if (now + prd < *t) *t = 0; // Time wrapped? Reset timer
if (*t == 0) *t = now + prd; // Firt poll? Set expiration
if (*t > now) return false; // Not expired yet, return
*t = (now - *t) > prd ? now + prd : *t + prd; // Next expiration time
return true; // Expired, return true
}
static inline void clock_init(void) {
SCB->CPACR |= (15U << 20); // Enable FPU
SysTick_Config(clock_sys_freq() / 1000); // Sys tick every 1ms
}
static inline void gpio_set_irq_handler(uint16_t pin, void (*fn)(void *),
void *arg) {
(void) pin, (void) fn, (void) arg;
}
static inline void ethernet_init(void) {
uint16_t pins[] = {PIN('A', 12), PIN('A', 13), PIN('A', 14), PIN('A', 15),
PIN('A', 17), PIN('A', 18), PIN('A', 19), PIN('C', 11),
PIN('C', 12), PIN('C', 20)};
uint32_t af[] = {MUX_PA12L_GMAC_GRX1, MUX_PA13L_GMAC_GRX0,
MUX_PA14L_GMAC_GTXCK, MUX_PA15L_GMAC_GRXER,
MUX_PA17L_GMAC_GTXEN, MUX_PA18L_GMAC_GTX0,
MUX_PA19L_GMAC_GTX1, MUX_PC11L_GMAC_GMDC,
MUX_PC12L_GMAC_GMDIO, MUX_PC20L_GMAC_GRXDV};
MCLK_REGS->MCLK_APBBMASK |= MCLK_APBBMASK_PORT_Msk;
for (size_t i = 0; i < sizeof(pins) / sizeof(pins[0]); i++) {
int bank = PINBANK(pins[i]), no = PINNO(pins[i]);
PORT_REGS->GROUP[bank].PORT_PINCFG[no] |= PORT_PINCFG_PMUXEN_Msk;
volatile uint8_t *m = &PORT_REGS->GROUP[bank].PORT_PMUX[no / 2], v = m[0];
if (no & 1) {
m[0] = (uint8_t) ((v & ~0xf0) | PORT_PMUX_PMUXO(af[i]));
} else {
m[0] = (uint8_t) ((v & ~0x0f) | PORT_PMUX_PMUXE(af[i]));
}
}
PORT_REGS->GROUP[0].PORT_PINCFG[17] |= PORT_PINCFG_DRVSTR_Msk;
PORT_REGS->GROUP[0].PORT_PINCFG[18] |= PORT_PINCFG_DRVSTR_Msk;
PORT_REGS->GROUP[0].PORT_PINCFG[19] |= PORT_PINCFG_DRVSTR_Msk;
// Reset PHY
uint16_t phy_pin = PIN('C', 21);
gpio_output(phy_pin);
gpio_write(phy_pin, false);
spin(999);
gpio_write(phy_pin, true);
spin(999);
}
@@ -0,0 +1,25 @@
ENTRY(Reset_Handler);
MEMORY {
flash(rx) : ORIGIN = 0x00000000, LENGTH = 1024k
sram(rwx) : ORIGIN = 0x20000000, LENGTH = 256k
}
_estack = ORIGIN(sram) + LENGTH(sram);
SECTIONS {
.vectors : { FILL(256) KEEP(*(.vectors)) } > flash
.text : { *(.text*) } > flash
.rodata : { *(.rodata*) } > flash
.data : {
_sdata = .;
*(.first_data)
*(.data SORT(.data.*))
_edata = .;
} > sram AT > flash
_sidata = LOADADDR(.data);
.bss : { _sbss = .; *(.bss SORT(.bss.*) COMMON) _ebss = .; } > sram
. = ALIGN(8);
_end = .;
}
@@ -0,0 +1,120 @@
// Copyright (c) 2023 Cesanta Software Limited
// All rights reserved
#include "hal.h"
#include "mongoose.h"
static const char *s_url = "mqtt://broker.hivemq.com:1883";
static const char *s_sub_topic = "mg/+/test"; // Publish topic
static const char *s_pub_topic = "mg/clnt/test"; // Subscribe topic
static const uint8_t s_qos = 1; // MQTT QoS
static struct mg_connection *s_conn; // Client connection
void SystemInit(void) { // Called automatically by startup code
clock_init();
rng_init();
}
static volatile uint64_t s_ticks; // Milliseconds since boot
void SysTick_Handler(void) { // SyStick IRQ handler, triggered every 1ms
s_ticks++;
}
uint64_t mg_millis(void) { // Let Mongoose use our uptime function
return s_ticks; // Return number of milliseconds since boot
}
void mg_random(void *buf, size_t len) { // Use on-board RNG
for (size_t n = 0; n < len; n += sizeof(uint32_t)) {
uint32_t r = rng_read();
memcpy((char *) buf + n, &r, n + sizeof(r) > len ? len - n : sizeof(r));
}
}
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_OPEN) {
MG_INFO(("%lu CREATED", c->id));
// c->is_hexdumping = 1;
} else if (ev == MG_EV_ERROR) {
// On error, log error message
MG_ERROR(("%lu ERROR %s", c->id, (char *) ev_data));
} else if (ev == MG_EV_MQTT_OPEN) {
// MQTT connect is successful
struct mg_str subt = mg_str(s_sub_topic);
struct mg_str pubt = mg_str(s_pub_topic), data = mg_str("hello");
MG_INFO(("%lu CONNECTED to %s", c->id, s_url));
struct mg_mqtt_opts sub_opts;
memset(&sub_opts, 0, sizeof(sub_opts));
sub_opts.topic = subt;
sub_opts.qos = s_qos;
mg_mqtt_sub(c, &sub_opts);
MG_INFO(("%lu SUBSCRIBED to %.*s", c->id, (int) subt.len, subt.ptr));
struct mg_mqtt_opts pub_opts;
memset(&pub_opts, 0, sizeof(pub_opts));
pub_opts.topic = pubt;
pub_opts.message = data;
pub_opts.qos = s_qos, pub_opts.retain = false;
mg_mqtt_pub(c, &pub_opts);
MG_INFO(("%lu PUBLISHED %.*s -> %.*s", c->id, (int) data.len, data.ptr,
(int) pubt.len, pubt.ptr));
} else if (ev == MG_EV_MQTT_MSG) {
// When we get echo response, print it
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
MG_INFO(("%lu RECEIVED %.*s <- %.*s", c->id, (int) mm->data.len,
mm->data.ptr, (int) mm->topic.len, mm->topic.ptr));
} else if (ev == MG_EV_CLOSE) {
MG_INFO(("%lu CLOSED", c->id));
s_conn = NULL; // Mark that we're closed
}
(void) fn_data;
}
// Timer function - recreate client connection if it is closed
static void timer_fn(void *arg) {
struct mg_mgr *mgr = (struct mg_mgr *) arg;
struct mg_mqtt_opts opts = {.clean = true,
.qos = s_qos,
.topic = mg_str(s_pub_topic),
.version = 4,
.message = mg_str("bye")};
if (s_conn == NULL) s_conn = mg_mqtt_connect(mgr, s_url, &opts, fn, NULL);
}
int main(void) {
gpio_input(BUTTON_PIN);
gpio_output(LED_PIN);
uart_init(UART_DEBUG, 115200);
ethernet_init();
MG_INFO(("Starting, CPU freq %g MHz", (double) clock_sys_freq() / 1000000));
struct mg_mgr mgr; // Initialise
mg_mgr_init(&mgr); // Mongoose event manager
mg_log_set(MG_LL_DEBUG); // Set log level
// Initialise Mongoose network stack
struct mg_tcpip_driver_same54_data driver_data = {.mdc_cr = 5};
struct mg_tcpip_if mif = {
.mac = GENERATE_LOCALLY_ADMINISTERED_MAC() /*{2, 3, 4, 5, 6, 7}*/,
// Uncomment below for static configuration:
// .ip = mg_htonl(MG_U32(192, 168, 0, 223)),
// .mask = mg_htonl(MG_U32(255, 255, 255, 0)),
// .gw = mg_htonl(MG_U32(192, 168, 0, 1)),
.driver = &mg_tcpip_driver_same54,
.driver_data = &driver_data};
mg_tcpip_init(&mgr, &mif);
MG_INFO(("MAC: %M. Waiting for IP...", mg_print_mac, mif.mac));
while (mif.state != MG_TCPIP_STATE_READY) {
mg_mgr_poll(&mgr, 0);
}
MG_INFO(("Initialising application..."));
mg_timer_add(&mgr, 3000, MG_TIMER_REPEAT | MG_TIMER_RUN_NOW, timer_fn, &mgr);
MG_INFO(("Starting event loop"));
for (;;) {
mg_mgr_poll(&mgr, 0);
}
return 0;
}
@@ -0,0 +1 @@
../../../../mongoose.c
@@ -0,0 +1 @@
../../../../mongoose.h
@@ -0,0 +1,332 @@
// SPDX-FileCopyrightText: 2022-2023 Cesanta Software Limited
// SPDX-License-Identifier: MIT
#include "hal.h"
void Reset_Handler(void); // Defined below
void Dummy_Handler(void); // Defined below
void SysTick_Handler(void); // Defined in main.c
void SystemInit(void); // Defined in main.c, called by reset handler
void _estack(void); // Defined in link.ld
#define WEAK_ALIAS __attribute__((weak, alias("Default_Handler")))
WEAK_ALIAS void NMI_Handler(void);
WEAK_ALIAS void HardFault_Handler(void);
WEAK_ALIAS void MemoryManagement_Handler(void);
WEAK_ALIAS void BusFault_Handler(void);
WEAK_ALIAS void UsageFault_Handler(void);
WEAK_ALIAS void SVCall_Handler(void);
WEAK_ALIAS void DebugMonitor_Handler(void);
WEAK_ALIAS void PendSV_Handler(void);
WEAK_ALIAS void SysTick_Handler(void);
WEAK_ALIAS void PM_Handler(void);
WEAK_ALIAS void MCLK_Handler(void);
WEAK_ALIAS void OSCCTRL_XOSC0_Handler(void);
WEAK_ALIAS void OSCCTRL_XOSC1_Handler(void);
WEAK_ALIAS void OSCCTRL_DFLL_Handler(void);
WEAK_ALIAS void OSCCTRL_DPLL0_Handler(void);
WEAK_ALIAS void OSCCTRL_DPLL1_Handler(void);
WEAK_ALIAS void OSC32KCTRL_Handler(void);
WEAK_ALIAS void SUPC_OTHER_Handler(void);
WEAK_ALIAS void SUPC_BODDET_Handler(void);
WEAK_ALIAS void WDT_Handler(void);
WEAK_ALIAS void RTC_Handler(void);
WEAK_ALIAS void EIC_EXTINT_0_Handler(void);
WEAK_ALIAS void EIC_EXTINT_1_Handler(void);
WEAK_ALIAS void EIC_EXTINT_2_Handler(void);
WEAK_ALIAS void EIC_EXTINT_3_Handler(void);
WEAK_ALIAS void EIC_EXTINT_4_Handler(void);
WEAK_ALIAS void EIC_EXTINT_5_Handler(void);
WEAK_ALIAS void EIC_EXTINT_6_Handler(void);
WEAK_ALIAS void EIC_EXTINT_7_Handler(void);
WEAK_ALIAS void EIC_EXTINT_8_Handler(void);
WEAK_ALIAS void EIC_EXTINT_9_Handler(void);
WEAK_ALIAS void EIC_EXTINT_10_Handler(void);
WEAK_ALIAS void EIC_EXTINT_11_Handler(void);
WEAK_ALIAS void EIC_EXTINT_12_Handler(void);
WEAK_ALIAS void EIC_EXTINT_13_Handler(void);
WEAK_ALIAS void EIC_EXTINT_14_Handler(void);
WEAK_ALIAS void EIC_EXTINT_15_Handler(void);
WEAK_ALIAS void FREQM_Handler(void);
WEAK_ALIAS void NVMCTRL_0_Handler(void);
WEAK_ALIAS void NVMCTRL_1_Handler(void);
WEAK_ALIAS void DMAC_0_Handler(void);
WEAK_ALIAS void DMAC_1_Handler(void);
WEAK_ALIAS void DMAC_2_Handler(void);
WEAK_ALIAS void DMAC_3_Handler(void);
WEAK_ALIAS void DMAC_OTHER_Handler(void);
WEAK_ALIAS void EVSYS_0_Handler(void);
WEAK_ALIAS void EVSYS_1_Handler(void);
WEAK_ALIAS void EVSYS_2_Handler(void);
WEAK_ALIAS void EVSYS_3_Handler(void);
WEAK_ALIAS void EVSYS_OTHER_Handler(void);
WEAK_ALIAS void PAC_Handler(void);
WEAK_ALIAS void RAMECC_Handler(void);
WEAK_ALIAS void SERCOM0_0_Handler(void);
WEAK_ALIAS void SERCOM0_1_Handler(void);
WEAK_ALIAS void SERCOM0_2_Handler(void);
WEAK_ALIAS void SERCOM0_OTHER_Handler(void);
WEAK_ALIAS void SERCOM1_0_Handler(void);
WEAK_ALIAS void SERCOM1_1_Handler(void);
WEAK_ALIAS void SERCOM1_2_Handler(void);
WEAK_ALIAS void SERCOM1_OTHER_Handler(void);
WEAK_ALIAS void SERCOM2_0_Handler(void);
WEAK_ALIAS void SERCOM2_1_Handler(void);
WEAK_ALIAS void SERCOM2_2_Handler(void);
WEAK_ALIAS void SERCOM2_OTHER_Handler(void);
WEAK_ALIAS void SERCOM3_0_Handler(void);
WEAK_ALIAS void SERCOM3_1_Handler(void);
WEAK_ALIAS void SERCOM3_2_Handler(void);
WEAK_ALIAS void SERCOM3_OTHER_Handler(void);
WEAK_ALIAS void SERCOM4_0_Handler(void);
WEAK_ALIAS void SERCOM4_1_Handler(void);
WEAK_ALIAS void SERCOM4_2_Handler(void);
WEAK_ALIAS void SERCOM4_OTHER_Handler(void);
WEAK_ALIAS void SERCOM5_0_Handler(void);
WEAK_ALIAS void SERCOM5_1_Handler(void);
WEAK_ALIAS void SERCOM5_2_Handler(void);
WEAK_ALIAS void SERCOM5_OTHER_Handler(void);
WEAK_ALIAS void SERCOM6_0_Handler(void);
WEAK_ALIAS void SERCOM6_1_Handler(void);
WEAK_ALIAS void SERCOM6_2_Handler(void);
WEAK_ALIAS void SERCOM6_OTHER_Handler(void);
WEAK_ALIAS void SERCOM7_0_Handler(void);
WEAK_ALIAS void SERCOM7_1_Handler(void);
WEAK_ALIAS void SERCOM7_2_Handler(void);
WEAK_ALIAS void SERCOM7_OTHER_Handler(void);
WEAK_ALIAS void CAN0_Handler(void);
WEAK_ALIAS void CAN1_Handler(void);
WEAK_ALIAS void USB_OTHER_Handler(void);
WEAK_ALIAS void USB_SOF_HSOF_Handler(void);
WEAK_ALIAS void USB_TRCPT0_Handler(void);
WEAK_ALIAS void USB_TRCPT1_Handler(void);
WEAK_ALIAS void GMAC_Handler(void);
WEAK_ALIAS void TCC0_OTHER_Handler(void);
WEAK_ALIAS void TCC0_MC0_Handler(void);
WEAK_ALIAS void TCC0_MC1_Handler(void);
WEAK_ALIAS void TCC0_MC2_Handler(void);
WEAK_ALIAS void TCC0_MC3_Handler(void);
WEAK_ALIAS void TCC0_MC4_Handler(void);
WEAK_ALIAS void TCC0_MC5_Handler(void);
WEAK_ALIAS void TCC1_OTHER_Handler(void);
WEAK_ALIAS void TCC1_MC0_Handler(void);
WEAK_ALIAS void TCC1_MC1_Handler(void);
WEAK_ALIAS void TCC1_MC2_Handler(void);
WEAK_ALIAS void TCC1_MC3_Handler(void);
WEAK_ALIAS void TCC2_OTHER_Handler(void);
WEAK_ALIAS void TCC2_MC0_Handler(void);
WEAK_ALIAS void TCC2_MC1_Handler(void);
WEAK_ALIAS void TCC2_MC2_Handler(void);
WEAK_ALIAS void TCC3_OTHER_Handler(void);
WEAK_ALIAS void TCC3_MC0_Handler(void);
WEAK_ALIAS void TCC3_MC1_Handler(void);
WEAK_ALIAS void TCC4_OTHER_Handler(void);
WEAK_ALIAS void TCC4_MC0_Handler(void);
WEAK_ALIAS void TCC4_MC1_Handler(void);
WEAK_ALIAS void TC0_Handler(void);
WEAK_ALIAS void TC1_Handler(void);
WEAK_ALIAS void TC2_Handler(void);
WEAK_ALIAS void TC3_Handler(void);
WEAK_ALIAS void TC4_Handler(void);
WEAK_ALIAS void TC5_Handler(void);
WEAK_ALIAS void TC6_Handler(void);
WEAK_ALIAS void TC7_Handler(void);
WEAK_ALIAS void PDEC_OTHER_Handler(void);
WEAK_ALIAS void PDEC_MC0_Handler(void);
WEAK_ALIAS void PDEC_MC1_Handler(void);
WEAK_ALIAS void ADC0_OTHER_Handler(void);
WEAK_ALIAS void ADC0_RESRDY_Handler(void);
WEAK_ALIAS void ADC1_OTHER_Handler(void);
WEAK_ALIAS void ADC1_RESRDY_Handler(void);
WEAK_ALIAS void AC_Handler(void);
WEAK_ALIAS void DAC_OTHER_Handler(void);
WEAK_ALIAS void DAC_EMPTY_0_Handler(void);
WEAK_ALIAS void DAC_EMPTY_1_Handler(void);
WEAK_ALIAS void DAC_RESRDY_0_Handler(void);
WEAK_ALIAS void DAC_RESRDY_1_Handler(void);
WEAK_ALIAS void I2S_Handler(void);
WEAK_ALIAS void PCC_Handler(void);
WEAK_ALIAS void AES_Handler(void);
WEAK_ALIAS void TRNG_Handler(void);
WEAK_ALIAS void ICM_Handler(void);
WEAK_ALIAS void PUKCC_Handler(void);
WEAK_ALIAS void QSPI_Handler(void);
WEAK_ALIAS void SDHC0_Handler(void);
WEAK_ALIAS void SDHC1_Handler(void);
__attribute__((section(".vectors"))) void (*const tab[16 + 138])(void) = {
_estack,
Reset_Handler,
NMI_Handler,
HardFault_Handler,
MemoryManagement_Handler,
BusFault_Handler,
UsageFault_Handler,
NULL,
NULL,
NULL,
NULL,
SVCall_Handler,
DebugMonitor_Handler,
NULL,
PendSV_Handler,
SysTick_Handler,
PM_Handler, // 0 Power
MCLK_Handler, // 1 Main
OSCCTRL_XOSC0_Handler, // 2 Oscillators
OSCCTRL_XOSC1_Handler, // 3 Oscillators
OSCCTRL_DFLL_Handler, // 4 Oscillators
OSCCTRL_DPLL0_Handler, // 5 Oscillators
OSCCTRL_DPLL1_Handler, // 6 Oscillators
OSC32KCTRL_Handler, // 7 32kHz
SUPC_OTHER_Handler, // 8 Supply
SUPC_BODDET_Handler, // 9 Supply
WDT_Handler, // 10 Watchdog
RTC_Handler, // 11 Real-Time
EIC_EXTINT_0_Handler, // 12 External
EIC_EXTINT_1_Handler, // 13 External
EIC_EXTINT_2_Handler, // 14 External
EIC_EXTINT_3_Handler, // 15 External
EIC_EXTINT_4_Handler, // 16 External
EIC_EXTINT_5_Handler, // 17 External
EIC_EXTINT_6_Handler, // 18 External
EIC_EXTINT_7_Handler, // 19 External
EIC_EXTINT_8_Handler, // 20 External
EIC_EXTINT_9_Handler, // 21 External
EIC_EXTINT_10_Handler, // 22 External
EIC_EXTINT_11_Handler, // 23 External
EIC_EXTINT_12_Handler, // 24 External
EIC_EXTINT_13_Handler, // 25 External
EIC_EXTINT_14_Handler, // 26 External
EIC_EXTINT_15_Handler, // 27 External
FREQM_Handler, // 28 Frequency
NVMCTRL_0_Handler, // 29 Non-Volatile
NVMCTRL_1_Handler, // 30 Non-Volatile
DMAC_0_Handler, // 31 Direct
DMAC_1_Handler, // 32 Direct
DMAC_2_Handler, // 33 Direct
DMAC_3_Handler, // 34 Direct
DMAC_OTHER_Handler, // 35 Direct
EVSYS_0_Handler, // 36 Event
EVSYS_1_Handler, // 37 Event
EVSYS_2_Handler, // 38 Event
EVSYS_3_Handler, // 39 Event
EVSYS_OTHER_Handler, // 40 Event
PAC_Handler, // 41 Peripheral
0, // 42 Reserved
0, // 43 Reserved
0, // 44 Reserved
RAMECC_Handler, // 45 RAM
SERCOM0_0_Handler, // 46 Serial
SERCOM0_1_Handler, // 47 Serial
SERCOM0_2_Handler, // 48 Serial
SERCOM0_OTHER_Handler, // 49 Serial
SERCOM1_0_Handler, // 50 Serial
SERCOM1_1_Handler, // 51 Serial
SERCOM1_2_Handler, // 52 Serial
SERCOM1_OTHER_Handler, // 53 Serial
SERCOM2_0_Handler, // 54 Serial
SERCOM2_1_Handler, // 55 Serial
SERCOM2_2_Handler, // 56 Serial
SERCOM2_OTHER_Handler, // 57 Serial
SERCOM3_0_Handler, // 58 Serial
SERCOM3_1_Handler, // 59 Serial
SERCOM3_2_Handler, // 60 Serial
SERCOM3_OTHER_Handler, // 61 Serial
SERCOM4_0_Handler, // 62 Serial
SERCOM4_1_Handler, // 63 Serial
SERCOM4_2_Handler, // 64 Serial
SERCOM4_OTHER_Handler, // 65 Serial
SERCOM5_0_Handler, // 66 Serial
SERCOM5_1_Handler, // 67 Serial
SERCOM5_2_Handler, // 68 Serial
SERCOM5_OTHER_Handler, // 69 Serial
SERCOM6_0_Handler, // 70 Serial
SERCOM6_1_Handler, // 71 Serial
SERCOM6_2_Handler, // 72 Serial
SERCOM6_OTHER_Handler, // 73 Serial
SERCOM7_0_Handler, // 74 Serial
SERCOM7_1_Handler, // 75 Serial
SERCOM7_2_Handler, // 76 Serial
SERCOM7_OTHER_Handler, // 77 Serial
CAN0_Handler, // 78 Control
CAN1_Handler, // 79 Control
USB_OTHER_Handler, // 80 Universal
USB_SOF_HSOF_Handler, // 81 Universal
USB_TRCPT0_Handler, // 82 Universal
USB_TRCPT1_Handler, // 83 Universal
GMAC_Handler, // 84 Ethernet
TCC0_OTHER_Handler, // 85 Timer
TCC0_MC0_Handler, // 86 Timer
TCC0_MC1_Handler, // 87 Timer
TCC0_MC2_Handler, // 88 Timer
TCC0_MC3_Handler, // 89 Timer
TCC0_MC4_Handler, // 90 Timer
TCC0_MC5_Handler, // 91 Timer
TCC1_OTHER_Handler, // 92 Timer
TCC1_MC0_Handler, // 93 Timer
TCC1_MC1_Handler, // 94 Timer
TCC1_MC2_Handler, // 95 Timer
TCC1_MC3_Handler, // 96 Timer
TCC2_OTHER_Handler, // 97 Timer
TCC2_MC0_Handler, // 98 Timer
TCC2_MC1_Handler, // 99 Timer
TCC2_MC2_Handler, // 100 Timer
TCC3_OTHER_Handler, // 101 Timer
TCC3_MC0_Handler, // 102 Timer
TCC3_MC1_Handler, // 103 Timer
TCC4_OTHER_Handler, // 104 Timer
TCC4_MC0_Handler, // 105 Timer
TCC4_MC1_Handler, // 106 Timer
TC0_Handler, // 107 Basic
TC1_Handler, // 108 Basic
TC2_Handler, // 109 Basic
TC3_Handler, // 110 Basic
TC4_Handler, // 111 Basic
TC5_Handler, // 112 Basic
TC6_Handler, // 113 Basic
TC7_Handler, // 114 Basic
PDEC_OTHER_Handler, // 115 Quadrature
PDEC_MC0_Handler, // 116 Quadrature
PDEC_MC1_Handler, // 117 Quadrature
ADC0_OTHER_Handler, // 118 Analog
ADC0_RESRDY_Handler, // 119 Analog
ADC1_OTHER_Handler, // 120 Analog
ADC1_RESRDY_Handler, // 121 Analog
AC_Handler, // 122 Analog
DAC_OTHER_Handler, // 123 Digital-to-Analog
DAC_EMPTY_0_Handler, // 124 Digital-to-Analog
DAC_EMPTY_1_Handler, // 125 Digital-to-Analog
DAC_RESRDY_0_Handler, // 126 Digital-to-Analog
DAC_RESRDY_1_Handler, // 127 Digital-to-Analog
I2S_Handler, // 128 Inter-IC
PCC_Handler, // 129 Parallel
AES_Handler, // 130 Advanced
TRNG_Handler, // 131 True
ICM_Handler, // 132 Integrity
PUKCC_Handler, // 133 PUblic-Key
QSPI_Handler, // 134 Quad
SDHC0_Handler, // 135 SD/MMC
SDHC1_Handler // 136 SD/MMC
};
__attribute__((naked, noreturn)) void Reset_Handler(void) {
// Clear BSS section, and copy data section from flash to RAM
extern long _sbss, _ebss, _sdata, _edata, _sidata;
for (long *src = &_sbss; src < &_ebss; src++) *src = 0;
for (long *src = &_sdata, *dst = &_sidata; src < &_edata;) *src++ = *dst++;
SCB->VTOR = (uint32_t) &tab;
SystemInit();
// Call main()
extern void main(void);
main();
for (;;) (void) 0; // Infinite loop
}
void Default_Handler(void) {
for (;;) (void) 0;
}
@@ -0,0 +1,89 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
#include <sys/stat.h>
#include "hal.h"
int _fstat(int fd, struct stat *st) {
if (fd < 0) return -1;
st->st_mode = S_IFCHR;
return 0;
}
void *_sbrk(int incr) {
extern char _end;
static unsigned char *heap = NULL;
unsigned char *prev_heap;
if (heap == NULL) heap = (unsigned char *) &_end;
prev_heap = heap;
heap += incr;
return prev_heap;
}
int _open(const char *path) {
(void) path;
return -1;
}
int _close(int fd) {
(void) fd;
return -1;
}
int _isatty(int fd) {
(void) fd;
return 1;
}
int _lseek(int fd, int ptr, int dir) {
(void) fd, (void) ptr, (void) dir;
return 0;
}
void _exit(int status) {
(void) status;
for (;;) asm volatile("BKPT #0");
}
void _kill(int pid, int sig) {
(void) pid, (void) sig;
}
int _getpid(void) {
return -1;
}
int _write(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
if (fd == 1) uart_write_buf(UART_DEBUG, ptr, (size_t) len);
return -1;
}
int _read(int fd, char *ptr, int len) {
(void) fd, (void) ptr, (void) len;
return -1;
}
int _link(const char *a, const char *b) {
(void) a, (void) b;
return -1;
}
int _unlink(const char *a) {
(void) a;
return -1;
}
int _stat(const char *path, struct stat *st) {
(void) path, (void) st;
return -1;
}
int mkdir(const char *path, mode_t mode) {
(void) path, (void) mode;
return -1;
}
void _init(void) {
}
+3 -3
View File
@@ -1,12 +1,12 @@
PROG ?= example # Program we are building PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c net.c packed_fs.c # Source code files SOURCES = main.c mongoose.c # Source code files
CFLAGS = -W -Wall -Wextra -g -I. # Build options CFLAGS = -W -Wall -Wextra -g -I. # Build options
CFLAGS += -lpcap # link with libpcap CFLAGS += -lpcap # link with libpcap
# Mongoose build options. See https://mongoose.ws/documentation/#build-options # Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_SOCKET=0 -DMG_ENABLE_PACKED_FS=1 CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_SOCKET=0
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary PROG ?= example.exe # Use .exe suffix for the binary
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
+1 -4
View File
@@ -47,7 +47,7 @@ static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data; struct mg_http_message *hm = (struct mg_http_message *) ev_data;
MG_DEBUG(("Got response (%d) %.*s...", (int) hm->message.len, 12, MG_DEBUG(("Got response (%d) %.*s...", (int) hm->message.len, 12,
hm->message.ptr)); hm->message.ptr));
c->is_closing = 1; c->is_draining = 1;
} else if (ev == MG_EV_CONNECT) { } else if (ev == MG_EV_CONNECT) {
mg_printf(c, "GET %s HTTP/1.1\r\n\r\n", mg_url_uri((char *) fn_data)); mg_printf(c, "GET %s HTTP/1.1\r\n\r\n", mg_url_uri((char *) fn_data));
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
@@ -138,9 +138,6 @@ int main(int argc, char *argv[]) {
&mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]); &mif.mac[2], &mif.mac[3], &mif.mac[4], &mif.mac[5]);
mg_tcpip_init(&mgr, &mif); mg_tcpip_init(&mgr, &mif);
MG_INFO(("Init done, starting main loop")); MG_INFO(("Init done, starting main loop"));
// extern void device_dashboard_fn(struct mg_connection *, int, void *, void
// *);
mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr); mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr);
while (s_signo == 0) mg_mgr_poll(&mgr, 100); // Infinite event loop while (s_signo == 0) mg_mgr_poll(&mgr, 100); // Infinite event loop
-1
View File
@@ -1 +0,0 @@
../device-dashboard/net.c
-1
View File
@@ -1 +0,0 @@
../device-dashboard/packed_fs.c
+3
View File
@@ -7,6 +7,9 @@ CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options # Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_SOCKET=0 -DMG_ENABLE_PACKED_FS=1 CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_TCPIP=1 -DMG_ENABLE_SOCKET=0 -DMG_ENABLE_PACKED_FS=1
# Example specific build options. See README.md
CFLAGS += -DHTTP_URL=\"http://0.0.0.0/\" -DHTTPS_URL=\"https://0.0.0.0/\"
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary PROG ?= example.exe # Use .exe suffix for the binary
CC = gcc # Use MinGW gcc compiler CC = gcc # Use MinGW gcc compiler
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
+10 -11
View File
@@ -1,9 +1,7 @@
// Copyright (c) 2022 Cesanta Software Limited // Copyright (c) 2022 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// example using MIP and a TUN/TAP interface // example using built-in TCP/IP stack and TUN/TAP interface
static const char *s_listening_address = "http://0.0.0.0:8000";
#include <sys/socket.h> #include <sys/socket.h>
#ifndef __OpenBSD__ #ifndef __OpenBSD__
@@ -15,7 +13,9 @@ static const char *s_listening_address = "http://0.0.0.0:8000";
#include <net/if_types.h> #include <net/if_types.h>
#endif #endif
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include "mongoose.h" #include "mongoose.h"
#include "net.h"
static int s_signo; static int s_signo;
void signal_handler(int signo) { void signal_handler(int signo) {
@@ -23,7 +23,7 @@ void signal_handler(int signo) {
} }
static size_t tap_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) { static size_t tap_tx(const void *buf, size_t len, struct mg_tcpip_if *ifp) {
ssize_t res = write(*(int*) ifp->driver_data, buf, len); ssize_t res = write(*(int *) ifp->driver_data, buf, len);
if (res < 0) { if (res < 0) {
MG_ERROR(("tap_tx failed: %d", errno)); MG_ERROR(("tap_tx failed: %d", errno));
return 0; return 0;
@@ -62,9 +62,9 @@ int main(int argc, char *argv[]) {
// Open network interface // Open network interface
#ifndef __OpenBSD__ #ifndef __OpenBSD__
const char* tuntap_device = "/dev/net/tun"; const char *tuntap_device = "/dev/net/tun";
#else #else
const char* tuntap_device = "/dev/tap0"; const char *tuntap_device = "/dev/tap0";
#endif #endif
int fd = open(tuntap_device, O_RDWR); int fd = open(tuntap_device, O_RDWR);
struct ifreq ifr; struct ifreq ifr;
@@ -77,7 +77,7 @@ int main(int argc, char *argv[]) {
abort(); // return EXIT_FAILURE; abort(); // return EXIT_FAILURE;
} }
#else #else
ifr.ifr_flags = (short)(IFF_UP | IFF_BROADCAST | IFF_MULTICAST); ifr.ifr_flags = (short) (IFF_UP | IFF_BROADCAST | IFF_MULTICAST);
if (ioctl(fd, TUNSIFMODE, (void *) &ifr) < 0) { if (ioctl(fd, TUNSIFMODE, (void *) &ifr) < 0) {
MG_ERROR(("Failed to setup TAP interface: %s", ifr.ifr_name)); MG_ERROR(("Failed to setup TAP interface: %s", ifr.ifr_name));
abort(); // return EXIT_FAILURE; abort(); // return EXIT_FAILURE;
@@ -101,11 +101,10 @@ int main(int argc, char *argv[]) {
// Start infinite event loop // Start infinite event loop
MG_INFO(("Mongoose version : v%s", MG_VERSION)); MG_INFO(("Mongoose version : v%s", MG_VERSION));
MG_INFO(("Listening on : %s", s_listening_address)); MG_INFO(("Listening on : %s", HTTP_URL));
MG_INFO(("Listening on : %s", HTTPS_URL));
extern void device_dashboard_fn(struct mg_connection *, int, void *, void *);
mg_http_listen(&mgr, s_listening_address, device_dashboard_fn, NULL);
web_init(&mgr);
while (s_signo == 0) mg_mgr_poll(&mgr, 100); // Infinite event loop while (s_signo == 0) mg_mgr_poll(&mgr, 100); // Infinite event loop
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
+1
View File
@@ -0,0 +1 @@
../device-dashboard/net.h
+2 -2
View File
@@ -1,11 +1,11 @@
PROG ?= example # Program we are building PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files SOURCES = main.c mongoose.c packed_fs.c # Source code files, packed_fs.c contains ca.pem, which contains CA certs for TLS
CFLAGS = -W -Wall -Wextra -g -I. # Build options CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options # Mongoose build options. See https://mongoose.ws/documentation/#build-options
#CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_PACKED_FS=1
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary PROG ?= example.exe # Use .exe suffix for the binary
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
+8 -7
View File
@@ -29,8 +29,8 @@ static const char *s_url =
// 3. From the dialog box that appears, download: // 3. From the dialog box that appears, download:
// xxx-certificate.pem.crt as cert.pem to the example directory // xxx-certificate.pem.crt as cert.pem to the example directory
// xxx-private.pem.key as key.pem to the example directory // xxx-private.pem.key as key.pem to the example directory
static const char *s_cert = "cert.pem"; // static const char *s_cert = "cert.pem";
static const char *s_key = "key.pem"; // static const char *s_key = "key.pem";
static const char *s_rx_topic = "d/rx"; static const char *s_rx_topic = "d/rx";
static const char *s_tx_topic = "d/tx"; static const char *s_tx_topic = "d/tx";
@@ -41,14 +41,15 @@ static int s_qos = 1;
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_OPEN) { if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1; // c->is_hexdumping = 1;
} else if (ev == MG_EV_CONNECT) {
if (mg_url_is_ssl(s_url)) {
struct mg_tls_opts opts = {.ca = mg_unpacked("/certs/ca.pem"),
.name = mg_url_host(s_url)};
mg_tls_init(c, &opts);
}
} else if (ev == MG_EV_ERROR) { } else if (ev == MG_EV_ERROR) {
// On error, log error message // On error, log error message
MG_ERROR(("%p %s", c->fd, (char *) ev_data)); MG_ERROR(("%p %s", c->fd, (char *) ev_data));
} else if (ev == MG_EV_CONNECT) {
// Set up 2-way TLS that is required by AWS IoT
struct mg_tls_opts opts = {
.ca = "ca.pem", .cert = s_cert, .certkey = s_key};
mg_tls_init(c, &opts);
} else if (ev == MG_EV_MQTT_OPEN) { } else if (ev == MG_EV_MQTT_OPEN) {
// MQTT connect is successful // MQTT connect is successful
struct mg_str topic = mg_str(s_rx_topic); struct mg_str topic = mg_str(s_rx_topic);
+1
View File
@@ -0,0 +1 @@
../http-client/packed_fs.c
+2 -2
View File
@@ -1,11 +1,11 @@
PROG ?= example # Program we are building PROG ?= example # Program we are building
DELETE = rm -rf # Command to remove files DELETE = rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c mongoose.c # Source code files SOURCES = main.c mongoose.c packed_fs.c # Source code files, packed_fs.c contains ca.pem, which contains CA certs for TLS
CFLAGS = -W -Wall -Wextra -g -I. # Build options CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options # Mongoose build options. See https://mongoose.ws/documentation/#build-options
#CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1 -DMG_ENABLE_PACKED_FS=1
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
PROG ?= example.exe # Use .exe suffix for the binary PROG ?= example.exe # Use .exe suffix for the binary
-1
View File
@@ -1 +0,0 @@
../../test/data/ca.pem
Loaded 100 of 1755 files, more files were not shown because too many files have changed in this diff. Show more