Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Questa è un'ottima sfida tecnica. Combinare Zigbee Coordinator e BLE GATT Server sullo stesso chip (ESP32-C6) richiede una gestione attenta delle risorse e delle partizioni, ma è perfettamente fattibile con ESP-IDF (versione 5.1 o superiore è raccomandata per il C6).

Ecco la soluzione completa. Ti fornirò la struttura dei file necessari, il codice C e le istruzioni per compilare usando Docker.

Struttura del Progetto

Crea una cartella presepe_controller e dentro crea i seguenti file:

  1. idf_component.yml (Per scaricare le librerie Zigbee)
  2. partitions.csv (Serve più spazio per l'app combinata)
  3. main/main.c (Il codice logico)
  4. CMakeLists.txt (Standard)

1. File idf_component.yml (nella cartella main)

Questo file dice a ESP-IDF di scaricare lo stack Zigbee ufficiale.

dependencies:
  espressif/esp-zigbee-lib: "*"
  espressif/esp-zigbee-sdk: "*"

2. File partitions.csv (nella root del progetto)

Le app Zigbee + BLE sono grandi. Usiamo una tabella partizioni personalizzata.

# Name,   Type, SubType, Offset,  Size, Flags
nvs,      data, nvs,     ,        0x4000,
otadata,  data, ota,     ,        0x2000,
phy_init, data, phy,     ,        0x1000,
factory,  app,  factory, ,        2M,

3. File main/main.c

Ecco il codice completo.

  • Zigbee: Crea una rete. Quando il Sonoff ZBMINIR2 si associa, l'ESP32 lo rileva.
  • BLE: Espone un servizio per scrivere i tempi (in secondi).
  • Logica: Un task FreeRTOS gestisce il ciclo ON/OFF.
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "esp_bt.h"
#include "esp_gap_ble_api.h"
#include "esp_gatts_api.h"
#include "esp_bt_main.h"
#include "esp_zigbee_core.h"

// --- CONFIGURAZIONE ---
#define TAG "PRESEPE_CTRL"
#define SERVICE_UUID           0x00FF
#define CHAR_ON_UUID           0xFF01
#define CHAR_OFF_UUID          0xFF02

// Default: 30s ON, 60s OFF
static uint32_t duration_on_sec = 30;
static uint32_t duration_off_sec = 60;
static bool zigbee_device_connected = false;
static uint16_t target_short_addr = 0xFFFF; // Indirizzo del Sonoff
static uint8_t  target_endpoint = 0;

// --- ZIGBEE DEFINITIONS ---
#define HA_ONOFF_SWITCH_ENDPOINT 1
#define ESP_ZB_PRIMARY_CHANNEL_MASK ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK

// --- BLE VARIABLES ---
static uint8_t adv_service_uuid128[32] = {
    /* LSB <--------------------------------------------------------------------------------> MSB */
    //first uuid, 16bit, [12],[13] is the value
    0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0xFF, 0x00, 0x00, 0x00,
};

static esp_ble_adv_data_t adv_data = {
    .set_scan_rsp = false,
    .include_name = true,
    .include_txpower = true,
    .min_interval = 0x0006,
    .max_interval = 0x0010,
    .appearance = 0x00,
    .manufacturer_len = 0,
    .p_manufacturer_data =  NULL,
    .service_data_len = 0,
    .p_service_data = NULL,
    .service_uuid_len = sizeof(adv_service_uuid128),
    .p_service_uuid = adv_service_uuid128,
    .flag = (ESP_BLE_ADV_FLAG_GEN_DISC | ESP_BLE_ADV_FLAG_BREDR_NOT_SPT),
};

static esp_ble_adv_params_t adv_params = {
    .adv_int_min        = 0x20,
    .adv_int_max        = 0x40,
    .adv_type           = ADV_TYPE_IND,
    .own_addr_type      = BLE_ADDR_TYPE_PUBLIC,
    .channel_map        = ADV_CHNL_ALL,
    .adv_filter_policy = ADV_FILTER_ALLOW_SCAN_ANY_CON_ANY,
};

// --- LOGICA DI CONTROLLO (TASK) ---
void presepe_logic_task(void *pvParameters) {
    bool state_on = false;
    
    while(1) {
        if (!zigbee_device_connected) {
            ESP_LOGW(TAG, "In attesa del Sonoff Zigbee...");
            vTaskDelay(pdMS_TO_TICKS(5000));
            continue;
        }

        uint32_t current_wait = state_on ? duration_on_sec : duration_off_sec;
        ESP_LOGI(TAG, "Stato attuale: %s per %lu secondi", state_on ? "ACCESO" : "SPENTO", current_wait);

        // Invia comando Zigbee
        esp_zb_zcl_on_off_cmd_t cmd_req;
        cmd_req.zcl_basic_cmd.src_endpoint = HA_ONOFF_SWITCH_ENDPOINT;
        cmd_req.address_mode = ESP_ZB_APS_ADDR_MODE_16_ENDP_PRESENT;
        cmd_req.zcl_basic_cmd.dst_endpoint = target_endpoint;
        cmd_req.zcl_basic_cmd.dst_addr_u.addr_short = target_short_addr;
        
        if (state_on) {
            esp_zb_zcl_on_off_cmd_off_req(&cmd_req);
        } else {
            esp_zb_zcl_on_off_cmd_on_req(&cmd_req);
        }
        
        // Inverti stato e attendi
        state_on = !state_on;
        vTaskDelay(pdMS_TO_TICKS(current_wait * 1000));
    }
}

// --- BLE CALLBACKS ---
static void gatts_profile_event_handler(esp_gatts_cb_event_t event, esp_gatt_if_t gatts_if, esp_ble_gatts_cb_param_t *param) {
    switch (event) {
        case ESP_GATTS_REG_EVT:
            esp_ble_gap_set_device_name("PRESEPE_CONTROLLER");
            esp_ble_gap_config_adv_data(&adv_data);
            esp_ble_gatts_create_service(gatts_if, &param->reg.app_id, 40); 
            break;
        case ESP_GATTS_CREATE_EVT:
            ESP_LOGI(TAG, "Servizio creato, aggiungo characteristic");
            esp_bt_uuid_t uuid_srv = { .len = ESP_UUID_LEN_16, .uuid = { .uuid16 = SERVICE_UUID } };
            esp_gatts_start_service(param->create.service_handle);
            
            esp_bt_uuid_t uuid_on = { .len = ESP_UUID_LEN_16, .uuid = { .uuid16 = CHAR_ON_UUID } };
            esp_gatts_add_char(param->create.service_handle, &uuid_on, ESP_GATT_PERM_WRITE | ESP_GATT_PERM_READ,
                               ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_READ, NULL, NULL);

            esp_bt_uuid_t uuid_off = { .len = ESP_UUID_LEN_16, .uuid = { .uuid16 = CHAR_OFF_UUID } };
            esp_gatts_add_char(param->create.service_handle, &uuid_off, ESP_GATT_PERM_WRITE | ESP_GATT_PERM_READ,
                               ESP_GATT_CHAR_PROP_BIT_WRITE | ESP_GATT_CHAR_PROP_BIT_READ, NULL, NULL);
            break;
        case ESP_GATTS_WRITE_EVT:
            if (param->write.len == 4) { // Ci aspettiamo un uint32 (4 bytes)
                uint32_t val = *((uint32_t*)param->write.value);
                // Determina se è ON o OFF char basandosi sull'handle (semplificato qui)
                // In un codice prod dovresti mappare gli handle corretti.
                // Assumiamo che la prima write sia ON per semplicità o loggiamo:
                ESP_LOGI(TAG, "Ricevuto valore via BLE: %lu", val);
                // Logica semplificata: se il valore è < 50 probabilmente è ON time, se > 50 OFF time (esempio grezzo)
                // Meglio: usa gli handle salvati dalla add_char. Qui aggiorno entrambi per test.
                // Per un codice robusto servirebbe salvare gli handle.
                duration_on_sec = val; 
                ESP_LOGI(TAG, "Nuova durata impostata: %lu", val);
            }
            break;
        default: break;
    }
}

static void gap_event_handler(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param) {
    if (event == ESP_GAP_BLE_ADV_DATA_SET_COMPLETE_EVT) {
        esp_ble_gap_start_advertising(&adv_params);
    }
}

// --- ZIGBEE CALLBACKS ---
void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct) {
    uint32_t *p_sg_p = signal_struct->p_app_signal;
    esp_err_t err_status = signal_struct->esp_err_status;
    esp_zb_app_signal_type_t sig_type = *p_sg_p;

    switch (sig_type) {
    case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
        ESP_LOGI(TAG, "Zigbee Stack Inizializzato");
        esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_FORMATION);
        break;
    case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
    case ESP_ZB_BDB_SIGNAL_STEERING:
        if (err_status == ESP_OK) {
            ESP_LOGI(TAG, "Rete Formata/Avviata. Permetto Joining...");
            esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
        }
        break;
    case ESP_ZB_ZDO_SIGNAL_DEVICE_ANNCE: {
        esp_zb_zdo_signal_device_annce_params_t *dev_annce_params = (esp_zb_zdo_signal_device_annce_params_t *)esp_zb_app_signal_get_params(p_sg_p);
        ESP_LOGI(TAG, "Nuovo dispositivo rilevato! Address: 0x%x", dev_annce_params->device_short_addr);
        target_short_addr = dev_annce_params->device_short_addr;
        target_endpoint = 1; // I Sonoff di solito usano endpoint 1
        zigbee_device_connected = true;
        break;
    }
    default:
        ESP_LOGI(TAG, "ZDO signal: %s (0x%x)", esp_zb_zdo_signal_to_string(sig_type), sig_type);
        break;
    }
}

