X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=aircon%2Fdaemon%2Fburrow-aircond.c;h=7db3864ca9f4bf3b9fb425f3eb724bb7c1eaf0db;hb=85197adaf7c1c7a993fee6b237f9d4d3d8b95104;hp=629c202606d6c5e241868927ffbe45d5e426d1fc;hpb=25ebbf744a095be742a9be429c9b621374a6635b;p=home-hw.git diff --git a/aircon/daemon/burrow-aircond.c b/aircon/daemon/burrow-aircond.c index 629c202..7db3864 100644 --- a/aircon/daemon/burrow-aircond.c +++ b/aircon/daemon/burrow-aircond.c @@ -16,71 +16,145 @@ #include #include #include +#include +#include #include #include #include "../firmware/registers.h" +/*** MQTT ***/ + static struct mosquitto *mosq; -static modbus_t *modbus; +static bool mqtt_connected; + +static void mqtt_error(const char *operation, int err, bool teardown) +{ + msg(L_ERROR, "Mosquitto: %s failed: error %d", operation, err); + + if (teardown) { + mosquitto_destroy(mosq); + mosq = NULL; + mqtt_connected = false; + } else if (err == MOSQ_ERR_NO_CONN || err == MOSQ_ERR_CONN_REFUSED || err == MOSQ_ERR_CONN_LOST) { + mqtt_connected = false; + } +} 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"); + + 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) + mqtt_error("publish", err, false); + } + va_end(args); } -static void mqtt_setup(void) +static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int level UNUSED, const char *message UNUSED) { - if (mosquitto_subscribe(mosq, NULL, "burrow/air/#", 1) != MOSQ_ERR_SUCCESS) - die("Mosquitto: subscribe failed"); + // 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); + +static bool mqtt_connect(void) +{ + int err; + + if (mqtt_connected) + return true; + + if (!mosq) { + mosq = mosquitto_new("aircond", 1, NULL); + if (!mosq) + die("Mosquitto: initialization failed"); + + mosquitto_log_callback_set(mosq, mqtt_log_callback); + mosquitto_message_callback_set(mosq, mqtt_msg_callback); + + err = mosquitto_will_set(mosq, "status/aircon", 4, "dead", 0, true); + if (err != MOSQ_ERR_SUCCESS) { + mqtt_error("will_set", err, true); + return false; + } + + err = mosquitto_connect(mosq, "127.0.0.1", 1883, 60); + if (err != MOSQ_ERR_SUCCESS) { + mqtt_error("connect", err, true); + return false; + } + } else { + err = mosquitto_reconnect(mosq); + if (err != MOSQ_ERR_SUCCESS) { + mqtt_error("reconnect", err, false); + return false; + } + } + + err = mosquitto_subscribe(mosq, NULL, "burrow/air/#", 1); + if (err != MOSQ_ERR_SUCCESS) { + mqtt_error("subscribe", err, false); + return false; + } + + mqtt_connected = true; mqtt_publish("status/aircon", "ok"); + + return mqtt_connected; } -static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int level, const char *message) +/*** MODBUS ***/ + +static modbus_t *modbus; +static bool mb_is_open; + +static void mb_error(const char *operation, bool need_close) { - // msg(L_INFO, "MQTT(%d): %s", level, message); + msg(L_ERROR, "MODBUS: %s failed: %s", operation, modbus_strerror(errno)); + + if (need_close && modbus) { + if (mb_is_open) { + modbus_close(modbus); + mb_is_open = false; + } + modbus_free(modbus); + modbus = NULL; + } } -static void mqtt_msg_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, const struct mosquitto_message *m) +static bool mb_connect(void) { - char val[256]; - if (m->payloadlen >= sizeof(val) - 1) { - msg(L_ERROR, "Invalid value for topic %s", m->topic); - return; + if (modbus) + return true; + + modbus = modbus_new_rtu("/dev/modbus-aircon", 19200, 'E', 8, 1); + if (!modbus) { + mb_error("open", true); + return false; } - memcpy(val, m->payload, m->payloadlen); - val[m->payloadlen] = 0; - msg(L_DEBUG, "MQTT < %s %s", m->topic, val); - if (!strcmp(m->topic, "burrow/air/bypass")) { - uint x; - const char *err; - if ((err = str_to_uint(&x, val, NULL, 10 | STN_WHOLE)) || x >= 2) { - msg(L_ERROR, "Received invalid bypass setting %s: %s", val, err); - return; - } - if (modbus_write_bit(modbus, AIRCON_COIL_EXCHANGER_BYPASS, x) < 0) - msg(L_ERROR, "Modbus write failed: %s", modbus_strerror(errno)); - } else if (!strcmp(m->topic, "burrow/air/exchanger-fan")) { - uint x; - const char *err; - if ((err = str_to_uint(&x, val, NULL, 10 | STN_WHOLE)) || x >= 256) { - msg(L_ERROR, "Received invalid exchanger fan setting %s: %s", val, err); - return; - } - if (modbus_write_register(modbus, AIRCON_HREG_EXCHANGER_FAN, x) < 0) - msg(L_ERROR, "Modbus write failed: %s", modbus_strerror(errno)); + modbus_set_slave(modbus, 42); + + if (modbus_connect(modbus) < 0) { + mb_error("connect", true); + return false; } + + mb_is_open = true; + return true; } +/*** Main loop ***/ + static void scan_temperatures(time_t now) { static const char * const temp_names[] = { @@ -93,7 +167,7 @@ static void scan_temperatures(time_t now) u16 regs[5]; if (modbus_read_input_registers(modbus, AIRCON_IREG_TEMP_FROM_INSIDE, 5, regs) < 0) { - msg(L_ERROR, "Modbus read failed: %s", modbus_strerror(errno)); + mb_error("read", true); return; } @@ -107,6 +181,45 @@ static void scan_temperatures(time_t now) } } +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_DEBUG, "MQTT < %s %s", m->topic, val); + + if (!strcmp(m->topic, "burrow/air/bypass")) { + uint x; + const char *err; + if ((err = str_to_uint(&x, val, NULL, 10 | STN_WHOLE)) || x >= 2) { + msg(L_ERROR, "Received invalid bypass setting %s: %s", val, err); + return; + } + if (modbus_write_bit(modbus, AIRCON_COIL_EXCHANGER_BYPASS, x) < 0) + mb_error("coil write", false); + } else if (!strcmp(m->topic, "burrow/air/exchanger-fan")) { + uint x; + const char *err; + if ((err = str_to_uint(&x, val, NULL, 10 | STN_WHOLE)) || x >= 256) { + msg(L_ERROR, "Received invalid exchanger fan setting %s: %s", val, err); + return; + } + if (modbus_write_register(modbus, AIRCON_HREG_EXCHANGER_FAN, x) < 0) + mb_error("fan register write", false); + } else if (!strcmp(m->topic, "burrow/air/aircon-remote")) { + if (strlen(val) != 1) { + msg(L_ERROR, "Received invalid aircon remote command %s", val); + return; + } + if (modbus_write_register(modbus, AIRCON_HREG_REMOTE_CONTROL, val[0]) < 0) + mb_error("remote control register write", false); + } +} + static int use_daemon; static int use_debug; @@ -135,43 +248,20 @@ int main(int argc UNUSED, char **argv) if (!use_debug) log_default_stream()->levels &= ~(1U << L_DEBUG); - // FIXME: Find the right device. Reconnect if needed. - modbus = modbus_new_rtu("/dev/ttyUSB1", 19200, 'E', 8, 1); - if (!modbus) - die("Unable to establish Modbus context"); - modbus_set_slave(modbus, 42); - if (modbus_connect(modbus) < 0) - die("Unable to connect to Modbus"); - mosquitto_lib_init(); - mosq = mosquitto_new("aircond", 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, "status/aircon", 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"); - - mqtt_setup(); time_t next_run = 0; for (;;) { + if (!mqtt_connect() || !mb_connect()) { + sleep(5); + continue; + } + time_t now = time(NULL); if (now < next_run) { int err = mosquitto_loop(mosq, (next_run - now) * 1000, 1); - if (err == MOSQ_ERR_NO_CONN) { - err = mosquitto_reconnect(mosq); - if (err == MOSQ_ERR_SUCCESS) - mqtt_setup(); - else - msg(L_ERROR, "Mosquitto: cannot reconnect, error %d", err); - } else if (err != MOSQ_ERR_SUCCESS) - msg(L_ERROR, "Mosquitto: loop returned error %d", err); + if (err != MOSQ_ERR_SUCCESS) + mqtt_error("loop", err, false); continue; }