mirror of
https://github.com/cesanta/mongoose.git
synced 2026-10-26 12:14:56 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
52997c6c51 | ||
|
|
93148e3223 | ||
|
|
f01807ef17 | ||
|
|
a4ed740518 | ||
|
|
0e2f3de154 | ||
|
|
d128d2146a | ||
|
|
c683e1e988 | ||
|
|
0b7038d44d | ||
|
|
55698b91c0 | ||
|
|
eff54827b2 | ||
|
|
2c2c7f6894 | ||
|
|
5baa4d969e | ||
|
|
d745c41d5b | ||
|
|
a86bde7b3f | ||
|
|
4ab2309a5d | ||
|
|
9172f62970 | ||
|
|
daa47e8272 | ||
|
|
2419f02766 | ||
|
|
c8d45c9ff1 | ||
|
|
0b667ca869 | ||
|
|
1a5ea930ab | ||
|
|
e4bfb2b5b0 | ||
|
|
936e7bbc88 | ||
|
|
93b5063251 | ||
|
|
e0668bc1c0 | ||
|
|
fa5aa308bc | ||
|
|
cbfa57a955 | ||
|
|
f8898b016e | ||
|
|
8a9630485f | ||
|
|
066e4ac332 | ||
|
|
f231760395 | ||
|
|
ebfa5f0ba7 | ||
|
|
d7754516f0 | ||
|
|
00fd1884ff | ||
|
|
b828be4873 | ||
|
|
4045a541ef | ||
|
|
f7557c9e48 | ||
|
|
7c62b5005e | ||
|
|
b804217501 | ||
|
|
c6128094b6 | ||
|
|
0fc0230364 | ||
|
|
dd6458262d | ||
|
|
eb68bb2ce1 | ||
|
|
8dc62c25ed | ||
|
|
d8fec13d2c | ||
|
|
922d148ab5 | ||
|
|
08f05d6f8c | ||
|
|
7bec3b4954 | ||
|
|
4d3fba57d4 | ||
|
|
1a8a144726 | ||
|
|
00ea3830a7 | ||
|
|
3880300cd9 | ||
|
|
6b0e4a425d | ||
|
|
f883504d2d | ||
|
|
a1c5995967 | ||
|
|
8500afbbd4 | ||
|
|
13cb94cf2b | ||
|
|
f26a79d75c | ||
|
|
68e2cd9b29 | ||
|
|
6e44bab3d5 | ||
|
|
677fea7bfa | ||
|
|
0d2ae10bd9 | ||
|
|
faa582040b | ||
|
|
0ad4002569 | ||
|
|
dfc2e97d5e | ||
|
|
57159dc5c0 | ||
|
|
77ff1511ff | ||
|
|
124ab99995 | ||
|
|
8ea08306b8 | ||
|
|
3e0d20a335 | ||
|
|
727b3d4f19 | ||
|
|
80782b289f | ||
|
|
72127a4bed | ||
|
|
d4a49e00e7 | ||
|
|
5826d0e41c | ||
|
|
68cb7ce8d9 | ||
|
|
514699fc41 | ||
|
|
deca1aaf46 | ||
|
|
b8bfa5565b | ||
|
|
50c7844031 | ||
|
|
cba0d1b2f9 | ||
|
|
5735ee3269 | ||
|
|
ee3593b887 | ||
|
|
9bebe7cf68 | ||
|
|
bd5e4dba26 | ||
|
|
2427c40a4c | ||
|
|
744df7b244 | ||
|
|
3965aecfd4 | ||
|
|
93bcde0176 | ||
|
|
4b3884a49d | ||
|
|
768908ae4a | ||
|
|
1a965d5c43 | ||
|
|
01b612cff5 | ||
|
|
176db1aedb | ||
|
|
06f8238107 | ||
|
|
97127a57da | ||
|
|
61825b2390 | ||
|
|
669ffe2c74 | ||
|
|
a50b8ff5d7 | ||
|
|
aae2954c14 | ||
|
|
abab569778 | ||
|
|
365a731574 | ||
|
|
285dc63c5c | ||
|
|
24d9fbffae | ||
|
|
04178ad0f7 | ||
|
|
a0e66fb9e6 | ||
|
|
df3c216315 | ||
|
|
e9a6bad751 | ||
|
|
4b7d871048 | ||
|
|
2528dd6425 | ||
|
|
b3fcd9c420 | ||
|
|
9270fc0b15 | ||
|
|
33aa021e96 | ||
|
|
1c78ed1fed | ||
|
|
562c318648 | ||
|
|
65cb894f0d | ||
|
|
bbd27a0a9c | ||
|
|
79e138ef28 | ||
|
|
387334c8c2 | ||
|
|
cd021daf79 | ||
|
|
536aa1fb62 | ||
|
|
a14a838dab | ||
|
|
ad57731ae1 | ||
|
|
ad90b0b7b1 | ||
|
|
18325cb216 | ||
|
|
8df70ba58b | ||
|
|
ee43a5389f | ||
|
|
6192573eb8 | ||
|
|
92ecef7bd3 | ||
|
|
45d7f4ca7e | ||
|
|
144c2f443e | ||
|
|
98d877c5f0 | ||
|
|
9de31ba816 | ||
|
|
560561c5b7 | ||
|
|
ed2d4c1c2b | ||
|
|
e70628cbdc | ||
|
|
161edec195 | ||
|
|
2ba4a74762 | ||
|
|
693316de44 | ||
|
|
6e68124405 | ||
|
|
c8105cffba | ||
|
|
21f6944fc7 | ||
|
|
5adcba7ba1 | ||
|
|
fc8ba744dc | ||
|
|
4fc43cfbf7 | ||
|
|
22837f93c8 | ||
|
|
b03ececbbb | ||
|
|
708908dce5 | ||
|
|
f33302acc9 | ||
|
|
7f8cdf1e79 | ||
|
|
dbc7b5e70b | ||
|
|
4c445453d3 | ||
|
|
4b8fb23def | ||
|
|
253bc64c9e | ||
|
|
32d3fc6803 | ||
|
|
920875ecc9 | ||
|
|
85d3e7f375 | ||
|
|
4ceb50d485 | ||
|
|
7d2fca9574 | ||
|
|
d88e60a525 | ||
|
|
4b4dcb20ca | ||
|
|
d4ab0aa7fd | ||
|
|
c02c1130ed | ||
|
|
f9bbb37721 | ||
|
|
57837f285f | ||
|
|
3379f11b23 | ||
|
|
c74534cf5e | ||
|
|
64ca1ad787 | ||
|
|
8022635c04 | ||
|
|
1f37595200 | ||
|
|
30738f6638 | ||
|
|
bd53e46873 | ||
|
|
122115dbea | ||
|
|
28162f8034 | ||
|
|
9cfd3124ef | ||
|
|
36748ee406 | ||
|
|
2a25e449a2 | ||
|
|
f2f60a7b3b | ||
|
|
82916a1cef | ||
|
|
f86c14abb3 | ||
|
|
fa76312313 | ||
|
|
46687f6fe3 | ||
|
|
6386cdff50 | ||
|
|
7c25c0d6f3 | ||
|
|
8ed57f57cb | ||
|
|
c8f207c277 | ||
|
|
d70d9aff11 | ||
|
|
1118c997b5 | ||
|
|
b999275550 | ||
|
|
f549646ddd | ||
|
|
5ee8552e6c | ||
|
|
b0eb7b6dec | ||
|
|
a546d360d0 | ||
|
|
3cc8a98b9d | ||
|
|
1032f0ade7 | ||
|
|
eb65d269eb | ||
|
|
b379f81004 | ||
|
|
d1b0342a18 | ||
|
|
b815e6bc04 | ||
|
|
81bf65954c | ||
|
|
9cfe72af4a | ||
|
|
7c2e1e1339 | ||
|
|
817115cb6c | ||
|
|
7a3734e54d | ||
|
|
79c3341f74 | ||
|
|
abdd14b0a1 | ||
|
|
1f77e53b23 | ||
|
|
a619de0508 | ||
|
|
8faa11585c | ||
|
|
13259f8902 | ||
|
|
bb5aba2e19 | ||
|
|
22fc01d5f2 | ||
|
|
3b4672c109 | ||
|
|
351bd42bc4 | ||
|
|
e418da4bd8 | ||
|
|
18d13f9848 | ||
|
|
ee792db6a3 | ||
|
|
129bf93b30 | ||
|
|
1dda8b9e60 | ||
|
|
107e303282 | ||
|
|
22f76091a2 | ||
|
|
376e5ccaae | ||
|
|
b792304cf8 | ||
|
|
b357485523 | ||
|
|
07936343c8 | ||
|
|
1b93ca0fb2 | ||
|
|
18cd18d1e5 | ||
|
|
deab6772e8 | ||
|
|
d6d2d7575a | ||
|
|
d63ec65663 | ||
|
|
f0a927831e | ||
|
|
ef16a733d2 | ||
|
|
9044c02edc | ||
|
|
2fa98b00f2 | ||
|
|
15b4d566e7 | ||
|
|
6dabbe237d | ||
|
|
2caff93f83 | ||
|
|
2903937cba | ||
|
|
c33fd6a7fb | ||
|
|
64180e8fe8 | ||
|
|
001134af89 | ||
|
|
b91044dea7 | ||
|
|
34639f86b0 |
No files matched your search
@@ -1,9 +1,10 @@
|
|||||||
name: CIFuzz
|
name: CIFuzz
|
||||||
on:
|
on:
|
||||||
pull_request:
|
schedule:
|
||||||
paths:
|
- cron: '0 2 * * *' # run at 2 AM UTC
|
||||||
- "mongoose.c"
|
# Allow manual runs
|
||||||
- "mongoose.h"
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
Fuzzing:
|
Fuzzing:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|||||||
@@ -2,13 +2,10 @@ name: "CodeQL Scanning"
|
|||||||
# https://github.com/github/codeql-action
|
# https://github.com/github/codeql-action
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
schedule:
|
||||||
paths:
|
- cron: '30 2 * * *' # run at 2:30 AM UTC
|
||||||
- "Makefile"
|
# Allow manual runs
|
||||||
- "mongoose.c"
|
workflow_dispatch:
|
||||||
- "mongoose.h"
|
|
||||||
- test/unit-test
|
|
||||||
- test/mip-test
|
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IPV6: 0
|
IPV6: 0
|
||||||
|
|||||||
@@ -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}}
|
||||||
|
|
||||||
@@ -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
@@ -1,10 +1,6 @@
|
|||||||
name: build
|
name: original workflow, updated
|
||||||
on:
|
on:
|
||||||
push:
|
workflow_dispatch:
|
||||||
paths:
|
|
||||||
- "**Makefile"
|
|
||||||
- "**.c"
|
|
||||||
- "**.h"
|
|
||||||
env:
|
env:
|
||||||
IPV6: 0
|
IPV6: 0
|
||||||
jobs:
|
jobs:
|
||||||
@@ -78,7 +74,7 @@ jobs:
|
|||||||
name: macos SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
|
name: macos SSL=${{ matrix.ssl }} TFLAGS=${{ matrix.select }}
|
||||||
env:
|
env:
|
||||||
SSL: ${{ matrix.ssl }}
|
SSL: ${{ matrix.ssl }}
|
||||||
TFLAGS: ${{ matrix.select }} -DNO_SNTP_CHECK
|
TFLAGS: ${{ matrix.select }} -DNO_SNTP_CHECK -Wno-sign-conversion # Workaround for MbedTLS 3.5.0
|
||||||
HOMEBREW_NO_AUTO_UPDATE: 1
|
HOMEBREW_NO_AUTO_UPDATE: 1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
@@ -149,154 +145,60 @@ jobs:
|
|||||||
- run: make clean_examples_mac
|
- run: make clean_examples_mac
|
||||||
|
|
||||||
|
|
||||||
test_f7:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with: { fetch-depth: 2 }
|
|
||||||
- run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-f7.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/stm32/nucleo-f746zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/stm32/nucleo-f746zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/stm32/nucleo-f746zg-make-freertos-tcp test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- run: echo GO=0 >> $GITHUB_ENV ; if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f7.*-cube-'; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
|
|
||||||
|
|
||||||
test_f4:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with: { fetch-depth: 2 }
|
|
||||||
- run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-f4.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/stm32/nucleo-f429zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/stm32/nucleo-f429zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- run: echo GO=0 >> $GITHUB_ENV ; if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f4.*-cube-'; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
|
|
||||||
|
|
||||||
test_h7:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with: { fetch-depth: 3 }
|
|
||||||
- run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h7.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/stm32/nucleo-h743zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/stm32/nucleo-h743zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- run: echo GO=0 >> $GITHUB_ENV ; if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h7.*-cube-'; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
|
|
||||||
|
|
||||||
test_h5:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with: { fetch-depth: 3 }
|
|
||||||
- run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h5.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/stm32/nucleo-h563zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
# - if: ${{ env.GO == 1 }}
|
|
||||||
# WIP run: make -C examples/stm32/nucleo-h563zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
|
|
||||||
test_rt1020:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with: { fetch-depth: 3 }
|
|
||||||
- run: if ./test/match_changed_files.sh "^src|examples/nxp/rt1020.*-make-" ; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/nxp/rt1020-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/nxp/rt1020-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
|
|
||||||
test_tm4c:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with: { fetch-depth: 2 }
|
|
||||||
- run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/ti/ek-tm4c" ; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/ti/ek-tm4c1294xl-baremetal test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/ti/ek-tm4c1294xl-freertos test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
|
|
||||||
test_same54:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with: { fetch-depth: 2 }
|
|
||||||
- run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/microchip/same54-xpro" ; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/microchip/same54-xpro/device-dashboard test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/microchip/same54-xpro/mqtt-client test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
|
|
||||||
test_pico_w5500:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
with: { fetch-depth: 2 }
|
|
||||||
- run: if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/rp2040/pico-w5500" ; then echo GO=1 >> $GITHUB_ENV ; fi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: sudo apt -y update; sudo apt -y install gcc-arm-none-eabi
|
|
||||||
- if: ${{ env.GO == 1 }}
|
|
||||||
run: make -C examples/rp2040/pico-w5500 test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
|
||||||
|
|
||||||
|
|
||||||
arduino:
|
arduino:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: if ./test/match_changed_files.sh '^examples/arduino|^src/.*.[ch]' ; then echo GO=1 >> $GITHUB_ENV ; fi
|
- run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/arduino" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
# nothing specific to install or do
|
||||||
|
fi
|
||||||
- if: ${{ env.GO == 1 }}
|
- if: ${{ env.GO == 1 }}
|
||||||
run: make -C examples/arduino arduino-xiao
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/arduino'; then
|
||||||
|
make -C examples/arduino arduino-xiao
|
||||||
|
fi
|
||||||
# - if: ${{ env.GO == 1 }}
|
# - if: ${{ env.GO == 1 }}
|
||||||
# run: make -C examples/arduino arduino-nano
|
# run: |
|
||||||
|
# if ./test/match_changed_files.sh '^src|examples/arduino'; then
|
||||||
|
# make -C examples/arduino arduino-nano
|
||||||
|
# fi
|
||||||
- if: ${{ env.GO == 1 }}
|
- if: ${{ env.GO == 1 }}
|
||||||
run: make -C examples/arduino clean
|
run: make -C examples/arduino clean
|
||||||
|
|
||||||
|
|
||||||
matrix_examples:
|
generic_examples:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
example:
|
example:
|
||||||
- path: esp32/device-dashboard
|
|
||||||
- path: esp32/uart-bridge
|
- path: esp32/uart-bridge
|
||||||
|
- path: esp32/micropython
|
||||||
- path: esp8266/http-client-server
|
- path: esp8266/http-client-server
|
||||||
- path: microchip/same54-xpro/device-dashboard
|
name: ${{ matrix.example.path }}
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: if ./test/match_changed_files.sh '^src|^examples/${{ matrix.example.path }}'; then echo GO=1 >> $GITHUB_ENV ; fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
# nothing specific to install or do
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C examples/${{ matrix.example.path }} build
|
||||||
|
|
||||||
|
generic_examples_arm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
example:
|
||||||
- path: microchip/same54-xpro/mqtt-client
|
- path: microchip/same54-xpro/mqtt-client
|
||||||
- path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
|
- path: infineon/infineon-xmc4700_4800-lwip-rtx-rtos
|
||||||
- path: nxp/nxp-mimxrt1020-azurertos
|
- path: nxp/nxp-mimxrt1020-azurertos
|
||||||
@@ -316,54 +218,217 @@ jobs:
|
|||||||
- path: nxp/nxp-lpcxpresso54608-lwip-freertos
|
- path: nxp/nxp-lpcxpresso54608-lwip-freertos
|
||||||
- path: nxp/nxp-lpcxpresso54618-lwip-freertos
|
- path: nxp/nxp-lpcxpresso54618-lwip-freertos
|
||||||
- path: nxp/nxp-lpcxpresso54628-lwip-freertos
|
- path: nxp/nxp-lpcxpresso54628-lwip-freertos
|
||||||
- path: nxp/rt1020-evk-make-baremetal-builtin
|
|
||||||
- path: nxp/rt1020-evk-make-freertos-builtin
|
|
||||||
- path: nxp/nxp-twrk65f180m-lwip-freertos
|
- path: nxp/nxp-twrk65f180m-lwip-freertos
|
||||||
- path: nxp/nxp-twrkv58f220m-lwip-freertos
|
- path: nxp/nxp-twrkv58f220m-lwip-freertos
|
||||||
- path: rp2040/pico-rmii
|
- path: rp2040/pico-rmii
|
||||||
- path: rp2040/pico-rndis-dashboard
|
|
||||||
- path: rp2040/pico-w
|
|
||||||
- path: rp2040/pico-w5500
|
|
||||||
- path: stm32/nucleo-f429zi-make-baremetal-builtin
|
|
||||||
- path: stm32/nucleo-f429zi-make-freertos-builtin
|
|
||||||
- path: stm32/nucleo-f429zi-make-baremetal-builtin-rndis
|
- path: stm32/nucleo-f429zi-make-baremetal-builtin-rndis
|
||||||
- path: stm32/nucleo-f746zg-make-baremetal-builtin
|
|
||||||
- path: stm32/nucleo-f746zg-make-freertos-builtin
|
|
||||||
- path: stm32/nucleo-f746zg-make-freertos-tcp
|
|
||||||
- path: stm32/nucleo-f746zg-make-baremetal-builtin-rndis
|
- path: stm32/nucleo-f746zg-make-baremetal-builtin-rndis
|
||||||
- path: stm32/nucleo-g031-make-baremetal-builtin
|
- path: stm32/nucleo-g031-make-baremetal-builtin
|
||||||
- path: stm32/nucleo-h563zi-make-baremetal-builtin
|
|
||||||
- path: stm32/nucleo-h563zi-make-freertos-builtin
|
|
||||||
- path: stm32/nucleo-h743zi-make-baremetal-builtin
|
|
||||||
- path: stm32/nucleo-h743zi-make-freertos-builtin
|
|
||||||
- path: ti/ti-ek-tm4c1294xl-http-server
|
- path: ti/ti-ek-tm4c1294xl-http-server
|
||||||
- path: ti/ek-tm4c1294xl-baremetal
|
- path: ti/ek-tm4c1294xl-make-baremetal-builtin-rndis
|
||||||
- path: ti/ek-tm4c1294xl-freertos
|
|
||||||
name: ${{ matrix.example.path }}
|
name: ${{ matrix.example.path }}
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: if ./test/match_changed_files.sh '^src|^examples/${{ matrix.example.path }}'; then echo GO=1 >> $GITHUB_ENV ; fi
|
- run: if ./test/match_changed_files.sh '^src|^examples/${{ matrix.example.path }}'; then echo GO=1 >> $GITHUB_ENV ; fi
|
||||||
- if: ${{ env.GO == 1 }}
|
- if: ${{ env.GO == 1 }}
|
||||||
run: sudo apt -y update ; sudo apt -y install gcc-arm-none-eabi
|
run: sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
- if: ${{ env.GO == 1 }}
|
- if: ${{ env.GO == 1 }}
|
||||||
run: make -C examples/${{ matrix.example.path }} build
|
run: make -C examples/${{ matrix.example.path }} build
|
||||||
|
|
||||||
cube_examples:
|
device_dashboard_examples:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
example:
|
||||||
|
- path: esp32/device-dashboard
|
||||||
|
name: ${{ matrix.example.path }}
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: if ./test/match_changed_files.sh '^src|examples/stm32/nucleo-*-cube-*'; then echo GO=1 >> $GITHUB_ENV ; fi
|
- run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
# nothing specific to install or do
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C examples/${{ matrix.example.path }} build
|
||||||
|
|
||||||
|
device_dashboard_examples_arm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
example:
|
||||||
|
- path: microchip/same54-xpro/device-dashboard
|
||||||
|
- path: nxp/rt1020-evk-make-baremetal-builtin
|
||||||
|
- path: nxp/rt1020-evk-make-freertos-builtin
|
||||||
|
- path: nxp/rt1060-evk-make-baremetal-builtin
|
||||||
|
- path: nxp/rt1060-evk-make-freertos-builtin
|
||||||
|
- path: rp2040/pico-rndis-dashboard
|
||||||
|
- path: rp2040/pico-w
|
||||||
|
- path: rp2040/pico-w5500
|
||||||
|
- path: stm32/nucleo-f429zi-make-baremetal-builtin
|
||||||
|
- path: stm32/nucleo-f429zi-make-freertos-builtin
|
||||||
|
- path: stm32/nucleo-f746zg-make-baremetal-builtin
|
||||||
|
- path: stm32/nucleo-f746zg-make-freertos-builtin
|
||||||
|
- path: stm32/nucleo-f746zg-make-freertos-tcp
|
||||||
|
- path: stm32/nucleo-f746zg-make-baremetal-builtin-cmsis_driver
|
||||||
|
- path: stm32/nucleo-h563zi-make-baremetal-builtin
|
||||||
|
- path: stm32/nucleo-h563zi-make-freertos-builtin
|
||||||
|
- path: stm32/nucleo-h723zg-make-freertos-builtin
|
||||||
|
- path: stm32/nucleo-h743zi-make-baremetal-builtin
|
||||||
|
- path: stm32/nucleo-h743zi-make-freertos-builtin
|
||||||
|
- path: stm32/stm32h573i-dk-make-baremetal-builtin
|
||||||
|
- path: stm32/stm32h573i-dk-make-freertos-builtin
|
||||||
|
- path: ti/ek-tm4c1294xl-make-baremetal-builtin
|
||||||
|
- path: ti/ek-tm4c1294xl-make-freertos-builtin
|
||||||
|
name: ${{ matrix.example.path }}
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C examples/${{ matrix.example.path }} build
|
||||||
|
|
||||||
|
# mqtt_dashboard_examples:
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# strategy:
|
||||||
|
# fail-fast: false
|
||||||
|
# matrix:
|
||||||
|
# example:
|
||||||
|
# - path:
|
||||||
|
# name: ${{ matrix.example.path }}
|
||||||
|
# env:
|
||||||
|
# GO: 0
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v3
|
||||||
|
# with: { fetch-depth: 2 }
|
||||||
|
# - run: |
|
||||||
|
# if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
|
||||||
|
# echo GO=1 >> $GITHUB_ENV
|
||||||
|
# # nothing specific to install or do
|
||||||
|
# fi
|
||||||
|
# - if: ${{ env.GO == 1 }}
|
||||||
|
# run: make -C examples/${{ matrix.example.path }} build
|
||||||
|
|
||||||
|
mqtt_dashboard_examples_arm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
example:
|
||||||
|
- path: rp2040/pico-rndis-device
|
||||||
|
name: ${{ matrix.example.path }}
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C examples/${{ matrix.example.path }} build
|
||||||
|
|
||||||
|
# dual_examples:
|
||||||
|
# runs-on: ubuntu-latest
|
||||||
|
# strategy:
|
||||||
|
# fail-fast: false
|
||||||
|
# matrix:
|
||||||
|
# example:
|
||||||
|
# - path:
|
||||||
|
# name: ${{ matrix.example.path }}
|
||||||
|
# env:
|
||||||
|
# GO: 0
|
||||||
|
# steps:
|
||||||
|
# - uses: actions/checkout@v3
|
||||||
|
# with: { fetch-depth: 2 }
|
||||||
|
# - run: |
|
||||||
|
# if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
|
||||||
|
# echo GO=1 >> $GITHUB_ENV
|
||||||
|
# # nothing specific to install or do
|
||||||
|
# fi
|
||||||
|
# - if: ${{ env.GO == 1 }}
|
||||||
|
# run: |
|
||||||
|
# if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
|
||||||
|
# make -C examples/${{ matrix.example.path }} device_dashboard
|
||||||
|
# fi
|
||||||
|
# - if: ${{ env.GO == 1 }}
|
||||||
|
# run: |
|
||||||
|
# if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
|
||||||
|
# make -C examples/${{ matrix.example.path }} mqtt_dashboard
|
||||||
|
# fi
|
||||||
|
|
||||||
|
dual_examples_arm:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
example:
|
||||||
|
- path: stm32/nucleo-h723zg-make-baremetal-builtin
|
||||||
|
name: ${{ matrix.example.path }}
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|^examples/${{ matrix.example.path }}'; then
|
||||||
|
make -C examples/${{ matrix.example.path }} device_dashboard
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/mqtt-dashboard/device|^examples/${{ matrix.example.path }}'; then
|
||||||
|
make -C examples/${{ matrix.example.path }} mqtt_dashboard
|
||||||
|
fi
|
||||||
|
|
||||||
|
cube_examples:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/stm32/nucleo-*-cube-*'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
# nothing specific to install or do
|
||||||
|
fi
|
||||||
- if: ${{ env.GO == 1 }}
|
- if: ${{ env.GO == 1 }}
|
||||||
run: cd test/cube && make
|
run: cd test/cube && make
|
||||||
|
|
||||||
zephyr_examples:
|
zephyr_examples:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v3
|
- uses: actions/checkout@v3
|
||||||
with: { fetch-depth: 2 }
|
with: { fetch-depth: 2 }
|
||||||
- run: if ./test/match_changed_files.sh '^src/.*.[ch]|^examples/zephyr'; then sudo apt -y update ; sudo apt -y install gcc-arm-none-eabi ; fi
|
- run: |
|
||||||
|
if ./test/match_changed_files.sh '^src/.*.[ch]|^examples/zephyr'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
- if: ${{ env.GO == 1 }}
|
- if: ${{ env.GO == 1 }}
|
||||||
run: make -C examples/zephyr init
|
run: make -C examples/zephyr init
|
||||||
- name: minify manifest
|
- name: minify manifest
|
||||||
@@ -383,3 +448,293 @@ jobs:
|
|||||||
run: make -C examples/zephyr/mqtt-aws-client build
|
run: make -C examples/zephyr/mqtt-aws-client build
|
||||||
- if: ${{ env.GO == 1 }}
|
- if: ${{ env.GO == 1 }}
|
||||||
run: make -C examples/zephyr/websocket-server build
|
run: make -C examples/zephyr/websocket-server build
|
||||||
|
|
||||||
|
test_f7:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- name: baremetal
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-f7.*-make-" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-make-baremetal-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-f746zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-make-freertos-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-f746zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-make-freertos-tcp'; then
|
||||||
|
make -C examples/stm32/nucleo-f746zg-make-freertos-tcp test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- name: Cube
|
||||||
|
run: |
|
||||||
|
echo GO=0 >> $GITHUB_ENV
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f7.*-cube-'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
# nothing specific to install or do
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-cube-baremetal-builtin'; then
|
||||||
|
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-cube-freertos-builtin'; then
|
||||||
|
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f746zg-cube-freertos-lwip'; then
|
||||||
|
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f746zg-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=5
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
test_f4:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- name: baremetal
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-f4.*-make-" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-make-baremetal-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-f429zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-make-freertos-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-f429zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- name: Cube
|
||||||
|
run: |
|
||||||
|
echo GO=0 >> $GITHUB_ENV
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f4.*-cube-'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
# nothing specific to install or do
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-cube-baremetal-builtin'; then
|
||||||
|
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-cube-freertos-builtin'; then
|
||||||
|
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-f429zi-cube-freertos-lwip'; then
|
||||||
|
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-f429zi-cube-freertos-lwip VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=2
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_h743:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 3 }
|
||||||
|
- name: baremetal
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h74.*-make-" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h743zi-make-baremetal-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-h743zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h743zi-make-freertos-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-h743zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- name: Cube
|
||||||
|
run: |
|
||||||
|
echo GO=0 >> $GITHUB_ENV
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h74.*-cube-'; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
# nothing specific to install or do
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h743zi-cube-baremetal-builtin'; then
|
||||||
|
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-baremetal-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h743zi-cube-freertos-builtin'; then
|
||||||
|
make -C test/cube test PROJECTS=../../examples/stm32/nucleo-h743zi-cube-freertos-builtin VCON_API_KEY=${{secrets.VCON_API_KEY}} DEVICE=6
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_h723:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 3 }
|
||||||
|
- name: baremetal
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h72.*-make-" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h723zg-make-baremetal-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-h723zg-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h723zg-make-freertos-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-h723zg-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_h5:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 3 }
|
||||||
|
- name: baremetal
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/stm32/nucleo-h5.*-make-" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h563zi-make-baremetal-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-h563zi-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/stm32/nucleo-h563zi-make-freertos-builtin'; then
|
||||||
|
make -C examples/stm32/nucleo-h563zi-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_rt1020:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 3 }
|
||||||
|
- name: baremetal
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/nxp/rt1020.*-make-" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
#if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1020-evk-make-baremetal-builtin'; then
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/nxp/rt1020-evk-make-baremetal-builtin'; then
|
||||||
|
make -C examples/nxp/rt1020-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
#if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1020-evk-make-freertos-builtin'; then
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/nxp/rt1020-evk-make-freertos-builtin'; then
|
||||||
|
make -C examples/nxp/rt1020-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_rt1060:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 3 }
|
||||||
|
- name: baremetal
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/nxp/rt1060.*-make-" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1060-evk-make-baremetal-builtin'; then
|
||||||
|
make -C examples/nxp/rt1060-evk-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh '^src|examples/device-dashboard|examples/nxp/rt1060-evk-make-freertos-builtin'; then
|
||||||
|
make -C examples/nxp/rt1060-evk-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
fi
|
||||||
|
|
||||||
|
test_tm4c:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- name: baremetal
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/ti/ek-tm4c.*-make-" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C examples/ti/ek-tm4c1294xl-make-baremetal-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C examples/ti/ek-tm4c1294xl-make-freertos-builtin test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
|
||||||
|
test_same54:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- name: baremetal
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/microchip/same54-xpro" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C examples/microchip/same54-xpro/device-dashboard test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C examples/microchip/same54-xpro/mqtt-client test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
|
||||||
|
test_pico_w5500:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
env:
|
||||||
|
GO: 0
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with: { fetch-depth: 2 }
|
||||||
|
- name: SDK
|
||||||
|
run: |
|
||||||
|
if ./test/match_changed_files.sh "^src|examples/device-dashboard|examples/rp2040/pico-w5500" ; then
|
||||||
|
echo GO=1 >> $GITHUB_ENV
|
||||||
|
sudo apt -y update && sudo apt -y install gcc-arm-none-eabi
|
||||||
|
fi
|
||||||
|
- if: ${{ env.GO == 1 }}
|
||||||
|
run: make -C examples/rp2040/pico-w5500 test VCON_API_KEY=${{secrets.VCON_API_KEY}}
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ IPV6 ?= 1
|
|||||||
ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common
|
ASAN ?= -fsanitize=address,undefined,alignment -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common
|
||||||
ASAN_OPTIONS ?= detect_leaks=1
|
ASAN_OPTIONS ?= detect_leaks=1
|
||||||
EXAMPLES := $(dir $(wildcard examples/*/Makefile))
|
EXAMPLES := $(dir $(wildcard examples/*/Makefile))
|
||||||
EXAMPLES_MAC := $(filter-out examples/mip-pcap/ examples/mip-tap/, $(EXAMPLES))
|
EXAMPLES_MAC := $(filter-out examples/mip-pcap/ examples/mip-tap/ examples/micropython/, $(EXAMPLES))
|
||||||
EXAMPLES_WIN := $(dir $(wildcard examples/device-dashboard/Makefile) $(wildcard examples/file-*/Makefile) $(wildcard examples/http-*/Makefile) $(wildcard examples/mqtt-*/Makefile) $(wildcard examples/websocket-*/Makefile) $(wildcard examples/webui-*/Makefile))
|
EXAMPLES_WIN := $(dir $(wildcard examples/device-dashboard/Makefile) $(wildcard examples/file-*/Makefile) $(wildcard examples/http-*/Makefile) $(wildcard examples/mqtt-*/Makefile) $(wildcard examples/websocket-*/Makefile) $(wildcard examples/webui-*/Makefile))
|
||||||
EXAMPLES_EMBEDDED := $(filter-out $(wildcard examples/zephyr/*/), $(dir $(wildcard examples/*/*/Makefile)))
|
EXAMPLES_EMBEDDED := $(filter-out $(wildcard examples/zephyr/*/), $(dir $(wildcard examples/*/*/Makefile)))
|
||||||
PREFIX ?= /usr/local
|
PREFIX ?= /usr/local
|
||||||
@@ -90,6 +90,7 @@ musl: CC = $(DOCKER) mdashnet/cc1 gcc
|
|||||||
musl: RUN = $(DOCKER) mdashnet/cc1
|
musl: RUN = $(DOCKER) mdashnet/cc1
|
||||||
|
|
||||||
# Make sure we can build from unamalgamated sources.
|
# Make sure we can build from unamalgamated sources.
|
||||||
|
unamalgamated: CFLAGS += -DMG_ENABLE_MD5=1
|
||||||
unamalgamated: $(HDRS) Makefile test/packed_fs.c
|
unamalgamated: $(HDRS) Makefile test/packed_fs.c
|
||||||
$(CC) src/*.c test/packed_fs.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -g -o unit_test
|
$(CC) src/*.c test/packed_fs.c test/unit_test.c $(CFLAGS) $(LDFLAGS) -g -o unit_test
|
||||||
|
|
||||||
@@ -119,6 +120,13 @@ valgrind: Makefile mongoose.h mongoose.c
|
|||||||
$(CC) $(SRCS) $(VALGRIND_CFLAGS) $(LDFLAGS) -g -o unit_test
|
$(CC) $(SRCS) $(VALGRIND_CFLAGS) $(LDFLAGS) -g -o unit_test
|
||||||
$(VALGRIND_RUN) ./unit_test
|
$(VALGRIND_RUN) ./unit_test
|
||||||
|
|
||||||
|
misra:
|
||||||
|
cppcheck --addon=misra -DMG_ARCH=MG_ARCH_UNIX -DMG_ENABLE_PACKED_FS=1 -DMG_ENABLE_SSI=1 mongoose.c >/tmp/x 2>&1
|
||||||
|
cppcheck --addon=misra -DMG_ARCH=MG_ARCH_CUSTOM -DMG_ENABLE_LINES=1 -DTLS=MG_TLS_BUILTIN -DMG_ENABLE_TCPIP=1 mongoose.c >>/tmp/x 2>&1
|
||||||
|
cppcheck --addon=misra --clang -DMG_ENABLE_LINES=1 -DTLS=MG_TLS_BUILTIN -DMG_ENABLE_TCPIP=1 mongoose.c >>/tmp/x 2>&1
|
||||||
|
cppcheck --addon=misra --clang -DMG_ENABLE_LINES=1 -DMG_ENABLE_SSI=1 mongoose.c >>/tmp/x 2>&1
|
||||||
|
less /tmp/x
|
||||||
|
|
||||||
armhf: ASAN=
|
armhf: ASAN=
|
||||||
armhf: IPV6=0
|
armhf: IPV6=0
|
||||||
armhf: CC = $(DOCKER) mdashnet/armhf cc
|
armhf: CC = $(DOCKER) mdashnet/armhf cc
|
||||||
@@ -131,11 +139,11 @@ s390: CC = $(DOCKER) mdashnet/s390 cc
|
|||||||
s390: RUN = $(DOCKER) mdashnet/s390
|
s390: RUN = $(DOCKER) mdashnet/s390
|
||||||
s390: test
|
s390: test
|
||||||
|
|
||||||
arm: DEFS += -DMG_ENABLE_FILE=0 -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB
|
arm: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB
|
||||||
arm: mongoose.h $(SRCS)
|
arm: mongoose.h $(SRCS)
|
||||||
$(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0
|
$(DOCKER) mdashnet/armgcc arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test -nostartfiles --specs nosys.specs -e 0
|
||||||
|
|
||||||
riscv: DEFS += -DMG_ENABLE_FILE=0 -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB
|
riscv: DEFS += -DMG_ENABLE_POSIX_FS=0 -DMG_ENABLE_TCPIP=1 -DMG_ARCH=MG_ARCH_NEWLIB
|
||||||
riscv: mongoose.h $(SRCS)
|
riscv: mongoose.h $(SRCS)
|
||||||
$(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test
|
$(DOCKER) mdashnet/riscv riscv-none-elf-gcc -march=rv32imc -mabi=ilp32 $(SRCS) $(OPTS) $(WARN) $(INCS) $(DEFS) $(TFLAGS) -o unit_test
|
||||||
|
|
||||||
@@ -173,10 +181,10 @@ uninstall:
|
|||||||
rm -rf $(DESTDIR)$(LIBDIR)/libmongoose.a $(DESTDIR)$(LIBDIR)/libmongoose.so.$(VERSION) $(DESTDIR)$(INCLUDEDIR)/mongoose.h $(DESTDIR)$(LIBDIR)/libmongoose.so
|
rm -rf $(DESTDIR)$(LIBDIR)/libmongoose.a $(DESTDIR)$(LIBDIR)/libmongoose.so.$(VERSION) $(DESTDIR)$(INCLUDEDIR)/mongoose.h $(DESTDIR)$(LIBDIR)/libmongoose.so
|
||||||
|
|
||||||
mongoose.c: Makefile $(wildcard src/*.c) $(wildcard src/drivers/*.c)
|
mongoose.c: Makefile $(wildcard src/*.c) $(wildcard src/drivers/*.c)
|
||||||
(cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
|
(export LC_ALL=C ; cat src/license.h; echo; echo '#include "mongoose.h"' ; (for F in src/*.c src/drivers/*.c ; do echo; echo '#ifdef MG_ENABLE_LINES'; echo "#line 1 \"$$F\""; echo '#endif'; cat $$F | sed -e 's,#include ".*,,'; done))> $@
|
||||||
|
|
||||||
mongoose.h: $(HDRS) Makefile
|
mongoose.h: $(HDRS) Makefile
|
||||||
(cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/device.h src/net_builtin.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
(cat src/license.h; echo; echo '#ifndef MONGOOSE_H'; echo '#define MONGOOSE_H'; echo; cat src/version.h ; echo; echo '#ifdef __cplusplus'; echo 'extern "C" {'; echo '#endif'; cat src/arch.h src/arch_*.h src/net_ft.h src/net_lwip.h src/net_rl.h src/config.h src/str.h src/queue.h src/fmt.h src/printf.h src/log.h src/timer.h src/fs.h src/util.h src/url.h src/iobuf.h src/base64.h src/md5.h src/sha1.h src/sha256.h src/tls_aes128.h src/tls_uecc.h src/event.h src/net.h src/http.h src/ssi.h src/tls.h src/tls_mbed.h src/tls_openssl.h src/ws.h src/sntp.h src/mqtt.h src/dns.h src/json.h src/rpc.h src/ota.h src/device.h src/net_builtin.h src/profile.h src/drivers/*.h | sed -e '/keep/! s,#include ".*,,' -e 's,^#pragma once,,'; echo; echo '#ifdef __cplusplus'; echo '}'; echo '#endif'; echo '#endif // MONGOOSE_H')> $@
|
||||||
|
|
||||||
|
|
||||||
clean: clean_examples clean_embedded
|
clean: clean_examples clean_embedded
|
||||||
|
|||||||
@@ -19,23 +19,82 @@ robust, and easy. Features include:
|
|||||||
- write code once - and it'll work everywhere
|
- write code once - and it'll work everywhere
|
||||||
- ideal for the unification of the network infrastructure code across company
|
- ideal for the unification of the network infrastructure code across company
|
||||||
- Built-in protocols: plain TCP/UDP, SNTP, HTTP, MQTT, Websocket
|
- Built-in protocols: plain TCP/UDP, SNTP, HTTP, MQTT, Websocket
|
||||||
- SSL/TLS support: mbedTLS, OpenSSL or custom (via API)
|
- SSL/TLS support for mbedTLS and OpenSSL
|
||||||
- Asynchronous DNS resolver
|
- Asynchronous DNS resolver
|
||||||
- Tiny static and run-time footprint
|
- Tiny static and run-time footprint
|
||||||
- Source code is both ISO C and ISO C++ compliant
|
- Source code is both ISO C and ISO C++ compliant
|
||||||
- Very easy to integrate: just copy `mongoose.c` and `mongoose.h` files to your source tree. See
|
- Very easy to integrate: just copy `mongoose.c` and `mongoose.h` files to your source tree. See
|
||||||
[exact steps](https://mongoose.ws/documentation/#2-minute-integration-guide)
|
[exact steps](https://mongoose.ws/documentation/#2-minute-integration-guide)
|
||||||
- Works with any network stack with socket API, like LwIP or FreeRTOS-Plus-TCP
|
- Works in any environment with socket API, like LwIP, Zephyr, Azure
|
||||||
- Built-in TCP/IP stack with drivers for bare metal or RTOS systems
|
- Built-in TCP/IP stack with drivers for bare metal or RTOS systems
|
||||||
- Available drivers: STM32 F4, F7, H5, H7; NXP RT1020; TI TM4C; Microchip SAME54; Wiznet W5500
|
- Available drivers: STM32F, STM32H; NXP RT1xxx; TI TM4C; Microchip SAME54; Wiznet W5500
|
||||||
- A complete Web device dashboard on bare metal
|
- A complete Web device dashboard on bare metal
|
||||||
[Nucleo-F429ZI](examples/stm32/nucleo-f429zi-baremetal) is only 6 files
|
[Nucleo-F429ZI](examples/stm32/nucleo-f429zi-baremetal) is only 6 files
|
||||||
- For comparison, a CubeIDE generated HTTP example is 400+ files
|
- For comparison, a CubeIDE generated HTTP example is 400+ files
|
||||||
|
- Built-in TLS 1.3 server-side support
|
||||||
|
- Does not depend on any other software to implement networking
|
||||||
- Built-in firmware updates for STM32H5, STM32H7, and more coming
|
- Built-in firmware updates for STM32H5, STM32H7, and more coming
|
||||||
- Detailed [user guide, API reference and tons of tutorials](https://mongoose.ws/documentation/) available
|
- Detailed [user guide, API reference and tons of tutorials](https://mongoose.ws/documentation/)
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
# Commercial use
|
Create a simple web server that serves a directory. The behavior of the
|
||||||
|
HTTP server is specified by its event handler function:
|
||||||
|
|
||||||
|
```c
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
struct mg_mgr mgr; // Declare event manager
|
||||||
|
mg_mgr_init(&mgr); // Initialise event manager
|
||||||
|
mg_http_listen(&mgr, "http://0.0.0.0:8000", fn, NULL); // Setup listener
|
||||||
|
for (;;) {
|
||||||
|
mg_mgr_poll(&mgr, 1000); // Run an infinite event loop
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP server event handler function
|
||||||
|
void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
struct mg_http_serve_opts opts = { .root_dir = "./web_root/" };
|
||||||
|
mg_http_serve_dir(c, hm, &opts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
HTTP server implements a REST API that returns current time. JSON formatting:
|
||||||
|
```c
|
||||||
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
if (mg_http_match_uri(hm, "/api/time/get")) {
|
||||||
|
mg_http_reply(c, 200, "", "{%m:%lu}\n", MG_ESC("time"), time(NULL));
|
||||||
|
} else {
|
||||||
|
mg_http_reply(c, 500, "", "{%m:%m}\n", MG_ESC("error"), MG_ESC("Unsupported URI"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
MQTT client that subscribes to a topic `aa/bb` and prints all incoming messages:
|
||||||
|
|
||||||
|
```c
|
||||||
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
|
if (ev == MG_EV_MQTT_OPEN) {
|
||||||
|
struct mg_mqtt_opts opts = {.qos = 1, .topic = mg_str("aa/bb")};
|
||||||
|
mg_mqtt_sub(c, &opts);
|
||||||
|
} else if (ev == MG_EV_MQTT_MSG) {
|
||||||
|
struct mg_mqtt_message *mm = (struct mg_mqtt_message *) ev_data;
|
||||||
|
printf("Topic: %.*s, Message: %.*s",
|
||||||
|
mm->topic.len, mm->topic.ptr,
|
||||||
|
mm->data.len, mm->data.ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Commercial use
|
||||||
- Mongoose is used by hundreds of businesses, from Fortune500 giants like
|
- Mongoose is used by hundreds of businesses, from Fortune500 giants like
|
||||||
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses
|
Siemens, Schneider Electric, Broadcom, Bosch, Google, Samsung, Qualcomm, Caterpillar to the small businesses
|
||||||
- Used to solve a wide range of business needs, like implementing Web UI
|
- Used to solve a wide range of business needs, like implementing Web UI
|
||||||
@@ -49,7 +108,7 @@ robust, and easy. Features include:
|
|||||||
services](https://mongoose.ws/integration/) - don't hesitate to [contact us](https://mongoose.ws/contact/)
|
services](https://mongoose.ws/integration/) - don't hesitate to [contact us](https://mongoose.ws/contact/)
|
||||||
|
|
||||||
|
|
||||||
# Security
|
## Security
|
||||||
|
|
||||||
We take security seriously:
|
We take security seriously:
|
||||||
1. Mongoose repository runs a
|
1. Mongoose repository runs a
|
||||||
@@ -75,7 +134,7 @@ We take security seriously:
|
|||||||
of which we get notified and in case of any issue, act similar to (3).
|
of which we get notified and in case of any issue, act similar to (3).
|
||||||
|
|
||||||
|
|
||||||
# Contributions
|
## Contributions
|
||||||
|
|
||||||
Contributions are welcome! Please follow the guidelines below:
|
Contributions are welcome! Please follow the guidelines below:
|
||||||
|
|
||||||
|
|||||||
File renamed without changes.
File renamed without changes.
@@ -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"
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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);
|
||||||
|
}
|
||||||
@@ -38,7 +38,7 @@ void setup() {
|
|||||||
// Setup HTTP listener. Respond "ok" on any HTTP request
|
// Setup HTTP listener. Respond "ok" on any HTTP request
|
||||||
mg_http_listen(
|
mg_http_listen(
|
||||||
&mgr, "http://0.0.0.0",
|
&mgr, "http://0.0.0.0",
|
||||||
[](struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
[](struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "ok\n");
|
if (ev == MG_EV_HTTP_MSG) mg_http_reply(c, 200, "", "ok\n");
|
||||||
},
|
},
|
||||||
&mgr);
|
&mgr);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ void mqtt_publish(const char *message) {
|
|||||||
if (mqtt_connection) mg_mqtt_pub(mqtt_connection, &opts);
|
if (mqtt_connection) mg_mqtt_pub(mqtt_connection, &opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_MQTT_OPEN) {
|
if (ev == MG_EV_MQTT_OPEN) {
|
||||||
MG_INFO(("%lu CONNECTED to %s", c->id, MQTT_SERVER));
|
MG_INFO(("%lu CONNECTED to %s", c->id, MQTT_SERVER));
|
||||||
struct mg_mqtt_opts opts = {};
|
struct mg_mqtt_opts opts = {};
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ uint8_t answer[] = {
|
|||||||
1, 2, 3, 4 // 4 bytes - IP address
|
1, 2, 3, 4 // 4 bytes - IP address
|
||||||
};
|
};
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_OPEN) {
|
if (ev == MG_EV_OPEN) {
|
||||||
c->is_hexdumping = 1;
|
c->is_hexdumping = 1;
|
||||||
} else if (ev == MG_EV_READ) {
|
} else if (ev == MG_EV_READ) {
|
||||||
@@ -41,7 +41,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
}
|
}
|
||||||
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
mg_iobuf_del(&c->recv, 0, c->recv.len);
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ GZIP ?= gzip # For compressing files in web_root/
|
|||||||
OUT ?= -o $(PROG) # Compiler argument for output file
|
OUT ?= -o $(PROG) # Compiler argument for output file
|
||||||
SOURCES = main.c mongoose.c net.c packed_fs.c # Source code files
|
SOURCES = main.c mongoose.c net.c packed_fs.c # Source code files
|
||||||
CFLAGS = -W -Wall -Wextra -g -I. # Build options
|
CFLAGS = -W -Wall -Wextra -g -I. # Build options
|
||||||
|
NPX ?= npx
|
||||||
|
|
||||||
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
|
# Mongoose build options. See https://mongoose.ws/documentation/#build-options
|
||||||
CFLAGS_MONGOOSE += -DMG_ENABLE_PACKED_FS=1
|
CFLAGS_MONGOOSE += -DMG_ENABLE_PACKED_FS=1
|
||||||
@@ -32,7 +33,7 @@ web_root/bundle.js:
|
|||||||
|
|
||||||
# Create optimised CSS. Prerequisite: npm -g i tailwindcss tailwindcss-font-inter
|
# Create optimised CSS. Prerequisite: npm -g i tailwindcss tailwindcss-font-inter
|
||||||
web_root/main.css: web_root/index.html $(wildcard web_root/*.js)
|
web_root/main.css: web_root/index.html $(wildcard web_root/*.js)
|
||||||
npx tailwindcss -o $@ --minify
|
$(NPX) tailwindcss -o $@ --minify
|
||||||
|
|
||||||
# Generate packed filesystem for serving Web UI
|
# Generate packed filesystem for serving Web UI
|
||||||
packed_fs.c: $(wildcard web_root/*) $(wildcard certs/*) Makefile web_root/main.css web_root/bundle.js
|
packed_fs.c: $(wildcard web_root/*) $(wildcard certs/*) Makefile web_root/main.css web_root/bundle.js
|
||||||
|
|||||||
@@ -23,4 +23,4 @@ The main dashboard page shows the interactive console
|
|||||||

|

|
||||||
|
|
||||||
|
|
||||||
<!-- See a detailed tutorial at https://mongoose.ws/tutorials/device-dashboard/ -->
|
See a detailed tutorial at https://mongoose.ws/tutorials/device-dashboard/
|
||||||
@@ -51,7 +51,7 @@ int ui_event_next(int no, struct ui_event *e) {
|
|||||||
|
|
||||||
// SNTP connection event handler. When we get a response from an SNTP server,
|
// SNTP connection event handler. When we get a response from an SNTP server,
|
||||||
// adjust s_boot_timestamp. We'll get a valid time from that point on
|
// adjust s_boot_timestamp. We'll get a valid time from that point on
|
||||||
static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void sfn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
uint64_t *expiration_time = (uint64_t *) c->data;
|
uint64_t *expiration_time = (uint64_t *) c->data;
|
||||||
if (ev == MG_EV_OPEN) {
|
if (ev == MG_EV_OPEN) {
|
||||||
*expiration_time = mg_millis() + 3000; // Store expiration time in 3s
|
*expiration_time = mg_millis() + 3000; // Store expiration time in 3s
|
||||||
@@ -62,7 +62,6 @@ static void sfn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
} else if (ev == MG_EV_POLL) {
|
} else if (ev == MG_EV_POLL) {
|
||||||
if (mg_millis() > *expiration_time) c->is_closing = 1;
|
if (mg_millis() > *expiration_time) c->is_closing = 1;
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_sntp_fn(void *param) { // SNTP timer function. Sync up time
|
static void timer_sntp_fn(void *param) { // SNTP timer function. Sync up time
|
||||||
@@ -123,9 +122,9 @@ static void handle_debug(struct mg_connection *c, struct mg_http_message *hm) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t print_int_arr(void (*out)(char, void *), void *ptr, va_list *ap) {
|
static size_t print_int_arr(void (*out)(char, void *), void *ptr, va_list *ap) {
|
||||||
size_t len = 0, num = va_arg(*ap, size_t); // Number of items in the array
|
size_t i, len = 0, num = va_arg(*ap, size_t); // Number of items in the array
|
||||||
int *arr = va_arg(*ap, int *); // Array ptr
|
int *arr = va_arg(*ap, int *); // Array ptr
|
||||||
for (size_t i = 0; i < num; i++) {
|
for (i = 0; i < num; i++) {
|
||||||
len += mg_xprintf(out, ptr, "%s%d", i == 0 ? "" : ",", arr[i]);
|
len += mg_xprintf(out, ptr, "%s%d", i == 0 ? "" : ",", arr[i]);
|
||||||
}
|
}
|
||||||
return len;
|
return len;
|
||||||
@@ -133,7 +132,7 @@ static size_t print_int_arr(void (*out)(char, void *), void *ptr, va_list *ap) {
|
|||||||
|
|
||||||
static void handle_stats_get(struct mg_connection *c) {
|
static void handle_stats_get(struct mg_connection *c) {
|
||||||
int points[] = {21, 22, 22, 19, 18, 20, 23, 23, 22, 22, 22, 23, 22};
|
int points[] = {21, 22, 22, 19, 18, 20, 23, 23, 22, 22, 22, 23, 22};
|
||||||
mg_http_reply(c, 200, s_json_header, "{%m:%d,%m:%d,%m:[%M]}",
|
mg_http_reply(c, 200, s_json_header, "{%m:%d,%m:%d,%m:[%M]}\n",
|
||||||
MG_ESC("temperature"), 21, //
|
MG_ESC("temperature"), 21, //
|
||||||
MG_ESC("humidity"), 67, //
|
MG_ESC("humidity"), 67, //
|
||||||
MG_ESC("points"), print_int_arr,
|
MG_ESC("points"), print_int_arr,
|
||||||
@@ -148,11 +147,11 @@ static size_t print_events(void (*out)(char, void *), void *ptr, va_list *ap) {
|
|||||||
int end = no + EVENTS_PER_PAGE;
|
int end = no + EVENTS_PER_PAGE;
|
||||||
|
|
||||||
while ((no = ui_event_next(no, &ev)) != 0 && no <= end) {
|
while ((no = ui_event_next(no, &ev)) != 0 && no <= end) {
|
||||||
len += mg_xprintf(out, ptr, "%s{%m:%lu,%m:%d,%m:%d,%m:%m}", //
|
len += mg_xprintf(out, ptr, "%s{%m:%lu,%m:%d,%m:%d,%m:%m}\n", //
|
||||||
len == 0 ? "" : ",", //
|
len == 0 ? "" : ",", //
|
||||||
MG_ESC("time"), ev.timestamp, //
|
MG_ESC("time"), ev.timestamp, //
|
||||||
MG_ESC("type"), ev.type, //
|
MG_ESC("type"), ev.type, //
|
||||||
MG_ESC("prio"), ev.prio, //
|
MG_ESC("prio"), ev.prio, //
|
||||||
MG_ESC("text"), MG_ESC(ev.text));
|
MG_ESC("text"), MG_ESC(ev.text));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,27 +161,25 @@ static size_t print_events(void (*out)(char, void *), void *ptr, va_list *ap) {
|
|||||||
static void handle_events_get(struct mg_connection *c,
|
static void handle_events_get(struct mg_connection *c,
|
||||||
struct mg_http_message *hm) {
|
struct mg_http_message *hm) {
|
||||||
int pageno = mg_json_get_long(hm->body, "$.page", 1);
|
int pageno = mg_json_get_long(hm->body, "$.page", 1);
|
||||||
mg_http_reply(c, 200, s_json_header, "{%m:[%M], %m:%d}", MG_ESC("arr"),
|
mg_http_reply(c, 200, s_json_header, "{%m:[%M], %m:%d}\n", MG_ESC("arr"),
|
||||||
print_events, pageno, MG_ESC("totalCount"), MAX_EVENTS_NO);
|
print_events, pageno, MG_ESC("totalCount"), MAX_EVENTS_NO);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
|
static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
|
||||||
struct settings settings;
|
struct settings settings;
|
||||||
|
char *s = mg_json_get_str(body, "$.device_name");
|
||||||
|
bool ok = true;
|
||||||
memset(&settings, 0, sizeof(settings));
|
memset(&settings, 0, sizeof(settings));
|
||||||
mg_json_get_bool(body, "$.log_enabled", &settings.log_enabled);
|
mg_json_get_bool(body, "$.log_enabled", &settings.log_enabled);
|
||||||
settings.log_level = mg_json_get_long(body, "$.log_level", 0);
|
settings.log_level = mg_json_get_long(body, "$.log_level", 0);
|
||||||
settings.brightness = mg_json_get_long(body, "$.brightness", 0);
|
settings.brightness = mg_json_get_long(body, "$.brightness", 0);
|
||||||
char *s = mg_json_get_str(body, "$.device_name");
|
|
||||||
if (s && strlen(s) < MAX_DEVICE_NAME) {
|
if (s && strlen(s) < MAX_DEVICE_NAME) {
|
||||||
free(settings.device_name);
|
free(settings.device_name);
|
||||||
settings.device_name = s;
|
settings.device_name = s;
|
||||||
} else {
|
} else {
|
||||||
free(s);
|
free(s);
|
||||||
}
|
}
|
||||||
|
s_settings = settings; // Save to the device flash
|
||||||
// Save to the device flash
|
|
||||||
s_settings = settings;
|
|
||||||
bool ok = true;
|
|
||||||
mg_http_reply(c, 200, s_json_header,
|
mg_http_reply(c, 200, s_json_header,
|
||||||
"{%m:%s,%m:%m}", //
|
"{%m:%s,%m:%m}", //
|
||||||
MG_ESC("status"), ok ? "true" : "false", //
|
MG_ESC("status"), ok ? "true" : "false", //
|
||||||
@@ -190,7 +187,7 @@ static void handle_settings_set(struct mg_connection *c, struct mg_str body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void handle_settings_get(struct mg_connection *c) {
|
static void handle_settings_get(struct mg_connection *c) {
|
||||||
mg_http_reply(c, 200, s_json_header, "{%m:%s,%m:%hhu,%m:%hhu,%m:%m}", //
|
mg_http_reply(c, 200, s_json_header, "{%m:%s,%m:%hhu,%m:%hhu,%m:%m}\n", //
|
||||||
MG_ESC("log_enabled"),
|
MG_ESC("log_enabled"),
|
||||||
s_settings.log_enabled ? "true" : "false", //
|
s_settings.log_enabled ? "true" : "false", //
|
||||||
MG_ESC("log_level"), s_settings.log_level, //
|
MG_ESC("log_level"), s_settings.log_level, //
|
||||||
@@ -239,7 +236,7 @@ static void handle_firmware_rollback(struct mg_connection *c) {
|
|||||||
|
|
||||||
static size_t print_status(void (*out)(char, void *), void *ptr, va_list *ap) {
|
static size_t print_status(void (*out)(char, void *), void *ptr, va_list *ap) {
|
||||||
int fw = va_arg(*ap, int);
|
int fw = va_arg(*ap, int);
|
||||||
return mg_xprintf(out, ptr, "{%m:%d,%m:%c%lx%c,%m:%u,%m:%u}",
|
return mg_xprintf(out, ptr, "{%m:%d,%m:%c%lx%c,%m:%u,%m:%u}\n",
|
||||||
MG_ESC("status"), mg_ota_status(fw), MG_ESC("crc32"), '"',
|
MG_ESC("status"), mg_ota_status(fw), MG_ESC("crc32"), '"',
|
||||||
mg_ota_crc32(fw), '"', MG_ESC("size"), mg_ota_size(fw),
|
mg_ota_crc32(fw), '"', MG_ESC("size"), mg_ota_size(fw),
|
||||||
MG_ESC("timestamp"), mg_ota_timestamp(fw));
|
MG_ESC("timestamp"), mg_ota_timestamp(fw));
|
||||||
@@ -255,10 +252,18 @@ static void handle_device_reset(struct mg_connection *c) {
|
|||||||
mg_timer_add(c->mgr, 500, 0, (void (*)(void *)) mg_device_reset, NULL);
|
mg_timer_add(c->mgr, 500, 0, (void (*)(void *)) mg_device_reset, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void handle_device_eraselast(struct mg_connection *c) {
|
||||||
|
size_t ss = mg_flash_sector_size(), size = mg_flash_size();
|
||||||
|
char *base = (char *) mg_flash_start(), *last = base + size - ss;
|
||||||
|
if (mg_flash_bank() == 2) last -= size / 2;
|
||||||
|
mg_flash_erase(last);
|
||||||
|
mg_http_reply(c, 200, s_json_header, "true\n");
|
||||||
|
}
|
||||||
|
|
||||||
// HTTP request handler function
|
// HTTP request handler function
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_ACCEPT) {
|
if (ev == MG_EV_ACCEPT) {
|
||||||
if (fn_data != NULL) { // TLS listener!
|
if (c->fn_data != NULL) { // TLS listener!
|
||||||
struct mg_tls_opts opts = {0};
|
struct mg_tls_opts opts = {0};
|
||||||
opts.cert = mg_unpacked("/certs/server_cert.pem");
|
opts.cert = mg_unpacked("/certs/server_cert.pem");
|
||||||
opts.key = mg_unpacked("/certs/server_key.pem");
|
opts.key = mg_unpacked("/certs/server_key.pem");
|
||||||
@@ -294,6 +299,8 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
handle_firmware_status(c);
|
handle_firmware_status(c);
|
||||||
} else if (mg_http_match_uri(hm, "/api/device/reset")) {
|
} else if (mg_http_match_uri(hm, "/api/device/reset")) {
|
||||||
handle_device_reset(c);
|
handle_device_reset(c);
|
||||||
|
} else if (mg_http_match_uri(hm, "/api/device/eraselast")) {
|
||||||
|
handle_device_eraselast(c);
|
||||||
} else {
|
} else {
|
||||||
struct mg_http_serve_opts opts;
|
struct mg_http_serve_opts opts;
|
||||||
memset(&opts, 0, sizeof(opts));
|
memset(&opts, 0, sizeof(opts));
|
||||||
|
|||||||
@@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
#if !defined(HTTP_URL)
|
#if !defined(HTTP_URL)
|
||||||
#define HTTP_URL "http://0.0.0.0:8000"
|
#define HTTP_URL "http://0.0.0.0:8000"
|
||||||
#endif
|
#endif
|
||||||
@@ -25,3 +29,7 @@ struct ui_event {
|
|||||||
};
|
};
|
||||||
|
|
||||||
void web_init(struct mg_mgr *mgr);
|
void web_init(struct mg_mgr *mgr);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
+1524
-1508
File diff suppressed because it is too large.
Load diff
Binary file not shown.
|
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 24 KiB |
@@ -42,6 +42,7 @@ export const Icons = {
|
|||||||
chip: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-15 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-1.5h10.5a2.25 2.25 0 002.25-2.25V6.75a2.25 2.25 0 00-2.25-2.25H6.75A2.25 2.25 0 004.5 6.75v10.5a2.25 2.25 0 002.25 2.25zm.75-12h9v9h-9v-9z" /> </svg>`,
|
chip: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M8.25 3v1.5M4.5 8.25H3m18 0h-1.5M4.5 12H3m18 0h-1.5m-15 3.75H3m18 0h-1.5M8.25 19.5V21M12 3v1.5m0 15V21m3.75-18v1.5m0 15V21m-9-1.5h10.5a2.25 2.25 0 002.25-2.25V6.75a2.25 2.25 0 00-2.25-2.25H6.75A2.25 2.25 0 004.5 6.75v10.5a2.25 2.25 0 002.25 2.25zm.75-12h9v9h-9v-9z" /> </svg>`,
|
||||||
camera: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM18.75 10.5h.008v.008h-.008V10.5z" /> </svg>`,
|
camera: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M6.827 6.175A2.31 2.31 0 015.186 7.23c-.38.054-.757.112-1.134.175C2.999 7.58 2.25 8.507 2.25 9.574V18a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9.574c0-1.067-.75-1.994-1.802-2.169a47.865 47.865 0 00-1.134-.175 2.31 2.31 0 01-1.64-1.055l-.822-1.316a2.192 2.192 0 00-1.736-1.039 48.774 48.774 0 00-5.232 0 2.192 2.192 0 00-1.736 1.039l-.821 1.316z" /> <path stroke-linecap="round" stroke-linejoin="round" d="M16.5 12.75a4.5 4.5 0 11-9 0 4.5 4.5 0 019 0zM18.75 10.5h.008v.008h-.008V10.5z" /> </svg>`,
|
||||||
arrows: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5" /> </svg>`,
|
arrows: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M7.5 21L3 16.5m0 0L7.5 12M3 16.5h13.5m0-13.5L21 7.5m0 0L16.5 12M21 7.5H7.5" /> </svg>`,
|
||||||
|
doc: props => html`<svg class=${props.class} xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor"> <path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" /></svg>`,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const tipColors = {
|
export const tipColors = {
|
||||||
@@ -85,7 +86,7 @@ export function Notification({ok, text, close}) {
|
|||||||
<div class="p-4">
|
<div class="p-4">
|
||||||
<div class="flex items-start">
|
<div class="flex items-start">
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<${ok ? Icons.ok : Icons.failed} class="h-6 w-6 ${ok ? 'text-green-400' : 'text-red-400'}" />
|
<${ok ? Icons.ok : Icons.fail} class="h-6 w-6 ${ok ? 'text-green-400' : 'text-red-400'}" />
|
||||||
<//>
|
<//>
|
||||||
<div class="ml-3 w-0 flex-1 pt-0.5">
|
<div class="ml-3 w-0 flex-1 pt-0.5">
|
||||||
<p class="text-sm font-medium text-gray-900">${text}</p>
|
<p class="text-sm font-medium text-gray-900">${text}</p>
|
||||||
@@ -157,7 +158,7 @@ export function Colored({icon, text, colors}) {
|
|||||||
export function Stat({title, text, tipText, tipIcon, tipColors, colors}) {
|
export function Stat({title, text, tipText, tipIcon, tipColors, colors}) {
|
||||||
return html`
|
return html`
|
||||||
<div class="flex flex-col bg-white border shadow-sm rounded-xl dark:bg-slate-900 dark:border-gray-800">
|
<div class="flex flex-col bg-white border shadow-sm rounded-xl dark:bg-slate-900 dark:border-gray-800">
|
||||||
<div class="overflow-auto rounded-lg bg-white px-4 py-2 shadow">
|
<div class="overflow-auto rounded-lg bg-white px-4 py-2 ">
|
||||||
<div class="flex items-center gap-x-2">
|
<div class="flex items-center gap-x-2">
|
||||||
<p class="text-sm truncate text-gray-500 font-medium"> ${title} </p>
|
<p class="text-sm truncate text-gray-500 font-medium"> ${title} </p>
|
||||||
<//>
|
<//>
|
||||||
@@ -173,27 +174,34 @@ export function Stat({title, text, tipText, tipIcon, tipColors, colors}) {
|
|||||||
<//>`;
|
<//>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function TextValue({value, setfn, disabled, placeholder, type, addonRight, addonLeft, attr, min, max, step}) {
|
export function TextValue({value, setfn, disabled, placeholder, type, addonRight, addonLeft, attr, min, max, step, mult}) {
|
||||||
const [bg, setBg] = useState('bg-white');
|
const [bg, setBg] = useState('bg-white');
|
||||||
useEffect(() => { checkval(value); }, []);
|
useEffect(() => { if (type == 'number') checkval(+min, +max, +value); }, []);
|
||||||
const checkval = function(v) {
|
step ||= '1', mult ||= 1;
|
||||||
|
const checkval = function(min, max, v) {
|
||||||
setBg('bg-white');
|
setBg('bg-white');
|
||||||
if (min && v < min) setBg('bg-red-100 border-red-200');
|
if (min && v < min) setBg('bg-red-100 border-red-200');
|
||||||
if (max && v > max) setBg('bg-red-100 border-red-200');
|
if (max && v > max) setBg('bg-red-100 border-red-200');
|
||||||
};
|
};
|
||||||
const oninput = ev => {
|
const m = step.match(/^.+\.(.+)/);
|
||||||
|
const digits = m ? m[1].length : 0;
|
||||||
|
const onchange = ev => {
|
||||||
let v = ev.target.value;
|
let v = ev.target.value;
|
||||||
if (type == 'number') v = parseInt(v);
|
if (type == 'number') {
|
||||||
|
checkval(+min, +max, +v);
|
||||||
|
v = +(parseFloat(v) / mult).toFixed(digits);
|
||||||
|
}
|
||||||
setfn(v);
|
setfn(v);
|
||||||
checkval(v);
|
|
||||||
};
|
};
|
||||||
|
if (type == 'number') value = +(value * mult).toFixed(digits);
|
||||||
return html`
|
return html`
|
||||||
<div class="flex w-full items-center rounded border shadow-sm ${bg}">
|
<div class="flex w-full items-center rounded border shadow-sm ${bg}">
|
||||||
${ addonLeft && html`<span class="inline-flex font-normal truncate py-1 border-r bg-slate-100 items-center border-gray-300 px-2 text-gray-500 text-xs">${addonLeft}<//>` }
|
${addonLeft && html`<span class="inline-flex font-normal truncate py-1 border-r bg-slate-100 items-center border-gray-300 px-2 text-gray-500 text-xs">${addonLeft}<//>` }
|
||||||
<input type=${type || 'text'} disabled=${disabled}
|
<input type=${type || 'text'} disabled=${disabled} value=${value}
|
||||||
oninput=${oninput} ...${attr}
|
step=${step} min=${min} max=${max}
|
||||||
class="${bg} font-normal text-sm rounded w-full flex-1 py-0.5 px-2 text-gray-700 placeholder:text-gray-400 focus:outline-none disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-500" placeholder=${placeholder} value=${value} />
|
onchange=${onchange} ...${attr}
|
||||||
${ addonRight && html`<span class="inline-flex font-normal truncate py-1 border-l bg-slate-100 items-center border-gray-300 px-2 text-gray-500 text-xs overflow-scroll" style="min-width: 50%;">${addonRight}<//>` }
|
class="${bg} font-normal text-sm rounded w-full flex-1 py-0.5 px-2 text-gray-700 placeholder:text-gray-400 focus:outline-none disabled:cursor-not-allowed disabled:bg-gray-100 disabled:text-gray-500" placeholder=${placeholder} />
|
||||||
|
${addonRight && html`<span class="inline-flex font-normal truncate py-1 border-l bg-slate-100 items-center border-gray-300 px-2 text-gray-500 text-xs overflow-scroll" style="min-width: 50%;">${addonRight}<//>` }
|
||||||
<//>`;
|
<//>`;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -222,7 +230,7 @@ export function Setting(props) {
|
|||||||
if (props.type == 'select') input = SelectValue;
|
if (props.type == 'select') input = SelectValue;
|
||||||
return html`
|
return html`
|
||||||
<div class=${props.cls || 'grid grid-cols-2 gap-2 my-1'}>
|
<div class=${props.cls || 'grid grid-cols-2 gap-2 my-1'}>
|
||||||
<label class="flex items-center text-sm text-gray-700 mr-2 font-medium">${props.title}<//>
|
<label class="flex items-center text-sm text-gray-700 mr-2 font-medium ${props.title || 'hidden'}">${props.title}<//>
|
||||||
<div class="flex items-center">${h(input, props)}<//>
|
<div class="flex items-center">${h(input, props)}<//>
|
||||||
<//>`;
|
<//>`;
|
||||||
};
|
};
|
||||||
@@ -317,7 +325,6 @@ export function UploadFileButton(props) {
|
|||||||
finish(ok); // All chunks sent
|
finish(ok); // All chunks sent
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
//setFailed(false);
|
|
||||||
sendChunk(0);
|
sendChunk(0);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -344,6 +351,6 @@ export function UploadFileButton(props) {
|
|||||||
<div class="inline-flex flex-col ${props.class}">
|
<div class="inline-flex flex-col ${props.class}">
|
||||||
<input class="hidden" type="file" ref=${input} onchange=${onchange} accept=${props.accept} />
|
<input class="hidden" type="file" ref=${input} onchange=${onchange} accept=${props.accept} />
|
||||||
<${Button} title=${props.title} icon=${Icons.download} onclick=${onclick} ref=${btn} colors=${props.colors} />
|
<${Button} title=${props.title} icon=${Icons.download} onclick=${onclick} ref=${btn} colors=${props.colors} />
|
||||||
<div class="py-2 text-sm text-slate-400 ${status || 'hidden'}">${status}<//>
|
<div class="pt-2 text-sm text-slate-400 ${status || 'hidden'}">${status}<//>
|
||||||
<//>`;
|
<//>`;
|
||||||
};
|
};
|
||||||
@@ -1 +1 @@
|
|||||||
/*! 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
|
||||||
@@ -216,36 +216,49 @@ function FirmwareUpdate({}) {
|
|||||||
.then(r => new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000)));
|
.then(r => new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000)));
|
||||||
const onrollback = ev => fetch('api/firmware/rollback')
|
const onrollback = ev => fetch('api/firmware/rollback')
|
||||||
.then(onreboot);
|
.then(onreboot);
|
||||||
|
const onerase = ev => fetch('api/device/eraselast').then(refresh);
|
||||||
const onupload = function(ok, name, size) {
|
const onupload = function(ok, name, size) {
|
||||||
if (!ok) return false;
|
if (!ok) return false;
|
||||||
return new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000));
|
return new Promise(r => setTimeout(ev => { refresh(); r(); }, 3000));
|
||||||
};
|
};
|
||||||
const clean = info[0].valid && info[0].golden == 0;
|
|
||||||
return html`
|
return html`
|
||||||
<div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-2">
|
<div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-3">
|
||||||
<${FirmwareStatus} title="Current firmware image" info=${info[0]}>
|
<${FirmwareStatus} title="Current firmware image" info=${info[0]}>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<${Button} title="Commit this firmware"
|
<${Button} title="Commit this firmware" onclick=${oncommit}
|
||||||
onclick=${oncommit} icon=${Icons.thumbUp} disabled=${clean} />
|
icon=${Icons.thumbUp} disabled=${info[0].status == 3} cls="w-full" />
|
||||||
<${Button} title="Reboot device" onclick=${onreboot} icon=${Icons.refresh} clsx="absolute top-4 right-4" />
|
|
||||||
<${UploadFileButton}
|
|
||||||
title="Upload new firmware: choose .bin file:" onupload=${onupload}
|
|
||||||
url="api/firmware/upload" accept=".bin,.uf2" />
|
|
||||||
<//>
|
<//>
|
||||||
<//>
|
<//>
|
||||||
<${FirmwareStatus} title="Previous firmware image" info=${info[1]}>
|
<${FirmwareStatus} title="Previous firmware image" info=${info[1]}>
|
||||||
<${Button} title="Rollback to this firmware" onclick=${onrollback}
|
<${Button} title="Rollback to this firmware" onclick=${onrollback}
|
||||||
icon=${Icons.backward} disabled=${info[1].valid == false} />
|
icon=${Icons.backward} disabled=${info[1].status == 0} cls="w-full" />
|
||||||
<//>
|
<//>
|
||||||
|
<div class="bg-white xm-4 divide-y border rounded flex flex-col">
|
||||||
|
<div class="font-light uppercase flex items-center text-gray-600 px-4 py-2">
|
||||||
|
Device control
|
||||||
|
<//>
|
||||||
|
<div class="px-4 py-3 flex flex-col gap-2 grow">
|
||||||
|
<${UploadFileButton}
|
||||||
|
title="Upload new firmware .bin file" onupload=${onupload}
|
||||||
|
url="api/firmware/upload" accept=".bin,.uf2" />
|
||||||
|
<div class="grow"><//>
|
||||||
|
<${Button} title="Reboot device" onclick=${onreboot} icon=${Icons.refresh} cls="w-full" />
|
||||||
|
<${Button} title="Erase last sector" onclick=${onerase} icon=${Icons.doc} cls="w-full hidden" />
|
||||||
|
<//>
|
||||||
|
<//>
|
||||||
|
<//>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="m-4 gap-4 grid grid-cols-1 lg:grid-cols-2">
|
||||||
<div class="bg-white border shadow-lg">
|
<div class="bg-white border shadow-lg">
|
||||||
<${DeveloperNote}>
|
<${DeveloperNote}>
|
||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
When a new firmware gets flashed, its status is marked as, "first_boot".
|
Firmware status and other information is stored in the last sector
|
||||||
That is an unreliable (uncommitted) firmware. A user may choose
|
of flash
|
||||||
to revert back to the previous committed firmware on the subsequent
|
<//>
|
||||||
boots. Clicking on the "commit" button calls "mg_ota_commit()" function
|
<div class="my-2">
|
||||||
which commits the firmware.
|
Firmware status can be FIRST_BOOT, UNCOMMITTED or COMMITTED. If no
|
||||||
|
information is available, it is UNAVAILABLE.
|
||||||
<//>
|
<//>
|
||||||
<div class="my-2">
|
<div class="my-2">
|
||||||
This GUI loads a firmware file and sends it chunk by chunk to the
|
This GUI loads a firmware file and sends it chunk by chunk to the
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
const char *s_listening_url = "http://0.0.0.0:8000";
|
const char *s_listening_url = "http://0.0.0.0:8000";
|
||||||
|
|
||||||
// HTTP request handler function
|
// HTTP request handler function
|
||||||
void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_serve_opts opts = {
|
struct mg_http_serve_opts opts = {
|
||||||
.root_dir = "/web_root",
|
.root_dir = "/web_root",
|
||||||
@@ -14,7 +14,6 @@ void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
};
|
};
|
||||||
mg_http_serve_dir(c, ev_data, &opts);
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../micropython/mongoose/micropython.mk
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../micropython/mongoose/module.c
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
../../../../mongoose.c
|
||||||
@@ -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
|
||||||
@@ -5,9 +5,11 @@
|
|||||||
|
|
||||||
const char *s_listening_url = "http://0.0.0.0:80";
|
const char *s_listening_url = "http://0.0.0.0:80";
|
||||||
|
|
||||||
|
#if 0
|
||||||
char *config_read(void) {
|
char *config_read(void) {
|
||||||
return mg_file_read(&mg_fs_posix, FS_ROOT "/config.json", NULL);
|
return mg_file_read(&mg_fs_posix, FS_ROOT "/config.json", NULL);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void config_write(struct mg_str config) {
|
void config_write(struct mg_str config) {
|
||||||
mg_file_write(&mg_fs_posix, FS_ROOT "/config.json", config.ptr, config.len);
|
mg_file_write(&mg_fs_posix, FS_ROOT "/config.json", config.ptr, config.len);
|
||||||
@@ -21,14 +23,14 @@ void app_main(void) {
|
|||||||
MG_INFO(("FS at %s initialised, status: %d", conf.base_path, res));
|
MG_INFO(("FS at %s initialised, status: %d", conf.base_path, res));
|
||||||
|
|
||||||
// Try to connect to wifi by using saved WiFi credentials
|
// Try to connect to wifi by using saved WiFi credentials
|
||||||
char *json = mg_file_read(&mg_fs_posix, WIFI_FILE, NULL);
|
struct mg_str json = mg_file_read(&mg_fs_posix, WIFI_FILE);
|
||||||
if (json != NULL) {
|
if (json.ptr != NULL) {
|
||||||
char *ssid = mg_json_get_str(mg_str(json), "$.ssid");
|
char *ssid = mg_json_get_str(json, "$.ssid");
|
||||||
char *pass = mg_json_get_str(mg_str(json), "$.pass");
|
char *pass = mg_json_get_str(json, "$.pass");
|
||||||
while (!wifi_init(ssid, pass)) (void) 0;
|
while (!wifi_init(ssid, pass)) (void) 0;
|
||||||
free(ssid);
|
free(ssid);
|
||||||
free(pass);
|
free(pass);
|
||||||
free(json);
|
free((void *) json.ptr);
|
||||||
} else {
|
} else {
|
||||||
// If WiFi is not configured, run CLI until configured
|
// If WiFi is not configured, run CLI until configured
|
||||||
MG_INFO(("WiFi is not configured, running CLI. Press enter"));
|
MG_INFO(("WiFi is not configured, running CLI. Press enter"));
|
||||||
@@ -45,6 +47,6 @@ void app_main(void) {
|
|||||||
mg_mgr_init(&mgr);
|
mg_mgr_init(&mgr);
|
||||||
mg_log_set(MG_LL_DEBUG); // Set log level
|
mg_log_set(MG_LL_DEBUG); // Set log level
|
||||||
MG_INFO(("Mongoose v%s on %s", MG_VERSION, s_listening_url));
|
MG_INFO(("Mongoose v%s on %s", MG_VERSION, s_listening_url));
|
||||||
mg_http_listen(&mgr, s_listening_url, uart_bridge_fn, &mgr);
|
mg_http_listen(&mgr, s_listening_url, uart_bridge_fn, NULL);
|
||||||
for (;;) mg_mgr_poll(&mgr, 10); // Infinite event loop
|
for (;;) mg_mgr_poll(&mgr, 10); // Infinite event loop
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
#define WIFI_FILE FS_ROOT "/wifi.json"
|
#define WIFI_FILE FS_ROOT "/wifi.json"
|
||||||
#define UART_NO 1
|
#define UART_NO 1
|
||||||
|
|
||||||
void uart_bridge_fn(struct mg_connection *, int, void *, void *);
|
void uart_bridge_fn(struct mg_connection *, int, void *);
|
||||||
int uart_read(void *buf, size_t len);
|
int uart_read(void *buf, size_t len);
|
||||||
bool wifi_init(const char *ssid, const char *pass);
|
bool wifi_init(const char *ssid, const char *pass);
|
||||||
void cli(uint8_t ch);
|
void cli(uint8_t ch);
|
||||||
|
|||||||
@@ -9,8 +9,8 @@
|
|||||||
#define SERVER_URL "http://0.0.0.0:80"
|
#define SERVER_URL "http://0.0.0.0:80"
|
||||||
#define CLIENT_URL "http://info.cern.ch"
|
#define CLIENT_URL "http://info.cern.ch"
|
||||||
|
|
||||||
// Event handler for an server (accepted) connection
|
// Event handler for a server (accepted) connection
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
mg_http_reply(c, 200, "", "Hello from ESP!\n");
|
mg_http_reply(c, 200, "", "Hello from ESP!\n");
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
// To enable TLS for HTTP,
|
// To enable TLS for HTTP,
|
||||||
// 1. Copy "ca.pem" file to the ESP8266 flash FS
|
// 1. Copy "ca.pem" file to the ESP8266 flash FS
|
||||||
// 2. Add TLS init snippet for the connection, see examples/http-client
|
// 2. Add TLS init snippet for the connection, see examples/http-client
|
||||||
static void cb2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void cb2(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_CONNECT) {
|
if (ev == MG_EV_CONNECT) {
|
||||||
struct mg_str s = mg_url_host(CLIENT_URL);
|
struct mg_str s = mg_url_host(CLIENT_URL);
|
||||||
mg_printf(c, "GET / HTTP/1.0\r\nHost: %.*s\r\n\r\n", (int) s.len, s.ptr);
|
mg_printf(c, "GET / HTTP/1.0\r\nHost: %.*s\r\n\r\n", (int) s.len, s.ptr);
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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/
|
||||||
@@ -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
@@ -0,0 +1 @@
|
|||||||
|
../../mongoose.c
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../mongoose.h
|
||||||
@@ -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">😃</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
//
|
//
|
||||||
// Also, consider changing -DMG_IO_SIZE=SOME_BIG_VALUE to increase IO buffer
|
// Also, consider changing -DMG_IO_SIZE=SOME_BIG_VALUE to increase IO buffer
|
||||||
// increment when reading data.
|
// increment when reading data.
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
MG_INFO(("New request to: [%.*s], body size: %lu", (int) hm->uri.len,
|
MG_INFO(("New request to: [%.*s], body size: %lu", (int) hm->uri.len,
|
||||||
@@ -35,7 +35,6 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
mg_http_serve_dir(c, ev_data, &opts);
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|||||||
@@ -6,26 +6,16 @@
|
|||||||
// HTTP request handler function. It implements the following endpoints:
|
// HTTP request handler function. It implements the following endpoints:
|
||||||
// /upload - Saves the next file chunk
|
// /upload - Saves the next file chunk
|
||||||
// all other URI - serves web_root/ directory
|
// all other URI - serves web_root/ directory
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_http_match_uri(hm, "/upload")) {
|
if (mg_http_match_uri(hm, "/upload")) {
|
||||||
char path[80], name[64];
|
mg_http_upload(c, hm, &mg_fs_posix, "/tmp", 99999);
|
||||||
mg_http_get_var(&hm->query, "name", name, sizeof(name));
|
|
||||||
mg_snprintf(path, sizeof(path), "/tmp/%s", name);
|
|
||||||
if (name[0] == '\0') {
|
|
||||||
mg_http_reply(c, 400, "", "%s", "name required");
|
|
||||||
} else if (!mg_path_is_sane(path)) {
|
|
||||||
mg_http_reply(c, 400, "", "%s", "invalid path");
|
|
||||||
} else {
|
|
||||||
mg_http_upload(c, hm, &mg_fs_posix, path, 99999);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
struct mg_http_serve_opts opts = {.root_dir = "web_root"};
|
||||||
mg_http_serve_dir(c, ev_data, &opts);
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ var sendFileData = function(name, data, chunkSize) {
|
|||||||
var sendChunk = function(offset) {
|
var sendChunk = function(offset) {
|
||||||
var chunk = data.subarray(offset, offset + chunkSize) || '';
|
var chunk = data.subarray(offset, offset + chunkSize) || '';
|
||||||
var opts = {method: 'POST', body: chunk};
|
var opts = {method: 'POST', body: chunk};
|
||||||
var url = '/upload?offset=' + offset + '&name=' + encodeURIComponent(name);
|
var url = '/upload?offset=' + offset + '&file=' + encodeURIComponent(name);
|
||||||
var ok;
|
var ok;
|
||||||
setStatus(
|
setStatus(
|
||||||
'Uploading ' + name + ', bytes ' + offset + '..' +
|
'Uploading ' + name + ', bytes ' + offset + '..' +
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Copyright (c) 2020 Cesanta Software Limited
|
// Copyright (c) 2020-2024 Cesanta Software Limited
|
||||||
// All rights reserved
|
// All rights reserved
|
||||||
//
|
//
|
||||||
// Streaming upload example. Demonstrates how to use MG_EV_READ events
|
// Streaming upload example. Demonstrates how to use MG_EV_READ events
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
// HTTP request handler function. It implements the following endpoints:
|
// HTTP request handler function. It implements the following endpoints:
|
||||||
// /upload - Saves the next file chunk
|
// /upload - Saves the next file chunk
|
||||||
// all other URI - serves web_root/ directory
|
// all other URI - serves web_root/ directory
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_READ) {
|
if (ev == MG_EV_READ) {
|
||||||
// Parse the incoming data ourselves. If we can parse the request,
|
// Parse the incoming data ourselves. If we can parse the request,
|
||||||
// store two size_t variables in the c->data: expected len and recv len.
|
// store two size_t variables in the c->data: expected len and recv len.
|
||||||
@@ -38,7 +38,7 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) fn_data, (void) ev_data;
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
@@ -46,7 +46,7 @@ int main(void) {
|
|||||||
|
|
||||||
mg_mgr_init(&mgr);
|
mg_mgr_init(&mgr);
|
||||||
mg_log_set(MG_LL_DEBUG); // Set debug log level
|
mg_log_set(MG_LL_DEBUG); // Set debug log level
|
||||||
mg_http_listen(&mgr, "http://localhost:8000", cb, NULL);
|
mg_listen(&mgr, "http://localhost:8000", fn, NULL);
|
||||||
|
|
||||||
for (;;) mg_mgr_poll(&mgr, 50);
|
for (;;) mg_mgr_poll(&mgr, 50);
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ static const char *s_post_data = NULL; // POST data
|
|||||||
static const uint64_t s_timeout_ms = 1500; // Connect timeout in milliseconds
|
static const uint64_t s_timeout_ms = 1500; // Connect timeout in milliseconds
|
||||||
|
|
||||||
// Print HTTP response and signal that we're done
|
// Print HTTP response and signal that we're done
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_OPEN) {
|
if (ev == MG_EV_OPEN) {
|
||||||
// Connection created. Store connect expiration time in c->data
|
// Connection created. Store connect expiration time in c->data
|
||||||
*(uint64_t *) c->data = mg_millis() + s_timeout_ms;
|
*(uint64_t *) c->data = mg_millis() + s_timeout_ms;
|
||||||
@@ -50,9 +50,9 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
printf("%.*s", (int) hm->message.len, hm->message.ptr);
|
printf("%.*s", (int) hm->message.len, hm->message.ptr);
|
||||||
c->is_draining = 1; // Tell mongoose to close this connection
|
c->is_draining = 1; // Tell mongoose to close this connection
|
||||||
*(bool *) fn_data = true; // Tell event loop to stop
|
*(bool *) c->fn_data = true; // Tell event loop to stop
|
||||||
} else if (ev == MG_EV_ERROR) {
|
} else if (ev == MG_EV_ERROR) {
|
||||||
*(bool *) fn_data = true; // Error, tell event loop to stop
|
*(bool *) c->fn_data = true; // Error, tell event loop to stop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,8 +10,8 @@
|
|||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
// Print HTTP response and signal that we're done
|
// Print HTTP response and signal that we're done
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
const char *url = fn_data;
|
const char *url = c->fn_data;
|
||||||
static bool connected;
|
static bool connected;
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
|
|||||||
@@ -56,8 +56,8 @@ static const char *s_tls_key =
|
|||||||
|
|
||||||
// We use the same event handler function for HTTP and HTTPS connections
|
// We use the same event handler function for HTTP and HTTPS connections
|
||||||
// fn_data is NULL for plain HTTP, and non-NULL for HTTPS
|
// fn_data is NULL for plain HTTP, and non-NULL for HTTPS
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_ACCEPT && fn_data != NULL) {
|
if (ev == MG_EV_ACCEPT && c->fn_data != NULL) {
|
||||||
struct mg_tls_opts opts = {
|
struct mg_tls_opts opts = {
|
||||||
#ifdef TLS_TWOWAY
|
#ifdef TLS_TWOWAY
|
||||||
.ca = mg_str(s_tls_ca),
|
.ca = mg_str(s_tls_ca),
|
||||||
@@ -89,7 +89,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
mg_http_serve_dir(c, ev_data, &opts);
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ static void forward_request(struct mg_http_message *hm,
|
|||||||
(int) hm->uri.len, hm->uri.ptr));
|
(int) hm->uri.len, hm->uri.ptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn2(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
struct mg_connection *c2 = (struct mg_connection *) fn_data;
|
struct mg_connection *c2 = (struct mg_connection *) c->fn_data;
|
||||||
if (ev == MG_EV_READ) {
|
if (ev == MG_EV_READ) {
|
||||||
// All incoming data from the backend, forward to the client
|
// All incoming data from the backend, forward to the client
|
||||||
if (c2 != NULL) mg_send(c2, c->recv.buf, c->recv.len);
|
if (c2 != NULL) mg_send(c2, c->recv.buf, c->recv.len);
|
||||||
@@ -48,8 +48,8 @@ static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
struct mg_connection *c2 = fn_data;
|
struct mg_connection *c2 = c->fn_data;
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
// Client request, create backend connection Note that we're passing
|
// Client request, create backend connection Note that we're passing
|
||||||
|
|||||||
+45
-14
@@ -9,6 +9,7 @@ static const char *s_root_dir = ".";
|
|||||||
static const char *s_listening_address = "http://0.0.0.0:8000";
|
static const char *s_listening_address = "http://0.0.0.0:8000";
|
||||||
static const char *s_enable_hexdump = "no";
|
static const char *s_enable_hexdump = "no";
|
||||||
static const char *s_ssi_pattern = "#.html";
|
static const char *s_ssi_pattern = "#.html";
|
||||||
|
static const char *s_upload_dir = NULL; // File uploads disabled by default
|
||||||
|
|
||||||
// Handle interrupts, like Ctrl-C
|
// Handle interrupts, like Ctrl-C
|
||||||
static int s_signo;
|
static int s_signo;
|
||||||
@@ -18,22 +19,48 @@ static void signal_handler(int signo) {
|
|||||||
|
|
||||||
// Event handler for the listening connection.
|
// Event handler for the listening connection.
|
||||||
// Simply serve static files from `s_root_dir`
|
// Simply serve static files from `s_root_dir`
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = ev_data, tmp = {0};
|
struct mg_http_message *hm = ev_data;
|
||||||
struct mg_str unknown = mg_str_n("?", 1), *cl;
|
|
||||||
struct mg_http_serve_opts opts = {0};
|
if (mg_match(hm->uri, mg_str("/upload"), NULL)) {
|
||||||
opts.root_dir = s_root_dir;
|
// Serve file upload
|
||||||
opts.ssi_pattern = s_ssi_pattern;
|
if (s_upload_dir == NULL) {
|
||||||
mg_http_serve_dir(c, hm, &opts);
|
mg_http_reply(c, 403, "", "Denied: file upload directory not set\n");
|
||||||
mg_http_parse((char *) c->send.buf, c->send.len, &tmp);
|
} else {
|
||||||
cl = mg_http_get_header(&tmp, "Content-Length");
|
struct mg_http_part part;
|
||||||
if (cl == NULL) cl = &unknown;
|
size_t pos = 0, total_bytes = 0, num_files = 0;
|
||||||
MG_INFO(("%.*s %.*s %.*s %.*s", (int) hm->method.len, hm->method.ptr,
|
while ((pos = mg_http_next_multipart(hm->body, pos, &part)) > 0) {
|
||||||
(int) hm->uri.len, hm->uri.ptr, (int) tmp.uri.len, tmp.uri.ptr,
|
char path[MG_PATH_MAX];
|
||||||
(int) cl->len, cl->ptr));
|
MG_INFO(("Chunk name: [%.*s] filename: [%.*s] length: %lu bytes",
|
||||||
|
part.name.len, part.name.ptr, part.filename.len,
|
||||||
|
part.filename.ptr, part.body.len));
|
||||||
|
mg_snprintf(path, sizeof(path), "%s/%.*s", s_upload_dir,
|
||||||
|
part.filename.len, part.filename.ptr);
|
||||||
|
if (mg_path_is_sane(path)) {
|
||||||
|
mg_file_write(&mg_fs_posix, path, part.body.ptr, part.body.len);
|
||||||
|
total_bytes += part.body.len;
|
||||||
|
num_files++;
|
||||||
|
} else {
|
||||||
|
MG_ERROR(("Rejecting dangerous path %s", path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mg_http_reply(c, 200, "", "Uploaded %lu files, %lu bytes\n", num_files,
|
||||||
|
total_bytes);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Serve web root directory
|
||||||
|
struct mg_http_serve_opts opts = {0};
|
||||||
|
opts.root_dir = s_root_dir;
|
||||||
|
opts.ssi_pattern = s_ssi_pattern;
|
||||||
|
mg_http_serve_dir(c, hm, &opts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log request
|
||||||
|
MG_INFO(("%.*s %.*s %lu -> %.*s %lu", hm->method.len, hm->method.ptr,
|
||||||
|
hm->uri.len, hm->uri.ptr, hm->body.len, 3, c->send.buf + 9,
|
||||||
|
c->send.len));
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usage(const char *prog) {
|
static void usage(const char *prog) {
|
||||||
@@ -44,6 +71,7 @@ static void usage(const char *prog) {
|
|||||||
" -S PAT - SSI filename pattern, default: '%s'\n"
|
" -S PAT - SSI filename pattern, default: '%s'\n"
|
||||||
" -d DIR - directory to serve, default: '%s'\n"
|
" -d DIR - directory to serve, default: '%s'\n"
|
||||||
" -l ADDR - listening address, default: '%s'\n"
|
" -l ADDR - listening address, default: '%s'\n"
|
||||||
|
" -u DIR - file upload directory, default: unset\n"
|
||||||
" -v LEVEL - debug level, from 0 to 4, default: %d\n",
|
" -v LEVEL - debug level, from 0 to 4, default: %d\n",
|
||||||
MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir,
|
MG_VERSION, prog, s_enable_hexdump, s_ssi_pattern, s_root_dir,
|
||||||
s_listening_address, s_debug_level);
|
s_listening_address, s_debug_level);
|
||||||
@@ -66,6 +94,8 @@ int main(int argc, char *argv[]) {
|
|||||||
s_ssi_pattern = argv[++i];
|
s_ssi_pattern = argv[++i];
|
||||||
} else if (strcmp(argv[i], "-l") == 0) {
|
} else if (strcmp(argv[i], "-l") == 0) {
|
||||||
s_listening_address = argv[++i];
|
s_listening_address = argv[++i];
|
||||||
|
} else if (strcmp(argv[i], "-u") == 0) {
|
||||||
|
s_upload_dir = argv[++i];
|
||||||
} else if (strcmp(argv[i], "-v") == 0) {
|
} else if (strcmp(argv[i], "-v") == 0) {
|
||||||
s_debug_level = atoi(argv[++i]);
|
s_debug_level = atoi(argv[++i]);
|
||||||
} else {
|
} else {
|
||||||
@@ -96,6 +126,7 @@ int main(int argc, char *argv[]) {
|
|||||||
MG_INFO(("Mongoose version : v%s", MG_VERSION));
|
MG_INFO(("Mongoose version : v%s", MG_VERSION));
|
||||||
MG_INFO(("Listening on : %s", s_listening_address));
|
MG_INFO(("Listening on : %s", s_listening_address));
|
||||||
MG_INFO(("Web root : [%s]", s_root_dir));
|
MG_INFO(("Web root : [%s]", s_root_dir));
|
||||||
|
MG_INFO(("Upload dir : [%s]", s_upload_dir ? s_upload_dir : "unset"));
|
||||||
while (s_signo == 0) mg_mgr_poll(&mgr, 1000);
|
while (s_signo == 0) mg_mgr_poll(&mgr, 1000);
|
||||||
mg_mgr_free(&mgr);
|
mg_mgr_free(&mgr);
|
||||||
MG_INFO(("Exiting on signal %d", s_signo));
|
MG_INFO(("Exiting on signal %d", s_signo));
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
static const char *s_url = "http://info.cern.ch";
|
static const char *s_url = "http://info.cern.ch";
|
||||||
|
|
||||||
// Print HTTP response and signal that we're done
|
// Print HTTP response and signal that we're done
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_CONNECT) {
|
if (ev == MG_EV_CONNECT) {
|
||||||
// Connected to server. Extract host name from URL
|
// Connected to server. Extract host name from URL
|
||||||
struct mg_str host = mg_url_host(s_url);
|
struct mg_str host = mg_url_host(s_url);
|
||||||
@@ -27,53 +27,44 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
// Send request
|
// Send request
|
||||||
mg_printf(c,
|
mg_printf(c,
|
||||||
"GET %s HTTP/1.1\r\n"
|
"GET %s HTTP/1.1\r\n"
|
||||||
"Connection: keep-alive\r\n"
|
"Connection: close\r\n"
|
||||||
"Keep-Alive: timeout=60\r\n"
|
|
||||||
"Host: %.*s\r\n"
|
"Host: %.*s\r\n"
|
||||||
"\r\n",
|
"\r\n",
|
||||||
mg_url_uri(s_url), (int) host.len, host.ptr);
|
mg_url_uri(s_url), (int) host.len, host.ptr);
|
||||||
} else if (ev == MG_EV_READ) {
|
} else if (ev == MG_EV_READ) {
|
||||||
// Parse the incoming data ourselves. If we can parse the request,
|
// c->data[0] holds a flag, whether we have parsed the request already
|
||||||
// store two size_t variables in the c->data: expected len and recv len.
|
if (c->data[0] == 0) {
|
||||||
size_t *data = (size_t *) c->data;
|
|
||||||
if (data[0]) { // Already parsed, simply print received data
|
|
||||||
data[1] += c->recv.len;
|
|
||||||
fwrite(c->recv.buf, 1, c->recv.len, stdout);
|
|
||||||
c->recv.len = 0; // And cleanup the receive buffer. Streming!
|
|
||||||
if (data[1] >= data[0]) * (bool *) fn_data = true;
|
|
||||||
} else {
|
|
||||||
struct mg_http_message hm;
|
struct mg_http_message hm;
|
||||||
int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
|
int n = mg_http_parse((char *) c->recv.buf, c->recv.len, &hm);
|
||||||
if (n < 0) mg_error(c, "Bad response");
|
if (n < 0) mg_error(c, "Bad response");
|
||||||
if (n > 0) {
|
if (n > 0) {
|
||||||
fwrite(c->recv.buf + n, 1, c->recv.len - n, stdout); // Print body
|
fwrite(c->recv.buf + n, 1, c->recv.len - n, stdout); // Print body
|
||||||
data[0] = n + hm.body.len;
|
|
||||||
data[1] = c->recv.len;
|
|
||||||
c->recv.len = 0; // Cleanup receive buffer
|
c->recv.len = 0; // Cleanup receive buffer
|
||||||
if (data[1] >= data[0]) * (bool *) fn_data = true;
|
c->data[0] = 1; // Request parsed, set the flag
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
fwrite(c->recv.buf, 1, c->recv.len, stdout);
|
||||||
|
c->recv.len = 0; // Cleanup the receive buffer
|
||||||
}
|
}
|
||||||
} else if (ev == MG_EV_CLOSE) {
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
*(bool *) fn_data = true; // tell event loop to stop
|
*(bool *) c->fn_data = true; // Done, tell event loop to stop
|
||||||
} else if (ev == MG_EV_ERROR) {
|
} else if (ev == MG_EV_ERROR) {
|
||||||
*(bool *) fn_data = true; // Error, tell event loop to stop
|
*(bool *) c->fn_data = true; // Error, Tell event loop to stop
|
||||||
}
|
}
|
||||||
(void) ev_data;
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
struct mg_mgr mgr; // Event manager
|
struct mg_mgr mgr; // Event manager
|
||||||
bool done = false; // Event handler flips it to true
|
bool done = false; // Event handler flips it to true
|
||||||
const char *log_level = getenv("V"); // Log level
|
|
||||||
|
|
||||||
mg_mgr_init(&mgr); // Initialise event manager
|
mg_mgr_init(&mgr); // Initialise event manager
|
||||||
if (log_level == NULL) log_level = "2"; // If not set, set to DEBUG
|
mg_log_set(MG_LL_INFO); // Set to 0 to disable debug log
|
||||||
mg_log_set(atoi(log_level)); // Set to 0 to disable debug log
|
|
||||||
if (argc > 1) s_url = argv[1]; // Use URL from command line
|
|
||||||
|
|
||||||
mg_http_connect(&mgr, s_url, fn, &done); // Create client connection
|
if (argc > 1) s_url = argv[1]; // Use URL from command line
|
||||||
while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop
|
mg_connect(&mgr, s_url, fn, &done); // Create client connection
|
||||||
mg_mgr_free(&mgr); // Free resources
|
while (!done) mg_mgr_poll(&mgr, 1000); // Infinite event loop
|
||||||
|
mg_mgr_free(&mgr); // Free resources
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -41,7 +41,7 @@ static size_t printdata(mg_pfn_t out, void *ptr, va_list *ap) {
|
|||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = ev_data;
|
struct mg_http_message *hm = ev_data;
|
||||||
if (mg_http_match_uri(hm, "/api/data")) {
|
if (mg_http_match_uri(hm, "/api/data")) {
|
||||||
@@ -66,7 +66,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
mg_http_serve_dir(c, hm, &opts);
|
mg_http_serve_dir(c, hm, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_fn(void *arg) {
|
static void timer_fn(void *arg) {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ static void rpc_mul(struct mg_rpc_req *r) {
|
|||||||
// This RESTful server implements the following endpoints:
|
// This RESTful server implements the following endpoints:
|
||||||
// /websocket - upgrade to Websocket, and implement websocket echo server
|
// /websocket - upgrade to Websocket, and implement websocket echo server
|
||||||
// any other URI serves static files from s_web_root
|
// any other URI serves static files from s_web_root
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_OPEN) {
|
if (ev == MG_EV_OPEN) {
|
||||||
// c->is_hexdumping = 1;
|
// c->is_hexdumping = 1;
|
||||||
} else if (ev == MG_EV_WS_OPEN) {
|
} else if (ev == MG_EV_WS_OPEN) {
|
||||||
@@ -51,7 +51,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
if (io.buf) mg_ws_send(c, (char *) io.buf, io.len, WEBSOCKET_OP_TEXT);
|
if (io.buf) mg_ws_send(c, (char *) io.buf, io.len, WEBSOCKET_OP_TEXT);
|
||||||
mg_iobuf_free(&io);
|
mg_iobuf_free(&io);
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void timer_fn(void *arg) {
|
static void timer_fn(void *arg) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
// /api/log/static - returns contents of log.txt file
|
// /api/log/static - returns contents of log.txt file
|
||||||
// /api/log/live - hangs forever, and returns live log messages
|
// /api/log/live - hangs forever, and returns live log messages
|
||||||
// all other URI - serves web_root/ directory
|
// all other URI - serves web_root/ directory
|
||||||
static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void cb(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_http_match_uri(hm, "/api/log/static")) {
|
if (mg_http_match_uri(hm, "/api/log/static")) {
|
||||||
@@ -21,7 +21,6 @@ static void cb(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
mg_http_serve_dir(c, ev_data, &opts);
|
mg_http_serve_dir(c, ev_data, &opts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void log_message(const char *filename, const char *message) {
|
static void log_message(const char *filename, const char *message) {
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ void mg_random(void *buf, size_t len) { // Use on-board RNG
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_OPEN) {
|
if (ev == MG_EV_OPEN) {
|
||||||
MG_INFO(("%lu CREATED", c->id));
|
MG_INFO(("%lu CREATED", c->id));
|
||||||
// c->is_hexdumping = 1;
|
// c->is_hexdumping = 1;
|
||||||
@@ -66,7 +66,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
MG_INFO(("%lu CLOSED", c->id));
|
MG_INFO(("%lu CLOSED", c->id));
|
||||||
s_conn = NULL; // Mark that we're closed
|
s_conn = NULL; // Mark that we're closed
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timer function - recreate client connection if it is closed
|
// Timer function - recreate client connection if it is closed
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import mongoose
|
||||||
|
|
||||||
|
mongoose.init()
|
||||||
|
while True:
|
||||||
|
mongoose.poll(1000)
|
||||||
@@ -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)
|
||||||
@@ -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);
|
||||||
File renamed without changes.
File renamed without changes.
@@ -42,20 +42,20 @@ static size_t pcap_rx(void *buf, size_t len, struct mg_tcpip_if *ifp) {
|
|||||||
return received;
|
return received;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn2(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn2(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
MG_DEBUG(("Got response (%d) %.*s...", (int) hm->message.len, 12,
|
MG_DEBUG(("Got response (%d) %.*s...", (int) hm->message.len, 12,
|
||||||
hm->message.ptr));
|
hm->message.ptr));
|
||||||
c->is_draining = 1;
|
c->is_draining = 1;
|
||||||
} else if (ev == MG_EV_CONNECT) {
|
} else if (ev == MG_EV_CONNECT) {
|
||||||
mg_printf(c, "GET %s HTTP/1.1\r\n\r\n", mg_url_uri((char *) fn_data));
|
mg_printf(c, "GET %s HTTP/1.1\r\n\r\n", mg_url_uri((char *) c->fn_data));
|
||||||
} else if (ev == MG_EV_CLOSE) {
|
} else if (ev == MG_EV_CLOSE) {
|
||||||
free(fn_data);
|
free(c->fn_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_HTTP_MSG) {
|
if (ev == MG_EV_HTTP_MSG) {
|
||||||
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
struct mg_http_message *hm = (struct mg_http_message *) ev_data;
|
||||||
if (mg_http_match_uri(hm, "/api/debug")) {
|
if (mg_http_match_uri(hm, "/api/debug")) {
|
||||||
@@ -75,7 +75,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
hm->message.ptr);
|
hm->message.ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(void) ev_data, (void) fn_data;
|
(void) ev_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Main dashboard
|
||||||
|
|
||||||
|
The main dashboard page shows the interactive console
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
|
||||||
|
See a detailed tutorial at https://mongoose.ws/tutorials/device-dashboard/
|
||||||
Symlink
+1
@@ -0,0 +1 @@
|
|||||||
|
../device-dashboard/certs
|
||||||
@@ -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
@@ -0,0 +1 @@
|
|||||||
|
../../mongoose.c
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
../../mongoose.h
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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);
|
||||||
@@ -38,7 +38,7 @@ static int s_qos = 1;
|
|||||||
|
|
||||||
#include "mongoose.h"
|
#include "mongoose.h"
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_OPEN) {
|
if (ev == MG_EV_OPEN) {
|
||||||
// c->is_hexdumping = 1;
|
// c->is_hexdumping = 1;
|
||||||
} else if (ev == MG_EV_CONNECT) {
|
} else if (ev == MG_EV_CONNECT) {
|
||||||
@@ -84,7 +84,7 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
|
|
||||||
if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE) {
|
if (ev == MG_EV_ERROR || ev == MG_EV_CLOSE) {
|
||||||
MG_INFO(("Got event %d, stopping...", ev));
|
MG_INFO(("Got event %d, stopping...", ev));
|
||||||
*(bool *) fn_data = true; // Signal that we're done
|
*(bool *) c->fn_data = true; // Signal that we're done
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ static void signal_handler(int signo) {
|
|||||||
s_signo = signo;
|
s_signo = signo;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
static void fn(struct mg_connection *c, int ev, void *ev_data) {
|
||||||
if (ev == MG_EV_OPEN) {
|
if (ev == MG_EV_OPEN) {
|
||||||
MG_INFO(("%lu CREATED", c->id));
|
MG_INFO(("%lu CREATED", c->id));
|
||||||
// c->is_hexdumping = 1;
|
// c->is_hexdumping = 1;
|
||||||
@@ -65,7 +65,6 @@ static void fn(struct mg_connection *c, int ev, void *ev_data, void *fn_data) {
|
|||||||
MG_INFO(("%lu CLOSED", c->id));
|
MG_INFO(("%lu CLOSED", c->id));
|
||||||
s_conn = NULL; // Mark that we're closed
|
s_conn = NULL; // Mark that we're closed
|
||||||
}
|
}
|
||||||
(void) fn_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timer function - recreate client connection if it is closed
|
// Timer function - recreate client connection if it is closed
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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.
@@ -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;
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "mongoose.h"
|
||||||
|
|
||||||
|
bool gpio_write(uint16_t pin, bool status);
|
||||||
|
bool gpio_read(uint16_t pin);
|
||||||
@@ -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
Reference in new issue
Block a user