*/
#include <ucw/lib.h>
+#include <ucw/clists.h>
#include <ucw/log.h>
#include <ucw/opt.h>
#include <ucw/string.h>
#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;
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;
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)
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;
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();