友讯达模块调试笔记
仪表为 Slave
模式, 主动上报数据, 网关为 Master
模式, 负责接收数据
模块配置
设备以 ATJ
为起始指令, 配置结束后通过 ATJX
退出寄存器配置模式, 再用 ATQ
退出配置模式, 配置命令执行成功均会返回 OK
配置模式包含两种, AT
命令和寄存器配置命令, AT
命令掉电丢失, 寄存器命令掉电保存, 进入配置模式后, 可进行多个配置后再退出
AT 方式配置
掉电后配置恢复初始状态
- 前置指令
// AT (进入配置模式)
41 54
// ATQ (退出配置模式)
41 54 51
// ATY
41 54 59
// AT0 (获取当前模块的配置信息) 先进入AT在输入AT0
41 54 00
寄存器方式配置
掉电后保存状态
- 前置指令
// ATJ (进入寄存器配置模式)
41 54 4A
// ATJX (退出寄存器配置模式)
41 54 4A 58
// ATQ (退出配置模式)
41 54 51
- 常用指令
// 设置工作模式为 C1
41 54 4A 01 0A
// 设置工作角色为 Slave
41 54 4A 03 00
// 安装模式设置为 Listing
41 54 4A 36 02
// 打开串口收发 LED
41 54 4A 38 02
// 设置发射功率为14db
41 54 4A 00 07
// 设置休眠模式使能(0禁止、3延时使能)
41 54 4A 04 01
// 串口速率19200
41 54 4A 30 19200
/***********配置设备信息M字段 和A字段********/
//M1 设置为FF
41 54 4A 20 FF
//M2
41 54 4A 21 FF
//ID1
41 54 4A 22 FF
//ID2
41 54 4A 23 FF
//ID3
41 54 4A 24 FF
//ID4
41 54 4A 25 FF
//VER
41 54 4A 26 FF
//DEV
41 54 4A 27 FF
/***********配置设备信息M字段 和A字段----EDN******/
/****** 数据接收格式******/
//增加CRC
41 54 4A 35 08
// 00 数据包和ID(默认情况)
// 01 应用层数据
// 02 03 保留
// 04 增加起始和终止字
// 08 增加CRC
// 0C 增加起始字、终止字和CRC
/****** 数据接收格式******/
// 设置C 字段的值
41 54 4A 11 06
//开启RSSI 信号强度(0失能,1使能)
41 54 4A 37 01
- 不常用
// 设置前导码长度为 短 (S模式下才有该设置)
41 54 4A 10 00
// RF 接收超时10毫秒
41 54 4A 13 10
// 串口流控CTS 使能
41 54 4A 31 01
// T_C 兼容模式 使能
41 54 4A 05 01
// 通道号 设置为0A(默认0x01)(仅适用于R模式)
41 54 4A 02 0A
// 温度校准 有符号数,用来保存偏差值
41 54 4A 39 20
// 超时时间 <1-254> 也就是2^8-2 默认值为0x7D
41 54 4A 3B 01
- 获取模块数据
// 其次进入AT 命令
41 54
// 然后查询你想要的寄存器的数值 ATY 20 即使M1字段 的值
< 41 54 59 20
> F8 4F 4B // 得到的返回值 F8 OK
// 比如要查询发射功率
< 41 54
< 41 54 59 00
> 07 4F 4B // 得到的返回值 07 OK (07 对应的就是14dBm)
数据解包
作为 Master 模式, 只负责接收数据并解包
数据发包的时候会自动添加M、A、Ver、Dev字段,接收的时候自动添加L、CRC 字段,或者RSSI (如果配置的话)
数据解析代码
#include "stdio.h"
#include "stdint.h"
//typedef unsigned char uint8_t;
//typedef unsigned short uint16_t;
typedef struct {
//uint8_t start; // 判断第一个结构体的值是否为正常 ===== 这里没有start 字段,就目前的数据帧格式,不应该会出现它
uint8_t len;
uint8_t c_field;
uint16_t m_field; //2个字节
//uint8_t m_field[2]; //2个字节
uint8_t a_field[6];
uint8_t ci_field;
char* data[255]; //结构体中默认开启的默认缓存可能是8(long类型),所以在下方的 数据解析函数中,i>8的时候 就会栈溢出
//uint8_t rssi;
//uint16_t crc;
//uint8_t stop;
} wm_data_t;
/**
* 数据解析接口
* @param data
* @param item
*/
void wmbus_data_decode(const char data[], wm_data_t * item) {
// TODO 数据解析
// 此处的decode 用于将data 赋值到item 中,item 传入一个空的结构体参数,
/*
思路: 拿到len 解析出来,然后将
char* data[] 赋值出来。
*/
item->len = data[0];
item->c_field = data[1];
//item->m_field[0] = data[2];
//item->m_field[1] = data[3];
item->m_field = (data[2] << 8) + data[3]; // 使用左移运算符实现16进制 的拼接。
for (size_t i = 0; i < 6; i++)
{
item->a_field[i] = data[4+i];
}
item->ci_field = data[10];
for (size_t i = 0; i < item->len - 10; i++)
{
item->data[i] = data[11 + i];
}
}
/**
* 打印数据内容
* @param item
*/
void wmbus_show(wm_data_t* item) {
short dataLen = item->len -10;
printf("字段长度:%d \n", sizeof(*(item->data))); //2个字节
//printf("%x\n", item->start);
printf("len字段为:%x\n", item->len);
printf("c_field字段:%x\n", item->c_field);
/************** M_field 和 A_field 字段表示的是Master 发布数据的信息 ********************/
//printf("m_field 字段的数值:%x,%x\n",item->m_field[0], item->m_field[1]);
printf("m_field 字段的数值:%x\n",item->m_field);
printf("a_field 字段的数值:");
for (int i = 0; i < sizeof(item->a_field); i++)
{
printf(" %x", item->a_field[i]);
}
printf("\n");
/**************************************************************************************/
printf("CI_field 字段:%d\n", item->ci_field); // CI 控制字段,用来说明 表示出接下来的数据的架构(一层或者多层)
// 下方打印data 数据
printf("data: ");
printf(" item-> dataLen = %d\n", dataLen);
for (int i = 0;i < dataLen;i++)
{
printf(" %x", (item->data)[i]);
}
printf("\n");
}
int main() {
char data[] = {
0x15, 0x46,
0xF8, 0x02, 0xFA, 0xB2, 0x66, 0x02, 0xB6, 0xAA, // 这里是M - A 字段
0x7A,
0x00, 0x01, 0x02, 0x03, 0x04, 0x05,0x06, 0x07, 0x08, 0x09, 0x0A // data 字段
};
wm_data_t item = { 0 };
wmbus_data_decode(&data, &item); // 传入地址节约空间(防止内存拷贝),
wmbus_show(&item);
system("pause");
return 0;
}
字段的解释
- 704模块发送的帧格式: Data-field 第一个Data 最多只有15 字节,而后每一个Data 最多只有16,且后面都会跟一个CRC 校验位
L C M A-field 是一起校验的,CI 和第一个Data 的数据 是另外一个校验位
CI-Field 字段包头,CI-Field 决定应用层数据包的头部结构
如果是长包头,那么应用层数据包还包括厂商信息,版本信息等
短包头:0x5A 、61、65、6A、6E、74、7A、7B、7D、7F、8A
长包头:0x5B、60、64、6B、6C、6D、6F、72、73、75、7C、7E、80、84、85、8B
-
这个软件之前串口发送 0xFF 的含义是:唤醒设备,(如果设备设置了低功耗模式,休眠的时候不会收到RF 数据包,需要通过串口命令“0xFF" 来唤醒)
-
C和CI 的字段解释
CI字段参考地址
国标参考地址 (国标GB T 26831.3)
欧标参考地址 (欧标EN 13757-3:2004)
//C 字段 46 //表示 SDN-IR(Slaver-> Master) 发送安装请求,在安装模式下 44 //SND-NR (Slaver->Master) 08 //RSP-UD(Slaver->Master) 00 //ACK(Slaver->Master) 06 //CNF-IR(Master->Slaver) 接收到安装请求 53 //SND-UD(Master->Slaver) 5B //REQ-UD2(Master->Slaver) //CI 字段 70 //Slaver->Master 应用层错误报告(可选项) 7A //Slaver->Master 可变格式数据的应答跟随4 字节数据头 72 //Slaver->Master 可变格式数据跟随12 字节数据头 6d //60~6F 保留 80 //7B~80 保留 08 //基于DLMS 应用的保留 5b //55~5B 保留
安装和绑定:
-
手动绑定:
安装可以在现场使用 C-Field 字段 发送 安装请求帧(SND-IR) ,Master 应答安装响应帧(CNF-IR)进行部署。模式需要设置为安装模式。
// TODO : 目前来讲,这里使用AT命令,掉电之后也不会丢失
//AT 进入
41 54
//ATB 绑定 假设device ID 为1, 设备号为后面的 安装模式设置为1之后再进行绑定 第四个字节是device ID
41 54 42 01 F8 02 FA B2 62 02 B6 BB
//ATL 读绑定的ID 这里读取01 当然这个也是要先进AT
41 54 4C 01
-
自动安装:
通过ATJ指令来配置。 参照AT 的指令来配置ATJ 命令
使用ATT 来进行测试
// Master
// 进入ATJ 模式
41 54 4A
// Master设置为安装模式 01
41 54 36 01
// Slaver
// Slave 发送安装请求(SND-IR)
// 配置地址
41 54 54 F8 02 FA DD CC BB AA B6
// Master 自动应答ATW 参数为 L+ NUM+ C+CI+ DATA 返回 应答安装帧(CNF-IR)
加密解密:
- 开启加密
// AT 进入配置界面
41 54
// ATA 加密使能,设置值: ID + TYPE + SIGN 三个字节 sign: 00->none, 80->Encryption, 40->Decryption,C0->Both
41 54 41 01 00 C0
// ATQ 退出配置模式
41 54 51
- 设置用户密钥
/********* Master **********/
// 进入 AT 模式
41 54
// ATK 设置用户密钥 01终端 Device地址序列号 () 只能Master 配置
// 如果第一字节为1,则后面16 字节为transfer key(密文)
// 第一字节为0,后面16 字节为key(明文)
41 54 4B 00 0A 0B 0C 0D 0E 0F 01 02 03 04 05 06 07 08 09 00
/******************测试******************/
密钥: 0A 0B 0C 0D 0E 0F 01 02 03 04 05 06 07 08 09 00
0A0B0C0D0E0F01020304050607080900
/******************测试******************/
/********* Slaver ***********/
//进入ATJ 模式
41 54 4A
// 在软件那一栏是配置默认密钥。
// ATJ根据寄存器来进行配置密钥,一个寄存器保存一个位,所以需要16个寄存器来保存,40~4F
41 54 4A 40 0A 41 0B 42 0C 43 0D 44 0E 45 0F 46 01 47 02 48 03 49 04 4A 05 4B 06 4C 07 4D 08 4E 09 4F 00
//ATJX 退出ATJ
41 54 4A 58
//ATQ 退出配置模式
41 54 51
// 配置密钥绑定,Master 绑定 Slaver
数据记录
- Slaver发送 Master 收
Slaver发送 | ||
---|---|---|
C-field | CI-field | Data |
5b | 80 | AAAAAAAAAAAAAAAA021700C02F2F |
Master接收 | |||||
---|---|---|---|---|---|
L-Field | C | M | A Ver DEV | CI | Data |
18 | 5B | 02F8 | 0262B2FA B6 AA | 80 | AAAAAAAAAAAAAAAA021700C02F2F |
- Master发 Slaver 收
Master发送 | ||
---|---|---|
C-field | CI-field | Data |
5b | 08 | 000102030405060708090A |
Slaver接收 | |||||
---|---|---|---|---|---|
L-Field | C | M | A Ver DEV | CI | Data |
16 | 5B | 0100 | 01006600 01 22 | 08 | 000102030405060708090A |
其它需要了解的部分
L固定式数据长度: 不包含自身、起始字节、CRC字节、结束字节。(如果设置了RSSI 就包含RSSI )
FC-703 发送帧格式: 采用格式A发送,也就是
-
CRC 校验算法:
参考资料
WMbus CRC
- https://patentimages.storage.googleapis.com/78/4e/5f/657609af97180a/CN105931445A.pdf
- https://www.st.com/resource/en/application_note/an4772-wmbus-2013-firmware-stack-overview-stmicroelectronics.pdf
- https://stackoverflow.com/questions/43052658/how-to-calculate-the-crc16-for-wireless-m-bus-messages
CRC 计算
https://www.lddgo.net/encrypt/crc
CRC 算法
https://github.com/E5PRO5-2020/crc16_wmbus/blob/master/crc16_wmbus.py