#include "mqtt_publisher.h" #include "mqtt_client.h" #include "esp_log.h" #include "cJSON.h" #include #include #define TAG "mqtt_publisher" #define TOPIC_PREFIX "localiser/sensor" static esp_mqtt_client_handle_t s_client = NULL; static char s_sensor_id[32]; static EventGroupHandle_t s_evt = NULL; static mqtt_reconfigure_data_t s_reconfigure_data; const mqtt_reconfigure_data_t *mqtt_publisher_get_reconfigure_data(void) { return &s_reconfigure_data; } /* Pre-built topic strings */ static char s_topic_rssi[96]; static char s_topic_announce[96]; static char s_topic_cmd[96]; static void build_topics(void) { snprintf(s_topic_rssi, sizeof(s_topic_rssi), "%s/%s/rssi", TOPIC_PREFIX, s_sensor_id); snprintf(s_topic_announce, sizeof(s_topic_announce), "%s/%s/announce", TOPIC_PREFIX, s_sensor_id); snprintf(s_topic_cmd, sizeof(s_topic_cmd), "%s/%s/cmd", TOPIC_PREFIX, s_sensor_id); } static void handle_cmd(const char *data, int data_len) { char *buf = strndup(data, data_len); if (!buf) return; cJSON *root = cJSON_Parse(buf); free(buf); if (!root) return; cJSON *action = cJSON_GetObjectItemCaseSensitive(root, "action"); if (cJSON_IsString(action)) { const char *a = action->valuestring; if (strcmp(a, "calibrate_start") == 0) xEventGroupSetBits(s_evt, MQTT_CALIBRATE_START); else if (strcmp(a, "calibrate_stop") == 0) xEventGroupSetBits(s_evt, MQTT_CALIBRATE_STOP); else if (strcmp(a, "selected") == 0) xEventGroupSetBits(s_evt, MQTT_SELECTED_BIT); else if (strcmp(a, "deselected") == 0) xEventGroupSetBits(s_evt, MQTT_DESELECTED_BIT); else if (strcmp(a, "factory_reset") == 0) xEventGroupSetBits(s_evt, MQTT_FACTORY_RESET_BIT); else if (strcmp(a, "reconfigure_settings") == 0) { memset(&s_reconfigure_data, 0, sizeof(s_reconfigure_data)); cJSON *ssid_j = cJSON_GetObjectItemCaseSensitive(root, "ssid"); cJSON *pass_j = cJSON_GetObjectItemCaseSensitive(root, "password"); cJSON *host_j = cJSON_GetObjectItemCaseSensitive(root, "mqtt_host"); cJSON *port_j = cJSON_GetObjectItemCaseSensitive(root, "mqtt_port"); if (cJSON_IsString(ssid_j)) strncpy(s_reconfigure_data.ssid, ssid_j->valuestring, sizeof(s_reconfigure_data.ssid) - 1); if (cJSON_IsString(pass_j)) strncpy(s_reconfigure_data.password, pass_j->valuestring, sizeof(s_reconfigure_data.password) - 1); if (cJSON_IsString(host_j)) strncpy(s_reconfigure_data.mqtt_host, host_j->valuestring, sizeof(s_reconfigure_data.mqtt_host) - 1); if (cJSON_IsNumber(port_j)) s_reconfigure_data.mqtt_port = (uint16_t)port_j->valuedouble; xEventGroupSetBits(s_evt, MQTT_RECONFIGURE_SETTINGS_BIT); } else ESP_LOGW(TAG, "Unknown cmd action: %s", a); } cJSON_Delete(root); } static void mqtt_event_handler(void *arg, esp_event_base_t base, int32_t id, void *data) { esp_mqtt_event_handle_t event = (esp_mqtt_event_handle_t)data; switch ((esp_mqtt_event_id_t)id) { case MQTT_EVENT_CONNECTED: ESP_LOGI(TAG, "Connected to broker"); esp_mqtt_client_subscribe(s_client, s_topic_cmd, 1); xEventGroupSetBits(s_evt, MQTT_CONNECTED_BIT); mqtt_publisher_announce(); break; case MQTT_EVENT_DISCONNECTED: ESP_LOGW(TAG, "Disconnected from broker"); xEventGroupClearBits(s_evt, MQTT_CONNECTED_BIT); break; case MQTT_EVENT_DATA: if (strncmp(event->topic, s_topic_cmd, event->topic_len) == 0) { handle_cmd(event->data, event->data_len); } break; case MQTT_EVENT_ERROR: ESP_LOGE(TAG, "MQTT error"); break; default: break; } } esp_err_t mqtt_publisher_init(const char *sensor_id, const char *broker_uri, EventGroupHandle_t evt_group) { strncpy(s_sensor_id, sensor_id, sizeof(s_sensor_id) - 1); s_evt = evt_group; build_topics(); esp_mqtt_client_config_t cfg = { .broker.address.uri = broker_uri, .credentials.client_id = sensor_id, .session.keepalive = 30, .network.reconnect_timeout_ms = 5000, }; s_client = esp_mqtt_client_init(&cfg); if (!s_client) return ESP_FAIL; ESP_ERROR_CHECK(esp_mqtt_client_register_event( s_client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL)); return esp_mqtt_client_start(s_client); } void mqtt_publisher_announce(void) { if (!s_client) return; esp_mqtt_client_publish(s_client, s_topic_announce, "", 0, 1, 0); ESP_LOGI(TAG, "Announced on %s", s_topic_announce); } void mqtt_publisher_send_rssi(const char *tag_id, int8_t rssi) { if (!s_client) return; char payload[80]; int len = snprintf(payload, sizeof(payload), "{\"tag_id\":\"%s\",\"rssi\":%d}", tag_id, (int)rssi); esp_mqtt_client_publish(s_client, s_topic_rssi, payload, len, 1, 0); }