aramy 发表于 2024-5-9 10:07

Beetle ESP32 C6迷你开发板 使用ZigBee协议点灯

<div class='showpostmsg'> 本帖最后由 aramy 于 2024-5-9 10:57 编辑

<p><span style="font-size:16px;"><strong>一、ZigBee协议介绍:</strong></span></p>

<p>&nbsp; ZigBee技术是一种近距离、低复杂度、低功耗、低数据速率、低成本的双向无线通信技术。主要适合于自动控制和远程控制领域,可以嵌入各种设备中,同时支持地理定位功能。ZigBee是基于IEEE802.15.4协议发展起来的一种短距离无线通信技术,功耗低,被业界认为是最有可能应用在工控场合的无线方式。它是一个由可多到65000个无线数传模块组成的一个无线数传网络平台,在整个网络范围内,每一个ZigBee网络数传模块之间可以相互通信,每个网络节点间的距离可以从标准的75m无限扩展。</p>

<p>&nbsp; 日常中身边常见的短距离无线通讯方式整理一下有这么几种:wifi,很常见了,手机、电脑很多都是用这个协议上网。蓝牙,也是很常见,耳机、手环还有共享单车很多都用这个协议。2.4G无线通讯,一种使用私有协议,常见于无线鼠标键盘。其余还有遥控器上常用的红外通讯。这里就wifi、Bluetooth、ZigBee做个简单的比较。</p>

<table align="center" border="1" cellpadding="1" cellspacing="1">
        <tbody>
                <tr>
                        <td>&nbsp;</td>
                        <td>WiFi&nbsp;</td>
                        <td>Bluetooth</td>
                        <td>ZigBee</td>
                </tr>
                <tr>
                        <td>速度</td>
                        <td>WiFi提供最高的数据传输速率,达到9.6 Gbps,适合高速数据传输需求。</td>
                        <td>蓝牙的数据传输速率介于125kbps到2Mbps之间,虽然低于WiFi,但仍然可以满足一般的设备连接需求</td>
                        <td>ZigBee的最高数据传输速率为250kbps,适合周期性或间歇性的小数据包传输</td>
                </tr>
                <tr>
                        <td>功耗</td>
                        <td>功耗高。</td>
                        <td>功耗低,还具有低功耗模式,进一步降低功耗。</td>
                        <td>功耗低</td>
                </tr>
                <tr>
                        <td>覆盖范围</td>
                        <td>WiFi的覆盖范围较广,室内约50m,室外可达100m<canvas height="32" width="32"></canvas></td>
                        <td>蓝牙的典型覆盖范围可达100m</td>
                        <td>ZigBee的覆盖范围较短,通常在10-100m之间&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</td>
                </tr>
                <tr>
                        <td>应用场景</td>
                        <td>WiFi因其高速率和广泛的应用场景(如家庭网络),被广泛用于各种设备的连接。</td>
                        <td>蓝牙则因其简单的连接方式和低成本,在短距离设备配对中非常受欢迎</td>
                        <td>ZigBee由于其低功耗和自组网功能,被广泛应用于智能家居、工业控制等领域。</td>
                </tr>
        </tbody>
</table>

<p>日常生活学习中,确实是很少接触到ZigBee协议,所以是借此机会在C6的小板上实现用ZigBee协议通讯。</p>

<p><span style="font-size:16px;"><strong>二、硬件准备:</strong></span></p>

<p>Beetle ESP32-C6是一款基于ESP32-C6芯片设计的迷你体积的Arduino低功耗物联网开发板,该开发板仅有硬币大小(25*20.5mm)。ESP32-C6搭载160MHz的高性能RISC-V 32位处理器,支持Wi-Fi 6、Bluetooth 5、Zigbee 3.0、Thread 1.3通讯协议,可接入多种通讯协议的物联网网络。</p>

