From bead487489e1c7054c56d309a94b4f3c57a83328 Mon Sep 17 00:00:00 2001 From: Luis Alves Date: Sat, 22 Jan 2022 23:03:10 +0000 Subject: [PATCH] mqtt: enhancements Added last will topic/msg Implemented MQTT messaging similar to Tasmota Added "power on state" Added more wifi led settings (relay, mqtt topic) --- src/config.c | 61 +++++++++++++++------- src/config.h | 8 +-- src/gpio.c | 18 ++++++- src/gpio.h | 5 +- src/hfeasy.h | 2 +- src/led.h | 13 +++-- src/mqttcli.c | 138 +++++++++++++++++++++++++++++++++++++++++--------- src/mqttcli.h | 2 +- src/relay.c | 30 +++++++++-- src/relay.h | 3 +- 10 files changed, 220 insertions(+), 60 deletions(-) diff --git a/src/config.c b/src/config.c index 82530cb..7cce06b 100644 --- a/src/config.c +++ b/src/config.c @@ -23,7 +23,7 @@ SOFTWARE. #include "hfeasy.h" -#define CONFIG_MAGIC_VER1 0xd4 +#define CONFIG_MAGIC_VER1 0xd3 #define CONFIG_OFFSET 0x00 #define CONFIG_SIZE (sizeof(struct hfeasy_config)) @@ -64,6 +64,26 @@ void USER_FUNC reboot(void) hftimer_start(reset_timer); } +static const char *config_page_save = + ""\ + ""\ + "HFeasy config v%d.%d"\ + "Saving config to flash. Please wait..."; + +static void USER_FUNC httpd_page_save(char *url, char *rsp) +{ + char tmp[50]; + int ret; + + ret = httpd_arg_find(url, "save", tmp); + if (ret > 0) { + config_save(); + reboot(); + } + sprintf(rsp, config_page_save, HFEASY_VERSION_MAJOR, HFEASY_VERSION_MINOR); +} + + static const char *config_page = "HFeasy config v%d.%d"\ "

HFeasy config page


