/*
- * A Control Daemon for the Solid State Relay module
+ * A MQTT Gateway Daemon for the Solid State Relay module
*
* (c) 2018 Martin Mares <mj@ucw.cz>
*/
#include <ucw/lib.h>
#include <ucw/mainloop.h>
#include <ucw/opt.h>
+#include <ucw/strtonum.h>
#include <ucw/unaligned.h>
+#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <unistd.h>
#include <time.h>
#include <libusb-1.0/libusb.h>
struct libusb_context *usb_ctxt;
struct libusb_device_handle *devh;
+struct mosquitto *mosq;
+
+static u32 ssr_state;
+
void open_device(void)
{
int err;
return received;
}
+static void set_relays(void)
+{
+ msg(L_INFO, "Setting relays to %02x", ssr_state);
+ put_u32_be(req, 1);
+ put_u32_be(req+4, ssr_state);
+ transaction(8, 4);
+}
+
+static void mqtt_publish(const char *topic, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ char m[256];
+ int l = vsnprintf(m, sizeof(m), fmt, args);
+ if (mosquitto_publish(mosq, NULL, topic, l, m, 0, true) != MOSQ_ERR_SUCCESS)
+ msg(L_ERROR, "Mosquitto: publish failed");
+ va_end(args);
+}
+
+static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int level, const char *message)
+{
+ // msg(L_INFO, "MQTT(%d): %s", level, message);
+}
+
+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_INFO, "MQTT < %s %s", m->topic, val);
+
+ if (!strcmp(m->topic, "burrow/loft/fan")) {
+ uint x;
+ const char *err;
+ if ((err = str_to_uint(&x, val, NULL, 10 | STN_WHOLE)) || x >= 4) {
+ msg(L_ERROR, "Received invalid fan setting %s: %s", val, err);
+ x = 0;
+ }
+ msg(L_INFO, "Setting fan level to %u", x);
+ ssr_state &= ~7U;
+ switch (x) {
+ case 1:
+ ssr_state |= 4;
+ break;
+ case 2:
+ ssr_state |= 2;
+ break;
+ case 3:
+ ssr_state |= 1;
+ break;
+ }
+ set_relays();
+ } else if (!strcmp(m->topic, "burrow/loft/circulation")) {
+ uint x;
+ const char *err;
+ if ((err = str_to_uint(&x, val, NULL, 10 | STN_WHOLE)) || x >= 2) {
+ msg(L_ERROR, "Received invalid circulation setting %s: %s", val, err);
+ x = 0;
+ }
+ msg(L_INFO, "Setting circulation to %u", x);
+ ssr_state &= ~8U;
+ if (x)
+ ssr_state |= 8;
+ }
+}
+
static struct opt_section options = {
OPT_ITEMS {
- OPT_HELP("A daemon for controlling the solid state relay module"),
+ OPT_HELP("A daemon for controlling the solid state relay module via MQTT"),
OPT_HELP(""),
OPT_HELP("Options:"),
OPT_HELP_OPTION,
die("Cannot initialize libusb: error %d", err);
libusb_set_debug(usb_ctxt, 3);
open_device();
+ set_relays();
mosquitto_lib_init();
- struct mosquitto *mosq = mosquitto_new("ssrd", 1, NULL);
+ mosq = mosquitto_new("ssrd", 1, NULL);
if (!mosq)
die("Mosquitto: initialization failed");
+ mosquitto_log_callback_set(mosq, mqtt_log_callback);
+ mosquitto_message_callback_set(mosq, mqtt_msg_callback);
+
if (mosquitto_will_set(mosq, "burrow/loft/status", 4, "dead", 0, true) != MOSQ_ERR_SUCCESS)
die("Mosquitto: unable to set will");
if (mosquitto_connect(mosq, "127.0.0.1", 1883, 60) != MOSQ_ERR_SUCCESS)
die("Mosquitto: connect failed");
- if (mosquitto_publish(mosq, NULL, "burrow/loft/status", 2, "ok", 0, true) != MOSQ_ERR_SUCCESS)
- msg(L_ERROR, "Mosquitto: publish failed");
+ if (mosquitto_subscribe(mosq, NULL, "burrow/loft/#", 1) != MOSQ_ERR_SUCCESS)
+ die("Mosquitto: subscribe failed");
+
+ mqtt_publish("burrow/loft/status", "ok");
time_t next_run = 0;
for (;;) {
int t = get_u32_be(resp+4);
msg(L_DEBUG, "Measured raw temperature %d", t);
- byte mbuf[16];
- int mlen = snprintf(mbuf, sizeof(mbuf), "%.3f", t / 1000.);
- if (mosquitto_publish(mosq, NULL, "burrow/loft/temperature", mlen, mbuf, 0, true) != MOSQ_ERR_SUCCESS)
- msg(L_ERROR, "Mosquitto: publish failed");
-
- byte tbuf[16];
- int tlen = snprintf(tbuf, sizeof(tbuf), "%u", (int) now);
- if (mosquitto_publish(mosq, NULL, "burrow/loft/timestamp", tlen, tbuf, 0, true) != MOSQ_ERR_SUCCESS)
- msg(L_ERROR, "Mosquitto: publish failed");
+ mqtt_publish("burrow/loft/temperature", "%.3f", t / 1000.);
+ mqtt_publish("burrow/loft/timestamp", "%u", (int) now);
}
}