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> ZigBee技术是一种近距离、低复杂度、低功耗、低数据速率、低成本的双向无线通信技术。主要适合于自动控制和远程控制领域,可以嵌入各种设备中,同时支持地理定位功能。ZigBee是基于IEEE802.15.4协议发展起来的一种短距离无线通信技术,功耗低,被业界认为是最有可能应用在工控场合的无线方式。它是一个由可多到65000个无线数传模块组成的一个无线数传网络平台,在整个网络范围内,每一个ZigBee网络数传模块之间可以相互通信,每个网络节点间的距离可以从标准的75m无限扩展。</p>
<p> 日常中身边常见的短距离无线通讯方式整理一下有这么几种:wifi,很常见了,手机、电脑很多都是用这个协议上网。蓝牙,也是很常见,耳机、手环还有共享单车很多都用这个协议。2.4G无线通讯,一种使用私有协议,常见于无线鼠标键盘。其余还有遥控器上常用的红外通讯。这里就wifi、Bluetooth、ZigBee做个简单的比较。</p>
<table align="center" border="1" cellpadding="1" cellspacing="1">
<tbody>
<tr>
<td> </td>
<td>WiFi </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之间 </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;"> </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->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->p_app_signal;
esp_err_t err_status = signal_struct->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(&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, &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->info.status != ESP_ZB_ZCL_STATUS_SUCCESS) {
Serial.print("Received message: error status: ");
Serial.println(message->info.status);
}
Serial.print("Received message: endpoint 0x");
Serial.print(message->info.dst_endpoint);
Serial.print(" cluster:0x");
Serial.print(message->info.cluster, HEX);
Serial.print(" attribute:0x");
Serial.print(message->attribute.id, HEX);
Serial.print(" data size: ");
Serial.println(message->attribute.data.size);
if (message->info.dst_endpoint == HA_ESP_LIGHT_ENDPOINT) {
if (message->info.cluster == ESP_ZB_ZCL_CLUSTER_ID_ON_OFF) {
if (message->attribute.id == ESP_ZB_ZCL_ATTR_ON_OFF_ON_OFF_ID && message->attribute.data.type == ESP_ZB_ZCL_ATTR_TYPE_BOOL) {
light_state = message->attribute.data.value ? *(bool *)message->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(&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>烧写时需要注意。开发板需要选择“ESP32C6 Dev Module”;USB CDC On Boot需要选择“enabled”,否则串口无法输出内容。小板做为灯的控制板,也就是终端受控设备,所以ZigBee类型需要选择 end device。</p>
<p style="text-align: center;"></p>
<p>然后是 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)(&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(&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, &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(&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 >= 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)(&button_func_pair);
ledctl = (ledctl + 1) % 2;
}
}
}</code></pre>
<p>同样烧写时需要注意,需要按下图设置。</p>
<p style="text-align: center;"> </p>
<p><span style="font-size:16px;"><strong>四、效果演示:</strong></span></p>
<p style="text-align: center;"> </p>
<p style="text-align: center;"> </p>
<p style="text-align: center;"> </p>
<p style="text-align: center;"> </p>
<p style="text-align: center;">d28e1ed48e4ea9a3161e72fe214e9aa2<br />
</p>
<p><span style="font-size:16px;"><strong>五、额外的记录:</strong></span></p>
<p>ZigBee组网,感觉和WiFi有点类似。组网完成后,只需要调用相关的方法即可传递消息。ZigBee通讯部分使用单独的任务进行管理,无需花过多的精力再去关注它。其它功能模块与通讯部分做到了最小耦合。不过在开发过程中遇到了蛮多问题,最烦的就是Beetle ESP32-C6串口不工作的问题。在烧写过程中已经很仔细地将USB CDC On Boot需要选择了“enabled”。但还是常常出现在板子复位后串口无输出的现象(这也是在接收程序中添加定时输出任务的原因)。</p>
<div><br />
<br />
</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]