From 1ad7313a9f2f0ff238c9d21f9fa147b0864244c5 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sun, 27 Feb 2022 14:19:13 +0100 Subject: [PATCH] Rainbow: LEDs have optional owners --- rainbow/daemon/burrow-rainbowd.c | 180 ++++++++++++++++++++++++------- 1 file changed, 139 insertions(+), 41 deletions(-) diff --git a/rainbow/daemon/burrow-rainbowd.c b/rainbow/daemon/burrow-rainbowd.c index 95cbfe1..7080076 100644 --- a/rainbow/daemon/burrow-rainbowd.c +++ b/rainbow/daemon/burrow-rainbowd.c @@ -5,6 +5,7 @@ */ #include +#include #include #include #include @@ -31,17 +32,64 @@ typedef unsigned int uint; #include "../firmware/interface.h" +/*** LED status ***/ + +struct client { + cnode n; + char *name; + int status; // 0=unknown, 1=running, -1=dead +}; + +static clist clients; +static struct client *null_client; + static mtx_t led_mutex; static cnd_t led_cond; static bool led_refresh; struct led { - double r, g, b; + double r, g, b; + struct client *sender; }; static struct led leds[NPIX_NUM_LEDS]; static double led_brightness = 1; +static struct client *find_client(const char *name) +{ + CLIST_FOR_EACH(struct client *, c, clients) + if (!strcmp(c->name, name)) + return c; + + struct client *c = xmalloc_zero(sizeof(*c)); + clist_add_tail(&clients, &c->n); + c->name = xstrdup(name); + return c; +} + +static void led_init(void) { + clist_init(&clients); + null_client = find_client(""); + + for (uint i=0; i < NPIX_NUM_LEDS; i++) + leds[i].sender = null_client; + + mtx_init(&led_mutex, mtx_plain); + cnd_init(&led_cond); +} + +static void led_begin_update(void) +{ + mtx_lock(&led_mutex); +} + +static void led_end_update(void) +{ + led_refresh = 1; + cnd_broadcast(&led_cond); + mtx_unlock(&led_mutex); +} + /*** MQTT ***/ static struct mosquitto *mosq; @@ -68,9 +116,11 @@ static void mqtt_conn_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, if (!status) { msg(L_DEBUG, "MQTT: Connection established"); mqtt_connected = true; - mqtt_publish("status/rainbow", "ok"); if (mosquitto_subscribe(mosq, NULL, "burrow/lights/rainbow/#", 1) != MOSQ_ERR_SUCCESS) die("Mosquitto: subscribe failed"); + if (mosquitto_subscribe(mosq, NULL, "status/#", 1) != MOSQ_ERR_SUCCESS) + die("Mosquitto: subscribe failed"); + mqtt_publish("status/rainbow", "ok"); } else if (mqtt_connected) { msg(L_DEBUG, "MQTT: Connection lost"); mqtt_connected = false; @@ -82,55 +132,102 @@ static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, i msg(L_DEBUG, "MQTT(%d): %s", level, message); } -static void mqtt_msg_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, const struct mosquitto_message *m) +static void msg_status(const char *topic, const char *key, const char *val) { - char val[256]; - if (m->payloadlen >= sizeof(val) - 1) { - msg(L_ERROR, "Invalid value for topic %s", m->topic); + if (!*key) { + msg(L_ERROR, "Unknown status topic %s", topic); return; } - memcpy(val, m->payload, m->payloadlen); - val[m->payloadlen] = 0; - msg(L_DEBUG, "MQTT < %s %s", m->topic, val); - static const char px[] = "burrow/lights/rainbow/"; - if (!str_has_prefix(m->topic, px)) - return; - - const char *top = m->topic + strlen(px); - if (!strcmp(top, "brightness")) { - double b = atof(val); - if (!(b >= 0 && b <= 1)) { - msg(L_ERROR, "Invalid value of %s: %s", m->topic, val); - return; + int status; + if (!*val) + status = 0; + else if (!strcmp(val, "ok")) + status = 1; + else + status = -1; + + led_begin_update(); + struct client *c = find_client(key); + c->status = status; + if (status < 0) { + for (uint i=0; i < NPIX_NUM_LEDS; i++) { + struct led *led = &leds[i]; + if (led->sender == c) { + led->r = led->g = led->b = 0; + led->sender = null_client; + } } - mtx_lock(&led_mutex); - led_brightness = b; - led_refresh = 1; - cnd_broadcast(&led_cond); - mtx_unlock(&led_mutex); + } + led_end_update(); +} + +static void msg_brightness(const char *topic, const char *val) +{ + double b = atof(val); + if (!(b >= 0 && b <= 1)) { + msg(L_ERROR, "Invalid value of %s: %s", topic, val); return; } + led_begin_update(); + led_brightness = b; + led_end_update(); +} + +static void msg_rainbow(const char *topic, const char *key, const char *val) +{ + if (!strcmp(key, "brightness")) + return msg_brightness(topic, val); + uint index; - if (str_to_uint(&index, m->topic + strlen(px), NULL, 10 | STN_WHOLE) || index >= NPIX_NUM_LEDS) { - msg(L_ERROR, "Unsupported topic: %s", m->topic); + if (str_to_uint(&index, key, NULL, 10 | STN_WHOLE) || index >= NPIX_NUM_LEDS) { + msg(L_ERROR, "Unknown topic: %s", topic); return; } double r, g, b; - if (sscanf(val, "%lf%lf%lf", &r, &g, &b) != 3 || !(r >= 0 && r <= 1) || !(g >= 0 && g <= 1) || !(b >= 0 && b <= 1)) { - msg(L_ERROR, "Invalid value of %s: %s", m->topic, val); + char sender[strlen(val) + 1]; + sender[0] = 0; + + if (!*val) { + r = g = b = 0; + } else if (sscanf(val, "%lf%lf%lf %s", &r, &g, &b, sender) < 3 || !(r >= 0 && r <= 1) || !(g >= 0 && g <= 1) || !(b >= 0 && b <= 1)) { + msg(L_ERROR, "Invalid value of %s: %s", topic, val); return; } - mtx_lock(&led_mutex); - leds[index].r = r; - leds[index].g = g; - leds[index].b = b; - led_refresh = 1; - cnd_broadcast(&led_cond); - mtx_unlock(&led_mutex); + led_begin_update(); + struct led *led = &leds[index]; + struct client *client = find_client(sender); + if (client->status < 0) { + msg(L_ERROR, "LED update from a dead client %s", client->name); + } else { + led->r = r; + led->g = g; + led->b = b; + led->sender = client; + } + led_end_update(); +} + +static void mqtt_msg_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, const struct mosquitto_message *m) +{ + char val[256]; + if (m->payloadlen >= sizeof(val) - 1) { + msg(L_ERROR, "Invalid value for topic %s", m->topic); + return; + } + memcpy(val, m->payload, m->payloadlen); + val[m->payloadlen] = 0; + msg(L_DEBUG, "MQTT < %s %s", m->topic, val); + + static const char px_status[] = "status/"; + static const char px_rainbow[] = "burrow/lights/rainbow/"; + if (str_has_prefix(m->topic, px_status)) + msg_status(m->topic, m->topic + strlen(px_status), val); + else if (str_has_prefix(m->topic, px_rainbow)) + msg_rainbow(m->topic, m->topic + strlen(px_rainbow), val); } static void mqtt_init(void) @@ -228,9 +325,12 @@ static int npix_build_packet(void) byte *pkt = npix_packet; for (int i=0; i < NPIX_NUM_LEDS; i++) { - *pkt++ = (int)(leds[i].r * led_brightness * 255); - *pkt++ = (int)(leds[i].g * led_brightness * 255); - *pkt++ = (int)(leds[i].b * led_brightness * 255); + struct led *led = &leds[i]; + struct client *sender = led->sender; + msg(L_DEBUG, "LED #%d: r=%.3f g=%.3f b=%.3f client=%s status=%d", i, led->r, led->g, led->b, sender->name, sender->status); + *pkt++ = (int)(led->r * led_brightness * 255); + *pkt++ = (int)(led->g * led_brightness * 255); + *pkt++ = (int)(led->b * led_brightness * 255); } return pkt - npix_packet; @@ -266,9 +366,7 @@ int main(int argc UNUSED, char **argv) if (!use_debug) log_default_stream()->levels &= ~(1U << L_DEBUG); - mtx_init(&led_mutex, mtx_plain); - cnd_init(&led_cond); - + led_init(); mqtt_init(); init_usb(); -- 2.39.2