Files
chargeflow/components/protocols/src/mqtt.c
2025-06-06 21:17:25 +01:00

340 lines
8.8 KiB
C
Executable File

#include <string.h>
#include <sys/param.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_log.h"
#include "esp_event.h"
#include "mqtt_client.h"
#include "nvs.h"
#include "mqtt.h"
#include "json.h"
#include "board_config.h"
#include "timeout_utils.h"
#define NVS_NAMESPACE "mqtt"
#define NVS_ENABLED "enabled"
#define NVS_SERVER "server"
#define NVS_BASE_TOPIC "base_topic"
#define NVS_USER "user"
#define NVS_PASSWORD "password"
#define NVS_PERIODICITY "periodicity"
static const char* TAG = "mqtt";
static nvs_handle nvs;
static TaskHandle_t client_task = NULL;
static esp_mqtt_client_handle_t client = NULL;
static uint16_t periodicity = 30;
static void subcribe_topics(void)
{
ESP_LOGI(TAG, "subcribe_topics");
char topic[48];
mqtt_get_base_topic(topic);
strcat(topic, "/request/#");
esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "data: %s", topic);
mqtt_get_base_topic(topic);
strcat(topic, "/set/config/#");
esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "data: %s", topic);
mqtt_get_base_topic(topic);
strcat(topic, "/enable");
esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "data: %s", topic);
}
static void publish_message(const char* topic, cJSON* root)
{
ESP_LOGI(TAG, "publish_message");
char target_topic[48];
mqtt_get_base_topic(target_topic);
strcat(target_topic, topic);
const char* json = cJSON_PrintUnformatted(root);
esp_mqtt_client_publish(client, target_topic, json, 0, 1, 0);
free((void*)json);
}
static void handle_message(const char* topic, const char* data)
{
char base_topic[32];
mqtt_get_base_topic(base_topic);
ESP_LOGI(TAG, "Topic: %s", topic);
ESP_LOGI(TAG, "data: %s", data);
ESP_LOGI(TAG, "base_topic: %s", base_topic);
if (strncmp(topic, base_topic, strlen(base_topic)) == 0) {
const char* sub_topic = &topic[strlen(base_topic)];
ESP_LOGI(TAG, "Sub_topic: %s", sub_topic);
if (strcmp(sub_topic, "/request/config/evse") == 0) {
cJSON* root = json_get_evse_config();
publish_message("/response/config/evse", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/config/wifi") == 0) {
cJSON* root = json_get_wifi_config();
publish_message("/response/config/wifi", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/config/mqtt") == 0) {
cJSON* root = json_get_mqtt_config();
publish_message("/response/config/mqtt", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/boardConfig") == 0) {
cJSON* root = json_get_board_config();
publish_message("/response/boardConfig", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/info") == 0) {
cJSON* root = json_get_info();
publish_message("/response/info", root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/request/restart") == 0) {
timeout_restart();
} else if (strcmp(sub_topic, "/set/config/evse") == 0) {
cJSON* root = cJSON_Parse(data);
json_set_evse_config(root);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/set/config/wifi") == 0) {
cJSON* root = cJSON_Parse(data);
json_set_wifi_config(root, true);
cJSON_Delete(root);
} else if (strcmp(sub_topic, "/set/config/mqtt") == 0) {
cJSON* root = cJSON_Parse(data);
json_set_mqtt_config(root);
cJSON_Delete(root);
}
}
}
static void event_handler(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data)
{
esp_mqtt_event_handle_t event = event_data;
char topic[48];
char data[256];
ESP_LOGI(TAG, "Handle Data 1");
switch (event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "Connected");
vTaskResume(client_task);
subcribe_topics();
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Disconnected");
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "Handle Data 2");
memset(topic, 0, sizeof(topic));
strncpy(topic, event->topic, MIN(event->topic_len, sizeof(topic) - 1));
memset(data, 0, sizeof(data));
strncpy(data, event->data, MIN(event->data_len, sizeof(data) - 1));
handle_message(topic, data);
break;
default:
break;
}
}
static void client_task_func(void* param)
{
while (true) {
if (!client) {
vTaskSuspend(NULL);
}
cJSON* root = json_get_state();
publish_message("/state", root);
cJSON_Delete(root);
vTaskDelay(pdMS_TO_TICKS(periodicity * 1000));
}
}
static void client_start(void)
{
char server[64];
char user[32];
char password[64];
mqtt_get_server(server);
mqtt_get_user(user);
mqtt_get_password(password);
ESP_LOGI(TAG, "Client Start");
esp_mqtt_client_config_t cfg = {
.broker.address.uri = server,
.credentials.username = user,
.credentials.authentication.password = password
};
if (client) {
if (esp_mqtt_set_config(client, &cfg) != ESP_OK) {
ESP_LOGW(TAG, "Cant set config");
}
} else {
client = esp_mqtt_client_init(&cfg);
if (esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, event_handler, client) != ESP_OK) {
ESP_LOGW(TAG, "Cant register handler");
}
if (client == NULL) {
ESP_LOGW(TAG, "Cant set config");
} else {
if (esp_mqtt_client_start(client) != ESP_OK) {
ESP_LOGW(TAG, "Cant start");
}
}
}
}
static void client_stop(void)
{
if (client != NULL) {
esp_mqtt_client_destroy(client);
client = NULL;
}
}
void mqtt_init(void)
{
ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs));
nvs_get_u16(nvs, NVS_PERIODICITY, &periodicity);
esp_register_shutdown_handler(&client_stop);
xTaskCreate(client_task_func, "mqtt_client_task", 3 * 1024, NULL, 5, &client_task);
if (mqtt_get_enabled()) {
client_start();
}
}
esp_err_t mqtt_set_config(bool enabled, const char* server, const char* base_topic, const char* user, const char* password, uint16_t _periodicity)
{
if (enabled) {
if (server == NULL || strlen(server) == 0) {
size_t len = 0;
nvs_get_str(nvs, NVS_SERVER, NULL, &len);
if (len <= 1) {
ESP_LOGE(TAG, "Required server");
return ESP_ERR_INVALID_ARG;
}
}
if (base_topic == NULL || strlen(base_topic) == 0) {
size_t len = 0;
nvs_get_str(nvs, NVS_BASE_TOPIC, NULL, &len);
if (len <= 1) {
ESP_LOGE(TAG, "Required base topic");
return ESP_ERR_INVALID_ARG;
}
}
if (_periodicity == 0) {
ESP_LOGE(TAG, "Periodicity muse be larger than zero");
return ESP_ERR_INVALID_ARG;
}
}
if (server != NULL && strlen(server) > 63) {
ESP_LOGE(TAG, "Server out of range");
return ESP_ERR_INVALID_ARG;
}
if (base_topic != NULL && strlen(base_topic) > 31) {
ESP_LOGE(TAG, "Base topic out of range");
return ESP_ERR_INVALID_ARG;
}
if (user != NULL && strlen(user) > 31) {
ESP_LOGE(TAG, "User out of range");
return ESP_ERR_INVALID_ARG;
}
if (password != NULL && strlen(password) > 63) {
ESP_LOGE(TAG, "Password out of range");
return ESP_ERR_INVALID_ARG;
}
nvs_set_u8(nvs, NVS_ENABLED, enabled);
nvs_set_str(nvs, NVS_SERVER, server);
nvs_set_str(nvs, NVS_BASE_TOPIC, base_topic);
nvs_set_str(nvs, NVS_USER, user);
nvs_set_str(nvs, NVS_PASSWORD, password);
nvs_set_u16(nvs, NVS_PERIODICITY, _periodicity);
periodicity = _periodicity;
nvs_commit(nvs);
if (enabled) {
client_start();
} else {
client_stop();
}
return ESP_OK;
}
bool mqtt_get_enabled(void)
{
uint8_t value = false;
nvs_get_u8(nvs, NVS_ENABLED, &value);
return value;
}
void mqtt_get_server(char* value)
{
size_t len = 64;
value[0] = '\0';
nvs_get_str(nvs, NVS_SERVER, value, &len);
}
void mqtt_get_base_topic(char* value)
{
size_t len = 32;
value[0] = '\0';
nvs_get_str(nvs, NVS_BASE_TOPIC, value, &len);
}
void mqtt_get_user(char* value)
{
size_t len = 32;
value[0] = '\0';
nvs_get_str(nvs, NVS_USER, value, &len);
}
void mqtt_get_password(char* value)
{
size_t len = 64;
value[0] = '\0';
nvs_get_str(nvs, NVS_PASSWORD, value, &len);
}
uint16_t mqtt_get_periodicity(void)
{
return periodicity;
}