Added MQTT

This commit is contained in:
2025-07-17 20:41:25 -06:00
parent abe63b0ae9
commit 5fd11369cc
5 changed files with 858 additions and 268 deletions

View File

@ -3,6 +3,7 @@ idf_component_register(
"main.c"
"wifi_manager.c"
"ota_server.c"
"plant_mqtt.c"
"led_strip.c"
INCLUDE_DIRS
"."
@ -12,4 +13,5 @@ idf_component_register(
esp_http_server
app_update
driver
mqtt
)

View File

@ -5,54 +5,89 @@
#include "esp_system.h"
#include "esp_log.h"
#include "esp_chip_info.h"
#include "esp_random.h"
#include "wifi_manager.h"
#include "ota_server.h"
#include "led_strip.h"
#include "plant_mqtt.h"
#include "sdkconfig.h"
static const char *TAG = "MAIN";
// WiFi credentials - Change these to your network
// #define WIFI_SSID "YOUR_SSID"
// #define WIFI_PASSWORD "YOUR_PASSWORD"
const char *ssid = CONFIG_WIFI_SSID;
const char *password = CONFIG_WIFI_PASSWORD;
// Application version
#define APP_VERSION "1.0.1"
#define APP_VERSION "2.0.0-mqtt"
// LED colors and timing
typedef struct {
uint8_t r, g, b;
const char *name;
} color_t;
// Test data
static int test_moisture_1 = 45;
static int test_moisture_2 = 62;
static bool test_pump_1 = false;
static bool test_pump_2 = false;
static const color_t colors[] = {
{255, 0, 0, "Red"},
{0, 255, 0, "Green"},
{0, 0, 255, "Blue"},
{255, 255, 0, "Yellow"},
{255, 0, 255, "Magenta"},
{0, 255, 255, "Cyan"},
{255, 255, 255, "White"},
};
// MQTT Callbacks
static void mqtt_connected_callback(void)
{
ESP_LOGI(TAG, "MQTT Connected - Publishing initial status");
// Publish initial states
mqtt_publish_moisture(1, test_moisture_1);
mqtt_publish_moisture(2, test_moisture_2);
mqtt_publish_pump_state(1, test_pump_1);
mqtt_publish_pump_state(2, test_pump_2);
}
#define NUM_COLORS (sizeof(colors) / sizeof(colors[0]))
#define BLINK_DELAY_MS 200
static void mqtt_disconnected_callback(void)
{
ESP_LOGW(TAG, "MQTT Disconnected");
}
static void mqtt_data_callback(const char* topic, const char* data, int data_len)
{
ESP_LOGI(TAG, "MQTT Data received on topic: %s", topic);
ESP_LOGI(TAG, "Data: %.*s", data_len, data);
// Handle pump control commands
if (strcmp(topic, TOPIC_PUMP_1_CMD) == 0) {
if (strncmp(data, "on", data_len) == 0) {
test_pump_1 = true;
ESP_LOGI(TAG, "Pump 1 turned ON");
mqtt_publish_pump_state(1, test_pump_1);
} else if (strncmp(data, "off", data_len) == 0) {
test_pump_1 = false;
ESP_LOGI(TAG, "Pump 1 turned OFF");
mqtt_publish_pump_state(1, test_pump_1);
}
} else if (strcmp(topic, TOPIC_PUMP_2_CMD) == 0) {
if (strncmp(data, "on", data_len) == 0) {
test_pump_2 = true;
ESP_LOGI(TAG, "Pump 2 turned ON");
mqtt_publish_pump_state(2, test_pump_2);
} else if (strncmp(data, "off", data_len) == 0) {
test_pump_2 = false;
ESP_LOGI(TAG, "Pump 2 turned OFF");
mqtt_publish_pump_state(2, test_pump_2);
}
} else if (strcmp(topic, TOPIC_CONFIG) == 0) {
ESP_LOGI(TAG, "Configuration update received");
// Parse JSON configuration here
}
}
// WiFi event handler
static void wifi_event_handler(wifi_state_t state)
{
switch (state) {
case WIFI_STATE_CONNECTED:
ESP_LOGI(TAG, "WiFi connected - starting OTA server");
ESP_LOGI(TAG, "WiFi connected - starting services");
ota_server_start();
// Start MQTT client
if (mqtt_client_start() != ESP_OK) {
ESP_LOGE(TAG, "Failed to start MQTT client");
}
break;
case WIFI_STATE_DISCONNECTED:
ESP_LOGW(TAG, "WiFi disconnected - stopping OTA server");
ESP_LOGW(TAG, "WiFi disconnected - stopping services");
mqtt_client_stop();
ota_server_stop();
break;
@ -71,6 +106,35 @@ static void ota_progress_handler(int percent)
ESP_LOGI(TAG, "OTA Progress: %d%%", percent);
}
// Task to simulate sensor readings
static void sensor_simulation_task(void *pvParameters)
{
while (1) {
// Wait for MQTT connection
if (mqtt_client_is_connected()) {
// Simulate moisture sensor readings with some variation
test_moisture_1 += (esp_random() % 5) - 2; // +/- 2
test_moisture_2 += (esp_random() % 5) - 2; // +/- 2
// Keep values in range
if (test_moisture_1 < 0) test_moisture_1 = 0;
if (test_moisture_1 > 100) test_moisture_1 = 100;
if (test_moisture_2 < 0) test_moisture_2 = 0;
if (test_moisture_2 > 100) test_moisture_2 = 100;
// Publish sensor data
mqtt_publish_moisture(1, test_moisture_1);
mqtt_publish_moisture(2, test_moisture_2);
ESP_LOGI(TAG, "Published moisture: Sensor1=%d%%, Sensor2=%d%%",
test_moisture_1, test_moisture_2);
}
// Update every 10 seconds
vTaskDelay(10000 / portTICK_PERIOD_MS);
}
}
void print_chip_info(void)
{
esp_chip_info_t chip_info;
@ -83,55 +147,38 @@ void print_chip_info(void)
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
ESP_LOGI(TAG, "silicon revision %d, ", chip_info.revision);
ESP_LOGI(TAG, "Minimum free heap size: %d bytes", esp_get_minimum_free_heap_size());
}
void app_main(void)
{
ESP_LOGI(TAG, "ESP32-S3 Thing Plus RGB Blinker v%s", APP_VERSION);
ESP_LOGI(TAG, "Plant Watering System v%s", APP_VERSION);
// Print chip information
print_chip_info();
// Initialize RGB LED
led_strip_t *led_strip = led_strip_init(LED_STRIP_GPIO, LED_STRIP_LED_COUNT);
if (!led_strip) {
ESP_LOGE(TAG, "Failed to initialize LED strip");
} else {
ESP_LOGI(TAG, "RGB LED initialized on GPIO %d", LED_STRIP_GPIO);
// Turn LED off initially
led_strip_clear(led_strip);
}
// Print MQTT configuration
ESP_LOGI(TAG, "MQTT Broker: %s", CONFIG_MQTT_BROKER_URL);
ESP_LOGI(TAG, "MQTT Username: %s", CONFIG_MQTT_USERNAME);
// Initialize WiFi manager
ESP_ERROR_CHECK(wifi_manager_init());
wifi_manager_register_callback(wifi_event_handler);
// TEMPORARY: Clear stored credentials to force use of new ones
// wifi_manager_clear_credentials();
// ESP_LOGI(TAG, "Cleared stored WiFi credentials");
// Initialize OTA server
ESP_ERROR_CHECK(ota_server_init());
ota_server_set_version(APP_VERSION);
ota_server_register_progress_callback(ota_progress_handler);
// Check if we have stored WiFi credentials
char stored_ssid[33] = {0};
char stored_pass[65] = {0};
// Force update with new credentials (remove this after first successful connection)
ESP_LOGI(TAG, "Updating WiFi credentials - SSID: '%s'", ssid);
wifi_manager_set_credentials(ssid, password);
/*
// Normal flow - only update if no credentials stored
if (wifi_manager_get_credentials(stored_ssid, sizeof(stored_ssid),
stored_pass, sizeof(stored_pass)) != ESP_OK) {
ESP_LOGI(TAG, "No stored WiFi credentials, saving default ones");
ESP_LOGI(TAG, "Setting SSID: '%s'", ssid);
wifi_manager_set_credentials(ssid, password);
} else {
ESP_LOGI(TAG, "Found stored credentials - SSID: '%s'", stored_ssid);
}
*/
// Initialize MQTT client
ESP_ERROR_CHECK(mqtt_client_init());
mqtt_client_register_callbacks(mqtt_connected_callback,
mqtt_disconnected_callback,
mqtt_data_callback);
// Start WiFi connection
esp_err_t ret = wifi_manager_start();
@ -139,42 +186,23 @@ void app_main(void)
ESP_LOGE(TAG, "Failed to start WiFi manager");
}
// Main loop with RGB LED blinking
int color_index = 0;
bool led_on = false;
// Create sensor simulation task
xTaskCreate(sensor_simulation_task, "sensor_sim", 4096, NULL, 5, NULL);
// Main loop - monitor system status
while (1) {
// Blink the RGB LED through different colors
if (led_strip) {
if (led_on) {
// Turn LED on with current color
led_strip_set_pixel(led_strip, 0,
colors[color_index].r,
colors[color_index].g,
colors[color_index].b);
led_strip_refresh(led_strip);
ESP_LOGI(TAG, "LED ON - Color: %s", colors[color_index].name);
} else {
// Turn LED off
led_strip_clear(led_strip);
ESP_LOGI(TAG, "LED OFF");
// Move to next color when turning off
color_index = (color_index + 1) % NUM_COLORS;
}
led_on = !led_on;
ESP_LOGI(TAG, "System Status - WiFi: %s, MQTT: %s, Free heap: %d bytes",
wifi_manager_is_connected() ? "Connected" : "Disconnected",
mqtt_client_is_connected() ? "Connected" : "Disconnected",
esp_get_free_heap_size());
// Print pump states
if (mqtt_client_is_connected()) {
ESP_LOGI(TAG, "Pump States - Pump1: %s, Pump2: %s",
test_pump_1 ? "ON" : "OFF",
test_pump_2 ? "ON" : "OFF");
}
// Print heap info every 10 blinks (5 seconds)
static int blink_count = 0;
if (++blink_count >= 10) {
ESP_LOGI(TAG, "Free heap: %d bytes, WiFi: %s",
esp_get_free_heap_size(),
wifi_manager_is_connected() ? "Connected" : "Disconnected");
blink_count = 0;
}
vTaskDelay(BLINK_DELAY_MS / portTICK_PERIOD_MS);
vTaskDelay(30000 / portTICK_PERIOD_MS); // Every 30 seconds
}
}

457
main/plant_mqtt.c Normal file
View File

@ -0,0 +1,457 @@
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "plant_mqtt.h"
#include "mqtt_client.h" // ESP-IDF MQTT client header
static const char *TAG = "MQTT_CLIENT";
// NVS namespace for MQTT settings
#define MQTT_NVS_NAMESPACE "mqtt_config"
// MQTT client handle
static esp_mqtt_client_handle_t s_mqtt_client = NULL;
// Current state
static mqtt_state_t s_mqtt_state = MQTT_STATE_DISCONNECTED;
// Callbacks
static mqtt_connected_callback_t s_connected_callback = NULL;
static mqtt_disconnected_callback_t s_disconnected_callback = NULL;
static mqtt_data_callback_t s_data_callback = NULL;
// Mutex for thread safety
static SemaphoreHandle_t s_mqtt_mutex = NULL;
// Forward declarations
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data);
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event);
esp_err_t mqtt_client_init(void)
{
if (s_mqtt_client != NULL) {
ESP_LOGW(TAG, "MQTT client already initialized");
return ESP_OK;
}
// Create mutex
s_mqtt_mutex = xSemaphoreCreateMutex();
if (s_mqtt_mutex == NULL) {
ESP_LOGE(TAG, "Failed to create mutex");
return ESP_FAIL;
}
// Try to load credentials from NVS
char url[128] = {0};
char username[64] = {0};
char password[64] = {0};
if (mqtt_client_get_config(url, sizeof(url), username, sizeof(username),
password, sizeof(password)) != ESP_OK) {
// Use defaults from menuconfig
ESP_LOGI(TAG, "No stored MQTT config, using defaults from menuconfig");
strlcpy(url, CONFIG_MQTT_BROKER_URL, sizeof(url));
strlcpy(username, CONFIG_MQTT_USERNAME, sizeof(username));
strlcpy(password, CONFIG_MQTT_PASSWORD, sizeof(password));
// Save defaults to NVS
mqtt_client_set_broker_url(url);
mqtt_client_set_credentials(username, password);
} else {
ESP_LOGI(TAG, "Loaded MQTT config from NVS");
}
// Configure MQTT client - ESP-IDF v5+ format
esp_mqtt_client_config_t mqtt_cfg = {
.broker.address.uri = url,
.credentials.username = username,
.credentials.authentication.password = password,
.credentials.client_id = MQTT_CLIENT_ID,
.session.keepalive = MQTT_KEEPALIVE,
.session.last_will.topic = TOPIC_LAST_WILL,
.session.last_will.msg = STATUS_OFFLINE,
.session.last_will.qos = MQTT_QOS_1,
.session.last_will.retain = MQTT_RETAIN,
.network.reconnect_timeout_ms = 10000,
};
// Create MQTT client
s_mqtt_client = esp_mqtt_client_init(&mqtt_cfg);
if (s_mqtt_client == NULL) {
ESP_LOGE(TAG, "Failed to create MQTT client");
vSemaphoreDelete(s_mqtt_mutex);
s_mqtt_mutex = NULL;
return ESP_FAIL;
}
// Register event handler
esp_err_t ret = esp_mqtt_client_register_event(s_mqtt_client, ESP_EVENT_ANY_ID,
mqtt_event_handler, s_mqtt_client);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to register event handler");
esp_mqtt_client_destroy(s_mqtt_client);
s_mqtt_client = NULL;
vSemaphoreDelete(s_mqtt_mutex);
s_mqtt_mutex = NULL;
return ret;
}
ESP_LOGI(TAG, "MQTT client initialized");
return ESP_OK;
}
esp_err_t mqtt_client_start(void)
{
if (s_mqtt_client == NULL) {
ESP_LOGE(TAG, "MQTT client not initialized");
return ESP_ERR_INVALID_STATE;
}
ESP_LOGI(TAG, "Starting MQTT client...");
ESP_LOGI(TAG, "Broker URL: %s", CONFIG_MQTT_BROKER_URL);
ESP_LOGI(TAG, "Client ID: %s", MQTT_CLIENT_ID);
esp_err_t ret = esp_mqtt_client_start(s_mqtt_client);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to start MQTT client");
s_mqtt_state = MQTT_STATE_ERROR;
return ret;
}
s_mqtt_state = MQTT_STATE_CONNECTING;
ESP_LOGI(TAG, "MQTT client started");
return ESP_OK;
}
esp_err_t mqtt_client_stop(void)
{
if (s_mqtt_client == NULL) {
return ESP_OK;
}
ESP_LOGI(TAG, "Stopping MQTT client...");
// Publish offline status before stopping
if (s_mqtt_state == MQTT_STATE_CONNECTED) {
mqtt_publish_status(STATUS_OFFLINE);
vTaskDelay(100 / portTICK_PERIOD_MS); // Give time to send
}
esp_err_t ret = esp_mqtt_client_stop(s_mqtt_client);
if (ret == ESP_OK) {
s_mqtt_state = MQTT_STATE_DISCONNECTED;
ESP_LOGI(TAG, "MQTT client stopped");
}
return ret;
}
esp_err_t mqtt_client_publish(const char* topic, const char* data, int qos, int retain)
{
if (s_mqtt_client == NULL || s_mqtt_state != MQTT_STATE_CONNECTED) {
ESP_LOGW(TAG, "MQTT client not connected");
return ESP_ERR_INVALID_STATE;
}
if (topic == NULL || data == NULL) {
return ESP_ERR_INVALID_ARG;
}
xSemaphoreTake(s_mqtt_mutex, portMAX_DELAY);
int msg_id = esp_mqtt_client_publish(s_mqtt_client, topic, data, strlen(data), qos, retain);
xSemaphoreGive(s_mqtt_mutex);
if (msg_id < 0) {
ESP_LOGE(TAG, "Failed to publish to topic: %s", topic);
return ESP_FAIL;
}
ESP_LOGD(TAG, "Published to %s: %s (msg_id: %d)", topic, data, msg_id);
return ESP_OK;
}
esp_err_t mqtt_client_subscribe(const char* topic, int qos)
{
if (s_mqtt_client == NULL || s_mqtt_state != MQTT_STATE_CONNECTED) {
ESP_LOGW(TAG, "MQTT client not connected");
return ESP_ERR_INVALID_STATE;
}
if (topic == NULL) {
return ESP_ERR_INVALID_ARG;
}
int msg_id = esp_mqtt_client_subscribe(s_mqtt_client, topic, qos);
if (msg_id < 0) {
ESP_LOGE(TAG, "Failed to subscribe to topic: %s", topic);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Subscribed to topic: %s (msg_id: %d)", topic, msg_id);
return ESP_OK;
}
esp_err_t mqtt_client_unsubscribe(const char* topic)
{
if (s_mqtt_client == NULL || s_mqtt_state != MQTT_STATE_CONNECTED) {
ESP_LOGW(TAG, "MQTT client not connected");
return ESP_ERR_INVALID_STATE;
}
if (topic == NULL) {
return ESP_ERR_INVALID_ARG;
}
int msg_id = esp_mqtt_client_unsubscribe(s_mqtt_client, topic);
if (msg_id < 0) {
ESP_LOGE(TAG, "Failed to unsubscribe from topic: %s", topic);
return ESP_FAIL;
}
ESP_LOGI(TAG, "Unsubscribed from topic: %s (msg_id: %d)", topic, msg_id);
return ESP_OK;
}
bool mqtt_client_is_connected(void)
{
return s_mqtt_state == MQTT_STATE_CONNECTED;
}
mqtt_state_t mqtt_client_get_state(void)
{
return s_mqtt_state;
}
void mqtt_client_register_callbacks(mqtt_connected_callback_t on_connected,
mqtt_disconnected_callback_t on_disconnected,
mqtt_data_callback_t on_data)
{
s_connected_callback = on_connected;
s_disconnected_callback = on_disconnected;
s_data_callback = on_data;
}
// Utility functions
esp_err_t mqtt_publish_status(const char* status)
{
return mqtt_client_publish(TOPIC_STATUS, status, MQTT_QOS_1, MQTT_RETAIN);
}
esp_err_t mqtt_publish_moisture(int sensor_id, int value)
{
char topic[64];
char data[32];
snprintf(topic, sizeof(topic), "plant_watering/moisture/%d", sensor_id);
snprintf(data, sizeof(data), "%d", value);
return mqtt_client_publish(topic, data, MQTT_QOS_0, MQTT_NO_RETAIN);
}
esp_err_t mqtt_publish_pump_state(int pump_id, bool state)
{
char topic[64];
const char* state_str = state ? "on" : "off";
snprintf(topic, sizeof(topic), "plant_watering/pump/%d/state", pump_id);
return mqtt_client_publish(topic, state_str, MQTT_QOS_1, MQTT_RETAIN);
}
// Event handler
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%ld", base, event_id);
mqtt_event_handler_cb(event_data);
}
static esp_err_t mqtt_event_handler_cb(esp_mqtt_event_handle_t event)
{
switch (event->event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT connected");
s_mqtt_state = MQTT_STATE_CONNECTED;
// Publish online status
mqtt_publish_status(STATUS_ONLINE);
// Subscribe to command topics
mqtt_client_subscribe(TOPIC_PUMP_1_CMD, MQTT_QOS_1);
mqtt_client_subscribe(TOPIC_PUMP_2_CMD, MQTT_QOS_1);
mqtt_client_subscribe(TOPIC_CONFIG, MQTT_QOS_1);
// Call user callback
if (s_connected_callback) {
s_connected_callback();
}
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGW(TAG, "MQTT disconnected");
s_mqtt_state = MQTT_STATE_DISCONNECTED;
// Call user callback
if (s_disconnected_callback) {
s_disconnected_callback();
}
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "Subscribed to topic, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "Unsubscribed from topic, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGD(TAG, "Message published, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT data received");
ESP_LOGI(TAG, "Topic: %.*s", event->topic_len, event->topic);
ESP_LOGI(TAG, "Data: %.*s", event->data_len, event->data);
// Call user callback
if (s_data_callback) {
// Null-terminate the strings for easier handling
char topic[256] = {0};
char data[512] = {0};
int topic_len = event->topic_len < sizeof(topic) - 1 ? event->topic_len : sizeof(topic) - 1;
int data_len = event->data_len < sizeof(data) - 1 ? event->data_len : sizeof(data) - 1;
memcpy(topic, event->topic, topic_len);
memcpy(data, event->data, data_len);
s_data_callback(topic, data, data_len);
}
break;
case MQTT_EVENT_ERROR:
ESP_LOGE(TAG, "MQTT error");
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
ESP_LOGE(TAG, "Last error code reported from esp-tls: 0x%x", event->error_handle->esp_tls_last_esp_err);
ESP_LOGE(TAG, "Last tls stack error number: 0x%x", event->error_handle->esp_tls_stack_err);
ESP_LOGE(TAG, "Last captured errno : %d (%s)", event->error_handle->esp_transport_sock_errno,
strerror(event->error_handle->esp_transport_sock_errno));
} else if (event->error_handle->error_type == MQTT_ERROR_TYPE_CONNECTION_REFUSED) {
ESP_LOGE(TAG, "Connection refused error: 0x%x", event->error_handle->connect_return_code);
} else {
ESP_LOGW(TAG, "Unknown error type: 0x%x", event->error_handle->error_type);
}
s_mqtt_state = MQTT_STATE_ERROR;
break;
case MQTT_EVENT_BEFORE_CONNECT:
ESP_LOGI(TAG, "MQTT client connecting...");
s_mqtt_state = MQTT_STATE_CONNECTING;
break;
default:
ESP_LOGD(TAG, "Other MQTT event id: %d", event->event_id);
break;
}
return ESP_OK;
}
// Configuration management functions
esp_err_t mqtt_client_set_broker_url(const char* url)
{
nvs_handle_t nvs_handle;
esp_err_t ret;
ret = nvs_open(MQTT_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to open NVS namespace");
return ret;
}
ret = nvs_set_str(nvs_handle, "broker_url", url);
if (ret != ESP_OK) {
nvs_close(nvs_handle);
return ret;
}
ret = nvs_commit(nvs_handle);
nvs_close(nvs_handle);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "MQTT broker URL saved to NVS");
}
return ret;
}
esp_err_t mqtt_client_set_credentials(const char* username, const char* password)
{
nvs_handle_t nvs_handle;
esp_err_t ret;
ret = nvs_open(MQTT_NVS_NAMESPACE, NVS_READWRITE, &nvs_handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to open NVS namespace");
return ret;
}
ret = nvs_set_str(nvs_handle, "username", username);
if (ret != ESP_OK) {
nvs_close(nvs_handle);
return ret;
}
ret = nvs_set_str(nvs_handle, "password", password);
if (ret != ESP_OK) {
nvs_close(nvs_handle);
return ret;
}
ret = nvs_commit(nvs_handle);
nvs_close(nvs_handle);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "MQTT credentials saved to NVS");
}
return ret;
}
esp_err_t mqtt_client_get_config(char* url, size_t url_len,
char* username, size_t username_len,
char* password, size_t password_len)
{
nvs_handle_t nvs_handle;
esp_err_t ret;
ret = nvs_open(MQTT_NVS_NAMESPACE, NVS_READONLY, &nvs_handle);
if (ret != ESP_OK) {
return ret;
}
ret = nvs_get_str(nvs_handle, "broker_url", url, &url_len);
if (ret != ESP_OK) {
nvs_close(nvs_handle);
return ret;
}
ret = nvs_get_str(nvs_handle, "username", username, &username_len);
if (ret != ESP_OK) {
nvs_close(nvs_handle);
return ret;
}
ret = nvs_get_str(nvs_handle, "password", password, &password_len);
nvs_close(nvs_handle);
return ret;
}

