Compare commits

...
243 Commits
Author SHA1 Message Date
Sergio R. Caprile 52997c6c51 update version 2024-02-21 08:50:06 -03:00
Sergey Lyubka 93148e3223 Merge pull request #2616 from cesanta/upload
Add file upload functionality to the http-server example
2024-02-20 12:07:19 +00:00
Sergey Lyubka f01807ef17 Add file upload functionality to the http-server example 2024-02-20 11:39:28 +00:00
Sergio R. Caprile a4ed740518 Merge pull request #2610 from cesanta/file_read
Change mg_file_read() signature: return mg_str
2024-02-16 09:48:08 -03:00
Sergey Lyubka 0e2f3de154 Change mg_file_read() signature: return mg_str 2024-02-16 12:07:35 +00:00
Sergey Lyubka d128d2146a Online status 2024-02-15 09:49:59 +00:00
Sergey Lyubka c683e1e988 More style changes 2024-02-15 09:46:42 +00:00
Sergey Lyubka 0b7038d44d Fixed layout 2024-02-15 09:38:32 +00:00
Sergey Lyubka 55698b91c0 Disable OTA for now.. 2024-02-15 09:26:37 +00:00
Sergey Lyubka eff54827b2 Fix config reporting 2024-02-14 23:46:43 +00:00
Sergio R. Caprile 2c2c7f6894 Merge pull request #2609 from cesanta/loc
Fill c->loc for outbound connections
2024-02-14 09:51:10 -03:00
Sergey Lyubka 5baa4d969e Fill c->loc for outbound connections 2024-02-14 12:32:26 +00:00
Sergey Lyubka d745c41d5b Merge pull request #2608 from cesanta/mqtt-dash
Update mqtt-dashboard, adopt new UI, break OTA for now..
2024-02-14 12:18:17 +00:00
Sergey Lyubka a86bde7b3f Update mqtt-dashboard, adopt new UI, break OTA for now.. 2024-02-14 12:14:17 +00:00
Sergio R. Caprile 4ab2309a5d Merge pull request #2607 from cesanta/tls
Fix #2603: TLS read path
2024-02-14 08:50:44 -03:00
Sergey Lyubka 9172f62970 Fix #2603: TLS read path 2024-02-13 05:49:15 +00:00
Sergey Lyubka daa47e8272 Merge pull request #2604 from cesanta/pipeline
Fix #2592: do not close pipelined connection prematurely
2024-02-13 05:48:34 +00:00
Sergey Lyubka 2419f02766 Fix #2592: do not close pipelined connection prematurely 2024-02-11 09:05:19 +00:00
Sergio R. Caprile c8d45c9ff1 Merge pull request #2601 from cesanta/dns
cap # responses instead of rejecting
2024-02-09 12:35:15 -03:00
Sergio R. Caprile 0b667ca869 cap # responses instead of rejecting 2024-02-09 12:28:56 -03:00
Sergio R. Caprile 1a5ea930ab Update fuzz.c for mg_commalist -> mg_span 2024-02-04 23:41:16 -03:00
Sergey Lyubka e4bfb2b5b0 Merge pull request #2597 from cesanta/str
mg_split -> mg_span. Remove mg_commalist
2024-02-04 09:39:08 +00:00
Sergey Lyubka 936e7bbc88 mg_split -> mg_span. Remove mg_commalist 2024-02-04 09:26:42 +00:00
Sergey Lyubka 93b5063251 Merge pull request #2599 from cesanta/upload
Allow user specify file in mg_http_upload
2024-02-04 09:07:28 +00:00
Sergey Lyubka e0668bc1c0 Allow user specify file in mg_http_upload 2024-02-04 08:56:32 +00:00
Sergey Lyubka fa5aa308bc Merge pull request #2596 from cesanta/ls
Add helper mg_fs_ls()
2024-02-01 18:25:05 +00:00
Sergey Lyubka cbfa57a955 Add helper mg_fs_ls() 2024-02-01 18:24:37 +00:00
Sergey Lyubka f8898b016e More unit tests for mg_split() 2024-02-01 18:21:46 +00:00
Sergio R. Caprile 8a9630485f fix sorting issues with locales 2024-02-01 10:22:30 -03:00
Sergio R. Caprile 066e4ac332 Merge pull request #2594 from cesanta/mg_json_get_tok
Add helper function mg_json_get_tok
2024-02-01 09:35:08 -03:00
Sergey Lyubka f231760395 Add helper function mg_json_get_tok 2024-02-01 12:05:49 +00:00
Sergey Lyubka ebfa5f0ba7 Add ek-ra6m4 example 2024-01-27 17:06:28 +00:00
Sergio R. Caprile d7754516f0 no diff checks in nightly tests 2024-01-26 14:33:22 -03:00
Sergio R. Caprile 00fd1884ff no diff checks in nightly tests 2024-01-26 12:54:07 -03:00
Sergio R. Caprile b828be4873 no diff checks in nightly tests 2024-01-26 12:48:04 -03:00
Sergio R. Caprile 4045a541ef Merge pull request #2590 from cesanta/rewact
Rework tests
2024-01-26 12:28:48 -03:00
Sergio R. Caprile f7557c9e48 trigger 2024-01-26 12:27:04 -03:00
Sergio R. Caprile 7c62b5005e work in progress 2024-01-26 12:15:48 -03:00
Sergey Lyubka b804217501 Merge pull request #2581 from cesanta/2559
Fix #2559: order of handlers: protocol first, user second
2024-01-26 14:03:54 +00:00
Sergio R. Caprile c6128094b6 rebase and update file-transfer example 2024-01-26 10:40:59 -03:00
Sergey Lyubka 0fc0230364 Changed the order of handlers: protocol first, user second 2024-01-26 10:34:24 -03:00
Sergio R. Caprile dd6458262d Merge pull request #2588 from cesanta/ft
Add file transfer example
2024-01-25 15:12:49 -03:00
Sergio R. Caprile eb68bb2ce1 Add file-transfer example 2024-01-25 15:05:48 -03:00
Sergio R. Caprile 8dc62c25ed Merge pull request #2587 from cesanta/tls
Fix #2570: close only when TLS buffers drained
2024-01-23 14:03:33 -03:00
Sergey Lyubka d8fec13d2c Fix #2570: close only when TLS buffers drained 2024-01-23 11:10:15 +00:00
Sergey Lyubka 922d148ab5 Add misra target 2024-01-23 11:09:16 +00:00
Sergio R. Caprile 08f05d6f8c Merge pull request #2585 from cesanta/fai
prefix PHY-related constants
2024-01-22 18:45:00 -03:00
Sergio R. Caprile 7bec3b4954 Merge pull request #2586 from cesanta/pos9
rename MG_ENABLE_FILE to MG_ENABLE_POSIX_FS
2024-01-22 16:48:17 -03:00
Sergio R. Caprile 4d3fba57d4 rename MG_ENABLE_FILE to MG_ENABLE_POSIX_FS 2024-01-22 16:21:57 -03:00
Sergio R. Caprile 1a8a144726 prefix PHY-related constants 2024-01-22 14:27:22 -03:00
Sergio R. Caprile 00ea3830a7 Merge pull request #2580 from cesanta/2552
Fix #2552 - reject requests with invalid/absent chunk length
2024-01-22 09:20:56 -03:00
Sergey Lyubka 3880300cd9 Add 256/64 build 2024-01-21 12:49:57 +00:00
Sergey Lyubka 6b0e4a425d Fix #2552 - reject requests with invalid/absent chunk length 2024-01-20 09:35:01 +00:00
Sergey Lyubka f883504d2d Fix #2573 - GCC build warning in tls_builtin.c::mul() 2024-01-20 08:51:01 +00:00
robertc2000 a1c5995967 Merge pull request #2562 from cesanta/imxflash-2
RT1020 and RT1060 OTA
2024-01-19 15:55:35 +02:00
robert 8500afbbd4 RT1020 and RT1060 OTA 2024-01-19 02:47:23 -05:00
Sergio R. Caprile 13cb94cf2b Merge pull request #2575 from cesanta/flexsdk
remove flexspi.h
2024-01-18 17:51:02 -03:00
Sergio R. Caprile f26a79d75c remove flexspi.h 2024-01-18 17:47:02 -03:00
Sergey Lyubka 68e2cd9b29 Fix WCH example 2024-01-17 18:07:25 +00:00
Sergio R. Caprile 6e44bab3d5 Merge pull request #2572 from cesanta/armccnodet
change mongoose_custom for Keil
2024-01-16 16:59:23 -03:00
Sergio R. Caprile 677fea7bfa change mongoose_custom for Keil 2024-01-16 16:21:05 -03:00
Sergio R. Caprile 0d2ae10bd9 Merge pull request #2571 from cesanta/dhcpsidfb
Fallback to IP source address on DHCP servers not filling sid
2024-01-16 10:26:34 -03:00
Sergio R. Caprile faa582040b Fallback to IP source address on DHCP servers not filling sid 2024-01-16 09:56:05 -03:00
Sergey Lyubka 0ad4002569 Make TLS less verbose 2024-01-14 23:27:52 +00:00
Sergey Lyubka dfc2e97d5e Squash warning 2024-01-14 22:10:18 +00:00
Sergio R. Caprile 57159dc5c0 Merge pull request #2568 from cesanta/1020flash
default to flash 1020
2024-01-12 14:23:48 -03:00
Sergio R. Caprile 77ff1511ff default to flash 1020 2024-01-12 14:20:46 -03:00
Sergio R. Caprile 124ab99995 Merge pull request #2554 from cesanta/1060flash
1060flash
2024-01-11 16:25:21 -03:00
Sergey Lyubka 8ea08306b8 Update year to 2024 2024-01-10 14:31:09 +00:00
Sergey Lyubka 3e0d20a335 Fix H573 example 2024-01-10 14:25:38 +00:00
Sergio R. Caprile 727b3d4f19 Merge pull request #2565 from cesanta/backlog
Set default listen backlog size back to 128
2024-01-10 08:45:33 -03:00
Sergey Lyubka 80782b289f Set default listen backlog size back to 128 2024-01-10 11:10:43 +00:00
Sergey Lyubka 72127a4bed README 2024-01-10 11:01:20 +00:00
Sergey Lyubka d4a49e00e7 Add Usage section to README 2024-01-10 10:58:30 +00:00
Sergio R. Caprile 5826d0e41c Merge pull request #2560 from cesanta/fn_data
remove fn_data from event handler signature
2024-01-09 15:40:46 -03:00
Sergio R. Caprile 68cb7ce8d9 cleanup 2024-01-09 14:55:04 -03:00
Sergio R. Caprile 514699fc41 cleanup 2024-01-09 14:54:19 -03:00
Sergio R. Caprile deca1aaf46 remove fn_data from event handler signature 2024-01-09 14:51:19 -03:00
Sergey Lyubka b8bfa5565b Merge pull request #2556 from cesanta/mt12m
add multi-threaded one to many example
2024-01-07 19:04:53 +00:00
Sergio R. Caprile 50c7844031 Merge pull request #2557 from cesanta/xpresso
add RT1060 Xpresso example
2024-01-05 18:38:32 -03:00
Sergio R. Caprile cba0d1b2f9 add RT1060 Xpresso example 2024-01-05 18:24:47 -03:00
Sergio R. Caprile 5735ee3269 default to flash 1060
workaround SysTick race condition in FreeRTOS example
2024-01-05 13:49:50 -03:00
Sergio R. Caprile ee3593b887 add multi-threaded one to many example 2024-01-05 12:12:00 -03:00
Sergey Lyubka 9bebe7cf68 Update readme 2024-01-02 10:54:00 +00:00
Sergio R. Caprile bd5e4dba26 Merge pull request #2551 from cesanta/mqttpayload
Avoid calling mg_send on no actual payload
2023-12-29 15:13:02 -03:00
Sergio R. Caprile 2427c40a4c Avoid calling mg_send on no actual payload 2023-12-29 14:58:03 -03:00
Sergey Lyubka 744df7b244 Better checks in skip_chunk() 2023-12-29 11:59:02 +00:00
Sergey Lyubka 3965aecfd4 32/288, no TLS by default 2023-12-27 22:53:48 +00:00
Sergey Lyubka 93bcde0176 Enable TLS and 256/64 mode 2023-12-27 22:45:16 +00:00
Sergio R. Caprile 4b3884a49d Update README.md 2023-12-27 18:34:59 -03:00
Sergey Lyubka 768908ae4a Merge pull request #2547 from cesanta/opta
Add STM32H747I-DISCO example
2023-12-27 20:06:30 +00:00
Sergey Lyubka 1a965d5c43 Add STM32H747I-DISCO example 2023-12-27 20:04:53 +00:00
Sergey Lyubka 01b612cff5 WCH FW build fix. Introduce phy_addr for STM32F driver 2023-12-27 15:38:51 +00:00
Sergio R. Caprile 176db1aedb remove some compiler warning 2023-12-23 19:02:05 -03:00
Sergey Lyubka 06f8238107 Fix TLS codepath for pending data. Reuse c->rtls 2023-12-22 16:17:06 +00:00
Sergio R. Caprile 97127a57da Merge pull request #2542 from cesanta/pair
Add mg_wakeup()
2023-12-21 14:22:10 -03:00
Sergey Lyubka 61825b2390 Add mg_wakeup() 2023-12-21 14:03:33 -03:00
Sergio R. Caprile 669ffe2c74 Merge pull request #2543 from cesanta/gettimeofday
Remove gettimeofday implementation, to avoid clashes
2023-12-21 13:50:09 -03:00
Sergey Lyubka a50b8ff5d7 Remove gettimeofday implementation, to avoid clashes 2023-12-21 15:05:07 +00:00
Sergey Lyubka aae2954c14 Merge pull request #2541 from cesanta/multitls
default again to current doc defaults
2023-12-21 05:10:42 +00:00
Sergio R. Caprile abab569778 default again to current doc defaults 2023-12-20 18:13:09 -03:00
Sergio R. Caprile 365a731574 Merge pull request #2540 from cesanta/reformat_comments
reformat comments
2023-12-20 16:58:56 -03:00
Sergio R. Caprile 285dc63c5c reformat comments 2023-12-20 16:40:31 -03:00
Sergio R. Caprile 24d9fbffae fix MQTT device 2023-12-20 14:49:25 -03:00
Sergey Lyubka 04178ad0f7 Merge pull request #2538 from cesanta/tls13
Add built-in TLS 1.3 stack: server side, EC support
2023-12-20 16:36:04 +00:00
Sergey Lyubka a0e66fb9e6 Add built-in TLS 1.3 stack: server side, EC support 2023-12-20 11:04:31 +00:00
Sergio R. Caprile df3c216315 Merge pull request #2535 from cesanta/hal
Move clock_init() to hal.h
2023-12-18 16:36:51 -03:00
Sergey Lyubka e9a6bad751 Move clock_init() to hal.h 2023-12-18 19:21:12 +00:00
Sergio R. Caprile 4b7d871048 Merge pull request #2537 from cesanta/stm32driver
stm32 -> stm32f
2023-12-18 16:00:27 -03:00
Sergio R. Caprile 2528dd6425 stm32 -> stm32f 2023-12-18 14:33:49 -03:00
Sergey Lyubka b3fcd9c420 Win32 build fixes 2023-12-17 10:14:08 +00:00
Sergey Lyubka 9270fc0b15 Allow to bypass CSS rebuild 2023-12-16 20:42:34 +00:00
Sergey Lyubka 33aa021e96 Allow to bypass CSS rebuild 2023-12-16 20:39:29 +00:00
Sergey Lyubka 1c78ed1fed Prevent main.css rebuild 2023-12-16 20:19:21 +00:00
Sergey Lyubka 562c318648 Recreate packed fs 2023-12-16 20:11:59 +00:00
Sergey Lyubka 65cb894f0d Change TLS port 2023-12-16 20:01:52 +00:00
Sergey Lyubka bbd27a0a9c MQTT dashboard nitpicks 2023-12-16 19:57:18 +00:00
Sergio R. Caprile 79e138ef28 Merge pull request #2534 from cesanta/stm32driver
stm32 -> stm32f
2023-12-15 22:19:20 -03:00
Sergio R. Caprile 387334c8c2 stm32 -> stm32f 2023-12-15 21:55:52 -03:00
Sergio R. Caprile cd021daf79 Merge pull request #2531 from cesanta/mqttrndis
Add MQTT device through RNDIS example
2023-12-15 13:39:21 -03:00
Sergey Lyubka 536aa1fb62 Fix post-OTA status save 2023-12-15 11:32:46 +00:00
Sergio R. Caprile a14a838dab undo leftover from PR 2508 2023-12-14 11:52:19 -03:00
Sergio R. Caprile ad57731ae1 Add MQTT device through RNDIS example 2023-12-13 17:24:52 -03:00
Sergio R. Caprile ad90b0b7b1 fix for new IDF required version 2023-12-13 17:24:16 -03:00
Sergio R. Caprile 18325cb216 Merge pull request #2529 from cesanta/573
Add Discovery STM32H573I-DK examples
2023-12-13 11:45:43 -03:00
Sergio R. Caprile 8df70ba58b Add Discovery STM32H573I-DK examples 2023-12-13 10:50:28 -03:00
Sergey Lyubka ee43a5389f Merge pull request #2522 from cesanta/mqttd
MQTT Dashboard fixes/updates
2023-12-13 11:53:59 +00:00
Sergio R. Caprile 6192573eb8 Merge pull request #2525 from cesanta/bquiet
Be less verbose on unkown eth types
2023-12-10 16:21:50 -03:00
Sergio R. Caprile 92ecef7bd3 Be less verbose on unkown eth types 2023-12-10 15:17:20 -03:00
Sergio R. Caprile 45d7f4ca7e Merge pull request #2523 from cesanta/am
TLS layer: c->rtls to optimise recvd TLS data
2023-12-10 14:54:25 -03:00
cpq 144c2f443e TLS layer: c->rtls to optimise recvd TLS data 2023-12-10 12:18:07 +00:00
Sergey Lyubka 98d877c5f0 Reenable UART logging to fix the test 2023-12-10 12:17:54 +00:00
Sergio R. Caprile 9de31ba816 updates 2023-12-08 18:36:59 -03:00
Sergio R. Caprile 560561c5b7 fixes 2023-12-08 16:35:03 -03:00
Sergey Lyubka ed2d4c1c2b Merge pull request #2519 from cesanta/prof
Add profiling capability
2023-12-08 18:43:14 +00:00
Sergio R. Caprile e70628cbdc Merge pull request #2518 from cesanta/tls
Move TLS API to tls.h, fix builtin handshake codepath
2023-12-08 14:54:50 -03:00
Sergio R. Caprile 161edec195 Merge pull request #2521 from cesanta/z
update to current TLS API
2023-12-08 14:49:45 -03:00
Sergio R. Caprile 2ba4a74762 update to current API 2023-12-08 14:41:10 -03:00
cpq 693316de44 Add profiling capability 2023-12-08 17:00:42 +00:00
cpq 6e68124405 Move TLS API to tls.h, fix builtin handshake codepath 2023-12-08 00:30:09 +00:00
Sergio R. Caprile c8105cffba Merge pull request #2517 from cesanta/mqttest
refactor matrix build tests, add mqtt-dashboard build tests for embed…
2023-12-07 11:44:41 -03:00
Sergio R. Caprile 21f6944fc7 refactor matrix build tests, add mqtt-dashboard build tests for embedded targets 2023-12-07 11:42:45 -03:00
Sergio R. Caprile 5adcba7ba1 Merge pull request #2515 from cesanta/imxflash
fix for flash boot (ROM changes defaults)
2023-12-06 17:52:49 -03:00
Sergio R. Caprile fc8ba744dc fix for flash boot (ROM changes defaults) 2023-12-06 17:45:22 -03:00
Sergey Lyubka 4fc43cfbf7 Merge pull request #2514 from cesanta/mqtt2
Mock LED settings
2023-12-06 09:43:02 +00:00
cpq 22837f93c8 Mock LED settings 2023-12-06 09:41:48 +00:00
cpq b03ececbbb Disable mbedtls, add flash target 2023-12-06 09:10:46 +00:00
Sergio R. Caprile 708908dce5 Merge pull request #2511 from cesanta/workflow
hw tests
2023-12-04 17:23:10 -03:00
Sergio R. Caprile f33302acc9 hw tests 2023-12-04 17:21:48 -03:00
Sergio R. Caprile 7f8cdf1e79 fs ls 2023-12-01 20:43:39 -03:00
Sergey Lyubka dbc7b5e70b Merge pull request #2509 from cesanta/te
Fix #2460 - sanity check of the Transfer-Encoding: chunked header
2023-12-01 13:48:35 +00:00
cpq 4c445453d3 Fix #2460 - sanity check of the Transfer-Encoding: chunked header 2023-12-01 11:24:32 +00:00
cpq 4b8fb23def Typo in v307 makefile 2023-12-01 08:36:18 +00:00
cpq 253bc64c9e Add zip target 2023-12-01 00:03:31 +00:00
cpq 32d3fc6803 Add zip target 2023-12-01 00:02:48 +00:00
Sergey Lyubka 920875ecc9 Merge pull request #2508 from cesanta/v307
Use V307 flash
2023-12-01 00:01:20 +00:00
cpq 85d3e7f375 Use V307 flash 2023-11-30 23:51:34 +00:00
Sergio R. Caprile 4ceb50d485 more Mongoose-style 2023-11-30 12:16:56 -03:00
Sergey Lyubka 7d2fca9574 Merge pull request #2505 from cesanta/mqttdevice
more Mongoose-style
2023-11-30 11:15:39 +00:00
Sergey Lyubka d88e60a525 Merge pull request #2501 from cesanta/opthwtst
test specific hw example
2023-11-30 11:14:43 +00:00
Sergio R. Caprile 4b4dcb20ca more Mongoose-style 2023-11-29 16:14:09 -03:00
Sergio R. Caprile d4ab0aa7fd Merge pull request #2503 from cesanta/imxflash
Generate flash images for RT10xx
2023-11-29 11:04:38 -03:00
Sergio R. Caprile c02c1130ed Generate flash images
TODO(scaprile): check when and if following is needed

  flash_init();                   // setup pins and clocks to access board flash
