]> mj.ucw.cz Git - home-hw.git/commitdiff
Clock: A new daemon
authorMartin Mares <mj@ucw.cz>
Sun, 14 May 2023 12:35:03 +0000 (14:35 +0200)
committerMartin Mares <mj@ucw.cz>
Sun, 14 May 2023 12:35:03 +0000 (14:35 +0200)
clock/host/Makefile
clock/host/burrow-clock.c

index 18c9d412fc7015aee9cb1afe0da10501eafd7e8e..17f6a9588f460c5d183f5df3af76a4af20cf27b0 100644 (file)
@@ -1,13 +1,21 @@
-UCWCF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --cflags libucw)
-UCWLF:=$(shell PKG_CONFIG_PATH=$(LIBUCW)/lib/pkgconfig pkg-config --libs libucw)
+PC=pkg-config
+UCW_CFLAGS := $(shell $(PC) --cflags libucw)
+UCW_LIBS := $(shell $(PC) --libs libucw)
+USB_CFLAGS := $(shell $(PC) --cflags libusb-1.0)
+USB_LIBS := $(shell $(PC) --libs libusb-1.0)
+UCW_USB = ../../ucw-libusb
 
-CFLAGS=-std=gnu99 -O2 -Wall -Wextra -Wno-parentheses $(UCWCF)
-LDLIBS=-lusb-1.0 $(UCWLF)
+CFLAGS=-O2 -Wall -Wextra -Wno-parentheses $(UCW_CFLAGS) $(USB_CFLAGS) -I$(UCW_USB)
+LDLIBS=$(UCW_LIBS) $(USB_LIBS) -lmosquitto
 
 all: burrow-clock
 
-burrow-clock: burrow-clock.c
-burrow-clock: LDLIBS += -lmosquitto
+burrow-clock: burrow-clock.o usb-mainloop.o usb-helper.o
+
+burrow-clock.o: burrow-clock.c $(UCW_USB)/usb-helper.h
+
+%.o: $(UCW_USB)/%.c $(UCW_USB)/usb-mainloop.h $(UCW_USB)/usb-helper.h
+       $(CC) $(CFLAGS) -c -o $@ $<
 
 install: all
        install burrow-clock /usr/local/sbin/
index 4055743f23b8966de341f5437c1ef064eb61f5a2..d0a99de5f9714591c89d3c3a22bed78f9f541eaf 100644 (file)
@@ -1,9 +1,11 @@
 /*
- *     MJ's desktop clock daemon
+ *     MJ's Workshop Clock Daemon
  *
- *     (c) 2018 Martin Mares <mj@ucw.cz>
+ *     (c) 2023 Martin Mares <mj@ucw.cz>
  */
 
+#define LOCAL_DEBUG
+
 #include <ucw/lib.h>
 #include <ucw/log.h>
 #include <ucw/opt.h>
 #include <libusb-1.0/libusb.h>
 #include <mosquitto.h>
 
-static struct mosquitto *mosq;
+#include "usb-helper.h"
 
-struct libusb_context *usb_ctxt;
-struct libusb_device_handle *devh;
+struct device {
+       struct usb_dev *usb;
 
-static libusb_device *find_device(void)
-{
-       libusb_device **devlist;
-       ssize_t devn = libusb_get_device_list(usb_ctxt, &devlist);
-       if (devn < 0) {
-               fprintf(stderr, "Cannot enumerate USB devices: error %d\n", (int) devn);
-               exit(1);
-       }
+       struct libusb_transfer *rx_transfer, *tx_transfer;
+       bool rx_in_flight, tx_in_flight;
 
-       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 == 0x4242 && desc.idProduct == 0x0001) {
-                               msg(L_INFO, "Found device at usb%d.%d", libusb_get_bus_number(dev), libusb_get_device_address(dev));
-                               // FIXME: Free device list
-                               return dev;
-                       }
-               }
-       }
+       byte rx_buffer[4];
 
-       libusb_free_device_list(devlist, 1);
-       fprintf(stderr, "Device not found\n");
-       exit(1);
-}
+       struct main_timer clock_timer;
+};
+
+/*** MQTT ***/
+
+static struct mosquitto *mosq;
 
 static void mqtt_publish(const char *topic, const char *fmt, ...)
 {
@@ -69,16 +57,181 @@ static void mqtt_conn_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED,
        }
 }
 
-static int use_daemon;
+static void mqtt_init(void)
+{
+       mosquitto_lib_init();
+       mosq = mosquitto_new("clock", 1, NULL);
+       if (!mosq)
+               die("Mosquitto: initialization failed");
+
+       mosquitto_connect_callback_set(mosq, mqtt_conn_callback);
+
+       if (mosquitto_will_set(mosq, "status/clock", 4, "dead", 0, true) != MOSQ_ERR_SUCCESS)
+               die("Mosquitto: unable to set will");
+
+       if (mosquitto_connect_async(mosq, "10.32.184.5", 1883, 60) != MOSQ_ERR_SUCCESS)
+               die("Mosquitto: connect failed");
+
+       if (mosquitto_loop_start(mosq))
+               die("Mosquitto: cannot start service thread");
+}
+
+/*** USB ***/
+
+static void clock_timer_handler(struct main_timer *timer);
+static void rx_submit(struct device *dev);
+
+bool usb_dev_init(struct usb_dev *u)
+{
+       msg(L_INFO, "Connected clock at %s", u->port);
+
+       struct device *dev = xmalloc_zero(sizeof(*dev));
+       u->device = dev;
+       dev->usb = u;
+
+       dev->rx_transfer = libusb_alloc_transfer(0);
+       dev->tx_transfer = libusb_alloc_transfer(0);
+
+       dev->clock_timer.handler = clock_timer_handler;
+       dev->clock_timer.data = dev;
+
+       return true;
+}
+
+void usb_dev_connect(struct usb_dev *u)
+{
+       struct device *dev = u->device;
+       USB_DBG(u, "Connected!");
+
+       timer_add_rel(&dev->clock_timer, 0);
+       rx_submit(dev);
+}
+
+void usb_dev_cancel(struct usb_dev *u)
+{
+       struct device *dev = u->device;
+       USB_DBG(u, "Cancelling transfers");
+
+       if (dev->rx_in_flight)
+               libusb_cancel_transfer(dev->rx_transfer);
+       if (dev->tx_in_flight)
+               libusb_cancel_transfer(dev->tx_transfer);
+
+       timer_del(&dev->clock_timer);
+}
+
+bool usb_dev_in_flight(struct usb_dev *u)
+{
+       struct device *dev = u->device;
+       return dev->rx_in_flight || dev->tx_in_flight;
+}
+
+void usb_dev_cleanup(struct usb_dev *u)
+{
+       struct device *dev = u->device;
+       msg(L_INFO, "Disconnected clock at %s", u->port);
+
+       libusb_free_transfer(dev->rx_transfer);
+       libusb_free_transfer(dev->tx_transfer);
+}
+
+static void tx_callback(struct libusb_transfer *xfer)
+{
+       struct device *dev = xfer->user_data;
+       struct usb_dev *u = dev->usb;
+
+       USB_DBG(u, "Bulk TX done (status=%d, len=%d/%d)", xfer->status, xfer->actual_length, xfer->length);
+       dev->tx_in_flight = false;
+
+       if (xfer->status != LIBUSB_TRANSFER_COMPLETED)
+               usb_error(u, "Bulk TX transfer failed with status %d", xfer->status);
+}
+
+static void clock_timer_handler(struct main_timer *timer)
+{
+       struct device *dev = timer->data;
+       struct usb_dev *u = dev->usb;
+       msg(L_DEBUG, "Clock tick");
+
+       if (dev->tx_in_flight) {
+               msg(L_ERROR, "Transfer overrun");
+               timer_add_rel(timer, 1000);
+       }
+
+       time_t t = time(NULL);
+       struct tm *tm = localtime(&t);
+
+       unsigned char req[8] = {
+               tm->tm_hour / 10,
+               tm->tm_hour % 10,
+               tm->tm_min / 10,
+               tm->tm_min % 10,
+               (tm->tm_sec % 2 ? 0xff : 0),
+       };
+
+       libusb_fill_bulk_transfer(dev->tx_transfer, u->devh, 0x01, req, sizeof(req), tx_callback, dev, 5000);
+
+       int err;
+       if (err = libusb_submit_transfer(dev->tx_transfer))
+               usb_error(u, "Cannot submit bulk TX transfer: error %d", err);
+       else
+               dev->tx_in_flight = true;
+
+       timer_add_rel(timer, 1000);
+}
+
+static void rx_callback(struct libusb_transfer *xfer)
+{
+       struct device *dev = xfer->user_data;
+       struct usb_dev *u = dev->usb;
+
+       USB_DBG(u, "Bulk RX done (status=%d, len=%d/%d)", xfer->status, xfer->actual_length, xfer->length);
+       dev->rx_in_flight = false;
+
+       if (xfer->status == LIBUSB_TRANSFER_TIMED_OUT) {
+               // Should not happen
+               rx_submit(dev);
+               return;
+       }
+
+       if (xfer->status != LIBUSB_TRANSFER_COMPLETED) {
+               usb_error(u, "Bulk RX transfer failed with status %d", xfer->status);
+               return;
+       }
+
+       if (xfer->actual_length < 4) {
+               msg(L_ERROR, "Short RX transfer");
+       } else {
+               u32 key = get_u32_be(dev->rx_buffer);
+               msg(L_INFO, "IR key %08x", key);
+       }
+
+       rx_submit(dev);
+}
+
+static void rx_submit(struct device *dev)
+{
+       struct usb_dev *u = dev->usb;
+
+       libusb_fill_bulk_transfer(dev->rx_transfer, u->devh, 0x82, dev->rx_buffer, sizeof(dev->rx_buffer), rx_callback, dev, 0);
+
+       int err;
+       if (err = libusb_submit_transfer(dev->rx_transfer))
+               usb_error(u, "Cannot submit bulk RX transfer: error %d", err);
+       else
+               dev->rx_in_flight = true;
+}
+
+/*** Main loop ***/
+
 static int use_debug;
 
 static struct opt_section options = {
        OPT_ITEMS {
-               OPT_HELP("A daemon for controlling the solid state relay module via MQTT"),
+               OPT_HELP("A daemon for controlling MJ's workshop clock"),
                OPT_HELP(""),
                OPT_HELP("Options:"),
                OPT_BOOL('d', "debug", use_debug, 0, "\tLog debugging messages"),
-               OPT_BOOL(0, "daemon", use_daemon, 0, "\tDaemonize"),
                OPT_HELP_OPTION,
                OPT_CONF_OPTIONS,
                OPT_END
@@ -90,76 +243,13 @@ int main(int argc UNUSED, char **argv)
        log_init(argv[0]);
        opt_parse(&options, argv+1);
 
-       if (use_daemon) {
-               struct log_stream *ls = log_new_syslog("daemon", LOG_PID);
-               log_set_default_stream(ls);
-       }
        if (!use_debug)
                log_default_stream()->levels &= ~(1U << L_DEBUG);
 
-       mosquitto_lib_init();
-       mosq = mosquitto_new("clock", 1, NULL);
-       if (!mosq)
-               die("Mosquitto: initialization failed");
-
-       mosquitto_connect_callback_set(mosq, mqtt_conn_callback);
-
-       if (mosquitto_will_set(mosq, "status/clock", 4, "dead", 0, true) != MOSQ_ERR_SUCCESS)
-               die("Mosquitto: unable to set will");
-
-       if (mosquitto_connect_async(mosq, "10.32.184.5", 1883, 60) != MOSQ_ERR_SUCCESS)
-               die("Mosquitto: connect failed");
-
-       if (mosquitto_loop_start(mosq))
-               die("Mosquitto: cannot start service thread");
-
-       int err;
-       if (err = libusb_init(&usb_ctxt))
-               die("Cannot initialize libusb: error %d", err);
-
-       libusb_device *dev = find_device();
-
-       if (err = libusb_open(dev, &devh))
-               die("Cannot open device: error %d", err);
-       libusb_reset_device(devh);
-       if (err = libusb_claim_interface(devh, 0))
-               die("Cannot claim interface: error %d", err);
-
-       uint cnt = 0;
-       for (;;) {
-               time_t t = time(NULL);
-               struct tm *tm = localtime(&t);
-
-               unsigned char req[8] = {
-                       tm->tm_hour / 10,
-                       tm->tm_hour % 10,
-                       (tm->tm_sec % 2 ? 10 : 0xff),
-                       tm->tm_min / 10,
-                       tm->tm_min % 10,
-               };
-               int transferred;
-               if (err = libusb_bulk_transfer(devh, 0x01, req, 8, &transferred, 2000))
-                       die("Transfer failed: error %d", err);
-
-               unsigned char resp[64];
-               int received;
-               if (err = libusb_bulk_transfer(devh, 0x82, resp, 64, &received, 2000))
-                       die("Receive failed: error %d", err);
-               // printf("Received %d bytes\n", received);
-               if (received >= 12) {
-                       int temp = get_u32_be(resp);
-                       int press = get_u32_be(resp + 4);
-                       uint cycle = get_u32_be(resp + 8);
-                       msg(L_DEBUG, "Temperature %d ddegC, pressure %d Pa, cycle %u", temp, press, cycle);
-                       if (!(cnt % 10) && press) {
-                               mqtt_publish("burrow/temp/clock", "%.1f %llu", temp / 10., (long long) t);
-                               mqtt_publish("burrow/pressure/clock", "%d %llu", press, (long long) t);
-                       }
-               }
-
-               sleep(1);
-               cnt++;
-       }
+       mqtt_init();
+       main_init();
+       usb_helper_init(0x4242, 0x0007);
 
+       main_loop();
        return 0;
 }