summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorxengineering <me@xengineering.eu>2025-03-21 22:39:30 +0100
committerxengineering <me@xengineering.eu>2025-03-21 22:39:30 +0100
commit2ddf4682f4c11f4356b030b6474fb12fea55b8ea (patch)
treed71f952f46808d725da6f0e84dac765385df2ca1
parente042129eddeb06c9272a6544c67f9222c347ab10 (diff)
downloadiot-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.txt7
-rw-r--r--fw/src/http.c16
-rw-r--r--fw/src/index.html5
-rw-r--r--fw/src/iot-contact.js48
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);
+})