2023-11-29 10:47:05 -03:00
Sergio R. Caprile f9bbb37721 Merge pull request #2502 from cesanta/fixcubex
fix text
2023-11-28 20:13:28 -03:00
Sergio R. Caprile 57837f285f fix text 2023-11-28 19:55:27 -03:00
Sergio R. Caprile 3379f11b23 test specific hw example 2023-11-28 19:34:05 -03:00
Sergey Lyubka c74534cf5e Merge pull request #2495 from cesanta/modbus
Add modbus dashboard
2023-11-27 21:31:37 +00:00
cpq 64ca1ad787 Add modbus dashboard 2023-11-27 21:26:19 +00:00
Sergey Lyubka 8022635c04 Merge pull request #2494 from cesanta/mqttws
make Mosquitto happy
2023-11-27 13:13:14 +00:00
Sergio R. Caprile 1f37595200 make Mosquitto happy 2023-11-26 15:38:28 -03:00
Sergio R. Caprile 30738f6638 Merge pull request #2493 from cesanta/wake
Fix #2456 - wake up mongoose thread
2023-11-25 09:27:52 -03:00
cpq bd53e46873 Do not allow invalid UTF8 in method, uri, header names 2023-11-25 11:33:07 +00:00
cpq 122115dbea Fix #2456 - wake up mongoose thread 2023-11-25 08:28:39 +00:00
Sergey Lyubka 28162f8034 Merge pull request #2492 from cesanta/xpro
Fix #2487 - correct GMAC_DCFGR, receive size -> full size
2023-11-25 08:05:52 +00:00
cpq 9cfd3124ef Fix #2487 - correct GMAC_DCFGR, receive size -> full size 2023-11-25 08:04:38 +00:00
Sergey Lyubka 36748ee406 Merge pull request #2486 from cesanta/st_v307
Post-IRQ DMASR flags cleanup
2023-11-25 07:49:32 +00:00
Sergey Lyubka 2a25e449a2 Merge pull request #2476 from cesanta/mqtt-dashboard-part-2
Added MQTT Dashboard support for H723ZG
2023-11-24 10:45:49 +00:00
cpq f2f60a7b3b Post-IRQ DMASR flags cleanup 2023-11-24 10:43:56 +00:00
cpq 82916a1cef Wrap dirty IRQ flag cleanup for v307 into a conditional 2023-11-22 15:41:45 +00:00
Sergey Lyubka f86c14abb3 Merge pull request #2485 from cesanta/ch32v307
Add example for ch32v307
2023-11-22 10:23:48 +00:00
cpq fa76312313 Add example for ch32v307 2023-11-22 10:22:10 +00:00
robert 46687f6fe3 Added MQTT Dashboard support for H723ZG 2023-11-20 02:08:07 -05:00
Sergio R. Caprile 6386cdff50 Merge pull request #2478 from cesanta/mg_bit
BIT -> MG_BIT in drivers
2023-11-16 17:32:49 -03:00
Sergio R. Caprile 7c25c0d6f3 BIT -> MG_BIT 2023-11-16 17:11:04 -03:00
Sergio R. Caprile 8ed57f57cb Merge pull request #2475 from cesanta/rt
Rt
2023-11-15 19:28:36 -03:00
Sergio R. Caprile c8f207c277 rename iMX-RT 2023-11-15 19:27:20 -03:00
Sergio R. Caprile d70d9aff11 fix bits not being reset 2023-11-15 16:33:20 -03:00
cpq 1118c997b5 Nitpick - simple handler changed 2023-11-15 18:41:03 +00:00
Sergio R. Caprile b999275550 fix Teensy PHY code in _up 2023-11-15 10:46:44 -03:00
Sergey Lyubka f549646ddd Merge pull request #2474 from cesanta/rtphy
update PHY code in _up
2023-11-15 08:27:38 +00:00
Sergey Lyubka 5ee8552e6c Merge pull request #2472 from cesanta/mqtt-dashboard
Added MQTT Dashboard
2023-11-14 12:34:03 +00:00
robert b0eb7b6dec Added MQTT Dashboard 2023-11-14 05:52:33 -05:00
Sergio R. Caprile a546d360d0 update PHY code in _up 2023-11-13 23:54:37 -03:00
Sergio R. Caprile 3cc8a98b9d Merge pull request #2473 from cesanta/db
update screenshot and README
2023-11-13 11:22:46 -03:00
Sergio R. Caprile 1032f0ade7 update screenshot and README 2023-11-13 09:55:54 -03:00
cpq eb65d269eb Disable noisy debug 2023-11-13 01:57:59 +00:00
cpq b379f81004 Nit picks 2023-11-13 01:56:50 +00:00
Sergey Lyubka d1b0342a18 Merge pull request #2468 from cesanta/teensy
Add teensy41 example
2023-11-11 21:28:28 +00:00
cpq b815e6bc04 Add teensy41 example 2023-11-11 21:22:00 +00:00
Sergio R. Caprile 81bf65954c Merge pull request #2469 from cesanta/std
standardize build options
2023-11-09 14:44:34 -03:00
Sergio R. Caprile 9cfe72af4a standardize build options 2023-11-09 14:40:12 -03:00
Sergey Lyubka 7c2e1e1339 Merge pull request #2459 from cesanta/openssl
Wrap OpenSSL errors in Mongoose log
2023-11-08 17:08:04 +00:00
Sergio R. Caprile 817115cb6c Merge pull request #2467 from cesanta/std
standardize naming and build options
2023-11-08 14:00:52 -03:00
Sergio R. Caprile 7a3734e54d standardize naming and build options 2023-11-08 13:54:44 -03:00
Sergio R. Caprile 79c3341f74 Merge pull request #2465 from cesanta/std
standardize build options
2023-11-07 17:23:44 -03:00
Sergio R. Caprile abdd14b0a1 standardize build options 2023-11-07 17:05:41 -03:00
Sergio R. Caprile 1f77e53b23 Wrap OpenSSL errors in Mongoose log 2023-11-06 17:24:30 -03:00
Sergey Lyubka a619de0508 Merge pull request #2455 from cesanta/micropython
Add Micropython on ESP32 example
2023-11-04 13:31:44 +00:00
Sergio R. Caprile 8faa11585c Add Micropython on ESP32 example 2023-11-03 19:15:57 -03:00
Sergio R. Caprile 13259f8902 Merge pull request #2452 from cesanta/tlsbuiltin
Make tls_builtin build with current API
2023-11-03 15:02:27 -03:00
Sergio R. Caprile bb5aba2e19 Make tls_builtin build with current API 2023-11-03 14:02:41 -03:00
Sergio R. Caprile 22fc01d5f2 workaround warning in mbedTLS code 2023-11-03 14:02:08 -03:00
Sergio R. Caprile 3b4672c109 workaround warning in mbedTLS code 2023-11-03 10:37:14 -03:00
Sergey Lyubka 351bd42bc4 Merge pull request #2450 from cesanta/micropython
Add Micropython example
2023-11-03 06:35:38 +00:00
Sergio R. Caprile e418da4bd8 Merge pull request #2451 from cesanta/fastclose
Allow fast close
2023-11-02 18:52:11 -03:00
Sergio R. Caprile 18d13f9848 Allow fast close 2023-11-02 18:27:39 -03:00
Sergio R. Caprile ee792db6a3 Add Micropython example 2023-11-02 18:17:33 -03:00
Sergio R. Caprile 129bf93b30 Merge pull request #2448 from cesanta/fixh743f
Fix H743 FreeRTOS early SysTick crash
2023-11-02 15:14:27 -03:00
Sergio R. Caprile 1dda8b9e60 Fix H743 FreeRTOS early SysTick crash 2023-11-02 14:58:52 -03:00
Sergey Lyubka 107e303282 Merge pull request #2445 from cesanta/cmsis-driver
Add driver for CMSIS-Driver
2023-11-02 14:55:16 +00:00
Sergey Lyubka 22f76091a2 Merge pull request #2446 from cesanta/o2
Single bank OTA
2023-11-02 14:55:01 +00:00
cpq 376e5ccaae Single bank OTA 2023-11-02 09:30:55 +00:00
Sergio R. Caprile b792304cf8 Add driver for CMSIS-Driver 2023-11-01 17:26:34 -03:00
Sergio R. Caprile b357485523 Merge pull request #2444 from cesanta/mqttst
improve randomness in non-random test environments
2023-10-31 19:24:26 -03:00
Sergio R. Caprile 07936343c8 improve randomness in non-random environments 2023-10-31 19:17:58 -03:00
Sergio R. Caprile 1b93ca0fb2 Merge pull request #2443 from cesanta/h563
fix H563 FreeRTOS Cortex-M33 port SystemInit
2023-10-31 12:43:36 -03:00
Sergio R. Caprile 18cd18d1e5 fix H563 FreeRTOS Cortex-M33 port SystemInit 2023-10-31 12:40:18 -03:00
Sergio R. Caprile deab6772e8 Merge pull request #2442 from cesanta/nerr
honor ifp->nerr
2023-10-31 12:06:44 -03:00
Sergey Lyubka d6d2d7575a Merge pull request #2441 from cesanta/mqttst
improve MQTT test speed
2023-10-30 23:12:30 +00:00
Sergio R. Caprile d63ec65663 honor ifp->nerr 2023-10-30 16:36:35 -03:00
Sergio R. Caprile f0a927831e improve MQTT test speed 2023-10-30 16:20:55 -03:00
Sergey Lyubka ef16a733d2 Merge pull request #2440 from cesanta/h723
Fix H723 FreeRTOS example failing on some compiler versions with optimizer on
2023-10-30 17:55:29 +00:00
Sergio R. Caprile 9044c02edc Fix H723 FreeRTOS example failing on some compiler versions with optimizer on 2023-10-30 14:13:28 -03:00
Sergey Lyubka 2fa98b00f2 Merge pull request #2437 from cesanta/rt1060
Add RT1060 examples
2023-10-27 20:42:42 +01:00
Sergio R. Caprile 15b4d566e7 Add RT1060 examples 2023-10-27 16:19:12 -03:00
Sergio R. Caprile 6dabbe237d unbreak RT1020 examples after removing IMX prefix 2023-10-25 14:56:29 -03:00
cpq 2caff93f83 Remove IMX, imx prefix for NXP 2023-10-24 17:30:22 +01:00
Sergio R. Caprile 2903937cba fix old trashing 2023-10-23 16:32:51 -03:00
Sergey Lyubka c33fd6a7fb Merge pull request #2429 from cesanta/h723
H7 combo
2023-10-17 10:22:00 +01:00
Sergio R. Caprile 64180e8fe8 minor fixes for H743
Clock H723 at 550MHz, fix RNG initialization
Add FreeRTOS example for H723
Add tests
2023-10-16 17:27:02 -03:00
Sergey Lyubka 001134af89 Merge pull request #2424 from cesanta/unit-test-coverage
Improved test coverage
2023-10-14 10:59:01 +01:00
cpq b91044dea7 Update UM 2023-10-14 10:34:41 +01:00
robert 34639f86b0 Improved test coverage 2023-10-13 07:22:21 -04:00
646 changed files with 138289 additions and 5285 deletions

No files matched your search

