From fd250baa183ef25f14996274df1fce05f6878b36 Mon Sep 17 00:00:00 2001 From: Jmspencer41 Date: Wed, 13 Aug 2025 15:10:38 -0400 Subject: [PATCH] Finnished example program --- main/main.c | 151 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 148 insertions(+), 3 deletions(-) diff --git a/main/main.c b/main/main.c index b4258a7..ec14a35 100644 --- a/main/main.c +++ b/main/main.c @@ -4,16 +4,141 @@ #include "driver/gpio.h" #include "esp_log.h" #include "nvs_flash.h" +#include "esp_zigbee_core.h" +#include "ha/esp_zigbee_ha_standard.h" #include "ha/esp_zigbee_ha_standard.h" +#define LED_PIN GPIO_NUM_2 + static const char *TAG = "SIMPLE_LIGHT_CONTROLLER"; +static bool led_is_on = false; +static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message); + +static void led_task(void *pvParameters) { + + gpio_reset_pin(LED_PIN); // Reset pin to default state (clean slate) + gpio_set_direction(LED_PIN, GPIO_MODE_OUTPUT); // Set pin as output (can send voltage) + + while(1) { + gpio_set_level(LED_PIN, led_is_on ? 1 : 0); //set whether the light is on or off + vTaskDelay(100 / portTICK_PERIOD_MS); // delay by 100 ms + } +} + +static void zigbee_task(void *pvParameter) { + + ESP_LOGI(TAG, "Starting Zigbee... "); // print log + + esp_zb_cfg_t zb_nwk_cfg = { // default end device config + .esp_zb_role = ESP_ZB_DEVICE_TYPE_ED, + .install_code_policy = true, + .nwk_cfg.zed_cfg = { + .ed_timeout = ESP_ZB_ED_AGING_TIMEOUT_64MIN, + .keep_alive = 60000, //respond to coordinator every 60 seconds to let it know its alive. + } + }; + esp_zb_init(&zb_nwk_cfg); // Initialize Zigbee stack with config + + // Create a light device with default settings + esp_zb_on_off_light_cfg_t light_cfg = ESP_ZB_DEFAULT_ON_OFF_LIGHT_CONFIG(); + esp_zb_ep_list_t *ep_list = esp_zb_on_off_light_ep_create(1, &light_cfg); //TODO: endpoint_ID set to 1 for testing + + esp_zb_device_register(ep_list); // Tell Zigbee stack about our device + esp_zb_core_action_handler_register(zb_action_handler); // Set function to handle commands + + ESP_ERROR_CHECK(esp_zb_start(false)); // Start Zigbee (false = don't erase network info) + esp_zb_stack_main_loop(); // Run Zigbee forever (never returns) + +} + +// ========== HANDLE COMMANDS FROM HOME ASSISTANT ========== +// This function is called when Home Assistant sends commands to our device +static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message) +{ + // callback_id = what type of action/command this is + // message = pointer to the actual command data + + // Check if this is a "set attribute value" command (like turn on/off) + if (callback_id == ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID) { + + // Cast the generic message pointer to the specific message type we expect + const esp_zb_zcl_set_attr_value_message_t *msg = + (esp_zb_zcl_set_attr_value_message_t *)message; + + // Check if this command is for the on/off cluster AND the on/off attribute + if (msg->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF && // Is this an on/off command? + msg->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID) { // Is this the on/off attribute? + + // Extract the boolean value from the command + led_is_on = *(bool *)msg->attribute.data.value; // true = on, false = off + + // Print what happened to serial monitor + ESP_LOGI(TAG, "Home Assistant says: LED %s", led_is_on ? "ON" : "OFF"); + } + } + + return ESP_OK; // Return success status +} + +// Call this if device won't join network - forces it back to discoverable state +void force_factory_reset(void) +{ + ESP_LOGI(TAG, "Forcing factory reset - device will be discoverable again"); + esp_zb_factory_reset(); // Erases all network info + esp_restart(); // Restart device +} + +// This function is called when Zigbee network events happen (joining, errors, etc.) +void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) +{ + // Extract the signal type from the structure + uint32_t *p_sg_p = signal_struct->p_app_signal; // Pointer to signal data + esp_zb_app_signal_type_t sig_type = *p_sg_p; // Dereference to get signal type + + // Handle different types of network events + switch (sig_type) { + + // === ZIGBEE STACK INITIALIZED === + case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP: + ESP_LOGI(TAG, "Zigbee stack initialized"); + // Start the commissioning process (joining a network) + // BDB = Base Device Behavior (standard way devices join networks) + esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION); + break; + + // === DEVICE STARTED FOR FIRST TIME === + case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START: + ESP_LOGI(TAG, "Device started - looking for network to join"); + // Start network steering = look for open Zigbee networks to join + esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING); + break; + + // === NETWORK JOINING ATTEMPT RESULT === + case ESP_ZB_BDB_SIGNAL_STEERING: + // Check if joining was successful + if (signal_struct->esp_err_status == ESP_OK) { + ESP_LOGI(TAG, "Successfully joined Zigbee network!"); + ESP_LOGI(TAG, "Your device should now appear in Home Assistant"); + } else { + ESP_LOGI(TAG, "Failed to join network, retrying in 5 seconds..."); + // Retry joining after 5 second delay + esp_zb_scheduler_alarm((esp_zb_callback_t)esp_zb_bdb_start_top_level_commissioning, + ESP_ZB_BDB_MODE_NETWORK_STEERING, 5000); + } + break; + + // === ANY OTHER ZIGBEE EVENT === + default: + ESP_LOGI(TAG, "Other Zigbee event: %d", sig_type); + break; + } +} void app_main(void) { // Print startup banner to serial monitor - ESP_LOGI(TAG, "=== Simple Light controller for Zigbee & HA ==="); + ESP_LOGI(TAG, "=== Simple Light controller for Zigbee & HA ==="); - // === INITIALIZE STORAGE SYSTEM === // NVS = Non-Volatile Storage // Saves Zigbee network info so device remembers which network it joined ESP_ERROR_CHECK(nvs_flash_init()); // ESP_ERROR_CHECK = crash if this fails @@ -23,5 +148,25 @@ void app_main(void) .host_config = ESP_ZB_DEFAULT_HOST_CONFIG(), // Set up processor settings }; - ESP_ERROR_CHECK(esp_zb_platform_config(&config)); // Apply the configuration + ESP_ERROR_CHECK(esp_zb_platform_config(&config)); // Apply the configuration to board + + // === CREATE TASK 1: LED CONTROL === + // xTaskCreate = create a new task (mini-program that runs independently) + xTaskCreate(led_task, // Function to run (defined above) + "LED_Task", // Name for debugging (shows in task monitor) + 2048, // Stack size in bytes (how much memory task can use) + NULL, // Parameters to pass to task (we don't need any) + 1, // Priority 1-25 (1=lowest, 25=highest priority) + NULL); // Task handle (we don't need to control task later) + + // === CREATE TASK 2: ZIGBEE COMMUNICATION === + xTaskCreate(zigbee_task, // Function to run (defined above) + "Zigbee_Task", // Name for debugging + 4096, // Larger stack - Zigbee needs more memory + NULL, // No parameters + 5, // Higher priority - network communication is important + NULL); // No handle needed + + ESP_LOGI(TAG, "Both tasks started - LED and Zigbee running independently!"); + } \ No newline at end of file -- 2.49.1