Add motor control files
This commit is contained in:
232
main/main.c
232
main/main.c
@ -9,18 +9,56 @@
|
||||
#include "wifi_manager.h"
|
||||
#include "ota_server.h"
|
||||
#include "plant_mqtt.h"
|
||||
#include "motor_control.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char *TAG = "MAIN";
|
||||
|
||||
// Application version
|
||||
#define APP_VERSION "2.0.0-mqtt"
|
||||
#define APP_VERSION "2.1.0-motor"
|
||||
|
||||
// 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;
|
||||
|
||||
// Motor Control Callbacks
|
||||
static void motor_state_change_callback(motor_id_t id, motor_state_t state)
|
||||
{
|
||||
const char *state_str = "unknown";
|
||||
switch (state) {
|
||||
case MOTOR_STATE_STOPPED:
|
||||
state_str = "off";
|
||||
break;
|
||||
case MOTOR_STATE_RUNNING:
|
||||
state_str = "on";
|
||||
break;
|
||||
case MOTOR_STATE_ERROR:
|
||||
state_str = "error";
|
||||
break;
|
||||
case MOTOR_STATE_COOLDOWN:
|
||||
state_str = "cooldown";
|
||||
break;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Motor %d state changed to: %s", id, state_str);
|
||||
|
||||
// Publish state change to MQTT
|
||||
if (mqtt_client_is_connected()) {
|
||||
mqtt_publish_pump_state(id, state == MOTOR_STATE_RUNNING);
|
||||
}
|
||||
}
|
||||
|
||||
static void motor_error_callback(motor_id_t id, const char* error)
|
||||
{
|
||||
ESP_LOGE(TAG, "Motor %d error: %s", id, error);
|
||||
|
||||
// Publish error to MQTT alert topic
|
||||
if (mqtt_client_is_connected()) {
|
||||
char topic[64];
|
||||
snprintf(topic, sizeof(topic), "plant_watering/alerts/pump_error/%d", id);
|
||||
mqtt_client_publish(topic, error, MQTT_QOS_1, MQTT_NO_RETAIN);
|
||||
}
|
||||
}
|
||||
|
||||
// MQTT Callbacks
|
||||
static void mqtt_connected_callback(void)
|
||||
@ -30,8 +68,23 @@ static void mqtt_connected_callback(void)
|
||||
// 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);
|
||||
mqtt_publish_pump_state(1, motor_is_running(MOTOR_PUMP_1));
|
||||
mqtt_publish_pump_state(2, motor_is_running(MOTOR_PUMP_2));
|
||||
|
||||
// Publish motor statistics
|
||||
motor_stats_t stats;
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
if (motor_get_stats(i, &stats) == ESP_OK) {
|
||||
char topic[64];
|
||||
char data[128];
|
||||
|
||||
snprintf(topic, sizeof(topic), "plant_watering/pump/%d/stats", i);
|
||||
snprintf(data, sizeof(data),
|
||||
"{\"total_runtime\":%lu,\"run_count\":%lu,\"last_duration\":%lu}",
|
||||
stats.total_runtime_ms, stats.run_count, stats.last_run_duration_ms);
|
||||
mqtt_client_publish(topic, data, MQTT_QOS_0, MQTT_NO_RETAIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void mqtt_disconnected_callback(void)
|
||||
@ -47,27 +100,60 @@ static void mqtt_data_callback(const char* topic, const char* data, int data_len
|
||||
// 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);
|
||||
ESP_LOGI(TAG, "Starting pump 1 via MQTT");
|
||||
esp_err_t ret = motor_start(MOTOR_PUMP_1, MOTOR_DEFAULT_SPEED);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to start pump 1: %s", esp_err_to_name(ret));
|
||||
}
|
||||
} 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);
|
||||
ESP_LOGI(TAG, "Stopping pump 1 via MQTT");
|
||||
motor_stop(MOTOR_PUMP_1);
|
||||
} else if (strncmp(data, "pulse", data_len) == 0) {
|
||||
ESP_LOGI(TAG, "Pulse pump 1 for 5 seconds");
|
||||
motor_start_timed(MOTOR_PUMP_1, MOTOR_DEFAULT_SPEED, 5000);
|
||||
}
|
||||
} 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);
|
||||
ESP_LOGI(TAG, "Starting pump 2 via MQTT");
|
||||
esp_err_t ret = motor_start(MOTOR_PUMP_2, MOTOR_DEFAULT_SPEED);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Failed to start pump 2: %s", esp_err_to_name(ret));
|
||||
}
|
||||
} 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);
|
||||
ESP_LOGI(TAG, "Stopping pump 2 via MQTT");
|
||||
motor_stop(MOTOR_PUMP_2);
|
||||
} else if (strncmp(data, "pulse", data_len) == 0) {
|
||||
ESP_LOGI(TAG, "Pulse pump 2 for 5 seconds");
|
||||
motor_start_timed(MOTOR_PUMP_2, MOTOR_DEFAULT_SPEED, 5000);
|
||||
}
|
||||
} else if (strcmp(topic, "plant_watering/pump/1/speed") == 0) {
|
||||
int speed = atoi(data);
|
||||
if (speed >= 0 && speed <= 100) {
|
||||
motor_set_speed(MOTOR_PUMP_1, speed);
|
||||
ESP_LOGI(TAG, "Set pump 1 speed to %d%%", speed);
|
||||
}
|
||||
} else if (strcmp(topic, "plant_watering/pump/2/speed") == 0) {
|
||||
int speed = atoi(data);
|
||||
if (speed >= 0 && speed <= 100) {
|
||||
motor_set_speed(MOTOR_PUMP_2, speed);
|
||||
ESP_LOGI(TAG, "Set pump 2 speed to %d%%", speed);
|
||||
}
|
||||
} else if (strcmp(topic, TOPIC_CONFIG) == 0) {
|
||||
ESP_LOGI(TAG, "Configuration update received");
|
||||
// Parse JSON configuration here
|
||||
} else if (strcmp(topic, "plant_watering/commands/test_pump/1") == 0) {
|
||||
uint32_t duration = atoi(data);
|
||||
if (duration > 0 && duration <= 10000) { // Max 10 seconds for test
|
||||
motor_test_run(MOTOR_PUMP_1, duration);
|
||||
}
|
||||
} else if (strcmp(topic, "plant_watering/commands/test_pump/2") == 0) {
|
||||
uint32_t duration = atoi(data);
|
||||
if (duration > 0 && duration <= 10000) { // Max 10 seconds for test
|
||||
motor_test_run(MOTOR_PUMP_2, duration);
|
||||
}
|
||||
} else if (strcmp(topic, "plant_watering/commands/emergency_stop") == 0) {
|
||||
ESP_LOGW(TAG, "Emergency stop command received!");
|
||||
motor_emergency_stop();
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,9 +192,11 @@ static void ota_progress_handler(int percent)
|
||||
ESP_LOGI(TAG, "OTA Progress: %d%%", percent);
|
||||
}
|
||||
|
||||
// Task to simulate sensor readings
|
||||
// Task to simulate sensor readings and publish stats
|
||||
static void sensor_simulation_task(void *pvParameters)
|
||||
{
|
||||
TickType_t last_stats_publish = 0;
|
||||
|
||||
while (1) {
|
||||
// Wait for MQTT connection
|
||||
if (mqtt_client_is_connected()) {
|
||||
@ -128,6 +216,29 @@ static void sensor_simulation_task(void *pvParameters)
|
||||
|
||||
ESP_LOGI(TAG, "Published moisture: Sensor1=%d%%, Sensor2=%d%%",
|
||||
test_moisture_1, test_moisture_2);
|
||||
|
||||
// Publish pump runtime stats every minute
|
||||
if (xTaskGetTickCount() - last_stats_publish > pdMS_TO_TICKS(60000)) {
|
||||
last_stats_publish = xTaskGetTickCount();
|
||||
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
// Publish current runtime if running
|
||||
if (motor_is_running(i)) {
|
||||
char topic[64];
|
||||
char data[32];
|
||||
snprintf(topic, sizeof(topic), "plant_watering/pump/%d/runtime", i);
|
||||
snprintf(data, sizeof(data), "%lu", motor_get_runtime_ms(i));
|
||||
mqtt_client_publish(topic, data, MQTT_QOS_0, MQTT_NO_RETAIN);
|
||||
}
|
||||
|
||||
// Publish cooldown status
|
||||
if (motor_is_cooldown(i)) {
|
||||
char topic[64];
|
||||
snprintf(topic, sizeof(topic), "plant_watering/pump/%d/cooldown", i);
|
||||
mqtt_client_publish(topic, "true", MQTT_QOS_0, MQTT_NO_RETAIN);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update every 10 seconds
|
||||
@ -135,6 +246,33 @@ static void sensor_simulation_task(void *pvParameters)
|
||||
}
|
||||
}
|
||||
|
||||
// Task to demonstrate automated watering based on moisture
|
||||
static void automation_demo_task(void *pvParameters)
|
||||
{
|
||||
bool auto_mode = false; // Start with manual mode
|
||||
|
||||
while (1) {
|
||||
if (auto_mode && mqtt_client_is_connected()) {
|
||||
// Simple threshold-based automation demo
|
||||
if (test_moisture_1 < CONFIG_MOISTURE_THRESHOLD_LOW) {
|
||||
if (!motor_is_running(MOTOR_PUMP_1) && !motor_is_cooldown(MOTOR_PUMP_1)) {
|
||||
ESP_LOGI(TAG, "Auto: Moisture 1 low (%d%%), starting pump 1", test_moisture_1);
|
||||
motor_start_timed(MOTOR_PUMP_1, MOTOR_DEFAULT_SPEED, 10000); // 10 second watering
|
||||
}
|
||||
}
|
||||
|
||||
if (test_moisture_2 < CONFIG_MOISTURE_THRESHOLD_LOW) {
|
||||
if (!motor_is_running(MOTOR_PUMP_2) && !motor_is_cooldown(MOTOR_PUMP_2)) {
|
||||
ESP_LOGI(TAG, "Auto: Moisture 2 low (%d%%), starting pump 2", test_moisture_2);
|
||||
motor_start_timed(MOTOR_PUMP_2, MOTOR_DEFAULT_SPEED, 10000); // 10 second watering
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(30000 / portTICK_PERIOD_MS); // Check every 30 seconds
|
||||
}
|
||||
}
|
||||
|
||||
void print_chip_info(void)
|
||||
{
|
||||
esp_chip_info_t chip_info;
|
||||
@ -157,18 +295,17 @@ void app_main(void)
|
||||
// Print chip information
|
||||
print_chip_info();
|
||||
|
||||
// Print MQTT configuration
|
||||
ESP_LOGI(TAG, "MQTT Broker: %s", CONFIG_MQTT_BROKER_URL);
|
||||
ESP_LOGI(TAG, "MQTT Username: %s", CONFIG_MQTT_USERNAME);
|
||||
// Print configuration
|
||||
ESP_LOGI(TAG, "Configuration:");
|
||||
ESP_LOGI(TAG, " Moisture threshold low: %d%%", CONFIG_MOISTURE_THRESHOLD_LOW);
|
||||
ESP_LOGI(TAG, " Moisture threshold high: %d%%", CONFIG_MOISTURE_THRESHOLD_HIGH);
|
||||
ESP_LOGI(TAG, " Max watering duration: %d ms", CONFIG_WATERING_MAX_DURATION_MS);
|
||||
ESP_LOGI(TAG, " Min watering interval: %d ms", CONFIG_WATERING_MIN_INTERVAL_MS);
|
||||
|
||||
// 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);
|
||||
@ -180,6 +317,29 @@ void app_main(void)
|
||||
mqtt_disconnected_callback,
|
||||
mqtt_data_callback);
|
||||
|
||||
// Initialize Motor Control
|
||||
ESP_ERROR_CHECK(motor_control_init());
|
||||
motor_register_state_callback(motor_state_change_callback);
|
||||
motor_register_error_callback(motor_error_callback);
|
||||
|
||||
// Configure motor safety limits from Kconfig
|
||||
motor_set_max_runtime(MOTOR_PUMP_1, CONFIG_WATERING_MAX_DURATION_MS);
|
||||
motor_set_max_runtime(MOTOR_PUMP_2, CONFIG_WATERING_MAX_DURATION_MS);
|
||||
motor_set_min_interval(MOTOR_PUMP_1, CONFIG_WATERING_MIN_INTERVAL_MS);
|
||||
motor_set_min_interval(MOTOR_PUMP_2, CONFIG_WATERING_MIN_INTERVAL_MS);
|
||||
|
||||
// Subscribe to additional MQTT topics after connection
|
||||
static const char* additional_topics[] = {
|
||||
"plant_watering/pump/+/speed",
|
||||
"plant_watering/commands/test_pump/+",
|
||||
"plant_watering/commands/emergency_stop",
|
||||
"plant_watering/settings/+/+",
|
||||
NULL
|
||||
};
|
||||
|
||||
// This would need to be done after MQTT connection
|
||||
// You might want to add this to the mqtt_connected_callback
|
||||
|
||||
// Start WiFi connection
|
||||
esp_err_t ret = wifi_manager_start();
|
||||
if (ret != ESP_OK) {
|
||||
@ -189,6 +349,9 @@ void app_main(void)
|
||||
// Create sensor simulation task
|
||||
xTaskCreate(sensor_simulation_task, "sensor_sim", 4096, NULL, 5, NULL);
|
||||
|
||||
// Create automation demo task (disabled by default)
|
||||
xTaskCreate(automation_demo_task, "automation", 4096, NULL, 4, NULL);
|
||||
|
||||
// Main loop - monitor system status
|
||||
while (1) {
|
||||
ESP_LOGI(TAG, "System Status - WiFi: %s, MQTT: %s, Free heap: %d bytes",
|
||||
@ -196,11 +359,22 @@ void app_main(void)
|
||||
mqtt_client_is_connected() ? "Connected" : "Disconnected",
|
||||
esp_get_free_heap_size());
|
||||
|
||||
// Print pump states
|
||||
// Print pump states and runtime
|
||||
if (mqtt_client_is_connected()) {
|
||||
ESP_LOGI(TAG, "Pump States - Pump1: %s, Pump2: %s",
|
||||
test_pump_1 ? "ON" : "OFF",
|
||||
test_pump_2 ? "ON" : "OFF");
|
||||
for (int i = 1; i <= 2; i++) {
|
||||
motor_stats_t stats;
|
||||
motor_get_stats(i, &stats);
|
||||
|
||||
const char *state_str = "OFF";
|
||||
if (motor_is_running(i)) {
|
||||
state_str = "ON";
|
||||
} else if (motor_is_cooldown(i)) {
|
||||
state_str = "COOLDOWN";
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Pump %d: %s, Total runtime: %lu s, Runs: %lu",
|
||||
i, state_str, stats.total_runtime_ms / 1000, stats.run_count);
|
||||
}
|
||||
}
|
||||
|
||||
vTaskDelay(30000 / portTICK_PERIOD_MS); // Every 30 seconds
|
||||
|
||||
Reference in New Issue
Block a user