跳到主要内容

Mesh 2.4G 点对点通信

前言

基于 ShineBlink C1 (Core)实现 Mesh 通信功能。注意:只能用C1不能用C2,因为C2 不支持Mesh。

C1Mini

一、实现功能

本章节实现了两个Core设备进行通信,其中一个设备是server、另一个设备是client。每当client设备按下电路板上的BTN1按键时,会向server上传数据。当server收到client的数据并验证通过后也会切换电路板上的LED1的亮灭状态,同时client也会收到server下发的命令,client验证命令通过后也会切换电路板上的LED1的亮灭状态。

另外server和client的电路板上的LED2都用来指示连接状态,只有当两边的LED2都同时亮时,才说明二者可以开始正常通信。

注意:由于Core内部自带2.4G无线功能,所以无需外部器件或模块即可实现本章节的无线通讯功能。但如果用了2.4G功能,Core的Ble蓝牙功能和USB功能就无法使用了,这一点开发者需要注意。

二、通信原理概述

  • Core在底层采用了基于 Thread无线网络的COAP协议,其实它是一个多点mesh网络通信协议,但是本例中的点对点通信只是实现了两个结点而已。

    通过了解Thread无线网络的机制应该知道,在Thread中的节点分为三类:Leader,Router,EndDevice。但请不要将它们和COAP协议中的server,client概念混淆。因为在Thread网络中的server和client节点可能是Leader,Router,EndDevice三者中的任意一种,而且还不是固定的,会随着网络的动态变化而变化,而这也体现出了Thread网络的强大之处,即网络中如果某一个路由节点出现了问题,网络其他节点会动态调整自己的角色来自愈网络。

  • Server和Client间通信示意图

    server_client

  • 通信限制

    • 一个网络中只能有一个server,但可以有上百个client。

      所以server可以作为整个网络的网关,来连接到外部其他的网络比如wifi,NBIOT,LORA等。

    • PanID和频率Channel决定了mesh网络的唯一性。

      基于此,我们可以设计PanID和频率Channel不同的Mesh网络来共存在同一个物理空间。

    • client的名字必须为8个字符,client上传给server的数据长度必须为8个字节,server下发给client的命令长度也必须为8个字节。(一般8个字节的空间对于传感器数据上传或控制类命令下发来讲已经足够了。)

    • server下发给client的命令不会马上被client收到,而是等到client下次上传数据给server时,server才会在应答过程中附带上命令数据下发给client。所以如果想让client尽早收到命令,client可以增加向server上传数据的频率。

      这么做的原因有三点:(1) 如果client节点是低功耗休眠的传感器设备,大多数时候可能都不会在线,所以server也不可能实时下发数据 (2) 如果client节点是动态入网离网的,server也无法和这种节点建立稳定的连接 (3) Core的内部资源有限,如果为每个client节点都维护一个连接会耗费大量的Ram。

    • Client端代码中的ClientName名字变量可以是任意的8个字符(必须是8个),例如本例中ClientName名字为"Client01"。

      注意:ClientName是Client在网络中被Server所识别的唯一身份ID。

三、Client端完整代码

--PanID和Channel决定了mesh网络的唯一性
PanID = 0x1234 --16位整型
Channel = 11 --选择范围(11~26)
MyRole = "Client" --定义为client角色
LIB_MeshConfig(MyRole,PanID,Channel) --启动并加入mesh网络
--client自身的名字"Client01"必须为8个字符,且和server端所提及到的client名字要一致
ClientName = "Client01" --ClientName是Client在网络中被Server所识别的唯一身份ID
LIB_GpioOutputConfig("D8","STANDARD") --LED1
LIB_GpioOutputConfig("D9","STANDARD") --LED2
LIB_GpioWrite("D8",1) --灭
LIB_GpioWrite("D9",1) --灭
--设置按键1(占用D0口,低电平有效)
LIB_ButtonConfig("BTN1","D10","L")
net_state = 0
--开始大循环
while(GC(1) == true)
do
--网络状态led指示
net_state = LIB_MeshClientNetStateQuery()
if net_state == 2 then --client已加入mesh网且找到了server
LIB_GpioWrite("D9",0) --LED2亮
else
LIB_GpioWrite("D9",1) --LED2灭
end
--如果BTN1按键短按且client已经加入mesh网并找到了server,就向Server上传数据
key = LIB_ButtonQuery("BTN1")
if key == 1 and net_state == 2 then
data = {0xA1,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
LIB_MeshClientSendData(ClientName,data)
end
--查询是否收到来自server下发的8字节命令,并解析
recv_flag, cmd = LIB_MeshClientRecvCommand()
if recv_flag == 1 and #cmd == 8 then
if cmd[1] == 0x01 then --这里只解析server下发的命令的第一个字节
LIB_GpioToggle("D8") --LED1亮或灭切换
end
end
end
如果感兴趣,上面代码中出现的LIB开头的库函数可以在 API文档 中通过Ctrl+F查询。

四、Server端完整代码

--PanID和Channel决定了mesh网络的唯一性
PanID = 0x1234 --16位整型
Channel = 11 --选择范围(11~26)
MyRole = "Server" --一个mesh网络里只能有一个Server
LIB_MeshConfig(MyRole,PanID,Channel) --启动并加入mesh网络
LIB_GpioOutputConfig("D8","STANDARD") --LED1
LIB_GpioOutputConfig("D9","STANDARD") --LED2
LIB_GpioWrite("D8",1) --灭
LIB_GpioWrite("D9",1) --灭
net_state = 0
--开始大循环
while(GC(1) == true)
do
--网络状态led指示
net_state = LIB_MeshServerNetStateQuery()
if net_state == 1 then --server已加入mesh网
LIB_GpioWrite("D9",0) --LED2亮
else --server未加入mesh网
LIB_GpioWrite("D9",1) --LED2灭
end
--接收clients上传的8字节数据并进行解析(这里只解析名字、长度和data的第1个字节)
recv_flag, client_name, data = LIB_MeshServerRecvData()
if recv_flag == 1 then
--这里只解析client上传的data的第一个字节
if #data == 8 and client_name == "Client01" and data[1] == 0xA1 then
LIB_GpioToggle("D8") --LED1亮或灭切换
end
end
--这里看似一直在向clients下发命令,但实际上只是缓存了命令
--命令只有在对应的client上传消息时才会发送到client
if true then --这里的true可以替换成其他条件,取决于你的应用
--向名字为"Client01"的节点下发命令,cmd必须是8个字节
cmd01 = {0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00}
LIB_MeshServerSendCommand("Client01",cmd01)
end
end
如果感兴趣,上面代码中出现的LIB开头的库函数可以在 API文档 中通过Ctrl+F查询。