<p>但是实现ZigBee组网通讯,但有这一块板子是不够的。这里找来一块相同核心的FireBeetle 2 ESP32 C6开发板来共同组网。两个开发板的核心都是ESP32-C6芯片。Beetle ESP32-C6板子上有个LED灯,最终目的就是实现通过FireBeetle 2 ESP32 C6开发板上的按键,走ZigBee协议来远程控制Beetle ESP32-C6板子上LED灯的亮灭。</p>

<p style="text-align: center;"> &nbsp;</p>

<p><strong>三、<span style="font-size:16px;">需求实现</span>:</strong></p>

<p>由于vscode+platformio尚不支持ESP32-C6芯片,所以这里编程使用arduino来实现,这里有参考乔老师的<a href="https://github.com/HonestQiao/Esp32-C6-Bug-Arduino-Examples/tree/main/examples/esp32c6bugzigbeemqttbridge" target="_blank">帖子和工程</a>。首先是小板Beetle ESP32-C6的代码。这里开辟了两个任务,一个负责接收和处理ZigBee信号和LED开关的任务,一个是定时向串口输出当前系统工作时间片的任务。</p>

<pre>
<code class="language-cpp">#ifndef ZIGBEE_MODE_ED
#error "Zigbee end device mode is not selected in Tools-&gt;Zigbee mode"
#endif
#include "esp_zigbee_core.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "ha/esp_zigbee_ha_standard.h"

// #define LIGHT_IS_HIGH
#define LED_PIN 15
bool ledstate = true;

/* Default End Device config */
#define ESP_ZB_ZED_CONFIG()                                     \
{                                                         \
    .esp_zb_role = ESP_ZB_DEVICE_TYPE_ED,                   \
                   .install_code_policy = INSTALLCODE_POLICY_ENABLE,       \
                                          .nwk_cfg = {                                          \
                                                                                                .zed_cfg = {                                        \
                                                                                                                                                      .ed_timeout = ED_AGING_TIMEOUT,               \
                                                                                                                                                      .keep_alive = ED_KEEP_ALIVE,                  \
                                                                                                             },                                                \
                                                   },                                                      \
}

#define ESP_ZB_DEFAULT_RADIO_CONFIG()                           \
{                                                         \
    .radio_mode = RADIO_MODE_NATIVE,                        \
}

#define ESP_ZB_DEFAULT_HOST_CONFIG()                            \
{                                                         \
    .host_connection_mode = HOST_CONNECTION_MODE_NONE,      \
}

/* Zigbee configuration */
#define INSTALLCODE_POLICY_ENABLE       false    /* enable the install code policy for security */
#define ED_AGING_TIMEOUT                ESP_ZB_ED_AGING_TIMEOUT_64MIN
#define ED_KEEP_ALIVE                   3000    /* 3000 millisecond */
#define HA_ESP_LIGHT_ENDPOINT         10    /* esp light bulb device endpoint, used to process light controlling commands */
#define ESP_ZB_PRIMARY_CHANNEL_MASK   ESP_ZB_TRANSCEIVER_ALL_CHANNELS_MASK/* Zigbee primary channel mask use in the example */

/********************* Zigbee functions **************************/
static void bdb_start_top_level_commissioning_cb(uint8_t mode_mask)
{
ESP_ERROR_CHECK(esp_zb_bdb_start_top_level_commissioning(mode_mask));
}

