这次只说如何用C语言实现通信协议的,具体SX1278底层驱动代码有机会再说。
LoRaWAN有点麻烦,需要接入网络,或者自建服务器,都太贵了,还不如自己弄个自组网完全免费,完全自己定制。
写之前先要感谢我的导师任老师,没有老师的监督偏爱我现在可能不是这个样子,也不会这些东西。
这篇文章可能有些地方不正确的地方,请各位多多指正。
为了节省时间和精力,我就直接改装现有协议了,自己设计太麻烦了,设计完我也变秃了。
#1.设计改进通信协议
我用的是在公司和工业中用的比较多的Modbus协议,ModBus 是由 Modicon 公司在 1979 年发明的,是全球第一个真正用于工业现场的总线协议。
modbus协议数据帧
我要用的话需要改进一下,在数据帧里再加一个网络地址,这样设备就有两个ID,设备命名更规范。
改进后的数据帧如下:
网关:
从机:
其实参数和数据都差不多,就是改了个名字。
#2.具体实施步骤
#2.1 通信过程
通信无非就是网关发送命令,从机接受命令并处理返回处理数据,网关接受数据的过程。
网关发送命令函数:sendMasterAsk(unsigned char slave_addr,unsigned char op_code,unsigned char pram);
网关接受数据函数:receiveSlaveAck(unsigned char slave_addr,unsigned char op_code,unsigned char pram,DeviceBlock * pdevblock);
从机处理网关命令函数:processMasterAsk(DeviceBlock * pdevblock);
#2.2 相关地址定义部分
在写发送接收函数之前先要把准备工作做好,先把各个参数的定义写好,这个可以写成变量可以写成数组也可以写成宏定义,这个看个人爱好。
我是写成宏定义了,到时候改起来好改。
网关地址、从机地址、操作码、数据都是8个字节,unsigned char刚好就是8个字节,所以相关定义都是unsigned char;给的空间比较豪华,因为我再也不想挨个位去看有什么含义了,太难受了,尤其是1553,一把辛酸泪。
/*网络相关宏定义*/
#define NET_ADDR 0xB9 //网络地址
#define SLAVE1_ADDR 0x01 //子设备1地址
#define SLAVE2_ADDR 0x02 //子设备2地址
#define SLAVE3_ADDR 0x03 //子设备3地址
/*操作码相关宏定义*/
#define OP_R_SENSOR 0x01 //读传感器数据
/*参数相关宏定义*/
#define PRAM_R_TEMPERATURE 0x01 //只读取温度
#define PRAM_R_BPM 0x02 //只读取心跳
#define PRAM_R_HUMIDITY 0x03 //只读取湿度
#define PRAM_R_ALL 0x07 //读取所有传感器
设备块---主要是用起来方便
typedef struct
{
unsigned char Temperature; //温度
unsigned char BPM; //BPM
unsigned short int HUMIDITY; //环境湿度
}DeviceBlock;
#2.3 网关发送部分
代码部分:
sendMasterAsk(unsigned char slave_addr,unsigned char op_code,unsigned char pram)
{
unsigned char sendbuffer[6] = {NET_ADDR,slave_addr,op_code,pram,0,0}; 按照通信协议排列
unsigned short int CRC16 = getModbusCRC16(sendbuffer,4);
sendbuffer[4] = CRC16>>8;
sendbuffer[5] = CRC16;
transmitPackets(sendbuffer,sizeof(sendbuffer)); //发送
}
设置一个数组写入网络地址代码,从机地址,操作码,参数(也就是在这个操作码下要干什么事情),剩下两个就是CRC了,这个CRC可以用硬件实现,也可以用软件,这个无所谓。最后用SX1278的API发送就行了。
slave_addr是子机地址,调用时输入定义的地址
op_code是操作码,pram是操作码下相关操作
#2.4 从机处理指令部分
代码部分:
processMasterAsk(DeviceBlock * pdevblock)
{
unsigned char Askbuffer[6]; 网关发给从机的数据
unsigned char Ackbuffer[9] = {NET_ADDR,SLAVE1_ADDR}; 从机返回给网关的数据,按照通信协议排列
unsigned char len;
unsigned short int CRC16;
unsigned char i;
if(receivePackets(Askbuffer)==1) 表示收到数据
{
if(Askbuffer[0] != NET_ADDR) 检测网络地址对不对
{
return FRAME_NETADDR_ERR; 返回网络地址错误代码
}
if(Askbuffer[1] != SLAVE3_ADDR) 检测从机地址对不对
{
return FRAME_SLAVEADDR_ERR; 返回从机地址错误代码
}
len = getFrameLength(Askbuffer,sizeof(Askbuffer)); 检测实际长度
没问题后开始处理数据
if(Askbuffer[2] == OP_R_SENSOR)
{
Ackbuffer[2] = OP_R_SENSOR;
for(i=0;i<8;++i) 8个字节每个位都来一遍,从第0位到第7位
{
switch(Askbuffer[3] & (0x01<<i)) i从0位开始移位1每次移移位,移8次,对比呢个位是1,相同就走哪一步,具体定义见参数定义数字,然后将对应传感器数据写入设备块数据准备发送
{
case PRAM_R_TEMPERATURE:Ackbuffer[3] = pdevblock->Temperature;break;
case PRAM_R_HUMIDITY :Ackbuffer[4] = pdevblock->HUMIDITY; break;
case PRAM_R_BPM :
{
Ackbuffer[5] = pdevblock->BPM;
} break;
default : break;
}
}
CRC16 = getModbusCRC16(Ackbuffer,7);
Ackbuffer[7] = CRC16>>8;
Ackbuffer[8] = CRC16;
}
}
#2.4 网关接受部分
代码部分:
大部分都和从机部分一样,对着看吧
receiveSlaveAck(unsigned char slave_addr,unsigned char op_code,unsigned char pram,DeviceBlock * pdevblock)
{
unsigned char receivebuffer[9];收到的从机数据
unsigned char len;
unsigned char i;
if(receivePackets(receivebuffer)==1)
{
if(receivebuffer[0] != NET_ADDR)
{
return FRAME_NETADDR_ERR;
}
if(receivebuffer[1] != slave_addr)
{
return FRAME_SLAVEADDR_ERR;
}
len = getFrameLength(receivebuffer,sizeof(receivebuffer));
if(op_code==OP_R_SENSOR)
{
for(i=0;i<8;++i)
{
switch(pram & (0x01<<i))
{
case PRAM_R_TEMPERATURE:(pdevblock+slave_addr)->Temperature = receivebuffer[3]; break;
读出从机传来的数据
case PRAM_R_HUMIDITY :(pdevblock+slave_addr)->HUMIDITY = receivebuffer[4]; break;
case PRAM_R_BPM :(pdevblock+slave_addr)->BPM = receivebuffer[5]; break;
default : break;
}
}
}
}