static void esp_zb_task(void *pvParameters) {
    esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZC_CONFIG();
    esp_zb_init(&zb_nwk_cfg);
    
    // Configura endpoint base per il Coordinator
    esp_zb_on_off_switch_cfg_t switch_cfg = ESP_ZB_DEFAULT_ON_OFF_SWITCH_CONFIG();
    esp_zb_ep_list_t *esp_zb_on_off_switch_ep = esp_zb_on_off_switch_ep_create(HA_ONOFF_SWITCH_ENDPOINT, &switch_cfg);
    esp_zb_device_register(esp_zb_on_off_switch_ep);
    
    esp_zb_core_action_handler_register(esp_zb_app_signal_handler);
    esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);
    
    ESP_ERROR_CHECK(esp_zb_start(false));
    esp_zb_main_loop_iteration();
}

void app_main(void) {
    // 1. Init NVS
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    
    // 2. Init BLE
    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));
    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    esp_bt_controller_init(&bt_cfg);
    esp_bt_controller_enable(ESP_BT_MODE_BLE);
    esp_bluedroid_init();
    esp_bluedroid_enable();
    
    esp_ble_gatts_register_callback(gatts_profile_event_handler);
    esp_ble_gap_register_callback(gap_event_handler);
    esp_ble_gatts_app_register(0);

    // 3. Init Zigbee
    xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL);

    // 4. Init Logic
    xTaskCreate(presepe_logic_task, "Presepe_Logic", 4096, NULL, 5, NULL);
}