void esp_zb_app_signal_handler(esp_zb_app_signal_t *signal_struct)
{
uint32_t *p_sg_p       = signal_struct-&gt;p_app_signal;
esp_err_t err_status = signal_struct-&gt;esp_err_status;
esp_zb_app_signal_type_t sig_type = (esp_zb_app_signal_type_t) * p_sg_p;
switch (sig_type) {
    case ESP_ZB_ZDO_SIGNAL_SKIP_STARTUP:
      Serial.println("Zigbee stack initialized");
      esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_INITIALIZATION);
      break;
    case ESP_ZB_BDB_SIGNAL_DEVICE_FIRST_START:
    case ESP_ZB_BDB_SIGNAL_DEVICE_REBOOT:
      if (err_status == ESP_OK) {
      Serial.println("Start network steering");
      esp_zb_bdb_start_top_level_commissioning(ESP_ZB_BDB_MODE_NETWORK_STEERING);
      } else {
      /* commissioning failed */
      Serial.print("Failed to initialize Zigbee stack, status:");
      Serial.println(esp_err_to_name(err_status));
      }
      break;
    case ESP_ZB_BDB_SIGNAL_STEERING:
      if (err_status == ESP_OK) {
      esp_zb_ieee_addr_t extended_pan_id;
      esp_zb_get_extended_pan_id(extended_pan_id);
      Serial.print("Joined network successfully (Extended PAN ID:");
      Serial.print(extended_pan_id, HEX);
      Serial.print(":");
      Serial.print(extended_pan_id, HEX);
      Serial.print(":");
      Serial.print(extended_pan_id, HEX);
      Serial.print(":");
      Serial.print(extended_pan_id, HEX);
      Serial.print(":");
      Serial.print(extended_pan_id, HEX);
      Serial.print(":");
      Serial.print(extended_pan_id, HEX);
      Serial.print(":");
      Serial.print(extended_pan_id, HEX);
      Serial.print(":");
      Serial.print(extended_pan_id, HEX);
      Serial.print(", PAN ID");
      Serial.print(esp_zb_get_pan_id(), HEX);
      Serial.print(" Channel:");
      Serial.print(esp_zb_get_current_channel());
      Serial.print(" Short address:");
      Serial.println(esp_zb_get_short_address(), HEX);
      } else {
      Serial.print("Network steering was not successful status:");
      Serial.println(esp_err_to_name(err_status));
      esp_zb_scheduler_alarm((esp_zb_callback_t)bdb_start_top_level_commissioning_cb, ESP_ZB_BDB_MODE_NETWORK_STEERING, 1000);
      }
      break;
    default:
      //      log_i("ZDO signal: %s (0x%x), status: %s", esp_zb_zdo_signal_to_string(sig_type), sig_type,
      //               esp_err_to_name(err_status));
      break;
}
}

static esp_err_t zb_action_handler(esp_zb_core_action_callback_id_t callback_id, const void *message)
{
esp_err_t ret = ESP_OK;
switch (callback_id) {
    case ESP_ZB_CORE_SET_ATTR_VALUE_CB_ID:
      ret = zb_attribute_handler((esp_zb_zcl_set_attr_value_message_t *)message);
      break;
    default:
      log_w("Receive Zigbee action(0x%x) callback", callback_id);
      break;
}
return ret;
}

static void esp_zb_task(void *pvParameters)
{
esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZED_CONFIG();
esp_zb_init(&amp;zb_nwk_cfg);
esp_zb_on_off_light_cfg_t light_cfg = ESP_ZB_DEFAULT_ON_OFF_LIGHT_CONFIG();
esp_zb_ep_list_t *esp_zb_on_off_light_ep = esp_zb_on_off_light_ep_create(HA_ESP_LIGHT_ENDPOINT, &amp;light_cfg);
esp_zb_device_register(esp_zb_on_off_light_ep);
esp_zb_core_action_handler_register(zb_action_handler);
esp_zb_set_primary_network_channel_set(ESP_ZB_PRIMARY_CHANNEL_MASK);

//Erase NVRAM before creating connection to new Coordinator
//esp_zb_nvram_erase_at_start(true); //Comment out this line to erase NVRAM data if you are conneting to new Coordinator

ESP_ERROR_CHECK(esp_zb_start(false));
esp_zb_main_loop_iteration();
}

/* Handle the light attribute */

