+++ /dev/null
-/*
- * Daemon for X-mas Led Strip
- *
- * (c) 2025 Martin Mares <mj@ucw.cz>
- */
-
-#include <ucw/lib.h>
-#include <ucw/log.h>
-#include <ucw/string.h>
-#include <ucw/unaligned.h>
-
-#include <math.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <syslog.h>
-#include <threads.h>
-#include <time.h>
-#include <unistd.h>
-
-#include <libusb.h>
-#include <mosquitto.h>
-
-typedef unsigned char byte;
-typedef uint32_t u32;
-typedef unsigned int uint;
-
-#include "../firmware/interface.h"
-
-static mtx_t light_mutex;
-static cnd_t light_cond;
-static byte lights[NPIX_NUM_LEDS][3];
-static bool light_refresh;
-
-/*** MQTT ***/
-
-static struct mosquitto *mosq;
-static bool mqtt_connected;
-
-static void mqtt_publish(const char *topic, const char *fmt, ...)
-{
- va_list args;
- va_start(args, fmt);
-
- if (mqtt_connected) {
- char m[256];
- int l = vsnprintf(m, sizeof(m), fmt, args);
- int err = mosquitto_publish(mosq, NULL, topic, l, m, 0, true);
- if (err != MOSQ_ERR_SUCCESS)
- msg(L_ERROR, "Mosquitto: Publish failed, error=%d", err);
- }
-
- va_end(args);
-}
-
-static void mqtt_conn_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int status)
-{
- if (!status) {
- msg(L_DEBUG, "MQTT: Connection established");
- mqtt_connected = true;
- mqtt_publish("status/led-strip", "ok");
- if (mosquitto_subscribe(mosq, NULL, "ksp/led-strip", 1) != MOSQ_ERR_SUCCESS)
- die("Mosquitto: subscribe failed");
- } else if (mqtt_connected) {
- msg(L_DEBUG, "MQTT: Connection lost");
- mqtt_connected = false;
- }
-}
-
-static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int level, const char *message)
-{
- 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)
-{
- char val[4096];
- 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);
-
- if (strcmp(m->topic, "ksp/led-strip"))
- return;
-
- mtx_lock(&light_mutex);
- uint i = 0;
- char *x = val;
- while (*x) {
- if (*x == ' ')
- x++;
- else {
- char *end;
- unsigned long val = strtoul(x, &end, 16);
- if (end == x)
- break;
- if (i >= NPIX_NUM_LEDS)
- break;
- lights[i][0] = (val >> 16) & 0xff;
- lights[i][1] = (val >> 8) & 0xff;
- lights[i][2] = (val >> 0) & 0xff;
- i++;
- x = end;
- }
- }
- if (i == 0) {
- for (uint i=0; i<NPIX_NUM_LEDS; i++)
- lights[i][0] = lights[i][1] = lights[i][2] = 7;
- } else {
- uint m = i;
- while (i < NPIX_NUM_LEDS) {
- lights[i][0] = lights[i % m][0];
- lights[i][1] = lights[i % m][1];
- lights[i][2] = lights[i % m][2];
- i++;
- }
- }
- light_refresh = 1;
- cnd_broadcast(&light_cond);
- mtx_unlock(&light_mutex);
-}
-
-static void mqtt_init(void)
-{
- mosquitto_lib_init();
-
- mosq = mosquitto_new("led-strip", 1, NULL);
- if (!mosq)
- die("Mosquitto: Initialization failed");
-
- mosquitto_connect_callback_set(mosq, mqtt_conn_callback);
- mosquitto_log_callback_set(mosq, mqtt_log_callback);
- mosquitto_message_callback_set(mosq, mqtt_msg_callback);
-
- if (mosquitto_will_set(mosq, "status/led-strip", 4, "dead", 0, true) != MOSQ_ERR_SUCCESS)
- die("Mosquitto: Unable to set will");
-
- if (mosquitto_connect_async(mosq, "192.168.42.2", 1883, 60) != MOSQ_ERR_SUCCESS)
- die("Mosquitto: Unable to connect");
-
- if (mosquitto_loop_start(mosq))
- die("Mosquitto: Cannot start service thread");
-}
-
-/*** USB ***/
-
-static struct libusb_context *usb_ctxt;
-static struct libusb_device_handle *devh;
-
-static void usb_error(const char *msg, ...)
-{
- va_list args;
- va_start(args, msg);
- ucw_vmsg(L_ERROR, msg, args);
- va_end(args);
-
- if (devh) {
- libusb_close(devh);
- devh = NULL;
- }
-}
-
-static void open_device(void)
-{
- int err;
- libusb_device **devlist;
- ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist);
- if (devn < 0)
- die("Cannot enumerate USB devices: error %d", (int) devn);
-
- for (ssize_t i=0; i<devn; i++) {
- struct libusb_device_descriptor desc;
- libusb_device *dev = devlist[i];
- if (!libusb_get_device_descriptor(dev, &desc)) {
- if (desc.idVendor == NPIX_USB_VENDOR && desc.idProduct == NPIX_USB_PRODUCT) {
- msg(L_INFO, "Found LED strip device at usb%d.%d", libusb_get_bus_number(dev), libusb_get_device_address(dev));
-
- if (err = libusb_open(dev, &devh)) {
- usb_error("Cannot open device: error %d", err);
- goto out;
- }
- libusb_reset_device(devh);
- if (err = libusb_claim_interface(devh, 0)) {
- usb_error("Cannot claim interface: error %d", err);
- goto out;
- }
-
- goto out;
- }
- }
- }
-
-out:
- libusb_free_device_list(devlist, 1);
-}
-
-static void init_usb(void)
-{
- int err;
- if (err = libusb_init(&usb_ctxt))
- die("Cannot initialize libusb: error %d", err);
- // libusb_set_debug(usb_ctxt, 3);
- open_device();
-}
-
-/*** Main ***/
-
-int main(int argc UNUSED, char **argv)
-{
- log_init(argv[0]);
- log_default_stream()->levels &= ~(1U << L_DEBUG);
-
- mtx_init(&light_mutex, mtx_plain);
- cnd_init(&light_cond);
-
- mqtt_init();
- init_usb();
-
- bool need_resend = true;
- for (;;) {
- if (!devh) {
- msg(L_INFO, "Waiting for device to appear...");
- while (!devh) {
- sleep(5);
- open_device();
- }
- need_resend = true;
- }
-
- mtx_lock(&light_mutex);
- while (!need_resend && !light_refresh)
- cnd_wait(&light_cond, &light_mutex);
- light_refresh = 0;
- need_resend = 0;
- mtx_unlock(&light_mutex);
-
- msg(L_DEBUG, "Sending USB packet");
-
- uint batch = 63/3;
- for (uint i=0; i < NPIX_NUM_LEDS; i += batch) {
- byte packet[64];
- uint n = NPIX_NUM_LEDS - i;
- if (n > batch)
- n = batch;
- packet[0] = i;
- mtx_lock(&light_mutex);
- for (uint j=0; j<n; j++) {
- // Packet format is GRB
- packet[1 + 3*j + 0] = lights[i+j][1];
- packet[1 + 3*j + 1] = lights[i+j][0];
- packet[1 + 3*j + 2] = lights[i+j][2];
- }
- mtx_unlock(&light_mutex);
-
- int len = 1 + 3*n;
- int err, transferred;
- if (err = libusb_bulk_transfer(devh, 0x01, packet, len, &transferred, 1000)) {
- usb_error("USB transfer failed: error %d", err);
- break;
- } else if (transferred != len)
- usb_error("USB short transfer: %d out of %d bytes", transferred, len);
- }
- }
-}
--- /dev/null
+/*
+ * Daemon for X-mas Led Strip
+ *
+ * (c) 2025 Martin Mares <mj@ucw.cz>
+ */
+
+#include <ucw/lib.h>
+#include <ucw/log.h>
+#include <ucw/string.h>
+#include <ucw/unaligned.h>
+
+#include <math.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <threads.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <libusb.h>
+#include <mosquitto.h>
+
+typedef unsigned char byte;
+typedef uint32_t u32;
+typedef unsigned int uint;
+
+#include "../firmware/interface.h"
+
+static mtx_t light_mutex;
+static cnd_t light_cond;
+static byte lights[NPIX_NUM_LEDS][3];
+static bool light_refresh;
+
+/*** MQTT ***/
+
+#if 0
+
+static struct mosquitto *mosq;
+static bool mqtt_connected;
+
+static void mqtt_publish(const char *topic, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ if (mqtt_connected) {
+ char m[256];
+ int l = vsnprintf(m, sizeof(m), fmt, args);
+ int err = mosquitto_publish(mosq, NULL, topic, l, m, 0, true);
+ if (err != MOSQ_ERR_SUCCESS)
+ msg(L_ERROR, "Mosquitto: Publish failed, error=%d", err);
+ }
+
+ va_end(args);
+}
+
+static void mqtt_conn_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int status)
+{
+ if (!status) {
+ msg(L_DEBUG, "MQTT: Connection established");
+ mqtt_connected = true;
+ mqtt_publish("status/led-strip", "ok");
+ if (mosquitto_subscribe(mosq, NULL, "ksp/led-strip", 1) != MOSQ_ERR_SUCCESS)
+ die("Mosquitto: subscribe failed");
+ } else if (mqtt_connected) {
+ msg(L_DEBUG, "MQTT: Connection lost");
+ mqtt_connected = false;
+ }
+}
+
+static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int level, const char *message)
+{
+ 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)
+{
+ char val[4096];
+ 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);
+
+ if (strcmp(m->topic, "ksp/led-strip"))
+ return;
+
+ mtx_lock(&light_mutex);
+ uint i = 0;
+ char *x = val;
+ while (*x) {
+ if (*x == ' ')
+ x++;
+ else {
+ char *end;
+ unsigned long val = strtoul(x, &end, 16);
+ if (end == x)
+ break;
+ if (i >= NPIX_NUM_LEDS)
+ break;
+ lights[i][0] = (val >> 16) & 0xff;
+ lights[i][1] = (val >> 8) & 0xff;
+ lights[i][2] = (val >> 0) & 0xff;
+ i++;
+ x = end;
+ }
+ }
+ if (i == 0) {
+ for (uint i=0; i<NPIX_NUM_LEDS; i++)
+ lights[i][0] = lights[i][1] = lights[i][2] = 7;
+ } else {
+ uint m = i;
+ while (i < NPIX_NUM_LEDS) {
+ lights[i][0] = lights[i % m][0];
+ lights[i][1] = lights[i % m][1];
+ lights[i][2] = lights[i % m][2];
+ i++;
+ }
+ }
+ light_refresh = 1;
+ cnd_broadcast(&light_cond);
+ mtx_unlock(&light_mutex);
+}
+
+static void mqtt_init(void)
+{
+ mosquitto_lib_init();
+
+ mosq = mosquitto_new("led-strip", 1, NULL);
+ if (!mosq)
+ die("Mosquitto: Initialization failed");
+
+ mosquitto_connect_callback_set(mosq, mqtt_conn_callback);
+ mosquitto_log_callback_set(mosq, mqtt_log_callback);
+ mosquitto_message_callback_set(mosq, mqtt_msg_callback);
+
+ if (mosquitto_will_set(mosq, "status/led-strip", 4, "dead", 0, true) != MOSQ_ERR_SUCCESS)
+ die("Mosquitto: Unable to set will");
+
+ if (mosquitto_connect_async(mosq, "192.168.42.2", 1883, 60) != MOSQ_ERR_SUCCESS)
+ die("Mosquitto: Unable to connect");
+
+ if (mosquitto_loop_start(mosq))
+ die("Mosquitto: Cannot start service thread");
+}
+
+#endif
+
+/*** USB ***/
+
+static struct libusb_context *usb_ctxt;
+static struct libusb_device_handle *devh;
+
+static void usb_error(const char *msg, ...)
+{
+ va_list args;
+ va_start(args, msg);
+ ucw_vmsg(L_ERROR, msg, args);
+ va_end(args);
+
+ if (devh) {
+ libusb_close(devh);
+ devh = NULL;
+ }
+}
+
+static void open_device(void)
+{
+ int err;
+ libusb_device **devlist;
+ ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist);
+ if (devn < 0)
+ die("Cannot enumerate USB devices: error %d", (int) devn);
+
+ for (ssize_t i=0; i<devn; i++) {
+ struct libusb_device_descriptor desc;
+ libusb_device *dev = devlist[i];
+ if (!libusb_get_device_descriptor(dev, &desc)) {
+ if (desc.idVendor == NPIX_USB_VENDOR && desc.idProduct == NPIX_USB_PRODUCT) {
+ msg(L_INFO, "Found LED strip device at usb%d.%d", libusb_get_bus_number(dev), libusb_get_device_address(dev));
+
+ if (err = libusb_open(dev, &devh)) {
+ usb_error("Cannot open device: error %d", err);
+ goto out;
+ }
+ libusb_reset_device(devh);
+ if (err = libusb_claim_interface(devh, 0)) {
+ usb_error("Cannot claim interface: error %d", err);
+ goto out;
+ }
+
+ goto out;
+ }
+ }
+ }
+
+out:
+ libusb_free_device_list(devlist, 1);
+}
+
+static void init_usb(void)
+{
+ int err;
+ if (err = libusb_init(&usb_ctxt))
+ die("Cannot initialize libusb: error %d", err);
+ // libusb_set_debug(usb_ctxt, 3);
+ open_device();
+}
+
+/*** Clock ***/
+
+static void clock_countdown(uint t)
+{
+ memset(lights, 1, sizeof(lights));
+
+ const uint NBITS = 17;
+ const uint BITSIZE = 3;
+ const uint BITGAP = 4;
+ const uint BITOFF = (NPIX_NUM_LEDS - NBITS * BITSIZE - (NBITS - 1) * BITGAP) / 2;
+
+ for (uint i=0; i < NBITS; i++) {
+ uint j = BITOFF + (BITSIZE + BITGAP) * i;
+ for (uint k = 0; k < BITSIZE; k++) {
+ if (t & (1 << i)) {
+ lights[j+k][0] = 0;
+ lights[j+k][1] = 255;
+ lights[j+k][2] = 0;
+ } else {
+ lights[j+k][0] = 255;
+ lights[j+k][1] = 0;
+ lights[j+k][2] = 0;
+ }
+ }
+ }
+}
+
+static void clock_year(uint t)
+{
+ memset(lights, 1, sizeof(lights));
+
+ const uint NBITS = 11;
+ const uint BITSIZE = 5;
+ const uint BITGAP = 6;
+ const uint BITOFF = (NPIX_NUM_LEDS - NBITS * BITSIZE - (NBITS - 1) * BITGAP) / 2;
+
+ for (uint i=0; i < NBITS; i++) {
+ uint j = BITOFF + (BITSIZE + BITGAP) * i;
+ for (uint k = 0; k < BITSIZE; k++) {
+ if (t & (1 << i)) {
+ lights[j+k][0] = 0;
+ lights[j+k][1] = 255;
+ lights[j+k][2] = 0;
+ } else {
+ lights[j+k][0] = 255;
+ lights[j+k][1] = 0;
+ lights[j+k][2] = 0;
+ }
+ }
+ }
+}
+
+static void clock_tick(void)
+{
+ struct timeval tv;
+ if (gettimeofday(&tv, NULL) < 0) {
+ msg(L_ERROR, "gettimeofday: %m");
+ sleep(1);
+ return;
+ }
+ uint t = (uint) tv.tv_sec + 1;
+ usleep(1000000 - tv.tv_usec);
+
+ uint midnight = 1767222000;
+ if (t < midnight)
+ clock_countdown(midnight - t);
+ else
+ clock_year(2026);
+}
+
+/*** Main ***/
+
+int main(int argc UNUSED, char **argv)
+{
+ log_init(argv[0]);
+ log_default_stream()->levels &= ~(1U << L_DEBUG);
+
+ mtx_init(&light_mutex, mtx_plain);
+ cnd_init(&light_cond);
+
+ // mqtt_init();
+ init_usb();
+
+ bool need_resend = true;
+ for (;;) {
+ if (!devh) {
+ msg(L_INFO, "Waiting for device to appear...");
+ while (!devh) {
+ sleep(5);
+ open_device();
+ }
+ need_resend = true;
+ }
+
+#if 0
+ mtx_lock(&light_mutex);
+ while (!need_resend && !light_refresh)
+ cnd_wait(&light_cond, &light_mutex);
+ light_refresh = 0;
+ need_resend = 0;
+ mtx_unlock(&light_mutex);
+#endif
+
+ clock_tick();
+
+ msg(L_DEBUG, "Sending USB packet");
+
+ uint batch = 63/3;
+ for (uint i=0; i < NPIX_NUM_LEDS; i += batch) {
+ byte packet[64];
+ uint n = NPIX_NUM_LEDS - i;
+ if (n > batch)
+ n = batch;
+ packet[0] = i;
+ mtx_lock(&light_mutex);
+ for (uint j=0; j<n; j++) {
+ // Packet format is RGB
+ packet[1 + 3*j + 0] = lights[i+j][0];
+ packet[1 + 3*j + 1] = lights[i+j][1];
+ packet[1 + 3*j + 2] = lights[i+j][2];
+ }
+ mtx_unlock(&light_mutex);
+
+ int len = 1 + 3*n;
+ int err, transferred;
+ if (err = libusb_bulk_transfer(devh, 0x01, packet, len, &transferred, 1000)) {
+ usb_error("USB transfer failed: error %d", err);
+ break;
+ } else if (transferred != len)
+ usb_error("USB short transfer: %d out of %d bytes", transferred, len);
+ }
+ }
+}