本文使用环境:
主控:W800-KIT (开发板)
兼容:W800 W801 AIR101
开发环境:CDK
SDK:W801/W800的SDK(tls库)
我的联盛德问答社区主页
前文:
W801/W800-wifi-socket开发(一)-UDP
W801蓝牙收发数据与控制设计(一)-INDICATE
W801蓝牙收发数据与控制设计(二)-NOTIFY方式
本文github工程
这份代码包含前文的某些函数,所以可能会比较乱。
写在前面:
这个代码有很多地方都有BUG,有遇到的请联系我修改。比如未对传输的数据进行严格筛查,每次都要重新输入账户和密码,可以将数据写入flash。。。。因为只是一个基础的学习思路,所以并未深入完善代码。
^^^^程序功能: 通过手机的蓝牙输入wifi密码和账户连接至路由器,使用开发板I连接电脑端的服务器(UDP协议,使用网络调试助手模拟),传输数据。同时手机端支持重连和停止连接。
^^^^本文是在前文的基础上完成的 。需要借鉴前文的配置。。。
1、项目整体设计
^^^^整体流程如下所示。指令字符串必须严格执行,程序只判断每一串字符的前四个进行状态的选择。
连接WIFI指令的格式为:conn+账号+密码
。比如:conn+yyds+1234567890
注意:上述流程只是本文设计所用函数,并不是整个工程的流程图。
^^^^主任务主要创建两个任务,一个用于蓝牙信息解析,一个用于wifi连接和数据发送。
//add by zxx satrt
//创建任务
void My_task(void)
{
//蓝牙接收消息队列
if(tls_os_queue_create(&ble_q, 32)!=TLS_OS_SUCCESS)
{
printf("create queue fail\n");
return;
}
//wifi连接消息队列
if(tls_os_queue_create(&ble_wifi_q, 32)!=TLS_OS_SUCCESS)
{
printf("create queue fail\n");
return;
}
tls_os_task_create(NULL, NULL,
my_ble_msg_task, //蓝牙接收任务
NULL,
(void *)MyBLETaskStk, /* task's stack start address */
MYBLE_TASK_SIZE * sizeof(u32), /* task's stack size, unit:byte */
MYBLE_TASK_PRIO,
0);
tls_os_task_create(NULL, NULL,
my_ble_wifi_task, //wifi连接任务
NULL,
(void *)MyBLEWIFITaskStk, /* task's stack start address */
MYBLE_TASK_SIZE * sizeof(u32), /* task's stack size, unit:byte */
MYBLE_TASK_PRIO,
0);
}
//add by zxx end
^^^^首先重新解析一下蓝牙接收的数据,这里因为要处理字符串,所以不再单字节进行处理,打开gatt_svr_chr_demo_access_func()
函数,做如下修改:
static int
gatt_svr_chr_demo_access_func(uint16_t conn_handle, uint16_t attr_handle,
struct ble_gatt_access_ctxt *ctxt, void *arg)
{
int i = 0;
struct os_mbuf *om = ctxt->om;
switch (ctxt->op) {
case BLE_GATT_ACCESS_OP_WRITE_CHR:
while(om) {
if(g_ble_uart_output_fptr)
{
g_ble_uart_output_fptr((uint8_t *)om->om_data, om->om_len);
}else
{
//add by zxx start
//print_bytes(om->om_data, om->om_len);
//首字节为长度,需要添加一个'\0',所以字节长度加一
ble_data[0] = om->om_len+1;
//其他的原样复制
memcpy(&ble_data[1],om->om_data,om->om_len);
printf("rec: %s len:%d\n",om->om_data,om->om_len);
//添加字符串结束符号
ble_data[om->om_len+1] = '\0';
if(om->om_len>0)
tls_os_queue_send(ble_q,ble_data, 0);
//add by zxx end
}
om = SLIST_NEXT(om, om_next);
}
return 0;
default:
assert(0);
return BLE_ATT_ERR_UNLIKELY;
}
}
^^^^定义一个全局变量,用于wifi的重连,手机端可以通过发送信息,改变该状态:
//全局变量,表示wifi的重连状态,0表示正常,1表示重新连接
u8 wifi_reconnect_state = 0;
^^^^蓝牙信息解析函数
//定义四种状态
#define MY_BLE_WIFI_STATE_START 1 //开始连接
#define MY_BLE_WIFI_STATE_STOP 2 //停止连接
#define MY_BLE_WIFI_STATE_RECONNECT 3 //重新连接
#define MY_BLE_WIFI_STATE_CONNECT 4 //连接wifi,并执行发送程序
void my_ble_msg_task(void *sdata)
{
u8 msg_state[4];
u8 ble_wifi_state = 0;
u8 *msg;
demo_bt_enable();
while(bt_adapter_state == WM_BT_STATE_OFF)
{
tls_os_time_delay(5000 /HZ);
}
tls_os_time_delay(5000 /HZ);
demo_ble_server_on();
printf("ble ready ok \r\n");
while(1)
{
//接收手机发送的数据,注意是数据是按照字节进行的接收
//msg[0]表示数据长度已经包含有'\0'
tls_os_queue_receive(ble_q,&msg, 0, 0);
printf("rev main:%s len:%d\n",&msg[1] ,msg[0]);
//提取前4位,前四位为状态标志
strncpy(msg_state,&msg[1],4);
//判断前四位的状态
if(!strncmp(msg_state,"star",4)) ble_wifi_state = MY_BLE_WIFI_STATE_START;
else if(!strncmp(msg_state,"stop",4)) ble_wifi_state = MY_BLE_WIFI_STATE_STOP;
else if(!strncmp(msg_state,"reco",4)) ble_wifi_state = MY_BLE_WIFI_STATE_RECONNECT;
else if(!strncmp(msg_state,"conn",4))
{
//conn命令至少应该是"conn+s+p'\0'" 九个字节
if(msg[0] < 9) //小于9说明命令是错的
{
printf("conn err...\n");
tls_ble_server_demo_api_send_notify_msg("conn err...",sizeof("conn err..."));
ble_wifi_state = 0;
}
else
ble_wifi_state = MY_BLE_WIFI_STATE_CONNECT;
}
else ble_wifi_state = 0;
switch(ble_wifi_state)
{
case MY_BLE_WIFI_STATE_CONNECT:
tls_ble_server_demo_api_send_notify_msg("\'conn+ssid+pwd\'",sizeof("\'conn+ssid+pwd\'"));
wifi_reconnect_state = 0; //重置
tls_os_queue_send(ble_wifi_q,&msg[6], 0);
break;
case MY_BLE_WIFI_STATE_STOP:
tls_ble_server_demo_api_send_notify_msg("stop connect",sizeof("stop connect"));
wifi_reconnect_state = 1; //断开连接
break;
case MY_BLE_WIFI_STATE_RECONNECT:
tls_ble_server_demo_api_send_notify_msg("start reconnect",sizeof("start reconnect"));
wifi_reconnect_state = 1; //断开连接
break;
//只发送信息至手机端
case MY_BLE_WIFI_STATE_START:
tls_ble_server_demo_api_send_notify_msg("ready connect",sizeof("ready connect"));
break;
default:
printf("ble_wifi_state is err \n");
break;
}
}
}
^^^^连接函数较为简单,使用消息队列等待蓝牙解析任务发送的数据,正确接收后,进入服务器连接,并循环发送数据。当接收重连指令后,会更新wifi_reconnect_state
,退出while发送程序,重连时,必须closesocket,否者无法正常绑定端口。
void my_ble_wifi_task(void *sdata)
{
//密码和账户的数组,其实这里可以定义指针并使用malloc灵活申请,我图方便直接定义固定长度
u8 ssid[50];
u8 pwd[50];
//测试发送数组
u8 test_data[10] = {0,1,2,3,4,5,6,7,8,9};
//消息队列接收
u8 *msg;
while(1)
{
printf("wait connect..\n");
//必须关闭socket,否者不能正常绑定端口
close_udp_socket_demo();
//消息队列,接收蓝牙解析任务传输的账号和密码
tls_os_queue_receive(ble_wifi_q,&msg, 0, 0);
printf("rev task:%s len:%d\n",msg,strlen(msg));
//字符串的总长度 不带结束字符的长度
u8 msg_len = strlen(msg);
u8 ssid_size = 0; //账户的长度 不带结束字符
//下列循环主要用于找出账户的长度
for(int i=0; msg[i] != '\0'; i++)
{
if(msg[i] != '+')
ssid_size++;
else
break;
}
//拷贝账户名
memcpy(ssid,msg,ssid_size);
//添加结束字符
ssid[ssid_size] = '\0';
//拷贝密码,注意要排除加号
memcpy(pwd,&msg[ssid_size+1],msg_len-ssid_size-1); //排除掉+号
//末尾加上结束字符
pwd[msg_len-ssid_size-1] = '\0';
printf("ssid:%s len:%d pwd:%s len:%d \n",ssid,strlen(ssid),pwd,strlen(pwd));
//连接WIFI
demo_connect_net(ssid,pwd);
//延时
tls_os_time_delay(3000);
//连接服务器
socket_udp_demo(1,10086,"192.168.1.87");
//正常情况下发送程序,当wifi_reconnect_state为1时,表示重新连接
while(0 == wifi_reconnect_state)
{
udp_send_data_self(test_data,10);
tls_os_time_delay(500);
}
}
}
^^^^上述程序中close_udp_socket_demo()
为自定义函数,在wm_udp_demo.c
文件中。
void close_udp_socket_demo(void)
{
printf("close udp socket \n");
closesocket(demo_udp->socket_num);
}
^^^^同时在wm_demo_console.h
申明。
extern void udp_send_data_self(u8 *data,int data_len);
extern void close_udp_socket_demo(void);
#endif /*__WM_DEMO_CMD_H__*/
^^^^在手机端提前设置好如下发送指令。
^^^^手机蓝牙APP如下:
^^^^程序下载至开发板,点击账户+密码。程序运行正常,配网成功,并且正常和服务器发送数据。
^^^^点击重连后开发板断开连接,并等待新连接。
^^^^再次发送账户+密码指令。