static esp_err_t zb_attribute_handler(const esp_zb_zcl_set_attr_value_message_t *message)
{
esp_err_t ret = ESP_OK;
bool light_state = 0;

if (!message) {
    Serial.println("Empty message");
}
if (message-&gt;info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
    Serial.print("Received message: error status: ");
    Serial.println(message-&gt;info.status);
}

Serial.print("Received message: endpoint 0x");
Serial.print(message-&gt;info.dst_endpoint);
Serial.print(" cluster:0x");
Serial.print(message-&gt;info.cluster, HEX);
Serial.print(" attribute:0x");
Serial.print(message-&gt;attribute.id, HEX);
Serial.print(" data size: ");
Serial.println(message-&gt;attribute.data.size);
if (message-&gt;info.dst_endpoint == HA_ESP_LIGHT_ENDPOINT) {
    if (message-&gt;info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) {
      if (message-&gt;attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID &amp;&amp; message-&gt;attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) {
      light_state = message-&gt;attribute.data.value ? *(bool *)message-&gt;attribute.data.value : light_state;
#ifndef LIGHT_IS_HIGH
      ledstate = !light_state;
      Serial.print("Light sets to ");
      if (ledstate)Serial.println("Off");
      if (!ledstate)Serial.println("On");
      digitalWrite(LED_PIN, ledstate);
#else
      ledstate = light_state;
      Serial.print("Light sets to ");
      if (ledstate)Serial.println("On");
      if (!ledstate)Serial.println("Off");
      digitalWrite(LED_PIN, ledstate);
#endif
      }
    }
}
return ret;
}

/********************* Arduino functions **************************/
void setup() {
// Init Zigbee
Serial.begin(115200);
pinMode(LED_PIN , OUTPUT);
digitalWrite(LED_PIN, ledstate);

Serial.println("Esp32-C6-Bug Zigbee bulb begin!");
esp_zb_platform_config_t config = {
    .radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),
    .host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),
};
ESP_ERROR_CHECK(esp_zb_platform_config(&amp;config));

// Init RMT and leave light OFF
pinMode(LED_PIN , OUTPUT);
digitalWrite(LED_PIN, ledstate);

// Start Zigbee task
xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL);
}

void loop() {
vTaskDelay(5000 / portTICK_PERIOD_MS);
Serial.println(millis());
}</code></pre>

<p>烧写时需要注意。开发板需要选择&ldquo;ESP32C6 Dev Module&rdquo;;USB CDC On Boot需要选择&ldquo;enabled&rdquo;,否则串口无法输出内容。小板做为灯的控制板,也就是终端受控设备,所以ZigBee类型需要选择 end device。</p>

<p style="text-align: center;"></p>

<p>然后是&nbsp;FireBeetle 2 ESP32 C6开发板做的控制端。也是两个任务,一个任务用来监听按键是否有按下,提供两种控制状态,自动控制,每隔固定时间就通过ZigBee网络发送改变LED灯状态的命令。手动控制,通过按键改变小板的LED灯状态的方式。另外一个任务,就是将命令字通过ZigBee网络传输出去。</p>

<pre>
<code class="language-cpp">#include "Zigbee.h"
#include "OneButton.h"
#define BUTTON_PIN 9//将按钮引脚绑定到GPIO 9

char ledctl = 0;
bool auto_ctl = true;
long num = 0;

OneButton button(BUTTON_PIN, true);
void doubleclick() {
if(auto_ctl) auto_ctl=false;
else auto_ctl=true;
Serial.print("auto_ctl=");
Serial.print(auto_ctl);
Serial.print("      ");
Serial.println("doubleclick");
}
void click() {
switch_func_pair_t button_func_pair;
Serial.println("click");
button_func_pair.state = ledctl;
button_func_pair.func = SWITCH_ONOFF_TOGGLE_CONTROL;
(*esp_zb_buttons_handler)(&amp;button_func_pair);
ledctl = (ledctl + 1) % 2;
}

