串口透传 前言 : 有了上面的基础, 接下来就可以打造无线串口功能了 实现平台 :WeBee CC2540 模块及功能底板 图 3.117 网蜂 CC2540 模块及功能底板实验现象 : 两台 PC 通过串口连接 CC2540, 通过设置好串口调试助手, 就可以相互收发信息 也可在一台 PC 利用两个串口实现这个功能 实验讲解 : 整个实验用到两个模块, 一个作为服务器 一个作为客户端, 重点为下面两个方向 : 1 客户端接收串口数据并写入特征值 2 服务器接收串口数据并写入特征值, 再通知主机
本实验需要用到两个特征值, 两个特征值的属性各不相同, 我们同样在 SimpleGATTProfile 中新建即可, 接下来就开始吧 新建特征值表 : 表 3.3 串口透传特征值属性 长度 属性 UUID 功能 (byte) SIMPLEPROFILE_CHA R6 15 可读可写 FFF6 服务器接收客户端的串口 数据 SIMPLEPROFILE_CHA 15 不能直接读写, FFF7 客户端接收服务器的串口 R7 通过通知发送 数据 1.1.1 客户端接收串口数据并写入特征值 如何新建特征值我们上一节讲得很清楚了,SIMPLEPROFILE_CHAR6 可以直接利用上一节中新建的特征值, 这里把字长改为 15 即可, 并在 static void simpleprofilechangecb( uint8 paramid ) 函数中添加 CHAR6 改变的处理函数, 这里是串口输出, 我们的格式是 : 串口数据的长度 ( 1 Byte) + 数据 ( 14 Byte) 1. case SIMPLEPROFILE_CHAR6: // 串口透传 Profile 2. char newchar[15]; 3. SimpleProfile_GetParameter( SIMPLEPROFILE_CHAR6, newchar); 4. if( newchar[0] >= 15 ) 5. { 6. HalUARTWrite(0,&newchar[1],14); 7. HalUARTWrite(0,"...\n",4 ); 8. } 9. else 10. { 1
11. HalUARTWrite(0,&newchar[1],newchar[0]); 12. } 13. break; 接下来是 simpleblecentral 的写入 : 图 3.118CHAR6 接收 在对 CHAR1 的写入代码中 1. // Do a write 2. attwritereq_t req; 3. 4. req.handle = simpleblecharhdl; // 这个是在哪里得到的? 5. req.len = 1; 6. req.value[0] = 0x00; 7. req.sig = 0; 8. req.cmd = 0; 9. GATT_WriteCharValue( simplebleconnhandle, &req, simplebletaskid ); 2
进入 static void simpleblegattdiscoveryevent( gattmsgevent_t *pmsg ) 函 数 图 3.119 读取 Handle 图 3.120 已获取 CHAR1 handle 3
这里我们就可以明白 simpleblecharhd1 何来了, 把这段改为我们需要的读取 CHAR6 的 Handle 然后调用 GATT_ReadUsingCharUUID( simplebleconnhandle, &req, simplebletaskid ); 就可以获取到该特征值的属性 图 3.121 读取 CHAR6Handle 值 下一步就是串口的接收与 CHAR6 的写入, 直接来分析代码 1. uint8 gstatus; 2. static void simplebleperipheral_handleserial(mtosalserialdata_t *cmdmsg) 3. { 4. uint8 i,len,*str=null; //len 有用数据长度 5. uint8 CMD; 6. str=cmdmsg->msg; // 指向数据开头 7. len=*str; //msg 里的第 1 个字节代表后面的数据长度 8. 4
9. if ( ( simpleblestate == BLE_STATE_CONNECTED ) && ( simpleblecharhd6!= 0 ) ) // 写 char6 10. { 11. uint8 ValueBuf[SIMPLEPROFILE_CHAR6_LEN]; 12. 13. if ( len >= SIMPLEPROFILE_CHAR6_LEN ) len = SIMPLEPROFILE_CHAR6_LEN; 14. else len += 1; 15. for(i=0;i<=len;i++) 16. ValueBuf[i] = str[i]; 17. 18. gattpreparewritereq_t req; 19. 20. req.handle = simpleblecharhd6; 21. req.len = SIMPLEPROFILE_CHAR6_LEN; 22. req.offset = 0; 23. req.pvalue = osal_msg_allocate(simpleprofile_char6_len); 24. osal_memcpy(req.pvalue,valuebuf,simpleprofile_char6_len); 25. GATT_WriteLongCharValue(simpleBLEConnHandle, &req, simplebletaskid ); 26. 27. } 28. else 29. { 30. HalUARTWrite(0,"Not Connect\n", 12 ); 31. } 32. 33. } 5
第 9 行 : 判断是否处于连接状态并且以获取 CHAR6 的 handle 值第 18-23 行 : 设置对应的参数第 24 行 : 将串口的数据赋给发送数组中第 25 行 : 调用 GATT 层中的 API 写入特征值 下载连接后, 在 simpleblecentral 模块中通过串口发送小于 15 字节的数据, 就可以在 SimpleBLEPeripheral 的串口输出中看到对应的数据, 到这里就实现 了第一个方向的传输 图 3.122 单方向透传测试 可在 SimpleBLEPeripheral 中看到的串口输出 : 6
图 3.123 接收成功 7
1.1.2 服务器接收串口数据并写入特征值, 再通知主机 下面主要注意一下第二个特征值的添加, 特别是其属性, 他的属性与 CHAR4 的一样, 这里可以参考一下 下面主要看一下和之前的差别之处 : 图 3.124CHAR7 属性 bstatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value ) 函数中 8
图 3.125CHAR7 通知 具体代码 1. else if ( ( pmsg->method == ATT_HANDLE_VALUE_NOTI ) ) // 通知 2. { 3. //CHAR7 的通知 串口打印 4. if( pmsg->msg.handlevaluenoti.handle == 0x0038) { 5. if(pmsg->msg.handlevaluenoti.value[0]>=15) 6. { 7. HalUARTWrite(0,&pMsg->msg.handleValueNoti.value[1],14 ); 8. HalUARTWrite(0,"...\n",4 ); 9. } 10. else 11. { 12. HalUARTWrite(0,&pMsg->msg.handleValueNoti.value[1], 13. pmsg->msg.handlevaluenoti.value[0] ); 14. } 15. } 9
16. } 第 1 行 : 判断是否为通知事件第 4 行 : 判断是否为 CHAR7 写入事件第 5-9 行 : 如果串口接收的数据大于 15, 则只能显示部分数据第 9-14 行 : 显示串口接收到的全部数据 这两处就是最大的区别, 当服务器端自己把数据改变时, 客户端并不会自己主动来读取数据的变化, 这里就有一个通知机制, 服务器要接收到 CHAR7 是否改变, 就需要打开对 CHAR7 的通知, 就是在 CHAR7 的 Handle+1 写入 0x0001 下面就进入 SimpleBLECentral 工程中, 我们利用按键 S1 来使能这个通知 图 3.126 使能 CHAR7 通知 具体代码 : 1. if ( keys & HAL_KEY_SW_1 ) 2. { 3. /* 使能通知 Char7 */ 10
4. uint8 ValueBuf[2]; 5. gattpreparewritereq_t req; 6. 7. req.handle = 0x0039; 8. req.len = 2; 9. ValueBuf[0] = 0x01; 10. ValueBuf[1] = 0x00; 11. req.offset = 0; 12. req.pvalue = osal_msg_allocate(2); 13. osal_memcpy(req.pvalue,valuebuf,2); 14. GATT_WriteLongCharValue( simplebleconnhandle, 15. &req, simplebletaskid ); 16. 17. HalUARTWrite(0,"Enable Notice\n", 14 ); 18. } 第 7 行 : 选择要打开通知的特征值 handle 第 9-10 行 : 赋给对应的命令, 这里是打开通知, 即 0x0001 第 14 行 : 调用 API 使能 CHAR7 的通知 使能通知后, 当服务器端有数据更新的通知会在哪里得到, 这就要找到 static void simpleblecentralprocessgattmsg( gattmsgevent_t *pmsg ) 函数, 这个函数式对 GATT 的事件进行处理的, 包括通知 在函数中添加如下 : 11
图 3.127CHAR7 接收 这里就完成 SimpleBLECentral 端的数据接收处理 最后一步就是 SimpleBLECentral 工程 的串口接收了 具体代码 : 1. static void simplebleperipheral_handleserial(mtosalserialdata_t *cmdmsg) 2. { 3. uint8 i,len,*str=null; //len 有用数据长度 4. str=cmdmsg->msg; // 指向数据开头 5. len=*str; //msg 里的第 1 个字节代表后面的数据长度 6. 7. // 串口透传 设置 char7 参数 8. { 9. uint8 charvalue7[simpleprofile_char7_len]; 10. 12
11. if ( len >= SIMPLEPROFILE_CHAR7_LEN ) len = SIMPLEPROFILE_CHAR7_LEN; 12. else len += 1; 13. for(i=0;i<=len;i++) 14. charvalue7[i] = str[i]; 15. 16. SimpleProfile_SetParameter( SIMPLEPROFILE_CHAR7, 17. SIMPLEPROFILE_CHAR7_LEN, charvalue7 ); 18. } 19. 20. } 第 16 行 : 将接收到的数据写入特征值 接收到串口数据后马上更新 CHAR7 的数据, 这时就会调用到刚才的函数通知客户 端 图 3.128CHAR7 通知 13
至此第二个方向就完成了 下载后等待连接完成, 按下客户端的 S1 按键, 就可在 simpleblecentral 端或 SimpleBLEPeripheral 端中通过串口发送小于 15 字节的数据, 并可从对方的串口输出中看到对应的数据 图 3.129 按键使能通知 simpleblecentral 端 14
SimpleBLEPeripheral 端 图 3.130 simpleblecentral 端透传测试 图 3.131 SimpleBLEPeripheral 端透传测试 到这里就完成两个方向的传输, 相应的我们的串口透传已完成测试 15