Made a little more progress in the zephyr-siot project.
The code like the following is used in the Zephyr C code to serve various status endpoints:
// ********************************
// CPU usage handler
static int cpu_usage_handler(struct http_client_ctx *client, enum http_data_status status,
uint8_t *buffer, size_t len, void *user_data)
{
char *end = recv_buffer;
static bool processed = false;
int rc = 0;
k_thread_runtime_stats_t stats;
rc = k_thread_runtime_stats_all_get(&stats);
if (rc == 0) { /* item was found, show it */
sprintf(recv_buffer, "%0.2lf%%",
((double)stats.total_cycles) * 100 / stats.execution_cycles);
} else {
strcpy(recv_buffer, "error");
}
if (processed) {
processed = false;
return 0;
}
processed = true;
return strlen(recv_buffer);
}
struct http_resource_detail_dynamic cpu_usage_resource_detail = {
.common =
{
.type = HTTP_RESOURCE_TYPE_DYNAMIC,
.bitmask_of_supported_http_methods = BIT(HTTP_GET) | BIT(HTTP_POST),
},
.cb = cpu_usage_handler,
.data_buffer = recv_buffer,
.data_buffer_len = sizeof(recv_buffer),
.user_data = NULL,
};
HTTP_RESOURCE_DEFINE(cpu_usage_resource, siot_http_service, "/cpu-usage",
&cpu_usage_resource_detail);
And then the following HTML snippets load data from the endpoints and display it:
<li>Board: <span hx-get="/board" hx-trigger="load" />
<li>Boot count: <span hx-get="/bootcount" hx-trigger="load" />
<li>CPU Usage: <span hx-get="/cpu-usage" hx-trigger="every 2s" />
The entire HTML file is very simple:
<!DOCTYPE html>
<html>
<head>
<title>Simple IoT Zephyr</title>
<script src="https://unpkg.com/htmx.org@2.0.2"
integrity="sha384-Y7hw+L/jvKeWIRRkqWYfPcvVxHzVzn5REgzbawhxAuQGwX1XWe70vji+VSeHOThJ"
crossorigin="anonymous"></script>
<link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css" />
<style>
.on {
display: inline-block;
width: 15px;
height: 15px;
background-color: #0eff00;
border-radius: 50%;
}
.off {
display: inline-block;
width: 15px;
height: 15px;
background-color: lightgrey;
border-radius: 50%;
}
</style>
</head>
<body>
<h1>SIOT Zephyr</h1>
<h2>Status</h2>
<ul>
<li>Board: <span hx-get="/board" hx-trigger="load" />
<li>Boot count: <span hx-get="/bootcount" hx-trigger="load" />
<li>CPU Usage: <span hx-get="/cpu-usage" hx-trigger="every 2s" />
</ul>
<hr />
<h2>Settings</h2>
<form action="/" method="POST">
<div>
<label for="fid">Device ID:</label>
<input type="text" id="fid" name="fid"><br>
</div>
<div>
<input type="submit" value="Save">
</div>
</form>
<hr />
</body>
</html>
So, I’m liking it for this kind of stuff. It is extremely simple and effective.
The nice thing about htmx is I don’t have to render templates on the backend or do full page reloads – just fetch one page, and then any dynamic data at load time using htmx. It is not as efficient, but with 6ms load times for an htmx transaction, we don’t really care.
And for small bits of data, templates are not needed on the backend – sprintf
works just fine.