diff options
author | xengineering <me@xengineering.eu> | 2025-03-21 22:39:30 +0100 |
---|---|---|
committer | xengineering <me@xengineering.eu> | 2025-03-21 22:39:30 +0100 |
commit | 2ddf4682f4c11f4356b030b6474fb12fea55b8ea (patch) | |
tree | d71f952f46808d725da6f0e84dac765385df2ca1 | |
parent | e042129eddeb06c9272a6544c67f9222c347ab10 (diff) | |
download | iot-contact-2ddf4682f4c11f4356b030b6474fb12fea55b8ea.tar iot-contact-2ddf4682f4c11f4356b030b6474fb12fea55b8ea.tar.zst iot-contact-2ddf4682f4c11f4356b030b6474fb12fea55b8ea.zip |
fw: js: Add web frontend to display heartbeat
This makes it transparent to the user that there is an active connection
to the firmware. If the connection is broken the user notices that
quickly and can re-load the page.
-rw-r--r-- | fw/CMakeLists.txt | 7 | ||||
-rw-r--r-- | fw/src/http.c | 16 | ||||
-rw-r--r-- | fw/src/index.html | 5 | ||||
-rw-r--r-- | fw/src/iot-contact.js | 48 |
4 files changed, 76 insertions, 0 deletions
diff --git a/fw/CMakeLists.txt b/fw/CMakeLists.txt index 5aaf319..5a8d6c4 100644 --- a/fw/CMakeLists.txt +++ b/fw/CMakeLists.txt @@ -39,3 +39,10 @@ generate_inc_file_for_target( ${ZEPHYR_BINARY_DIR}/include/generated/index.html.gz.inc --gzip ) + +generate_inc_file_for_target( + app + src/iot-contact.js + ${ZEPHYR_BINARY_DIR}/include/generated/iot-contact.js.gz.inc + --gzip +) diff --git a/fw/src/http.c b/fw/src/http.c index b9a76ba..e206f86 100644 --- a/fw/src/http.c +++ b/fw/src/http.c @@ -34,6 +34,21 @@ struct http_resource_detail_static index_resource_detail = { .static_data_len = sizeof(index_html_gz), }; +static const uint8_t js_html_gz[] = { + #include "iot-contact.js.gz.inc" +}; + +struct http_resource_detail_static js_resource_detail = { + .common = { + .type = HTTP_RESOURCE_TYPE_STATIC, + .bitmask_of_supported_http_methods = BIT(HTTP_GET), + .content_encoding = "gzip", + .content_type = "text/javascript", + }, + .static_data = js_html_gz, + .static_data_len = sizeof(js_html_gz), +}; + static int favicon_handler( struct http_client_ctx *client, enum http_data_status status, @@ -80,6 +95,7 @@ HTTP_SERVICE_DEFINE(http_service, NULL, &http_port, 1, 10, NULL, NULL); HTTP_RESOURCE_DEFINE(index_resource, http_service, "/", &index_resource_detail); HTTP_RESOURCE_DEFINE(websocket_resource, http_service, "/", &websocket_resource_detail); HTTP_RESOURCE_DEFINE(favicon_resource, http_service, "/favicon.ico", &favicon_resource_detail); +HTTP_RESOURCE_DEFINE(js_resource, http_service, "/iot-contact.js", &js_resource_detail); int init_http_server(void) { LOG_DBG("Starting HTTP server"); diff --git a/fw/src/index.html b/fw/src/index.html index 142a1b9..5817818 100644 --- a/fw/src/index.html +++ b/fw/src/index.html @@ -9,8 +9,13 @@ <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>iot-contact</title> + <script type="text/javascript" src="/iot-contact.js"></script> </head> <body> <h4>iot-contact</h4> + <p> + <label for="heartbeat">Heartbeat</label> + <meter id="heartbeat" min="0" max="1" value="0"></meter> + </p> </body> </html> diff --git a/fw/src/iot-contact.js b/fw/src/iot-contact.js new file mode 100644 index 0000000..e8e966f --- /dev/null +++ b/fw/src/iot-contact.js @@ -0,0 +1,48 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public License, + * v. 2.0. If a copy of the MPL was not distributed with this file, You can + * obtain one at https://mozilla.org/MPL/2.0/. + */ + +function blinkHeartbeat() { + const heartbeat = document.getElementById("heartbeat"); + heartbeat.value = 1; + setTimeout(() => heartbeat.value = 0, 300); +} + +window.addEventListener("DOMContentLoaded", (ev) => { + const ws = new WebSocket("/"); + console.log("WebSocket object created"); + + ws.onopen = (event) => { + console.log("WebSocket connection opened"); + }; + + ws.onmessage = (event) => { + try { + const data = JSON.parse(event.data); + console.log("Message received: ", data); + if (data.type === 0 && data.ttl_ms) { + blinkHeartbeat(); + } + } catch (error) { + console.error("Invalid message received:", event.data); + } + }; + + ws.onclose = (event) => { + console.log("WebSocket connection closed"); + }; + + ws.onerror = (event) => { + console.log("WebSocket error: ", event); + }; + + setInterval(() => { + if (ws.readyState === WebSocket.OPEN) { + const data = "{\"type\":0,\"ttl_ms\":1100}\n"; + ws.send(data); + console.log("Heartbeat sent: '", data, "'"); + } + }, 1000); +}) |