]> mj.ucw.cz Git - home-hw.git/commitdiff
X-mas: Countdown
authorMartin Mareš <mj@ucw.cz>
Wed, 31 Dec 2025 18:55:23 +0000 (19:55 +0100)
committerMartin Mareš <mj@ucw.cz>
Wed, 31 Dec 2025 18:55:23 +0000 (19:55 +0100)
xmas-lights/daemon/Makefile
xmas-lights/daemon/led-strip.c [deleted file]
xmas-lights/daemon/xmas-lights.c [new file with mode: 0644]
xmas-lights/firmware/main.c

index 5fffc46d3159f0276bb79fc68e140fb754d7f9dd..ea3f973048d52e2e3e678c50ce6ab23e7705b430 100644 (file)
@@ -9,20 +9,20 @@ MOSQUITTO_LIBS := $(shell $(PC) --libs libmosquitto)
 CFLAGS=-O2 -Wall -Wextra -Wno-sign-compare -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes $(UCW_CFLAGS) $(USB_CFLAGS) $(MOSQUITTO_CFLAGS) -g
 LDLIBS=$(UCW_LIBS) $(USB_LIBS) $(MOSQUITTO_LIBS) -lpthread -lm
 
-all: led-strip
+all: xmas-lights
 
-led-strip: led-strip.o
+xmas-lights: xmas-lights.o
 
-led-strip.o: led-strip.c ../firmware/interface.h
+xmas-lights.o: xmas-lights.c ../firmware/interface.h
 
-install: led-strip
-       install led-strip /usr/local/sbin/
+install: xmas-lights
+       install xmas-lights /usr/local/sbin/
 
 clean:
-       rm -f *.o led-strip
+       rm -f *.o xmas-lights
 
 deploy:
-       rsync -av --delete .. root@berry.lan:ksp/led-strip/
-       ssh root@berry.lan 'cd ksp/led-strip/daemon/ && make install'
+       rsync -av --delete .. root@berry:xmas-lights/src
+       ssh root@berry 'cd xmas-lights/src/daemon/ && make clean && make install'
 
 .PHONY: all install clean upload
diff --git a/xmas-lights/daemon/led-strip.c b/xmas-lights/daemon/led-strip.c
deleted file mode 100644 (file)
index 73df90d..0000000
+++ /dev/null
@@ -1,269 +0,0 @@
-/*
- *     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);
-               }
-       }
-}
diff --git a/xmas-lights/daemon/xmas-lights.c b/xmas-lights/daemon/xmas-lights.c
new file mode 100644 (file)
index 0000000..c8d5aa4
--- /dev/null
@@ -0,0 +1,347 @@
+/*
+ *     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);
+               }
+       }
+}
index 9e1104d604bb6def825f8d50f3e6e4e770e822e4..ce82cde9f3141278c99373a28a9c81861a2ee005 100644 (file)
@@ -459,7 +459,7 @@ int main(void)
                if (ms_ticks - last_blink >= 100) {
                        last_blink = ms_ticks;
                        debug_led_toggle();
-#if 0
+#if 1
                        if (!got_first_message) {
                                static uint i;
                                neopixel_set(i, 0, 0, 7);
@@ -468,7 +468,7 @@ int main(void)
                        }
 #endif
                }
-               effect();
+               // effect();
                if (usb_event_pending) {
                        usbd_poll(usbd_dev);
                        usb_event_pending = 0;