+5 -4
View File
@@ -1,9 +1,10 @@
name: CIFuzz name: CIFuzz
on: on:
pull_request: schedule:
paths: - cron: '0 2 * * *' # run at 2 AM UTC
- "mongoose.c" # Allow manual runs
- "mongoose.h" workflow_dispatch:
jobs: jobs:
Fuzzing: Fuzzing:
runs-on: ubuntu-latest runs-on: ubuntu-latest
+4 -7
View File
@@ -2,13 +2,10 @@ name: "CodeQL Scanning"
# https://github.com/github/codeql-action # https://github.com/github/codeql-action
on: on:
push: schedule:
paths: - cron: '30 2 * * *' # run at 2:30 AM UTC
- "Makefile" # Allow manual runs
- "mongoose.c" workflow_dispatch:
- "mongoose.h"
- test/unit-test
- test/mip-test
env: env:
IPV6: 0 IPV6: 0
+453
View File
@@ -0,0 +1,453 @@
name: Full build and test
on:
schedule:
- cron: '0 1 * * *' # run at 1 AM UTC
# Allow manual runs
workflow_dispatch:
env:
IPV6: 0
jobs:
linux:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
cc: [gcc, clang, g++, clang++]
target: [test, mip_test]
ssl: ["", MBEDTLS, OPENSSL]
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:
CC: ${{ matrix.cc }}
SSL: ${{ matrix.ssl }}
TFLAGS: ${{ matrix.select }}
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: ./test/setup_ga_network.sh && sudo apt -y update ; sudo apt -y install libmbedtls-dev && make ${{ matrix.target }}
s390:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: sudo apt -y update ; sudo apt -y install qemu binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make s390
armhf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: sudo apt -y update ; sudo apt -y install qemu binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make armhf
linux2:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: sudo apt -y update ; sudo apt -y install libmbedtls-dev valgrind
- run: make unamalgamated && make valgrind && make mg_prefix
macos:
runs-on: macos-latest
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 -Wno-sign-conversion # Workaround for MbedTLS 3.5.0
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: brew install jq mbedtls openssl
- run: make test ASAN_OPTIONS= MBEDTLS=`echo /usr/local/Cellar/mbedtls*/*` OPENSSL=`echo /usr/local/Cellar/openssl*/*`
- run: make mg_prefix
windows:
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:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: make ${{ matrix.target }}
arm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: make arm
riscv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: make riscv
examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: sudo apt -y install libmbedtls-dev libpcap-dev
- run: make examples
- run: make clean
examples_win:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
# - uses: egor-tensin/setup-mingw@v2
# with:
# platform: x64
- name: test they build
run: make examples_win
shell: cmd
- name: test they clean
run: make clean_examples_win
shell: cmd
examples_mac:
runs-on: macos-latest
env: { HOMEBREW_NO_AUTO_UPDATE: 1 }
steps:
- uses: actions/checkout@v3
- run: make examples_mac
- run: make clean_examples_mac
arduino:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: echo # nothing specific to install or do
- run: make -C examples/arduino arduino-xiao
# - run: make -C examples/arduino arduino-nano
- run: make -C examples/arduino clean
generic_examples:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: esp32/uart-bridge
- path: esp32/micropython
- path: esp8266/http-client-server
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^src|^examples/${{ matrix.example.path }}'; then echo GO=1 >> $GITHUB_ENV ; fi
- run: echo # nothing specific to install or do
- run: make -C examples/${{ matrix.example.path }} build
generic_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: microchip/same54-xpro/mqtt-client
- path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
- path: nxp/nxp-mimxrt1020-azurertos
- path: nxp/nxp-frdmk66f-freertos
- path: nxp/nxp-lpcxpresso54s018m-freertos
- path: nxp/nxp-mimxrt1020-freertos
- path: nxp/nxp-evkbimxrt1050-lwip-freertos
- path: nxp/nxp-evkmimxrt1020-lwip-freertos
- path: nxp/nxp-evkmimxrt1024-lwip-freertos
- path: nxp/nxp-evkmimxrt1060-lwip-freertos
- path: nxp/nxp-evkmimxrt1064-lwip-freertos
- path: nxp/nxp-evkmimxrt1160-cm7-lwip-freertos
- path: nxp/nxp-evkmimxrt1170-cm7-lwip-freertos
- path: nxp/nxp-frdmk64f-lwip-freertos
- path: nxp/nxp-frdmk66f-lwip-freertos
- path: nxp/nxp-lpcxpresso54018-lwip-freertos
- path: nxp/nxp-lpcxpresso54608-lwip-freertos
- path: nxp/nxp-lpcxpresso54618-lwip-freertos
- path: nxp/nxp-lpcxpresso54628-lwip-freertos
- path: nxp/nxp-twrk65f180m-lwip-freertos
- path: nxp/nxp-twrkv58f220m-lwip-freertos
- path: rp2040/pico-rmii
- path: stm32/nucleo-f429zi-make-baremetal-builtin-rndis
- path: stm32/nucleo-f746zg-make-baremetal-builtin-rndis
- path: stm32/nucleo-g031-make-baremetal-builtin
- path: ti/ti-ek-tm4c1294xl-http-server
- path: ti/ek-tm4c1294xl-make-baremetal-builtin-rndis
name: ${{ matrix.example.path }}
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/${{ matrix.example.path }} build
device_dashboard_examples:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: esp32/device-dashboard
name: ${{ matrix.example.path }}
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: echo # nothing specific to install or do
- run: make -C examples/${{ matrix.example.path }} build
device_dashboard_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: microchip/same54-xpro/device-dashboard
- path: nxp/rt1020-evk-make-baremetal-builtin
- path: nxp/rt1020-evk-make-freertos-builtin
- path: nxp/rt1060-evk-make-baremetal-builtin
- path: nxp/rt1060-evk-make-freertos-builtin
- path: rp2040/pico-rndis-dashboard
- path: rp2040/pico-w
- path: rp2040/pico-w5500
- path: stm32/nucleo-f429zi-make-baremetal-builtin
- path: stm32/nucleo-f429zi-make-freertos-builtin
- 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-cmsis_driver
- path: stm32/nucleo-h563zi-make-baremetal-builtin
- path: stm32/nucleo-h563zi-make-freertos-builtin
- path: stm32/nucleo-h723zg-make-freertos-builtin
- path: stm32/nucleo-h743zi-make-baremetal-builtin
- path: stm32/nucleo-h743zi-make-freertos-builtin
- path: stm32/stm32h573i-dk-make-baremetal-builtin
- path: stm32/stm32h573i-dk-make-freertos-builtin
- path: ti/ek-tm4c1294xl-make-baremetal-builtin
- path: ti/ek-tm4c1294xl-make-freertos-builtin
name: ${{ matrix.example.path }}
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/${{ matrix.example.path }} build
# mqtt_dashboard_examples:
# runs-on: ubuntu-latest
# strategy:
# fail-fast: false
# matrix:
# example:
# - path:
# name: ${{ matrix.example.path }}
# steps:
# - uses: actions/checkout@v3
# with: { fetch-depth: 2 }
# - run: echo # nothing specific to install or do
# - run: make -C examples/${{ matrix.example.path }} build
mqtt_dashboard_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: rp2040/pico-rndis-device
name: ${{ matrix.example.path }}
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/${{ matrix.example.path }} build
# dual_examples:
# runs-on: ubuntu-latest
# strategy:
# fail-fast: false
# matrix:
# example:
# - path:
# name: ${{ matrix.example.path }}
# steps:
# - uses: actions/checkout@v3
# with: { fetch-depth: 2 }
# - run: echo # nothing specific to install or do
# - run: make -C examples/${{ matrix.example.path }} device_dashboard
# - run: make -C examples/${{ matrix.example.path }} mqtt_dashboard
dual_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: stm32/nucleo-h723zg-make-baremetal-builtin
name: ${{ matrix.example.path }}
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/${{ matrix.example.path }} device_dashboard
- run: make -C examples/${{ matrix.example.path }} mqtt_dashboard
cube_examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: echo # nothing specific to install or do
- run: cd test/cube && make
zephyr_examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/zephyr init
- name: minify manifest
uses: mikefarah/yq@master
with:
cmd: yq -i eval '(.manifest.defaults, .manifest.remotes, .manifest.projects[] | select(.name == "cmsis" or .name == "hal_stm32" or .name == "mbedtls" or .name == "mcuboot" or .name == "picolibc" | del(.null) ), .manifest.self) as $i ireduce({};setpath($i | path; $i)) | del(.manifest.projects.[].null) | del(..|select(length==0))' examples/zephyr/zephyrproject/zephyr/west.yml
- run: make -C examples/zephyr update
- run: make -C examples/zephyr/device-dashboard build
- run: make -C examples/zephyr/http-client build
- run: make -C examples/zephyr/http-server build
- run: make -C examples/zephyr/mqtt-aws-client build
- run: make -C examples/zephyr/websocket-server build
test_f7:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/stm32/nucleo-f746zg-make-baremetal-builtin 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}}
- run: make -C examples/stm32/nucleo-f746zg-make-freertos-tcp test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- name: Cube
run: echo # nothing specific to install or do
- run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
- run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
- 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:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/stm32/nucleo-f429zi-make-baremetal-builtin 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}}
- name: Cube
run: echo # nothing specific to install or do
- run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
- run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
- run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
test_h743:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/stm32/nucleo-h743zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C examples/stm32/nucleo-h743zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- name: Cube
run: echo # nothing specific to install or do
- run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
- run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
test_h723:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/stm32/nucleo-h723zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C examples/stm32/nucleo-h723zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_h5:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/stm32/nucleo-h563zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- 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 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/nxp/rt1020-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C examples/nxp/rt1020-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_rt1060:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/nxp/rt1060-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C examples/nxp/rt1060-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_tm4c:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/ti/ek-tm4c1294xl-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C examples/ti/ek-tm4c1294xl-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_same54:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/microchip/same54-xpro/device-dashboard test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- run: make -C examples/microchip/same54-xpro/mqtt-client test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_pico_w5500:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: SDK
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
- run: make -C examples/rp2040/pico-w5500 test VCON_API_KEY=${{secrets.VCON_API_KEY}}
+674
View File
@@ -0,0 +1,674 @@
name: Build and test - essentials
on:
push:
paths:
- "**Makefile"
- "**.c"
- "**.h"
env:
IPV6: 0
jobs:
linux:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
cc: [gcc, clang++]
target: [test, mip_test]
ssl: ["", MBEDTLS]
name: linux ${{ matrix.target }} CC=${{ matrix.cc }} SSL=${{ matrix.ssl }}}
env:
CC: ${{ matrix.cc }}
SSL: ${{ matrix.ssl }}
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^test|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: ./test/setup_ga_network.sh && sudo apt -y update ; sudo apt -y install libmbedtls-dev && make ${{ matrix.target }}
s390:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^test|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: sudo apt -y update ; sudo apt -y install qemu binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make s390
armhf:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^test|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: sudo apt -y update ; sudo apt -y install qemu binfmt-support qemu-user-static && docker run --rm --privileged multiarch/qemu-user-static --reset -p yes && make armhf
linux2:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^test|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: sudo apt -y update ; sudo apt -y install libmbedtls-dev valgrind
- if: ${{ env.GO == 1 }}
run: make unamalgamated && make valgrind && make mg_prefix
macos:
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
ssl: ["", MBEDTLS]
name: macos SSL=${{ matrix.ssl }}
env:
SSL: ${{ matrix.ssl }}
TFLAGS: -DNO_SNTP_CHECK -Wno-sign-conversion # Workaround for MbedTLS 3.5.0
HOMEBREW_NO_AUTO_UPDATE: 1
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^test|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: brew install jq mbedtls openssl
- if: ${{ env.GO == 1 }}
run: make test ASAN_OPTIONS= MBEDTLS=`echo /usr/local/Cellar/mbedtls*/*` OPENSSL=`echo /usr/local/Cellar/openssl*/*`
- if: ${{ env.GO == 1 }}
run: make mg_prefix
windows:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [vc98, vc22, mingw++]
name: windows ${{ matrix.target }}
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^test|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: make ${{ matrix.target }}
arm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: make arm
riscv:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: make riscv
examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: sudo apt -y install libmbedtls-dev libpcap-dev
- run: make examples
- run: make clean
examples_win:
runs-on: windows-latest
steps:
- uses: actions/checkout@v3
# - uses: egor-tensin/setup-mingw@v2
# with:
# platform: x64
- name: test they build
run: make examples_win
shell: cmd
- name: test they clean
run: make clean_examples_win
shell: cmd
examples_mac:
runs-on: macos-latest
env: { HOMEBREW_NO_AUTO_UPDATE: 1 }
steps:
- uses: actions/checkout@v3
- run: make examples_mac
- run: make clean_examples_mac
arduino:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/arduino" ; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/arduino'; then
make -C examples/arduino arduino-xiao
fi
# - if: ${{ env.GO == 1 }}
# run: |
# if ./test/match_changed_files.sh '^src|examples/arduino'; then
# make -C examples/arduino arduino-nano
# fi
- if: ${{ env.GO == 1 }}
run: make -C examples/arduino clean
generic_examples:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: esp32/uart-bridge
- path: esp8266/http-client-server
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^src|^examples/${{ matrix.example.path }}'; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: |
# nothing specific to install or do
- if: ${{ env.GO == 1 }}
run: make -C examples/${{ matrix.example.path }} build
generic_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
- path: stm32/nucleo-g031-make-baremetal-builtin
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^src|^examples/${{ matrix.example.path }}'; 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/${{ matrix.example.path }} build
device_dashboard_examples:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: esp32/device-dashboard
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: make -C examples/${{ matrix.example.path }} build
device_dashboard_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: microchip/same54-xpro/device-dashboard
- path: nxp/rt1020-evk-make-baremetal-builtin
- path: nxp/rt1020-evk-make-freertos-builtin
- path: nxp/rt1060-evk-make-baremetal-builtin
- path: nxp/rt1060-evk-make-freertos-builtin
- path: rp2040/pico-rndis-dashboard
- path: rp2040/pico-w
- path: rp2040/pico-w5500
- path: stm32/nucleo-f429zi-make-baremetal-builtin
- path: stm32/nucleo-f429zi-make-freertos-builtin
- 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-cmsis_driver
- path: stm32/nucleo-h563zi-make-baremetal-builtin
- path: stm32/nucleo-h563zi-make-freertos-builtin
- path: stm32/nucleo-h723zg-make-freertos-builtin
- path: stm32/nucleo-h743zi-make-baremetal-builtin
- path: stm32/nucleo-h743zi-make-freertos-builtin
- path: stm32/stm32h573i-dk-make-baremetal-builtin
- path: stm32/stm32h573i-dk-make-freertos-builtin
- path: ti/ek-tm4c1294xl-make-baremetal-builtin
- path: ti/ek-tm4c1294xl-make-freertos-builtin
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: make -C examples/${{ matrix.example.path }} build
# mqtt_dashboard_examples:
# runs-on: ubuntu-latest
# strategy:
# fail-fast: false
# matrix:
# example:
# - path:
# name: ${{ matrix.example.path }}
# env:
# GO: 0
# steps:
# - uses: actions/checkout@v3
# with: { fetch-depth: 2 }
# - run: |
# if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
# echo GO=1 >> $GITHUB_ENV
# # nothing specific to install or do
# fi
# - if: ${{ env.GO == 1 }}
# run: make -C examples/${{ matrix.example.path }} build
mqtt_dashboard_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: rp2040/pico-rndis-device
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: make -C examples/${{ matrix.example.path }} build
# dual_examples:
# runs-on: ubuntu-latest
# strategy:
# fail-fast: false
# matrix:
# example:
# - path:
# name: ${{ matrix.example.path }}
# env:
# GO: 0
# steps:
# - uses: actions/checkout@v3
# with: { fetch-depth: 2 }
# - run: |
# if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
# echo GO=1 >> $GITHUB_ENV
# # nothing specific to install or do
# fi
# - if: ${{ env.GO == 1 }}
# run: |
# if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
# make -C examples/${{ matrix.example.path }} device_dashboard
# fi
# - if: ${{ env.GO == 1 }}
# run: |
# if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
# make -C examples/${{ matrix.example.path }} mqtt_dashboard
# fi
dual_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: stm32/nucleo-h723zg-make-baremetal-builtin
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
make -C examples/${{ matrix.example.path }} device_dashboard
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
make -C examples/${{ matrix.example.path }} mqtt_dashboard
fi
cube_examples:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|examples/stm32/nucleo-*-cube-*'; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: cd test/cube && make
test_f7:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32|examples/nxp|examples/ti|examples/microchip/same" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
# always run baremetal on core or dashboard or specific example changes; other examples only on specific example changes
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-f746zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f746zg-make-freertos-builtin'; then
make -C examples/stm32/nucleo-f746zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f746zg-make-freertos-tcp'; then
make -C examples/stm32/nucleo-f746zg-make-freertos-tcp test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- name: Cube
run: |
echo GO=0 >> $GITHUB_ENV
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f7.*-cube-'; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f746zg-cube-baremetal-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f746zg-cube-freertos-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f746zg-cube-freertos-lwip'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
fi
test_f4:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-f4.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-f429zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f429zi-make-freertos-builtin'; then
make -C examples/stm32/nucleo-f429zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- name: Cube
run: |
echo GO=0 >> $GITHUB_ENV
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f4.*-cube-'; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f429zi-cube-baremetal-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f429zi-cube-freertos-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-f429zi-cube-freertos-lwip'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
fi
test_h743:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h74.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h743zi-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-h743zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-h743zi-make-freertos-builtin'; then
make -C examples/stm32/nucleo-h743zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- name: Cube
run: |
echo GO=0 >> $GITHUB_ENV
if ./test/match_changed_files.sh 'examples/stm32/nucleo-h74.*-cube-'; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-h743zi-cube-baremetal-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-h743zi-cube-freertos-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
fi
test_h723:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h72.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h723zg-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-h723zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-h723zg-make-freertos-builtin'; then
make -C examples/stm32/nucleo-h723zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_h5:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h5.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h563zi-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-h563zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/stm32/nucleo-h563zi-make-freertos-builtin'; then
make -C examples/stm32/nucleo-h563zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_rt1020:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/nxp/rt1020.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1020-evk-make-baremetal-builtin'; then
make -C examples/nxp/rt1020-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/nxp/rt1020-evk-make-freertos-builtin'; then
make -C examples/nxp/rt1020-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_rt1060:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/nxp/rt1060.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1060-evk-make-baremetal-builtin'; then
make -C examples/nxp/rt1060-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/nxp/rt1060-evk-make-freertos-builtin'; then
make -C examples/nxp/rt1060-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_tm4c:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/ti/ek-tm4c.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/ti/ek-tm4c1294xl-make-baremetal-builtin'; then
make -C examples/ti/ek-tm4c1294xl-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/ti/ek-tm4c1294xl-make-freertos-builtin'; then
make -C examples/ti/ek-tm4c1294xl-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_same54:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/microchip/same54-xpro" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/microchip/same54-xpro/device-dashboard'; then
make -C examples/microchip/same54-xpro/device-dashboard test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh 'examples/microchip/same54-xpro/mqtt-client'; then
make -C examples/microchip/same54-xpro/mqtt-client test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_pico_w5500:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: SDK
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/rp2040/pico-w5500" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: make -C examples/rp2040/pico-w5500 test VCON_API_KEY=${{secrets.VCON_API_KEY}}
+513 -158
View File
@@ -1,10 +1,6 @@
name: build name: original workflow, updated
on: on:
push: workflow_dispatch:
paths:
- "**Makefile"
- "**.c"
- "**.h"
env: env:
IPV6: 0 IPV6: 0
jobs: jobs:
@@ -78,7 +74,7 @@ jobs:
name: macos SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }} name: macos SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
env: env:
SSL: ${{ matrix.ssl }} SSL: ${{ matrix.ssl }}
TFLAGS: ${{ matrix.select }} -DNO_SNTP_CHECK TFLAGS: ${{ matrix.select }} -DNO_SNTP_CHECK -Wno-sign-conversion # Workaround for MbedTLS 3.5.0
HOMEBREW_NO_AUTO_UPDATE: 1 HOMEBREW_NO_AUTO_UPDATE: 1
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@@ -149,154 +145,60 @@ jobs:
- run: make clean_examples_mac - run: make clean_examples_mac
test_f7:
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/stm32/nucleo-f7.*-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-f746zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }}
run: make -C examples/stm32/nucleo-f746zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }}
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:
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/stm32/nucleo-f4.*-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-f429zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }}
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:
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-h7.*-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-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:
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/ti/ek-tm4c" ; 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/ti/ek-tm4c1294xl-baremetal test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }}
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:
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/rp2040/pico-w5500" ; 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/rp2040/pico-w5500 test VCON_API_KEY=${{secrets.VCON_API_KEY}}
arduino: arduino:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
GO: 0
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 '^examples/arduino|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi - run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/arduino" ; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/arduino arduino-xiao run: |
if ./test/match_changed_files.sh '^src|examples/arduino'; then
make -C examples/arduino arduino-xiao
fi
# - if: ${{ env.GO == 1 }} # - if: ${{ env.GO == 1 }}
# run: make -C examples/arduino arduino-nano # run: |
# if ./test/match_changed_files.sh '^src|examples/arduino'; then
# make -C examples/arduino arduino-nano
# fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/arduino clean run: make -C examples/arduino clean
matrix_examples: generic_examples:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
example: example:
- path: esp32/device-dashboard
- path: esp32/uart-bridge - path: esp32/uart-bridge
- path: esp32/micropython
- path: esp8266/http-client-server - path: esp8266/http-client-server
- path: microchip/same54-xpro/device-dashboard name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^src|^examples/${{ matrix.example.path }}'; then echo GO=1 >> $GITHUB_ENV ; fi
- if: ${{ env.GO == 1 }}
run: |
# nothing specific to install or do
- if: ${{ env.GO == 1 }}
run: make -C examples/${{ matrix.example.path }} build
generic_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: microchip/same54-xpro/mqtt-client - path: microchip/same54-xpro/mqtt-client
- path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos - path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
- path: nxp/nxp-mimxrt1020-azurertos - path: nxp/nxp-mimxrt1020-azurertos
@@ -316,54 +218,217 @@ 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: rp2040/pico-rmii - path: rp2040/pico-rmii
- path: rp2040/pico-rndis-dashboard
- path: rp2040/pico-w
- 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-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-f746zg-make-baremetal-builtin-rndis
- path: stm32/nucleo-g031-make-baremetal-builtin - 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/ti-ek-tm4c1294xl-http-server
- path: ti/ek-tm4c1294xl-baremetal - path: ti/ek-tm4c1294xl-make-baremetal-builtin-rndis
- path: ti/ek-tm4c1294xl-freertos
name: ${{ matrix.example.path }} name: ${{ matrix.example.path }}
env:
GO: 0
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/${{ matrix.example.path }}'; then echo GO=1 >> $GITHUB_ENV ; fi - run: if ./test/match_changed_files.sh '^src|^examples/${{ matrix.example.path }}'; 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/${{ matrix.example.path }} build run: make -C examples/${{ matrix.example.path }} build
cube_examples: device_dashboard_examples:
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: esp32/device-dashboard
name: ${{ matrix.example.path }}
env:
GO: 0
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-*-cube-*'; then echo GO=1 >> $GITHUB_ENV ; fi - run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: make -C examples/${{ matrix.example.path }} build
device_dashboard_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: microchip/same54-xpro/device-dashboard
- path: nxp/rt1020-evk-make-baremetal-builtin
- path: nxp/rt1020-evk-make-freertos-builtin
- path: nxp/rt1060-evk-make-baremetal-builtin
- path: nxp/rt1060-evk-make-freertos-builtin
- path: rp2040/pico-rndis-dashboard
- path: rp2040/pico-w
- path: rp2040/pico-w5500
- path: stm32/nucleo-f429zi-make-baremetal-builtin
- path: stm32/nucleo-f429zi-make-freertos-builtin
- 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-cmsis_driver
- path: stm32/nucleo-h563zi-make-baremetal-builtin
- path: stm32/nucleo-h563zi-make-freertos-builtin
- path: stm32/nucleo-h723zg-make-freertos-builtin
- path: stm32/nucleo-h743zi-make-baremetal-builtin
- path: stm32/nucleo-h743zi-make-freertos-builtin
- path: stm32/stm32h573i-dk-make-baremetal-builtin
- path: stm32/stm32h573i-dk-make-freertos-builtin
- path: ti/ek-tm4c1294xl-make-baremetal-builtin
- path: ti/ek-tm4c1294xl-make-freertos-builtin
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: make -C examples/${{ matrix.example.path }} build
# mqtt_dashboard_examples:
# runs-on: ubuntu-latest
# strategy:
# fail-fast: false
# matrix:
# example:
# - path:
# name: ${{ matrix.example.path }}
# env:
# GO: 0
# steps:
# - uses: actions/checkout@v3
# with: { fetch-depth: 2 }
# - run: |
# if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
# echo GO=1 >> $GITHUB_ENV
# # nothing specific to install or do
# fi
# - if: ${{ env.GO == 1 }}
# run: make -C examples/${{ matrix.example.path }} build
mqtt_dashboard_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: rp2040/pico-rndis-device
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: make -C examples/${{ matrix.example.path }} build
# dual_examples:
# runs-on: ubuntu-latest
# strategy:
# fail-fast: false
# matrix:
# example:
# - path:
# name: ${{ matrix.example.path }}
# env:
# GO: 0
# steps:
# - uses: actions/checkout@v3
# with: { fetch-depth: 2 }
# - run: |
# if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
# echo GO=1 >> $GITHUB_ENV
# # nothing specific to install or do
# fi
# - if: ${{ env.GO == 1 }}
# run: |
# if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
# make -C examples/${{ matrix.example.path }} device_dashboard
# fi
# - if: ${{ env.GO == 1 }}
# run: |
# if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
# make -C examples/${{ matrix.example.path }} mqtt_dashboard
# fi
dual_examples_arm:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: stm32/nucleo-h723zg-make-baremetal-builtin
name: ${{ matrix.example.path }}
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
make -C examples/${{ matrix.example.path }} device_dashboard
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
make -C examples/${{ matrix.example.path }} mqtt_dashboard
fi
cube_examples:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: |
if ./test/match_changed_files.sh '^src|examples/stm32/nucleo-*-cube-*'; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: cd test/cube && make run: cd test/cube && make
zephyr_examples: zephyr_examples:
runs-on: ubuntu-latest runs-on: ubuntu-latest
env:
GO: 0
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/.*.[ch]|^examples/zephyr'; then sudo apt -y update ; sudo apt -y install gcc-arm-none-eabi ; fi - run: |
if ./test/match_changed_files.sh '^src/.*.[ch]|^examples/zephyr'; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/zephyr init run: make -C examples/zephyr init
- name: minify manifest - name: minify manifest
@@ -383,3 +448,293 @@ jobs:
run: make -C examples/zephyr/mqtt-aws-client build run: make -C examples/zephyr/mqtt-aws-client build
- if: ${{ env.GO == 1 }} - if: ${{ env.GO == 1 }}
run: make -C examples/zephyr/websocket-server build run: make -C examples/zephyr/websocket-server build
test_f7:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-f7.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-f746zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-make-freertos-builtin'; then
make -C examples/stm32/nucleo-f746zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-make-freertos-tcp'; then
make -C examples/stm32/nucleo-f746zg-make-freertos-tcp test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- name: Cube
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
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-cube-baremetal-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-cube-freertos-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-cube-freertos-lwip'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
fi
test_f4:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-f4.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-f429zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-make-freertos-builtin'; then
make -C examples/stm32/nucleo-f429zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- name: Cube
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
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-cube-baremetal-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-cube-freertos-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-cube-freertos-lwip'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
fi
test_h743:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h74.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h743zi-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-h743zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h743zi-make-freertos-builtin'; then
make -C examples/stm32/nucleo-h743zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- name: Cube
run: |
echo GO=0 >> $GITHUB_ENV
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h74.*-cube-'; then
echo GO=1 >> $GITHUB_ENV
# nothing specific to install or do
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h743zi-cube-baremetal-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h743zi-cube-freertos-builtin'; then
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
fi
test_h723:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h72.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h723zg-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-h723zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h723zg-make-freertos-builtin'; then
make -C examples/stm32/nucleo-h723zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_h5:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h5.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h563zi-make-baremetal-builtin'; then
make -C examples/stm32/nucleo-h563zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h563zi-make-freertos-builtin'; then
make -C examples/stm32/nucleo-h563zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_rt1020:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/nxp/rt1020.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
#if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1020-evk-make-baremetal-builtin'; then
if ./test/match_changed_files.sh '^src|examples/nxp/rt1020-evk-make-baremetal-builtin'; then
make -C examples/nxp/rt1020-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
#if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1020-evk-make-freertos-builtin'; then
if ./test/match_changed_files.sh '^src|examples/nxp/rt1020-evk-make-freertos-builtin'; then
make -C examples/nxp/rt1020-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_rt1060:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 3 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/nxp/rt1060.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1060-evk-make-baremetal-builtin'; then
make -C examples/nxp/rt1060-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
- if: ${{ env.GO == 1 }}
run: |
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1060-evk-make-freertos-builtin'; then
make -C examples/nxp/rt1060-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
fi
test_tm4c:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/ti/ek-tm4c.*-make-" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: make -C examples/ti/ek-tm4c1294xl-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
- if: ${{ env.GO == 1 }}
run: make -C examples/ti/ek-tm4c1294xl-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
test_same54:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: baremetal
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/microchip/same54-xpro" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- 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:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- name: SDK
run: |
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/rp2040/pico-w5500" ; then
echo GO=1 >> $GITHUB_ENV
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
fi
- if: ${{ env.GO == 1 }}
run: make -C examples/rp2040/pico-w5500 test VCON_API_KEY=${{secrets.VCON_API_KEY}}
+13 -5
View File
@@ -14,7 +14,7 @@ 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
ASAN_OPTIONS ?= detect_leaks=1 ASAN_OPTIONS ?= detect_leaks=1
EXAMPLES := $(dir $(wildcard examples/*/Makefile)) EXAMPLES := $(dir $(wildcard examples/*/Makefile))
EXAMPLES_MAC := $(filter-out examples/mip-pcap/ examples/mip-tap/, $(EXAMPLES)) EXAMPLES_MAC := $(filter-out examples/mip-pcap/ examples/mip-tap/ examples/micropython/, $(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
@@ -90,6 +90,7 @@ musl: CC = $(DOCKER) mdashnet/cc1 gcc
musl: RUN = $(DOCKER) mdashnet/cc1 musl: RUN = $(DOCKER) mdashnet/cc1
# Make sure we can build from unamalgamated sources. # Make sure we can build from unamalgamated sources.
unamalgamated: CFLAGS += -DMG_ENABLE_MD5=1
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
@@ -119,6 +120,13 @@ valgrind: Makefile mongoose.h mongoose.c
$(CC) $(SRCS) $(VALGRIND_CFLAGS) $(LDFLAGS) -g -o unit_test $(CC) $(SRCS) $(VALGRIND_CFLAGS) $(LDFLAGS) -g -o unit_test
$(VALGRIND_RUN) ./unit_test $(VALGRIND_RUN) ./unit_test
misra:
cppcheck --addon=misra -DMG_ARCH=MG_ARCH_UNIX -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_SSI=1 mongoose.c >/tmp/x 2>&1
cppcheck --addon=misra -DMG_ARCH=MG_ARCH_CUSTOM -DMG_ENABLE_LINES=1 -DTLS=MG_TLS_BUILTIN -DMG_ENABLE_TCPIP=1 mongoose.c >>/tmp/x 2>&1
cppcheck --addon=misra --clang -DMG_ENABLE_LINES=1 -DTLS=MG_TLS_BUILTIN -DMG_ENABLE_TCPIP=1 mongoose.c >>/tmp/x 2>&1
cppcheck --addon=misra --clang -DMG_ENABLE_LINES=1 -DMG_ENABLE_SSI=1 mongoose.c >>/tmp/x 2>&1
less /tmp/x
armhf: ASAN= armhf: ASAN=
armhf: IPV6=0 armhf: IPV6=0
armhf: CC = $(DOCKER) mdashnet/armhf cc armhf: CC = $(DOCKER) mdashnet/armhf cc
@@ -131,11 +139,11 @@ s390: CC = $(DOCKER) mdashnet/s390 cc
s390: RUN = $(DOCKER) mdashnet/s390 s390: RUN = $(DOCKER) mdashnet/s390
s390: test s390: test
arm: DEFS += -DMG_ENABLE_FILE=0 -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB arm: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB
arm: mongoose.h $(SRCS) arm: mongoose.h $(SRCS)
$(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0 $(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0
riscv: DEFS += -DMG_ENABLE_FILE=0 -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB riscv: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB
riscv: mongoose.h $(SRCS) riscv: mongoose.h $(SRCS)
$(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test $(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test
@@ -173,10 +181,10 @@ uninstall:
rm -rf $(DESTDIR)$(LIBDIR)/libmongoose.a $(DESTDIR)$(LIBDIR)/libmongoose.so.$(VERSION) $(DESTDIR)$(INCLUDEDIR)/mongoose.h $(DESTDIR)$(LIBDIR)/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/drivers/*.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/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@ (export LC_ALL=C ; 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_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')> $@ (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/sha256.h src/tls_aes128.h src/tls_uecc.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/profile.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
+66 -7
View File
@@ -19,23 +19,82 @@ robust, and easy. Features include:
- 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, SNTP, 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 for mbedTLS and OpenSSL
- Asynchronous DNS resolver - Asynchronous DNS resolver
- Tiny static and run-time footprint - Tiny static and run-time footprint
- Source code is both ISO C and ISO C++ compliant - Source code is both ISO C and ISO C++ compliant
- 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 in any environment with socket API, like LwIP, Zephyr, Azure
- 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
- Available drivers: STM32 F4, F7, H5, H7; NXP RT1020; TI TM4C; Microchip SAME54; Wiznet W5500 - Available drivers: STM32F, STM32H; NXP RT1xxx; TI TM4C; Microchip SAME54; Wiznet W5500
- A complete Web device dashboard on bare metal - A complete Web device dashboard on bare metal
[Nucleo-F429ZI](examples/stm32/nucleo-f429zi-baremetal) 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 TLS 1.3 server-side support
- Does not depend on any other software to implement networking
- Built-in firmware updates for STM32H5, STM32H7, and more coming - 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/)
## Usage
# Commercial use Create a simple web server that serves a directory. The behavior of the
HTTP server is specified by its event handler function:
```c
#include "mongoose.h"
int main(void) {
struct mg_mgr mgr; // Declare event manager
mg_mgr_init(&mgr); // Initialise event manager
mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, NULL); // Setup listener
for (;;) {
mg_mgr_poll(&mgr, 1000); // Run an infinite event loop
}
return 0;
}
// HTTP server event handler function
void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
struct mg_http_serve_opts opts = { .root_dir = "./web_root/" };
mg_http_serve_dir(c, hm, &opts);
}
}
```
HTTP server implements a REST API that returns current time. JSON formatting:
```c
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/api/time/get")) {
mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("time"), time(NULL));
} else {
mg_http_reply(c, 500, "", "{%m:%m}\n", MG_ESC("error"), MG_ESC("Unsupported URI"));
}
}
}
```
MQTT client that subscribes to a topic `aa/bb` and prints all incoming messages:
```c
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_MQTT_OPEN) {
struct mg_mqtt_opts opts = {.qos = 1, .topic = mg_str("aa/bb")};
mg_mqtt_sub(c, &opts);
} else if (ev == MG_EV_MQTT_MSG) {
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
printf("Topic: %.*s, Message: %.*s",
mm->topic.len, mm->topic.ptr,
mm->data.len, mm->data.ptr);
}
}
```
## Commercial use
- Mongoose is used by hundreds of businesses, from Fortune500 giants like - Mongoose is used by hundreds of businesses, from Fortune500 giants like
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses
- Used to solve a wide range of business needs, like implementing Web UI - Used to solve a wide range of business needs, like implementing Web UI
@@ -49,7 +108,7 @@ robust, and easy. Features include:
services](https://mongoose.ws/integration/) - 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
We take security seriously: We take security seriously:
1. Mongoose repository runs a 1. Mongoose repository runs a
@@ -75,7 +134,7 @@ We take security seriously:
of which we get notified and in case of any issue, act similar to (3). of which we get notified and in case of any issue, act similar to (3).
# Contributions ## Contributions
Contributions are welcome! Please follow the guidelines below: Contributions are welcome! Please follow the guidelines below:
@@ -0,0 +1,11 @@
#pragma once
#define MG_ARCH MG_ARCH_NEWLIB // Use ARM toolchain
#define MG_ENABLE_TCPIP 1 // Enable built-in network stack
#define MG_ENABLE_DRIVER_IMXRT 1 // Enable RTxx driver
#define MG_ENABLE_CUSTOM_MILLIS 1 // Let user implement mg_millis()
#define MG_ENABLE_POSIX_FS 0 // Disable POSIX filesystem
#define MG_ENABLE_PACKED_FS 1 // Enable packed filesystem
#define HTTP_URL "http://0.0.0.0"
#define HTTPS_URL "https://0.0.0.0"
@@ -0,0 +1,149 @@
#include "mongoose.h"
#include "net.h"
void ethernet_init(void);
struct mg_mgr mgr;
struct mg_tcpip_driver_imxrt_data data = {.mdc_cr = 24, .phy_addr = 0};
struct mg_tcpip_if mif = {
// Construct MAC address from the unique chip ID
.mac = {2, (uint8_t) (HW_OCOTP_CFG0 & 255),
(uint8_t) ((HW_OCOTP_CFG0 >> 10) & 255),
(uint8_t) (((HW_OCOTP_CFG0 >> 19) ^ (HW_OCOTP_CFG1 >> 19)) & 255),
(uint8_t) ((HW_OCOTP_CFG1 >> 10) & 255),
(uint8_t) (HW_OCOTP_CFG1 & 255)},
// The default is DHCP. Uncomment 3 lines below for static IP config:
// .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_imxrt,
.driver_data = &data};
uint64_t mg_millis(void) { // Let Mongoose use our uptime function
return millis();
}
// Simple HTTP server that runs on port 8000
// Mongoose event handler function, gets called by the mg_mgr_poll()
// See https://mongoose.ws/documentation/#2-minute-integration-guide
static void simple_http_listener(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
// The MG_EV_HTTP_MSG event means HTTP request. `hm` holds parsed request,
// see https://mongoose.ws/documentation/#struct-mg_http_message
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// If the requested URI is "/api/hi", send a simple JSON response back
if (mg_http_match_uri(hm, "/api/hi")) {
// Use mg_http_reply() API function to generate JSON response. It adds a
// Content-Length header automatically. In the response, we show
// the requested URI and HTTP body:
mg_http_reply(c, 200, "", "{%m:%m,%m:%m}\n", // See mg_snprintf doc
MG_ESC("uri"), mg_print_esc, hm->uri.len, hm->uri.ptr,
MG_ESC("body"), mg_print_esc, hm->body.len, hm->body.ptr);
} else {
// For all other URIs, serve some static content
mg_http_reply(c, 200, "", "<html>millis: %lu</html>", millis());
}
}
}
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200);
while (!Serial) delay(50);
mg_mgr_init(&mgr); // Initialise Mongoose event manager
mg_log_set(MG_LL_DEBUG); // Set log level and log function
mg_log_set_fn([](char ch, void *) { Serial.print(ch); }, NULL);
MG_INFO(("CPU %g MHz. Starting TCP/IP stack", (double) F_CPU / 1000000));
ethernet_init();
mg_tcpip_init(&mgr, &mif); // Initialise built-in TCP/IP stack
MG_INFO(("Waiting for IP..."));
while (mif.state != MG_TCPIP_STATE_READY) mg_mgr_poll(&mgr, 1);
// We start two HTTP listeners: one is a simple one on port 8000,
// with event handler function defined above - simple_http_listener
// See https://mongoose.ws/documentation/#2-minute-integration-guide
// Another listener is for a more sophisticated Web device dashboard
MG_INFO(("Starting web dashboard"));
mg_http_listen(&mgr, "http://0.0.0.0:8000", simple_http_listener, NULL);
web_init(&mgr); // Sophisticated Web UI, see net.c :: fn()
}
void loop() {
static uint64_t timer;
if (mg_timer_expired(&timer, 500, mg_millis())) { // Every 500ms
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // blink an LED
}
mg_mgr_poll(&mgr, 1); // Process network events
}
extern "C" void ENET_IRQHandler(void);
#define CLRSET(reg, clear, set) ((reg) = ((reg) & ~(clear)) | (set))
#define RMII_PAD_INPUT_PULLDOWN 0x30E9
#define RMII_PAD_INPUT_PULLUP 0xB0E9
#define RMII_PAD_CLOCK 0x0031
// initialize the ethernet hardware
void ethernet_init(void) {
CCM_CCGR1 |= CCM_CCGR1_ENET(CCM_CCGR_ON);
// configure PLL6 for 50 MHz, pg 1173
CCM_ANALOG_PLL_ENET_CLR =
CCM_ANALOG_PLL_ENET_POWERDOWN | CCM_ANALOG_PLL_ENET_BYPASS | 0x0F;
CCM_ANALOG_PLL_ENET_SET = CCM_ANALOG_PLL_ENET_ENABLE |
CCM_ANALOG_PLL_ENET_BYPASS
/*| CCM_ANALOG_PLL_ENET_ENET2_REF_EN*/
| CCM_ANALOG_PLL_ENET_ENET_25M_REF_EN
/*| CCM_ANALOG_PLL_ENET_ENET2_DIV_SELECT(1)*/
| CCM_ANALOG_PLL_ENET_DIV_SELECT(1);
while (!(CCM_ANALOG_PLL_ENET & CCM_ANALOG_PLL_ENET_LOCK))
; // wait for PLL lock
CCM_ANALOG_PLL_ENET_CLR = CCM_ANALOG_PLL_ENET_BYPASS;
// configure REFCLK to be driven as output by PLL6, pg 326
CLRSET(IOMUXC_GPR_GPR1,
IOMUXC_GPR_GPR1_ENET1_CLK_SEL | IOMUXC_GPR_GPR1_ENET_IPG_CLK_S_EN,
IOMUXC_GPR_GPR1_ENET1_TX_CLK_DIR);
// Configure pins
IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_14 = 5; // Reset B0_14 Alt5 GPIO7.15
IOMUXC_SW_MUX_CTL_PAD_GPIO_B0_15 = 5; // Power B0_15 Alt5 GPIO7.14
GPIO7_GDIR |= (1 << 14) | (1 << 15);
GPIO7_DR_SET = (1 << 15); // Power on
GPIO7_DR_CLEAR = (1 << 14); // Reset PHY chip
IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_04 = RMII_PAD_INPUT_PULLDOWN; // PhyAdd[0] = 0
IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_06 = RMII_PAD_INPUT_PULLDOWN; // PhyAdd[1] = 1
IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_05 = RMII_PAD_INPUT_PULLUP; // Slave mode
IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_11 = RMII_PAD_INPUT_PULLDOWN; // Auto MDIX
IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_07 = RMII_PAD_INPUT_PULLUP;
IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_08 = RMII_PAD_INPUT_PULLUP;
IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_09 = RMII_PAD_INPUT_PULLUP;
IOMUXC_SW_PAD_CTL_PAD_GPIO_B1_10 = RMII_PAD_CLOCK;
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_05 = 3; // RXD1 B1_05 Alt3, pg 525
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_04 = 3; // RXD0 B1_04 Alt3, pg 524
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_10 = 6 | 0x10; // REFCLK B1_10 Alt6, pg 530
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_11 = 3; // RXER B1_11 Alt3, pg 531
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_06 = 3; // RXEN B1_06 Alt3, pg 526
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_09 = 3; // TXEN B1_09 Alt3, pg 529
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_07 = 3; // TXD0 B1_07 Alt3, pg 527
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_08 = 3; // TXD1 B1_08 Alt3, pg 528
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_15 = 0; // MDIO B1_15 Alt0, pg 535
IOMUXC_SW_MUX_CTL_PAD_GPIO_B1_14 = 0; // MDC B1_14 Alt0, pg 534
IOMUXC_ENET_MDIO_SELECT_INPUT = 2; // GPIO_B1_15_ALT0, pg 792
IOMUXC_ENET0_RXDATA_SELECT_INPUT = 1; // GPIO_B1_04_ALT3, pg 792
IOMUXC_ENET1_RXDATA_SELECT_INPUT = 1; // GPIO_B1_05_ALT3, pg 793
IOMUXC_ENET_RXEN_SELECT_INPUT = 1; // GPIO_B1_06_ALT3, pg 794
IOMUXC_ENET_RXERR_SELECT_INPUT = 1; // GPIO_B1_11_ALT3, pg 795
IOMUXC_ENET_IPG_CLK_RMII_SELECT_INPUT = 1; // GPIO_B1_10_ALT6, pg 791
delay(1);
GPIO7_DR_SET = (1 << 14); // Start PHY chip
// ENET_MSCR = ENET_MSCR_MII_SPEED(9);
delay(1);
// Setup IRQ handler
attachInterruptVector(IRQ_ENET, ENET_IRQHandler);
NVIC_ENABLE_IRQ(IRQ_ENET);
}
+1 -1
View File
@@ -38,7 +38,7 @@ void setup() {
// Setup HTTP listener. Respond "ok" on any HTTP request // Setup HTTP listener. Respond "ok" on any HTTP request
mg_http_listen( mg_http_listen(
&mgr, "http://0.0.0.0", &mgr, "http://0.0.0.0",
[](struct mg_connection *c, int ev, void *ev_data, void *fn_data) { [](struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "ok\n"); if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "ok\n");
}, },
&mgr); &mgr);
+1 -1
View File
@@ -15,7 +15,7 @@ void mqtt_publish(const char *message) {
if (mqtt_connection) mg_mqtt_pub(mqtt_connection, &opts); if (mqtt_connection) mg_mqtt_pub(mqtt_connection, &opts);
} }
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) {
if (ev == MG_EV_MQTT_OPEN) { if (ev == MG_EV_MQTT_OPEN) {
MG_INFO(("%lu CONNECTED to %s", c->id, MQTT_SERVER)); MG_INFO(("%lu CONNECTED to %s", c->id, MQTT_SERVER));
struct mg_mqtt_opts opts = {}; struct mg_mqtt_opts opts = {};
+1 -2
View File
@@ -20,7 +20,7 @@ uint8_t answer[] = {
1, 2, 3, 4 // 4 bytes - IP address 1, 2, 3, 4 // 4 bytes - IP address
}; };
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) {
if (ev == MG_EV_OPEN) { if (ev == MG_EV_OPEN) {
c->is_hexdumping = 1; c->is_hexdumping = 1;
} else if (ev == MG_EV_READ) { } else if (ev == MG_EV_READ) {
@@ -41,7 +41,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
} }
mg_iobuf_del(&c->recv, 0, c->recv.len); mg_iobuf_del(&c->recv, 0, c->recv.len);
} }
(void) fn_data;
(void) ev_data; (void) ev_data;
} }
+2 -1
View File
@@ -5,6 +5,7 @@ GZIP ?= gzip # For compressing files in web_root/
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 net.c packed_fs.c # Source code files
CFLAGS = -W -Wall -Wextra -g -I. # Build options CFLAGS = -W -Wall -Wextra -g -I. # Build options
NPX ?= npx
# 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
@@ -32,7 +33,7 @@ web_root/bundle.js:
# Create optimised CSS. Prerequisite: npm -g i tailwindcss tailwindcss-font-inter # Create optimised CSS. Prerequisite: npm -g i tailwindcss tailwindcss-font-inter
web_root/main.css: web_root/index.html $(wildcard web_root/*.js) web_root/main.css: web_root/index.html $(wildcard web_root/*.js)
npx tailwindcss -o $@ --minify $(NPX) tailwindcss -o $@ --minify
# Generate packed filesystem for serving Web UI # Generate packed filesystem for serving Web UI
packed_fs.c: $(wildcard web_root/*) $(wildcard certs/*) Makefile web_root/main.css web_root/bundle.js packed_fs.c: $(wildcard web_root/*) $(wildcard certs/*) Makefile web_root/main.css web_root/bundle.js
+1 -1
View File
@@ -23,4 +23,4 @@ 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/
+27 -20
View File
@@ -51,7 +51,7 @@ int ui_event_next(int no, struct ui_event *e) {
// 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) {
uint64_t *expiration_time = (uint64_t *) c->data; uint64_t *expiration_time = (uint64_t *) c->data;
if (ev == MG_EV_OPEN) { if (ev == MG_EV_OPEN) {
*expiration_time = mg_millis() + 3000; // Store expiration time in 3s *expiration_time = mg_millis() + 3000; // Store expiration time in 3s
@@ -62,7 +62,6 @@ static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
} else if (ev == MG_EV_POLL) { } else if (ev == MG_EV_POLL) {
if (mg_millis() > *expiration_time) c->is_closing = 1; if (mg_millis() > *expiration_time) c->is_closing = 1;
} }
(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
@@ -123,9 +122,9 @@ static void handle_debug(struct mg_connection *c, struct mg_http_message *hm) {
} }
static size_t print_int_arr(void (*out)(char, void *), void *ptr, va_list *ap) { 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 size_t i, len = 0, num = va_arg(*ap, size_t); // Number of items in the array
int *arr = va_arg(*ap, int *); // Array ptr int *arr = va_arg(*ap, int *); // Array ptr
for (size_t i = 0; i < num; i++) { for (i = 0; i < num; i++) {
len += mg_xprintf(out, ptr, "%s%d", i == 0 ? "" : ",", arr[i]); len += mg_xprintf(out, ptr, "%s%d", i == 0 ? "" : ",", arr[i]);
} }
return len; return len;
@@ -133,7 +132,7 @@ static size_t print_int_arr(void (*out)(char, void *), void *ptr, va_list *ap) {
static void handle_stats_get(struct mg_connection *c) { static void handle_stats_get(struct mg_connection *c) {
int points[] = {21, 22, 22, 19, 18, 20, 23, 23, 22, 22, 22, 23, 22}; 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_http_reply(c, 200, s_json_header, "{%m:%d,%m:%d,%m:[%M]}\n",
MG_ESC("temperature"), 21, // MG_ESC("temperature"), 21, //
MG_ESC("humidity"), 67, // MG_ESC("humidity"), 67, //
MG_ESC("points"), print_int_arr, MG_ESC("points"), print_int_arr,
@@ -148,11 +147,11 @@ static size_t print_events(void (*out)(char, void *), void *ptr, va_list *ap) {
int end = no + EVENTS_PER_PAGE; int end = no + EVENTS_PER_PAGE;
while ((no = ui_event_next(no, &ev)) != 0 && no <= end) { 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 += mg_xprintf(out, ptr, "%s{%m:%lu,%m:%d,%m:%d,%m:%m}\n", //
len == 0 ? "" : ",", // len == 0 ? "" : ",", //
MG_ESC("time"), ev.timestamp, // MG_ESC("time"), ev.timestamp, //
MG_ESC("type"), ev.type, // MG_ESC("type"), ev.type, //
MG_ESC("prio"), ev.prio, // MG_ESC("prio"), ev.prio, //
MG_ESC("text"), MG_ESC(ev.text)); MG_ESC("text"), MG_ESC(ev.text));
} }
@@ -162,27 +161,25 @@ static size_t print_events(void (*out)(char, void *), void *ptr, va_list *ap) {
static void handle_events_get(struct mg_connection *c, static void handle_events_get(struct mg_connection *c,
struct mg_http_message *hm) { struct mg_http_message *hm) {
int pageno = mg_json_get_long(hm->body, "$.page", 1); 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"), mg_http_reply(c, 200, s_json_header, "{%m:[%M], %m:%d}\n", MG_ESC("arr"),
print_events, pageno, MG_ESC("totalCount"), MAX_EVENTS_NO); print_events, pageno, MG_ESC("totalCount"), MAX_EVENTS_NO);
} }
static void handle_settings_set(struct mg_connection *c, struct mg_str body) { static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
struct settings settings; struct settings settings;
char *s = mg_json_get_str(body, "$.device_name");
bool ok = true;
memset(&settings, 0, sizeof(settings)); memset(&settings, 0, sizeof(settings));
mg_json_get_bool(body, "$.log_enabled", &settings.log_enabled); mg_json_get_bool(body, "$.log_enabled", &settings.log_enabled);
settings.log_level = mg_json_get_long(body, "$.log_level", 0); settings.log_level = mg_json_get_long(body, "$.log_level", 0);
settings.brightness = mg_json_get_long(body, "$.brightness", 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) { if (s && strlen(s) < MAX_DEVICE_NAME) {
free(settings.device_name); free(settings.device_name);
settings.device_name = s; settings.device_name = s;
} else { } else {
free(s); free(s);
} }
s_settings = settings; // Save to the device flash
// Save to the device flash
s_settings = settings;
bool ok = true;
mg_http_reply(c, 200, s_json_header, mg_http_reply(c, 200, s_json_header,
"{%m:%s,%m:%m}", // "{%m:%s,%m:%m}", //
MG_ESC("status"), ok ? "true" : "false", // MG_ESC("status"), ok ? "true" : "false", //
@@ -190,7 +187,7 @@ static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
} }
static void handle_settings_get(struct mg_connection *c) { 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_http_reply(c, 200, s_json_header, "{%m:%s,%m:%hhu,%m:%hhu,%m:%m}\n", //
MG_ESC("log_enabled"), MG_ESC("log_enabled"),
s_settings.log_enabled ? "true" : "false", // s_settings.log_enabled ? "true" : "false", //
MG_ESC("log_level"), s_settings.log_level, // MG_ESC("log_level"), s_settings.log_level, //
@@ -239,7 +236,7 @@ static void handle_firmware_rollback(struct mg_connection *c) {
static size_t print_status(void (*out)(char, void *), void *ptr, va_list *ap) { static size_t print_status(void (*out)(char, void *), void *ptr, va_list *ap) {
int fw = va_arg(*ap, int); int fw = va_arg(*ap, int);
return mg_xprintf(out, ptr, "{%m:%d,%m:%c%lx%c,%m:%u,%m:%u}", return mg_xprintf(out, ptr, "{%m:%d,%m:%c%lx%c,%m:%u,%m:%u}\n",
MG_ESC("status"), mg_ota_status(fw), MG_ESC("crc32"), '"', MG_ESC("status"), mg_ota_status(fw), MG_ESC("crc32"), '"',
mg_ota_crc32(fw), '"', MG_ESC("size"), mg_ota_size(fw), mg_ota_crc32(fw), '"', MG_ESC("size"), mg_ota_size(fw),
MG_ESC("timestamp"), mg_ota_timestamp(fw)); MG_ESC("timestamp"), mg_ota_timestamp(fw));
@@ -255,10 +252,18 @@ static void handle_device_reset(struct mg_connection *c) {
mg_timer_add(c->mgr, 500, 0, (void (*)(void *)) mg_device_reset, NULL); mg_timer_add(c->mgr, 500, 0, (void (*)(void *)) mg_device_reset, NULL);
} }
static void handle_device_eraselast(struct mg_connection *c) {
size_t ss = mg_flash_sector_size(), size = mg_flash_size();
char *base = (char *) mg_flash_start(), *last = base + size - ss;
if (mg_flash_bank() == 2) last -= size / 2;
mg_flash_erase(last);
mg_http_reply(c, 200, s_json_header, "true\n");
}
// HTTP request handler function // HTTP request handler function
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) {
if (ev == MG_EV_ACCEPT) { if (ev == MG_EV_ACCEPT) {
if (fn_data != NULL) { // TLS listener! if (c->fn_data != NULL) { // TLS listener!
struct mg_tls_opts opts = {0}; struct mg_tls_opts opts = {0};
opts.cert = mg_unpacked("/certs/server_cert.pem"); opts.cert = mg_unpacked("/certs/server_cert.pem");
opts.key = mg_unpacked("/certs/server_key.pem"); opts.key = mg_unpacked("/certs/server_key.pem");
@@ -294,6 +299,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
handle_firmware_status(c); handle_firmware_status(c);
} else if (mg_http_match_uri(hm, "/api/device/reset")) { } else if (mg_http_match_uri(hm, "/api/device/reset")) {
handle_device_reset(c); handle_device_reset(c);
} else if (mg_http_match_uri(hm, "/api/device/eraselast")) {
handle_device_eraselast(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));
+8
View File
@@ -4,6 +4,10 @@
#include "mongoose.h" #include "mongoose.h"
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(HTTP_URL) #if !defined(HTTP_URL)
#define HTTP_URL "http://0.0.0.0:8000" #define HTTP_URL "http://0.0.0.0:8000"
#endif #endif
@@ -25,3 +29,7 @@ struct ui_event {
}; };
void web_init(struct mg_mgr *mgr); void web_init(struct mg_mgr *mgr);
#ifdef __cplusplus
}
#endif
File diff suppressed because it is too large. Load diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 24 KiB

@@ -42,6 +42,7 @@ export const Icons = {
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>`, 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>`, 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>`, 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>`,
doc: 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="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /></svg>`,
}; };
export const tipColors = { export const tipColors = {
@@ -85,7 +86,7 @@ export function Notification({ok, text, close}) {
<div class="p-4"> <div class="p-4">
<div class="flex items-start"> <div class="flex items-start">
<div class="flex-shrink-0"> <div class="flex-shrink-0">
<${ok ? Icons.ok : Icons.failed} class="h-6 w-6 ${ok ? 'text-green-400' : 'text-red-400'}" /> <${ok ? Icons.ok : Icons.fail} class="h-6 w-6 ${ok ? 'text-green-400' : 'text-red-400'}" />
<//> <//>
<div class="ml-3 w-0 flex-1 pt-0.5"> <div class="ml-3 w-0 flex-1 pt-0.5">
<p class="text-sm font-medium text-gray-900">${text}</p> <p class="text-sm font-medium text-gray-900">${text}</p>
@@ -157,7 +158,7 @@ export function Colored({icon, text, colors}) {
export function Stat({title, text, tipText, tipIcon, tipColors, colors}) { export function Stat({title, text, tipText, tipIcon, tipColors, colors}) {
return html` return html`
<div class="flex flex-col bg-white border shadow-sm rounded-xl dark:bg-slate-900 dark:border-gray-800"> <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="overflow-auto rounded-lg bg-white px-4 py-2 ">
<div class="flex items-center gap-x-2"> <div class="flex items-center gap-x-2">
<p class="text-sm truncate text-gray-500 font-medium"> ${title} </p> <p class="text-sm truncate text-gray-500 font-medium"> ${title} </p>
<//> <//>
@@ -173,27 +174,34 @@ export function Stat({title, text, tipText, tipIcon, tipColors, colors}) {
<//>`; <//>`;
}; };
export function TextValue({value, setfn, disabled, placeholder, type, addonRight, addonLeft, attr, min, max, step}) { export function TextValue({value, setfn, disabled, placeholder, type, addonRight, addonLeft, attr, min, max, step, mult}) {
const [bg, setBg] = useState('bg-white'); const [bg, setBg] = useState('bg-white');
useEffect(() => { checkval(value); }, []); useEffect(() => { if (type == 'number') checkval(+min, +max, +value); }, []);
const checkval = function(v) { step ||= '1', mult ||= 1;
const checkval = function(min, max, v) {
setBg('bg-white'); setBg('bg-white');
if (min && v < min) setBg('bg-red-100 border-red-200'); if (min && v < min) setBg('bg-red-100 border-red-200');
if (max && v > max) setBg('bg-red-100 border-red-200'); if (max && v > max) setBg('bg-red-100 border-red-200');
}; };
const oninput = ev => { const m = step.match(/^.+\.(.+)/);
const digits = m ? m[1].length : 0;
const onchange = ev => {
let v = ev.target.value; let v = ev.target.value;
if (type == 'number') v = parseInt(v); if (type == 'number') {
checkval(+min, +max, +v);
v = +(parseFloat(v) / mult).toFixed(digits);
}
setfn(v); setfn(v);
checkval(v);
}; };
if (type == 'number') value = +(value * mult).toFixed(digits);
return html` return html`
<div class="flex w-full items-center rounded border shadow-sm ${bg}"> <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}<//>` } ${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} <input type=${type || 'text'} disabled=${disabled} value=${value}
oninput=${oninput} ...${attr} step=${step} min=${min} max=${max}
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} /> onchange=${onchange} ...${attr}
${ 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}<//>` } 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} />
${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}<//>` }
<//>`; <//>`;
}; };
@@ -222,7 +230,7 @@ export function Setting(props) {
if (props.type == 'select') input = SelectValue; if (props.type == 'select') input = SelectValue;
return html` return html`
<div class=${props.cls || 'grid grid-cols-2 gap-2 my-1'}> <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}<//> <label class="flex items-center text-sm text-gray-700 mr-2 font-medium ${props.title || 'hidden'}">${props.title}<//>
<div class="flex items-center">${h(input, props)}<//> <div class="flex items-center">${h(input, props)}<//>
<//>`; <//>`;
}; };
@@ -317,7 +325,6 @@ export function UploadFileButton(props) {
finish(ok); // All chunks sent finish(ok); // All chunks sent
}); });
}; };
//setFailed(false);
sendChunk(0); sendChunk(0);
}); });
}; };
@@ -344,6 +351,6 @@ export function UploadFileButton(props) {
<div class="inline-flex flex-col ${props.class}"> <div class="inline-flex flex-col ${props.class}">
<input class="hidden" type="file" ref=${input} onchange=${onchange} accept=${props.accept} /> <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} /> <${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}<//> <div class="pt-2 text-sm text-slate-400 ${status || 'hidden'}">${status}<//>
<//>`; <//>`;
}; };
+1 -1
View File
@@ -1 +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 /*! 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-auto{right:auto}.top-0{top:0}.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{flex-grow:1}.grow-0{flex-grow:0}.basis-\[30px\]{flex-basis:30px}.border-separate{border-collapse:initial}.border-spacing-0Line truncated
+27 -14
View File
@@ -216,36 +216,49 @@ function FirmwareUpdate({}) {
.then(r => new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000))); .then(r => new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000)));
const onrollback = ev => fetch('api/firmware/rollback') const onrollback = ev => fetch('api/firmware/rollback')
.then(onreboot); .then(onreboot);
const onerase = ev => fetch('api/device/eraselast').then(refresh);
const onupload = function(ok, name, size) { const onupload = function(ok, name, size) {
if (!ok) return false; if (!ok) return false;
return new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000)); return new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000));
}; };
const clean = info[0].valid && info[0].golden == 0;
return html` return html`
<div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-2"> <div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-3">
<${FirmwareStatus} title="Current firmware image" info=${info[0]}> <${FirmwareStatus} title="Current firmware image" info=${info[0]}>
<div class="flex flex-wrap gap-2"> <div class="flex flex-wrap gap-2">
<${Button} title="Commit this firmware" <${Button} title="Commit this firmware" onclick=${oncommit}
onclick=${oncommit} icon=${Icons.thumbUp} disabled=${clean} /> icon=${Icons.thumbUp} disabled=${info[0].status == 3} cls="w-full" />
<${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]}> <${FirmwareStatus} title="Previous firmware image" info=${info[1]}>
<${Button} title="Rollback to this firmware" onclick=${onrollback} <${Button} title="Rollback to this firmware" onclick=${onrollback}
icon=${Icons.backward} disabled=${info[1].valid == false} /> icon=${Icons.backward} disabled=${info[1].status == 0} cls="w-full" />
<//> <//>
<div class="bg-white xm-4 divide-y border rounded flex flex-col">
<div class="font-light uppercase flex items-center text-gray-600 px-4 py-2">
Device control
<//>
<div class="px-4 py-3 flex flex-col gap-2 grow">
<${UploadFileButton}
title="Upload new firmware .bin file" onupload=${onupload}
url="api/firmware/upload" accept=".bin,.uf2" />
<div class="grow"><//>
<${Button} title="Reboot device" onclick=${onreboot} icon=${Icons.refresh} cls="w-full" />
<${Button} title="Erase last sector" onclick=${onerase} icon=${Icons.doc} cls="w-full hidden" />
<//>
<//>
<//>
<div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-2">
<div class="bg-white border shadow-lg"> <div class="bg-white border shadow-lg">
<${DeveloperNote}> <${DeveloperNote}>
<div class="my-2"> <div class="my-2">
When a new firmware gets flashed, its status is marked as, "first_boot". Firmware status and other information is stored in the last sector
That is an unreliable (uncommitted) firmware. A user may choose of flash
to revert back to the previous committed firmware on the subsequent <//>
boots. Clicking on the "commit" button calls "mg_ota_commit()" function <div class="my-2">
which commits the firmware. Firmware status can be FIRST_BOOT, UNCOMMITTED or COMMITTED. If no
information is available, it is UNAVAILABLE.
<//> <//>
<div class="my-2"> <div class="my-2">
This GUI loads a firmware file and sends it chunk by chunk to the This GUI loads a firmware file and sends it chunk by chunk to the
+1 -2
View File
@@ -6,7 +6,7 @@
const char *s_listening_url = "http://0.0.0.0:8000"; const char *s_listening_url = "http://0.0.0.0:8000";
// HTTP request handler function // HTTP request handler function
void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_HTTP_MSG) {
struct mg_http_serve_opts opts = { struct mg_http_serve_opts opts = {
.root_dir = "/web_root", .root_dir = "/web_root",
@@ -14,7 +14,6 @@ void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
}; };
mg_http_serve_dir(c, ev_data, &opts); mg_http_serve_dir(c, ev_data, &opts);
} }
(void) fn_data;
} }
int main(void) { int main(void) {
+26
View File
@@ -0,0 +1,26 @@
THISDIR = $(realpath $(CURDIR))
ROOTDIR = $(realpath $(CURDIR)/../../..)
PORT ?= /dev/ttyUSB0
DOCKER ?= docker run --rm $(DA) -v $(ROOTDIR):$(ROOTDIR) -w $(THISDIR) espressif/idf:v5.0.4
# Note that the esp32 port needs the extra .. for relative paths due to the location of its main CMakeLists.txt file
CMD ?= bash -c '$(MAKE) -C micropython/ports/esp32 submodules && $(MAKE) -C micropython/ports/esp32 USER_C_MODULES=../../../../mongoose/micropython.cmake'
all: example
example:
true
build: micropython
$(DOCKER) $(CMD)
micropython:
$(DOCKER) git clone https://github.com/micropython/micropython.git
clean:
test -d micropython && $(DOCKER) rm -rf micropython || true
flash:
flash: DA = --device $(PORT)
flash: CMD = bash -c '$(MAKE) -C micropython/ports/esp32 erase deploy'
flash: build
+23
View File
@@ -0,0 +1,23 @@
# Mongoose as a MicroPython user module on the ESP32
The most basic usage is:
```python
import network
import mongoose
sta_if = network.WLAN(network.STA_IF)
sta_if.active(True)
sta_if.connect("<SSID>", "<PASSWORD>")
while not sta_if.isconnected():
pass
mongoose.init()
while True:
mongoose.poll(1000)
```
Check [_thread](https://docs.micropython.org/en/latest/library/_thread.html) support for more advanced usage
https://docs.micropython.org/en/latest/develop/cmodules.html
@@ -0,0 +1,28 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
static void t_fn(void *arg) { // Tick periodically
MG_INFO(("tick"));
(void) arg;
}
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
(void) ev_data;
}
static struct mg_mgr mgr;
void mgmp_init(void) {
mg_mgr_init(&mgr); // Mongoose event manager
mg_log_set(MG_LL_DEBUG); // Set log level
mg_timer_add(&mgr, 1000, MG_TIMER_REPEAT, t_fn, &mgr);
mg_http_listen(&mgr, "http://0.0.0.0:80", fn, &mgr);
}
void mgmp_poll(int t) {
mg_mgr_poll(&mgr, t);
}
@@ -0,0 +1 @@
../../../micropython/mongoose/micropython.cmake
+1
View File
@@ -0,0 +1 @@
../../../micropython/mongoose/micropython.mk
+1
View File
@@ -0,0 +1 @@
../../../micropython/mongoose/module.c
+1
View File
@@ -0,0 +1 @@
../../../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../../../mongoose.h
@@ -0,0 +1,2 @@
// For some reason, IDF detection at build time does not work here
#define MG_ARCH MG_ARCH_ESP32
+8 -6
View File
@@ -5,9 +5,11 @@
const char *s_listening_url = "http://0.0.0.0:80"; const char *s_listening_url = "http://0.0.0.0:80";
#if 0
char *config_read(void) { char *config_read(void) {
return mg_file_read(&mg_fs_posix, FS_ROOT "/config.json", NULL); return mg_file_read(&mg_fs_posix, FS_ROOT "/config.json", NULL);
} }
#endif
void config_write(struct mg_str config) { void config_write(struct mg_str config) {
mg_file_write(&mg_fs_posix, FS_ROOT "/config.json", config.ptr, config.len); mg_file_write(&mg_fs_posix, FS_ROOT "/config.json", config.ptr, config.len);
@@ -21,14 +23,14 @@ void app_main(void) {
MG_INFO(("FS at %s initialised, status: %d", conf.base_path, res)); MG_INFO(("FS at %s initialised, status: %d", conf.base_path, res));
// Try to connect to wifi by using saved WiFi credentials // Try to connect to wifi by using saved WiFi credentials
char *json = mg_file_read(&mg_fs_posix, WIFI_FILE, NULL); struct mg_str json = mg_file_read(&mg_fs_posix, WIFI_FILE);
if (json != NULL) { if (json.ptr != NULL) {
char *ssid = mg_json_get_str(mg_str(json), "$.ssid"); char *ssid = mg_json_get_str(json, "$.ssid");
char *pass = mg_json_get_str(mg_str(json), "$.pass"); char *pass = mg_json_get_str(json, "$.pass");
while (!wifi_init(ssid, pass)) (void) 0; while (!wifi_init(ssid, pass)) (void) 0;
free(ssid); free(ssid);
free(pass); free(pass);
free(json); free((void *) json.ptr);
} else { } else {
// If WiFi is not configured, run CLI until configured // If WiFi is not configured, run CLI until configured
MG_INFO(("WiFi is not configured, running CLI. Press enter")); MG_INFO(("WiFi is not configured, running CLI. Press enter"));
@@ -45,6 +47,6 @@ void app_main(void) {
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
mg_log_set(MG_LL_DEBUG); // Set log level mg_log_set(MG_LL_DEBUG); // Set log level
MG_INFO(("Mongoose v%s on %s", MG_VERSION, s_listening_url)); MG_INFO(("Mongoose v%s on %s", MG_VERSION, s_listening_url));
mg_http_listen(&mgr, s_listening_url, uart_bridge_fn, &mgr); mg_http_listen(&mgr, s_listening_url, uart_bridge_fn, NULL);
for (;;) mg_mgr_poll(&mgr, 10); // Infinite event loop for (;;) mg_mgr_poll(&mgr, 10); // Infinite event loop
} }
+1 -1
View File
@@ -11,7 +11,7 @@
#define WIFI_FILE FS_ROOT "/wifi.json" #define WIFI_FILE FS_ROOT "/wifi.json"
#define UART_NO 1 #define UART_NO 1
void uart_bridge_fn(struct mg_connection *, int, void *, void *); void uart_bridge_fn(struct mg_connection *, int, void *);
int uart_read(void *buf, size_t len); int uart_read(void *buf, size_t len);
bool wifi_init(const char *ssid, const char *pass); bool wifi_init(const char *ssid, const char *pass);
void cli(uint8_t ch); void cli(uint8_t ch);
@@ -9,8 +9,8 @@
#define SERVER_URL "http://0.0.0.0:80" #define SERVER_URL "http://0.0.0.0:80"
#define CLIENT_URL "http://info.cern.ch" #define CLIENT_URL "http://info.cern.ch"
// Event handler for an server (accepted) connection // Event handler for a server (accepted) connection
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) {
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_HTTP_MSG) {
mg_http_reply(c, 200, "", "Hello from ESP!\n"); mg_http_reply(c, 200, "", "Hello from ESP!\n");
} }
@@ -20,7 +20,7 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
// To enable TLS for HTTP, // To enable TLS for HTTP,
// 1. Copy "ca.pem" file to the ESP8266 flash FS // 1. Copy "ca.pem" file to the ESP8266 flash FS
// 2. Add TLS init snippet for the connection, see examples/http-client // 2. Add TLS init snippet for the connection, see examples/http-client
static void cb2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) { static void cb2(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_CONNECT) { if (ev == MG_EV_CONNECT) {
struct mg_str s = mg_url_host(CLIENT_URL); struct mg_str s = mg_url_host(CLIENT_URL);
mg_printf(c, "GET / HTTP/1.0\r\nHost: %.*s\r\n\r\n", (int) s.len, s.ptr); mg_printf(c, "GET / HTTP/1.0\r\nHost: %.*s\r\n\r\n", (int) s.len, s.ptr);
+35
View File
@@ -0,0 +1,35 @@
SPROG ?= server # Program we are building
CPROG ?= client # Program we are building
DELETE = rm -rf # Command to remove files
SOUT ?= -o $(SPROG) # Compiler argument for output file
COUT ?= -o $(CPROG) # Compiler argument for output file
SSOURCES = server.c mongoose.c # Source code files
CSOURCES = client.c mongoose.c # Source code files
CFLAGS = -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
#CFLAGS_MONGOOSE += -DMG_ENABLE_LINES
ifeq ($(OS),Windows_NT) # Windows settings. Assume MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feprog.exe
SPROG ?= server.exe # Use .exe suffix for the binary
CPROG ?= client.exe # Use .exe suffix for the binary
CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 # Link against Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
SOUT ?= -o $(SPROG) # Build output
COUT ?= -o $(CPROG) # Build output
endif
all: example # Default target. Build all and run server
$(RUN) ./$(SPROG) $(SARGS)
example: $(SPROG) $(CPROG)
$(SPROG): $(SSOURCES) # Build program from sources
$(CC) $(SSOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(SOUT)
$(CPROG): $(CSOURCES) # Build program from sources
$(CC) $(CSOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(COUT)
clean: # Cleanup. Delete built program and all build artifacts
$(DELETE) $(SPROG) $(CPROG) *.o *.obj *.exe *.dSYM
+47
View File
@@ -0,0 +1,47 @@
# File Transfer
This example contains minimal HTTP client and server.
The client uploads a file to the server in a single POST, shaping traffic to send small data chunks.
The server manually processes requests in order to be able to write as soon as data arrives, to avoid buffering a whole (possibly huge) file not fitting in RAM.
Uploads are authenticated using Basic Auth. Both client and server have a default user/pass and can be configured using the command line. Only authenticated users can upload a file.
The server can also accept regular uploads from any HTTP client, for example curl:
```sh
curl -su user:pass http://localhost:8090/upload/foo.txt --data-binary @Makefile
```
- Follow the [Build Tools](../tools/) tutorial to setup your development environment.
- Start a terminal in this project directory; and build the example:
```sh
cd mongoose/examples/file-transfer
make clean all
```
- Manually start the server, either in background (to reuse the same terminal window) or in foreground; in which case you'll need another terminal to run the client. The server will listen at all interfaces in port 8090
```sh
./server
6332b7 2 server.c:157:main Mongoose version : v7.12
6332b7 2 server.c:158:main Listening on : http://0.0.0.0:8090
6332b7 2 server.c:159:main Web root : [/home/mongoose/examples/file-transfer/web_root]
6332b7 2 server.c:160:main Uploading to : [/home/mongoose/examples/file-transfer/upload]
```
- Manually run the client to send a file, default is to send it as "foo.txt" to the server in localhost at port 8090
```sh
./client -f Makefile
ok
```
Default operation is to assume hardcoded username and password. Call both server and client with no arguments to see usage instructions
See detailed tutorials at
https://mongoose.ws/tutorials/file-uploads/
https://mongoose.ws/tutorials/http-server/
https://mongoose.ws/tutorials/http-client/
+117
View File
@@ -0,0 +1,117 @@
// Copyright (c) 2021 Cesanta Software Limited
// All rights reserved
//
// Example HTTP client. Connect to `s_url`, send request, wait for a response,
// print the response and exit.
// 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
#include "mongoose.h"
static int s_debug_level = MG_LL_INFO;
static const char *s_user = "user";
static const char *s_pass = "pass";
static const char *s_fname = NULL;
static struct mg_fd *fd; // file descriptor
static size_t fsize;
static const char *s_url = "http://localhost:8090/upload/foo.txt";
static const uint64_t s_timeout_ms = 1500; // Connect timeout in milliseconds
// Print HTTP response and signal that we're done
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_OPEN) {
// Connection created. Store connect expiration time in c->data
*(uint64_t *) c->data = mg_millis() + s_timeout_ms;
} else if (ev == MG_EV_POLL) {
if (mg_millis() > *(uint64_t *) c->data &&
(c->is_connecting || c->is_resolving)) {
mg_error(c, "Connect timeout");
}
} else if (ev == MG_EV_CONNECT) {
// Connected to server. Extract host name from URL
struct mg_str host = mg_url_host(s_url);
// Send request
MG_DEBUG(("Connected, send request"));
mg_printf(c,
"POST %s HTTP/1.0\r\n"
"Host: %.*s\r\n"
"Content-Type: octet-stream\r\n"
"Content-Length: %d\r\n",
mg_url_uri(s_url), (int) host.len, host.ptr, fsize);
mg_http_bauth(c, s_user, s_pass); // Add Basic auth header
mg_printf(c, "%s", "\r\n"); // End HTTP headers
} else if (ev == MG_EV_WRITE && c->send.len < MG_IO_SIZE) {
uint8_t *buf = alloca(MG_IO_SIZE);
size_t len = MG_IO_SIZE - c->send.len;
len = fsize < len ? fsize : len;
fd->fs->rd(fd->fd, buf, len);
mg_send(c, buf, len);
fsize -= len;
MG_DEBUG(("sent %u bytes", len));
} else if (ev == MG_EV_HTTP_MSG) {
MG_DEBUG(("MSG"));
// Response is received. Print it
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
printf("%.*s", (int) hm->body.len, hm->body.ptr);
c->is_draining = 1; // Tell mongoose to close this connection
mg_fs_close(fd);
*(bool *) c->fn_data = true; // Tell event loop to stop
} else if (ev == MG_EV_ERROR) {
MG_DEBUG(("ERROR"));
mg_fs_close(fd);
*(bool *) c->fn_data = true; // Error, tell event loop to stop
}
}
static void usage(const char *prog) {
fprintf(stderr,
"File Transfer client based on Mongoose v.%s\n"
"Usage: %s -f NAME OPTIONS\n"
" -u NAME - user name, default: '%s'\n"
" -p PWD - password, default: '%s'\n"
" -U URL - Full server URL, including destination file name; "
"default: '%s'\n"
" -f NAME - File to send\n"
" -v LEVEL - debug level, from 0 to 4, default: %d\n",
MG_VERSION, prog, s_user, s_pass, s_url, s_debug_level);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr; // Event manager
bool done = false; // Event handler flips it to true
time_t mtime;
int i;
// Parse command-line flags
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-f") == 0) {
s_fname = argv[++i];
} else if (strcmp(argv[i], "-u") == 0) {
s_user = argv[++i];
} else if (strcmp(argv[i], "-p") == 0) {
s_pass = argv[++i];
} else if (strcmp(argv[i], "-U") == 0) {
s_url = argv[++i];
} else if (strcmp(argv[i], "-v") == 0) {
s_debug_level = atoi(argv[++i]);
} else {
usage(argv[0]);
}
}
if (s_fname == NULL) usage(argv[0]);
mg_fs_posix.st(s_fname, &fsize, &mtime);
if (fsize == 0 ||
(fd = mg_fs_open(&mg_fs_posix, s_fname, MG_FS_READ)) == NULL) {
MG_ERROR(("open failed: %d", errno));
exit(EXIT_FAILURE);
}
mg_log_set(s_debug_level);
mg_mgr_init(&mgr); // Initialise event manager
mg_http_connect(&mgr, s_url, fn, &done); // Create client connection
while (!done) mg_mgr_poll(&mgr, 50); // Event manager loops until 'done'
mg_mgr_free(&mgr); // Free resources
return 0;
}
+1
View File
@@ -0,0 +1 @@
../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../mongoose.h
+178
View File
@@ -0,0 +1,178 @@
// Copyright (c) 2024 Cesanta Software Limited
// All rights reserved
#include <signal.h>
#include "mongoose.h"
static int s_debug_level = MG_LL_INFO;
static int s_max_size = 10000;
static const char *s_root_dir = "web_root";
static const char *s_upld_dir = "upload";
static const char *s_listening_address = "http://0.0.0.0:8090";
static const char *s_user = "user";
static const char *s_pass = "pass";
// Handle interrupts, like Ctrl-C
static int s_signo;
static void signal_handler(int signo) {
s_signo = signo;
}
static bool authuser(struct mg_http_message *hm) {
char user[256], pass[256];
mg_http_creds(hm, user, sizeof(user), pass, sizeof(pass));
if (strcmp(user, s_user) == 0 && strcmp(pass, s_pass) == 0) return true;
return false;
}
// Streaming upload example. Demonstrates how to use MG_EV_READ events
// to get large payload in smaller chunks. To test, use curl utility:
static void cb(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_READ) {
// Parse the incoming data ourselves. If we can parse the request,
// store two size_t variables in c->data: expected len and recv len.
size_t *data = (size_t *) c->data;
struct mg_fd *fd = (struct mg_fd *) c->fn_data; // get file descriptor
if (data[0]) { // Already parsed, receiving body
data[1] += c->recv.len;
MG_DEBUG(("Got chunk len %lu, %lu total", c->recv.len, data[1]));
fd->fs->wr(fd->fd, c->recv.buf, c->recv.len);
c->recv.len = 0; // And cleanup the receive buffer. Streaming!
if (data[1] >= data[0]) {
mg_fs_close(fd);
mg_http_reply(c, 200, "", "ok\n");
}
} else if (c->is_resp == 0) {
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/#")) {
if (!authuser(&hm)) {
mg_http_reply(c, 403, "", "Denied\n");
c->is_draining = 1; // Tell mongoose to close this connection
} else if (hm.body.len > (size_t) s_max_size) {
mg_http_reply(c, 400, "", "Too long\n");
c->is_draining = 1; // Tell mongoose to close this connection
} else if (hm.uri.len == 8) { // 8: /upload/
mg_http_reply(c, 400, "", "Name required\n");
c->is_draining = 1; // Tell mongoose to close this connection
} else if (strlen(s_upld_dir) + (hm.uri.len - 8) + 2 >
MG_PATH_MAX) { // 2: MG_DIRSEP + NUL
mg_http_reply(c, 400, "", "Path is too long\n");
c->is_draining = 1; // Tell mongoose to close this connection
} else {
char fpath[MG_PATH_MAX];
snprintf(fpath, MG_PATH_MAX, "%s%c", s_upld_dir, MG_DIRSEP);
strncat(fpath, hm.uri.ptr + 8, hm.uri.len - 8);
if (!mg_path_is_sane(fpath)) {
mg_http_reply(c, 400, "", "Invalid path\n");
c->is_draining = 1; // Tell mongoose to close this connection
} else {
MG_DEBUG(("Got request, chunk len %lu", c->recv.len - n));
if ((fd = mg_fs_open(&mg_fs_posix, fpath, MG_FS_WRITE)) == NULL) {
mg_http_reply(c, 400, "", "open failed: %d\n", errno);
c->is_draining = 1; // Tell mongoose to close this connection
} else {
c->fn_data = fd;
c->recv.len -= n; // remove headers
data[0] = hm.body.len;
data[1] = c->recv.len;
if (c->recv.len)
fd->fs->wr(fd->fd, c->recv.buf + n, c->recv.len);
c->recv.len = 0; // consume data
if (data[1] >= data[0]) {
mg_fs_close(fd);
mg_http_reply(c, 200, "", "ok\n");
}
}
}
}
c->is_resp = 1; // ignore the rest of the body
} else {
struct mg_http_serve_opts opts = {0};
opts.root_dir = s_root_dir;
mg_http_serve_dir(c, &hm, &opts);
}
}
}
}
(void) ev_data;
}
static void usage(const char *prog) {
fprintf(stderr,
"File Transfer server based on Mongoose v.%s\n"
"Usage: %s OPTIONS\n"
" -u NAME - user name, default: '%s'\n"
" -p PWD - password, default: '%s'\n"
" -d DIR - directory to serve, default: '%s'\n"
" -D DIR - directory to store uploads, default: '%s'\n"
" -s SIZE - maximum allowed file size, default: '%d'\n"
" -l ADDR - listening address, default: '%s'\n"
" -v LEVEL - debug level, from 0 to 4, default: %d\n",
MG_VERSION, prog, s_user, s_pass, s_root_dir, s_upld_dir, s_max_size,
s_listening_address, s_debug_level);
exit(EXIT_FAILURE);
}
int main(int argc, char *argv[]) {
char spath[MG_PATH_MAX] = ".";
char upath[MG_PATH_MAX] = ".";
struct mg_mgr mgr;
int i;
// Parse command-line flags
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-d") == 0) {
s_root_dir = argv[++i];
} else if (strcmp(argv[i], "-D") == 0) {
s_upld_dir = argv[++i];
} else if (strcmp(argv[i], "-u") == 0) {
s_user = argv[++i];
} else if (strcmp(argv[i], "-p") == 0) {
s_pass = argv[++i];
} else if (strcmp(argv[i], "-l") == 0) {
s_listening_address = argv[++i];
} else if (strcmp(argv[i], "-v") == 0) {
s_debug_level = atoi(argv[++i]);
} else if (strcmp(argv[i], "-s") == 0) {
s_max_size = atoi(argv[++i]);
} else {
usage(argv[0]);
}
}
// Root directory must not contain double dots. Make it absolute
// Do the conversion only if the root dir spec does not contain overrides
if (strchr(s_root_dir, ',') == NULL) {
realpath(s_root_dir, spath);
s_root_dir = spath;
}
if (strchr(s_upld_dir, ',') == NULL) {
realpath(s_upld_dir, upath);
s_upld_dir = upath;
}
// Initialise stuff
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
mg_log_set(s_debug_level);
mg_mgr_init(&mgr);
// use mg_listen instead of mg_http_listen to be able to override the parser
// and shape buffering
if (mg_listen(&mgr, s_listening_address, cb, NULL) == NULL) {
MG_ERROR(("Cannot listen on %s.", s_listening_address));
exit(EXIT_FAILURE);
}
// Start infinite event loop
MG_INFO(("Mongoose version : v%s", MG_VERSION));
MG_INFO(("Listening on : %s", s_listening_address));
MG_INFO(("Web root : [%s]", s_root_dir));
MG_INFO(("Uploading to : [%s]", s_upld_dir));
while (s_signo == 0) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
MG_INFO(("Exiting on signal %d", s_signo));
return 0;
}
Whitespace-only changes.
@@ -0,0 +1,9 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>File Transfer</title>
</head>
<body>
<p style="font-size:100px">&#128515;</p>
</body>
</html>
+1 -2
View File
@@ -16,7 +16,7 @@
// //
// Also, consider changing -DMG_IO_SIZE=SOME_BIG_VALUE to increase IO buffer // Also, consider changing -DMG_IO_SIZE=SOME_BIG_VALUE to increase IO buffer
// increment when reading data. // increment when reading data.
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) {
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;
MG_INFO(("New request to: [%.*s], body size: %lu", (int) hm->uri.len, MG_INFO(("New request to: [%.*s], body size: %lu", (int) hm->uri.len,
@@ -35,7 +35,6 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
mg_http_serve_dir(c, ev_data, &opts); mg_http_serve_dir(c, ev_data, &opts);
} }
} }
(void) fn_data;
} }
int main(void) { int main(void) {
+2 -12
View File
@@ -6,26 +6,16 @@
// HTTP request handler function. It implements the following endpoints: // HTTP request handler function. It implements the following endpoints:
// /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) {
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, "/upload")) { if (mg_http_match_uri(hm, "/upload")) {
char path[80], name[64]; mg_http_upload(c, hm, &mg_fs_posix, "/tmp", 99999);
mg_http_get_var(&hm->query, "name", name, sizeof(name));
mg_snprintf(path, sizeof(path), "/tmp/%s", name);
if (name[0] == '\0') {
mg_http_reply(c, 400, "", "%s", "name required");
} else if (!mg_path_is_sane(path)) {
mg_http_reply(c, 400, "", "%s", "invalid path");
} else {
mg_http_upload(c, hm, &mg_fs_posix, path, 99999);
}
} else { } else {
struct mg_http_serve_opts opts = {.root_dir = "web_root"}; struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts); mg_http_serve_dir(c, ev_data, &opts);
} }
} }
(void) fn_data;
} }
int main(void) { int main(void) {
@@ -17,7 +17,7 @@ var sendFileData = function(name, data, chunkSize) {
var sendChunk = function(offset) { var sendChunk = function(offset) {
var chunk = data.subarray(offset, offset + chunkSize) || ''; var chunk = data.subarray(offset, offset + chunkSize) || '';
var opts = {method: 'POST', body: chunk}; var opts = {method: 'POST', body: chunk};
var url = '/upload?offset=' + offset + '&name=' + encodeURIComponent(name); var url = '/upload?offset=' + offset + '&file=' + encodeURIComponent(name);
var ok; var ok;
setStatus( setStatus(
'Uploading ' + name + ', bytes ' + offset + '..' + 'Uploading ' + name + ', bytes ' + offset + '..' +
+4 -4
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2020 Cesanta Software Limited // Copyright (c) 2020-2024 Cesanta Software Limited
// All rights reserved // All rights reserved
// //
// Streaming upload example. Demonstrates how to use MG_EV_READ events // Streaming upload example. Demonstrates how to use MG_EV_READ events
@@ -11,7 +11,7 @@
// HTTP request handler function. It implements the following endpoints: // HTTP request handler function. It implements the following endpoints:
// /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 fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_READ) { if (ev == MG_EV_READ) {
// Parse the incoming data ourselves. If we can parse the request, // Parse the incoming data ourselves. If we can parse the request,
// store two size_t variables in the c->data: expected len and recv len. // store two size_t variables in the c->data: expected len and recv len.
@@ -38,7 +38,7 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
} }
} }
} }
(void) fn_data, (void) ev_data; (void) ev_data;
} }
int main(void) { int main(void) {
@@ -46,7 +46,7 @@ int main(void) {
mg_mgr_init(&mgr); mg_mgr_init(&mgr);
mg_log_set(MG_LL_DEBUG); // Set debug log level mg_log_set(MG_LL_DEBUG); // Set debug log level
mg_http_listen(&mgr, "http://localhost:8000", cb, NULL); mg_listen(&mgr, "http://localhost:8000", fn, NULL);
for (;;) mg_mgr_poll(&mgr, 50); for (;;) mg_mgr_poll(&mgr, 50);
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
+3 -3
View File
@@ -15,7 +15,7 @@ static const char *s_post_data = NULL; // POST data
static const uint64_t s_timeout_ms = 1500; // Connect timeout in milliseconds static const uint64_t s_timeout_ms = 1500; // Connect timeout in milliseconds
// Print HTTP response and signal that we're done // Print HTTP response and signal that we're done
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) {
if (ev == MG_EV_OPEN) { if (ev == MG_EV_OPEN) {
// Connection created. Store connect expiration time in c->data // Connection created. Store connect expiration time in c->data
*(uint64_t *) c->data = mg_millis() + s_timeout_ms; *(uint64_t *) c->data = mg_millis() + s_timeout_ms;
@@ -50,9 +50,9 @@ static void fn(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;
printf("%.*s", (int) hm->message.len, hm->message.ptr); printf("%.*s", (int) hm->message.len, hm->message.ptr);
c->is_draining = 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 *) c->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 *) c->fn_data = true; // Error, tell event loop to stop
} }
} }
+2 -2
View File
@@ -10,8 +10,8 @@
#include "mongoose.h" #include "mongoose.h"
// Print HTTP response and signal that we're done // Print HTTP response and signal that we're done
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) {
const char *url = fn_data; const char *url = c->fn_data;
static bool connected; static bool connected;
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;
+2 -3
View File
@@ -56,8 +56,8 @@ static const char *s_tls_key =
// 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) {
if (ev == MG_EV_ACCEPT && fn_data != NULL) { if (ev == MG_EV_ACCEPT && c->fn_data != NULL) {
struct mg_tls_opts opts = { struct mg_tls_opts opts = {
#ifdef TLS_TWOWAY #ifdef TLS_TWOWAY
.ca = mg_str(s_tls_ca), .ca = mg_str(s_tls_ca),
@@ -89,7 +89,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
mg_http_serve_dir(c, ev_data, &opts); mg_http_serve_dir(c, ev_data, &opts);
} }
} }
(void) fn_data;
} }
int main(void) { int main(void) {
+4 -4
View File
@@ -36,8 +36,8 @@ static void forward_request(struct mg_http_message *hm,
(int) hm->uri.len, hm->uri.ptr)); (int) hm->uri.len, hm->uri.ptr));
} }
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) {
struct mg_connection *c2 = (struct mg_connection *) fn_data; struct mg_connection *c2 = (struct mg_connection *) c->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);
@@ -48,8 +48,8 @@ static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
(void) ev_data; (void) ev_data;
} }
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) {
struct mg_connection *c2 = fn_data; struct mg_connection *c2 = c->fn_data;
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;
// Client request, create backend connection Note that we're passing // Client request, create backend connection Note that we're passing
+45 -14
View File
@@ -9,6 +9,7 @@ static const char *s_root_dir = ".";
static const char *s_listening_address = "http://0.0.0.0:8000"; static const char *s_listening_address = "http://0.0.0.0:8000";
static const char *s_enable_hexdump = "no"; static const char *s_enable_hexdump = "no";
static const char *s_ssi_pattern = "#.html"; static const char *s_ssi_pattern = "#.html";
static const char *s_upload_dir = NULL; // File uploads disabled by default
// Handle interrupts, like Ctrl-C // Handle interrupts, like Ctrl-C
static int s_signo; static int s_signo;
@@ -18,22 +19,48 @@ static void signal_handler(int signo) {
// Event handler for the listening connection. // Event handler for the listening connection.
// Simply serve static files from `s_root_dir` // Simply serve static files from `s_root_dir`
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) {
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = ev_data, tmp = {0}; struct mg_http_message *hm = ev_data;
struct mg_str unknown = mg_str_n("?", 1), *cl;
struct mg_http_serve_opts opts = {0}; if (mg_match(hm->uri, mg_str("/upload"), NULL)) {
opts.root_dir = s_root_dir; // Serve file upload
opts.ssi_pattern = s_ssi_pattern; if (s_upload_dir == NULL) {
mg_http_serve_dir(c, hm, &opts); mg_http_reply(c, 403, "", "Denied: file upload directory not set\n");
mg_http_parse((char *) c->send.buf, c->send.len, &tmp); } else {
cl = mg_http_get_header(&tmp, "Content-Length"); struct mg_http_part part;
if (cl == NULL) cl = &unknown; size_t pos = 0, total_bytes = 0, num_files = 0;
MG_INFO(("%.*s %.*s %.*s %.*s", (int) hm->method.len, hm->method.ptr, while ((pos = mg_http_next_multipart(hm->body, pos, &part)) > 0) {
(int) hm->uri.len, hm->uri.ptr, (int) tmp.uri.len, tmp.uri.ptr, char path[MG_PATH_MAX];
(int) cl->len, cl->ptr)); MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes",
part.name.len, part.name.ptr, part.filename.len,
part.filename.ptr, part.body.len));
mg_snprintf(path, sizeof(path), "%s/%.*s", s_upload_dir,
part.filename.len, part.filename.ptr);
if (mg_path_is_sane(path)) {
mg_file_write(&mg_fs_posix, path, part.body.ptr, part.body.len);
total_bytes += part.body.len;
num_files++;
} else {
MG_ERROR(("Rejecting dangerous path %s", path));
}
}
mg_http_reply(c, 200, "", "Uploaded %lu files, %lu bytes\n", num_files,
total_bytes);
}
} else {
// Serve web root directory
struct mg_http_serve_opts opts = {0};
opts.root_dir = s_root_dir;
opts.ssi_pattern = s_ssi_pattern;
mg_http_serve_dir(c, hm, &opts);
}
// Log request
MG_INFO(("%.*s %.*s %lu -> %.*s %lu", hm->method.len, hm->method.ptr,
hm->uri.len, hm->uri.ptr, hm->body.len, 3, c->send.buf + 9,
c->send.len));
} }
(void) fn_data;
} }
static void usage(const char *prog) { static void usage(const char *prog) {
@@ -44,6 +71,7 @@ static void usage(const char *prog) {
" -S PAT - SSI filename pattern, default: '%s'\n" " -S PAT - SSI filename pattern, default: '%s'\n"
" -d DIR - directory to serve, default: '%s'\n" " -d DIR - directory to serve, default: '%s'\n"
" -l ADDR - listening address, default: '%s'\n" " -l ADDR - listening address, default: '%s'\n"
" -u DIR - file upload directory, default: unset\n"
" -v LEVEL - debug level, from 0 to 4, default: %d\n", " -v LEVEL - debug level, from 0 to 4, default: %d\n",
MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir, MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir,
s_listening_address, s_debug_level); s_listening_address, s_debug_level);
@@ -66,6 +94,8 @@ int main(int argc, char *argv[]) {
s_ssi_pattern = argv[++i]; s_ssi_pattern = argv[++i];
} else if (strcmp(argv[i], "-l") == 0) { } else if (strcmp(argv[i], "-l") == 0) {
s_listening_address = argv[++i]; s_listening_address = argv[++i];
} else if (strcmp(argv[i], "-u") == 0) {
s_upload_dir = argv[++i];
} else if (strcmp(argv[i], "-v") == 0) { } else if (strcmp(argv[i], "-v") == 0) {
s_debug_level = atoi(argv[++i]); s_debug_level = atoi(argv[++i]);
} else { } else {
@@ -96,6 +126,7 @@ int main(int argc, char *argv[]) {
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", s_listening_address));
MG_INFO(("Web root : [%s]", s_root_dir)); MG_INFO(("Web root : [%s]", s_root_dir));
MG_INFO(("Upload dir : [%s]", s_upload_dir ? s_upload_dir : "unset"));
while (s_signo == 0) mg_mgr_poll(&mgr, 1000); while (s_signo == 0) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr); mg_mgr_free(&mgr);
MG_INFO(("Exiting on signal %d", s_signo)); MG_INFO(("Exiting on signal %d", s_signo));
+18 -27
View File
@@ -15,7 +15,7 @@
static const char *s_url = "http://info.cern.ch"; static const char *s_url = "http://info.cern.ch";
// Print HTTP response and signal that we're done // Print HTTP response and signal that we're done
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) {
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);
@@ -27,53 +27,44 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
// Send request // Send request
mg_printf(c, mg_printf(c,
"GET %s HTTP/1.1\r\n" "GET %s HTTP/1.1\r\n"
"Connection: keep-alive\r\n" "Connection: close\r\n"
"Keep-Alive: timeout=60\r\n"
"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_READ) { } else if (ev == MG_EV_READ) {
// Parse the incoming data ourselves. If we can parse the request, // c->data[0] holds a flag, whether we have parsed the request already
// store two size_t variables in the c->data: expected len and recv len. if (c->data[0] == 0) {
size_t *data = (size_t *) c->data;
if (data[0]) { // Already parsed, simply print received data
data[1] += c->recv.len;
fwrite(c->recv.buf, 1, c->recv.len, stdout);
c->recv.len = 0; // And cleanup the receive buffer. Streming!
if (data[1] >= data[0]) * (bool *) fn_data = true;
} else {
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);
if (n < 0) mg_error(c, "Bad response"); if (n < 0) mg_error(c, "Bad response");
if (n > 0) { if (n > 0) {
fwrite(c->recv.buf + n, 1, c->recv.len - n, stdout); // Print body 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 c->recv.len = 0; // Cleanup receive buffer
if (data[1] >= data[0]) * (bool *) fn_data = true; c->data[0] = 1; // Request parsed, set the flag
} }
} else {
fwrite(c->recv.buf, 1, c->recv.len, stdout);
c->recv.len = 0; // Cleanup the receive buffer
} }
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
*(bool *) fn_data = true; // tell event loop to stop *(bool *) c->fn_data = true; // Done, 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 *) c->fn_data = true; // Error, Tell event loop to stop
} }
(void) ev_data; (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
mg_mgr_init(&mgr); // Initialise event manager mg_mgr_init(&mgr); // Initialise event manager
if (log_level == NULL) log_level = "2"; // If not set, set to DEBUG mg_log_set(MG_LL_INFO); // Set to 0 to disable debug log
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 if (argc > 1) s_url = argv[1]; // Use URL from command line
while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop mg_connect(&mgr, s_url, fn, &done); // Create client connection
mg_mgr_free(&mgr); // Free resources while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr); // Free resources
return 0; return 0;
} }
+1 -2
View File
@@ -41,7 +41,7 @@ static size_t printdata(mg_pfn_t out, void *ptr, va_list *ap) {
return n; return n;
} }
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) {
if (ev == MG_EV_HTTP_MSG) { if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = ev_data; struct mg_http_message *hm = ev_data;
if (mg_http_match_uri(hm, "/api/data")) { if (mg_http_match_uri(hm, "/api/data")) {
@@ -66,7 +66,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
mg_http_serve_dir(c, hm, &opts); mg_http_serve_dir(c, hm, &opts);
} }
} }
(void) fn_data;
} }
static void timer_fn(void *arg) { static void timer_fn(void *arg) {
+1 -2
View File
@@ -26,7 +26,7 @@ static void rpc_mul(struct mg_rpc_req *r) {
// This RESTful server implements the following endpoints: // This RESTful server implements the following endpoints:
// /websocket - upgrade to Websocket, and implement websocket echo server // /websocket - upgrade to Websocket, and implement websocket echo server
// any other URI serves static files from s_web_root // any other URI serves static files from s_web_root
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) {
if (ev == MG_EV_OPEN) { if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1; // c->is_hexdumping = 1;
} else if (ev == MG_EV_WS_OPEN) { } else if (ev == MG_EV_WS_OPEN) {
@@ -51,7 +51,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (io.buf) mg_ws_send(c, (char *) io.buf, io.len, WEBSOCKET_OP_TEXT); if (io.buf) mg_ws_send(c, (char *) io.buf, io.len, WEBSOCKET_OP_TEXT);
mg_iobuf_free(&io); mg_iobuf_free(&io);
} }
(void) fn_data;
} }
static void timer_fn(void *arg) { static void timer_fn(void *arg) {
+1 -2
View File
@@ -7,7 +7,7 @@
// /api/log/static - returns contents of log.txt file // /api/log/static - returns contents of log.txt file
// /api/log/live - hangs forever, and returns live log messages // /api/log/live - hangs forever, and returns live log messages
// 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) {
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/log/static")) { if (mg_http_match_uri(hm, "/api/log/static")) {
@@ -21,7 +21,6 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
mg_http_serve_dir(c, ev_data, &opts); mg_http_serve_dir(c, ev_data, &opts);
} }
} }
(void) fn_data;
} }
static void log_message(const char *filename, const char *message) { static void log_message(const char *filename, const char *message) {
@@ -31,7 +31,7 @@ void mg_random(void *buf, size_t len) { // Use on-board RNG
} }
} }
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) {
if (ev == MG_EV_OPEN) { if (ev == MG_EV_OPEN) {
MG_INFO(("%lu CREATED", c->id)); MG_INFO(("%lu CREATED", c->id));
// c->is_hexdumping = 1; // c->is_hexdumping = 1;
@@ -66,7 +66,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
MG_INFO(("%lu CLOSED", c->id)); MG_INFO(("%lu CLOSED", c->id));
s_conn = NULL; // Mark that we're closed s_conn = NULL; // Mark that we're closed
} }
(void) fn_data;
} }
// Timer function - recreate client connection if it is closed // Timer function - recreate client connection if it is closed
+13
View File
@@ -0,0 +1,13 @@
all: example
micropython/ports/unix/build-standard/micropython main.py
example: micropython
$(MAKE) -C micropython/ports/unix submodules
$(MAKE) -C micropython/ports/unix USER_C_MODULES=../../..
micropython:
git clone https://github.com/micropython/micropython.git
clean:
rm -rf micropython
+9
View File
@@ -0,0 +1,9 @@
# Mongoose as a MicroPython user module
The most basic usage is in `main.py`.
You'll have to configure Mongoose for your desired platform, this example runs on the `unix` port and Mongoose detects it as a known architecture and hence configures itself to use sockets and time base. For other platforms, you'll have to do your homework (there's plenty of Mongoose examples for several platforms); also check respective MicroPython build instructions.
Check [_thread](https://docs.micropython.org/en/latest/library/_thread.html) support for more advanced usage
https://docs.micropython.org/en/latest/develop/cmodules.html
+5
View File
@@ -0,0 +1,5 @@
import mongoose
mongoose.init()
while True:
mongoose.poll(1000)
+28
View File
@@ -0,0 +1,28 @@
// Copyright (c) 2022 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
static void t_fn(void *arg) { // Tick periodically
MG_INFO(("tick"));
(void) arg;
}
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "hi\n");
(void) ev_data;
}
static struct mg_mgr mgr;
void mgmp_init(void) {
mg_mgr_init(&mgr); // Mongoose event manager
mg_log_set(MG_LL_DEBUG); // Set log level
mg_timer_add(&mgr, 1000, MG_TIMER_REPEAT, t_fn, &mgr);
mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, &mgr);
}
void mgmp_poll(int t) {
mg_mgr_poll(&mgr, t);
}
@@ -0,0 +1,17 @@
# Create an INTERFACE library for our C module.
add_library(usermod_mongoose INTERFACE)
# Add our source files to the lib
target_sources(usermod_mongoose INTERFACE
${CMAKE_CURRENT_LIST_DIR}/module.c
${CMAKE_CURRENT_LIST_DIR}/mongoose.c
${CMAKE_CURRENT_LIST_DIR}/main.c
)
# Add the current directory as an include directory.
target_include_directories(usermod_mongoose INTERFACE
${CMAKE_CURRENT_LIST_DIR}
)
# Link our INTERFACE library to the usermod target.
target_link_libraries(usermod INTERFACE usermod_mongoose)
@@ -0,0 +1,9 @@
CEXAMPLE_MOD_DIR := $(USERMOD_DIR)
# Add all C files to SRC_USERMOD.
SRC_USERMOD += $(CEXAMPLE_MOD_DIR)/module.c
SRC_USERMOD += $(CEXAMPLE_MOD_DIR)/mongoose.c
SRC_USERMOD += $(CEXAMPLE_MOD_DIR)/main.c
# add our module folder to include paths
CFLAGS_USERMOD += -I$(CEXAMPLE_MOD_DIR)
+30
View File
@@ -0,0 +1,30 @@
#include "py/runtime.h"
extern void mgmp_init(void);
extern void mgmp_poll(int);
STATIC mp_obj_t mongoose_init(void) {
mgmp_init();
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mongoose_init_obj, mongoose_init);
STATIC mp_obj_t mongoose_poll(mp_obj_t t_obj) {
mgmp_poll(mp_obj_get_int(t_obj));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mongoose_poll_obj, mongoose_poll);
STATIC const mp_rom_map_elem_t mongoose_module_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_mongoose) },
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&mongoose_init_obj) },
{ MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mongoose_poll_obj) },
};
STATIC MP_DEFINE_CONST_DICT(mongoose_module_globals, mongoose_module_globals_table);
const mp_obj_module_t mongoose_module = {
.base = { &mp_type_module },
.globals = (mp_obj_dict_t *)&mongoose_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_mongoose, mongoose_module);
+5 -5
View File
@@ -42,20 +42,20 @@ static size_t pcap_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
return received; return received;
} }
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) {
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;
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_draining = 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 *) c->fn_data));
} else if (ev == MG_EV_CLOSE) { } else if (ev == MG_EV_CLOSE) {
free(fn_data); free(c->fn_data);
} }
} }
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) {
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/debug")) { if (mg_http_match_uri(hm, "/api/debug")) {
@@ -75,7 +75,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
hm->message.ptr); hm->message.ptr);
} }
} }
(void) ev_data, (void) fn_data; (void) ev_data;
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
+55
View File
@@ -0,0 +1,55 @@
PROG ?= ./example # Program we are building
PACK ?= ./pack # Packing executable
DELETE = rm -rf # Command to remove files
GZIP ?= gzip # For compressing files in web_root/
OUT ?= -o $(PROG) # Compiler argument for output file
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
CFLAGS_MONGOOSE += -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
PROG = example.exe # Use .exe suffix for the binary
PACK = pack.exe # Packing executable
CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 # Link against Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
GZIP = echo # No gzip on Windows
endif
# Default target. Build and run program
all: $(PROG)
$(RUN) $(PROG) $(ARGS)
# Build program from sources
$(PROG): $(SOURCES)
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
# Bundle JS libraries (preact, preact-router, ...) into a single file
web_root/bundle.js:
curl -s https://npm.reversehttp.com/preact,preact/hooks,htm/preact,preact-router -o $@
# 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
# 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 $@
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 *.bin *.dSYM mbedtls
+26
View File
@@ -0,0 +1,26 @@
# A complete device dashboard
This example is a demonstration of how Mongoose Library could be integrated
into an embedded device and provide a complete device dashboard with the
following features:
- Authentication: login-protected dashboard
- Multiple logins (with possibly different permissions)
- The Web UI can be fully embedded into the firmware binary, then not
needing a filesystem to serve it; so being resilient to FS problems
- All changes are propagated to all connected clients
## Screenshots
This is a login screen that prompts for user/password
![](screenshots/login.webp)
## Main dashboard
The main dashboard page shows the interactive console
![](screenshots/dashboard.webp)
See a detailed tutorial at https://mongoose.ws/tutorials/device-dashboard/
+1
View File
@@ -0,0 +1 @@
../device-dashboard/certs
+53
View File
@@ -0,0 +1,53 @@
// Copyright (c) 2020-2023 Cesanta Software Limited
// All rights reserved
#include "mongoose.h"
#include "net.h"
#define CONFIG_FILE "settings.bin"
static int s_sig_num;
static void signal_handler(int sig_num) {
signal(sig_num, signal_handler);
s_sig_num = sig_num;
}
bool web_load_settings(void *buf, size_t len) {
bool ok = false;
struct mg_str data = mg_file_read(&mg_fs_posix, CONFIG_FILE);
if (data.ptr == NULL) {
MG_ERROR(("Error reading %s", CONFIG_FILE));
} else if (data.len != len) {
MG_ERROR(("%s size != %lu", CONFIG_FILE, len));
} else {
memcpy(buf, data.ptr, len);
}
free((void *) data.ptr);
return ok;
}
bool web_save_settings(void *buf, size_t len) {
MG_INFO(("Saving to %s", CONFIG_FILE));
return mg_file_write(&mg_fs_posix, CONFIG_FILE, buf, len);
}
int main(void) {
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_mgr_init(&mgr);
web_init(&mgr);
while (s_sig_num == 0) {
mg_mgr_poll(&mgr, 50);
}
mg_mgr_free(&mgr);
MG_INFO(("Exiting on signal %d", s_sig_num));
return 0;
}
+1
View File
@@ -0,0 +1 @@
../../mongoose.c
+1
View File
@@ -0,0 +1 @@
../../mongoose.h
+304
View File
@@ -0,0 +1,304 @@
// Copyright (c) 2023 Cesanta Software Limited
// All rights reserved
#include "net.h"
// Device settings
struct device_settings {
uint32_t magic;
int log_level;
bool mqtt_enabled;
char mqtt_server_url[64];
char mqtt_topic_tx[16];
char mqtt_topic_rx[16];
};
struct conndata {
uint64_t expiration_time; // Modbus request timeout
unsigned long id; // Connection ID waiting for the Modbus response
};
static struct device_settings s_settings;
static const char *s_json_header =
"Content-Type: application/json\r\n"
"Cache-Control: no-cache\r\n";
static uint64_t s_boot_timestamp = 0; // Updated by SNTP
static void set_default_settings(struct device_settings *s) {
s->magic = SETTINGS_MAGIC;
s->log_level = MG_LL_DEBUG;
mg_snprintf(s->mqtt_server_url, sizeof(s->mqtt_server_url), "%s",
"mqtt://broker.hivemq.com:1883");
mg_snprintf(s->mqtt_topic_tx, sizeof(s->mqtt_topic_tx), "%s", "modbus1/tx");
mg_snprintf(s->mqtt_topic_rx, sizeof(s->mqtt_topic_rx), "%s", "modbus1/rx");
}
// This is for newlib and TLS (mbedTLS)
uint64_t mg_now(void) {
return mg_millis() + s_boot_timestamp;
}
// 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
static void sfn(struct mg_connection *c, int ev, void *ev_data) {
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;
s_boot_timestamp = t - mg_millis();
c->is_closing = 1;
} else if (ev == MG_EV_POLL) {
if (mg_millis() > *expiration_time) c->is_closing = 1;
}
}
// SNTP timer function. Sync up time
static void timer_sntp_fn(void *param) {
mg_sntp_connect(param, "udp://time.google.com:123", sfn, NULL);
}
static void setfromjson(struct mg_str json, const char *jsonpath, char *buf,
size_t len) {
char *val = mg_json_get_str(json, jsonpath);
if (val != NULL) mg_snprintf(buf, len, "%s", val);
free(val);
}
static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
struct device_settings settings;
memset(&settings, 0, sizeof(settings));
set_default_settings(&settings);
mg_json_get_bool(body, "$.mqtt_enabled", &settings.mqtt_enabled);
settings.log_level = mg_json_get_long(body, "$.log_level", MG_LL_INFO);
setfromjson(body, "$.mqtt_server_url", settings.mqtt_server_url,
sizeof(settings.mqtt_server_url));
setfromjson(body, "$.mqtt_topic_rx", settings.mqtt_topic_rx,
sizeof(settings.mqtt_topic_rx));
setfromjson(body, "$.mqtt_topic_tx", settings.mqtt_topic_tx,
sizeof(settings.mqtt_topic_tx));
s_settings = settings;
bool ok = web_save_settings(&s_settings, sizeof(s_settings));
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:%d,%m:%m,%m:%m,%m:%m}\n", //
MG_ESC("mqtt_enabled"),
s_settings.mqtt_enabled ? "true" : "false", //
MG_ESC("log_level"), s_settings.log_level, //
MG_ESC("mqtt_server_url"), MG_ESC(s_settings.mqtt_server_url),
MG_ESC("mqtt_topic_rx"), MG_ESC(s_settings.mqtt_topic_rx),
MG_ESC("mqtt_topic_tx"), MG_ESC(s_settings.mqtt_topic_tx));
}
// Modbus handler function
static void mfn(struct mg_connection *c, int ev, void *ev_data) {
struct conndata *cd = (struct conndata *) c->data;
if (ev == MG_EV_READ) {
MG_INFO(("%lu RECEIVED %lu", c->id, c->recv.len));
if (c->recv.len < 8) return; // Less than minimum length, buffer more
uint16_t len = mg_ntohs(*(uint16_t *) &c->recv.buf[4]); // PDU length
if (c->recv.len < len + 4U) return; // Partial frame, buffer more
// Notify parent connection
for (struct mg_connection *t = c->mgr->conns; t != NULL; t = t->next) {
if (t->id == cd->id) mg_call(t, MG_EV_USER, &c->recv);
}
c->is_closing = 1;
} else if (MG_EV_POLL) {
// MG_INFO(("%lu closing tmout %llu", c->id, cd->expiration_time));
if (cd->expiration_time > 0 && cd->expiration_time < mg_millis()) {
c->is_closing = 1;
}
}
(void) ev_data;
}
static void send8(struct mg_connection *c, uint8_t val) {
mg_send(c, &val, sizeof(val));
}
static void send16(struct mg_connection *c, uint16_t val) {
send8(c, (uint8_t) (val >> 8) & 255);
send8(c, (uint8_t) (val & 255));
}
static struct mg_connection *start_modbus_request(struct mg_mgr *mgr,
struct mg_str json,
unsigned long cid) {
struct mg_connection *c = NULL;
char *url = mg_json_get_str(json, "$.url");
long timeout = mg_json_get_long(json, "$.timeout", 750);
uint8_t id = (uint8_t) mg_json_get_long(json, "$.id", 1);
uint16_t reg = (uint16_t) mg_json_get_long(json, "$.reg", 1);
uint8_t func = (uint8_t) mg_json_get_long(json, "$.func", 0);
uint16_t nregs = (uint16_t) mg_json_get_long(json, "$.nregs", 1);
MG_INFO(("%lu REQUEST: %.*s", cid, json.len, json.ptr));
if (func == 0) {
MG_ERROR(("Set func to a valid modbus function code"));
} else if ((c = mg_connect(mgr, url, mfn, NULL)) == NULL) {
MG_ERROR(("Failed to start modbus connection at %M", MG_ESC(url)));
} else {
send16(c, 1); // TID. Use random?
send16(c, 0); // Protocol identifier: 0 (modbus)
uint16_t *lp = (uint16_t *) &c->send.buf[c->send.len];
send16(c, 0); // Length: to be set later
size_t len = c->send.len;
send8(c, id); // Client ID
send8(c, func); // Function
if (func == 3 || func == 4 || func == 6 || func == 16) {
send16(c, reg); // Start register
send16(c, nregs); // Number of registers
if (func == 16) { // Fill in register values to write
send8(c, (uint8_t) (nregs * 2)); // Send number of bytes
for (uint16_t i = 0; i < nregs; i++) {
char path[20];
mg_snprintf(path, sizeof(path), "$.values[%hu]", i);
uint16_t r = (uint16_t) mg_json_get_long(json, path, 0);
send16(c, r);
}
}
} else if (func == 43) {
send8(c, 14); // MEI
send8(c, 1); // Basic device ID: 1
send8(c, 0); // Object ID: 0 (vendor name)
}
*lp = mg_htons((uint16_t) (c->send.len - len)); // Set length field
mg_hexdump(c->send.buf, c->send.len);
MG_INFO(("%lu SENDING %lu", c->id, c->send.len));
struct conndata *cd = (struct conndata *) c->data;
cd->id = cid; // Store parent connection ID
cd->expiration_time = mg_millis() + timeout;
}
free(url);
return c;
}
static void handle_modbus_exec(struct mg_connection *c, struct mg_str body) {
struct mg_connection *mc = start_modbus_request(c->mgr, body, c->id);
if (mc == NULL) {
mg_http_reply(c, 200, s_json_header, "false\n");
} else {
struct conndata *cd = (struct conndata *) c->data;
cd->expiration_time = mg_millis() + 1500;
}
}
// Print modbus response
static size_t print_mb_resp(void (*out)(char, void *), void *ptr, va_list *ap) {
int func = va_arg(*ap, int);
size_t len = va_arg(*ap, size_t);
uint8_t *buf = va_arg(*ap, uint8_t *);
size_t n = 0;
if (func == 3 || func == 4) {
// For "multiple" read responses, print registers
n += mg_xprintf(out, ptr, "[");
for (size_t ofs = 1; ofs < len; ofs += 2) {
uint16_t reg = mg_ntohs(*(uint8_t *) &buf[ofs]);
n += mg_xprintf(out, ptr, "%s%hu", ofs == 1 ? "" : ",", reg);
}
n += mg_xprintf(out, ptr, "]");
} else if (func == 43) {
n += mg_xprintf(out, ptr, "{");
if (len >= 6) {
n += mg_xprintf(out, ptr, "%m:%d", MG_ESC("mei"), buf[0]);
n += mg_xprintf(out, ptr, ",%m:%d", MG_ESC("devid"), buf[1]);
n += mg_xprintf(out, ptr, ",%m:%d", MG_ESC("conformity"), buf[2]);
n += mg_xprintf(out, ptr, ",%m:%d", MG_ESC("more"), buf[3]);
n += mg_xprintf(out, ptr, ",%m:%d", MG_ESC("next"), buf[4]);
n += mg_xprintf(out, ptr, ",%m:[", MG_ESC("objects"));
size_t ofs = 6;
while (ofs < len) {
if (ofs > 6) n += mg_xprintf(out, ptr, ",");
n += mg_xprintf(out, ptr, "{");
n += mg_xprintf(out, ptr, "%m:%d", MG_ESC("id"), buf[ofs++]);
if (ofs < len) {
uint8_t olen = buf[ofs++];
if (olen > (uint8_t) (len - ofs)) olen = (uint8_t) (len - ofs);
n += mg_xprintf(out, ptr, ",%m:%m", MG_ESC("value"), mg_print_esc,
olen, &buf[ofs]);
ofs += olen;
}
n += mg_xprintf(out, ptr, "}");
}
n += mg_xprintf(out, ptr, "]");
}
n += mg_xprintf(out, ptr, "}");
} else {
n += mg_xprintf(out, ptr, "null");
}
return n;
}
// HTTP request handler function
static void fn(struct mg_connection *c, int ev, void *ev_data) {
struct conndata *cd = (struct conndata *) c->data;
if (ev == MG_EV_ACCEPT) {
if (c->fn_data != NULL) { // TLS listener!
struct mg_tls_opts opts = {0};
opts.cert = mg_unpacked("/certs/server_cert.pem");
opts.key = mg_unpacked("/certs/server_key.pem");
mg_tls_init(c, &opts);
}
} else if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
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/settings/set")) {
handle_settings_set(c, hm->body);
} else if (mg_http_match_uri(hm, "/api/modbus/exec")) {
handle_modbus_exec(c, hm->body);
} else if (mg_http_match_uri(hm, "/api/device/reset")) {
mg_timer_add(c->mgr, 500, 0, (void (*)(void *)) mg_device_reset, NULL);
mg_http_reply(c, 200, s_json_header, "true\n");
} else {
struct mg_http_serve_opts opts;
memset(&opts, 0, sizeof(opts));
#if MG_ARCH == MG_ARCH_UNIX || MG_ARCH == MG_ARCH_WIN32
opts.root_dir = "web_root"; // On workstations, use filesystem
#else
opts.root_dir = "/web_root"; // On embedded, use packed files
opts.fs = &mg_fs_packed;
#endif
mg_http_serve_dir(c, ev_data, &opts);
}
MG_DEBUG(("%lu %.*s %.*s", c->id, (int) hm->method.len, hm->method.ptr,
(int) hm->uri.len, hm->uri.ptr));
} else if (ev == MG_EV_POLL) {
if (cd->expiration_time > 0 && cd->expiration_time < mg_millis()) {
cd->expiration_time = 0;
mg_http_reply(c, 200, s_json_header, "false\n");
}
} else if (ev == MG_EV_USER) {
cd->expiration_time = 0; // Cleanup timeout setting
struct mg_iobuf *io = ev_data;
uint8_t f = io->buf[7];
mg_http_reply(c, 200, s_json_header, "{%m:%s,%m:%m,%m:%M}\n", //
MG_ESC("success"), "true", //
MG_ESC("raw"), mg_print_hex, io->len - 7, io->buf + 7, //
MG_ESC("data"), print_mb_resp, f, io->len - 8, io->buf + 8);
}
}
void web_init(struct mg_mgr *mgr) {
set_default_settings(&s_settings);
web_load_settings(&s_settings, sizeof(s_settings));
mg_http_listen(mgr, HTTP_URL, fn, NULL);
mg_http_listen(mgr, HTTPS_URL, fn, (void *) 1);
mg_timer_add(mgr, 10 * 60 * 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"
#ifdef __cplusplus
extern "C" {
#endif
#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
void web_init(struct mg_mgr *mgr); // Initialise Web UI
bool web_load_settings(void *buf, size_t len); // Load from disk/flash
bool web_save_settings(void *buf, size_t len); // Save to disk/flash
#define SETTINGS_MAGIC 0xaabbccdd
#ifdef __cplusplus
}
#endif
File diff suppressed because it is too large. Load diff
@@ -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
+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
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en" class="h-full bg-white">
<head>
<title></title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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>
<body class="h-full"></body>
<script src="history.min.js"></script>
<script type="module" src="main.js"></script>
</html>
@@ -0,0 +1 @@
/*! tailwindcss v3.3.5 | 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-feature-settings:inherit;font-variation-settings: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}dialog{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-auto{right:auto}.top-0{top:0}.z-10{z-index:10}.z-\[48\]{z-index:48}.z-\[60\]{z-index:60}.m-4{margin:1rem}.my-0{margin-top:0;margin-bottom:0}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-2{margin-top:.5rem;margin-bottom:.5rem}.mb-1{margin-bottom:.25rem}.ml-3{margin-left:.75rem}.ml-4{margin-left:1rem}.mr-2{margin-right:.5rem}.mt-1{margin-top:.25rem}.mt-2{margin-top:.5rem}.mt-3{margin-top:.75rem}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.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%}.max-h-64{max-height:16rem}.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-full{width:100%}.max-w-sm{max-width:24rem}.flex-1{flex:1 1 0%}.flex-shrink-0,.shrink-0{flex-shrink:0}.grow-0{flex-grow:0}.basis-\[30px\]{flex-basis:30px}.-translate-x-full{--tw-translate-x:-100%}.-translate-x-full,.translate-x-0{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-0{--tw-translate-x:0px}.translate-x-5{--tw-translate-x:1.25rem}.translate-x-5,.translate-y-0{transform:tranLine truncated
+364
View File
@@ -0,0 +1,364 @@
// NOTE: API calls must start with 'api/' in order to serve the app at any URI
'use strict';
import { h, render, useState, useEffect, useRef, html, Router } from './bundle.js';
const Icons = {
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>`,
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>`,
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> `,
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>`,
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>`,
};
function TextValue({value, setfn, disabled, placeholder, type, addonRight, addonLeft, attr, min, max, step, mult}) {
const [bg, setBg] = useState('bg-white');
useEffect(() => { if (type == 'number') checkval(+min, +max, +value); }, []);
step ||= '1', mult ||= 1;
const checkval = function(min, max, 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 m = step.match(/^.+\.(.+)/);
const digits = m ? m[1].length : 0;
const onchange = ev => {
let v = ev.target.value;
if (type == 'number') {
checkval(+min, +max, +v);
v = +(parseFloat(v) / mult).toFixed(digits);
}
setfn(v);
};
if (type == 'number') value = +(value * mult).toFixed(digits);
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} value=${value}
step=${step} min=${min} max=${max}
onchange=${onchange} ...${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} />
${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}<//>` }
<//>`;
};
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]}<//>`) }
<//>`;
};
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>`;
};
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)}<//>
<//>`;
};
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' : ''}" />
<//>`
};
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.fail} 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" />
<//>
<//>
<//>
<//>
<//>
<//>
<//>
<//>`;
};
const Logo = props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 13 13"><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 ModbusLogo = props => html`
<svg version="1.1" class=${props.class} xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 652 195">
<style type="text/css"> .mb_st0{fill:#F49500;} .mb_st1{fill:#FCC000;} .mb_st2{fill:#03A239;} .mb_st3{fill:#1192CF;} </style>
<g transform="translate(0 -225)">
<path class="mb_st0" d="M189.4,323.1c0-36.5-31-66.1-69.3-66.1c-38.3,0-69.3,29.6-69.3,66.1c0,36.5,31,66.1,69.3,66.1 C158.4,389.1,189.4,359.6,189.4,323.1"/>
<path class="mb_st1" d="M127.3,251.6c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8 C115.7,225.8,127.3,237.4,127.3,251.6"/>
<path class="mb_st1" d="M198.9,273.5c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8 C187.4,247.7,198.9,259.3,198.9,273.5"/>
<path class="mb_st1" d="M223.5,341.5c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8 C212,315.8,223.5,327.3,223.5,341.5"/>
<path class="mb_st1" d="M168.9,391.3c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8 C157.3,365.5,168.9,377,168.9,391.3"/>
<path class="mb_st1" d="M94.3,373.4c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8 C82.7,347.6,94.3,359.2,94.3,373.4"/>
<path class="mb_st1" d="M73.5,303.1c0,14.2-11.5,25.8-25.8,25.8c-14.2,0-25.8-11.5-25.8-25.8c0-14.2,11.5-25.8,25.8-25.8 C61.9,277.3,73.5,288.9,73.5,303.1"/>
<polygon class="mb_st2" points="125.3,308 123.4,301.4 119.4,309 105.1,259.6 112.3,263.8 110.5,257.5 100.5,252 94.9,262 96.8,268.3 100.6,260.9 115,310.3 107.6,306 109.5,312.6 119.6,318 "/> <polygon class="mb_st2" points="137.7,320.1 142.1,316 134.1,315.4 167.7,284.3 167.7,292.1 171.9,288.2 171.7,277.3 160.9,276.3 156.7,280.3 164.5,280.8 130.9,312 131,303.9 126.5,308.1 126.8,319 "/>
<polygon class="mb_st2" points="82.1,373.4 86.6,368.9 78.4,368.7 112.1,335.5 112.2,343.5 116.5,339.3 116.2,328.2 105.1,327.7 100.9,331.9 108.8,332.1 75.1,365.4 75,357.2 70.5,361.6 71,372.7 "/>
<polygon class="mb_st2" points="108.7,312.2 102.3,310.2 106.4,317.6 58.1,302.8 65.4,299 59.3,297.1 49.3,302.5 54.6,312.6 60.7,314.5 56.8,307.3 105,322.1 97.5,325.9 103.9,327.9 113.9,322.4 "/>
<polygon class="mb_st2" points="147.9,378.9 145.9,373 142.1,380.2 127.4,336 134.6,339.3 132.7,333.7 122.6,329.3 117.3,338.8 119.2,344.4 122.9,337.5 137.7,381.7 130.3,378.2 132.3,384.1 142.4,388.5 "/>
<polygon class="mb_st2" points="183.6,332.6 177.6,330.8 181.3,338.1 136.5,325.1 143.3,321.1 137.7,319.5 128.3,325.2 133.2,335 138.8,336.7 135.2,329.6 180,342.5 173,346.6 179,348.4 188.3,342.5 "/>
<polygon class="mb_st3" points="129.6,357.5 143.8,286.8 176.2,286.8 190.8,329.3 222.5,286.8 255,286.8 240.6,357.5 220.6,357.5 230.4,307.5 193.3,357.5 177.6,357.5 159.9,307.6 150.1,357.5 "/>
<path class="mb_st3" d="M320.1,310.8c-2.8-2.8-12.6-4-24-4c-17.8,0-26.6,2.9-31.9,8.2c-5.3,5.3-7.9,25.3-7.9,27.5 c0,2.1-0.8,7.8,3.1,11.7c3.9,3.9,13.3,4.2,27.8,4.2c14.5,0,22.5-2.5,27.9-6.2c5.4-3.6,8.6-22.7,9.2-28.6 C324.8,317.6,322.9,313.5,320.1,310.8 M305.9,333.1c-1.5,5.3-2.1,7.5-3.9,8.8c-1.8,1.3-11,2.2-13,2.2c-2,0-11.8-0.2-13.8-2.2 c-2-2,1-16.9,3.9-18.5c2.9-1.6,8.9-2.1,13.2-2c6.6,0.1,11.6,0.3,13,1.7C306.7,324.7,307.4,327.8,305.9,333.1"/>
<path class="mb_st3" d="M389,286.8l-5.5,28.1c-2.6-6.2-7.5-8.1-18-8.1c-17.8,0-20.9,2.9-26.2,8.2s-7.9,25.3-7.9,27.5 c0,2.1-0.8,7.8,3.1,11.7c3.9,3.9,7,4.2,21.1,4.2c13.6,0,18.5-4.6,20.9-7.7l-1.3,6.8h17.8l13.8-70.7H389z M376,342 c-1.8,1.3-11,2.2-13.1,2.2s-11.8-0.2-13.8-2.2c-2-2,1-16.9,3.9-18.5c2.9-1.6,8.9-2.1,13.2-2c6.6,0.1,11.6,0.3,13.1,1.7 c0.9,0.9,1.5,2.6,1.4,5.1l-1.2,5.9C378.3,338.8,377.7,340.8,376,342"/>
<path class="mb_st3" d="M472.3,309.8c-2.2-1.9-7.8-3-18.4-3c-13.3,0-18.8,2.7-23,8.4l5.5-28.3H419L405,357.5h17.7l1.4-7.2 c2.1,4.9,5.3,8.1,20,8.1c14.5,0,18.4-0.7,23-5.4c4.9-4.9,8.7-23.4,9.2-29.4C476.8,317.7,476,312.9,472.3,309.8 M458.4,333.1 c-1.5,5.3-2.1,7.5-3.9,8.8c-1.8,1.3-11,2.2-13,2.2c-2,0-11.8-0.2-13.8-2.2c-2-2,1-16.9,3.9-18.5c2.9-1.6,8.9-2.1,13.2-2 c6.6,0.1,11.6,0.3,13,1.7C459.3,324.7,459.9,327.8,458.4,333.1"/>
<path class="mb_st3" d="M536.2,307.4h-0.7c0,0-4.7,27.5-9,31.8c-4.3,4.3-10.6,4.6-12.8,4.6c-2.1,0-8.2-0.9-10.4-3.1 c-2.7-2.8,3.2-28.7,4.3-33.3h-17.3c0,0-7,31.7-7,36.6c0,3.9,0.4,11.1,11.2,13.6c2.5,0.6,5.5,0.9,9.2,0.9c2.9,0,5.6-0.3,8-0.9 c8.4-2,13.7-6.8,16.6-10.4l-2,10.4h16.5l9.8-50.1h0H536.2z"/>
<path class="mb_st3" d="M605.6,323h16.1c0,0,0.3-3.9,0.3-5.5c0-1.7,0.3-3.9-3.1-7.3c-3.4-3.4-21.8-3.3-24.9-3.3 c-3.1,0-19.8-0.3-24.5,3.6c-4,3.2-7.3,7.4-7.3,15.6c0,8.3,5.5,11.5,16.4,11.5h19.5c2.7,0,6.1,0.8,6.1,3.3c0,2.5-1.9,5.9-8.7,5.9 h-12.9c-3.1,0-8.7-0.5-8.7-4v-1.6h-16.1c0,0-0.8,2.8-0.8,5.2c0,2.4,0.4,5.5,3.1,8.2c2.7,2.7,14,3.9,25.6,3.9 c11.6,0,24.1-0.5,28.3-4.8c4.3-4.3,6.2-12.3,6.2-16.5c0-4.2-1.4-6.2-2.8-7.6c-1.3-1.3-5.3-3.7-23-3.7c-15.8,0-16.3-1.5-16.3-3 c0-1.2,0.2-4.2,12.7-4.2C603.3,318.6,605.6,319.2,605.6,323"/>
</g>
</svg>`;
function Header({logout, user, setShowSidebar, showSidebar}) {
return html`
<div class="bg-white sticky top-0 z-[48] xw-full border-b py-2 ${showSidebar && 'pl-72'} transition-all duration-300 transform">
<div class="px-2 w-full py-0 my-0 flex items-center">
<button type="button" onclick=${ev => setShowSidebar(v => !v)} class="text-slate-400">
<${Icons.bars3} class="h-6" />
<//>
<div class="flex flex-1 gap-x-4 self-stretch lg:gap-x-6">
<div class="relative flex flex-1"><//>
<div class="flex items-center gap-x-4 lg:gap-x-6">
<span class="text-sm text-slate-400">developed with Mongoose Library<//>
<div class="hidden lg:block lg:h-4 lg:w-px lg:bg-gray-200" aria-hidden="true"><//>
<span><${ModbusLogo} class="h-10" /><//>
<//>
<//>
<//>
<//>`;
};
function Sidebar({url, show}) {
const NavLink = ({title, icon, href, url}) => html`
<div>
<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">
<${icon} class="w-6 h-6"/>
${title}
<///>
<//>`;
return html`
<div class="bg-violet-100 hs-overlay hs-overlay-open:translate-x-0
-translate-x-full transition-all duration-300 transform
fixed top-0 left-0 bottom-0 z-[60] w-72 bg-white border-r
border-gray-200 overflow-y-auto scrollbar-y
${show && 'translate-x-0'} right-auto bottom-0">
<div class="flex flex-col m-4 gap-y-6">
<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} />
<//>
<//>
<//>`;
};
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 [slaveUrl, setSlaveUrl] = useState('tcp://192.168.0.100:502');
//const [slaveUrl, setSlaveUrl] = useState('tcp://127.0.0.1:10502');
const [slaveId, setSlaveId] = useState(1);
const [startReg, setStartReg] = useState(0);
const [values, setValues] = useState('0');
const [numRegs, setNumRegs] = useState(1);
const [func, setFunc] = useState(3);
const [connectTimeout, setConnectTimeout] = useState(1500);
const [response, setResponse] = useState('');
const onexec = ev => (setResponse('') || 1) && fetch('api/modbus/exec' , {
method: 'post', body: JSON.stringify({
url: slaveUrl, id: slaveId, reg: startReg,
values: (values || '0').split(',').map(v => parseInt(v)),
timeout: connectTimeout, func: func, nregs: numRegs
})
}).then(r => r.json())
.then(r => setResponse(JSON.stringify(r, null, 2)));
const funcs = [
[3, '3: Read Multiple Holding Registers'],
[4, '4: Read Multiple Input Registers'],
// [6, '6: Write Single Holding Register'],
[16, '16: Write Multiple Holding Registers'],
[43, '43: Read Device ID'],
];
const setnumregs = function(val) { // Number of registers changed
setValues(Array(val).fill(0).join(',')); // Values: zero-filled array
return setNumRegs(val); // And set the number of regs
};
return html`
<div class="m-4 grid grid-cols-1 gap-4 md:grid-cols-1">
<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">
Modbus Master Console
<//>
<div class="py-2 px-4 grid grid-cols-1 gap-4 md:grid-cols-2">
<div class="flex-1 flex flex-col relative">
<${Setting} title="Slave URL" value=${slaveUrl} setfn=${val => setSlaveUrl(val)} type="" />
<${Setting} title="Connect timeout" value=${connectTimeout} setfn=${val => setConnectTimeout(val)} type="number" addonRight="milliseconds" min="50" max="5000"/>
<${Setting} title="Slave ID" value=${slaveId} setfn=${val => setSlaveId(val)} type="number" min="1" max="253" addonRight="1 .. 253" />
<${Setting} title="Starting Register" value=${startReg} setfn=${val => setStartReg(val)} type="number" min="0" max="65535" addonRight="0 .. 65535" />
<${Setting} title="Register Count" value=${numRegs} setfn=${setnumregs} type="number" min="1" max="125" addonRight="1 .. 125" />
<${Setting} title="Value(s), e.g. 1 or 1,2,3" value=${values} setfn=${val => setValues(val)} addonRight="1 .. 65535" cls=${func == 16 || func == 6 ? '' : 'hidden'} />
<${Setting} title="Function" value=${func} setfn=${val => setFunc(val)} type="select" options=${funcs} />
<div class="mb-1 mt-3 flex place-content-end gap-2 ">
<${Button} icon=${Icons.bolt} onclick=${onexec} title="Execute" />
<//>
<//>
<div class="flex-1 flex flex-col relative">
<pre class="flex-1 bg-gray-100 p-2 text-sm overflow-auto max-h-64">${response}<//>
<//>
<//>
<//>
<//>`;
};
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">
${saveResult && html`<${Notification} ok=${saveResult.status}
text=${saveResult.message} close=${() => setSaveResult(null)} />`}
<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">
MQTT Forwarding
<//>
<div class="py-2 px-5 flex-1 flex flex-col relative">
<${Setting} title="Enable MQTT forwarding" value=${settings.mqtt_enabled} setfn=${mksetfn('mqtt_enabled')} type="switch" />
<${Setting} title="MQTT Server URL" value=${settings.mqtt_server_url} setfn=${mksetfn('mqtt_server_url')} />
<${Setting} title="MQTT Topic RX" value=${settings.mqtt_topic_rx} setfn=${mksetfn('mqtt_topic_rx')} />
<${Setting} title="MQTT Topic TX" value=${settings.mqtt_topic_tx} setfn=${mksetfn('mqtt_topic_tx')} />
<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}>
<div>
MQTT forwarding sends MQTT message to the TX topic every time
a Modbus slave was successfully interrogated, in either manual
or automatic mode.
<//>
<div class="mt-2">
The RX topic can be used to send Modbus commands over MQTT. The
command should be a JSON message of the following format:
<//>
<div class="mt-2">
<code>{"url": "tcp://IP:PORT", "reg": NUM, "id": NUM, "func": NUM}<//>
<//>
<//>
<//>
<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">
Console Log
<//>
<div class="py-2 px-5 flex-1 flex flex-col relative">
<${Setting} title="Log Level" value=${settings.log_level} setfn=${mksetfn('log_level')} type="select" addonLeft="0-3" options=${logOptions} />
<div class="mb-1 mt-3 flex place-content-end"><${Button} icon=${Icons.save} onclick=${onsave} title="Save Settings" /><//>
<//>
<//>
<//>`;
};
const App = function({}) {
const [url, setUrl] = useState('/');
const [showSidebar, setShowSidebar] = useState(true);
return html`
<div class="min-h-screen bg-slate-100">
<${Sidebar} url=${url} show=${showSidebar} />
<${Header} 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" />
<//>
<//>
<//>`;
};
window.onload = () => render(h(App), document.body);
+2 -2
View File
@@ -38,7 +38,7 @@ static int s_qos = 1;
#include "mongoose.h" #include "mongoose.h"
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) {
if (ev == MG_EV_OPEN) { if (ev == MG_EV_OPEN) {
// c->is_hexdumping = 1; // c->is_hexdumping = 1;
} else if (ev == MG_EV_CONNECT) { } else if (ev == MG_EV_CONNECT) {
@@ -84,7 +84,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE) { if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE) {
MG_INFO(("Got event %d, stopping...", ev)); MG_INFO(("Got event %d, stopping...", ev));
*(bool *) fn_data = true; // Signal that we're done *(bool *) c->fn_data = true; // Signal that we're done
} }
} }
+1 -2
View File
@@ -24,7 +24,7 @@ static void signal_handler(int signo) {
s_signo = signo; s_signo = signo;
} }
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) {
if (ev == MG_EV_OPEN) { if (ev == MG_EV_OPEN) {
MG_INFO(("%lu CREATED", c->id)); MG_INFO(("%lu CREATED", c->id));
// c->is_hexdumping = 1; // c->is_hexdumping = 1;
@@ -65,7 +65,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
MG_INFO(("%lu CLOSED", c->id)); MG_INFO(("%lu CLOSED", c->id));
s_conn = NULL; // Mark that we're closed s_conn = NULL; // Mark that we're closed
} }
(void) fn_data;
} }
// Timer function - recreate client connection if it is closed // Timer function - recreate client connection if it is closed
+4
View File
@@ -0,0 +1,4 @@
# for internal testing purposes
all clean example:
$(MAKE) -C device $@ CFLAGS_EXTRA=$(CFLAGS_EXTRA)
@@ -0,0 +1,16 @@
PWD := $(shell pwd)
NPX ?= npx
all: bundle.js main.css
make -C ../../http-server ARGS="-d $(PWD)"
# Bundle JS libraries (preact, preact-router, ...) into a single file
bundle.js:
curl -s https://npm.reversehttp.com/preact,preact/hooks,htm/preact,preact-router -o $@
# Create optimised CSS. Prerequisite: npm -g i tailwindcss tailwindcss-font-inter
main.css: index.html $(wildcard *.js)
$(NPX) tailwindcss -o $@ --minify
clean:
true
+1
View File
@@ -0,0 +1 @@
../../device-dashboard/web_root/bundle.js
@@ -0,0 +1,15 @@
<!DOCTYPE html>
<html lang="en" class="h-full bg-white">
<head>
<title></title>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<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" />
<script src="https://unpkg.com/mqtt/dist/mqtt.min.js"></script>
</head>
<body class="h-full"></body>
<script type="module" src="main.js"></script>
</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: }.pointer-events-none{pointer-events:none}.absolute{position:absolute}.relative{position:relative}.right-4{right:1rem}.top-0{top:0}.top-4{top:1rem}.my-1{margin-top:.25rem;margin-bottom:.25rem}.my-auto{margin-top:auto;margin-bottom:auto}.mb-4{margin-bottom:1rem}.mt-3{margin-top:.75rem}.inline-block{display:inline-block}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.hidden{display:none}.h-5{height:1.25rem}.h-6{height:1.5rem}.h-full{height:100%}.min-h-screen{min-height:100vh}.w-11{width:2.75rem}.w-24{width:6rem}.w-4{width:1rem}.w-5{width:1.25rem}.w-60{width:15rem}.w-64{width:16rem}.w-96{width:24rem}.w-full{width:100%}.max-w-md{max-width:28rem}.flex-1{flex:1 1 0%}.flex-shrink-0{flex-shrink:0}.flex-grow,.grow{flex-grow:1}.translate-x-0{--tw-translate-x:0px}.translate-x-0,.translate-x-5{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.translate-x-5{--tw-translate-x:1.25rem}.transform{transform:translate(var(--tw-translate-x),var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}@keyframes spin{to{transform:rotate(1turn)}}.animate-spin{animation:spin 1s linear infinite}.cursor-pointer{cursor:pointer}.grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.gap-1{gap:.25rem}.gap-2{gap:.5rem}.gap-3{gap:.75rem}.gap-4{gap:1rem}.divide-y>:not([hidden])~:not([hidden]){--tw-divide-y-reverse:0;border-top-widtLine truncated
+615
View File
@@ -0,0 +1,615 @@
'use strict';
import {h, html, render, useEffect, useRef, useState} from './bundle.js';
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 DefaultTopic = 'mg_mqtt_dashboard';
const DefaultUrl = location.protocol == 'https:'
? 'wss://broker.hivemq.com:8884/mqtt'
: 'ws://broker.hivemq.com:8000/mqtt';
const DefaultDeviceConfig = {pin_map: [], pin_state: [], log_level: 0, pin_count: 0};
const Delay = (ms, val) => new Promise(resolve => setTimeout(resolve, ms, val));
const handleFetchError = r => r.ok || alert(`Error: ${r.statusText}`);
const LabelClass = 'text-sm truncate text-gray-500 font-medium my-auto whitespace-nowrap';
const BadgeClass = 'flex-inline text-sm rounded-md rounded px-2 py-0.5 ring-1 ring-inset';
const InputClass = '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 rounded border';
const TitleClass = 'font-semibold xuppercase xtext-gray-600';
const Colors = {
green: 'bg-green-100 text-green-900 ring-green-300',
yellow: 'bg-yellow-100 text-yellow-900 ring-yellow-300',
info: 'bg-zinc-100 text-zinc-900 ring-zinc-300',
red: 'bg-red-100 text-red-900 ring-red-300',
};
let MqttClient;
const Icons = {
logo: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="59.1" height="75.7" viewBox="0 0 1514 1182"> <path style="fill: #2f409a;" d="m52.8 572h345.1v346.5h-345.1zm-10.9-434.2c-12.6 6.4-32.2 20.9-35.5 37.9 0 0-6.1 35.9-6.1 36.8-2.1 13.1 10.5 10 15.8 6.7 53.9-31.8 113.9-60.7 168.7-77.5 258.2-78.2 585.9-80.2 643.1 88.8 40.3 117.1-123.6 214.8-216.1 258.4-35.7 16.8-47.6 32.8-50 49.8-2 15.3 3.2 37.8 23.8 68.3 129.7 194.8 515 474 893.1 571.7 15.7 4.2 23.5 2.8 29.9-1.1 3.5-2 7.9-4.8 1.1-8.2-251.1-132.2-490.9-353.9-610.9-525.1-27.4-39.5-34.5-65.9-32.7-87.2 1.7-18.4 18.7-34.5 54.8-51.4 134.1-62.3 270.9-157.1 230-314.9-59.5-226.9-700.5-260.1-1109-53z"/></svg>`,
activity: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="22 12 18 12 15 21 9 3 6 12 2 12"></polyline></svg>`,
refresh: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><polyline points="1 20 1 14 7 14"></polyline><path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"></path></svg>`,
info: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><line x1="12" y1="8" x2="12" y2="12"></line><line x1="12" y1="16" x2="12.01" y2="16"></line></svg>`,
login: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path><polyline points="10 17 15 12 10 7"></polyline><line x1="15" y1="12" x2="3" y2="12"></line></svg>`,
logout: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"></path><polyline points="16 17 21 12 16 7"></polyline><line x1="21" y1="12" x2="9" y2="12"></line></svg>`,
menu: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg>`,
upload: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="17 8 12 3 7 8"></polyline><line x1="12" y1="3" x2="12" y2="15"></line></svg>`,
monitor: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect><line x1="8" y1="21" x2="16" y2="21"></line><line x1="12" y1="17" x2="12" y2="21"></line></svg>`,
settings: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>`,
download: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`,
file: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg>`,
check: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"></polyline></svg>`,
rollback: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="11 17 6 12 11 7"></polyline><polyline points="18 17 13 12 18 7"></polyline></svg>`,
save: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"></path><polyline points="17 21 17 13 7 13 7 21"></polyline><polyline points="7 3 7 8 15 8"></polyline></svg>`,
bolt: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>`,
delete: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`,
};
export function Button({title, onclick, disabled, extraClass, 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-0.5 text-sm font-semibold text-white shadow-sm ${colors} ${extraClass}"
ref=${ref} onclick=${cb} disabled=${disabled || spin} >
${title}
<${spin ? Icons.refresh : icon} class="w-4 ${spin ? 'animate-spin' : ''}" />
<//>`
};
function UploadFileButton({oncomplete, onprogress, onfileselect, url, title, colors, accept, extraClass}) {
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 => {
input.fn = null;
const res = oncomplete ? oncomplete(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 +
'&file=' + encodeURIComponent(fileName);
onprogress && onprogress(offset, fileData.length, fileName);
fetch(fullUrl, opts)
.then(function(res) {
if (!res.ok) {
finish(false);
} else if (offset >= fileData.length) {
finish(true);
} else {
sendChunk(offset + chunk.length);
}
});
};
sendChunk(0);
});
};
const onchange = function(ev) {
if (!ev.target.files[0]) return;
let r = new FileReader(), f = ev.target.files[0];
if (onfileselect && !onfileselect(f.name)) {
ev.target.value = '';
return;
}
r.readAsArrayBuffer(f);
r.onload = function() {
input.fn = sendFileData(url, f.name, new Uint8Array(r.result), 2048);
ev.target.value = '';
ev.preventDefault();
btn.current && btn.current.base.click();
};
};
const onclick = function(ev) {
//if (!input.fn) input.current.click(); // No upload in progress, show file dialog
};
return html`
<div class="inline-flex flex-col ${extraClass}">
<input class="hidden" type="file" ref=${input} onchange=${onchange} accept=${accept} />
<${Button} title=${title} icon=${Icons.download} onclick=${onclick} ref=${btn} colors=${colors} />
<//>`;
};
function Toggle({value, onclick, disabled}) {
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} disabled=${disabled}
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>`;
};
function Header({topic, setTopic, url, setUrl, connected}) {
const forbiddenChars = ['$', '*', '+', '#', '/'];
const onClick = () => {
const isValidTopic = val => !forbiddenChars.some(char => val.includes(char));
if (isValidTopic(topic)) {
localStorage.setItem('topic', topic)
setTopic(topic);
window.location.reload();
} else {
setSaveResult('Error: The topic cannot contain these characters: ' + forbiddenChars);
}
};
return html`
<div class="top-0 py-2 border-b-2 bg-stone-100">
<div class="flex px-4">
<div class="flex grow gap-4">
<h1 class="text-2xl font-semibold">Device Management Dashboard</h1>
<//>
<div class="flex gap-2 items-center">
<div class="flex w-96 align-center justify-between gap-2">
<span class=${LabelClass}>MQTT Server<//>
<input disabled=${!connected} value=${url}
onchange=${ev => setUrl(ev.target.value)} class=${InputClass} />
<//>
<div class="flex w-64 align-center justify-between gap-2">
<span class=${LabelClass}>Root Topic<//>
<input disabled=${!connected} value=${topic}
onchange=${ev => setTopic(ev.target.value)} class=${InputClass} />
<//>
<${Button} icon=${Icons.bolt} onclick=${onClick} title=${connected ? 'Disconnect' : 'Connect'} />
<//>
<//>
<//>`;
};
function Sidebar({devices, onclick}) {
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 Device = ({d}) => html`
<div class="hover:bg-stone-100 cursor-pointer flex gap-3 px-4 py-2 justify-between"
onclick=${ev => onclick(d.id)}>
<span>${d.id}</span>
<span class="${BadgeClass} ${d.online ? Colors.green : Colors.red}"> ${d.online ? 'online' : 'offline'} <//>
<//>`;
return html`
<div class="overflow-auto divide-y">
<div class="${TitleClass} flex items-center px-4 py-2 bg-stone-100">
Device List
<//>
${(devices ? devices : []).map(d => h(Device, {d}))}
<//>`;
};
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 divide-y border rounded">
<div class="${TitleClass} bg-white flex items-center 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 FirmwareUpdatePanel({no, name, current, previous, updated_at, refresh}) {
const [color, setColor] = useState(Colors.green);
const [otaStatus, setOtaStatus] = useState('up-to-date');
const Descr = ({label, value}) => html`
<div class="flex items-center justify-between">
<span class=${LabelClass}>${label}<//>
<span class="text-sm">${value}<//>
<//>`;
const onprogress = (len, total) => {
setColor(Colors.info);
setOtaStatus(`uploading, ${parseInt(len * 100 /total)}% done...`);
}
const setparam = (key, val) => fetch('api/settings/set', {
method: 'POST',
body: JSON.stringify({key, val}),
}).then(handleFetchError);
const onupload = function(ok, fileName, fileLength) {
if (ok) {
return setparam(`ecu.${no}.new`, fileName).then(refresh);
} else {
setColor(Colors.red), setOtaStatus('upload failed');
}
};
const onfileselect = function(fileName, fileLength) {
const ok = fileName.startsWith(name);
if (!ok) alert(`Firmware file name must be ${name}.VERSION.BIN_OR_HEX`);
return ok;
}
const update = rollback => new Promise(function(resolve, reject) {
const opts = {method: 'POST', body: JSON.stringify({no, rollback})};
const fail = () => (reject(), setOtaStatus('update error'), setColor(Colors.red));
const finish = () => fetch('api/ecu/update/end', opts)
.then(r => r.ok ? resolve() : reject())
.then(refresh);
const write = () => fetch('api/ecu/update/write', opts).then(r => {
if (!r.ok) fail();
//if (r.ok) r.json().then(r => console.log('Goo', r));
if (r.ok) r.json().then(r => r == 1 ? write() : finish());
});
fetch('api/ecu/update/begin', opts).then(r => r.ok ? write() : fail());
});
const onflash = ev => update(false);
const onrollback = ev => update(true);
useEffect(function() {
//setOtaStatus(anew ? 'update pending' : 'up-to-date');
//setColor(anew ? Colors.yellow : Colors.green);
}, []);
return html`
<div class="divide-y border rounded bg-white">
<div class="px-4 py-2 flex justify-between items-center rounded">
<span class="${TitleClass}">Firmware Update<//>
<div class="flex gap-2"><span class=${LabelClass}>Status:</span><span class="${BadgeClass} ${color}">${otaStatus}<//><//>
<//>
<div class="p-4 flex flex-col gap-1">
<${Descr} label="Current firmware:" value=${current || 'n/a'} />
<${Descr} label="Updated at:" value=${updated_at || 'n/a'} />
<div class="flex items-center justify-between whitespace-nowrap">
<span class=${LabelClass}>Previous firmware:<//>
<span class="flex gap-1 items-center">
<span class="text-sm">${previous}<//>
<${Button} title="rollback" icon=${Icons.bolt} onclick=${onrollback}
extraClass="w-24" disabled=${!previous} />
<//>
<//>
<div class="flex items-center justify-between">
<span class=${LabelClass}>New firmware:<//>
<span class="flex gap-1 align-center justify-between">
<${UploadFileButton} title="upload" url="api/fw/upload"
onfileselect=${onfileselect}
onprogress=${onprogress} oncomplete=${onupload} />
<${Button} title="flash" icon=${Icons.bolt} onclick=${onflash}
extraClass="w-24" disabled=${!previous} />
<//>
<//>
<//>
<//>`;
};
function DeviceSettingsPanel({device, publishFn, connected}) {
const [config, setConfig] = useState(device.config);
const logOptions = [[0, 'Disable'], [1, 'Error'], [2, 'Info'], [3, 'Debug']];
const onSave = ev => publishFn('config.set', config).then(r => {
//setDeviceConfig(device.id, config);
}).catch(e => alert('Failure!'));
// To delete device, set an empty retained message
const onforget = function(ev) {
MqttClient.publish(device.topic, '', {retain: true});
location.reload();
};
const onloglevelchange = ev => setConfig(x => Object.assign({}, x, {log_level: parseInt(ev.target.value)}));
const onpinschange = ev => {
const pin_map = ev.target.value.split(/\s*,\s*/).map(x => parseInt(x));
setConfig(x => Object.assign({}, x, {
pin_map, // Send an updated pin map
pin_count: pin_map.length, // And pin count
pin_state:[] // Remove pin_state from the request, as pin_map change can invalidate it
}));
}
//console.log(config);
return html`
<div class="divide-y border rounded bg-white">
<div class="px-4 py-2 flex justify-between items-center rounded ${TitleClass}">
<div class="flex gap-2 items-center"> Settings <//>
<//>
<div class="p-4 gap-3">
<div class="flex justify-between my-1">
<span class=${LabelClass}>Status<//>
<span class="${BadgeClass} ${connected ? Colors.green : Colors.red}"> ${connected ? 'online' : 'offline'} <//>
<//>
<div class="flex justify-between my-1">
<span class=${LabelClass}>Device ID<//>
<span class="${LabelClass}">${device.id}<//>
<//>
<div class="grid grid-cols-2 my-1">
<span class="${LabelClass}">Log Level<//>
<select onchange=${onloglevelchange} 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=${!connected}>
${logOptions.map(v => html`<option value=${v[0]} selected=${v[0] == config.log_level}>${v[1]}<//>`) }
<//>
<//>
<div class="grid grid-cols-2 my-1">
<span class="${LabelClass}">Show Pins<//>
<div class="flex w-full items-center">
<input disabled=${!connected} value=${config.pin_map}
onchange=${onpinschange} class=${InputClass}
placeholder="comma-separated pin numbers" />
<//>
<//>
<div class="mt-3 flex justify-between gap-2">
<${Button} title="Forget this device"
disabled=${device.online || !connected} icon=${Icons.delete}
onclick=${onforget}/>
<${Button} icon=${Icons.save} onclick=${onSave} title="Save Settings" disabled=${!device.online} />
<//>
<//>
<//> `;
};
function DeviceControlPanel({device, config, setConfig, publishFn, connected}) {
const onclick = function(i) { // Send request to toggle pin i
let configCopy = Object.assign({}, config);
configCopy.pin_state[i] = !!configCopy.pin_state[i] ? 0 : 1;
return publishFn('config.set', configCopy).catch(e => alert('Failure!'));
};
const Pin = i => html`
<div class="inline-block my-1 bg-gray-100 rounded px-1 py-1">
<div class="flex align-center justify-between">
<span class="${LabelClass}">Pin ${config.pin_map[i]}<//>
<${Toggle} onclick=${ev => onclick(i)}
disabled=${!device.online || !connected}
value=${config.pin_state[i]} />
<//>
<//>`;
return html`
<div class="divide-y border rounded bg-white xbg-slate-100 my-y">
<div class="px-4 py-2 flex justify-between items-center rounded ${TitleClass}">
<div class="flex gap-2 items-center">Pin Control Panel<//>
<//>
<div class="p-4 grid grid-cols-2 lg:grid-cols-3 gap-2">
${(config.pin_map || []).map((_, i) => Pin(i))}
<div><//>
<//>
<//> `;
};
function DeviceDashboard({device, setDeviceConfig, publishFn, connected}) {
const cfg = device && device.config ? device.config : DefaultDeviceConfig;
const [localConfig, setLocalConfig] = useState(cfg);
useEffect(() => setLocalConfig(cfg), [device]);
if (!device || !localConfig) {
return html`
<div class="flex grow text-gray-400 justify-center items-center text-xl">
No device selected. Click on a device on a sidebar
<//>`;
}
return html`
<div class="p-3 grid grid-cols-3 gap-2">
<${DeviceSettingsPanel} device=${device} config=${localConfig} setConfig=${setLocalConfig} publishFn=${publishFn} connected=${connected} />
<${DeviceControlPanel} device=${device} config=${localConfig} setConfig=${setLocalConfig} publishFn=${publishFn} connected=${connected} />
<${FirmwareUpdatePanel} deviceID=${device.id} publishFn=${publishFn}
disabled=${!device.online}
info=${[localConfig.crnt_fw, localConfig.prev_fw]} />
</div>`;
}
const App = function() {
const [devices, setDevices] = useState([]);
const [currentDevID, setCurrentDevID] = useState(localStorage.getItem('currentDevID') || '');
const [loading, setLoading] = useState(true);
const [url, setUrl] = useState(localStorage.getItem('url') || DefaultUrl);
const [topic, setTopic] = useState(localStorage.getItem('topic') || DefaultTopic);
const [error, setError] = useState(null);
const [connected, setConnected] = useState(false);
const responseHandlers = useRef({});
function addResponseHandler(correlationId, handler) {
responseHandlers[correlationId] = handler;
}
function removeResponseHandler(correlationId) {
delete responseHandlers[correlationId];
}
const onRefresh = ev => window.location.reload();
const initConn = () => {
MqttClient = mqtt.connect(url, {connectTimeout: 5000, reconnectPeriod: 0});
MqttClient.on('connect', () => {
//console.log('Connected to the broker');
setLoading(false);
setError(null); // Reset error state upon successful connection
setConnected(true);
const statusTopic = topic + '/+/status'
const txTopic = topic + '/+/tx'
const subscribe = (topic) => {
MqttClient.subscribe(topic, (err) => {
if (err) {
console.error('Error subscribing to topic:', err);
setError('Error subscribing to topic');
} else {
//console.log('Successfully subscribed to ', topic);
}
});
};
subscribe(statusTopic);
subscribe(txTopic);
});
MqttClient.on('message', (topic, message) => {
// console.log(`Received message from ${topic}: ${message.toString()}`);
if (message.length == 0) return;
let response;
try {
response = JSON.parse(message.toString());
} catch (err) {
console.error(err);
return;
}
if (topic.endsWith('/status')) {
let device = {topic: topic, id: topic.split('/')[1], config: DefaultDeviceConfig};
const params = response.params;
if (!params) {
console.error('Invalid response');
return;
}
device.online = params.status === 'online';
setConnected(device.online);
if (device.online) {
device.config = params;
if (!device.config.pins) device.config.pins = [];
}
setDevice(device)
} else if (topic.endsWith('/tx')) {
if (!response.id) {
console.error('Invalid response');
return;
}
const handler = responseHandlers[response.id];
if (handler) {
handler(response);
removeResponseHandler(response.id);
}
}
});
MqttClient.on('error', (err) => {
console.error('Connection error:', err);
setError('Connection cannot be established.');
});
MqttClient.on('close', () => {
if (!connected) {
console.error('Failed to connect to the broker.');
setError('Connection cannot be established.');
setLoading(false);
}
});
};
useEffect(() => initConn(), []);
const handlePublish = (methodName, parameters, timeout = 5000) => {
return new Promise((resolve, reject) => {
const randomIdGenerator = function(length) {
return Math.random().toString(36).substring(2, length + 2);
};
const randomID = randomIdGenerator(40);
const timeoutID = setTimeout(() => {
removeResponseHandler(randomID);
reject(new Error('Request timed out'));
}, timeout);
addResponseHandler(randomID, (messageData) => {
clearTimeout(timeoutID);
resolve(messageData);
});
if (currentDevID) {
const rxTopic = topic + `/${currentDevID}/rx`;
const rpcPayload = {method: methodName, id: randomID};
if (parameters) {
rpcPayload.params = parameters;
}
MqttClient.publish(rxTopic, JSON.stringify(rpcPayload));
}
});
};
const getDeviceByID = (deviceID) => devices.find(d => d.id === deviceID);
const setDevice = (devData) => {
setDevices(prevDevices => {
const devIndex =
prevDevices.findIndex(device => device.id === devData.id);
if (devIndex !== -1) {
if (!devData.online && !devData.config) {
const updatedDevices = [...prevDevices];
updatedDevices[devIndex].online = false;
return updatedDevices;
} else {
return prevDevices.map(device => device.id === devData.id ? devData : device);
}
} else {
return [...prevDevices, devData];
}
});
};
const setDeviceConfig = (deviceID, config) => {
setDevices(prevDevices => {
return prevDevices.map(device => {
if (device.id === deviceID) {
return {...device, config: config};
}
return device;
});
});
};
const onDeviceClick = (deviceID) => {
const device = getDeviceByID(deviceID);
if (device) {
setCurrentDevID(device.id);
localStorage.setItem('currentDevID', device.id);
}
};
if (error) {
return html`
<div class="min-h-screen bg-slate-100 flex flex-col">
<${Header}/>
<div class="flex-grow flex items-center justify-center">
<div class="bg-slate-300 p-8 rounded-lg shadow-md w-full max-w-md">
<h1 class="text-2xl font-bold text-gray-700 mb-4 text-center">Connection Error</h1>
<p class="text-gray-600 text-center mb-4">
Unable to connect to the MQTT broker.
</p>
<div class="text-center relative">
<${Button} title="Retry" onclick=${onRefresh}
icon=${Icons.refresh} class="absolute top-4 right-4" />
</div>
</div>
</div>
</div>`;
}
return html`
<div class="h-full flex flex-col">
<${Header}
topic=${topic} setTopic=${setTopic} url=${url}
setUrl=${setUrl} connected=${connected}
/>
<div class="flex grow overflow-auto">
<div class="w-60 overflow-auto">
<${Sidebar} devices=${devices} onclick=${onDeviceClick} />
<//>
<div class="grow bg-gray-200">
<${DeviceDashboard}
device=${getDeviceByID(currentDevID)} connected=${connected}
setDeviceConfig=${setDeviceConfig} publishFn=${handlePublish} />
<//>
<//>
<//>`;
};
window.onload = () => render(h(App), document.body);
@@ -0,0 +1,17 @@
module.exports = {
content: ['./*.{html,js}'],
xplugins: [ 'tailwindcss', 'xautoprefixer' ],
corePlugins: {outline: false},
theme: {
extend: {},
fontFamily: {
sans:
[
"Inter var, Helvetica, sans-serif", {
fontFeatureSettings: '"cv11", "ss01"',
fontVariationSettings: '"opsz" 32',
}
]
}
}
}
+24
View File
@@ -0,0 +1,24 @@
PROG ?= ./example # Program we are building
DELETE ?= rm -rf # Command to remove files
OUT ?= -o $(PROG) # Compiler argument for output file
SOURCES = main.c net.c hal.c mongoose.c # Source code files
CFLAGS ?= -W -Wall -Wextra -g -I. # Build options
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
CFLAGS_MONGOOSE += -DMG_ENABLE_LINES=1
ifeq ($(OS),Windows_NT) # Windows settings for MinGW compiler. To use VC: make CC=cl CFLAGS=/MD OUT=/Feexample.exe
PROG = example.exe # Use .exe suffix for the binary
CC = gcc # Use MinGW gcc compiler
CFLAGS += -lws2_32 # Link against the Winsock library
DELETE = cmd /C del /Q /F /S # Command prompt command to delete files
endif
all: $(PROG)
$(RUN) $(PROG) $(ARGS)
$(PROG): $(SOURCES)
$(CC) $(SOURCES) $(CFLAGS) $(CFLAGS_MONGOOSE) $(CFLAGS_EXTRA) $(OUT)
clean:
$(DELETE) $(PROG) *.o *.obj *.exe *.dSYM
Whitespace-only changes.
+19
View File
@@ -0,0 +1,19 @@
#include "hal.h"
#define MAX_PIN_NO 144
// Mocked device pins
static bool s_pins[MAX_PIN_NO];
bool gpio_write(uint16_t pin, bool status) {
bool ok = false;
if (pin >= 0 && pin < MAX_PIN_NO) {
s_pins[pin] = status;
ok = true;
}
return ok;
}
bool gpio_read(uint16_t pin) {
return (pin >= 0 && pin < MAX_PIN_NO) ? s_pins[pin] : false;
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "mongoose.h"
bool gpio_write(uint16_t pin, bool status);
bool gpio_read(uint16_t pin);
+53
View File
@@ -0,0 +1,53 @@
// Copyright (c) 2023 Cesanta Software Limited
// All rights reserved
#include "net.h"
#include "hal.h"
// Handle interrupts, like Ctrl-C
static int s_signo;
static void signal_handler(int signo) {
s_signo = signo;
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr;
int i;
// Parse command-line flags
for (i = 1; i < argc; i++) {
if (strcmp(argv[i], "-u") == 0 && argv[i + 1] != NULL) {
g_mqtt_server_url = argv[++i];
} else if (strcmp(argv[i], "-i") == 0 && argv[i + 1] != NULL) {
g_device_id = strdup(argv[++i]);
} else if (strcmp(argv[i], "-t") == 0 && argv[i + 1] != NULL) {
g_root_topic = argv[++i];
} else if (strcmp(argv[i], "-v") == 0 && argv[i + 1] != NULL) {
mg_log_set(atoi(argv[++i]));
} else {
MG_ERROR(("Unknown option: %s. Usage:", argv[i]));
MG_ERROR(
("%s [-u mqtt://SERVER:PORT] [-i DEVICE_ID] [-t TOPIC_NAME] [-v "
"DEBUG_LEVEL]",
argv[0], argv[i]));
return 1;
}
}
signal(SIGINT, signal_handler); // Setup signal handlers - exist event
signal(SIGTERM, signal_handler); // manager loop on SIGINT and SIGTERM
mg_log_set(MG_LL_DEBUG); // Set debug log level
mg_mgr_init(&mgr);
web_init(&mgr);
while (s_signo == 0) {
mg_mgr_poll(&mgr, 50);
}
web_free();
mg_mgr_free(&mgr);
MG_INFO(("Exiting on signal %d", s_signo));
return 0;
}
Loaded 100 of 646 files, more files were not shown because too many files have changed in this diff. Show more