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>
23 #include <mosquitto.h>
27 static struct mosquitto *mosq;
28 static bool mqtt_connected;
30 static void mqtt_error(const char *operation, int err, bool teardown)
32 msg(L_ERROR, "Mosquitto: %s failed: error %d", operation, err);
35 mosquitto_destroy(mosq);
37 mqtt_connected = false;
38 } else if (err == MOSQ_ERR_NO_CONN || err == MOSQ_ERR_CONN_REFUSED || err == MOSQ_ERR_CONN_LOST) {
39 mqtt_connected = false;
43 static void mqtt_publish(const char *topic, const char *fmt, ...)
50 int l = vsnprintf(m, sizeof(m), fmt, args);
51 msg(L_DEBUG, "MQTT > %s %s", topic, m);
52 int err = mosquitto_publish(mosq, NULL, topic, l, m, 0, true);
53 if (err != MOSQ_ERR_SUCCESS)
54 mqtt_error("publish", err, false);
60 static void mqtt_log_callback(struct mosquitto *mosq UNUSED, void *obj UNUSED, int level UNUSED, const char *message UNUSED)
62 // msg(L_INFO, "MQTT(%d): %s", level, message);
65 static bool mqtt_connect(void)
73 mosq = mosquitto_new("powerd", 1, NULL);
75 die("Mosquitto: initialization failed");
77 mosquitto_log_callback_set(mosq, mqtt_log_callback);
79 err = mosquitto_will_set(mosq, "status/power-meter", 4, "dead", 0, true);
80 if (err != MOSQ_ERR_SUCCESS) {
81 mqtt_error("will_set", err, true);
85 err = mosquitto_connect(mosq, "127.0.0.1", 1883, 60);
86 if (err != MOSQ_ERR_SUCCESS) {
87 mqtt_error("connect", err, true);
91 err = mosquitto_reconnect(mosq);
92 if (err != MOSQ_ERR_SUCCESS) {
93 mqtt_error("reconnect", err, false);
98 mqtt_connected = true;
100 mqtt_publish("status/power-meter", "ok");
102 return mqtt_connected;
107 static modbus_t *modbus;
108 static bool mb_is_open;
110 static void mb_error(const char *operation)
112 msg(L_ERROR, "MODBUS: %s failed: %s", operation, modbus_strerror(errno));
116 modbus_close(modbus);
124 static bool mb_connect(void)
129 modbus = modbus_new_rtu("/dev/modbus-power", 9600, 'N', 8, 1);
135 modbus_set_slave(modbus, 42);
137 if (modbus_connect(modbus) < 0) {
148 static u16 mb_regs[22];
150 static s32 get_s32(uint i)
152 if (mb_regs[i+1] < 0x8000)
153 return (mb_regs[i+1] << 16) | mb_regs[i];
155 return ((mb_regs[i+1] - 65536) * 65536) + mb_regs[i];
158 static void scan_power_meter(time_t now)
162 if (modbus_read_registers(modbus, 0, 22, mb_regs) < 0) {
167 mqtt_publish("burrow/power/voltage/l1n", "%.1f %ld", get_s32(0) / 10., lnow);
168 mqtt_publish("burrow/power/voltage/l2n", "%.1f %ld", get_s32(2) / 10., lnow);
169 mqtt_publish("burrow/power/voltage/l3n", "%.1f %ld", get_s32(4) / 10., lnow);
171 mqtt_publish("burrow/power/current/l1", "%.3f %ld", get_s32(6) / 1000., lnow);
172 mqtt_publish("burrow/power/current/l2", "%.3f %ld", get_s32(8) / 1000., lnow);
173 mqtt_publish("burrow/power/current/l3", "%.3f %ld", get_s32(10) / 1000., lnow);
175 mqtt_publish("burrow/power/power", "%.1f %ld", get_s32(12) / 10., lnow);
176 mqtt_publish("burrow/power/energy", "%.1f %ld", get_s32(14) / 10., lnow);
178 mqtt_publish("burrow/power/reactive/power", "%.1f %ld", get_s32(18) / 10., lnow);
179 mqtt_publish("burrow/power/reactive/energy", "%.1f %ld", get_s32(20) / 10., lnow);
182 static int use_daemon;
183 static int use_debug;
185 static struct opt_section options = {
187 OPT_HELP("A daemon for sending power meter readings to MQTT"),
189 OPT_HELP("Options:"),
190 OPT_BOOL('d', "debug", use_debug, 0, "\tLog debugging messages"),
191 OPT_BOOL(0, "daemon", use_daemon, 0, "\tDaemonize"),
198 int main(int argc UNUSED, char **argv)
201 opt_parse(&options, argv+1);
204 struct log_stream *ls = log_new_syslog("daemon", LOG_PID);
205 log_set_default_stream(ls);
208 log_default_stream()->levels &= ~(1U << L_DEBUG);
210 mosquitto_lib_init();
214 if (!mqtt_connect() || !mb_connect()) {
219 time_t now = time(NULL);
220 if (now < next_run) {
221 int err = mosquitto_loop(mosq, (next_run - now) * 1000, 1);
222 if (err != MOSQ_ERR_SUCCESS)
223 mqtt_error("loop", err, false);
228 scan_power_meter(now);