diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/README.md b/APP_Framework/Applications/app_test/test_lora_net_final/README.md new file mode 100755 index 000000000..f74ac38f5 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/README.md @@ -0,0 +1,187 @@ +# 基于初赛二级赛题1,实现多个LoRa节点和LoRa网关通信私有协议 +## 1. 简介 +基于 E220-400T22S Lora模块设计实现通信私有协议,主要包含以下几个方面:
+- 协议实现完全遵循LoRa Adapter代码规范,可以直接使用原有的Adapter相关函数基于本协议构建Lora网络。 +- 重新设计实现E220底层函数,增加了IOCTL函数实现,支持在运行时对地址、信道、通信模式、空中速率等参数的动态配置。 +- E220模块只支持单通道通信,本协议基于定向传输模式实现了多下行信道通信。 +- 支持数据帧完整性校验、支持数据帧丢失或者跳帧检测。 +- 支持节点断网自动重联,在数据发送失败时可以进行重联或者搜索其他网关。 +- 支持节点对网关的搜索,可以主动寻找在线网关入网。 +- 支持信道冲突检测,检测上行通道是否占用。 +## 2. 数据结构说明 +### E220模块相关数据结构 + +```c +enum LoraMode // E220工作模式,支持四种,配置模式与休眠模式一致 +{ + MODE_TRANSFER_MODE = 0, // M0 : M1 = 0 : 0 可发可收 + MODE_WOR_SEND, // M0 : M1 = 1 : 0 可发可收 + MODE_WOR_RECV, // M0 : M1 = 0 : 1 可收不发 + MODE_CONFIG_SLEEP // M0 : M1 = 1 : 1 不收不发 +}; + +enum LoraState // 硬件状态 +{ + STATE_CLOSED, // 硬件关闭 + STATE_OPENED, // 已正常开启 + STATE_BROKEN // 硬件损坏 +}; + +enum LoraTransMode // 模块传输模式 +{ + TRANS_MODE_HYALINE = 0X0, // 透明传输 + TRANS_MODE_ORIENTED = 0X40, // 定点传输 +}; + +enum LoraAirRate // 空中速率,通信双方必须设置一致的空中速率方可进行通信 +{ + AIR_RATE_2D4K = 0X0, // 2.4K + AIR_RATE_4D8K = 0X3, // 4.8K + AIR_RATE_9D6K = 0X4, // 9.6K + AIR_RATE_19D2K = 0X5, // 19.2K + AIR_RATE_38D4K = 0X6, // 38.4K + AIR_RATE_62D5K = 0X7 // 62.5K +}; + +enum LoraConfig // 支持IOCTL函数进行配置的配置项枚举,某些项只支持9600波特率配置 +{ + CONFIG_SERIAL_TIME_OUT = 0X0, // 串口超时时间 + CONFIG_LORA_MODE = 0X1, // 配置Lora模式 + CONFIG_TRANS_MODE = 0X2, // 配置Lora传输模式(9600) + CONFIG_AIR_RATE = 0X3, // 配置空中速率(9600) + CONFIG_BAUD_RATE = 0X4, // 配置波特率 + CONFIG_ADDRESS = 0X5, // 配置设备地址(9600) + CONFIG_CHANNEL = 0X6, // 配置设备信道(9600) + CONFIG_SHOW = 0X7 // 打印配置信息 +}; +``` + +### 数据帧相关数据结构 + +```c +enum FrameType // 数据帧类型枚举 +{ + E_G_JOIN = 0, // 客户端入网请求,需回复 + E_G_QUIT, // 客户端退网请求,不需回复 + E_G_DATA, // 客户端上传数据,需回复 + G_E_ANS, // 网关响应客户端请求 +}; + +struct LoraFrame // 数据帧消息类型 +{ + uint16 begin_mark; // 开始标志 0x3C3C + uint16 session_id; // 会话ID,客户端每次数据上传会随机设置,若是回复ID不一致则表示数据丢失 + uint16 dev_addr; // 设备地址,特指终端地址,网关地址固定为 0XFFFF + uint8 down_channel; // 下行通道,即终端所在的信道 + uint8 frame_type; // 数据帧的类型 + uint8 frame_confirm; // 该数据帧是否需要确认回复 + uint8 frame_attach; // 用于携带简单的数据,例如用户数据长度 + uint8 *user_data; // 需要额外携带的大量数据 + uint8 crc_hi; // 校验位高字节 + uint8 crc_lo; // 校验位低字节 + uint16 end_mark; // 结束标志 0X5A5A +}; +``` + +### Lora节点相关数据结构 + +```c +enum EndNodeState //节点状态 +{ + NODE_STATE_DISCONNECT = 0, // 硬件开启,但是没有联网,后台程序可能开启 + NODE_STATE_CONNECT, // 硬件开启,已经联网,后台程序可能开启 + NODE_STATE_BROKEN, // 硬件损坏,无后台程序 + NODE_STATE_CLOSED // 硬件关闭,无后台程序 +}; + +struct EndNodeParam // 节点参数结构 +{ + uint16 dev_addr; // 设备地址信息 + uint8 down_channel; // 下行通道,终端可以接收信息的信道 + uint8 upload_channel; // 上行通道,终端可以给网关发送信息的信道 + uint16 session_id; // 会话ID,用于判定数据帧是否存在丢失 + + enum EndNodeState node_state; // 客户端状态 + enum LoraAirRate air_rate; // 终端的空中速率 + uint8 adr_enable; // 终端是否开启自动寻找上行网关 + uint8 reconnect_enable; // 终端断网自动重联 + uint8 recv_time; // 接受下行消息的时间窗口长度,单位为秒 +}; +``` + +### Lora网关相关数据结构 + +```c +struct EndNodeInfo // 网关存储的客户端信息 +{ + uint16 node_addr; // 客户端地址 + uint8 node_down_channel; // 客户端下行通道 + uint16 session_id; // 上一个命令的会话ID +}; + +enum GatewayState // 网关的状态枚举 +{ + GATEWAY_CLOSED, // Lora还没有打开 + GATEWAY_OPENED, // 已打开,但是未开始工作 + GATEWAY_WORKING, // 正常打开且已经开始正常工作 + GATEWAY_BROKEN // 模块损坏 +}; + +struct GatewayParam // 网关参数 +{ + uint16 dev_addr; // 设备ID + uint8 channel; // 网络编号 + + int recv_time; // 串口超时时间 + enum LoraAirRate air_rate; // 网关空中速率 + uint8 frame_retry; // 重传次数 + enum GatewayState gateway_state; // 网关状态 + struct EndNodeInfo *node_infos[LORA_GATEWAY_MAX_NODE]; // 客户端信息 + uint16 node_count; // 已链接客户端数量 +}; +``` + +## 3. 测试程序说明 +- 编译烧录两个不同地址、信道的客户端和一个网关。 +- 分别打开客户端和网关并联网。 +- 两个客户端分别向网关发送数据。 +## 4. 运行结果 +### (1)编译模块: +- 配置MENUCONFIG +![image](./images/0-1.png) +> 开启连接框架支持,同时开启Lora Adapter支持 + +![image](./images/0-2.png) +> 选择E220模块 + +![image](./images/0-3.png) +> 开启测试函数 + +![image](./images/0-4.png) +> 测试单通道网关 + +- 修改MakeFile:xiuos/APP_Framework/Applications/app_test/Makefile第141行修改为 +```c + SRC_FILES += test_lora_net_final/lora_driver/e220.c + SRC_FILES += test_lora_net_final/lora_mac/lora_mac.c + SRC_FILES += test_lora_net_final/test_lora_net_final.c +``` +- 修改框架初始函数: +> 1. 文件:xiuos/APP_Framework/Framework/framework_init.c 第27行修改为
extern int UsrAdapterLoraInit(void); +> 2. 文件:xiuos/APP_Framework/Framework/framework_init.c 第170行的AdapterLoraInit修改为UsrAdapterLoraInit +### (2)打开网关 +![image](./images/1.png) +> 网关打开,地址OXFFFF,信道OXA,空中速率2.4K + +### (3)客户端连接网关 +![image](./images/2.png) +> 两边客户端同时发送入网请求,可以看见信道检测效果 + +### (4)客户端发送数据 +![image](./images/3.png) +> 客户端向网关发送数据 + +![image](./images/4.png) +> 两边客户端同时发送数据 + +使用自带的天线,2.4K空中速率,载荷64字节,在无大型遮盖物的情况下传输距离近1500米 \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/0-1.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-1.png new file mode 100755 index 000000000..e3e3a8e74 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-1.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/0-2.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-2.png new file mode 100755 index 000000000..a24c14716 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-2.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/0-3.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-3.png new file mode 100755 index 000000000..544c91b43 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-3.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/0-4.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-4.png new file mode 100755 index 000000000..954d436c8 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/0-4.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/1.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/1.png new file mode 100755 index 000000000..77b3a5e7f Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/1.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/2.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/2.png new file mode 100755 index 000000000..4675aef37 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/2.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/3.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/3.png new file mode 100755 index 000000000..337d3de50 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/3.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/images/4.png b/APP_Framework/Applications/app_test/test_lora_net_final/images/4.png new file mode 100755 index 000000000..a28da7bad Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_net_final/images/4.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.c b/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.c new file mode 100755 index 000000000..cff76d97a --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.c @@ -0,0 +1,570 @@ +#include "e220.h" + +struct RegisterCfg // 寄存器结构 +{ + uint8 ADDH; + uint8 ADDL; // 地址 + uint8 REG0; // 波特率/串口校验/空中速率 + uint8 REG1; // 分包设定/RSSI环境噪声/发射功率 + uint8 REG2; // 信道 + uint8 REG3; // RSSI字节/传输方式/LBT使能/WOR周期 + uint8 KEYH; + uint8 KEYL; // 密钥 +}; + +/** + * 默认的寄存器配置信息,在每次配置更新后会同步更新该数据,保持其与硬件寄存器数据的一致性 +*/ +static struct RegisterCfg register_cfg = +{ + .ADDH = (E220_ADDRESS >> 8) & 0XFF, + .ADDL = E220_ADDRESS & 0XFF, + .REG0 = E220_DEFAULT_CONFIG_BAUD_RATE_BYTE | E220_DEFAULT_AIR_RATE, + .REG1 = 0X0, + .REG2 = E220_DEFAULT_CHANNEL, + .REG3 = E220_DEFAULT_TRANS_MODE, + .KEYH = 0X0, + .KEYL = 0X0, +}; + +/** + * 默认的串口配置信息,初始化时波特率为9600,保持与硬件一致,在波特率更改过程中也需要保证一致 +*/ +static struct SerialDataCfg serial_cfg = +{ + .serial_baud_rate = E220_DEFAULT_CONFIG_BAUD_RATE, // 串口波特率 + .serial_data_bits = DATA_BITS_8, + .serial_stop_bits = STOP_BITS_1, + .serial_parity_mode = PARITY_NONE, + .serial_bit_order = BIT_ORDER_LSB, + .serial_invert_mode = NRZ_NORMAL, + .serial_buffer_size = SERIAL_RB_BUFSZ, + .serial_timeout = E220_DAFAULT_SERIAL_TIMEOUT, // 串口超时配置 + .is_ext_uart = 0, +}; + +enum LoraMode current_mode = -1; // 当前模块处于什么模式 +enum LoraState lora_state = STATE_CLOSED; // 当前模块的状态 +uint8 lora_init = E220_NO; // 当前模块是否已经被初始化过了 + +/** + * @brief: 将硬件切换至指定的模式 + * @param mode: 支持休眠/配置模式 和 传输模式 +*/ +static uint8 E220LoraModeConfig(enum LoraMode mode) +{ + // 模式和状态判断 + if (lora_state == STATE_BROKEN) + { + DDBG("E220LoraModeConfig-Fail: lora broken!\n"); + return -1; + } + if(mode == current_mode) + { + // 同一个模式直接返回,不输出信息 + return 0; + } + // 打开PIN驱动 + int pin_fd = PrivOpen(E220_PIN_DRIVER,O_RDWR); + if (pin_fd < 0) + { + DDBG("E220LoraModeConfig-Fail: open pin driver fail %s!\n",E220_PIN_DRIVER); + lora_state = STATE_BROKEN; + return -1; + } + // 配置管脚属性 + struct PinParam pin_param ={.cmd = GPIO_CONFIG_MODE,.mode = GPIO_CFG_OUTPUT,.pin = E220_M0_PATH}; + struct PrivIoctlCfg ioctl_config = {.ioctl_driver_type = PIN_TYPE, .args = &pin_param}; + if (0 != PrivIoctl(pin_fd,OPE_CFG,&ioctl_config)) + { + DDBG("E220LoraModeConfig-Fail: config m0 fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + pin_param.pin = E220_M1_PATH; + ioctl_config.args = &pin_param; + if (0 != PrivIoctl(pin_fd,OPE_CFG,&ioctl_config)) + { + DDBG("E220LoraModeConfig-Fail: config m1 fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + + struct PinStat pin_stat; + switch (mode) + { + case MODE_TRANSFER_MODE: + { + pin_stat.pin = E220_M0_PATH; + pin_stat.val = GPIO_LOW; + if (-1 == PrivWrite(pin_fd, &pin_stat, 1)) + { + DDBG("E220LoraModeConfig-Fail: config m0 low fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + pin_stat.pin = E220_M1_PATH; + pin_stat.val = GPIO_LOW; + if (-1 == PrivWrite(pin_fd, &pin_stat, 1)) + { + DDBG("E220LoraModeConfig-Fail: config m1 low fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + break; + } + case MODE_CONFIG_SLEEP: + { + pin_stat.pin = E220_M0_PATH; + pin_stat.val = GPIO_HIGH; + if (-1 == PrivWrite(pin_fd, &pin_stat, 1)) + { + DDBG("E220LoraModeConfig-Fail: config m0 high fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + pin_stat.pin = E220_M1_PATH; + pin_stat.val = GPIO_HIGH; + if (-1 == PrivWrite(pin_fd, &pin_stat, 1)) + { + DDBG("E220LoraModeConfig-Fail: config m1 high fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + break; + } + default: + DDBG("E220LoraModeConfig-Fail: unsupported lora mode!\n"); + PrivClose(pin_fd); + return -1; + } + PrivTaskDelay(E220_CONFIG_DELAY); + PrivClose(pin_fd); + // 记录当前模式,避免不必要的重复配置 + current_mode = mode; + DDBG("E220LoraModeConfig-Success: change to mode %s !\n",mode == MODE_CONFIG_SLEEP ? "MODE_CONFIG_SLEEP" : "MODE_TRANSFER_MODE"); + return 0; +} + +/** + * @brief: 通过指令获取硬件寄存器参数后将其保存并输出 + * @param adapter: 需要打开 +*/ +static void PrintRegister(char* prefix) +{ + // 打印数据 + DDBG("%s Register:",prefix); + DDBG("ADDH:%X,ADDL:%X,REG0:%X,",register_cfg.ADDH,register_cfg.ADDL,register_cfg.REG0); + DDBG("REG1:%X,REG2:%X,REG3:%X,",register_cfg.REG1,register_cfg.REG2,register_cfg.REG3); + DDBG("KEYH:%X,KEYL:%X\n",register_cfg.KEYH,register_cfg.KEYL); +} + +/** + * @brief: 根据配置的数据覆写硬件寄存器参数 + * @param adapter: 一个已经打开了UART串口的Lora硬件 + * @param start_addr: 需要覆写的寄存器起始地址 + * @param byte_length: 需要覆写的寄存器数量 + * @param args: 需要覆写的寄存器参数 + * @return: 0 -> Success; -1 -> Fail +*/ +static uint8 WriteRegister(struct Adapter *adapter,uint8 start_addr,uint8 byte_length,void* args) +{ + if (STATE_BROKEN == lora_state) + { + DDBG("WriteRegister-Fail: lora broken!\n"); + return -1; + } + // 切换硬件到配置模式 + E220LoraModeConfig(MODE_CONFIG_SLEEP); + // 构造寄存器覆写数据 + uint8 buffer[11] = {0}; + buffer[0] = 0XC0; + buffer[1] = start_addr; + buffer[2] = byte_length; + memcpy(&buffer[3],args,byte_length); + // 覆写寄存器参数 + if (PrivWrite(adapter->fd, (void *)buffer, byte_length + 3) < 0) + { + PrintRegister("WriteRegister-Fail"); + lora_state = STATE_BROKEN; + return -1; + } + // 等待配置完成后读取回写数据 + PrivTaskDelay(E220_CONFIG_DELAY); + PrivRead(adapter->fd, buffer, byte_length + 3); + PrintRegister("WriteRegister-Success"); + return 0; +} + +/** + * @brief: 根据默认的配置对一个已经打开UART串口的Lora硬件进行寄存器参数的初始化,开启后只会被调用一次 + * @param adapter: 需要打开 +*/ +static uint8 E220RegisterInit(struct Adapter *adapter) +{ + if(STATE_BROKEN == lora_state) + { + DDBG("E220RegisterInit-Fail: lora broken!\n"); + return -1; + } + if (lora_init == E220_YES) + { + DDBG("E220RegisterInit-Success: inited before!\n"); + return 0; + } + // 按照默认的参数进行寄存器初始化 + if (0 != WriteRegister(adapter,0X0,0X8,®ister_cfg)) + { + lora_state = STATE_BROKEN; + return -1; + } + // 防止重复初始化 + lora_init = E220_YES; + DDBG("E220RegisterConfig-Success!\n"); + return 0; +} + +uint8 E220Open(struct Adapter *adapter) +{ + if (lora_state == STATE_BROKEN) + { + DDBG("E220Open-Fail: lora broken!\n"); + return -1; + } + if (lora_state == STATE_OPENED) + { + // 避免重复打开 + return 0; + } + // 打开UART串口 + adapter->fd = PrivOpen(E220_UART_DRIVER,O_RDWR); + if (adapter->fd < 0) + { + DDBG("E220Open-Fail: open uart fail - %s \n",E220_UART_DRIVER); + lora_state = STATE_BROKEN; + return -1; + } + // 按照默认串口配置信息初始化串口 + struct PrivIoctlCfg ioctl_cfg; + ioctl_cfg.ioctl_driver_type = SERIAL_TYPE; + ioctl_cfg.args = &serial_cfg; + if (0 != PrivIoctl(adapter->fd, OPE_INT,&ioctl_cfg)) + { + DDBG("E220Open-Fail: uart config fail - %s \n",E220_UART_DRIVER); + lora_state = STATE_BROKEN; + return -1; + } + // 硬件的寄存器初始化 + if (0 != E220RegisterInit(adapter)) + { + DDBG("E220Open-Fail: register config fail!\n"); + lora_state = STATE_BROKEN; + return -1; + } + lora_state = STATE_OPENED; + DDBG("E220Open-Success: e220 open success!\n"); + return 0; +} + +uint8 E220Close(struct Adapter *adapter) +{ + if (lora_state != STATE_OPENED) + { + DDBG("E220Close-Success: broken or closed before!\n"); + return 0; + } + // 关闭UART串口 + if (adapter->fd > 0) + { + if (0 != PrivClose(adapter->fd)) + { + DDBG("E220Close-Failed: result != 0\n"); + lora_state = STATE_BROKEN; + return -1; + } + } + adapter->fd = -1; + lora_init = E220_NO; + lora_state = STATE_CLOSED; + DDBG("E220Close-Success: success!\n"); + return 0; +} + +uint8 E220Ioctl(struct Adapter *adapter, int config_item, void *args) +{ + if (STATE_OPENED != lora_state) + { + DDBG("E220Ioctl-Fail: open lora or the lora is broken!\n"); + return -1; + } + switch (config_item) + { + // 配置Lora串口超时时间 + case CONFIG_SERIAL_TIME_OUT: + { + int32 time_out = *((int32*)args); + if (time_out == serial_cfg.serial_timeout) + { + DDBG("ConfigSerialTimeOut-Success: the same time_out %d !\n", time_out); + return 0; + } + + serial_cfg.serial_timeout = time_out; + struct PrivIoctlCfg ioctl_cfg = { .ioctl_driver_type = SERIAL_TYPE,.args = &serial_cfg }; + // 修改串口配置信息 + if (0 != PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg)) + { + DDBG("ConfigSerialTimeOut-Fail: %d\n", serial_cfg.serial_timeout); + lora_state = STATE_BROKEN; + return -1; + } + DDBG("ConfigSerialTimeOut-Success: %d\n", serial_cfg.serial_timeout); + return 0; + } + // 配置Lora模式 + case CONFIG_LORA_MODE: + { + enum LoraMode mode = *((enum LoraMode*)args); + if (mode == current_mode) + { + DDBG("ConfigLoraMode-Success: the same mode %s!\n",mode == MODE_TRANSFER_MODE ? "MODE_TRANSFER_MODE" : "MODE_CONFIG_SLEEP"); + return 0; + } + if (mode == MODE_TRANSFER_MODE || mode == MODE_CONFIG_SLEEP) + { + if (0 != E220LoraModeConfig(mode)) + { + DDBG("ConfigLoraMode-Fail: config mode fail %s!\n",mode == MODE_TRANSFER_MODE ? "MODE_TRANSFER_MODE" : "MODE_CONFIG_SLEEP"); + return -1; + } + DDBG("ConfigLoraMode-Success: change lora mode to %s!\n",mode == MODE_TRANSFER_MODE ? "MODE_TRANSFER_MODE" : "MODE_CONFIG_SLEEP"); + return 0; + } + DDBG("ConfigLoraMode-Fail: unsupported lora mode %x!\n",mode); + return -1; + } + // 配置Lora传输模式 + case CONFIG_TRANS_MODE: + { + if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE) + { + DDBG("ConfigTransMode-Fail: only config by baud rate 9600!\n"); + return -1; + } + uint8 trans_mode = *((uint8*)args); + if (trans_mode == TRANS_MODE_HYALINE || trans_mode == TRANS_MODE_ORIENTED) + { + // 判断参数是否有必要覆写 + if (register_cfg.REG3 & 0X40 == trans_mode) + { + DDBG("ConfigTransMode-Success: the same trans mode %x!\n",trans_mode); + return 0; + } + trans_mode = register_cfg.REG3 & 0xBF | trans_mode; + if (0 != WriteRegister(adapter,0X5,1,&trans_mode)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigTransMode-Fail: config trans mode fail %x!\n",trans_mode); + return -1; + } + register_cfg.REG3 = trans_mode; + DDBG("ConfigTransMode-Success: config trans mode success %x!\n",trans_mode); + return 0; + } + DDBG("ConfigTransMode-Fail: unsupported trans mode %x!\n",trans_mode); + return -1; + } + // 配置Lora空中速率 + case CONFIG_AIR_RATE: + { + if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE) + { + DDBG("ConfigAirRate-Fail: only config by baud rate 9600!\n"); + return -1; + } + uint8 air_rate = *((uint8*)args); + if (air_rate >= AIR_RATE_2D4K && air_rate <= AIR_RATE_62D5K) + { + // 判断参数是否有必要覆写 + if (register_cfg.REG0 & 0X7 == air_rate) + { + DDBG("ConfigAirRate-Success: the same air rate %x!\n",air_rate); + return 0; + } + air_rate = ((register_cfg.REG0 >> 3) << 3) | air_rate; + if (0 != WriteRegister(adapter,0X2,1,&air_rate)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigAirRate-Fail: write register fail %x!\n",air_rate); + return -1; + } + register_cfg.REG0 = air_rate; + DDBG("ConfigAirRate-Success: config air rate success %x!\n",air_rate & 0X7); + return 0; + } + DDBG("ConfigAirRate-Fail: unsupported air rate %x!\n",air_rate); + return -1; + } + // 配置Lora的波特率 + case CONFIG_BAUD_RATE: + { + uint8 arg_rate = *((uint8*)args); + uint8 temp_rate = arg_rate; + if (arg_rate == E220_DEFAULT_CONFIG_BAUD_RATE_BYTE || arg_rate == E220_DEFAULT_USED_BAUD_RATE_BYTE) + { + // 判断参数是否有必要覆写 + if (register_cfg.REG0 & 0XE0 == arg_rate) + { + DDBG("ConfigBaudRate-Success: the same baud rate %x!\n",arg_rate); + return 0; + } + temp_rate = register_cfg.REG0 & 0X1F | arg_rate; + // 先更改寄存器的波特率 + if (0 != WriteRegister(adapter,0X2,1,&temp_rate)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigBaudRate-Fail: write register config fail %x!\n",temp_rate); + return -1; + } + register_cfg.REG0 = temp_rate; + // 重新配置串口波特率 + serial_cfg.serial_baud_rate = arg_rate == E220_DEFAULT_CONFIG_BAUD_RATE_BYTE ? BAUD_RATE_9600 : BAUD_RATE_115200; + struct PrivIoctlCfg ioctl_cfg; + ioctl_cfg.ioctl_driver_type = SERIAL_TYPE; + ioctl_cfg.args = &serial_cfg; + // 重新配置串口波特率 + if (0 != PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigBaudRate-Fail: config serail fail %x!\n",temp_rate); + return -1; + } + DDBG("ConfigBaudRate-Success: config baud rate success %d !\n",arg_rate == E220_DEFAULT_CONFIG_BAUD_RATE_BYTE ? BAUD_RATE_9600 : BAUD_RATE_115200); + return 0; + } + DDBG("ConfigBaudRate-Fail: unsupported baud rate %x!\n",arg_rate); + return -1; + } + // 配置Lora地址参数 + case CONFIG_ADDRESS: + { + if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE) + { + DDBG("ConfigAddress-Fail: only config by baud rate 9600!\n"); + return -1; + } + uint16 address = *((uint16*)args); + if ((address >> 8) | 0XFF == register_cfg.ADDH && address & 0XFF == register_cfg.ADDL) + { + DDBG("ConfigAddress-Success: the same address %d!\n",address); + return 0; + } + register_cfg.ADDH = (address >> 8) & 0XFF; + register_cfg.ADDL = address & 0XFF; + if (0 != WriteRegister(adapter,0X0,2,®ister_cfg)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigAddress-Fail: write register fail %d!\n",address); + return -1; + } + DDBG("ConfigAddress-Success: config address success %d!\n",address); + return 0; + } + // 配置Lora信道参数 + case CONFIG_CHANNEL: + { + if (serial_cfg.serial_baud_rate != E220_DEFAULT_CONFIG_BAUD_RATE) + { + DDBG("ConfigChannel-Fail: only config by baud rate 9600!\n"); + return -1; + } + uint8 channel = *((uint8*)args); + if (channel <= E220_MAX_CHANNEL_NUMBER) + { + if (register_cfg.REG2 == channel) + { + DDBG("ConfigChannel-Success: the same channel %d!\n",channel); + return 0; + } + register_cfg.REG2 = channel; + if (0 != WriteRegister(adapter,0X4,1,®ister_cfg.REG2)) + { + lora_state = STATE_BROKEN; + DDBG("ConfigChannel-Fail: write register fail %d!\n",channel); + return -1; + } + DDBG("ConfigChannel-Success: config channel success %X !\n",channel); + return 0; + } + DDBG("ConfigChannel-Fail: unsupported channel %d!\n",channel); + return -1; + } + // 打印配置信息 + case CONFIG_SHOW: + { + DDBG("ConfigShow SerailTime: %d",serial_cfg.serial_timeout); + PrintRegister("CONFIG_SHOW"); + return 0; + } + default: + DDBG("E220Ioctl-Failed: config item unsupported %d!\n",config_item); + return -1; + } +} + +uint8 E220Send(struct Adapter *adapter, const void *buf, uint32 len) +{ + if (STATE_OPENED != lora_state) + { + DDBG("E220Send-Fail: open lora before send data!\n"); + return -1; + } + // 切换至传输模式 + if (0 != E220LoraModeConfig(MODE_TRANSFER_MODE)) + { + DDBG("E220Send-Fail: change mode transfer fail!\n"); + return -1; + } + + if (len != PrivWrite(adapter->fd, (void *)buf, len)) + { + DDBG("E220Send-Fail: send data fail!\n"); + return -1; + } + return 0; +} + +uint8 E220Recv(struct Adapter *adapter, void *buf, uint32 len) +{ + if (STATE_OPENED != lora_state) + { + DDBG("E220Recv-Fail: open lora before recv!\n"); + return -1; + } + // 确保传输模式 + if (0 != E220LoraModeConfig(MODE_TRANSFER_MODE)) + { + return -1; + } + + int recv_len = 0, recv_len_continue = 0; + + uint8 *recv_buf = PrivMalloc(len); + // 读取数据并返回已读取数据量 + recv_len = PrivRead(adapter->fd, recv_buf, len); + if (recv_len) { + while (recv_len < len) { + // 读取超时后退出循环 + recv_len_continue = PrivRead(adapter->fd, recv_buf + recv_len, len - recv_len); + if (recv_len_continue) { + recv_len += recv_len_continue; + } else { + recv_len = 0; + break; + } + } + memcpy(buf, recv_buf, len); + } + PrivFree(recv_buf); + return recv_len; +} diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.h b/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.h new file mode 100755 index 000000000..87ef4d0e8 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/lora_driver/e220.h @@ -0,0 +1,125 @@ +#include +#include + +#ifndef __E220_H__ +#define __E220_H__ + +// #define __DRIVER_DEBUG__ // 驱动调试信息输出开关 +#ifdef __DRIVER_DEBUG__ +#define DDBG(format,...) printf(format, ##__VA_ARGS__) +#else +#define DDBG(format,...) +#endif // __DRIVER_DEBUG__ + +#define E220_M0_PATH 32 // M0管脚位置 +#define E220_M1_PATH 33 // M1管脚位置 +#define E220_PIN_DRIVER "/dev/pin_dev" // PIN驱动名称 +#define E220_UART_DRIVER "/dev/uart2_dev2" // UART驱动名称 +#define E220_ADDRESS 0XA // 地址默认值 +#define E220_DAFAULT_SERIAL_TIMEOUT 3000 // 串口默认3S超时 +#define E220_DEFAULT_CONFIG_BAUD_RATE BAUD_RATE_9600 // 在进行寄存器参数配置时,除了波特率本身的配置外,其他的配置只能在9600进行配置 +#define E220_DEFAULT_CONFIG_BAUD_RATE_BYTE 0X60 // 9600波特率 +#define E220_DEFAULT_USED_BAUD_RATE BAUD_RATE_115200 // 在进行使用时可以切换为该波特率,但是在配置时需要切换回配置波特率(9600) +#define E220_DEFAULT_USED_BAUD_RATE_BYTE 0XE0 // 115200波特率 + +#define E220_MAX_CHANNEL_NUMBER 83 // E220最多支持84个信道,0~83 +#define E220_DEFAULT_CHANNEL 0XA // 默认通道 + +#define E220_DEFAULT_AIR_RATE 0X0 // 默认模块以2.4K的空中速率进行通信 + +#define E220_DEFAULT_TRANS_MODE 0X40 // 默认使用定点传输传输模式 +#define E220_CONFIG_DELAY 1000 // 配置的硬件响应时间 + +#define E220_YES 0XFF +#define E220_NO 0X00 + +enum LoraMode // E220工作模式,支持四种,配置模式与休眠模式一致 +{ + MODE_TRANSFER_MODE = 0, // M0 : M1 = 0 : 0 可发可收 + MODE_WOR_SEND, // M0 : M1 = 1 : 0 可发可收 + MODE_WOR_RECV, // M0 : M1 = 0 : 1 可收不发 + MODE_CONFIG_SLEEP // M0 : M1 = 1 : 1 不收不发 +}; + +enum LoraState // 硬件状态 +{ + STATE_CLOSED, // 硬件关闭 + STATE_OPENED, // 已正常开启 + STATE_BROKEN // 硬件损坏 +}; + +enum LoraTransMode // 模块传输模式 +{ + TRANS_MODE_HYALINE = 0X0, // 透明传输 + TRANS_MODE_ORIENTED = 0X40, // 定点传输 +}; + +enum LoraAirRate // 空中速率,通信双方必须设置一致的空中速率方可进行通信 +{ + AIR_RATE_2D4K = 0X0, // 2.4K + AIR_RATE_4D8K = 0X3, // 4.8K + AIR_RATE_9D6K = 0X4, // 9.6K + AIR_RATE_19D2K = 0X5, // 19.2K + AIR_RATE_38D4K = 0X6, // 38.4K + AIR_RATE_62D5K = 0X7 // 62.5K +}; + +enum LoraConfig // 支持IOCTL函数进行配置的配置项枚举,某些项只支持9600波特率配置 +{ + CONFIG_SERIAL_TIME_OUT = 0X0, // 串口超时时间 + CONFIG_LORA_MODE = 0X1, // 配置Lora模式 + CONFIG_TRANS_MODE = 0X2, // 配置Lora传输模式(9600) + CONFIG_AIR_RATE = 0X3, // 配置空中速率(9600) + CONFIG_BAUD_RATE = 0X4, // 配置波特率 + CONFIG_ADDRESS = 0X5, // 配置设备地址(9600) + CONFIG_CHANNEL = 0X6, // 配置设备信道(9600) + CONFIG_SHOW = 0X7 // 打印配置信息 +}; + +extern enum LoraMode current_mode; // 当前模块处于什么模式 +extern enum LoraState lora_state; // 当前模块的状态 + +/** + * @brief: 打开硬件设施,开启串口并按照配置的初始化参数配置相关硬件、串口 + * 重复开启会直接返回,开启失败后不能重复尝试 + * @param adapter: 任何状态 + * @return: 0 -> Success : -1 -> Fail +*/ +uint8 E220Open(struct Adapter *adapter); + +/** + * @brief: 关闭硬件设施,关闭串口,同时会将所有的数据回复到原始的配置状态 + * 关闭失败默认为硬件损坏,未开启不可关闭 + * @param adapter: 任何状态 + * @return: 0 -> Success (硬件不是开启状态即成功): -1 -> Fail (硬件关闭过错报错) +*/ +uint8 E220Close(struct Adapter *adapter); + +/** + * @brief: 给处于开启状态的硬件配置相关信息,包括串口配置,寄存器参数等等 + * 支持的配置类型为@LoraConfig + * @param adapter: 打开状态 + * @param config_item: 配置类型 + * @param args: 配置参数 + * @return: 0 -> Success : -1 -> Fail +*/ +uint8 E220Ioctl(struct Adapter *adapter, int config_item, void *args); + +/** + * @brief: 通过硬件发送指定长度的数据 + * @param adapter: 打开状态 + * @param buf: 数据缓存区 + * @param len: 缓存区大小 + * @return 0 -> Success(全部发送完毕) : -1 -> Fail(未发送或发送不完整) +*/ +uint8 E220Send(struct Adapter *adapter, const void *buf, uint32 len); + +/** + * @brief: 通过硬件接受指定长度的数据到缓冲区 + * @param adapter: 打开状态 + * @param buf: 接受缓冲区 + * @param len: 需要接受的数据长度,该长度小于等于接受缓存区长度 + * @return 0 -> 接收到了指定长度的数据 : -1 -> 未接受到指定长度的数据或者串口超时 +*/ +uint8 E220Recv(struct Adapter *adapter, void *buf, uint32 len); +#endif // __E220_H__ diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.c b/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.c new file mode 100755 index 000000000..f4fe29621 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.c @@ -0,0 +1,1182 @@ +#include "lora_mac.h" + +static const uint8 crc_hi_table[] = + { + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40 + }; + +static const uint8 crc_lo_table[] = + { + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, + 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, + 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, + 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, + 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, + 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, + 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, + 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, + 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, + 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, + 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, + 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, + 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40 + }; + +// 相关数据缓冲区,全局变量 +struct LoraFrame send_frame; // 数据帧发送缓冲区 +struct LoraFrame recv_frame; // 数据帧接受缓存区 +uint8 send_data_buffer[FRAME_MAX_USER_DATA_LENGTH]; // 数据发送缓冲区 +uint8 recv_data_buffer[FRAME_MAX_USER_DATA_LENGTH]; // 数据接受缓存区 +uint8 total_data_buffer[FRAME_MAX_DATA_LENGTH]; // 数据帧加数据的最大缓冲区 + +void InitLoraFrame(struct LoraFrame* frame) +{ + // 数据清空 + memset(frame,0,sizeof(struct LoraFrame)); + // 设置帧头、帧尾 + frame->begin_mark = FRAME_BEGIN_MARK; + frame->end_mark = FRAME_END_MARK; +} + +void ShowLoraFrame(struct LoraFrame* frame,char* prefix) +{ + printf("%s -> ",prefix); + printf("session_id:%d,dev_addr:%X,down_channel:%X,frame_confirm:%X ",frame->session_id,frame->dev_addr,frame->down_channel,frame->frame_confirm); + switch (frame->frame_type) + { + case E_G_JOIN: + printf("-E_G_JOIN-"); + break; + case E_G_QUIT: + printf("-E_G_QUIT-"); + break; + case E_G_DATA: + printf("-E_G_DATA-"); + printf("user_data:%s ",frame->user_data); + break; + case G_E_ANS: + printf("-G_E_ANS-"); + break; + } + printf("frame_attach:%d,crc_hi:%X,crc_lo:%X\n",frame->frame_attach,frame->crc_hi,frame->crc_lo); +} + +void CalCrcByte(uint8* data, uint8 data_length,unsigned int* temp,uint8* crc_hi,uint8* crc_lo) +{ + for (int i = 0; i < data_length; i++) + { + *temp = *crc_hi ^ *data++; + *crc_hi = *crc_lo ^ crc_hi_table[*temp]; + *crc_lo = crc_lo_table[*temp]; + } +} + +uint8 CalCrlLoraFrame(struct LoraFrame* frame) +{ + uint8 crc_hi = 0xFF; + uint8 crc_lo = 0xFF; + unsigned int temp; + // 转化为字节的方式进行数据访问 + uint8 *bit_arr = (uint8 *)frame; + // 计算除开帧头后面11位元数据的校验值 + CalCrcByte(bit_arr + FRAME_CRC_INDEX_BEGIN,FRAME_CRC_LENGTH,&temp,&crc_hi,&crc_lo); + if (frame->frame_type == E_G_DATA) + { + // 数据传输必须要携带数据,否则就是出错 + if (frame->frame_attach != 0 && frame->user_data != NULL) + { + // 计算用户数据的校验值 + CalCrcByte(frame->user_data,frame->frame_attach,&temp,&crc_hi,&crc_lo); + } + else + { + printf("CalCrlLoraFrame: have no data!\n"); + return -1; + } + } + frame->crc_lo = crc_lo; + frame->crc_hi = crc_hi; + return 0; +} + +uint8 CheckCrcLoraFrame(struct LoraFrame* frame) +{ + uint8 crc_hi_tmp = frame->crc_hi; + uint8 crc_lo_tmp = frame->crc_lo; + + if (0 == CalCrlLoraFrame(frame)) + { + if (crc_hi_tmp == frame->crc_hi && crc_lo_tmp == frame->crc_lo) + { + return 0; + } + } + return -1; +} + +static const uint16 header_mark = FRAME_BEGIN_MARK & 0XFF; // 数据帧头部 +static const uint16 tail_mark = FRAME_END_MARK & 0XFF; // 数据帧尾部 +static uint16 header_buffer = 0; // 用于数据帧头部检测 + +uint8 LoraRecvFrame(struct Adapter *adapter, struct LoraFrame *recv_buffer) +{ + if (lora_state != STATE_OPENED) + { + printf("LoraRecvFrame-Fail: lora closed or broken!\n"); + return -1; + } + // 初始化接受缓冲区 + memset(recv_buffer, 0, sizeof(struct LoraFrame)); + + header_buffer = 0; + // 用于查找数据帧的帧头 + uint8 *header_temp = (uint8*)(&header_buffer); + // 查找数据帧的开始标志 + while (1 == E220Recv(adapter, &header_temp[1], 1)) // 查找开始标志 + { + // 找到了开始标识的一半 + if (header_mark == header_temp[1]) + { + // 判断上一个字节是否是开始标志,若是,则已找到,若不是,则继续 + if (header_temp[1] == header_temp[0]) + { + recv_buffer->begin_mark = FRAME_BEGIN_MARK; + // 接收帧头以下,用户数据以上的数据 + if (FRAME_NECK_LENGTH == E220Recv(adapter, &recv_buffer->session_id, FRAME_NECK_LENGTH)) + { + // 查看是否有用户数据 + if (E_G_DATA == recv_buffer->frame_type) + { + // 如果携带了用户数据则接收用户数据 + if (0 != recv_buffer->frame_attach) + { + // 重置用户数据缓冲区 + memset(recv_data_buffer, 0, FRAME_MAX_USER_DATA_LENGTH); + // 接收用户数据 + if (recv_buffer->frame_attach != E220Recv(adapter, recv_data_buffer, recv_buffer->frame_attach)) + { + printf("LoraRecvFrame-Fail: recv user data fail!\n"); + header_buffer = 0; + break; + } + // 将数据嫁接到数据帧 + recv_buffer->user_data = recv_data_buffer; + } + } + // 开始接受最后的数据 + if (FRAME_FOOT_LENGTH != E220Recv(adapter,&recv_buffer->crc_hi,FRAME_FOOT_LENGTH)) + { + printf("LoraRecvFrame-Fail: recv foot frame fail!\n"); + header_buffer = 0; + break; + } + // 开始检查数据帧尾巴是否正确 + if (FRAME_END_MARK != recv_buffer->end_mark) + { + printf("LoraRecvFrame-Fail: frame end mark err!\n"); + header_buffer = 0; + break; + } + // 开始进行数据帧校验 + if (0 != CheckCrcLoraFrame(recv_buffer)) + { + printf("LoraRecvFrame-Fail: check frame crc fail!\n"); + header_buffer = 0; + break; + } + // 接受数据帧成功 + ShowLoraFrame(recv_buffer,"LoraRecvFrame"); + header_buffer = 0; + return 0; + } + else + { + printf("LoraRecvFrame-Fail: recv neck frame fail!\n"); + header_buffer = 0; + break; + } + } + } + // 将接受到的数据放置在上一个位置 + header_temp[0] = header_temp[1]; + } + header_buffer = 0; + return -1; +} + +uint8 LoraSendFrame(struct Adapter* adapter, struct LoraFrame* send_buffer,uint16 addr,uint8 channel) +{ + // 判断lora是否已经正确开启 + if (lora_state != STATE_OPENED) + { + printf("LoraSendFrame-Fail: lora closed or broken!\n"); + return -1; + } + // 计算数据帧的校验值 + if (0 != CalCrlLoraFrame(send_buffer)) + { + printf("LoraSendFrame-Fail: cal_crc fail!\n"); + return -1; + } + // 记录整个数据帧的长度 + int fram_length = 0; + // 重置数据发送缓冲区 + memset(&total_data_buffer,0,FRAME_MAX_DATA_LENGTH); + // 组装目标地址 + total_data_buffer[0] = (addr >> 8) & 0XFF; + total_data_buffer[1] = addr & 0XFF; + total_data_buffer[2] = channel; + fram_length += 3; + + // 数据帧前半部分 + memcpy(&total_data_buffer[fram_length],send_buffer,FRAME_META_LENGTH - FRAME_FOOT_LENGTH); + fram_length += (FRAME_META_LENGTH - FRAME_FOOT_LENGTH); + // 用户额外数据 + if (E_G_DATA == send_buffer->frame_type) + { + memcpy(&total_data_buffer[fram_length],send_buffer->user_data,send_buffer->frame_attach); + fram_length += send_buffer->frame_attach; + } + // 剩下来的数据 + memcpy(&total_data_buffer[fram_length],&send_buffer->crc_hi,FRAME_FOOT_LENGTH); + fram_length += FRAME_FOOT_LENGTH; + // 发送所有数据 + if (0 != E220Send(adapter,&total_data_buffer,fram_length)) + { + printf("LoraSendFrame-Fail: send fail!\n"); + return -1; + } + ShowLoraFrame(send_buffer,"LoraSendFrame"); + return 0; +} + +uint16 GetSessionId(void) +{ + return rand() & 0XFFFF; +} + +void SignalDetector(struct Adapter *adapter,uint8 download_channel,uint8 upload_channel) +{ + // 切换至上行信道,客户端上行通道为广播信息(地址为OXFFFF),该信道所有终端都可以接收到 + E220Ioctl(adapter,CONFIG_CHANNEL,&upload_channel); + // 监听信道 + uint8 temp_dst; + while (1 == E220Recv(adapter, &temp_dst, 1)) + { + printf("LoraSignalDetector-Fail: Channel occupied!\n"); + } + // 切换至切换至下行通道,准备发送数据 + E220Ioctl(adapter,CONFIG_CHANNEL,&download_channel); + printf("LoraSignalDetector-Success: Channel idle!\n"); +} + +int UsrAdapterLoraInit(void) +{ + struct Adapter *adapter = PrivMalloc(sizeof(struct Adapter)); + if (!adapter) + { + PrivFree(adapter); + return -1; + } + memset(adapter, 0, sizeof(struct Adapter)); + // Adapter注册函数,终端和网关需要自己实现并开放接口 + if (0 != UsrAdapterLoraRegister(adapter)) + { + printf("UsrAdapterLoraInit-Fail: register lora adapter error\n"); + PrivFree(adapter); + return -1; + } + // 获取函数操作集合,终端和网关需要自己实现并开放接口 + AdapterProductInfoType product_info = LoraAttach(adapter); + if (NULL == product_info) + { + printf("UsrAdapterLoraInit-Fail: e220 attach error\n"); + PrivFree(adapter); + return -1; + } + // 配置Adapter属性 + adapter->product_info_flag = 1; + adapter->info = product_info; + adapter->done = product_info->model_done; + adapter->adapter_status = REGISTERED; + // 创建信号量和互斥量 + PrivSemaphoreCreate(&adapter->sem, 0, 0); + PrivMutexCreate(&adapter->lock, 0); + + printf("UsrAdapterLoraInit-Success!\n"); + return 0; +} + +#ifdef AS_LORA_GATEWAY + +struct GatewayParam gateway_param = // 网关配置参数 +{ + .dev_addr = LORA_GATEWAY_ADDRESS, + .channel = LORA_GATEWAY_CHANNEL, + .recv_time = LORA_RECV_TIME, + .air_rate = LORA_AIR_RATE, + .frame_retry = LORA_FRAME_RETRY, + .gateway_state = GATEWAY_CLOSED, + .node_infos = {NULL}, + .node_count = 0, +}; + +struct PrivProtocolDone gateway_done = // 网关操作函数集合 +{ + .open = GatewayOpen, + .close = GatewayClose, + .ioctl = GatewayIoctl, + .setup = GatewaySetup, + .setdown = GatewaySetDown, + .netstat = GatewayNetState +}; + +int (*gateway_handlers[])(struct Adapter*,struct LoraFrame*) = // 网关处理程序 +{ + [E_G_JOIN] = GatewayJoinHandler, + [E_G_QUIT] = GatewayQuitHandler, + [E_G_DATA] = GatewayDataSendHandler, +}; + +static UtaskType gateway_task; // 网关任务 +static int32 gateway_task_id = -1; // 任务ID + +/** + * @brief: 根据地址和信道定位到终端的信息 +*/ +static struct EndNodeInfo* GetEndNodeInfo(uint16 node_addr,uint8 channel) +{ + for (int i = 0; i < gateway_param.node_count; i++) + { + if (node_addr == gateway_param.node_infos[i]->node_addr && channel == gateway_param.node_infos[i]->node_down_channel) + { + return gateway_param.node_infos[i]; + } + } + return NULL; +} + +int GatewayOpen(struct Adapter *adapter) +{ + switch (gateway_param.gateway_state) + { + case GATEWAY_WORKING: case GATEWAY_OPENED: + printf("GatewayOpen-Success: lora already opened before!\n"); + return 0; + case GATEWAY_BROKEN: + printf("GatewayOpen-Fail: lora broken!\n"); + return -1; + case GATEWAY_CLOSED: + PrivMutexObtain(&adapter->lock); + // 打开底层硬件 + if (0 != E220Open(adapter)) + { + gateway_param.gateway_state = GATEWAY_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 按照网关的配置信息配置底层的硬件 + uint32 recv_time = gateway_param.recv_time * 1000; + if (0 != E220Ioctl(adapter,CONFIG_CHANNEL,&gateway_param.channel) + || 0 != E220Ioctl(adapter,CONFIG_ADDRESS,&gateway_param.dev_addr) + || 0 != E220Ioctl(adapter,CONFIG_AIR_RATE,&gateway_param.air_rate) + || 0 != E220Ioctl(adapter,CONFIG_SERIAL_TIME_OUT,&recv_time)) + { + printf("GatewayOpen-Fail: config gateway fail -> channel: %X,address: %X,air_rate: %X!\n",gateway_param.channel,gateway_param.dev_addr,gateway_param.air_rate); + gateway_param.gateway_state = GATEWAY_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 更新网关的状态信息 + gateway_param.gateway_state = GATEWAY_OPENED; + printf("GatewayOpen-Success: channel: %X,address: %X!\n",gateway_param.channel,gateway_param.dev_addr); + PrivMutexAbandon(&adapter->lock); + return 0; + default: + printf("GatewayOpen-Fail: unknown gateway state!\n"); + return -1; + } +} + +int GatewayClose(struct Adapter *adapter) +{ + switch (gateway_param.gateway_state) + { + case GATEWAY_CLOSED: case GATEWAY_BROKEN: + printf("GatewayClose-Success: lora close or broken before!\n"); + return 0; + case GATEWAY_WORKING: + printf("GatewayClose-Fail: quit net before close!\n"); + return -1; + case GATEWAY_OPENED: + PrivMutexObtain(&adapter->lock); + if (0 != E220Close(adapter)) + { + gateway_param.gateway_state = GATEWAY_BROKEN; + printf("GatewayClose-Fail: close lora fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + gateway_param.gateway_state = GATEWAY_CLOSED; + printf("GatewayClose-Success\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + default: + printf("GatewayClose-Fail: unknown err!\n"); + return -1; + } +} + +int GatewayIoctl(struct Adapter *adapter, int cmd, void *args) +{ + switch (gateway_param.gateway_state) + { + case GATEWAY_WORKING: case GATEWAY_BROKEN: case GATEWAY_CLOSED: + printf("GatewayIoctl-Fail: cant be config when lora broken、working、clesed!\n"); + return -1; + case GATEWAY_OPENED: + PrivMutexObtain(&adapter->lock); + if (0 != E220Ioctl(adapter, cmd, args)) + { + gateway_param.gateway_state = GATEWAY_BROKEN; + printf("GatewayIoctl-Fail: ioctl fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + printf("GatewayIoctl-Success\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + default: + printf("GatewayIoctl-Fail: unknown err!\n"); + return -1; + } +} + +int GatewaySetup(struct Adapter *adapter) +{ + if (GATEWAY_BROKEN == gateway_param.gateway_state) + { + printf("GatewaySetup-Fail: lora broken!\n"); + return -1; + } + if (GATEWAY_CLOSED == gateway_param.gateway_state) + { + printf("GatewaySetup-Fail: please open lora before!\n"); + return -1; + } + if (GATEWAY_WORKING == gateway_param.gateway_state) + { + printf("GatewaySetup-Success: lora setup before!\n"); + return 0; + } + PrivMutexObtain(&adapter->lock); + // 配置硬件传输 + if (0 != E220Ioctl(adapter,CONFIG_LORA_MODE,MODE_TRANSFER_MODE)) + { + printf("GatewaySetup-Fail: config lora transfer fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + + gateway_task.prio = 24; + gateway_task.stack_size = 2048; + gateway_task.func_param = adapter; + gateway_task.func_entry = GatewayTask; + strncpy(gateway_task.name, "lora_gateway_task", strlen("lora_gateway_task")); + + gateway_task_id = UserTaskCreate(gateway_task); + + if (gateway_task_id < 0) + { + printf("GatewaySetup-Fail: create task fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + + if (0 == UserTaskStartup(gateway_task_id)) + { + gateway_param.gateway_state = GATEWAY_WORKING; + printf("GatewaySetup-Success: start task success!\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + } + else + { + UserTaskDelete(gateway_task_id); + gateway_task_id = -1; + printf("GatewaySetup-Fail: start task fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } +} + +int GatewaySetDown(struct Adapter *adapter) +{ + + if (GATEWAY_WORKING != gateway_param.gateway_state) + { + printf("GatewaySetDown-Success: not in working state!\n"); + return 0; + } + // 通知后台程序 + PrivSemaphoreAbandon(&adapter->sem); + PrivMutexObtain(&adapter->lock); + if (0 == UserTaskDelete(gateway_task_id)) + { + gateway_task_id = -1; + gateway_param.gateway_state = GATEWAY_OPENED; + printf("GatewaySetDown-Success!\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + } + printf("GatewaySetDown-Fail: delete task fail!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; +} + +int GatewayNetState(struct Adapter *adapter) +{ + printf("GatewayInfos: \n"); + printf("addr:%d,channel:%d,recv_time:%d,",gateway_param.dev_addr,gateway_param.channel,gateway_param.recv_time); + printf("air_rate:%d,state:%x,node_count:%d\n",gateway_param.air_rate,gateway_param.gateway_state,gateway_param.node_count); + for (int i = 0; i < gateway_param.node_count; i++) + { + struct EndNodeInfo *node = gateway_param.node_infos[i]; + printf("node_addr:%d,node_down_channel:%d!n",node->node_addr,node->node_down_channel); + } + return 0; +} + +void* GatewayTask(void* param) +{ + struct Adapter *gateway_adapter = (struct Adapter *)param; + if (gateway_adapter == NULL) + { + printf("GatewayTask-Fail: adapter is null!\n"); + return NULL; + } + + while (gateway_param.gateway_state != GATEWAY_WORKING) // 等待上一个函数修改网关状态 + { + PrivTaskDelay(100); + } + + while (1) // 业务大循环 + { + if (0 == PrivSemaphoreObtainNoWait(&gateway_adapter->sem)) + { + PrivTaskDelay(LORA_RECV_TIME); + continue; + } + PrivMutexObtain(&gateway_adapter->lock); + if (0 == LoraRecvFrame(gateway_adapter,&recv_frame)) + { + // 进行相应的处理 + if (0 == gateway_handlers[recv_frame.frame_type](gateway_adapter,&recv_frame)) + { + printf("GatewayTask: handle success!\n"); + } + else + { + printf("GatewayTask: handle fail!\n"); + } + } + PrivMutexAbandon(&gateway_adapter->lock); + } + + printf("GatewayTask: task exit!\n"); + return NULL; +} + +int GatewayJoinHandler(struct Adapter* adapter,struct LoraFrame* frame) +{ + // 获取终端的地址和信道,防止多个信道中同一个地址加入失败 + uint16 node_addr = frame->dev_addr; + uint8 node_channel = frame->down_channel; + struct EndNodeInfo* node = NULL; + // 从现有的数据中查找 + for (int i = 0; i < gateway_param.node_count; i++) + { + if (node_addr == gateway_param.node_infos[i]->node_addr && node_channel == gateway_param.node_infos[i]->node_down_channel) // 找到了这个终端,实现已经加入了 + { + node = gateway_param.node_infos[i]; + break; + } + } + // 如果事先没有加入就创建 + if (node == NULL) + { + if(gateway_param.node_count == LORA_GATEWAY_MAX_NODE) + { + printf("NodeJoinHandler: too much node!\n"); + return -1; + } + node = (struct EndNodeInfo *)PrivMalloc(sizeof(struct EndNodeInfo)); + memset(node, 0, sizeof(struct EndNodeInfo)); + node->node_addr = node_addr; + node->node_down_channel = node_channel; + gateway_param.node_infos[gateway_param.node_count] = node; + gateway_param.node_count++; + // 如果已经事先加入了,直接返回 + } + // 响应终端 + frame->frame_type = G_E_ANS; + frame->frame_confirm = FRAME_BYTE_NO; + frame->frame_attach = FRAME_BYTE_OK; + + if (0 != LoraSendFrame(adapter, frame,node->node_addr,node->node_down_channel)) + { + printf("NodeJoinHandler: response node fail!\n"); + return -1; + } + printf("NodeJoinHandler: join net success addr:%d,channel:%d!\n",node->node_addr,node->node_down_channel); + return 0; +} + +int GatewayQuitHandler(struct Adapter* adapter,struct LoraFrame* frame) +{ + uint16 node_addr = frame->dev_addr; + uint8 node_channel = frame->down_channel; + for (int i = 0; i < gateway_param.node_count; i++) // 查找是否存在这个终端 + { + if (node_addr == gateway_param.node_infos[i]->node_addr && node_channel == gateway_param.node_infos[i]->node_down_channel) + { + frame->frame_type = G_E_ANS; + frame->frame_confirm = FRAME_BYTE_NO; + frame->frame_attach = FRAME_BYTE_OK; + if (0 != LoraSendFrame(adapter, frame,gateway_param.node_infos[i]->node_addr,gateway_param.node_infos[i]->node_down_channel)) + { + printf("NodeQuitHandler: response node fail!\n"); + return -1; + } + // 释放相关的空间 + PrivFree(gateway_param.node_infos[i]); + for (int j = i; j < gateway_param.node_count - 1; j++) + { + gateway_param.node_infos[j] = gateway_param.node_infos[j+1]; + } + gateway_param.node_count--; + gateway_param.node_infos[gateway_param.node_count] = NULL; + break; + } + } + printf("NodeQuitHandler: quit net success or quit before!\n"); + return 0; +} + +int GatewayDataSendHandler(struct Adapter* adapter,struct LoraFrame* frame) +{ + // 查找该终端 + struct EndNodeInfo* node_info = GetEndNodeInfo(frame->dev_addr,frame->down_channel); + if (node_info == NULL) + { + printf("NodeDataSendHandler: no such node!\n"); + return 0; + } + // 响应数据上传 + frame->frame_type = G_E_ANS; + frame->frame_confirm = FRAME_BYTE_NO; + frame->frame_attach = LORA_OK; + if (0 != LoraSendFrame(adapter, frame,node_info->node_addr,node_info->node_down_channel)) + { + printf("NodeDataSendHandler: response fail!\n"); + return -1; + } + // TODO 数据上传在此处理 + printf("NodeDataSendHandler-Success: recv data: %s\n!",frame->user_data); + return 0; +} + +AdapterProductInfoType LoraAttach(struct Adapter *adapter) +{ + struct AdapterProductInfo *product_info = malloc(sizeof(struct AdapterProductInfo)); + if (!product_info) { + printf("LoraAttach-Failed-Node: malloc fail!\n"); + return NULL; + } + + strncpy(product_info->model_name, LORA_ADAPTER_NAME,sizeof(product_info->model_name)); + product_info->model_done = (void *)&gateway_done; + + return product_info; +} + +uint8 UsrAdapterLoraRegister(struct Adapter *adapter) +{ + strncpy(adapter->name, ADAPTER_LORA_NAME, NAME_NUM_MAX); + adapter->net_protocol = PRIVATE_PROTOCOL; + + adapter->net_role = GATEWAY; + adapter->net_role_id = gateway_param.channel; + adapter->adapter_param = (void *)(&gateway_param); + + adapter->adapter_status = UNREGISTERED; + + if (AdapterDeviceRegister(adapter) < 0) + { + printf("UsrAdapterLoraRegister-Fail: lora register error!\n"); + return -1; + } + + printf("UsrAdapterLoraRegister-Success!\n"); + return 0; +} +#else + +// 终端参数结构 +struct EndNodeParam node_param = +{ + .dev_addr = LORA_ADDRESS, + .down_channel = LORA_DOWN_CHANNEL, + .upload_channel = LORA_UP_CHANNEL, + .session_id = 0X0, + .node_state = NODE_STATE_CLOSED, + .air_rate = LORA_AIR_RATE, + .adr_enable = LORA_ADR_ENABLE, + .reconnect_enable = LORA_RECONNECT_ENABLE, + .recv_time = LORA_RECV_TIME, +}; +// 终端操作集合 +static struct PrivProtocolDone node_done = +{ + .open = NodeOpen, + .close = NodeClose, + .join = NodeJoin, + .send = NodeSend, + .quit = NodeQuit, + .netstat = NodeNetState +}; +// 空中速率枚举 +enum LoraAirRate air_rates[6] = + { + AIR_RATE_2D4K, + AIR_RATE_4D8K, + AIR_RATE_9D6K, + AIR_RATE_19D2K, + AIR_RATE_38D4K, + AIR_RATE_62D5K}; + +int air_rate_length = 6; + +/** + * @brief: 向特定信道以特定空中速率发送入网请求 + * @param adapter: 已打开的适配器 + * @param join_buffer: 已组装数据帧的缓冲区 + * @param upload_channel: 上行信道 + * @param air_rate: 空中速率 + * @return: 0 -> Success : -1 -> Fail(根据状态判定是否存在硬件损坏) +*/ +static uint8 TargetConnectGateway(struct Adapter *adapter,struct LoraFrame *join_buffer,uint8 upload_channel,enum LoraAirRate air_rate) +{ + if (0 != E220Ioctl(adapter, CONFIG_AIR_RATE, &air_rate)) // 配置硬件空中速率 + { + printf("TargetConnectGateway-Fail: config air rate fail %d!\n",air_rate); + node_param.node_state = NODE_STATE_BROKEN; + return -1; + } + node_param.air_rate = air_rate; + for (int i = 0; i < LORA_FRAME_RETRY; i++) + { + node_param.session_id = GetSessionId(); + send_frame.session_id = node_param.session_id; + + SignalDetector(adapter,node_param.down_channel,node_param.upload_channel); // 信道监听 + if (0 != LoraSendFrame(adapter, &send_frame, LORA_GATEWAY_ADDRESS,upload_channel)) // 发送入网请求 + { + node_param.node_state = NODE_STATE_BROKEN; + return -1; + } + PrivTaskDelay(LORA_TIME_ON_AIR * 1000); + while (0 == LoraRecvFrame(adapter, &recv_frame)) // 接收响应数据帧 + { + if (recv_frame.session_id == node_param.session_id) // 判断数据帧是否丢失 + { + if (G_E_ANS == recv_frame.frame_type && FRAME_BYTE_OK == recv_frame.frame_attach) // 成功加入网关 + { + node_param.node_state = NODE_STATE_CONNECT; // 更改终端状态 + node_param.upload_channel = upload_channel; + node_param.air_rate = air_rate; + printf("ConnectGateway-Success: upload_channel: %d, air_rate: %d !\n",upload_channel,air_rate); + return 0; + } + } + } + } + printf("ConnectGateway-Fail: upload_channel: %d, air_rate: %d !\n",upload_channel,air_rate); + return -1; +} + +int NodeOpen(struct Adapter *adapter) +{ + // 获取硬件控制权 + PrivMutexObtain(&adapter->lock); + // 硬件损坏 + if (NODE_STATE_BROKEN == node_param.node_state) + { + printf("NodeOpen-Fail: lora broken!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 硬件已打开 + if (NODE_STATE_CONNECT == node_param.node_state || NODE_STATE_DISCONNECT == node_param.node_state) + { + printf("NodeOpen-Success: opened before!\n"); + PrivMutexAbandon(&adapter->lock); + return 0; + } + // 打开硬件设施 + if (0 != E220Open(adapter)) + { + printf("NodeOpen-Fail: lora open fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 配置硬件地址、信道、超时时间,空中速率 + int time_out = node_param.recv_time * 1000; + if (0 != E220Ioctl(adapter,CONFIG_ADDRESS,&node_param.dev_addr) + || 0 != E220Ioctl(adapter,CONFIG_CHANNEL,&node_param.down_channel) + || 0 != E220Ioctl(adapter,CONFIG_SERIAL_TIME_OUT,&time_out) + || 0 != E220Ioctl(adapter,CONFIG_AIR_RATE,&node_param.air_rate)) + { + printf("NodeOpen-Fail: condfi client fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 配置硬件休眠 + enum LoraMode aim_mode = MODE_CONFIG_SLEEP; + if (0 != E220Ioctl(adapter, CONFIG_LORA_MODE, &aim_mode)) + { + printf("NodeOpen-Fail: config lora sleep fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + printf("NodeOpen-Success: node open success address: %d, dwon_channel: %d, upload_channel: %d!\n",node_param.dev_addr,node_param.down_channel,node_param.upload_channel); + node_param.node_state = NODE_STATE_DISCONNECT; + PrivMutexAbandon(&adapter->lock); + return 0; +} + +int NodeClose(struct Adapter *adapter) +{ + if (NODE_STATE_BROKEN == node_param.node_state || NODE_STATE_CLOSED == node_param.node_state) + { + printf("NodeClose-Success: lora broken or cloded before!\n"); + return 0; + } + if (NODE_STATE_CONNECT == node_param.node_state) + { + printf("NodeClose-Fail: quit net before close node!\n"); + return -1; + } + PrivMutexObtain(&adapter->lock); + // 关闭硬件设施 + if (0 != E220Close(adapter)) + { + printf("NodeClose-Fail: lora close fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + printf("NodeClose-Success: node close success!\n"); + node_param.node_state = NODE_STATE_CLOSED; + PrivMutexAbandon(&adapter->lock); + return 0; +} + +int NodeJoin(struct Adapter *adapter, unsigned char *net_group) +{ + switch (node_param.node_state) + { + case NODE_STATE_BROKEN: + printf("NodeJoin-Fail: lora broken!\n"); + return -1; + case NODE_STATE_CLOSED: + printf("NodeJoin-Fail: open lora before join net!\n"); + return -1; + case NODE_STATE_CONNECT: + if (node_param.upload_channel == *net_group && NODE_STATE_CONNECT == node_param.node_state) + { + printf("NodeJoin-Success: joined before!\n"); + return 0; + } + // 退出当前网络 + if (NODE_STATE_CONNECT == node_param.node_state) + { + // 退出当前的网络,断开上行通道 + if (0 != NodeQuit(adapter, &node_param.upload_channel)) + { + printf("NodeJoin-Fail: quit net before fail!\n"); + return -1; + } + } + default: + // 构建入网数据帧 + InitLoraFrame(&send_frame); + send_frame.dev_addr = node_param.dev_addr; // 当前设备地址 + send_frame.down_channel = node_param.down_channel; // 下行通道信息,当前设备的信道 + send_frame.frame_type = E_G_JOIN; + send_frame.frame_confirm = LORA_OK; + // 获取终端控制权 + PrivMutexObtain(&adapter->lock); + // 以默认的空中速率尝试连接传入的网络 + if (0 == TargetConnectGateway(adapter, &send_frame, *net_group,node_param.air_rate)) + { + PrivMutexAbandon(&adapter->lock); + return 0; + } + else + { + // ADR自动寻找网关 + if (LORA_OK == node_param.adr_enable) + { + // 默认连接出错 + if (NODE_STATE_BROKEN == node_param.node_state) + { + printf("NodeJoin-Fail: send join frame fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 尝试连接其他的网关 + for (int i = 0; i < E220_MAX_CHANNEL_NUMBER; i++) + { + for (int j = 0; j < air_rate_length; j++) + { + printf("NodeJoin-Try: channel: %d air rate: %x!\n",i,air_rates[j]); + if (0 == TargetConnectGateway(adapter, &send_frame, i, air_rates[j])) + { + PrivMutexAbandon(&adapter->lock); + return 0; + } + if (NODE_STATE_BROKEN == node_param.node_state) + { + printf("NodeJoin-Fail: send join frame fail!\n"); + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + } + } + // 不存在网关 + printf("NodeJoin-Fail: no gateway exits!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + else + { + printf("NodeJoin-Fail: the gateway config not exits!\n"); + PrivMutexAbandon(&adapter->lock); + return -1; + } + } + } +} + +int NodeSend(struct Adapter *adapter, const void *buf, size_t len) +{ + // 只有处于联网状态的终端才可发送数据 + if (NODE_STATE_CONNECT != node_param.node_state) + { + printf("NodeSend-Fail: open lora and join net before send data!\n"); + return -1; + } + if (len > FRAME_MAX_USER_DATA_LENGTH) // 数据量太大 + { + printf("NodeSend-Fail: to much data for send!\n"); + return -1; + } + // 构造数据帧并发送数据 + InitLoraFrame(&send_frame); // 初始化 + send_frame.dev_addr = node_param.dev_addr; + send_frame.down_channel = node_param.down_channel; + send_frame.frame_type = E_G_DATA; + send_frame.frame_confirm = LORA_OK; + // 复制待发送的数据 + memcpy(send_data_buffer,buf,len); + send_frame.user_data = send_data_buffer; + send_frame.frame_attach = len; + + PrivMutexObtain(&adapter->lock); + // 发送用户数据帧 + for (int i = 0; i < LORA_FRAME_RETRY; i++) + { + node_param.session_id = GetSessionId(); + send_frame.session_id = node_param.session_id; + + // 信道监听 + SignalDetector(adapter,node_param.down_channel,node_param.upload_channel); + // 发送数据帧 + if (0 != LoraSendFrame(adapter, &send_frame,LORA_GATEWAY_ADDRESS,node_param.upload_channel)) + { + PrivMutexAbandon(&adapter->lock); + node_param.node_state = NODE_STATE_BROKEN; + return -1; + } + // 等待信号到达 + PrivTaskDelay(LORA_TIME_ON_AIR * 1000); + // 准备数据缓冲区并等待网关响应 + while (0 == LoraRecvFrame(adapter, &recv_frame)) + { + // 判断数据是否丢失 + if (recv_frame.session_id == node_param.session_id) + { + // 判断是否发送成功 + if (G_E_ANS == recv_frame.frame_type && LORA_OK == recv_frame.frame_attach) + { + // 配置硬件休眠 + enum LoraMode aim_mode = MODE_CONFIG_SLEEP; + E220Ioctl(adapter,CONFIG_LORA_MODE,&aim_mode); + PrivMutexAbandon(&adapter->lock); + return 0; + } + + } + } + } + PrivMutexAbandon(&adapter->lock); + // 判定网关掉线 + node_param.node_state = NODE_STATE_DISCONNECT; + // 数据发送失败,需要重新联网 + if (LORA_OK == node_param.reconnect_enable) + { + printf("NodeSend-Fail: try to reconnect gateway!\n"); + if (0 == NodeJoin(adapter,&node_param.upload_channel)) + { + // 重新发送数据 + return NodeSend(adapter,buf,len); + } + printf("NodeSend-Fail: try to reconnect gateway fail!\n"); + } + return -1; +} + +int NodeQuit(struct Adapter *adapter, unsigned char *net_group) +{ + if (NODE_STATE_CONNECT != node_param.node_state) + { + printf("NodeQuit-Success: disconnected before!\n"); + return 0; + } + + if (node_param.upload_channel != *net_group) + { + printf("NodeQuit-Success: not such net!\n"); + return 0; + } + + // 构建退网数据帧 + InitLoraFrame(&send_frame); + send_frame.dev_addr = node_param.dev_addr; + send_frame.down_channel = node_param.down_channel; + send_frame.frame_type = E_G_QUIT; + send_frame.frame_confirm = FRAME_BYTE_OK; + + PrivMutexObtain(&adapter->lock); + // 发送用户数据帧 + for (int i = 0; i < LORA_FRAME_RETRY; i++) + { + node_param.session_id = GetSessionId(); + send_frame.session_id = node_param.session_id; + + // 信道监听 + SignalDetector(adapter,node_param.down_channel,node_param.upload_channel); + // 发送数据帧 + if (0 != LoraSendFrame(adapter, &send_frame,LORA_GATEWAY_ADDRESS,node_param.upload_channel)) + { + node_param.node_state = NODE_STATE_BROKEN; + PrivMutexAbandon(&adapter->lock); + return -1; + } + // 等待信号到达 + PrivTaskDelay(LORA_TIME_ON_AIR * 1000); + // 准备数据缓冲区并等待网关响应 + while (0 == LoraRecvFrame(adapter, &recv_frame)) + { + // 判断数据是否丢失 + if (recv_frame.session_id == node_param.session_id) + { + // 判断是否退出成功 + if (G_E_ANS == recv_frame.frame_type && FRAME_BYTE_OK == recv_frame.frame_attach) + { + // 配置硬件休眠 + enum LoraMode aim_mode = MODE_CONFIG_SLEEP; + E220Ioctl(adapter,CONFIG_LORA_MODE,&aim_mode); + PrivMutexAbandon(&adapter->lock); + return 0; + } + } + } + } + printf("NodeQuit-Fail: quit net fail %d!\n",*net_group); + // 配置硬件休眠 + enum LoraMode aim_mode = MODE_CONFIG_SLEEP; + E220Ioctl(adapter,CONFIG_LORA_MODE,&aim_mode); + PrivMutexAbandon(&adapter->lock); + return -1; +} + +int NodeNetState(struct Adapter *adapter) +{ + printf("EndNodeInfos: \n"); + printf("addr:%d,down_channel:%d,upload_channel:%d,",node_param.dev_addr,node_param.down_channel,node_param.upload_channel); + printf("state:%x,air_rate:%x,adr:%x,",node_param.node_state,node_param.air_rate,node_param.adr_enable); + printf("reconnect:%x,recv_time:%d \n",node_param.recv_time,node_param.reconnect_enable); + return 0; +} + +AdapterProductInfoType LoraAttach(struct Adapter *adapter) +{ + struct AdapterProductInfo *product_info = malloc(sizeof(struct AdapterProductInfo)); + if (!product_info) { + printf("LoraAttach-Failed-Node: malloc fail!\n"); + return NULL; + } + + strncpy(product_info->model_name, LORA_ADAPTER_NAME,sizeof(product_info->model_name)); + product_info->model_done = (void *)&node_done; + + return product_info; +} + +uint8 UsrAdapterLoraRegister(struct Adapter *adapter) +{ + strncpy(adapter->name, ADAPTER_LORA_NAME, NAME_NUM_MAX); + adapter->net_protocol = PRIVATE_PROTOCOL; + + adapter->net_role = CLIENT; + adapter->net_role_id = node_param.dev_addr; + adapter->adapter_param = (void *)(&node_param); + + adapter->adapter_status = UNREGISTERED; + + if (AdapterDeviceRegister(adapter) < 0) + { + printf("UsrAdapterLoraRegister-Fail: lora register error!\n"); + return -1; + } + + printf("UsrAdapterLoraRegister-Success!\n"); + return 0; +} +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.h b/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.h new file mode 100755 index 000000000..38a9b5e15 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/lora_mac/lora_mac.h @@ -0,0 +1,303 @@ +#include +#include +#include "../lora_driver/e220.h" + +// #define AS_LORA_GATEWAY + +/*数据帧**************************************************************************/ +#define FRAME_BEGIN_MARK 0x3C3C // 帧开始标志,高低字节相同 +#define FRAME_END_MARK 0X5A5A // 帧结束标志,高低字节相同 +#define FRAME_MAX_DATA_LENGTH 200 // 默认支持的最大数据包为200字节 +#define FRAME_ADDR_INFO_LENGTH 3 // 定点传输时前三字节为地址和信道共三个字节 +#define FRAME_META_LENGTH 14 // 数据帧相关信息长度 +#define FRAME_MAX_USER_DATA_LENGTH FRAME_MAX_DATA_LENGTH - FRAME_META_LENGTH - FRAME_ADDR_INFO_LENGTH +#define FRAME_CRC_INDEX_BEGIN 2 // CRC校验开始地址 +#define FRAME_CRC_LENGTH 8 // CRC校验元数据量长度 +#define FRAME_BYTE_OK 0XFF // 是/成功 +#define FRAME_BYTE_NO 0X00 // 否/失败 +#define FRAME_NECK_LENGTH 8 // 帧头以下,额外数据以上的长度 +#define FRAME_FOOT_LENGTH 4 // 额外数据以下至帧尾的数据长度 +#define FRAME_MARK_LENGTH 2 // 帧头帧尾的数据长度 + +enum FrameType // 数据帧类型枚举 +{ + E_G_JOIN = 0, // 客户端入网请求,需回复 + E_G_QUIT, // 客户端退网请求,不需回复 + E_G_DATA, // 客户端上传数据,需回复 + G_E_ANS, // 网关响应客户端请求 +}; + +struct LoraFrame // 数据帧消息类型 +{ + uint16 begin_mark; // 开始标志 0x3C3C + uint16 session_id; // 会话ID,客户端每次数据上传会随机设置,若是回复ID不一致则表示数据丢失 + uint16 dev_addr; // 设备地址,特指终端地址,网关地址固定为 0XFFFF + uint8 down_channel; // 下行通道,即终端所在的信道 + uint8 frame_type; // 数据帧的类型 + uint8 frame_confirm; // 该数据帧是否需要确认回复 + uint8 frame_attach; // 用于携带简单的数据,例如用户数据长度 + uint8 *user_data; // 需要额外携带的大量数据 + uint8 crc_hi; // 校验位高字节 + uint8 crc_lo; // 校验位低字节 + uint16 end_mark; // 结束标志 0X5A5A +}; + +extern struct LoraFrame send_frame; // 数据帧发送缓冲区 +extern struct LoraFrame recv_frame; // 数据帧接受缓存区 +extern uint8 send_data_buffer[FRAME_MAX_USER_DATA_LENGTH]; // 数据发送缓冲区 +extern uint8 recv_data_buffer[FRAME_MAX_USER_DATA_LENGTH]; // 数据接受缓存区 +extern uint8 total_data_buffer[FRAME_MAX_DATA_LENGTH]; // 数据帧加数据的最大缓冲区 + +/** + * @brief: 将数据帧缓冲区置为 0 ,并添加帧头,帧尾数据 + * @param frame: 数据帧缓冲区 +*/ +void InitLoraFrame(struct LoraFrame* frame); + +/** + * @brief: 打印数据帧结构信息,便于运行、开发调试 + * @param fram: 需要打印的数据帧 + * @param prefix: 那里打印的数据帧信息 +*/ +void ShowLoraFrame(struct LoraFrame* frame,char* prefix); + +/** + * @brief: 计算数据帧的校验值,除去帧头、帧尾、校验值本身,若是应该携带数据的类型未携带数据时报错 + * @return: 消息类型和数据不匹配时为 -1 +*/ +uint8 CalCrlLoraFrame(struct LoraFrame* frame); + +/** + * @brief: 校验数据帧的完整性 + * @return: 校验失败为 -1 +*/ +uint8 CheckCrcLoraFrame(struct LoraFrame* frame); +/*数据帧**************************************************************************/ + +/*基础操作************************************************************************/ +#define LORA_ADAPTER_NAME "e220" +#define LORA_OK 0XFF +#define LORA_NO 0X00 +#define LORA_TIME_ON_AIR 2 +#define LORA_AIR_RATE 0 +#define LORA_FRAME_RETRY 5 +#define LORA_GATEWAY_MAX_CMD 20 +#define LORA_GATEWAY_MAX_NODE 20 +#define LORA_GATEWAY_ADDRESS 0XFFFF + +#ifdef AS_LORA_GATEWAY + +#define LORA_RECV_TIME 10 +#define LORA_GATEWAY_CHANNEL 0XA + +#else + +#define LORA_ADDRESS 0X9 +#define LORA_RECV_TIME 5 +#define LORA_DOWN_CHANNEL 0X9 +#define LORA_UP_CHANNEL 0XA +#define LORA_ADR_ENABLE LORA_NO +#define LORA_RECONNECT_ENABLE LORA_OK +#endif + +/** + * @brief: 从硬件获取一个完整的数据帧 + * @param recv_buffer: 数据帧缓冲区,在检测到带数据的数据帧时会自动关联数据缓冲区 + * @param prefix: 调用位置,便于调试 + * @return 0: 成功接受到一个数据帧(从侦测到数据帧头部开始、成功接受到帧尾部且数据校验成功) + * -1:串口超时;数据帧接受不完整;数据帧完整性校验失败; +*/ +uint8 LoraRecvFrame(struct Adapter* adapter, struct LoraFrame* recv_buffer); + +/** + * @brief: 通过硬件向指定的位置的信道发送数据 + * @param send_buffer: 数据帧缓冲区,在检测到带数据的数据帧时会自动发送关联数据缓冲区 + * @param addr: 目标的地址 + * @param channel: 目标的信道 + * @return 0: 成功发送指定长度的数据 + * -1:未能成功发送制定数据 +*/ +uint8 LoraSendFrame(struct Adapter* adapter, struct LoraFrame* send_buffer,uint16 addr,uint8 channel); + +/** + * @brief: 在一轮超时后未能接收到信号时判定信道空闲 + * @param download_channel: 下行信道 + * @param upload_channel: 上行信道 +*/ +void SignalDetector(struct Adapter *adapter,uint8 download_channel,uint8 upload_channel); + +/** + * @brief: 获取随机数,用于客户端和网关之间的会话 +*/ +uint16 GetSessionId(void); + +/** + * @brief: 获取操作函数集合,客户端和网关各自实现 +*/ +AdapterProductInfoType LoraAttach(struct Adapter *adapter); + +/** + * @brief: 注册函数,需要各自实现 +*/ +uint8 UsrAdapterLoraRegister(struct Adapter *adapter); + +/** + * @brief: 框架初始化函数,需要替换掉原有的初始化函数 +*/ +int UsrAdapterLoraInit(void); + +/*基础操作************************************************************************/ + +/*客户端操作************************************************************************/ +enum EndNodeState //节点状态 +{ + NODE_STATE_DISCONNECT = 0, // 硬件开启,但是没有联网,后台程序可能开启 + NODE_STATE_CONNECT, // 硬件开启,已经联网,后台程序可能开启 + NODE_STATE_BROKEN, // 硬件损坏,无后台程序 + NODE_STATE_CLOSED // 硬件关闭,无后台程序 +}; + +struct EndNodeParam // 节点参数结构 +{ + uint16 dev_addr; // 设备地址信息 + uint8 down_channel; // 下行通道,终端可以接收信息的信道 + uint8 upload_channel; // 上行通道,终端可以给网关发送信息的信道 + uint16 session_id; // 会话ID,用于判定数据帧是否存在丢失 + + enum EndNodeState node_state; // 客户端状态 + enum LoraAirRate air_rate; // 终端的空中速率 + uint8 adr_enable; // 终端是否开启自动寻找上行网关 + uint8 reconnect_enable; // 终端断网自动重联 + uint8 recv_time; // 接受下行消息的时间窗口长度,单位为秒 +}; + +extern struct EndNodeParam node_param; // 客户端参数 +extern int (*node_handlers[])(struct Adapter*,struct LoraFrame*); // 客户端处理流程 + +/** + * @brief: 打开终端硬件,按照配置的CLASS决定是否开启硬件接收和后台处理程序 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeOpen(struct Adapter *adapter); + +/** + * @brief: 关闭终端硬件设施和后台处理程序 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeClose(struct Adapter *adapter); + +/** + * @brief: 终端接入网关,向该上行通道发送入网请求 + * @param adapter: 已经打开的适配器 + * @param net_group: 需要加入的网络编号 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeJoin(struct Adapter *adapter, unsigned char *net_group); + +/** + * @brief: 发送数据到网关 + * @param adapter: 已经联网的适配器 + * @param buf: 待发送数据 + * @param len: 待发送数据长度 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeSend(struct Adapter *adapter, const void *buf, size_t len); + +/** + * @brief: 终端退出某个网络 + * @param adapter: 已经联网的适配器 + * @param net_group: 需要退出的网络编号 + * @return: 0 -> Success : -1 -> Fail +*/ +int NodeQuit(struct Adapter *adapter, unsigned char *net_group); + +/** + * @brief: 打印终端的信息 +*/ +int NodeNetState(struct Adapter *adapter); + +/*客户端操作************************************************************************/ + +/*网关操作*************************************************************************/ +struct EndNodeInfo // 网关存储的客户端信息 +{ + uint16 node_addr; // 客户端地址 + uint8 node_down_channel; // 客户端下行通道 + uint16 session_id; // 上一个命令的会话ID +}; + +enum GatewayState // 网关的状态枚举 +{ + GATEWAY_CLOSED, // Lora还没有打开 + GATEWAY_OPENED, // 已打开,但是未开始工作 + GATEWAY_WORKING, // 正常打开且已经开始正常工作 + GATEWAY_BROKEN // 模块损坏 +}; + +struct GatewayParam // 网关参数 +{ + uint16 dev_addr; // 设备ID + uint8 channel; // 网络编号 + + int recv_time; // 串口超时时间 + enum LoraAirRate air_rate; // 网关空中速率 + uint8 frame_retry; // 重传次数 + enum GatewayState gateway_state; // 网关状态 + struct EndNodeInfo *node_infos[LORA_GATEWAY_MAX_NODE]; // 客户端信息 + uint16 node_count; // 已链接客户端数量 +}; + +extern struct GatewayParam gateway_param; // 网关参数 +extern int (*gateway_handlers[])(struct Adapter*,struct LoraFrame*); // 处理程序 + +/** + * @brief: 打开Lora硬件设施,但是不会将其接入网络中 +*/ +int GatewayOpen(struct Adapter *adapter); + +/** + * @brief: 关闭硬件设施,需要先断网 +*/ +int GatewayClose(struct Adapter *adapter); + +/** + * @brief: 配置网关参数的配置接口,这个函数配置的是网关的参数 +*/ +int GatewayIoctl(struct Adapter *adapter, int cmd, void *args); + +/** + * @brief: 启用网关,使其可以为其他终端服务 +*/ +int GatewaySetup(struct Adapter *adapter); + +/** + * @brief: 停用网关,但是不会给网络中的终端下线通知 +*/ +int GatewaySetDown(struct Adapter *adapter); + +/** + * @brief: 打印网关相关信息 +*/ +int GatewayNetState(struct Adapter *adapter); + +/** + * @brief: 网关主任务,负责终端各项事务的自动化处理 +*/ +void* GatewayTask(void* adapter); + +/** + * @brief: 处理终端入网请求 +*/ +int GatewayJoinHandler(struct Adapter* adapter,struct LoraFrame* frame); + +/** + * @brief: 处理终端退网 +*/ +int GatewayQuitHandler(struct Adapter* adapter,struct LoraFrame* frame); + +/** + * @brief: 处理终端数据传输 +*/ +int GatewayDataSendHandler(struct Adapter* adapter,struct LoraFrame* frame); + +/*网关操作*************************************************************************/ \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.c b/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.c new file mode 100755 index 000000000..e7bc5325f --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.c @@ -0,0 +1,96 @@ +#include "test_lora_net_final.h" + +#ifdef AS_LORA_GATEWAY + +/** + * @brief: 网关测试函数 + * 1. 打开Lora硬件设施. + * 2. 接入网络. +*/ +void TestGateway(void) +{ + struct Adapter* gateway_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + // 打开设备 + if (0 != AdapterDeviceOpen(gateway_adapter)) + { + printf("TestGateway-Fail: open lora fail!\n"); + return; + } + printf("TestGateway-Info: open lora success!\n"); + // 接入网络 + if (0 != AdapterDeviceSetUp(gateway_adapter)) + { + printf("TestGateway-Fail: setup net fail!\n"); + return; + } + printf("TestGateway-Success: gateway set up success!\n"); + AdapterDeviceNetstat(gateway_adapter); +} +PRIV_SHELL_CMD_FUNCTION(TestGateway,TestGateway, PRIV_SHELL_CMD_MAIN_ATTR); + +/** + * 打印网关信息 +*/ +void TestGatewayInfo(void) +{ + struct Adapter* gateway_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + AdapterDeviceNetstat(gateway_adapter); +} +PRIV_SHELL_CMD_FUNCTION(TestGatewayInfo,TestGatewayInfo, PRIV_SHELL_CMD_MAIN_ATTR); + +#else + +/** + * @brief: 节点测试函数 + * 1. 打开Lora硬件设施. + * 2. 连接到网关. +*/ +void TestNodeUp(void) +{ + struct Adapter* node_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + // 打开设备 + if (0 != AdapterDeviceOpen(node_adapter)) + { + printf("TestNodeUp-Fail: open lora fail!\n"); + return; + } + AdapterDeviceNetstat(node_adapter); + // 接入网络 + uint8 net_id = 0XA; + if (0 != AdapterDeviceJoin(node_adapter,&net_id)) + { + printf("TestNodeUp-Fail: join net fail!\n"); + return; + } + AdapterDeviceNetstat(node_adapter); +} +PRIV_SHELL_CMD_FUNCTION(TestNodeUp,TestNodeUp, PRIV_SHELL_CMD_MAIN_ATTR); + +/** + * @brief: 发送数据 +*/ +void TestNodeSend(void) +{ + char* data = "Hello,Gateway-OtherNode!"; + struct Adapter* node_adapter = (struct Adapter*)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + AdapterDeviceSend(node_adapter,data,strlen(data)); + AdapterDeviceNetstat(node_adapter); +} +PRIV_SHELL_CMD_FUNCTION(TestNodeSend,TestNodeSend, PRIV_SHELL_CMD_MAIN_ATTR); + +/** + * @brief: 循环发送数据 +*/ +void TestNodeSendCycle(void) +{ + int cycle_count = 5; + char* data = "Hello,Gateway-OtherNode-Cycle!"; + struct Adapter *node_adapter = (struct Adapter *)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + while (cycle_count -- > 0) + { + AdapterDeviceSend(node_adapter, data, strlen(data)); + AdapterDeviceNetstat(node_adapter); + } +} +PRIV_SHELL_CMD_FUNCTION(TestNodeSendCycle,TestNodeSendCycle, PRIV_SHELL_CMD_MAIN_ATTR); +#endif \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.h b/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.h new file mode 100755 index 000000000..1ab38fe96 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_net_final/test_lora_net_final.h @@ -0,0 +1,28 @@ +#include +#include + +// #define AS_LORA_GATEWAY + +/** + * @brief: 节点测试函数 + * 1. 打开Lora硬件设施. + * 2. 连接到网关. +*/ +void TestNodeUp(void); + +/** + * @brief: 发送数据 +*/ +void TestNodeSend(void); + +/** + * @brief: 循环发送数据 +*/ +void TestNodeSendCycle(void); + +/** + * @brief: 网关测试函数 + * 1. 打开Lora硬件设施. + * 2. 接入网络. +*/ +void TestGateway(void); \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/README.md b/APP_Framework/Applications/app_test/test_lora_p2p/README.md new file mode 100755 index 000000000..3bc748a9b --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_p2p/README.md @@ -0,0 +1,154 @@ +# 基于RISC-V终端,实现LoRa节点和LoRa网关通信私有协议 + +## 一、简介 +在RISC-V终端上实现LoRa节点和LoRa网关私有协议通信功能,客户端可以通过SHELL终端连接\断开网关,并可以向网关发送数据. + +## 二、数据结构设计说明 +### 1、Lora工作模式枚举 +```c +enum LoraMode +{ + LORA_SLEEP = 0, // 切换至休眠模式 + LORA_WORK = 1 // 切换至工作模式 +}; +``` +在客户端需要传输数据时,Lora会工作在普通模式进行数据传输,在客户端数据传输完毕后,Lora会切换到休眠模式以屏蔽数据。 + +### 2、客户端/网关状态信息 +```c +enum ClientState +{ + CLIENT_DISCONNECT = 0, // 开启但断网 + CLIENT_CONNECT, // 开启且联网 + CLIENT_BROKEN, // 硬件损坏 + CLIENT_CLOSED // 硬件关闭 +}; +enum GatewayState +{ + GATEWAY_ORIGINAL= 0, // 关闭且未开始工作 + GATEWAY_WORKING, // 开启且已经开始工作 + GATEWAY_BROKEN // 模块损坏 +}; +``` +### 3、客户端/网关参数信息 +```c +struct ClientParam +{ + uint8_t client_id; + uint8_t panid; + uint8_t gateway_id; + enum ClientState client_state; + pthread_mutex_t client_mutex; // 互斥量 +}; +struct GatewayParam +{ + uint8_t gateway_id; + uint8_t panid; + uint8_t client_infos[GATEWAY_MAX_CLIENT_NUM]; + uint8_t client_num; + enum GatewayState gateway_state; + pthread_mutex_t gateway_mutex; // 互斥量 +}; +``` +### 4、数据帧类型枚举 +```c +enum FrameType +{ + /*C ---> G*/ + CG_NET_JOIN = 0, // 入网请求 + CG_NET_QUIT, // 退网请求 + CG_DATA_SEND, // 数据传输请求 + /*G ---> C*/ + GC_REPLY_EXPECTED, // 上行请求执行成功 + GC_REPLY_UNEXPECTED, // 上行请求执行失败 +}; +``` +### 5、数据帧结构设计 +```c +struct DataFrameFormat +{ + uint8_t begin_mark_1; // 0XFF + uint8_t begin_mark_2; // 0XAA + uint8_t client_id; // 0 - 127 + uint8_t panid; // 0 - 127 + uint8_t gateway_id; // 0 - 127 + uint8_t frame_type; // 0 - 127 + uint8_t attach_data; // 0 - 127 可以是数据长度,该值的意义视数据帧类型而定 + uint8_t* user_data; // 在有数据携带时才会发送该指针所指缓冲区数据 + uint8_t crc_hi; // 这个字节没有0XFF + uint8_t crc_lo; // 这个字节会出现 0XFF,但是他的下一个字节不可能是00,必然是0XFF + uint8_t end_mark_1; // 0XFF + uint8_t end_mark_2; // 0X00 +}; +``` +## 三、包含以下功能 +### 1、客户端联网/退网/数据传输操作 +> (1)、获取使用权后将其配置为传输模式。
+> (2)、构建数据帧并设置好数据帧类型。
+> (3)、监听信道直到信道空闲。
+> (4)、发送数据帧(若是数据传输需要带上数据一起传输),若失败回到第三步。
+> (5)、接收数据帧并对数据帧进行数据过滤,若失败回到第三步。
+> (6)、解析数据帧并得到结果
+### 2、网关后台任务操作 +> (1)、获取使用权后开启数据接收。
+> (2)、根据数据帧结构,监听信道,过滤掉杂乱的信号,在接收到一个完整的数据帧并完整性校验通过后返回继续第三步,否则循环执行第二步。
+> (3)、根据得到的数据帧类型将其交付给对应的处理器处理,处理完毕后返回第二步。
+ +## 四、测试程序说明 +在进行测试时,最好先启动网关并开启网关程序,当然后启动网关也一样。 +### 1、客户端测试程序 +> (1)、`TestLoraConnectState`命令可以进行网络连接或者断开,`0-CLIENT_DISCONNECT 1-CLIENT_CONNECT`。 +> (2)、`TestLoraSendData`命令可以发送数据(联网状态下),请使用字符串(`void TestLoraSendData(char* data)`)。 +> (3)、`TestLoraClient`命令可以整体测试,先进行联网,再发送数据,再断开连接,最后再联网。 +### 2、网关测试程序 +> (1)、`TestLoraGateway`命令可以启动网关程序。 + +## 五、运行结果(##需结合运行测试截图按步骤说明##) +### 1、修改必要的框架和驱动代码 + +#### 修改文件: `xiuos/APP_Framework/Framework/framework_init.c` +1. 在第`28`行添加代码: `extern int UsrAdapterLoraInit(void);`; +2. 将第`170`行代码的`AdapterLoraInit`修改为`UsrAdapterLoraInit`,更换Lora的初始化函数; + +#### 修改文件:`xiuos/APP_Framework/Framework/connection/lora/e220/e220.c` +1. 将第`429`行代码修改为`cfg.serial_timeout = 5000;`,更改客户端超时时间; +2. 为第`478`行的`E220Ioctl`添加函数体: +```c +static int E220Ioctl(struct Adapter *adapter, int cmd, void *args) +{ + switch (cmd) + { + case 0: // LORA_SLEEP + E220LoraModeConfig(CONFIGURE_MODE_MODE); // 切换至休眠模式 + break; + case 1: // LORA_WORK + E220LoraModeConfig(DATA_TRANSFER_MODE); // 切换至传输模式 + break; + default: + break; + } + + return 0; +} +``` +### 2、修改Makefile + +#### 修改文件: `xiuos/APP_Framework/Applications/app_test/Makefile` 第 `137` 行为 `SRC_FILES += test_lora_p2p/test_lora_p2p.c` +### 3、menuconfig配置 +1. `APP_Framework > Applications > test app` 开启 `Enable application test function` ,然后进入其中,开启 `Config test lora p2p (NEW)` ; +![image](images/5-3-1.png) +2. `APP_Framework > Framework` 开启 `support connection framework (NEW)` ,然后进入其中,开启 `Using lora adapter device (NEW)` ,然后进入其中, 客户端将其配置如下图所示,网关将其配置为下下图所示; +![image](images/5-3-2-1.png) +![image](images/5-3-2-2.png) +3. 将客户端和网关分别编译、烧录到不同的板子并启动后用串口进行连接; +![image](images/5-3-3.png) +4. 启动网关程序; +![image](images/5-3-4.png) +5. 整体测试客户端; +![image](images/5-3-5.png) +6. 测试断网 +![image](images/5-3-6.png) +7. 测试联网 +![image](images/5-3-7.png) +8. 测试数据发送 +![image](images/5-3-8.png) \ No newline at end of file diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-1.png b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-1.png new file mode 100755 index 000000000..cd4f859f9 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-1.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-2-1.png b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-2-1.png new file mode 100755 index 000000000..aedf8eaf2 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-2-1.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-2-2.png b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-2-2.png new file mode 100755 index 000000000..18cd03d99 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-2-2.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-3.png b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-3.png new file mode 100755 index 000000000..181d471c0 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-3.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-4.png b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-4.png new file mode 100755 index 000000000..71673186c Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-4.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-5.png b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-5.png new file mode 100755 index 000000000..8a73cc1eb Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-5.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-6.png b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-6.png new file mode 100755 index 000000000..29ccfa34d Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-6.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-7.png b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-7.png new file mode 100755 index 000000000..2605a964e Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-7.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-8.png b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-8.png new file mode 100755 index 000000000..1aa130aa5 Binary files /dev/null and b/APP_Framework/Applications/app_test/test_lora_p2p/images/5-3-8.png differ diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/test_lora_p2p.c b/APP_Framework/Applications/app_test/test_lora_p2p/test_lora_p2p.c new file mode 100644 index 000000000..9e9ac72f1 --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_p2p/test_lora_p2p.c @@ -0,0 +1,826 @@ +#include "test_lora_p2p.h" + +/******************************public_operate*********************************/ +static const uint8_t table_crc_hi[] = + { + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, + 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40}; + +static const uint8_t table_crc_lo[] = + { + 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06, 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD, 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09, 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A, + 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4, 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3, 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3, 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4, + 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A, 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29, 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED, 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26, + 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60, 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67, 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F, 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68, + 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E, 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5, 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71, 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92, + 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C, 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B, 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B, 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C, + 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42, 0x43, 0x83, 0x41, 0x81, 0x80, 0x40}; + +struct DataFrameFormat frame_send_buffer; // 消息发送缓冲区 +struct DataFrameFormat frame_recv_buffer; // 消息接收缓冲区 +uint8_t data_buffer[LORA_FRAME_USER_MAX_LENGTH]; // 数据发送/接收缓冲区 + +// 按照要求计算出数据帧的校验值 +static uint8_t CalCrcDataFrameFormat(struct DataFrameFormat *frame) +{ + uint8_t crc_hi = 0xFF; + uint8_t crc_lo = 0xFF; + unsigned int temp; + uint8_t *bit_arr = (uint8_t *)frame; // 转化为字节的方式进行数据访问 + + uint8_t *temp_begin = bit_arr + 2; // 指向第二个数据段 + for (int i = 0; i < 5; i++) // 计算第二个字段到第六个字段,计算完毕该指针失效 + { + temp = crc_hi ^ *temp_begin++; + crc_hi = crc_lo ^ table_crc_hi[temp]; + crc_lo = table_crc_lo[temp]; + } + + if (CG_DATA_SEND == frame->frame_type) // 是否携带了上传数据 + { + if (NULL == frame->user_data) + { + printf("CalCrcDataFrameFormat: User data is null\n"); + return -1; + } + uint8_t data_length = frame->attach_data; + temp_begin = frame->user_data; + for (int i = 0; i < data_length; i++) // 计算上传数据的校验值 + { + temp = crc_hi ^ *temp_begin++; + crc_hi = crc_lo ^ table_crc_hi[temp]; + crc_lo = table_crc_lo[temp]; + } + } + + frame->crc_lo = crc_lo; + frame->crc_hi = crc_hi; + + return 0; +} + +// 按照要求计算出数据帧的校验值 +static uint8_t CheckCrcDataFrameFormat(struct DataFrameFormat *frame) +{ + uint8_t crc_hi = 0xFF; + uint8_t crc_lo = 0xFF; + unsigned int temp; + uint8_t *bit_arr = (uint8_t *)frame; // 转化为字节的方式进行数据访问 + + uint8_t *temp_begin = bit_arr + 2; // 指向第二个数据段 + for (int i = 0; i < 5; i++) // 计算第二个字段到第六个字段,计算完毕该指针失效 + { + temp = crc_hi ^ *temp_begin++; + crc_hi = crc_lo ^ table_crc_hi[temp]; + crc_lo = table_crc_lo[temp]; + } + + if (CG_DATA_SEND == frame->frame_type) // 是否携带了上传数据 + { + if (NULL == frame->user_data) + { + printf("CheckCrcDataFrameFormat: User data is null\n"); + return -1; + } + uint8_t data_length = frame->attach_data; + temp_begin = frame->user_data; + for (int i = 0; i < data_length; i++) // 计算上传数据的校验值 + { + temp = crc_hi ^ *temp_begin++; + crc_hi = crc_lo ^ table_crc_hi[temp]; + crc_lo = table_crc_lo[temp]; + } + } + + frame->crc_lo = crc_lo; + frame->crc_hi = crc_hi; + + return (frame->crc_hi == crc_hi && frame->crc_lo == crc_lo) ? 0 : -1; +} + +// 初始化数据帧的头部标识和尾部标识 +static void InitFrame(struct DataFrameFormat *frame) +{ + memset(frame, 0, sizeof(struct DataFrameFormat)); + frame->begin_mark_1 = LORA_FRAME_BEGIN_MARK_1; + frame->begin_mark_2 = LORA_FRAME_BEGIN_MARK_2; + frame->end_mark_1 = LORA_FRAME_END_MARK_1; + frame->end_mark_2 = LORA_FRAME_END_MARK_2; +} + +// 空中信号探测器,用于简易的信道碰撞检测,当一个周期未能收到数据时判定为信道空闲 +static uint8_t SignalDetector(struct Adapter *adapter) +{ + uint8_t temp_dst; + while (1 == AdapterDeviceRecv(adapter, &temp_dst, 1)) // 当读取数据超时时假定为信道空闲 + { + printf("SignalDetector: Channel Occupied\n"); + } + printf("SignalDetector: Channel Idle\n"); + return 0; +} + +// 打印消息头数据 +static void PrintFrame(struct DataFrameFormat *frame, char *prefix) +{ + printf("%s: ", prefix); + printf("client: %X, pan: %X, gateway: %X, frame_type: ", frame->client_id, frame->panid, frame->gateway_id); + switch (frame->frame_type) + { + case CG_NET_JOIN: + printf(" - CG_NET_JOIN- "); + break; + case CG_NET_QUIT: + printf(" - CG_NET_QUIT- "); + break; + case CG_DATA_SEND: + printf(" - CG_DATA_SEND- "); + break; + case GC_REPLY_EXPECTED: + printf(" - GC_REPLY_EXPECTED- "); + break; + case GC_REPLY_UNEXPECTED: + printf(" - GC_REPLY_UNEXPECTED- "); + break; + default: + break; + } + printf("attach_data: %X, crc: %X %X ", frame->attach_data, frame->crc_hi, frame->crc_lo); + if (frame->frame_type == CG_DATA_SEND) + { + frame->user_data[frame->attach_data] = '\0'; + printf("user_data: %s \n", frame->user_data); + } + else + { + printf("\n"); + } +} + +// 接收直到一个完整的数据帧并校验其完整性,不完整的数据会被丢弃 +static uint8_t RecvFrame(struct Adapter *adapter, struct DataFrameFormat *frame) +{ + memset(frame, 0, sizeof(struct DataFrameFormat)); // 缓冲区数据初始化 + uint8_t header_temp[2] = {0, 0}; // 头部数据查找缓冲区,一个用于接收数据,一个用于存储上一次接收到的数据 + + while (1 == AdapterDeviceRecv(adapter, &header_temp[1], 1)) // 查找开始标志 + { + if (LORA_FRAME_BEGIN_MARK_2 == header_temp[1]) // 查找到了第二个标志 + { + if (LORA_FRAME_BEGIN_MARK_1 == header_temp[0]) // 查看上一次的数据是不是第一个标识 + { + frame->begin_mark_1 = header_temp[0]; + frame->begin_mark_2 = header_temp[1]; + // 开始按照结构读取数据 + if (LORA_FRAME_NECK_LENGTH == AdapterDeviceRecv(adapter, &frame->client_id, LORA_FRAME_NECK_LENGTH)) // 接收除可能的用户数据以外的数据 + { + // 判断是否有用户数据 + if (CG_DATA_SEND == frame->frame_type) // 存在用户数据 + { + frame->user_data = data_buffer; // 关联数据缓冲区用于接收数据 + if (frame->attach_data != AdapterDeviceRecv(adapter, data_buffer, frame->attach_data)) // 接收数据 + { + printf("RecvFrame: Timeout or Failed 5\n"); + break; // 接收用户数据失败或者超时 + } + } + // 继续接收接下来的数据 + if (LORA_FRAME_CRC_LENGTH + LORA_FRAME_MARK_LENGTH == AdapterDeviceRecv(adapter, &frame->crc_hi, LORA_FRAME_CRC_LENGTH + LORA_FRAME_MARK_LENGTH)) + { + // 判断数据帧的完善性,看是否有尾巴 + if (LORA_FRAME_END_MARK_1 == frame->end_mark_1 && LORA_FRAME_END_MARK_2 == frame->end_mark_2) + { + // 进行数据完整性校验 + if (0 == CheckCrcDataFrameFormat(frame)) + { + return 0; // 成功接收了一帧数据 + } + printf("RecvFrame: CRC ERROR!\n"); + break; + } + printf("RecvFrame: End ERROR!\n"); + break; + } + printf("RecvFrame: Timeout or Failed 4\n"); + break; + } + printf("RecvFrame: Timeout or Failed 3\n"); + break; // 找到了头数据,却没能收到完整数据的全部失败 + } + else + { + header_temp[0] = header_temp[1]; // 记录这一次的数据 + } + } + else + { + header_temp[0] = header_temp[1]; // 记录这一次的数据 + } + } + + return -1; +} + +// 计算消息头校验并发送出去 +static uint8_t SendFrame(struct Adapter *adapter, struct DataFrameFormat *frame) +{ + // 校验数据 + if (0 == CalCrcDataFrameFormat(frame)) + { + // 发送公共信息 + if (LORA_FRAME_MARK_LENGTH + LORA_FRAME_NECK_LENGTH == AdapterDeviceSend(adapter, frame, LORA_FRAME_MARK_LENGTH + LORA_FRAME_NECK_LENGTH)) + { + // 判断是否有需要发送的用户数据 + if (CG_DATA_SEND == frame->frame_type) + { + if (frame->attach_data != AdapterDeviceSend(adapter, frame->user_data, frame->attach_data)) + { + return -1; + } + } + // 发送剩下的数据 + if (LORA_FRAME_MARK_LENGTH + LORA_FRAME_CRC_LENGTH == AdapterDeviceSend(adapter, &frame->crc_hi, LORA_FRAME_MARK_LENGTH + LORA_FRAME_CRC_LENGTH)) + { + return 0; + } + } + } + return -1; +} + +// 切换Lora的工作模式 +static void LoraModeConfig(struct Adapter *adapter, enum LoraMode mode) +{ + if (adapter->net_protocol == PRIVATE_PROTOCOL) + { + struct PrivProtocolDone *done = (struct PrivProtocolDone *)adapter->done; + done->ioctl(adapter, mode, NULL); + + printf("LoraModeConfig: Config Lora %s\n", mode == LORA_WORK ? "Working" : "Sleeping"); + } + else + { + printf("LoraModeConfig: Config Lora Fail\n"); + } +} + +// 客户端数据帧过滤器 +static uint8_t LoraClientFrameFilter(struct ClientParam *client_param, struct DataFrameFormat *frame) +{ + if (client_param->panid != frame->panid) // 是否是同一个网络 + { + printf("LoraFrameFilter: Different Networks\n"); + return -1; + } + + if (client_param->gateway_id != 0 && client_param->gateway_id != frame->gateway_id) // 不是该客户端所连接的网关 + { + printf("LoraFrameFilter: Different Gateway\n"); + return -1; + } + + if (client_param->client_id != frame->client_id) // 不是该客户端所连接的网关 + { + printf("LoraFrameFilter: Not For This Client\n"); + return -1; + } + + return 0; +} + +// 网关数据帧过滤器 +static uint8_t LoraGatewayFrameFilter(struct GatewayParam *gateway_param, struct DataFrameFormat *frame) +{ + if (gateway_param->panid != frame->panid) // 是否是同一个网络 + { + printf("LoraFrameFilter: Different Networks\n"); + return -1; + } + + if (frame->gateway_id == 0 && frame->frame_type == CG_NET_JOIN) // 入网请求 + { + return 0; + } + + if (frame->gateway_id != gateway_param->gateway_id) // 不是该网关所连接的客户端 + { + printf("LoraFrameFilter: Different Gateway\n"); + return -1; + } + + return 0; +} + +/******************************client_operate*********************************/ +static uint8_t LoraClientConnectState(struct Adapter *adapter, enum ClientState aim_state) +{ + if (aim_state == CLIENT_CONNECT || aim_state == CLIENT_DISCONNECT) // 只支持联网和退网 + { + struct ClientParam *client_param = (struct ClientParam *)adapter->adapter_param; + + if (client_param->client_state == aim_state) // 已经处于目标状态 + { + printf("LoraClientConnectState: Client %s Before\n", aim_state == CLIENT_CONNECT ? "Connect" : "Disconnect"); + return 0; + } + + PrivMutexObtain(&client_param->client_mutex); // 获取lora的使用权 + LoraModeConfig(adapter, LORA_WORK); // 切换Lora到普通接收模式 + + InitFrame(&frame_send_buffer); // 准备数据帧 + frame_send_buffer.client_id = client_param->client_id; + frame_send_buffer.panid = client_param->panid; + frame_send_buffer.gateway_id = client_param->gateway_id; + frame_send_buffer.frame_type = aim_state == CLIENT_CONNECT ? CG_NET_JOIN : CG_NET_QUIT; + + while (1) // 不停的发送信息直到收到正确的响应 + { + SignalDetector(adapter); // 监听信道直到信道空闲 + + if (0 == SendFrame(adapter, &frame_send_buffer)) + { + printf("LoraClientConnectState: Send Frame Success\n"); + PrintFrame(&frame_send_buffer, "LoraClientConnectState Send"); + if (0 == RecvFrame(adapter, &frame_recv_buffer)) // 成功接收到了一个数据帧 + { + printf("LoraClientConnectState: Recv Frame Success\n"); + PrintFrame(&frame_recv_buffer, "LoraClientConnectState Recv"); + if (0 == LoraClientFrameFilter(client_param, &frame_recv_buffer)) // 判断这个数据帧是不是自己 + { + if (frame_recv_buffer.frame_type == GC_REPLY_EXPECTED) // 请求成功 + { + client_param->gateway_id = aim_state == CLIENT_CONNECT ? frame_recv_buffer.gateway_id : 0; + client_param->client_state = aim_state; + + printf("LoraClientConnectState: Success - > Client(%X) Gateway(%X) %s\n", client_param->client_id, client_param->gateway_id, aim_state == CLIENT_CONNECT ? "Connect" : "Disconnect"); + break; + } + else + { + printf("LoraClientConnectState: Fail And Try Again\n"); + } + } + } + else + { + printf("LoraClientConnectState: Recv Frame Failed\n"); + } + } + else + { + printf("LoraClientConnectState: Send Frame Failed\n"); + } + } + + LoraModeConfig(adapter, LORA_SLEEP); // 切换Lora到休眠状态 + PrivMutexAbandon(&client_param->client_mutex); // 释放Lora + + return 0; + } + else + { + printf("LoraClientConnectState: Not Supported!\n"); + return -1; + } +} + +static uint8_t LoraClientSendData(struct Adapter *adapter, uint8_t *data_buffer, uint8_t data_length) +{ + struct ClientParam *client_param = (struct ClientParam *)adapter->adapter_param; + + if (client_param->client_state == CLIENT_CONNECT) // 只有处于联网状态的客户端才可以进行数据传输任务 + { + PrivMutexObtain(&client_param->client_mutex); // 拿到控制权 + LoraModeConfig(adapter, LORA_WORK); // 切换Lora为工作模式 + + InitFrame(&frame_send_buffer); // 准备消息头 + frame_send_buffer.client_id = client_param->client_id; + frame_send_buffer.panid = client_param->panid; + frame_send_buffer.gateway_id = client_param->gateway_id; + frame_send_buffer.frame_type = CG_DATA_SEND; + frame_send_buffer.user_data = data_buffer; // 关联数据缓冲区 + frame_send_buffer.attach_data = data_length; // 数据长度 + + while (1) // 不停的发送信息直到收到正确的响应 + { + SignalDetector(adapter); // 监听信道直到信道空闲 + + if (0 == SendFrame(adapter, &frame_send_buffer)) + { + printf("LoraClientSendData: Send Frame Success\n"); + PrintFrame(&frame_send_buffer, "LoraClientSendData Send"); + if (0 == RecvFrame(adapter, &frame_recv_buffer)) // 成功接收到了一个数据帧 + { + printf("LoraClientSendData: Recv Frame Success\n"); + PrintFrame(&frame_recv_buffer, "LoraClientSendData Recv"); + if (0 == LoraClientFrameFilter(client_param, &frame_recv_buffer)) // 判断这个数据帧是不是自己 + { + if (frame_recv_buffer.frame_type == GC_REPLY_EXPECTED) // 发送成功 + { + printf("LoraClientSendData: Send Data Success\n"); + break; + } + else + { + printf("LoraClientSendData: Fail And Try Again\n"); + } + } + } + else + { + printf("LoraClientSendData: Recv Frame Failed\n"); + } + } + else + { + printf("LoraClientSendData: Send Frame Failed\n"); + } + } + + LoraModeConfig(adapter, LORA_SLEEP); // 切换Lora到睡眠模式 + PrivMutexAbandon(&client_param->client_mutex); // 释放Lora + + return 0; + } + else + { + printf("LoraClientSendData: Client Is Disconnect\n"); + return -1; + } +} + +/**************************gateway_handlers*********************************/ + +// 网关处理客户端入网请求 +static uint8_t GatewayJoinNetHandler(struct Adapter *adapter, struct DataFrameFormat *frame) +{ + PrintFrame(frame, "GatewayJoinNetHandler Recv"); + + struct GatewayParam *gateway_param = (struct GatewayParam *)adapter->adapter_param; + + frame->gateway_id = gateway_param->gateway_id; // 确保网关设备参数正确传递 + + if (gateway_param->client_num == GATEWAY_MAX_CLIENT_NUM) // 判断网关连接的客户端是否达到上限 + { + printf("GatewayJoinNetHandler: Too much client\n"); + frame->frame_type = GC_REPLY_UNEXPECTED; + if (0 != SendFrame(adapter, frame)) + { + printf("GatewayJoinNetHandler: Client(%X) Join Gateway(%X) Failed 1 !\n", frame->client_id, gateway_param->gateway_id); + return -1; + } + PrintFrame(frame, "GatewayJoinNetHandler Send"); + printf("GatewayJoinNetHandler: Client(%X) Join Gateway(%X) Failed (MAX)!\n", frame->client_id, gateway_param->gateway_id); + return -1; + } + + for (int i = 0; i < gateway_param->client_num; i++) // 判断该客户端是否已经连接过了 + { + if (gateway_param->client_infos[i] == frame->client_id) + { + printf("GatewayJoinNetHandler: Joined before\n"); + frame->frame_type = GC_REPLY_EXPECTED; + if (0 != SendFrame(adapter, frame)) + { + printf("GatewayJoinNetHandler: Client(%X) Join Gateway(%X) Failed 2 !\n", frame->client_id, gateway_param->gateway_id); + return -1; + } + PrintFrame(frame, "GatewayJoinNetHandler Send"); + printf("GatewayJoinNetHandler: Client(%X) Join Gateway(%X) Success!\n", frame->client_id, gateway_param->gateway_id); + return 0; + } + } + + gateway_param->client_infos[gateway_param->client_num] = frame->client_id; + gateway_param->client_num++; + frame->frame_type = GC_REPLY_EXPECTED; // 成功连接到网关 + frame->gateway_id = gateway_param->gateway_id; + if (0 != SendFrame(adapter, frame)) + { + printf("GatewayJoinNetHandler: Client(%X) Join Gateway(%X) Failed 3 !\n", frame->client_id, gateway_param->gateway_id); + return -1; + } + PrintFrame(frame, "GatewayJoinNetHandler Send"); + printf("GatewayJoinNetHandler: Client(%X) Join Gateway(%X) Success!\n", frame->client_id, gateway_param->gateway_id); + return 0; +} + +// 网关处理客户端退网请求 +static uint8_t GatewayQuitNetHandler(struct Adapter *adapter, struct DataFrameFormat *frame) +{ + PrintFrame(frame, "GatewayQuitNetHandler Recv"); + + struct GatewayParam *gateway_param = (struct GatewayParam *)adapter->adapter_param; + + for (int i = 0; i < gateway_param->client_num; i++) + { + // 移除这个客户端的信息 + if (gateway_param->client_infos[i] == frame->client_id) + { + for (int j = i; j < gateway_param->client_num - 1; j++) + { + gateway_param->client_infos[j] = gateway_param->client_infos[j + 1]; + } + gateway_param->client_num--; + break; + } + } + + frame->frame_type = GC_REPLY_EXPECTED; + if (0 != SendFrame(adapter, frame)) + { + printf("GatewayQuitNetHandler: Client(%X) Quit Gateway(%X) Failed!\n", frame->client_id, gateway_param->gateway_id); + return -1; + } + PrintFrame(frame, "GatewayQuitNetHandler Send"); + printf("GatewayQuitNetHandler: Client(%X) Quit Gateway(%X) Success!\n", frame->client_id, gateway_param->gateway_id); + return 0; +} + +// 网关处理客户端的数据传输请求 +static uint8_t GatewayDataSendHandler(struct Adapter *adapter, struct DataFrameFormat *frame) +{ + PrintFrame(frame, "GatewayDataSendHandler Recv"); + + frame->frame_type = GC_REPLY_EXPECTED; + + if (0 != SendFrame(adapter, frame)) + { + printf("GatewayDataSendHandler: Client(%X) Send Data To Gateway(%X) Failed!\n", frame->client_id, frame->gateway_id); + return -1; + } + PrintFrame(frame, "GatewayDataSendHandler Send"); + printf("GatewayDataSendHandler: Client(%X) Send Data To Gateway(%X) Success!\n", frame->client_id, frame->gateway_id); + return 0; +} + +// 网关自动程序 +static void *GatewayDaemonTask(void *param) +{ + struct Adapter *gateway_adapter = (struct Adapter *)param; + if (gateway_adapter == NULL) // 判断参数合法性 + { + return NULL; + } + + struct GatewayParam *gateway_param = (struct GatewayParam *)gateway_adapter->adapter_param; + if (gateway_param->gateway_state != GATEWAY_WORKING) + { + if (0 != AdapterDeviceOpen(gateway_adapter)) // 开启Lora模块以接收消息 + { + return NULL; + } + gateway_param->gateway_state = GATEWAY_WORKING; + } + + PrivMutexObtain(&gateway_param->gateway_mutex); // 获取Lora控制权 + + while (1) // 内部业务大循环 + { + if (0 == RecvFrame(gateway_adapter, &frame_recv_buffer) && 0 == LoraGatewayFrameFilter(gateway_param, &frame_recv_buffer)) // 是否接收成功且是发给自己的 + { + gateway_handlers[frame_recv_buffer.frame_type](gateway_adapter, &frame_recv_buffer); // 根据消息类型,调用指定处理函数 + } + else + { + printf("GatewayDaemonTask: Recv No Frame\n"); + } + } + + PrivMutexAbandon(&gateway_param->gateway_mutex); // 释放Lora + + printf("GatewayDaemonTask: Exit!\n"); + return NULL; +} + +/**************************test_funcations*********************************/ +#ifdef AS_LORA_GATEWAY_ROLE + +void TestLoraGateway(void) +{ + struct Adapter *gateway_adapter = (struct Adapter *)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + + UtaskType gateway_task; + gateway_task.prio = 24; + gateway_task.stack_size = 2048; + gateway_task.func_param = gateway_adapter; + gateway_task.func_entry = GatewayDaemonTask; + strncpy(gateway_task.name, "lora_gateway_task", strlen("lora_gateway_task")); + + int32_t gateway_task_id = UserTaskCreate(gateway_task); + + if (gateway_task_id > 0) + { + printf("TestLoraGateway: Create Gateway Task Success\n"); + if (0 == UserTaskStartup(gateway_task_id)) + { + printf("TestLoraGateway: Start Gateway Task Success\n"); + } + else + { + printf("TestLoraGateway: Start Gateway Task Failed\n"); + } + } + else + { + printf("TestLoraGateway: Create Gateway Task Failed\n"); + } +} +PRIV_SHELL_CMD_FUNCTION(TestLoraGateway, TestLoraGateway No Param, PRIV_SHELL_CMD_MAIN_ATTR); + +#else +void TestLoraConnectState(int user_state) +{ + enum ClientState state; + if (user_state == 0) + { + state = CLIENT_DISCONNECT; + printf("TestLoraConnectState: Config Lora Disconnect\n"); + } + else if (user_state == 1) + { + state = CLIENT_CONNECT; + printf("TestLoraConnectState: Config Lora Connect\n"); + } + else + { + printf("TestLoraConnectState: Not Suport 0-CLIENT_DISCONNECT 1-CLIENT_CONNECT\n"); + } + + struct Adapter *client_adapter = (struct Adapter *)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + if (0 == AdapterDeviceOpen(client_adapter)) + { + if (0 == LoraClientConnectState(client_adapter, state)) + { + printf("TestLoraConnectState: Config Lora Connect State Success\n"); + } + else + { + printf("TestLoraConnectState: Config Lora Connect State Failed\n"); + } + } + else + { + printf("TestLoraConnectState: Lora Open Failed\n"); + } +} +PRIV_SHELL_CMD_FUNCTION(TestLoraConnectState, Only Suport 0-CLIENT_DISCONNECT 1-CLIENT_CONNECT, PRIV_SHELL_CMD_FUNC_ATTR); + +void TestLoraSendData(char* data) +{ + struct Adapter *client_adapter = (struct Adapter *)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + if (0 == AdapterDeviceOpen(client_adapter)) + { + if (0 == LoraClientSendData(client_adapter, data,strlen(data))) + { + printf("TestLoraSendData: Send Data Success\n"); + } + else + { + printf("TestLoraSendData: Send Data Failed\n"); + } + } + else + { + printf("TestLoraSendData: Lora Open Failed\n"); + } +} +PRIV_SHELL_CMD_FUNCTION(TestLoraSendData, char* data param, PRIV_SHELL_CMD_FUNC_ATTR); + +void TestLoraClient(void) +{ + struct Adapter *client_adapter = (struct Adapter *)AdapterDeviceFindByName(ADAPTER_LORA_NAME); + if (0 == AdapterDeviceOpen(client_adapter)) + { + printf("TestLoraClient: Open Lora Success!\n"); + if (0 == LoraClientConnectState(client_adapter, CLIENT_CONNECT)) + { + printf("TestLoraClient: Client Join Net Success!\n"); + if (0 == LoraClientSendData(client_adapter, "Hello,Gateway!", strlen("Hello,Gateway!"))) + { + printf("TestLoraClient: Client Send Data Success!\n"); + if (0 == LoraClientConnectState(client_adapter, CLIENT_DISCONNECT)) + { + printf("TestLoraClient: Client Quit Net Success!\n"); + if (0 == LoraClientConnectState(client_adapter, CLIENT_CONNECT)) + { + printf("TestLoraClient: Client Join Net Success!\n"); + } + else + { + printf("TestLoraClient: Client Join Net Failed!\n"); + } + } + else + { + printf("TestLoraClient: Client Quit Net Failed!\n"); + } + } + else + { + printf("TestLoraClient: Client Send Data Fail!\n"); + } + } + else + { + printf("TestLoraClient: Client Join Net Fail!\n"); + } + } + else + { + printf("TestLoraClient: Open Lora Failed!\n"); + } +} +PRIV_SHELL_CMD_FUNCTION(TestLoraClient, TestLoraClient No Param, PRIV_SHELL_CMD_MAIN_ATTR); + +#endif +/*******************LORA ADAPTER FUNCTION********************/ +static int UsrAdapterLoraRegister(struct Adapter *adapter) +{ + int ret = 0; + struct GatewayParam *gateway_param; + struct ClientParam *client_param; + strncpy(adapter->name, ADAPTER_LORA_NAME, NAME_NUM_MAX); + adapter->net_protocol = PRIVATE_PROTOCOL; +#ifdef AS_LORA_GATEWAY_ROLE + gateway_param = PrivMalloc(sizeof(struct GatewayParam)); + if (!gateway_param) + { + PrivFree(gateway_param); + return -1; + } + memset(gateway_param, 0, sizeof(struct GatewayParam)); + gateway_param->gateway_id = DEAFULT_GATEWAY_ID; + gateway_param->panid = DEAFULT_PANID; + gateway_param->gateway_state = GATEWAY_ORIGINAL; + PrivMutexCreate(&gateway_param->gateway_mutex, 0); + adapter->net_role = GATEWAY; + adapter->net_role_id = DEAFULT_GATEWAY_ID; + adapter->adapter_param = (void *)gateway_param; +#else // AS_LORA_CLIENT_ROLE + client_param = PrivMalloc(sizeof(struct ClientParam)); + if (!client_param) + { + PrivFree(client_param); + return -1; + } + memset(client_param, 0, sizeof(struct ClientParam)); + client_param->client_id = DEAFULT_CLIENT_ID; + client_param->panid = DEAFULT_PANID; + client_param->gateway_id = 0; + client_param->client_state = CLIENT_DISCONNECT; + PrivMutexCreate(&client_param->client_mutex, 0); + adapter->net_role = CLIENT; + adapter->net_role_id = DEAFULT_CLIENT_ID; + adapter->adapter_param = (void *)client_param; +#endif + adapter->adapter_status = UNREGISTERED; + ret = AdapterDeviceRegister(adapter); + if (ret < 0) + { + printf("Adapter lora register error\n"); + if (gateway_param) + PrivFree(gateway_param); + if (client_param) + PrivFree(client_param); + return -1; + } + return ret; +} + +int UsrAdapterLoraInit(void) +{ + int ret = 0; + struct Adapter *adapter = PrivMalloc(sizeof(struct Adapter)); + if (!adapter) + { + PrivFree(adapter); + return -1; + } + memset(adapter, 0, sizeof(struct Adapter)); + ret = UsrAdapterLoraRegister(adapter); + if (ret < 0) + { + printf("AdapterLoraInit register lora adapter error\n"); + PrivFree(adapter); + return -1; + } + AdapterProductInfoType product_info = E220Attach(adapter); + if (!product_info) + { + printf("AdapterLoraInit e220 attach error\n"); + PrivFree(adapter); + return -1; + } + adapter->product_info_flag = 1; + adapter->info = product_info; + adapter->done = product_info->model_done; + PrivSemaphoreCreate(&adapter->sem, 0, 0); + PrivMutexCreate(&adapter->lock, 0); + return ret; +} diff --git a/APP_Framework/Applications/app_test/test_lora_p2p/test_lora_p2p.h b/APP_Framework/Applications/app_test/test_lora_p2p/test_lora_p2p.h new file mode 100644 index 000000000..7e4d690aa --- /dev/null +++ b/APP_Framework/Applications/app_test/test_lora_p2p/test_lora_p2p.h @@ -0,0 +1,157 @@ +/* +* Copyright (c) 2023 AIIT XUOS Lab +* XiUOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** +* @file: test_lora_p2p.h +* @brief: a head file of lora structure +* @version: 1.0 +* @author: Chu yunfei +* @date: 2023/8/30 +*/ + +#include +#include + +#define LORA_FRAME_MAX_LENGTH 128 +#define LORA_FRAME_USER_MAX_LENGTH LORA_FRAME_MAX_LENGTH - 11 +#define LORA_FRAME_BEGIN_MARK_1 0XFF +#define LORA_FRAME_BEGIN_MARK_2 0XAA +#define LORA_FRAME_END_MARK_1 0XFF +#define LORA_FRAME_END_MARK_2 0X00 +#define LORA_FRAME_MARK_LENGTH 2 +#define LORA_FRAME_NECK_LENGTH 5 +#define LORA_FRAME_CRC_LENGTH 2 +#define LORA_FRAME_SEND_INTERVAL 500 + +#define GATEWAY_MAX_CLIENT_NUM 20 + +#define DEFAULT_ID_MAX 125 +#define DEFAULT_ID_MIN 1 +#define DEAFULT_PANID 0XA +#define DEAFULT_CLIENT_ID 0XB +#define DEAFULT_GATEWAY_ID 0XC + +extern AdapterProductInfoType E220Attach(struct Adapter *adapter); + +// Lora工作模式枚举 +enum LoraMode +{ + LORA_SLEEP = 0, // 切换至休眠模式 + LORA_WORK = 1 // 切换至工作模式 +}; + +// 客户端状态 +enum ClientState +{ + CLIENT_DISCONNECT = 0, // 开启但断网 + CLIENT_CONNECT, // 开启且联网 + CLIENT_BROKEN, // 硬件损坏 + CLIENT_CLOSED // 硬件关闭 +}; + +// 客户端参数 +struct ClientParam +{ + uint8_t client_id; + uint8_t panid; + uint8_t gateway_id; + enum ClientState client_state; + pthread_mutex_t client_mutex; // 互斥量 +}; + +// 网关状态 +enum GatewayState +{ + GATEWAY_ORIGINAL= 0, // 关闭且未开始工作 + GATEWAY_WORKING, // 开启且已经开始工作 + GATEWAY_BROKEN // 模块损坏 +}; + +// 网关参数 +struct GatewayParam +{ + uint8_t gateway_id; + uint8_t panid; + uint8_t client_infos[GATEWAY_MAX_CLIENT_NUM]; + uint8_t client_num; + enum GatewayState gateway_state; + pthread_mutex_t gateway_mutex; // 互斥量 +}; + +// 消息类型 +enum FrameType +{ + /*C ---> G*/ + CG_NET_JOIN = 0, // 入网请求 + CG_NET_QUIT, // 退网请求 + CG_DATA_SEND, // 数据传输请求 + /*G ---> C*/ + GC_REPLY_EXPECTED, // 上行请求执行成功 + GC_REPLY_UNEXPECTED, // 上行请求执行失败 +}; +// 消息数据帧格式 +struct DataFrameFormat +{ + uint8_t begin_mark_1; // 0XFF + uint8_t begin_mark_2; // 0XAA + uint8_t client_id; // 0 - 127 + uint8_t panid; // 0 - 127 + uint8_t gateway_id; // 0 - 127 + uint8_t frame_type; // 0 - 127 + uint8_t attach_data; // 0 - 127 + uint8_t* user_data; // 在有数据携带时才会发送该指针所指缓冲区数据 + uint8_t crc_hi; // 这个字节没有0XFF + uint8_t crc_lo; // 这个字节会出现 0XFF,但是他的下一个字节不可能是00,必然是0XFF + uint8_t end_mark_1; // 0XFF + uint8_t end_mark_2; // 0X00 +}; +/******************************client_operate*********************************/ +/** + * @description: 将客户端网络状态切换为联网状态或者离线状态 + * @param adapter - 需要是一个已经打开的设备 + * @param aim_state - 需要转换的状态: CLIENT_CONNECT OR CLIENT_DISCONNECT + * @return success: 0, failure: -1 + */ +static uint8_t LoraClientConnectState(struct Adapter *adapter, enum ClientState aim_state); + +/** + * @description: 在联网状态下向网关发送数据 + * @param adapter - 需要是一个已经打开的设备 + * @param data_buffer - 数据缓冲区地址 + * @param data_length - 数据长度 + * @return success: 0, failure: -1 + */ +static uint8_t LoraClientSendData(struct Adapter *adapter, uint8_t *data_buffer, uint8_t data_length); + +/*****************************gateway_handlers*********************************/ +// 网关处理客户端入网请求 +static uint8_t GatewayJoinNetHandler(struct Adapter* adapter,struct DataFrameFormat* frame); +// 网关处理客户端退网请求 +static uint8_t GatewayQuitNetHandler(struct Adapter* adapter,struct DataFrameFormat* frame); +// 网关处理客户端的数据传输请求 +static uint8_t GatewayDataSendHandler(struct Adapter* adapter,struct DataFrameFormat* frame); +// 网关自动化处理程序任务,需要启动 +static void *GatewayDaemonTask(void *param); + +// 网关处理程序与消息类型的对照表 +static uint8_t (*gateway_handlers[])(struct Adapter*,struct DataFrameFormat*) = +{ + [CG_NET_JOIN] = GatewayJoinNetHandler, + [CG_NET_QUIT] = GatewayQuitNetHandler, + [CG_DATA_SEND] = GatewayDataSendHandler +}; + +/************************lora adapter function*********************************/ +// 自定义注册函数 +static int UserAdapterLoraRegister(struct Adapter *adapter); +// 自定义初始化函数 +int UserAdapterLoraInit(void); \ No newline at end of file