86
main/plant_mqtt.h Normal file
View File

@ -0,0 +1,86 @@
#ifndef PLANT_MQTT_H
#define PLANT_MQTT_H
#include "esp_err.h"
#include <stdbool.h>
// MQTT Configuration - These can be overridden by Kconfig
#ifndef CONFIG_MQTT_BROKER_URL
#define CONFIG_MQTT_BROKER_URL "mqtt://192.168.4.56:1883"
#endif
#ifndef CONFIG_MQTT_USERNAME
#define CONFIG_MQTT_USERNAME "esp32"
#endif
#ifndef CONFIG_MQTT_PASSWORD
#define CONFIG_MQTT_PASSWORD "esp32-plant"
#endif
#ifndef MQTT_CLIENT_ID
#define MQTT_CLIENT_ID "plant_watering_esp32"
#endif
#define MQTT_KEEPALIVE 60
#define MQTT_QOS_0 0
#define MQTT_QOS_1 1
#define MQTT_RETAIN 1
#define MQTT_NO_RETAIN 0
// MQTT Topics
#define TOPIC_STATUS "plant_watering/status"
#define TOPIC_MOISTURE_1 "plant_watering/moisture/1"
#define TOPIC_MOISTURE_2 "plant_watering/moisture/2"
#define TOPIC_PUMP_1_CMD "plant_watering/pump/1/set"
#define TOPIC_PUMP_2_CMD "plant_watering/pump/2/set"
#define TOPIC_PUMP_1_STATE "plant_watering/pump/1/state"
#define TOPIC_PUMP_2_STATE "plant_watering/pump/2/state"
#define TOPIC_CONFIG "plant_watering/config"
#define TOPIC_WATERING_STATS "plant_watering/stats"
#define TOPIC_LAST_WILL "plant_watering/status"
// Status messages
#define STATUS_ONLINE "online"
#define STATUS_OFFLINE "offline"
// MQTT States
typedef enum {
MQTT_STATE_DISCONNECTED,
MQTT_STATE_CONNECTING,
MQTT_STATE_CONNECTED,
MQTT_STATE_ERROR
} mqtt_state_t;
// Callbacks
typedef void (*mqtt_connected_callback_t)(void);
typedef void (*mqtt_disconnected_callback_t)(void);
typedef void (*mqtt_data_callback_t)(const char* topic, const char* data, int data_len);
// MQTT client functions
esp_err_t mqtt_client_init(void);
esp_err_t mqtt_client_start(void);
esp_err_t mqtt_client_stop(void);
esp_err_t mqtt_client_publish(const char* topic, const char* data, int qos, int retain);
esp_err_t mqtt_client_subscribe(const char* topic, int qos);
esp_err_t mqtt_client_unsubscribe(const char* topic);
bool mqtt_client_is_connected(void);
mqtt_state_t mqtt_client_get_state(void);
// Callback registration
void mqtt_client_register_callbacks(mqtt_connected_callback_t on_connected,
mqtt_disconnected_callback_t on_disconnected,
mqtt_data_callback_t on_data);
// Configuration management
esp_err_t mqtt_client_set_broker_url(const char* url);
esp_err_t mqtt_client_set_credentials(const char* username, const char* password);
esp_err_t mqtt_client_get_config(char* url, size_t url_len,
char* username, size_t username_len,
char* password, size_t password_len);
// Utility functions for common publishes
esp_err_t mqtt_publish_status(const char* status);
esp_err_t mqtt_publish_moisture(int sensor_id, int value);
esp_err_t mqtt_publish_pump_state(int pump_id, bool state);
#endif // PLANT_MQTT_H