184 lines
6.9 KiB
C
184 lines
6.9 KiB
C
#include "mqtt_publisher.h"
|
|
#include "mqtt_client.h"
|
|
#include "esp_log.h"
|
|
#include "cJSON.h"
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
|
|
#define TAG "mqtt_publisher"
|
|
#define TOPIC_PREFIX "localiser/sensor"
|
|
#define TOPIC_OTA_BROADCAST "localiser/ota"
|
|
|
|
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;
|
|
static mqtt_ota_data_t s_ota_data;
|
|
|
|
const mqtt_reconfigure_data_t *mqtt_publisher_get_reconfigure_data(void)
|
|
{
|
|
return &s_reconfigure_data;
|
|
}
|
|
|
|
const mqtt_ota_data_t *mqtt_publisher_get_ota_data(void)
|
|
{
|
|
return &s_ota_data;
|
|
}
|
|
|
|
/* Pre-built topic strings */
|
|
static char s_topic_rssi[96];
|
|
static char s_topic_announce[96];
|
|
static char s_topic_cmd[96];
|
|
static char s_topic_ota[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);
|
|
snprintf(s_topic_ota, sizeof(s_topic_ota),
|
|
"%s/%s/ota", 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, "ota") == 0) {
|
|
memset(&s_ota_data, 0, sizeof(s_ota_data));
|
|
cJSON *url_j = cJSON_GetObjectItemCaseSensitive(root, "url");
|
|
cJSON *ver_j = cJSON_GetObjectItemCaseSensitive(root, "version");
|
|
if (cJSON_IsString(url_j)) strncpy(s_ota_data.url, url_j->valuestring, sizeof(s_ota_data.url) - 1);
|
|
if (cJSON_IsString(ver_j)) strncpy(s_ota_data.version, ver_j->valuestring, sizeof(s_ota_data.version) - 1);
|
|
if (s_ota_data.url[0] != '\0') xEventGroupSetBits(s_evt, MQTT_OTA_BIT);
|
|
else ESP_LOGW(TAG, "OTA cmd missing url");
|
|
}
|
|
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);
|
|
esp_mqtt_client_subscribe(s_client, TOPIC_OTA_BROADCAST, 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 ||
|
|
strncmp(event->topic, TOPIC_OTA_BROADCAST, 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);
|
|
}
|
|
|
|
void mqtt_publisher_send_beacon(const char *type, const char *id, int8_t tx_power, int8_t rssi)
|
|
{
|
|
if (!s_client) return;
|
|
|
|
char payload[128];
|
|
int len = snprintf(payload, sizeof(payload),
|
|
"{\"type\":\"%s\",\"id\":\"%s\",\"tx_power\":%d,\"rssi\":%d}",
|
|
type, id, (int)tx_power, (int)rssi);
|
|
esp_mqtt_client_publish(s_client, s_topic_rssi, payload, len, 1, 0);
|
|
}
|
|
|
|
void mqtt_publisher_send_ota_status(const char *status, const char *version)
|
|
{
|
|
if (!s_client) return;
|
|
|
|
char payload[96];
|
|
int len = snprintf(payload, sizeof(payload),
|
|
"{\"status\":\"%s\",\"version\":\"%s\"}", status, version ? version : "");
|
|
esp_mqtt_client_publish(s_client, s_topic_ota, payload, len, 1, 0);
|
|
}
|