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
on:
pull_request:
paths:
- "mongoose.c"
- "mongoose.h"
schedule:
- cron: '0 2 * * *' # run at 2 AM UTC
# Allow manual runs
workflow_dispatch:
jobs:
Fuzzing:
runs-on: ubuntu-latest
+4 -7
View File
@@ -2,13 +2,10 @@ name: "CodeQL Scanning"
# https://github.com/github/codeql-action
on:
push:
paths:
- "Makefile"
- "mongoose.c"
- "mongoose.h"
- test/unit-test
- test/mip-test
schedule:
- cron: '30 2 * * *' # run at 2:30 AM UTC
# Allow manual runs
workflow_dispatch:
env:
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:
push:
paths:
- "**Makefile"
- "**.c"
- "**.h"
workflow_dispatch:
env:
IPV6: 0
jobs:
@@ -78,7 +74,7 @@ jobs:
name: macos SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
env:
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
steps:
- uses: actions/checkout@v3
@@ -149,154 +145,60 @@ jobs:
- 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:
runs-on: ubuntu-latest
env:
GO: 0
steps:
- uses: actions/checkout@v3
with: { fetch-depth: 2 }
- run: if ./test/match_changed_files.sh '^examples/arduino|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
- 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: 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 }}
# 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 }}
run: make -C examples/arduino clean
matrix_examples:
generic_examples:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
example:
- path: esp32/device-dashboard
- path: esp32/uart-bridge
- path: esp32/micropython
- 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: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
- path: nxp/nxp-mimxrt1020-azurertos
@@ -316,54 +218,217 @@ jobs:
- path: nxp/nxp-lpcxpresso54608-lwip-freertos
- path: nxp/nxp-lpcxpresso54618-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-twrkv58f220m-lwip-freertos
- 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-f746zg-make-baremetal-builtin
- path: stm32/nucleo-f746zg-make-freertos-builtin
- path: stm32/nucleo-f746zg-make-freertos-tcp
- path: stm32/nucleo-f746zg-make-baremetal-builtin-rndis
- path: stm32/nucleo-g031-make-baremetal-builtin
- path: stm32/nucleo-h563zi-make-baremetal-builtin
- path: stm32/nucleo-h563zi-make-freertos-builtin
- path: stm32/nucleo-h743zi-make-baremetal-builtin
- path: stm32/nucleo-h743zi-make-freertos-builtin
- path: ti/ti-ek-tm4c1294xl-http-server
- path: ti/ek-tm4c1294xl-baremetal
- path: ti/ek-tm4c1294xl-freertos
- path: ti/ek-tm4c1294xl-make-baremetal-builtin-rndis
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
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
cube_examples:
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/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 }}
run: cd test/cube && make
zephyr_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/.*.[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 }}
run: make -C examples/zephyr init
- name: minify manifest
@@ -383,3 +448,293 @@ jobs:
run: make -C examples/zephyr/mqtt-aws-client build
- if: ${{ env.GO == 1 }}
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_OPTIONS ?= detect_leaks=1
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_EMBEDDED := $(filter-out $(wildcard examples/zephyr/*/), $(dir $(wildcard examples/*/*/Makefile)))
PREFIX ?= /usr/local
@@ -90,6 +90,7 @@ musl: CC = $(DOCKER) mdashnet/cc1 gcc
musl: RUN = $(DOCKER) mdashnet/cc1
# Make sure we can build from unamalgamated sources.
unamalgamated: CFLAGS += -DMG_ENABLE_MD5=1
unamalgamated: $(HDRS) Makefile test/packed_fs.c
$(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
$(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: IPV6=0
armhf: CC = $(DOCKER) mdashnet/armhf cc
@@ -131,11 +139,11 @@ s390: CC = $(DOCKER) mdashnet/s390 cc
s390: RUN = $(DOCKER) mdashnet/s390
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)
$(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)
$(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
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
(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
+66 -7
View File
@@ -19,23 +19,82 @@ robust, and easy. Features include:
- write code once - and it'll work everywhere
- ideal for the unification of the network infrastructure code across company
- 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
- Tiny static and run-time footprint
- 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
[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
- 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
[Nucleo-F429ZI](examples/stm32/nucleo-f429zi-baremetal) is only 6 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
- 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
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
@@ -49,7 +108,7 @@ robust, and easy. Features include:
services](https://mongoose.ws/integration/) - don't hesitate to [contact us](https://mongoose.ws/contact/)
# Security
## Security
We take security seriously:
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).
# Contributions
## Contributions
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
mg_http_listen(
&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");
},
&mgr);
+1 -1
View File
@@ -15,7 +15,7 @@ void mqtt_publish(const char *message) {
if (mqtt_connection) mg_mqtt_pub(mqtt_connection, &opts);
}
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_MQTT_OPEN) {
MG_INFO(("%lu CONNECTED to %s", c->id, MQTT_SERVER));
struct mg_mqtt_opts opts = {};
+1 -2
View File
@@ -20,7 +20,7 @@ uint8_t answer[] = {
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) {
c->is_hexdumping = 1;
} 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);
}
(void) fn_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
SOURCES = main.c mongoose.c net.c packed_fs.c # Source code files
CFLAGS = -W -Wall -Wextra -g -I. # Build options
NPX ?= npx
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
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
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
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)
<!-- 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,
// 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;
if (ev == MG_EV_OPEN) {
*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) {
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
@@ -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) {
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
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]);
}
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) {
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("humidity"), 67, //
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;
while ((no = ui_event_next(no, &ev)) != 0 && no <= end) {
len += mg_xprintf(out, ptr, "%s{%m:%lu,%m:%d,%m:%d,%m:%m}", //
len == 0 ? "" : ",", //
MG_ESC("time"), ev.timestamp, //
MG_ESC("type"), ev.type, //
MG_ESC("prio"), ev.prio, //
len += mg_xprintf(out, ptr, "%s{%m:%lu,%m:%d,%m:%d,%m:%m}\n", //
len == 0 ? "" : ",", //
MG_ESC("time"), ev.timestamp, //
MG_ESC("type"), ev.type, //
MG_ESC("prio"), ev.prio, //
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,
struct mg_http_message *hm) {
int pageno = mg_json_get_long(hm->body, "$.page", 1);
mg_http_reply(c, 200, s_json_header, "{%m:[%M], %m:%d}", MG_ESC("arr"),
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);
}
static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
struct settings settings;
char *s = mg_json_get_str(body, "$.device_name");
bool ok = true;
memset(&settings, 0, sizeof(settings));
mg_json_get_bool(body, "$.log_enabled", &settings.log_enabled);
settings.log_level = mg_json_get_long(body, "$.log_level", 0);
settings.brightness = mg_json_get_long(body, "$.brightness", 0);
char *s = mg_json_get_str(body, "$.device_name");
if (s && strlen(s) < MAX_DEVICE_NAME) {
free(settings.device_name);
settings.device_name = s;
} else {
free(s);
}
// Save to the device flash
s_settings = settings;
bool ok = true;
s_settings = settings; // Save to the device flash
mg_http_reply(c, 200, s_json_header,
"{%m:%s,%m:%m}", //
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) {
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"),
s_settings.log_enabled ? "true" : "false", //
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) {
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_ota_crc32(fw), '"', MG_ESC("size"), mg_ota_size(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);
}
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
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 (fn_data != NULL) { // TLS listener!
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");
@@ -294,6 +299,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
handle_firmware_status(c);
} else if (mg_http_match_uri(hm, "/api/device/reset")) {
handle_device_reset(c);
} else if (mg_http_match_uri(hm, "/api/device/eraselast")) {
handle_device_eraselast(c);
} else {
struct mg_http_serve_opts opts;
memset(&opts, 0, sizeof(opts));
+8
View File
@@ -4,6 +4,10 @@
#include "mongoose.h"
#ifdef __cplusplus
extern "C" {
#endif
#if !defined(HTTP_URL)
#define HTTP_URL "http://0.0.0.0:8000"
#endif
@@ -25,3 +29,7 @@ struct ui_event {
};
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>`,
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>`,
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 = {
@@ -85,7 +86,7 @@ export function Notification({ok, text, close}) {
<div class="p-4">
<div class="flex items-start">
<div class="flex-shrink-0">
<${ok ? Icons.ok : Icons.failed} class="h-6 w-6 ${ok ? 'text-green-400' : 'text-red-400'}" />
<${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>
@@ -157,7 +158,7 @@ export function Colored({icon, text, colors}) {
export function Stat({title, text, tipText, tipIcon, tipColors, colors}) {
return html`
<div class="flex flex-col bg-white border shadow-sm rounded-xl dark:bg-slate-900 dark:border-gray-800">
<div class="overflow-auto rounded-lg bg-white px-4 py-2 shadow">
<div class="overflow-auto rounded-lg bg-white px-4 py-2 ">
<div class="flex items-center gap-x-2">
<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');
useEffect(() => { checkval(value); }, []);
const checkval = function(v) {
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 oninput = ev => {
const m = step.match(/^.+\.(.+)/);
const digits = m ? m[1].length : 0;
const onchange = ev => {
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);
checkval(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}
oninput=${oninput} ...${attr}
class="${bg} font-normal text-sm rounded w-full flex-1 py-0.5 px-2 text-gray-700 placeholder:text-gray-400 focus:outline-none disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-500" placeholder=${placeholder} value=${value} />
${ addonRight && html`<span class="inline-flex font-normal truncate py-1 border-l bg-slate-100 items-center border-gray-300 px-2 text-gray-500 text-xs overflow-scroll" style="min-width: 50%;">${addonRight}<//>` }
${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}<//>` }
<//>`;
};
@@ -222,7 +230,7 @@ export function Setting(props) {
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}<//>
<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)}<//>
<//>`;
};
@@ -317,7 +325,6 @@ export function UploadFileButton(props) {
finish(ok); // All chunks sent
});
};
//setFailed(false);
sendChunk(0);
});
};
@@ -344,6 +351,6 @@ export function UploadFileButton(props) {
<div class="inline-flex flex-col ${props.class}">
<input class="hidden" type="file" ref=${input} onchange=${onchange} accept=${props.accept} />
<${Button} title=${props.title} icon=${Icons.download} onclick=${onclick} ref=${btn} colors=${props.colors} />
<div class="py-2 text-sm text-slate-400 ${status || 'hidden'}">${status}<//>
<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)));
const onrollback = ev => fetch('api/firmware/rollback')
.then(onreboot);
const onerase = ev => fetch('api/device/eraselast').then(refresh);
const onupload = function(ok, name, size) {
if (!ok) return false;
return new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000));
};
const clean = info[0].valid && info[0].golden == 0;
return html`
<div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-2">
<div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-3">
<${FirmwareStatus} title="Current firmware image" info=${info[0]}>
<div class="flex flex-wrap gap-2">
<${Button} title="Commit this firmware"
onclick=${oncommit} icon=${Icons.thumbUp} disabled=${clean} />
<${Button} title="Reboot device" onclick=${onreboot} icon=${Icons.refresh} clsx="absolute top-4 right-4" />
<${UploadFileButton}
title="Upload new firmware: choose .bin file:" onupload=${onupload}
url="api/firmware/upload" accept=".bin,.uf2" />
<${Button} title="Commit this firmware" onclick=${oncommit}
icon=${Icons.thumbUp} disabled=${info[0].status == 3} cls="w-full" />
<//>
<//>
<${FirmwareStatus} title="Previous firmware image" info=${info[1]}>
<${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">
<${DeveloperNote}>
<div class="my-2">
When a new firmware gets flashed, its status is marked as, "first_boot".
That is an unreliable (uncommitted) firmware. A user may choose
to revert back to the previous committed firmware on the subsequent
boots. Clicking on the "commit" button calls "mg_ota_commit()" function
which commits the firmware.
Firmware status and other information is stored in the last sector
of flash
<//>
<div class="my-2">
Firmware status can be FIRST_BOOT, UNCOMMITTED or COMMITTED. If no
information is available, it is UNAVAILABLE.
<//>
<div class="my-2">
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";
// 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) {
struct mg_http_serve_opts opts = {
.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);
}
(void) fn_data;
}
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";
#if 0
char *config_read(void) {
return mg_file_read(&mg_fs_posix, FS_ROOT "/config.json", NULL);
}
#endif
void config_write(struct mg_str config) {
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));
// Try to connect to wifi by using saved WiFi credentials
char *json = mg_file_read(&mg_fs_posix, WIFI_FILE, NULL);
if (json != NULL) {
char *ssid = mg_json_get_str(mg_str(json), "$.ssid");
char *pass = mg_json_get_str(mg_str(json), "$.pass");
struct mg_str json = mg_file_read(&mg_fs_posix, WIFI_FILE);
if (json.ptr != NULL) {
char *ssid = mg_json_get_str(json, "$.ssid");
char *pass = mg_json_get_str(json, "$.pass");
while (!wifi_init(ssid, pass)) (void) 0;
free(ssid);
free(pass);
free(json);
free((void *) json.ptr);
} else {
// If WiFi is not configured, run CLI until configured
MG_INFO(("WiFi is not configured, running CLI. Press enter"));
@@ -45,6 +47,6 @@ void app_main(void) {
mg_mgr_init(&mgr);
mg_log_set(MG_LL_DEBUG); // Set log level
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
}
+1 -1
View File
@@ -11,7 +11,7 @@
#define WIFI_FILE FS_ROOT "/wifi.json"
#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);
bool wifi_init(const char *ssid, const char *pass);
void cli(uint8_t ch);
@@ -9,8 +9,8 @@
#define SERVER_URL "http://0.0.0.0:80"
#define CLIENT_URL "http://info.cern.ch"
// Event handler for an server (accepted) connection
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
// Event handler for a server (accepted) connection
static void cb(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_HTTP_MSG) {
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,
// 1. Copy "ca.pem" file to the ESP8266 flash FS
// 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) {
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);
+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
// 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) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
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);
}
}
(void) fn_data;
}
int main(void) {
+2 -12
View File
@@ -6,26 +6,16 @@
// HTTP request handler function. It implements the following endpoints:
// /upload - Saves the next file chunk
// 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) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
if (mg_http_match_uri(hm, "/upload")) {
char path[80], name[64];
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);
}
mg_http_upload(c, hm, &mg_fs_posix, "/tmp", 99999);
} else {
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
mg_http_serve_dir(c, ev_data, &opts);
}
}
(void) fn_data;
}
int main(void) {
@@ -17,7 +17,7 @@ var sendFileData = function(name, data, chunkSize) {
var sendChunk = function(offset) {
var chunk = data.subarray(offset, offset + chunkSize) || '';
var opts = {method: 'POST', body: chunk};
var url = '/upload?offset=' + offset + '&name=' + encodeURIComponent(name);
var url = '/upload?offset=' + offset + '&file=' + encodeURIComponent(name);
var ok;
setStatus(
'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
//
// Streaming upload example. Demonstrates how to use MG_EV_READ events
@@ -11,7 +11,7 @@
// HTTP request handler function. It implements the following endpoints:
// /upload - Saves the next file chunk
// 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) {
// 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.
@@ -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) {
@@ -46,7 +46,7 @@ int main(void) {
mg_mgr_init(&mgr);
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);
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
// 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) {
// Connection created. Store connect expiration time in c->data
*(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;
printf("%.*s", (int) hm->message.len, hm->message.ptr);
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) {
*(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"
// Print HTTP response and signal that we're done
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
const char *url = fn_data;
static void fn(struct mg_connection *c, int ev, void *ev_data) {
const char *url = c->fn_data;
static bool connected;
if (ev == MG_EV_HTTP_MSG) {
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
// 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) {
if (ev == MG_EV_ACCEPT && fn_data != NULL) {
static void fn(struct mg_connection *c, int ev, void *ev_data) {
if (ev == MG_EV_ACCEPT && c->fn_data != NULL) {
struct mg_tls_opts opts = {
#ifdef TLS_TWOWAY
.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);
}
}
(void) fn_data;
}
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));
}
static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_connection *c2 = (struct mg_connection *) fn_data;
static void fn2(struct mg_connection *c, int ev, void *ev_data) {
struct mg_connection *c2 = (struct mg_connection *) c->fn_data;
if (ev == MG_EV_READ) {
// All incoming data from the backend, forward to the client
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;
}
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
struct mg_connection *c2 = fn_data;
static void fn(struct mg_connection *c, int ev, void *ev_data) {
struct mg_connection *c2 = c->fn_data;
if (ev == MG_EV_HTTP_MSG) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
// 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_enable_hexdump = "no";
static const char *s_ssi_pattern = "#.html";
static const char *s_upload_dir = NULL; // File uploads disabled by default
// Handle interrupts, like Ctrl-C
static int s_signo;
@@ -18,22 +19,48 @@ static void signal_handler(int signo) {
// Event handler for the listening connection.
// 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) {
struct mg_http_message *hm = ev_data, tmp = {0};
struct mg_str unknown = mg_str_n("?", 1), *cl;
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);
mg_http_parse((char *) c->send.buf, c->send.len, &tmp);
cl = mg_http_get_header(&tmp, "Content-Length");
if (cl == NULL) cl = &unknown;
MG_INFO(("%.*s %.*s %.*s %.*s", (int) hm->method.len, hm->method.ptr,
(int) hm->uri.len, hm->uri.ptr, (int) tmp.uri.len, tmp.uri.ptr,
(int) cl->len, cl->ptr));
struct mg_http_message *hm = ev_data;
if (mg_match(hm->uri, mg_str("/upload"), NULL)) {
// Serve file upload
if (s_upload_dir == NULL) {
mg_http_reply(c, 403, "", "Denied: file upload directory not set\n");
} else {
struct mg_http_part part;
size_t pos = 0, total_bytes = 0, num_files = 0;
while ((pos = mg_http_next_multipart(hm->body, pos, &part)) > 0) {
char path[MG_PATH_MAX];
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) {
@@ -44,6 +71,7 @@ static void usage(const char *prog) {
" -S PAT - SSI filename pattern, default: '%s'\n"
" -d DIR - directory to serve, 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",
MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir,
s_listening_address, s_debug_level);
@@ -66,6 +94,8 @@ int main(int argc, char *argv[]) {
s_ssi_pattern = argv[++i];
} else if (strcmp(argv[i], "-l") == 0) {
s_listening_address = argv[++i];
} else if (strcmp(argv[i], "-u") == 0) {
s_upload_dir = argv[++i];
} else if (strcmp(argv[i], "-v") == 0) {
s_debug_level = atoi(argv[++i]);
} else {
@@ -96,6 +126,7 @@ int main(int argc, char *argv[]) {
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(("Upload dir : [%s]", s_upload_dir ? s_upload_dir : "unset"));
while (s_signo == 0) mg_mgr_poll(&mgr, 1000);
mg_mgr_free(&mgr);
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";
// 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) {
// Connected to server. Extract host name from 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
mg_printf(c,
"GET %s HTTP/1.1\r\n"
"Connection: keep-alive\r\n"
"Keep-Alive: timeout=60\r\n"
"Connection: close\r\n"
"Host: %.*s\r\n"
"\r\n",
mg_url_uri(s_url), (int) host.len, host.ptr);
} else if (ev == MG_EV_READ) {
// 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.
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 {
// c->data[0] holds a flag, whether we have parsed the request already
if (c->data[0] == 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) {
fwrite(c->recv.buf + n, 1, c->recv.len - n, stdout); // Print body
data[0] = n + hm.body.len;
data[1] = c->recv.len;
c->recv.len = 0; // Cleanup receive buffer
if (data[1] >= data[0]) * (bool *) fn_data = true;
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) {
*(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) {
*(bool *) fn_data = true; // Error, tell event loop to stop
*(bool *) c->fn_data = true; // Error, Tell event loop to stop
}
(void) ev_data;
}
int main(int argc, char *argv[]) {
struct mg_mgr mgr; // Event manager
bool done = false; // Event handler flips it to true
const char *log_level = getenv("V"); // Log level
struct mg_mgr mgr; // Event manager
bool done = false; // Event handler flips it to true
mg_mgr_init(&mgr); // Initialise event manager
if (log_level == NULL) log_level = "2"; // If not set, set to DEBUG
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_mgr_init(&mgr); // Initialise event manager
mg_log_set(MG_LL_INFO); // Set to 0 to disable debug log
mg_http_connect(&mgr, s_url, fn, &done); // Create client connection
while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr); // Free resources
if (argc > 1) s_url = argv[1]; // Use URL from command line
mg_connect(&mgr, s_url, fn, &done); // Create client connection
while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop
mg_mgr_free(&mgr); // Free resources
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;
}
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) {
struct mg_http_message *hm = ev_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);
}
}
(void) fn_data;
}
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:
// /websocket - upgrade to Websocket, and implement websocket echo server
// 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) {
// c->is_hexdumping = 1;
} 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);
mg_iobuf_free(&io);
}
(void) fn_data;
}
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/live - hangs forever, and returns live log messages
// 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) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
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);
}
}
(void) fn_data;
}
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) {
MG_INFO(("%lu CREATED", c->id));
// 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));
s_conn = NULL; // Mark that we're closed
}
(void) fn_data;
}
// 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;
}
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) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
MG_DEBUG(("Got response (%d) %.*s...", (int) hm->message.len, 12,
hm->message.ptr));
c->is_draining = 1;
} 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) {
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) {
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
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);
}
}
(void) ev_data, (void) fn_data;
(void) ev_data;
}
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"
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) {
// c->is_hexdumping = 1;
} 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) {
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;
}
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) {
MG_INFO(("%lu CREATED", c->id));
// 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));
s_conn = NULL; // Mark that we're closed
}
(void) fn_data;
}
// 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