200 lines
6.0 KiB
C
200 lines
6.0 KiB
C
// ha_mqtt.c
|
|
|
|
#include "ha_mqtt.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "lwip/apps/mqtt.h"
|
|
#include "lwip/ip4_addr.h"
|
|
#include "lwip/netif.h"
|
|
#include "systick.h"
|
|
|
|
// config
|
|
#define MQTT_BROKER_IP_A 192
|
|
#define MQTT_BROKER_IP_B 168
|
|
#define MQTT_BROKER_IP_C 102
|
|
#define MQTT_BROKER_IP_D 1
|
|
#define MQTT_BROKER_PORT 1883
|
|
#define MQTT_CLIENT_ID "gxht30_sensor_node"
|
|
#define MQTT_RECONNECT_INTERVAL_MS 5000
|
|
|
|
#define DEVICE_UNIQUE_ID "gxht30_board_01"
|
|
#define DEVICE_NAME "Office Climate Sensor"
|
|
#define DEVICE_MANUFACTURER "me"
|
|
#define DEVICE_MODEL "GXHT30-ETH-v1"
|
|
|
|
// home assistant topics
|
|
#define HA_DISCOVERY_PREFIX "homeassistant"
|
|
|
|
#define TEMP_UNIQUE_ID DEVICE_UNIQUE_ID "_temperature"
|
|
#define TEMP_CONFIG_TOPIC \
|
|
HA_DISCOVERY_PREFIX "/sensor/" TEMP_UNIQUE_ID "/config"
|
|
#define TEMP_STATE_TOPIC "devices/" DEVICE_UNIQUE_ID "/temperature/state"
|
|
#define TEMP_NAME "Temperature"
|
|
|
|
#define HUM_UNIQUE_ID DEVICE_UNIQUE_ID "_humidity"
|
|
#define HUM_CONFIG_TOPIC HA_DISCOVERY_PREFIX "/sensor/" HUM_UNIQUE_ID "/config"
|
|
#define HUM_STATE_TOPIC "devices/" DEVICE_UNIQUE_ID "/humidity/state"
|
|
#define HUM_NAME "Humidity"
|
|
|
|
#define MQTT_RECONNECT_INTERVAL_MS 5000
|
|
|
|
static mqtt_client_t* s_mqtt_client;
|
|
static ip_addr_t s_mqtt_broker_ip;
|
|
static uint8_t s_mqtt_connected = 0;
|
|
static uint32_t s_last_mqtt_retry_time = 0;
|
|
|
|
extern struct netif g_netif;
|
|
|
|
static void publish_ha_discovery_configs(mqtt_client_t* client);
|
|
static void mqtt_pub_request_cb(void* arg, err_t err);
|
|
static void mqtt_connection_cb(mqtt_client_t* client, void* arg,
|
|
mqtt_connection_status_t status);
|
|
static void publish_sensor_value(const char* topic, int32_t value_x100);
|
|
|
|
// public fns
|
|
|
|
void ha_mqtt_init(void) {
|
|
s_mqtt_client = mqtt_client_new();
|
|
if (s_mqtt_client == NULL) {
|
|
printf("error: failed to create mqtt client\n");
|
|
return;
|
|
}
|
|
|
|
IP4_ADDR(&s_mqtt_broker_ip, MQTT_BROKER_IP_A, MQTT_BROKER_IP_B,
|
|
MQTT_BROKER_IP_C, MQTT_BROKER_IP_D);
|
|
|
|
ha_mqtt_check_and_reconnect();
|
|
}
|
|
|
|
void ha_mqtt_publish_sensor_data(int32_t temp_x100, int32_t hum_x100) {
|
|
if (!s_mqtt_connected) {
|
|
return;
|
|
}
|
|
publish_sensor_value(TEMP_STATE_TOPIC, temp_x100);
|
|
publish_sensor_value(HUM_STATE_TOPIC, hum_x100);
|
|
}
|
|
|
|
void ha_mqtt_check_and_reconnect(void) {
|
|
if (s_mqtt_connected || !netif_is_up(&g_netif) ||
|
|
!netif_is_link_up(&g_netif) || netif_ip4_addr(&g_netif)->addr == 0) {
|
|
return;
|
|
}
|
|
|
|
if (millis() - s_last_mqtt_retry_time > MQTT_RECONNECT_INTERVAL_MS) {
|
|
printf("attempting to connect to mqtt broker\n");
|
|
|
|
const struct mqtt_connect_client_info_t client_info = {
|
|
.client_id = MQTT_CLIENT_ID,
|
|
.keep_alive = 60,
|
|
// .client_user = "user", // authentication
|
|
// .client_pass = "pass",
|
|
};
|
|
|
|
err_t err =
|
|
mqtt_client_connect(s_mqtt_client, &s_mqtt_broker_ip, MQTT_BROKER_PORT,
|
|
mqtt_connection_cb, NULL, &client_info);
|
|
|
|
if (err != ERR_OK) {
|
|
printf("mqtt connect failed with immediate error: %d\n", err);
|
|
}
|
|
s_last_mqtt_retry_time = millis();
|
|
}
|
|
}
|
|
|
|
uint8_t ha_mqtt_is_connected(void) { return s_mqtt_connected; }
|
|
|
|
// static fns
|
|
|
|
/**
|
|
* @brief Publishes a sensor value after formatting it from a fixed-point
|
|
* int
|
|
* @param topic The MQTT topic to publish to.
|
|
* @param value_x100 The sensor value, scaled by 100.
|
|
*/
|
|
static void publish_sensor_value(const char* topic, int32_t value_x100) {
|
|
char payload_buf[16];
|
|
|
|
// format the fixed-point value (e.g., 2345) into a decimal string ("23.45")
|
|
int32_t val_int = value_x100 / 100;
|
|
int32_t val_frac = value_x100 % 100;
|
|
if (val_frac < 0) {
|
|
val_frac = -val_frac;
|
|
}
|
|
snprintf(payload_buf, sizeof(payload_buf), "%ld.%02ld", val_int, val_frac);
|
|
|
|
err_t err = mqtt_publish(s_mqtt_client, topic, payload_buf,
|
|
strlen(payload_buf), 1, 0, NULL, NULL);
|
|
|
|
if (err != ERR_OK) {
|
|
printf("failed to publish to topic %s: %d\n", topic, err);
|
|
}
|
|
}
|
|
|
|
static void publish_ha_discovery_configs(mqtt_client_t* client) {
|
|
char payload[512];
|
|
err_t err;
|
|
|
|
// publish temperature sensor config
|
|
snprintf(
|
|
payload, sizeof(payload),
|
|
"{"
|
|
"\"name\":\"%s\",\"unique_id\":\"%s\",\"stat_t\":\"%s\","
|
|
"\"dev_cla\":\"temperature\",\"unit_of_meas\":\"°C\","
|
|
"\"val_tpl\":\"{{ value|float(2) }}\","
|
|
"\"dev\":{\"ids\":[\"%s\"],\"name\":\"%s\",\"mf\":\"%s\",\"mdl\":\"%s\"}"
|
|
"}",
|
|
TEMP_NAME, TEMP_UNIQUE_ID, TEMP_STATE_TOPIC, DEVICE_UNIQUE_ID,
|
|
DEVICE_NAME, DEVICE_MANUFACTURER, DEVICE_MODEL);
|
|
|
|
printf("publishing ha discovery for temperature...\n");
|
|
err = mqtt_publish(client, TEMP_CONFIG_TOPIC, payload, strlen(payload), 1, 1,
|
|
mqtt_pub_request_cb, NULL);
|
|
if (err != ERR_OK) {
|
|
printf("failed to publish temp config: %d\n", err);
|
|
}
|
|
|
|
// publish humidity sensor config
|
|
snprintf(
|
|
payload, sizeof(payload),
|
|
"{"
|
|
"\"name\":\"%s\",\"unique_id\":\"%s\",\"stat_t\":\"%s\","
|
|
"\"dev_cla\":\"humidity\",\"unit_of_meas\":\"%%\","
|
|
"\"val_tpl\":\"{{ value|float(2) }}\","
|
|
"\"dev\":{\"ids\":[\"%s\"],\"name\":\"%s\",\"mf\":\"%s\",\"mdl\":\"%s\"}"
|
|
"}",
|
|
HUM_NAME, HUM_UNIQUE_ID, HUM_STATE_TOPIC, DEVICE_UNIQUE_ID, DEVICE_NAME,
|
|
DEVICE_MANUFACTURER, DEVICE_MODEL);
|
|
|
|
printf("publishing ha discovery for humidity...\n");
|
|
err = mqtt_publish(client, HUM_CONFIG_TOPIC, payload, strlen(payload), 1, 1,
|
|
mqtt_pub_request_cb, NULL);
|
|
if (err != ERR_OK) {
|
|
printf("failed to publish hum config: %d\n", err);
|
|
}
|
|
}
|
|
|
|
static void mqtt_pub_request_cb(void* arg, err_t err) {
|
|
(void)arg;
|
|
|
|
if (err != ERR_OK) {
|
|
printf("mqtt publish failed with error: %d\n", err);
|
|
}
|
|
}
|
|
|
|
static void mqtt_connection_cb(mqtt_client_t* client, void* arg,
|
|
mqtt_connection_status_t status) {
|
|
(void)arg;
|
|
|
|
if (status == MQTT_CONNECT_ACCEPTED) {
|
|
printf("mqtt connection successful\n");
|
|
s_mqtt_connected = 1;
|
|
// on connect, publish the discovery configuration messages
|
|
publish_ha_discovery_configs(client);
|
|
} else {
|
|
printf("mqtt connection failed, status: %d. will retry.\n", status);
|
|
s_mqtt_connected = 0;
|
|
}
|
|
}
|