"\ @@ -72,6 +92,7 @@ static const char *config_page = "Device"\ "GPIO"\ "Status"\ + "Upgrade"\ ""\ "
"\ "
Module"\ @@ -81,11 +102,14 @@ static const char *config_page = ""\ ""\ ""\ - ""\ - ""\ + ""\ + ""\ + ""\ + ""\ ""\ + "
Power on state (relay: 0=off, >0=on; dimmer: level)"\ "
"\ - "
"\ + "
"\ ""; static void USER_FUNC httpd_page_config(char *url, char *rsp) @@ -109,14 +133,13 @@ static void USER_FUNC httpd_page_config(char *url, char *rsp) ret = httpd_arg_find(url, "wifi_led", tmp); if (ret > 0) { state.cfg.wifi_led = atoi(tmp); - if (state.cfg.wifi_led > 4) + if (state.cfg.wifi_led >= LED_CONFIG_END) state.cfg.wifi_led = 0; } - ret = httpd_arg_find(url, "save", tmp); + ret = httpd_arg_find(url, "pwron", tmp); if (ret > 0) { - config_save(); - reboot(); + state.cfg.pwron_state = atoi(tmp); } sprintf(rsp, config_page, HFEASY_VERSION_MAJOR, HFEASY_VERSION_MINOR, @@ -125,7 +148,10 @@ static void USER_FUNC httpd_page_config(char *url, char *rsp) state.cfg.wifi_led == 1 ? "selected" : "", state.cfg.wifi_led == 2 ? "selected" : "", state.cfg.wifi_led == 3 ? "selected" : "", - state.cfg.wifi_led == 4 ? "selected" : "" + state.cfg.wifi_led == 4 ? "selected" : "", + state.cfg.wifi_led == 5 ? "selected" : "", + state.cfg.wifi_led == 6 ? "selected" : "", + state.cfg.pwron_state ); if (state.cfg.wifi_led == LED_CONFIG_FIND) { @@ -148,8 +174,8 @@ static const char *config_page_mqtt = "Server port (0=disabled)"\ "Username"\ "Password"\ - "Subscribe topic"\ - "Publish topic"\ + "Topic (%%topic%%)"\ + "Full topic"\ "QOS"\ "ON value"\ "OFF value"\ @@ -177,13 +203,13 @@ static void USER_FUNC httpd_page_config_mqtt(char *url, char *rsp) if (ret > 0) strcpy(state.cfg.mqtt_server_pass, tmp); - ret = httpd_arg_find(url, "sub_topic", tmp); + ret = httpd_arg_find(url, "topic", tmp); if (ret > 0) - strcpy(state.cfg.mqtt_sub_topic, tmp); + strcpy(state.cfg.mqtt_topic, tmp); - ret = httpd_arg_find(url, "pub_topic", tmp); + ret = httpd_arg_find(url, "full_topic", tmp); if (ret > 0) - strcpy(state.cfg.mqtt_pub_topic, tmp); + strcpy(state.cfg.mqtt_full_topic, tmp); ret = httpd_arg_find(url, "qos", tmp); if (ret > 0) @@ -207,7 +233,7 @@ static void USER_FUNC httpd_page_config_mqtt(char *url, char *rsp) sprintf(rsp, config_page_mqtt, HFEASY_VERSION_MAJOR, HFEASY_VERSION_MINOR, state.cfg.mqtt_server_hostname, state.cfg.mqtt_server_port, state.cfg.mqtt_server_user, state.cfg.mqtt_server_pass, - state.cfg.mqtt_sub_topic, state.cfg.mqtt_pub_topic, state.cfg.mqtt_qos, + state.cfg.mqtt_topic, state.cfg.mqtt_full_topic, state.cfg.mqtt_qos, state.cfg.mqtt_on_value, state.cfg.mqtt_off_value); u_printf("page_size=%d\r\n", strlen(rsp)); @@ -430,7 +456,7 @@ static const char *status_page = ""; -const char *relay_change_src[] = { "HTTP", "MQTT", "TIMER", "SWITCH" }; +const char *relay_change_src[] = { "HTTP", "MQTT", "TIMER", "SWITCH", "SWUP", "SWDOWN", "POWER ON" }; static void USER_FUNC httpd_page_status(char *url, char *rsp) { @@ -704,4 +730,5 @@ void USER_FUNC config_init(void) httpd_add_page("/config_gpio", httpd_page_config_gpio); httpd_add_page("/status", httpd_page_status); httpd_add_page("/log", httpd_page_log); + httpd_add_page("/save", httpd_page_save); } diff --git a/src/config.h b/src/config.h index 00bef88..f09ecd1 100644 --- a/src/config.h +++ b/src/config.h @@ -37,8 +37,8 @@ struct hfeasy_config { uint16_t mqtt_server_port; char mqtt_server_user[30]; char mqtt_server_pass[30]; - char mqtt_sub_topic[30]; - char mqtt_pub_topic[30]; + char mqtt_topic[30]; + char mqtt_full_topic[50]; uint8_t mqtt_qos; char mqtt_on_value[10]; char mqtt_off_value[10]; @@ -59,6 +59,8 @@ struct hfeasy_config { //time_t timer_on[CONFIG_MAX_TIMERS], timer_off[CONFIG_MAX_TIMERS]; + uint8_t pwron_state; + uint32_t log_ptr; }; @@ -89,8 +91,6 @@ enum { DEVICE_CUSTOM }; - - void USER_FUNC config_init(void); void USER_FUNC config_save(void); void USER_FUNC reboot(void); diff --git a/src/gpio.c b/src/gpio.c index 5f53461..c653167 100644 --- a/src/gpio.c +++ b/src/gpio.c @@ -271,6 +271,18 @@ int USER_FUNC gpio_i2c_recv(uint8_t addr, uint8_t *data) #endif +void USER_FUNC dimmer_publish_state(void) +{ + struct hfeasy_state *state = config_get_state(); + char buf[10]; + uint8_t lvl; + + lvl = state->dimmer_level; + + sprintf(buf, "%d", lvl); + mqttcli_publish(buf, "dimmer"); +} + void USER_FUNC gpio_set_dimmer(uint8_t lvl, uint8_t publish, uint8_t source) { struct hfeasy_state *state = config_get_state(); @@ -294,7 +306,6 @@ void USER_FUNC gpio_set_dimmer(uint8_t lvl, uint8_t publish, uint8_t source) state->relay_state = 1; - changed = (old_rs != state->relay_state) || ((lvl > 0) && (lvl != state->dimmer_level)); if (state->relay_state == 0) { @@ -314,7 +325,7 @@ void USER_FUNC gpio_set_dimmer(uint8_t lvl, uint8_t publish, uint8_t source) if (publish && changed) { sprintf(buf, "%d", lvl); - mqttcli_publish(buf); + mqttcli_publish(buf, "dimmer"); } } @@ -625,6 +636,9 @@ void USER_FUNC gpio_init(void) hfgpio_configure_fpin(GPIO_I2C_SDA, HFPIO_DEFAULT | HFM_IO_TYPE_INPUT); state->dimmer_level = 0x80; + + gpio_set_dimmer(state->cfg.pwron_state, 1, RELAY_SRC_POWERON); + } debounce_timer = hftimer_create("debouncer", 50, false, HFTIMER_ID_DEBOUNCE, debounce_timer_handler, 0); diff --git a/src/gpio.h b/src/gpio.h index 9cf25c4..64fb16c 100644 --- a/src/gpio.h +++ b/src/gpio.h @@ -17,9 +17,10 @@ enum { void USER_FUNC gpio_init(void); -void USER_FUNC gpio_set_dimmer(uint8_t lvl, uint8_t publish, uint8_t source); int USER_FUNC gpio_get_state(int fid); - int *gpio_pin(int n); +void USER_FUNC gpio_set_dimmer(uint8_t lvl, uint8_t publish, uint8_t source); +void USER_FUNC dimmer_publish_state(void); + #endif diff --git a/src/hfeasy.h b/src/hfeasy.h index 8d45913..8b4f10b 100644 --- a/src/hfeasy.h +++ b/src/hfeasy.h @@ -19,7 +19,7 @@ #define HFEASY_VERSION_MAJOR 0 -#define HFEASY_VERSION_MINOR 8 +#define HFEASY_VERSION_MINOR 9 enum { diff --git a/src/led.h b/src/led.h index 23af878..435c324 100644 --- a/src/led.h +++ b/src/led.h @@ -4,11 +4,14 @@ #include enum { - LED_CONFIG_OFF = 0, - LED_CONFIG_MQTT = 1, - LED_CONFIG_HTTP = 2, - LED_CONFIG_ALL = 3, - LED_CONFIG_FIND = 4, + LED_CONFIG_OFF = 0, + LED_CONFIG_MQTT = 1, + LED_CONFIG_HTTP = 2, + LED_CONFIG_ALL = 3, + LED_CONFIG_RELAY = 4, + LED_CONFIG_TOPIC = 5, + LED_CONFIG_FIND = 6, + LED_CONFIG_END }; enum { diff --git a/src/mqttcli.c b/src/mqttcli.c index 8ead4a0..5ea7419 100644 --- a/src/mqttcli.c +++ b/src/mqttcli.c @@ -50,12 +50,46 @@ static uint8_t *txbuf; /* should be large enough to hold multiple whole mqtt mes static uint8_t *rxbuf; /* should be large enough any whole mqtt message expected to be received */ + +int USER_FUNC mqttcli_strsub(char *str, char *what, char *with) +{ + char tmp[100], *p, i; + p = strstr(str, what); + if (p == NULL) + return -1; + + i = p-str; + memcpy(tmp, str, i); + strcpy(tmp + i, with); + strcpy(tmp + i + strlen(with), str + i + strlen(what)); + strcpy(str, tmp); + return 0; +} + +void USER_FUNC mqttcli_get_topic(char *topic, char *prefix, char *sufix) +{ + struct hfeasy_state *state = config_get_state(); + + /* get full topic */ + strcpy(topic, state->cfg.mqtt_full_topic); + + /* replace %prefix% */ + mqttcli_strsub(topic, "%prefix%", prefix); + + /* replace %topic% */ + mqttcli_strsub(topic, "%topic%", state->cfg.mqtt_topic); + + /* add suffix */ + strcat(topic, sufix); +} + + void publish_callback(void** unused, struct mqtt_response_publish *published) { struct hfeasy_state *state = config_get_state(); struct hfeasy_config *cfg = &state->cfg; - uint8_t publish; - + char t[100]; + char *topic_name = (char*) hfmem_malloc(published->topic_name_size + 1); char *msg = (char*) hfmem_malloc(published->application_message_size + 1); @@ -65,28 +99,62 @@ void publish_callback(void** unused, struct mqtt_response_publish *published) msg[published->application_message_size] = '\0'; u_printf("Received publish('%s'): %s\n", topic_name, msg); - if (strcmp(topic_name, cfg->mqtt_sub_topic) == 0) { - if (state->cfg.wifi_led & LED_CONFIG_MQTT) - led_ctrl("n1f"); /* got data = 1 blink */ - - publish = strcmp(cfg->mqtt_sub_topic, cfg->mqtt_pub_topic); + /* dimmer is setup */ + if (*gpio_pin(GPIO_I2C_SCL) != HFM_NOPIN) { + mqttcli_get_topic(t, "cmnd", "dimmer"); + if (strcmp(topic_name, t) == 0) { + if (state->cfg.wifi_led & LED_CONFIG_MQTT) + led_ctrl("n1f"); /* got data = 1 blink */ - if (*gpio_pin(GPIO_I2C_SCL) != HFM_NOPIN) { int lvl = atoi(msg); - gpio_set_dimmer(lvl, publish, RELAY_SRC_MQTT); + gpio_set_dimmer(lvl, 1, RELAY_SRC_MQTT); } - - if (*gpio_pin(GPIO_RELAY) != HFM_NOPIN) { + mqttcli_get_topic(t, "cmnd", "power"); + if (strcmp(topic_name, t) == 0) { + if (state->cfg.wifi_led & LED_CONFIG_MQTT) + led_ctrl("n1f"); /* got data = 1 blink */ + + /* set dimmer on/off */ if (strcmp(cfg->mqtt_on_value, msg) == 0) { if (state->relay_state != 1) - gpio_set_relay(1, publish, RELAY_SRC_MQTT); + gpio_set_dimmer(0xff, 1, RELAY_SRC_MQTT); } else if (strcmp(cfg->mqtt_off_value, msg) == 0) { if (state->relay_state != 0) - gpio_set_relay(0, publish, RELAY_SRC_MQTT); + gpio_set_dimmer(0, 1, RELAY_SRC_MQTT); } } + } + + /* relay is setup */ + if (*gpio_pin(GPIO_RELAY) != HFM_NOPIN) { + mqttcli_get_topic(t, "cmnd", "power"); + if (strcmp(topic_name, t) == 0) { + if (state->cfg.wifi_led & LED_CONFIG_MQTT) + led_ctrl("n1f"); /* got data = 1 blink */ + if (strcmp(cfg->mqtt_on_value, msg) == 0) { + if (state->relay_state != 1) + gpio_set_relay(1, 1, RELAY_SRC_MQTT); + } else if (strcmp(cfg->mqtt_off_value, msg) == 0) { + if (state->relay_state != 0) + gpio_set_relay(0, 1, RELAY_SRC_MQTT); + } + } } + + /* led */ + if ((*gpio_pin(GPIO_LED_WIFI) != HFM_NOPIN) && + (state->cfg.wifi_led == LED_CONFIG_TOPIC)) { + mqttcli_get_topic(t, "cmnd", "led"); + if (strcmp(topic_name, t) == 0) { + if (strcmp(cfg->mqtt_on_value, msg) == 0) { + led_ctrl("n"); + } else if (strcmp(cfg->mqtt_off_value, msg) == 0) { + led_ctrl("f"); + } + } + } + hfmem_free(topic_name); hfmem_free(msg); } @@ -150,6 +218,9 @@ static void USER_FUNC mqttcli_thread(void* client) struct hfeasy_state *state = config_get_state(); struct mqtt_client *c = (struct mqtt_client*) client; static uint8_t STATE = 0; + char topic[100]; + char lwm[10]; + while(1) { //u_printf("mqttcli_thread STATE=%d\r\n", STATE); @@ -186,17 +257,30 @@ static void USER_FUNC mqttcli_thread(void* client) char *pass = strlen(state->cfg.mqtt_server_pass) == 0 ? NULL : state->cfg.mqtt_server_pass; /* connected, init mqtt */ mqtt_init(client, fd, txbuf, MQTT_TX_BUF_SIZE, rxbuf, MQTT_RX_BUF_SIZE, publish_callback); - mqtt_connect(client, state->mac_addr_s, NULL, NULL, 0, user, pass, 0, 400); + + mqttcli_get_topic(topic, "tele", "LWT"); + strcpy(lwm, "Online"); + + mqtt_connect(client, state->mac_addr_s, topic, lwm, strlen(lwm), user, pass, MQTT_CONNECT_WILL_RETAIN, 60); if (c->error != MQTT_OK) { u_printf("error: %s\n", mqtt_error_str(c->error)); STATE = MQTTCLI_STATE_DISCONNECT; } else { STATE = MQTTCLI_STATE_RUN; state->mqtt_ready = 1; - if (strlen(state->cfg.mqtt_sub_topic) > 0) { - u_printf("mqtt subscribe to '%s'\r\n", state->cfg.mqtt_sub_topic); - mqtt_subscribe(&mqttcli, state->cfg.mqtt_sub_topic, state->cfg.mqtt_qos); - } + + mqttcli_get_topic(topic, "cmnd", "#"); + + //u_printf("mqtt subscribe to '%s'\r\n", state->cfg.mqtt_sub_topic); + mqtt_subscribe(&mqttcli, topic, state->cfg.mqtt_qos); + + + /* publish current state */ + if (*gpio_pin(GPIO_RELAY) != HFM_NOPIN) + relay_publish_state(); + if (*gpio_pin(GPIO_I2C_SCL) != HFM_NOPIN) + dimmer_publish_state(); + } } else { msleep(1000); @@ -243,10 +327,14 @@ static void USER_FUNC mqttcli_thread(void* client) } } -void USER_FUNC mqttcli_publish(char *value) + + +void USER_FUNC mqttcli_publish(char *value, char *sufix) { struct hfeasy_state *state = config_get_state(); + char topic[100]; uint8_t flags; + switch (state->cfg.mqtt_qos) { default: case 0: @@ -259,8 +347,10 @@ void USER_FUNC mqttcli_publish(char *value) flags = MQTT_PUBLISH_QOS_2; break; } + if (state->mqtt_ready) { - mqtt_publish(&mqttcli, state->cfg.mqtt_pub_topic, value, strlen(value), flags); + mqttcli_get_topic(topic, "stat", sufix); + mqtt_publish(&mqttcli, topic, value, strlen(value), flags); if (state->cfg.wifi_led & LED_CONFIG_MQTT) led_ctrl("n1f1n1f"); /* publish = 2 blinks */ } @@ -271,10 +361,10 @@ void USER_FUNC mqttcli_initcfg(void) { struct hfeasy_state *state = config_get_state(); - sprintf(state->cfg.mqtt_pub_topic, "hf%s", state->mac_addr_s); - sprintf(state->cfg.mqtt_sub_topic, "hf%s", state->mac_addr_s); - sprintf(state->cfg.mqtt_on_value, "1"); - sprintf(state->cfg.mqtt_off_value, "0"); + sprintf(state->cfg.mqtt_topic, "hfeasy_%s", &state->mac_addr_s[6]); + sprintf(state->cfg.mqtt_full_topic, "%%prefix%%/%%topic%%/"); + sprintf(state->cfg.mqtt_on_value, "ON"); + sprintf(state->cfg.mqtt_off_value, "OFF"); } diff --git a/src/mqttcli.h b/src/mqttcli.h index 030e930..fb8ed44 100644 --- a/src/mqttcli.h +++ b/src/mqttcli.h @@ -5,6 +5,6 @@ void USER_FUNC mqttcli_init(void); void USER_FUNC mqttcli_initcfg(void); -void USER_FUNC mqttcli_publish(char *value); +void USER_FUNC mqttcli_publish(char *value, char *sufix); #endif diff --git a/src/relay.c b/src/relay.c index 328d339..e817ca6 100644 --- a/src/relay.c +++ b/src/relay.c @@ -3,8 +3,18 @@ void USER_FUNC relay_init(void) { - if (*gpio_pin(GPIO_RELAY) != HFM_NOPIN) - hfgpio_configure_fpin(GPIO_RELAY, HFM_IO_OUTPUT_0); + struct hfeasy_state *state = config_get_state(); + if (*gpio_pin(GPIO_RELAY) == HFM_NOPIN) + return; + + hfgpio_configure_fpin(GPIO_RELAY, HFM_IO_OUTPUT_0); + + if (state->cfg.pwron_state) + gpio_set_relay(RELAY_ON, 1, RELAY_SRC_POWERON); + else + gpio_set_relay(RELAY_OFF, 1, RELAY_SRC_POWERON); + + } void USER_FUNC relay_deinit(void) @@ -14,6 +24,14 @@ void USER_FUNC relay_deinit(void) } +void USER_FUNC relay_publish_state(void) +{ + struct hfeasy_state *state = config_get_state(); + char *val = state->relay_state ? state->cfg.mqtt_on_value : state->cfg.mqtt_off_value; + mqttcli_publish(val, "power"); +} + + void USER_FUNC gpio_set_relay(uint8_t action, uint8_t publish, uint8_t source) { struct hfeasy_state *state = config_get_state(); @@ -43,12 +61,18 @@ void USER_FUNC gpio_set_relay(uint8_t action, uint8_t publish, uint8_t source) /* set gpio */ if (state->relay_state) { + if (state->cfg.wifi_led == LED_CONFIG_RELAY) + led_ctrl("n"); + hfgpio_fset_out_high(GPIO_RELAY); val = state->cfg.mqtt_on_value; } else { + if (state->cfg.wifi_led == LED_CONFIG_RELAY) + led_ctrl("f"); + hfgpio_fset_out_low(GPIO_RELAY); val = state->cfg.mqtt_off_value; } if (publish && changed) - mqttcli_publish(val); + mqttcli_publish(val, "power"); } diff --git a/src/relay.h b/src/relay.h index 23a5f04..96af814 100644 --- a/src/relay.h +++ b/src/relay.h @@ -15,11 +15,12 @@ #define RELAY_SRC_SWITCH 3 #define RELAY_SRC_SWITCH_UP 4 #define RELAY_SRC_SWITCH_DN 5 +#define RELAY_SRC_POWERON 6 void USER_FUNC relay_init(void); void USER_FUNC relay_deinit(void); void USER_FUNC gpio_set_relay(uint8_t action, uint8_t publish, uint8_t source); - +void USER_FUNC relay_publish_state(void); #endif