2 * A MQTT Gateway Daemon for the Power Meter
4 * (c) 2019 Martin Mares <mj@ucw.cz>
10 #include <ucw/strtonum.h>
11 #include <ucw/stkstring.h>
22 #include <mosquitto.h>
26 static struct mosquitto *mosq;
27 static bool mqtt_connected;
29 static void mqtt_error(const char *operation, int err, bool teardown)
31 msg(L_ERROR, "Mosquitto: %s failed: error %d", operation, err);
34 mosquitto_destroy(mosq);
36 mqtt_connected = false;
37 } else if (err == MOSQ_ERR_NO_CONN || err == MOSQ_ERR_CONN_REFUSED || err == MOSQ_ERR_CONN_LOST) {
38 mqtt_connected = false;
42 static void mqtt_publish(const char *topic, const char *fmt, ...)
49 int l = vsnprintf(m, sizeof(m), fmt, args);
50 msg(L_DEBUG, "MQTT > %s %s", topic, m);
51 int err = mosquitto_publish(mosq, NULL, topic, l, m, 0, true);
52 if (err != MOSQ_ERR_SUCCESS)
53 mqtt_error("publish", err, false);
59 static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int level, const char *message)
61 // msg(L_INFO, "MQTT(%d): %s", level, message);
64 static bool mqtt_connect(void)
72 mosq = mosquitto_new("powerd", 1, NULL);
74 die("Mosquitto: initialization failed");
76 mosquitto_log_callback_set(mosq, mqtt_log_callback);
78 err = mosquitto_will_set(mosq, "status/power-meter", 4, "dead", 0, true);
79 if (err != MOSQ_ERR_SUCCESS) {
80 mqtt_error("will_set", err, true);
84 err = mosquitto_connect(mosq, "127.0.0.1", 1883, 60);
85 if (err != MOSQ_ERR_SUCCESS) {
86 mqtt_error("connect", err, true);
90 err = mosquitto_reconnect(mosq);
91 if (err != MOSQ_ERR_SUCCESS) {
92 mqtt_error("reconnect", err, false);
97 mqtt_connected = true;
99 mqtt_publish("status/power-meter", "ok");
101 return mqtt_connected;
106 static modbus_t *modbus;
107 static bool mb_is_open;
109 static void mb_error(const char *operation)
111 msg(L_ERROR, "MODBUS: %s failed: %s", operation, modbus_strerror(errno));
115 modbus_close(modbus);
123 static bool mb_connect(void)
128 modbus = modbus_new_rtu("/dev/modbus-power", 9600, 'N', 8, 1);
134 modbus_set_slave(modbus, 42);
136 if (modbus_connect(modbus) < 0) {
146 static u16 mb_regs[22];
150 if (mb_regs[i+1] < 0x8000)
151 return (mb_regs[i+1] << 16) | mb_regs[i];
153 return ((mb_regs[i+1] - 65536) * 65536) + mb_regs[i];
156 static void scan_power_meter(time_t now)
160 if (modbus_read_registers(modbus, 0, 22, mb_regs) < 0) {
165 mqtt_publish("burrow/power/voltage/l1n", "%.1f %ld", get_s32(0) / 10., lnow);
166 mqtt_publish("burrow/power/voltage/l2n", "%.1f %ld", get_s32(2) / 10., lnow);
167 mqtt_publish("burrow/power/voltage/l3n", "%.1f %ld", get_s32(4) / 10., lnow);
169 mqtt_publish("burrow/power/current/l1", "%.3f %ld", get_s32(6) / 1000., lnow);
170 mqtt_publish("burrow/power/current/l2", "%.3f %ld", get_s32(8) / 1000., lnow);
171 mqtt_publish("burrow/power/current/l3", "%.3f %ld", get_s32(10) / 1000., lnow);
173 mqtt_publish("burrow/power/power", "%.1f %ld", get_s32(12) / 10., lnow);
174 mqtt_publish("burrow/power/energy", "%.1f %ld", get_s32(14) / 10., lnow);
176 mqtt_publish("burrow/power/reactive/power", "%.1f %ld", get_s32(18) / 10., lnow);
177 mqtt_publish("burrow/power/reactive/energy", "%.1f %ld", get_s32(20) / 10., lnow);
180 static int use_daemon;
181 static int use_debug;
183 static struct opt_section options = {
185 OPT_HELP("A daemon for sending power meter readings to MQTT"),
187 OPT_HELP("Options:"),
188 OPT_BOOL('d', "debug", use_debug, 0, "\tLog debugging messages"),
189 OPT_BOOL(0, "daemon", use_daemon, 0, "\tDaemonize"),
196 int main(int argc UNUSED, char **argv)
199 opt_parse(&options, argv+1);
202 struct log_stream *ls = log_new_syslog("daemon", LOG_PID);
203 log_set_default_stream(ls);
206 log_default_stream()->levels &= ~(1U << L_DEBUG);
208 mosquitto_lib_init();
212 if (!mqtt_connect() || !mb_connect()) {
217 time_t now = time(NULL);
218 if (now < next_run) {
219 int err = mosquitto_loop(mosq, (next_run - now) * 1000, 1);
220 if (err != MOSQ_ERR_SUCCESS)
221 mqtt_error("loop", err, false);
226 scan_power_meter(now);