static void esp_zb_task(void *pvParameters) {
esp_zb_cfg_t zb_nwk_cfg = ESP_ZB_ZC_CONFIG();
esp_zb_init(&amp;zb_nwk_cfg);
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, &amp;switch_cfg);
esp_zb_device_register(esp_zb_on_off_switch_ep);
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 setup() {
Serial.begin(115200);
delay(1000);
Serial.println("Let's go!");

button.reset();//清除一下按钮状态机的状态
button.attachClick(click);
button.attachDoubleClick(doubleclick);

// Init Zigbee
esp_zb_platform_config_t config = {
    .radio_config = ESP_ZB_DEFAULT_RADIO_CONFIG(),
    .host_config = ESP_ZB_DEFAULT_HOST_CONFIG(),
};

ESP_ERROR_CHECK(esp_zb_platform_config(&amp;config));
xTaskCreate(esp_zb_task, "Zigbee_main", 4096, NULL, 5, NULL);
delay(4000);
}

void loop() {
button.tick();
vTaskDelay(10 / portTICK_PERIOD_MS);
num++;
if (num &gt;= 500) {
    num = 0;
    if (auto_ctl) {
      switch_func_pair_t button_func_pair;
      button_func_pair.state = ledctl;
      button_func_pair.func = SWITCH_ONOFF_TOGGLE_CONTROL;
      (*esp_zb_buttons_handler)(&amp;button_func_pair);
      ledctl = (ledctl + 1) % 2;
    }
}
}</code></pre>

<p>同样烧写时需要注意,需要按下图设置。</p>

<p style="text-align: center;"> &nbsp;</p>

<p><span style="font-size:16px;"><strong>四、效果演示:</strong></span></p>

<p style="text-align: center;"> &nbsp;</p>

<p style="text-align: center;">&nbsp;</p>

<p style="text-align: center;"> &nbsp;</p>

<p style="text-align: center;"> &nbsp;</p>

<p style="text-align: center;">d28e1ed48e4ea9a3161e72fe214e9aa2<br />
&nbsp;</p>

<p><span style="font-size:16px;"><strong>五、额外的记录:</strong></span></p>

<p>ZigBee组网,感觉和WiFi有点类似。组网完成后,只需要调用相关的方法即可传递消息。ZigBee通讯部分使用单独的任务进行管理,无需花过多的精力再去关注它。其它功能模块与通讯部分做到了最小耦合。不过在开发过程中遇到了蛮多问题,最烦的就是Beetle ESP32-C6串口不工作的问题。在烧写过程中已经很仔细地将USB CDC On Boot需要选择了&ldquo;enabled&rdquo;。但还是常常出现在板子复位后串口无输出的现象(这也是在接收程序中添加定时输出任务的原因)。</p>

<div><br />
<br />
&nbsp;</div>
</div><script>                                        var loginstr = '<div class="locked">查看本帖全部内容,请<a href="javascript:;"   style="color:#e60000" class="loginf">登录</a>或者<a href="https://bbs.eeworld.com.cn/member.php?mod=register_eeworld.php&action=wechat" style="color:#e60000" target="_blank">注册</a></div>';
                                       
                                        if(parseInt(discuz_uid)==0){
                                                                                                (function($){
                                                        var postHeight = getTextHeight(400);
                                                        $(".showpostmsg").html($(".showpostmsg").html());
                                                        $(".showpostmsg").after(loginstr);
                                                        $(".showpostmsg").css({height:postHeight,overflow:"hidden"});
                                                })(jQuery);
                                        }                </script><script type="text/javascript">(function(d,c){var a=d.createElement("script"),m=d.getElementsByTagName("script"),eewurl="//counter.eeworld.com.cn/pv/count/";a.src=eewurl+c;m.parentNode.insertBefore(a,m)})(document,523)</script>
页: [1]
查看完整版本: Beetle ESP32 C6迷你开发板 使用ZigBee协议点灯