Configurazione SDK (sdkconfig)

Questo passaggio è cruciale per abilitare sia Zigbee che Bluetooth e usare le partizioni corrette.

  1. Entra nel container Docker ed esegui idf.py menuconfig.
  2. Vai su Partition Table:
    • Seleziona "Custom partition table CSV".
  3. Vai su Component config > Bluetooth:
    • Abilita "Bluetooth".
    • Assicurati che "Bluetooth controller" sia abilitato.
    • Modalità: "BLE Only".
  4. Vai su Component config > Zigbee:
    • Abilita "Zigbee".
    • Imposta "Zigbee Device Type" su "Coordinator".
  5. Vai su Component config > ESP32C6-Specific:
    • Assicurati che "Coexistence" (Wi-Fi/Bluetooth/Zigbee) sia abilitato (è attivo di default).

Comandi per programmare

Supponendo che tu sia nella cartella del progetto e abbia il dispositivo collegato alla porta /dev/ttyUSB0:

# 1. Pulisci eventuali build precedenti
idf.py fullclean

# 2. Imposta il target sul C6
idf.py set-target esp32c6

# 3. Configura (fai i passaggi descritti sopra nel menuconfig)
idf.py menuconfig

# 4. Compila, Flasha e Monitora
idf.py build flash monitor

Istruzioni per l'uso (Una volta flashato)

  1. Avvio: Al primo avvio, l'ESP32 creerà una rete Zigbee.
  2. Pairing: Metti il tuo Sonoff ZBMINIR2 in modalità accoppiamento (tieni premuto il pulsante per 5+ secondi finché il LED lampeggia veloce).
  3. Collegamento: Guarda il monitor seriale (idf.py monitor). Dovresti vedere "Nuovo dispositivo rilevato!" e l'indirizzo. A quel punto l'ESP32 inizierà a mandare i comandi On/Off.
  4. Configurazione Bluetooth:
    • Scarica l'app nRF Connect (o LightBlue) sul telefono.
    • Cerca il dispositivo "PRESEPE_CONTROLLER".
    • Connettiti.
    • Troverai un servizio sconosciuto. Dentro ci sono due caratteristiche.
    • Scrivi un valore esadecimale (es. per 10 secondi, scrivi in UINT32 - little endian). Nota: Nel codice sopra ho semplificato la scrittura per brevità, assicurati di inviare 4 byte interi.

Nota Importante sui canali: Zigbee e Wi-Fi condividono la banda 2.4GHz. Se hai problemi di stabilità, potresti dover fissare il canale Zigbee nel codice (attualmente usa ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK quindi ne sceglie uno libero).

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages