From bf0654541098924ece032c4ac12b8b5d2589f4b0 Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Fri, 28 Feb 2020 16:46:14 +0100 Subject: [PATCH] BSB: Daemon for Turris communicating via MQTT --- MQTT | 14 +- bsb/daemon/Makefile | 28 +++- bsb/daemon/bsb-monitor.c | 214 +++++++++++++++++++++++++++++++ bsb/daemon/burrow-bsb.c | 271 +++++++++++++++++++++++++++++---------- bsb/firmware/interface.h | 1 + 5 files changed, 454 insertions(+), 74 deletions(-) create mode 100644 bsb/daemon/bsb-monitor.c diff --git a/MQTT b/MQTT index 85e4c83..54e4856 100644 --- a/MQTT +++ b/MQTT @@ -38,9 +38,21 @@ burrow/temp/ursarium-rh burrow/pressure/clock +burrow/heating/water-pressure [bar] +burrow/heating/outside-temp +burrow/heating/circuit1/room-temp +burrow/heating/circuit1/mix-temp +burrow/heating/circuit1/mix-valve +burrow/heating/circuit2/room-temp + +bsb/stats/* +bsb/frame hex dump of raw frames received + status/aircon ok/dead status/auto ok/dead +status/bsb ok/dead status/clock ok/dead +status/influx ok/dead status/loft-ssr ok/dead status/power-meter ok/dead -status/prometheus ok/dead +status/prometheus ok/dead # Obsolete diff --git a/bsb/daemon/Makefile b/bsb/daemon/Makefile index 1e97498..9f2ce2b 100644 --- a/bsb/daemon/Makefile +++ b/bsb/daemon/Makefile @@ -1,4 +1,24 @@ +BUILD_FOR_TURRIS=1 + +ifdef BUILD_FOR_TURRIS + +TOPDIR=/root/turris + +include $(TOPDIR)/rules.mk +include $(TOPDIR)/include/package.mk + +PC := PATH=$(STAGING_DIR_HOST)/bin:$(PATH) PKG_CONFIG_PATH=$(PKG_CONFIG_PATH) PKG_CONFIG_LIBDIR=$(PKG_CONFIG_PATH) STAGING_PREFIX=$(STAGING_DIR)/usr $(PKG_CONFIG) + +export PATH=$(TARGET_PATH_PKG) +CC=$(TARGET_CC_NOCACHE) +LD=$(TARGET_LD_NOCACHE) + +else + PC := PKG_CONFIG_PATH=/home/mj/tmp/bsb/root/lib/pkgconfig pkg-config + +endif + USB_CFLAGS := $(shell $(PC) --cflags libusb-1.0) USB_LDFLAGS := $(shell $(PC) --libs libusb-1.0) UCW_CFLAGS := $(shell $(PC) --cflags libucw) @@ -7,9 +27,7 @@ UCW_LDFLAGS := $(shell $(PC) --libs libucw) CFLAGS=$(USB_CFLAGS) $(UCW_CFLAGS) -std=gnu1x -O2 -Wall -Wextra -Wno-parentheses LDFLAGS=$(USB_LDFLAGS) $(UCW_LDFLAGS) -lmosquitto -all: burrow-bsb - -burrow-usb: burrow-usb.c ../firmware/interface.h +all: burrow-bsb bsb-monitor -clean: - rm -f burrow-bsb +# burrow-bsb: burrow-bsb.c ../firmware/interface.h +# bsb-monitor: bsb-monitor.c ../firmware/interface.h diff --git a/bsb/daemon/bsb-monitor.c b/bsb/daemon/bsb-monitor.c new file mode 100644 index 0000000..e8bd4ca --- /dev/null +++ b/bsb/daemon/bsb-monitor.c @@ -0,0 +1,214 @@ +/* + * Boiler System Bus Interface Daemon + * + * (c) 2020 Martin Mares + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +static struct libusb_context *usb_ctxt; +static struct libusb_device_handle *devh; + +typedef unsigned char byte; +typedef uint32_t u32; +typedef unsigned int uint; + +#include "../firmware/interface.h" + +static void die(const char *msg, ...) +{ + va_list args; + va_start(args, msg); + + vfprintf(stderr, msg, args); + fputc('\n', stderr); + + exit(1); +} + +static void usb_error(const char *msg, ...) +{ + va_list args; + va_start(args, msg); + vfprintf(stderr, msg, args); + fputc('\n', stderr); + 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 %02x: ", pkt[BF_SRC] ^ 0x80, pkt[BF_DEST]); + if (status) + printf("[REPLY] "); + switch (pkt[4]) { + case 2: + printf("INFO %04x:%04x =", (pkt[5]<<8) | pkt[6], (pkt[7]<<8) | pkt[8]); + for (uint i=9; i */ +#include +#include +#include +#include +#include + #include #include #include #include #include +#include #include #include #include - -static struct libusb_context *usb_ctxt; -static struct libusb_device_handle *devh; +#include typedef unsigned char byte; typedef uint32_t u32; @@ -23,22 +28,75 @@ typedef unsigned int uint; #include "../firmware/interface.h" -static void die(const char *msg, ...) +/*** 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, msg); + 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); + } - vfprintf(stderr, msg, args); - fputc('\n', stderr); + va_end(args); +} - exit(1); +static void mqtt_conn_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int status) +{ + if (!status) { + msg(L_DEBUG, "MQTT: Connection established"); + mqtt_publish("status/bsb", "ok"); + mqtt_connected = true; + } 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_init(void) +{ + mosquitto_lib_init(); + + mosq = mosquitto_new("bsbd", 1, NULL); + if (!mosq) + die("Mosquitto: Initialization failed"); + + mosquitto_connect_callback_set(mosq, mqtt_conn_callback); + mosquitto_log_callback_set(mosq, mqtt_log_callback); + + if (mosquitto_will_set(mosq, "status/bsb", 4, "dead", 0, true) != MOSQ_ERR_SUCCESS) + die("Mosquitto: Unable to set will"); + + if (mosquitto_connect_async(mosq, "127.0.0.1", 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); - vfprintf(stderr, msg, args); + ucw_vmsg(L_ERROR, msg, args); fputc('\n', stderr); va_end(args); @@ -61,7 +119,7 @@ static void open_device(void) libusb_device *dev = devlist[i]; if (!libusb_get_device_descriptor(dev, &desc)) { if (desc.idVendor == BSB_USB_VENDOR && desc.idProduct == BSB_USB_PRODUCT) { - fprintf(stderr, "Found device at usb%d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev)); + msg(L_INFO, "Found 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); @@ -82,37 +140,99 @@ out: libusb_free_device_list(devlist, 1); } -static inline uint get_u32_le(byte *p) +static void init_usb(void) { - return (p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]; + int err; + if (err = libusb_init(&usb_ctxt)) + die("Cannot initialize libusb: error %d", err); + // libusb_set_debug(usb_ctxt, 3); + open_device(); + } +/*** Protocol ***/ + static const char * const stat_names[] = { #define P(x) #x, BSB_STATS #undef P }; -static void show_stats(byte *resp, uint len) +static void process_stats(time_t t, byte *resp, uint len) +{ + for (uint i=0; i < ARRAY_SIZE(stat_names) && 4*i + 3 < (uint) len; i++) { + char item[64]; + snprintf(item, sizeof(item), "burrow/bsb/stats/%s", stat_names[i]); + mqtt_publish(item, "%u", (uint) get_u32_le(resp+4*i), t); + } +} + +static void process_info(byte *p, uint len) { - printf("# Stats:"); - for (uint i=0; 4*i + 3 < (uint) len; i++) - printf(" %s=%u", (i < sizeof(stat_names) / sizeof(stat_names[0]) ? stat_names[i] : "?"), get_u32_le(resp+4*i)); - printf("\n"); + if (len < 4) + return; - fflush(stdout); + u32 addr = get_u32_be(p); + p += 4; + len -= 4; + + switch (addr) { + case 0x05000219: + if (len >= 4) { + uint temp = get_u16_be(p); + uint press = get_u16_be(p + 2); + mqtt_publish("burrow/heating/outside-temp", "%.2f", temp / 64.); + mqtt_publish("burrow/heating/water-pressure", "%.1f", press / 10.); + } + break; + case 0x05000229: + // AGU.2 status + if (len >= 2) { + uint temp = get_u16_be(p); + mqtt_publish("burrow/heating/circuit1/mix-temp", "%.2f", temp / 64.); + } + break; + case 0x05040227: + // AGU.2 control + if (len >= 2) { + uint m = get_u16_be(p); + mqtt_publish("burrow/heating/circuit1/mix-valve", "%u", m); + } + break; + case 0x3d2d0215: + // Room 1 status + if (len >= 2) { + uint temp = get_u16_be(p); + mqtt_publish("burrow/heating/circuit1/room-temp", "%.2f", temp / 64.); + } + break; + case 0x3e2e0215: + // Room 2 status + if (len >= 2) { + uint temp = get_u16_be(p); + mqtt_publish("burrow/heating/circuit2/room-temp", "%.2f", temp / 64.); + } + break; + } } -static void show_packet(byte *pkt, uint len) +static void process_answer(byte *p, uint len) { - time_t now = time(NULL); - struct tm *tm = localtime(&now); - char timebuf[64]; - strftime(timebuf, sizeof(timebuf), "%Y-%m-%d %H:%M:%S", tm); - printf("%s ", timebuf); + if (len < 4) + return; + + u32 addr = get_u32_be(p); + p += 4; + len -= 4; + + switch (addr) { + } +} +static void process_frame(time_t t, byte *pkt, uint len) +{ if (!len) { - printf("ERROR: Received empty frame!\n"); + msg(L_ERROR, "Received empty frame"); return; } @@ -120,60 +240,74 @@ static void show_packet(byte *pkt, uint len) len--; if (!len) { - printf("ERROR: Transmit status: %u\n", status); + msg(L_ERROR, "Frame transmit status: %u", status); return; } -#if 0 - printf(": [%d]", status); - for (uint i=0; i %02x: ", pkt[BF_SRC] ^ 0x80, pkt[BF_DEST]); - if (status) - printf("[REPLY] "); - switch (pkt[4]) { - case 2: - printf("INFO %04x:%04x =", (pkt[5]<<8) | pkt[6], (pkt[7]<<8) | pkt[8]); - for (uint i=9; ilevels &= ~(1U << L_DEBUG); + + mqtt_init(); + init_usb(); time_t now = time(NULL); time_t last_stats = 0; - time_t last_query = now; - int received; + // time_t last_query = now; + int err, received; byte resp[64]; for (;;) { if (!devh) { - fprintf(stderr, "Waiting for device to appear...\n"); + msg(L_INFO, "Waiting for device to appear..."); while (!devh) { sleep(5); open_device(); @@ -187,21 +321,22 @@ int main(void) continue; } - show_stats(resp, received); + process_stats(now, resp, received); last_stats = now; } +#if 0 if (last_query + 10 < now) { byte pkt[] = { 0xdc, 0xc2, 0x00, 0x0b, 0x06, 0x3d, 0x2e, 0x11, 0x25, 0x00, 0x00 }; if (err = libusb_bulk_transfer(devh, 0x01, pkt, sizeof(pkt), &received, 2000)) { - printf("Send failed: error %d\n", err); - // usb_error("Receive failed: error %d", received); - // continue; + usb_error("Send failed: error %d", err); + continue; } else { - // printf("Send OK: %d bytes\n", received); + // msg(L_DEBUG"Send OK: %d bytes", received); } last_query = now; } +#endif if (err = libusb_interrupt_transfer(devh, 0x82, resp, 64, &received, 1000)) { if (err != LIBUSB_ERROR_TIMEOUT) @@ -209,6 +344,6 @@ int main(void) continue; } - show_packet(resp, received); + process_frame(now, resp, received); } } diff --git a/bsb/firmware/interface.h b/bsb/firmware/interface.h index 6be9f8a..8c032c4 100644 --- a/bsb/firmware/interface.h +++ b/bsb/firmware/interface.h @@ -84,6 +84,7 @@ enum bsb_frame { BF_DEST, BF_LEN, BF_OP, + BF_PARAMS, }; enum bsb_address { -- 2.39.2