Files
ch32v208_sens/ha_mqtt.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;
}
}