文章目录:
4.蓝牙模块APP专业调试测试程序(操控界面:按钮、文本框、滑动条、摇杆等)
第二种 以电脑为服务器的TCP连接(WIFI模块 和 无限路由器 和 电脑)
第三种 以WIFI模块为服务器的TCP连接(WIFI模块 和 电脑)
第四种 WIFI模块与手机APP通信(WIFI模块 和 手机)
1.3.1 测试数据收发(阿里云服务器发送——>接收MQTT)
1.3.2 测试数据上行(MQTT发送——>阿里云服务器接收)
下面都是一些基本的入门操作!
一:蓝牙模块
如果想深入学习:可以了解 底层:蓝牙通讯协议 应用层(蓝牙与手机的高级连接):APP控制设备、在微信小程序上显示设备参数、两台设备之间传送图片和音乐....
产品简介 JDY-08透传模块是基于蓝牙4.0协议标准,工作频段为2.4GHZ范围,调制方式为GFSK, 最大发射功率为0db,最大发射距离80米,采用TICC2541芯片设计, 支持用户通过AT命令修改设备名、服务UUID、发射功率、配对密码等指令,方便快捷使用灵活 功能简介(本身就是一个单片机) 1:微信透传(支持AirSync 协议,应用于微信HS或厂家服务器通信、包括长包数据解析收发) 2:微信控制模式(电机调速、LED灯开关控制) 3:APP透传(支持Android、 IOS数据透传) 4: iBcacon模式(支持微信摇一摇协议与苹果iBcacon协议) 5:传感器模式(温度、湿度等众多传感器数据采集应用) 6:主机透传模式(应用模块间数据透传,主机与从机通信) 7:主机观察者模式《应用传感器、室内定位) 8: PWM模式(应用于电机调速、LED灯亮度调节) 9:IO模式(应用于手机控制继电器或LED亮灭) 10:室内室位应用(应用采集iBcacon 的数据来实现范围定位) 11:RTC模式 12:RTC报警模式:可以设置RTC的报警时间来控制IO口,支持两个报警时间设置
调试软件不一定 lOS手机:安装“蓝牙调试助手”APP 安卓手机:安装“蓝牙调试器”APP 透传:是指两台设备之间,不经过任何处理(直接收发)的数据传输 如何使用? 连接开发板电源——>手机打开调试器软件——>找到对应的蓝牙名称并点击连接——>可以进行对话
由于RS485总线与蓝牙模块共同占用:PA2 PA3 PA8引脚,所以二者不可以同时连接 使用蓝牙模块时:需要插入标注为“蓝牙模块”的P10跳线、将标注为RS485总线”的P14跳线断开 通信:单片机的USART2串口与蓝牙模块进行通信
设置
将PA2端口设置为——>USART_TX 将PA3端口设置为——>USART_RT Connectivity——>USART2——>Mode设置为异步模式Asynchronous ——>点击参数设置Parameter Seting Basic Parameters Baud Rate 115200 Bits/s Word Length 8 Bits (including Parity) Parity None Stop Bits 1 Advanced Parameters Data Direction Receive and Transmit Over Sampling 16 Samples ——>MVIC Setting——>勾选中断允许USART2 global interrupt System Core——>设置睡眠唤醒的GPIO端口——>设置PA8为GPIO输出 ——>点击右上GPIO选项卡——>点击PA8这行——>L O P H RS485_RE
1.蓝牙模块透传收发测试程序
bt文件夹(蓝牙模块驱动程序文件夹)
bt.h
#ifndef BT_BT_H_ #define BT_BT_H_ #include "stm32f1xx_hal.h" //HAL库文件声明 #include "main.h" #include "../usart/usart.h" #include "../delay/delay.h" #include <string.h>//用于字符串处理的库 #include <stdarg.h> #include <stdlib.h> #include "stdio.h" extern UART_HandleTypeDef huart2;//声明UART2的HAL库结构体 void BT_WEEKUP (void);//蓝牙模块唤醒函数 void BT_printf (char *fmt, ...); //BT蓝牙模块发送 #endif /* BT_BT_H_ */
bt.c
#include "bt.h" //蓝牙模块唤醒函数 //对蓝牙模块上的PWRC(P00)接口一个低电平脉冲,如不使用睡眠模式可忽略此函数 void BT_WEEKUP (void) { HAL_GPIO_WritePin(RS485_RE_GPIO_Port,RS485_RE_Pin, GPIO_PIN_RESET);//PWRC接口控制 delay_us(100); HAL_GPIO_WritePin(RS485_RE_GPIO_Port,RS485_RE_Pin, GPIO_PIN_SET);//PWRC接口控制 } //蓝牙模块通信,使用UART2(与RS485复用),这是BT蓝牙的printf函数 //调用方法:BT_printf("123"); //向UART3发送字符123 void BT_printf (char *fmt, ...) { char buff[USART2_REC_LEN+1]; //用于存放转换后的数据 [长度] uint16_t i=0; va_list arg_ptr; va_start(arg_ptr, fmt); vsnprintf(buff, USART2_REC_LEN+1, fmt, arg_ptr);//数据转换 i=strlen(buff);//得出数据长度 if(strlen(buff)>USART2_REC_LEN)i=USART2_REC_LEN;//如果长度大于最大值,则长度等于最大值(多出部分忽略) HAL_UART_Transmit(&huart2,(uint8_t *)buff,i,0xffff);//串口发送函数(串口号,内容,数量,溢出时间) va_end(arg_ptr); } //所有USART串口的中断回调函数HAL_UART_RxCpltCallback,统一存放在【USART1.C】文件中。
usart.c
#include "usart.h" uint8_t USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART1_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存 //蓝牙模块的接收数组和标记位 uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART2_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存 uint8_t RS485orBT;//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 uint8_t USART3_RX_BUF[USART3_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART3_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART3_NewData;//当前串口中断接收的1个字节数据的缓存 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数 { if(huart ==&huart1)//判断中断来源(串口1:USB转串口) { printf("%c",USART1_NewData); //把收到的数据以 a符号变量 发送回电脑 if((USART1_RX_STA&0x8000)==0){//接收未完成 if(USART1_RX_STA&0x4000){//接收到了0x0d if(USART1_NewData!=0x0a)USART1_RX_STA=0;//接收错误,重新开始 else USART1_RX_STA|=0x8000; //接收完成了 }else{ //还没收到0X0D if(USART1_NewData==0x0d)USART1_RX_STA|=0x4000; else{ USART1_RX_BUF[USART1_RX_STA&0X3FFF]=USART1_NewData; //将收到的数据放入数组 USART1_RX_STA++; //数据长度计数加1 if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收 } } } HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); //再开启接收中断 } if(huart ==&huart2)//判断中断来源(RS485/蓝牙) { if(RS485orBT){//判断当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 USART2_RX_BUF[0]=USART2_NewData;//收到数据放入缓存数组(只用到1个数据存放在数组[0]) USART2_RX_STA++;//数据接收标志位加1 HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData, 1); //再开启接收中断 }else{ if((USART2_RX_STA&0x8000)==0){//接收未完成 if(USART2_RX_STA&0x4000){//接收到了0x0d if(USART2_NewData!=0x0a)USART2_RX_STA=0;//接收错误,重新开始 else USART2_RX_STA|=0x8000; //接收完成了 }else{ //还没收到0X0D if(USART2_NewData==0x0d)USART2_RX_STA|=0x4000; else{ USART2_RX_BUF[USART2_RX_STA&0X3FFF]=USART2_NewData; //将收到的数据放入数组 USART2_RX_STA++; //数据长度计数加1 if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收 } } } HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //再开启接收中断 } } if(huart ==&huart3)//判断中断来源(串口3:WIFI模块) { printf("%c",USART3_NewData); //把收到的数据以 a符号变量 发送回电脑 HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData,1); //再开启接收中断 } }
main.c
#include "../../icode/bt/bt.h" int main(void) { HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //开启串口2接收中断 RS485orBT = 0; //当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 while (1) { if(USART2_RX_STA!=0){//判断中断接收标志位(蓝牙模块BT,使用USART2) BUZZER_SOLO1();//蜂鸣器输出单音的报警音 BT_printf("%c",USART2_RX_BUF[0]); //蓝牙发送(仅发送第一个字符) USART2_RX_STA=0;//标志位清0,准备下次接收 } if(KEY_1())//按下KEY1判断 { BUZZER_SOLO2();//提示音 BT_printf("A");//向蓝牙发送字符A } if(KEY_2())//按下KEY2判断 { BUZZER_SOLO2();//提示音 BT_printf("B");//向蓝牙发送字符B } } }
2.蓝牙模块AT指令发送与回复判断程序
什么是AT指令:质上就是对蓝氦模块肉置功能的设置指令 是一种设置格式,并没有统一标准,每个模块都有直己独特的设置与控制肉容 从终端设备或数据终端设备,向终端适配器或数据电路终端设备发送的 其对所传输的数据包大小有定义: 即对于AT指令的发送,除AT两个字符外,最多可以接收1056个字符的长度(包括最后的空字符〉 每个AT命令行中只能包含一条AT指令;对于由终端设备主动向PC端报告的URC指示或者response响应,也要求一行最多有一个,不允许上报的一行中有多条指示或者响应
usart.c
#include "usart.h" uint8_t USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART1_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存 uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART2_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存 uint8_t RS485orBT;//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 uint8_t USART3_RX_BUF[USART3_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART3_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART3_NewData;//当前串口中断接收的1个字节数据的缓存 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数 { if(huart ==&huart1)//判断中断来源(串口1:USB转串口) { printf("%c",USART1_NewData); //把收到的数据以 a符号变量 发送回电脑 if((USART1_RX_STA&0x8000)==0){//接收未完成 if(USART1_RX_STA&0x4000){//接收到了0x0d if(USART1_NewData!=0x0a)USART1_RX_STA=0;//接收错误,重新开始 else USART1_RX_STA|=0x8000; //接收完成了 }else{ //还没收到0X0D if(USART1_NewData==0x0d)USART1_RX_STA|=0x4000; else{ USART1_RX_BUF[USART1_RX_STA&0X3FFF]=USART1_NewData; //将收到的数据放入数组 USART1_RX_STA++; //数据长度计数加1 if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收 } } } HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); //再开启接收中断 } if(huart ==&huart2)//判断中断来源(RS485/蓝牙) { if(RS485orBT){//判断当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 USART2_RX_BUF[0]=USART2_NewData;//收到数据放入缓存数组(只用到1个数据存放在数组[0]) USART2_RX_STA++;//数据接收标志位加1 HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData, 1); //再开启接收中断 }else{ if((USART2_RX_STA&0x8000)==0){//接收未完成(将USART2_RX_STA最高位1位规定是接收完成标志位) if(USART2_NewData==0x0A)//如收到0x0A表示接收到结束符(蓝牙模块回复数据以0x0A为结束符) { USART2_RX_STA|=0x8000;//收到0x0A,接受完成 } else{ //如没有收到0x0A则继续接收数据内容并把数量加1 USART2_RX_BUF[USART2_RX_STA&0X7FFF]=USART2_NewData; //将收到的数据放入数组 USART2_RX_STA++; //数据长度计数加1 if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收 } } HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //再开启接收中断 } } if(huart ==&huart3)//判断中断来源(串口3:WIFI模块) { printf("%c",USART3_NewData); //把收到的数据以 a符号变量 发送回电脑 HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData,1); //再开启接收中断 } }
main.c
int main(void) { RetargetInit(&huart1);//将printf()函数映射到UART1串口上 HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1);//开启串口1接收中断 HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //开启串口2接收中断 RS485orBT = 0;当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 LED_1(0);//LED1控制 //LED状态复位 LED_2(0);//LED2控制 while (1) { //蓝牙模块回复数据的处理程序 if(USART2_RX_STA&0x8000){//判断中断接收标志位(蓝牙模块BT,使用USART2) if((USART2_RX_STA&0x7FFF) == 3 //判断接收数量3个 && USART2_RX_BUF[0]=='+' //判断接收第1个字符是不是+ && USART2_RX_BUF[1]=='O' //判断接收第2个字符是不是O && USART2_RX_BUF[2]=='K') //判断接收第3个字符是不是K { LED_1(1);//LED1控制 //LED1亮,表示接收到+OK(成功)的回复 } else if((USART2_RX_STA&0x7FFF) == 4 //判断接收数量4个 && USART2_RX_BUF[0]=='+' //判断接收第1个字符是不是+ && USART2_RX_BUF[1]=='E' //判断接收第2个字符是不是E && USART2_RX_BUF[2]=='R' //判断接收第3个字符是不是R && USART2_RX_BUF[3]=='R') //判断接收第4个字符是不是R { LED_2(1);//LED2控制 //LED2亮,表示接收到+ERR(失败)的回复 } USART2_RX_STA=0;//标志位清0,准备下次接收 } //AT指令的发送程序 if(KEY_1())//按下KEY1判断 { BUZZER_SOLO2();//提示音 LED_1(0);//LED1控制 //在蓝牙模块回复之前先将LED状态复位 LED_2(0);//LED2控制 BT_printf("AT+NAMECC2541");//向蓝牙模块发送AT指令(修改模块的广播名为CC2541) } if(KEY_2())//按下KEY2判断 { BUZZER_SOLO2();//提示音 LED_1(0);//LED1控制 //在蓝牙模块回复之前先将LED状态复位 LED_2(0);//LED2控制 BT_printf("AT+DISC");//向蓝牙模块发送AT指令(断开与手机的连接) } } }
3.蓝牙模块APP按钮控制应用程序
手机APP点击蓝牙连接——>点击按钮控制 首次使用需要打开编辑模式的开关:可以对下面的按钮设置名称、按下发送的数据、松开发送的数据——>开启HEX模式输入十六进制数据——>确定 设置好后——>点击关闭编辑模式的开关
main.c
int main(void) { RetargetInit(&huart1);//将printf()函数映射到UART1串口上 HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1);//开启串口1接收中断 HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //开启串口2接收中断 RS485orBT = 0;当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 LED_1(0);//LED1控制 //LED状态复位 LED_2(0);//LED2控制 while (1) { //蓝牙模块回复数据的处理程序 if(USART2_RX_STA&0x8000){//判断中断接收标志位(蓝牙模块BT,使用USART2) if((USART2_RX_STA&0x7FFF) == 3 //判断接收数量3个 && USART2_RX_BUF[0]=='+' //判断接收第1个字符是不是+ && USART2_RX_BUF[1]=='O' //判断接收第2个字符是不是O && USART2_RX_BUF[2]=='K') //判断接收第3个字符是不是K { LED_1(1);//LED1控制 //LED1亮,表示接收到+OK(成功)的回复 } else if((USART2_RX_STA&0x7FFF) == 4 //判断接收数量4个 && USART2_RX_BUF[0]=='+' //判断接收第1个字符是不是+ && USART2_RX_BUF[1]=='E' //判断接收第2个字符是不是E && USART2_RX_BUF[2]=='R' //判断接收第3个字符是不是R && USART2_RX_BUF[3]=='R') //判断接收第4个字符是不是R { LED_2(1);//LED2控制 //LED2亮,表示接收到+ERR(失败)的回复 } //按钮控制的处理程序 //收到一个指令表示收到的不是AT指令,而是按钮控制 else if((USART2_RX_STA&0x7FFF) == 1) //判断接收数量1个(手机控制程序) { switch (USART2_RX_BUF[0]){//判断接收数据的内容 case 0x41: RELAY_1(1);继电器的控制程序(0继电器放开,1继电器吸合) BT_printf("Relay ON");//返回数据内容,在手机APP上显示 break; case 0x44: RELAY_1(0);继电器的控制程序(0继电器放开,1继电器吸合) BT_printf("Relay OFF");//返回数据内容,在手机APP上显示 break; case 0x42: LED_1(1);//LED1控制 BT_printf("LED1 ON");//返回数据内容,在手机APP上显示 break; case 0x45: LED_1(0);//LED1控制 BT_printf("LED1 OFF");//返回数据内容,在手机APP上显示 break; case 0x43: BUZZER_SOLO1();//蜂鸣器输出单音的报警音 BT_printf("BEEP");//返回数据内容,在手机APP上显示 break; case 0x46: BT_printf("CPU Reset");//返回数据内容,在手机APP上显示 HAL_Delay(1000);//延时 NVIC_SystemReset();//系统软件复位函数 break; default: //冗余语句 break; } } USART2_RX_STA=0;//标志位清0,准备下次接收 } //AT指令的发送程序 if(KEY_1())//按下KEY1判断 { BUZZER_SOLO2();//提示音 LED_1(0);//LED1控制 //在蓝牙模块回复之前先将LED状态复位 LED_2(0);//LED2控制 BT_printf("AT+NAMECC2541");//向蓝牙模块发送AT指令(修改模块的广播名为CC2541) } if(KEY_2())//按下KEY2判断 { BUZZER_SOLO2();//提示音 LED_1(0);//LED1控制 //在蓝牙模块回复之前先将LED状态复位 LED_2(0);//LED2控制 BT_printf("AT+DISC");//向蓝牙模块发送AT指令(断开与手机的连接) } } }
4.蓝牙模块APP专业调试测试程序(操控界面:按钮、文本框、滑动条、摇杆等)
打开手机的蓝牙调试器界面,让开发板处于允许状态 ——>APP扫描蓝牙设备连接蓝牙 ——>APP点击专业调试按钮——>点击“+”号图标新建一个调试工程——>修改名称、屏幕方向——>确认 会出现:修改名称按钮、编辑控件按钮、通信设置按钮、分享工程按钮、删除工程按钮 通信设置按钮:发送数据包、接收数据包、通信模式(设置仅操作控件时发送 额外控件操作次数改为0) 编辑控件按钮: 添加新控件:里面有各种控件 设置控件链接:(T发送 R接收)、(选择位 字节 双字节)、(选择链接到那个数据位置) 设置控件属性(设置控件参数):按下时的数值、松开时的数值 移动 缩放 旋转控件、删除、退出
usart.c
#include "usart.h" uint8_t USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART1_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存 uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART2_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存 uint8_t RS485orBT;//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 uint8_t USART3_RX_BUF[USART3_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART3_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART3_NewData;//当前串口中断接收的1个字节数据的缓存 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数 { if(huart ==&huart1)//判断中断来源(串口1:USB转串口) { printf("%c",USART1_NewData); //把收到的数据以 a符号变量 发送回电脑 if((USART1_RX_STA&0x8000)==0){//接收未完成 if(USART1_RX_STA&0x4000){//接收到了0x0d if(USART1_NewData!=0x0a)USART1_RX_STA=0;//接收错误,重新开始 else USART1_RX_STA|=0x8000; //接收完成了 }else{ //还没收到0X0D if(USART1_NewData==0x0d)USART1_RX_STA|=0x4000; else{ USART1_RX_BUF[USART1_RX_STA&0X3FFF]=USART1_NewData; //将收到的数据放入数组 USART1_RX_STA++; //数据长度计数加1 if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收 } } } HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); //再开启接收中断 } if(huart ==&huart2)//判断中断来源(RS485/蓝牙) { if(RS485orBT){//判断当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 USART2_RX_BUF[0]=USART2_NewData;//收到数据放入缓存数组(只用到1个数据存放在数组[0]) USART2_RX_STA++;//数据接收标志位加1 HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData, 1); //再开启接收中断 }else{ if((USART2_RX_STA&0x8000)==0){//接收未完成(将USART2_RX_STA最高位1位规定是接收完成标志位) //包尾结束码 if(USART2_NewData==0x5A)//如收到0x5A表示接收到结束符(手机APP“蓝牙调试器”回复数据以0x5A为结束符) { USART2_RX_STA|=0x8000;//收到0x0A,接受完成 } else{ //如没有收到0x0A则继续接收数据内容并把数量加1 USART2_RX_BUF[USART2_RX_STA&0X7FFF]=USART2_NewData; //将收到的数据放入数组 USART2_RX_STA++; //数据长度计数加1 if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收 } } HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //再开启接收中断 } } if(huart ==&huart3)//判断中断来源(串口3:WIFI模块) { printf("%c",USART3_NewData); //把收到的数据以 a符号变量 发送回电脑 HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData,1); //再开启接收中断 } }
main.c
int main(void) { // 包头 数据~~~~~~~~~~~内容 校验码 包尾 uint8_t buf[7] = {0xA5,0x00,0x01,0x3B,0x00,0xFF,0x5A}; //创建要发送和数组 while (1) { //APP按钮控制开发板LED灯 if(USART2_RX_STA&0x8000){//判断中断接收标志位(蓝牙模块BT,使用USART2) if((USART2_RX_STA&0x7FFF) == 6 //判断接收数量6个 不包含结束码 && USART2_RX_BUF[0]==0xA5 //判断接收第1个数据是不是包头0xA5 && USART2_RX_BUF[5]== //判断接收第6个校验码是不是前4个数据之和(最低8位) (USART2_RX_BUF[1]+USART2_RX_BUF[2]+USART2_RX_BUF[3]+USART2_RX_BUF[4])%0x100) { if(USART2_RX_BUF[1]&0x01) //判断逻辑值中最低位是1则点亮LED { LED_1(1);//LED1控制 }else{ //如果是0则关LED LED_1(0);//LED1控制 } } USART2_RX_STA=0;//标志位清0,准备下次接收 } //通过开发板按键向手机APP发送数据包 从而控制APP开关 if(KEY_1())//按下KEY1判断 { BUZZER_SOLO2();//提示音 buf[1] = 0x01; //可在计算校验码之前按实现需求修改数据值 buf[5] = (buf[1]+buf[2]+buf[3]+buf[4])%0x100; //数据相加得出校验码,取最低8位 HAL_UART_Transmit(&huart2,(uint8_t *)buf,7,0xffff);//串口发送函数(串口号,内容,数量,溢出时间) } if(KEY_2())//按下KEY2判断 { BUZZER_SOLO2();//提示音 buf[1] = 0x00; //可在计算校验码之前按实现需求修改数据值 buf[5] = (buf[1]+buf[2]+buf[3]+buf[4])%0x100; //数据相加得出校验码,取最低8位 HAL_UART_Transmit(&huart2,(uint8_t *)buf,7,0xffff);//串口发送函数(串口号,内容,数量,溢出时间) } }
无单片机的APP控制蓝牙模块IO端口
手机JDY-IO控制-UUIDPP控制蓝牙模块IO端口 提取码: u8kp
二:WIFI模块
WIFI模块的连接
电脑TCP&UDP测试工具软件 提取码:1111 串口助手 提取码:iika
WIFI模块的连接 WIFI模块 和 无限路由器 AT指令连接路由器 WIFI模块 和 无限路由器 和 电脑(专用的TCP通信测试软件) 以电脑为服务器的TCP连接 WIFI模块 和 电脑 以WIFI模块为服务器的TCP连接 WIFI模块 和 手机(专用的TCP通信测试软件) WIFI模块与手机APP通信 WIFI的两种模式: AP模式:允许其他无线设备接入WIFI模块,WIFI模块作为路由器使用 STATION模式:WIFI模块需连接到AP设备上,WIFI模块作为终端设备使用 手机连接到家里的路由器(手机是STATION模式) 手机连接路由器的同时,开启WIFI热点功能(手机是AP+STATION模式) 手机断开家里的路由器,只开启WIFI热点(手机是AP模式)
第一种 AT指令连接路由器(WIFI模块 和 无限路由器)
第二种 以电脑为服务器的TCP连接(WIFI模块 和 无限路由器 和 电脑)
电 脑:定义为 服务器 WIFI模块:定义为 客户端
电脑向WIFI模块发送数据
WIFI模块向电脑发送数据
第三种 以WIFI模块为服务器的TCP连接(WIFI模块 和 电脑)
电脑向WIFI模块发送数据
WIFI模块向电脑发送数据
第四种 WIFI模块与手机APP通信(WIFI模块 和 手机)
1.WIFI模块与USART1串口透传程序
WIFI模块必须连接到路由器后才能和局域网内的手机电脑等设备进行通信 所以没有默认的透传通信模式,所有功能都要通过AT指令来完成
设置
USART1——>PA10设置为GPIO_Input PA9设置为GPIO_Output USART3——>PB10设置为GPIO_Output PB11设置为GPIO_Input
main.c
int main(void) { while (1) { //【GPIO对应关系】USART1:电脑USB转串口(RX:PA10,TX:PA9)。USART3:WIF工模块(RX:PB10,TX:PB11) //将PA10(电脑串口1的RX)的电平状态发送给PB10(WIFI模块串口3的TX) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_10,HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_10)); //将PB11(WIFI模块串口3的RX)的电平状态发送给PA9(电脑串口1的TX) HAL_GPIO_WritePin(GPIOA,GPIO_PIN_9,HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_11)); } }
2.WIFI模块的单片机控制程序
用手机进行远程控制
设置
设置PB10——>USART3_TX 设置PB11——>USART3_RX Connectivity——>USART3——>将Mode设置为异步模式Asynchronous ——>Parameter Setting Basic Parameters Baud Rate 115200 Bits/s Word Length 8 Bits (including Parity) Parity None Stop Bits 1 Advanced Parameters Data Direction Receive and Transmit Over Sampling 16 Samples ——>NVIC Setting——>勾选USART3 global interrupt允许中断
wifi文件夹
wifi.h
#ifndef WIFI_WIFI_H_ #define WIFI_WIFI_H_ #include "stm32f1xx_hal.h" //HAL库文件声明 #include "../usart/usart.h" #include "main.h" #include <string.h>//用于字符串处理的库 #include <stdarg.h> #include <stdlib.h> #include "stdio.h" extern UART_HandleTypeDef huart3;//声明UART2的HAL库结构体 void WIFI_printf (char *fmt, ...); //WIFI模块发送 void WIFI_TCP_SEND (char *fmt, ...);//在TCP模式下的发送数据(不处理返回状态的盲发) #endif /* WIFI_WIFI_H_ */
wifi.c
#include "wifi.h" //WIFI模块通信,使用UART3,这是专用的printf函数 //调用方法:WIFI_printf("123"); //向USART2发送字符123 void WIFI_printf (char *fmt, ...) { char buff[USART3_REC_LEN+1]; //用于存放转换后的数据 [长度] uint16_t i=0; va_list arg_ptr; va_start(arg_ptr, fmt); vsnprintf(buff, USART3_REC_LEN+1, fmt, arg_ptr);//数据转换 i=strlen(buff);//得出数据长度 if(strlen(buff)>USART3_REC_LEN)i=USART3_REC_LEN;//如果长度大于最大值,则长度等于最大值(多出部分忽略) HAL_UART_Transmit(&huart3,(uint8_t *)buff,i,0xffff);//串口发送函数(串口号,内容,数量,溢出时间) va_end(arg_ptr); } //WIFI模块在TCP模式下的数据发送:TCP发送的规定是先发AT+CIPSEND=数量,等待返回“>“后再发送数据内容。 //调用方法:WIFI_TCP_SEND("123\r\n"); //TCP方式发送字符123和回车换行 void WIFI_TCP_SEND (char *fmt, ...) { char buff[USART3_REC_LEN+1]; //用于存放转换后的数据 [长度] uint16_t i=0; va_list arg_ptr; va_start(arg_ptr, fmt); vsnprintf(buff, USART3_REC_LEN+1, fmt, arg_ptr);//数据转换 i=strlen(buff);//得出数据长度 if(strlen(buff)>USART3_REC_LEN)i=USART3_REC_LEN;//如果长度大于最大值,则长度等于最大值(多出部分忽略) WIFI_printf("AT+CIPSEND=%d\r\n",i);//先发送AT指令和数据数量 HAL_Delay(100);//等待WIFI模块返回">",此处没做返回是不是">"的判断。稳定性要求高的项目要另加判断。 HAL_UART_Transmit(&huart3,(uint8_t *)buff,i,0xffff);//发送数据内容(串口号,内容,数量,溢出时间) va_end(arg_ptr); } //所有USART串口的中断回调函数HAL_UART_RxCpltCallback,统一存放在【USART1.C】文件中。
usart.c
#include "usart.h" uint8_t USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART1_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存 uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART2_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存 uint8_t RS485orBT;//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 //定义了用于wifi模块接收的数组和标志位 uint8_t USART3_RX_BUF[USART3_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART3_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART3_NewData;//当前串口中断接收的1个字节数据的缓存 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数 { uint8_t a; if(huart ==&huart1)//判断中断来源(串口1:USB转串口) { printf("%c",USART1_NewData); //把收到的数据以 a符号变量 发送回电脑 if((USART1_RX_STA&0x8000)==0){//接收未完成 if(USART1_RX_STA&0x4000){//接收到了0x0d if(USART1_NewData!=0x0a)USART1_RX_STA=0;//接收错误,重新开始 else USART1_RX_STA|=0x8000; //接收完成了 }else{ //还没收到0X0D if(USART1_NewData==0x0d)USART1_RX_STA|=0x4000; else{ USART1_RX_BUF[USART1_RX_STA&0X3FFF]=USART1_NewData; //将收到的数据放入数组 USART1_RX_STA++; //数据长度计数加1 if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收 } } } HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); //再开启接收中断 } if(huart ==&huart2)//判断中断来源(RS485/蓝牙) { if(RS485orBT){//判断当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 USART2_RX_BUF[0]=USART2_NewData;//收到数据放入缓存数组(只用到1个数据存放在数组[0]) USART2_RX_STA++;//数据接收标志位加1 HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData, 1); //再开启接收中断 }else{ if((USART2_RX_STA&0x8000)==0){//接收未完成(将USART2_RX_STA最高位1位规定是接收完成标志位) if(USART2_NewData==0x5A)//如收到0x5A表示接收到结束符(手机APP“蓝牙调试器”回复数据以0x5A为结束符) { USART2_RX_STA|=0x8000;//收到0x0A,接受完成 } else{ //如没有收到0x0A则继续接收数据内容并把数量加1 USART2_RX_BUF[USART2_RX_STA&0X7FFF]=USART2_NewData; //将收到的数据放入数组 USART2_RX_STA++; //数据长度计数加1 if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收 } } HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //再开启接收中断 } } //wifi模块接收处理程序 if(huart ==&huart3)//判断中断来源(串口3:WIFI模块) { //【原始数据内容】字符:+IPD,1:A 十六进制:0D 0A 2B 49 50 44 2C 31 3A 41(其中1是数量,A是数据内容) //【数据接收原理】当接收到0x0A(即“回车”中的“\r“)时触发接下来的数据采集程序, //首先清空USART3_RX_BUF[]寄存器,然后将USART3_RX_STA的16位中最高位第2位置1(01000000 00000000) //此时开始采集接下来收到的数据,当收到前6个数据是“+IPD,1:”,且第7个数据不等于0时,表示成功收完数据 //然后将接收的第7位的一个字节数据内容放入USART3_RX_STA寄存器低8位,并将16位中最高位置1(10000000 xxxxxxxx)。 //【调用方法】在主函数中用if语句判断(USART_RX_STA&0x8000),为真时表示成功收到数据。 //然后读USART_RX_STA寄存器低14位的内容(USART_RX_STA&0x3FFF),即是数据的内容(1个字节)。 //主函数处理完数据后要将USART_RX_STA清0,才能开启下一次数据接收。 if(USART3_RX_STA&0x4000){//判断开始标志位为1时(16位中高位第2位)进入数据采集处理 USART3_RX_BUF[USART3_RX_STA&0x3FFF]=USART3_NewData;//将 USART3_RX_STA++; if(USART3_RX_BUF[0]=='+'&& //判断返回字符前几位是不是“+IPD,1:” USART3_RX_BUF[1]=='I'&& USART3_RX_BUF[2]=='P'&& USART3_RX_BUF[3]=='D'&& USART3_RX_BUF[4]==','&& USART3_RX_BUF[5]=='1'&&//限定只接收1个数量的数据(可根据实际要求的数量修改) USART3_RX_BUF[6]==':'&& USART3_RX_BUF[7]!=0){ //同时判断第1个数据内容是否为0,为0表示还没有收到数据 USART3_RX_STA = USART3_RX_BUF[7]+0x8000;//将数据内容写入寄存器,16位最高位置1表示接收完成 } } if(USART3_NewData==0x0A && !(USART3_RX_STA&0x8000)){//判断是否收到“回车”中的“\r“(0x0A) USART3_RX_STA=0x4000;//将开始采集标志位置1(16位中最高位第2位) for(a=0;a<200;a++){//循环200次 USART3_RX_BUF[a]=0;//将数据寄存器清0 } } HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData,1); //再开启串口3接收中断 } }
main.c
#include "../../icode/wifi/wifi.h" RetargetInit(&huart1);//将printf()函数映射到UART1串口上 HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1);//开启串口1接收中断 HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData,1); //再开启串口3接收中断 USART3_RX_STA=0;//标志位清0,准备下次接收 LED_1(0);//LED1控制 //LED状态复位 LED_2(0);//LED2控制 int main(void) { while (1) { //TCP接收数据的处理 //只有串口3接收到开头为“+IPD,“时才被识别为接收到数据,数据内容在USART3_RX_STA&0x3FFF。 if(USART3_RX_STA&0x8000)//判断中断接收标志位(WIFI模块使用USART3) { switch (USART3_RX_STA&0x3FFF)//判断接收数据的内容 { case 'A': RELAY_1(1);继电器的控制程序(0继电器放开,1继电器吸合) WIFI_TCP_SEND("Relay ON:OK!\r\n");//发送AT指令 TCP发送数据内容 break; case 'B': RELAY_1(0);继电器的控制程序(0继电器放开,1继电器吸合) WIFI_TCP_SEND("Relay OFF:OK!\r\n");//发送AT指令 TCP发送数据内容 break; case 'C': BUZZER_SOLO1();//蜂鸣器输出单音的报警音 WIFI_TCP_SEND("Beep:OK!\r\n");//发送AT指令 TCP发送数据内容 break; case 'D': LED_1(1);//LED1独立控制函数(0为熄灭,其他值为点亮) WIFI_TCP_SEND("LED1 ON:OK!\r\n");//发送AT指令 TCP发送数据内容 break; case 'E': LED_1(0);//LED1独立控制函数(0为熄灭,其他值为点亮) WIFI_TCP_SEND("LED1 OFF:OK!\r\n");//发送AT指令 TCP发送数据内容 break; default: //冗余语句 break; } USART3_RX_STA=0;//标志位清0,准备下次接收 } //按键处理程序 if(KEY_1())//按下KEY1判断 { BUZZER_SOLO2();//提示音 WIFI_printf("AT+CIPSTART=\"TCP\",\"192.168.1.4\",3456\r\n");//发送AT指令 连接TCP服务器(IP和端口号需按实际修改) HAL_Delay(100);//等待 } if(KEY_2())//按下KEY2判断 { BUZZER_SOLO2();//提示音 WIFI_TCP_SEND("www.doyoung.net\r\n");//发送AT指令 TCP模式下的发送数据 HAL_Delay(100);//等待 } } }
三:阿里云
1.MQTT.fx软件连接阿里云的设备
应用层通信协议的选择:HTTP和MQTT协议都可用于物联网开发,嵌入式单片机系统多采用MQTT协议 HTTP超文本传输协议:常见于浏览器网页,适用于性能高、内存大的网络设备(如手机、电脑) MQTT消息队列遥测传输协议:数据量小、网络稳定性要求低,适用于物联网设备〈如智能插座) FTP文件传输协议、SMTP邮件传送协议、DHCP动态主机配置协议等,不在物联网通信考虑之列
参考文档:阿里云支持与服务——>文档中心——>物联网——>阿里云物联网平台
1. 1 阿里云服务器添加设备
阿里云-计算,为了无法计算的价值:控制台——>产品与服务——>产品与服务列表——>物联网平台
1.2 利用MQQTT软件连接阿里云的设备
1.3 测试数据的收发
1.3.1 测试数据收发(阿里云服务器发送——>接收MQTT)
查看记录
1.3.2 测试数据上行(MQTT发送——>阿里云服务器接收)
2.WIFI模块连接阿里云的测试程序
aliyun文件夹 esp8266文件交:WIFI模块AT指令驱动程序 esp8266.h:设置AP热点、云平台IP与端口号 esp8266.c: hmac文件夹:加密算法相关程序 utils_hmac.h utils_hmac.c utils_md5.h utils_md5.c utils_sha1.h utils_sha1.c iot文件夹:物联网平台应用程序 iot.h:设置云平台三元组信息与订阅参数 iot.c mqtt文件夹:MQTT协议函数库 transport.c:MQTT数据收发底层接口函数 众多文件 tim文件夹 tim.c:TIM2定时器程序(辅助串口接收处理) usart文件夹 usart.c:串口接收中断回调函数
地址和端口号
复制三元组信息
复制订阅和发布地址
云平台的数据收发
连接后端数据收发:设备与云平台的双向通信
阿里云向核心板发
核心板在对阿里云发
esp8266.h
#ifndef ESP8266_ESP8266_H #define ESP8266_ESP8266_H #include "stm32f1xx_hal.h" #include "string.h" #include "stdlib.h" #include "../../usart/usart.h" #include "../../wifi/wifi.h" //【网络连接信息】在下方修改设置您的路由器热点和物联网平台IP地址+端口号信息(手动复制正确信息到双引号内) //修改你的路由器热点名称和密码 #define SSID "DYS2.4" //无线路由器热点名称【必须按您的实际情况修改】 #define PASS "duyang98765" //无线路由器热点密码【必须按您的实际情况修改】 //修改云平台的地址和端口号 #define IOT_DOMAIN_NAME "iot-06z00fwe7ssnjc0.mqtt.iothub.aliyuncs.com" //云服务器IP地址【必须按您的实际情况修改】 #define IOT_PORTNUM "1883" //云服务器端口号 uint8_t esp8266_send_cmd(char *cmd, char *ack, uint16_t waittime); uint8_t* esp8266_check_cmd(uint8_t *str); uint8_t esp8266_Connect_IOTServer(void); //连接物联网云服务器IP uint8_t esp8266_Connect_AP(void); //连接AP路由器 uint8_t esp8266_Connect_Server(void); //连接服务器 uint8_t esp8266_quit_trans(void); //判断指令退出 #endif
iot.h
#ifndef IOT_IOT_H #define IOT_IOT_H #include "stm32f1xx_hal.h" #include "string.h" #include "stdlib.h" #include "../../usart/usart.h" #include "../mqtt/MQTTPacket.h" #include "../mqtt/transport.h" //【三元组信息】在下方修改设置您的物联网云平台提供的三元组信息(手动复制正确信息到双引号内) //修改你实际的三元组信息 #define PRODUCTKEY "h28vhEe63jh" //产品ID(ProductKey)【必须按您的实际情况修改】 #define PRODUCTKEY_LEN strlen(PRODUCTKEY) //产品ID长度 #define DEVICENAME "ESP8266" //设备名(DeviceName)【必须按您的实际情况修改】 #define DEVICENAME_LEN strlen(DEVICENAME) //设备名长度 #define DEVICESECRE "17744974bd7851fdccfde1" //设备秘钥(DeviceSecret)【必须按您的实际情况修改】 #define DEVICESECRE_LEN strlen(DEVICESECRE) //设备秘钥长度 //修改你实际的订阅地址 #define TOPIC_SUBSCRIBE "/h28vhEe63jh/ESP8266/user/get" //订阅权限的地址【必须按您的实际情况修改】 #define TOPIC_QOS 0 //QoS服务质量数值(0/1) #define MSGID 1 //信息识别ID #define TOPIC_PUBLISH "/h28vhEe63jh/ESP8266/user/update/error" //发布权限的地址【必须按您的实际情况修改】 #define MQTTVERSION 4 //MQTT协议版本号(3表示V3.1,4表示V3.1.1) #define KEEPALIVEINTERVAL 120 //保活计时器,服务器收到客户端消息(含心跳包)的最大间隔(单位是秒) extern uint16_t buflen; //临时缓存数量 extern unsigned char buf[200]; //临时缓存数组 uint8_t IOT_connect(void); //IOT物联网平台连接 void IOT_ping(void); //IOT物联网平台PING(心跳包) uint8_t IOT_subscribe(void);//subscribe主题订阅(订阅成功后才能接收订阅消息) uint8_t IOT_publish(char* payload);//publish主题发布(参数是发布信息内容,用双引号包含) #endif
usart.c
#include "usart.h" uint8_t USART1_RX_BUF[USART1_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART1_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART1_NewData;//当前串口中断接收的1个字节数据的缓存 uint8_t USART2_RX_BUF[USART2_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART2_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART2_NewData;//当前串口中断接收的1个字节数据的缓存 uint8_t RS485orBT;//当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 uint8_t USART3_RX_BUF[USART3_REC_LEN];//接收缓冲,最大USART_REC_LEN个字节. uint16_t USART3_RX_STA=0;//接收状态标记//bit15:接收完成标志,bit14:接收到0x0d,bit13~0:接收到的有效字节数目 uint8_t USART3_NewData;//当前串口中断接收的1个字节数据的缓存 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)//串口中断回调函数 { if(huart ==&huart1)//判断中断来源(串口1:USB转串口) { printf("%c",USART1_NewData); //把收到的数据以 a符号变量 发送回电脑 if((USART1_RX_STA&0x8000)==0){//接收未完成 if(USART1_RX_STA&0x4000){//接收到了0x0d if(USART1_NewData!=0x0a)USART1_RX_STA=0;//接收错误,重新开始 else USART1_RX_STA|=0x8000; //接收完成了 }else{ //还没收到0X0D if(USART1_NewData==0x0d)USART1_RX_STA|=0x4000; else{ USART1_RX_BUF[USART1_RX_STA&0X3FFF]=USART1_NewData; //将收到的数据放入数组 USART1_RX_STA++; //数据长度计数加1 if(USART1_RX_STA>(USART1_REC_LEN-1))USART1_RX_STA=0;//接收数据错误,重新开始接收 } } } HAL_UART_Receive_IT(&huart1,(uint8_t *)&USART1_NewData,1); //再开启接收中断 } if(huart ==&huart2)//判断中断来源(RS485/蓝牙) { if(RS485orBT){//判断当RS485orBT标志位为1时是RS485模式,为0时是蓝牙模式 USART2_RX_BUF[0]=USART2_NewData;//收到数据放入缓存数组(只用到1个数据存放在数组[0]) USART2_RX_STA++;//数据接收标志位加1 HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData, 1); //再开启接收中断 }else{ if((USART2_RX_STA&0x8000)==0){//接收未完成(将USART2_RX_STA最高位1位规定是接收完成标志位) if(USART2_NewData==0x5A)//如收到0x5A表示接收到结束符(手机APP“蓝牙调试器”回复数据以0x5A为结束符) { USART2_RX_STA|=0x8000;//收到0x0A,接受完成 } else{ //如没有收到0x0A则继续接收数据内容并把数量加1 USART2_RX_BUF[USART2_RX_STA&0X7FFF]=USART2_NewData; //将收到的数据放入数组 USART2_RX_STA++; //数据长度计数加1 if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0;//接收数据错误,重新开始接收 } } HAL_UART_Receive_IT(&huart2,(uint8_t *)&USART2_NewData,1); //再开启接收中断 } } if(huart ==&huart3)//判断中断来源(串口3:WIFI模块)//接收完的一批数据,还没有被处理,则不再接收其他数据 { if(USART3_RX_STA<USART3_REC_LEN)//还可以接收数据 { __HAL_TIM_SET_COUNTER(&htim2,0); //计数器清空 if(USART3_RX_STA==0) //使能定时器2的中断 { __HAL_TIM_ENABLE(&htim2); //使能定时器2 } USART3_RX_BUF[USART3_RX_STA++] = USART3_NewData;//最新接收数据放入数组 }else { USART3_RX_STA|=0x8000;//强制标记接收完成 } HAL_UART_Receive_IT(&huart3,(uint8_t *)&USART3_NewData,1); //再开启串口3接收中断 } }
设置
Timers——>Clock Source选择为Internal Clock内部时钟 ——>参数设置选项卡Parameter Setting Counter Settings Prescaler (PSC- 16 bits value) 7199 Counter Mode up Counter Period (AutoReload Re. 999 lnternal Clock Division (CKD) No Division auto-reload preload Disable ——>NVIC Setting——>勾选TIM2 global interrupt允许中断
tim.c
#include "tim.h" void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //定时器中断回调函数 { if(htim ==&htim2)//判断是否是定时器2中断(定时器到时表示一组字符串接收结束) { USART3_RX_BUF[USART3_RX_STA&0X7FFF]=0;//添加结束符 USART3_RX_STA|=0x8000;//接收标志位最高位置1表示接收完成 __HAL_TIM_CLEAR_FLAG(&htim2,TIM_EVENTSOURCE_UPDATE );//清除TIM2更新中断标志 __HAL_TIM_DISABLE(&htim2);//关闭定时器2 } }
transport.c
#include "stm32f1xx_hal.h" #include "../../tim/tim.h" #include "../../usart/usart.h" #include "../esp8266/esp8266.h" #include "stdio.h" #include "stdlib.h" #include "string.h" #include "transport.h" #if !defined(SOCKET_ERROR) /** error in socket operation */ #define SOCKET_ERROR -1 #endif int transport_sendPacketBuffer(int sock, unsigned char* buf, int buflen) { USART3_RX_STA = 0; memset(USART3_RX_BUF,0,USART3_REC_LEN); HAL_UART_Transmit(&huart3, buf, buflen,1000);//调用串口3发送HAL库函数 return buflen; } int transport_getdata(unsigned char* buf, int count) { memcpy(buf, (const char*)USART3_RX_BUF, count); USART3_RX_STA = 0; //接收标志位清0 memset(USART3_RX_BUF,0,USART3_REC_LEN);//缓存清0 return count; } int transport_getdatanb(void *sck, unsigned char* buf, int count) { return 0; } int transport_open(char* addr, int port) { return 0; } int transport_close(int sock) { return 0; }
esp8266.c
#include "esp8266.h" uint8_t esp8266_send_cmd(char *cmd, char *ack, uint16_t waittime) //ESP8266发送指令(底层函数) { uint8_t res = 0; USART3_RX_STA = 0; memset(USART3_RX_BUF,0,USART3_REC_LEN); //将串口3的缓存空间清0 WIFI_printf("%s\r\n", cmd); //调用WIFI模块专用的发送函数 if(waittime) //需要等待应答 { while(--waittime) //等待倒计时 { HAL_Delay(10);//HAL库延时函数 if(USART3_RX_STA&0x8000) //接收到期待的应答结果 { if(esp8266_check_cmd((uint8_t *)ack)) { printf("回复信息:%s\r\n",(uint8_t *)ack);//反馈应答信息 break; //得到有效数据 } USART3_RX_STA=0; //串口3标志位清0 } } if(waittime==0)res=1; } return res; } uint8_t esp8266_Connect_IOTServer(void) //ESP8266连接到物联网平台服务器 { //状态检测 printf("准备配置模块\r\n"); HAL_Delay(100); esp8266_send_cmd("AT","OK",50); printf("准备退出透传模式\n"); if(esp8266_quit_trans()) { printf("退出透传模式失败,准备重启\r\n"); return 6; }else printf("退出透传模式成功\r\n"); printf("准备关闭回显\r\n"); if(esp8266_send_cmd("ATE0","OK",50)) { printf("关闭回显失败准备重启\r\n"); return 1; }else printf("关闭回显成功\r\n"); printf("查询模块是否在线\r\n"); if(esp8266_send_cmd("AT","OK",50)) { printf("模块不在线准备重启\r\n"); return 1; }else printf("设置查询在线成功\r\n"); ...... } uint8_t esp8266_Connect_AP() //ESP8266连接AP设备(无线路由器) { uint8_t i=10; char *p = (char*)malloc(50);//分配存储空间的指针 sprintf((char*)p,"AT+CWJAP=\"%s\",\"%s\"",SSID,PASS);//发送连接AT指令 while(esp8266_send_cmd(p,"WIFI GOT IP",1000) && i)//循环判断等待连接AP的结果 { printf("链接AP失败,尝试重新连接\r\n"); //连接失败的反馈信息 i--; } free(p);//释放分配的空间和指针 if(i) return 0;//执行成功返回0 else return 1;//执行失败返回1 } uint8_t esp8266_Connect_Server() //ESP8266连接到服务器 { uint8_t i=10; char *p = (char*)malloc(50);//分配存储空间的指针 sprintf((char*)p,"AT+CIPSTART=\"TCP\",\"%s\",\%s",IOT_DOMAIN_NAME,IOT_PORTNUM); while(esp8266_send_cmd(p,"CONNECT",1000) && i) { printf("链接服务器失败,尝试重新连接\r\n"); i--; } free(p);//释放分配的空间和指针 if(i)return 0;//执行成功返回0 else return 1;//执行失败返回1 }
iot.c
#include "iot.h" #include "../hmac/utils_hmac.h" uint16_t buflen=200; unsigned char buf[200]; char ClientID[128]; uint8_t ClientID_len; char Username[128]; uint8_t Username_len; char Password[128]; uint8_t Password_len; uint8_t IOT_connect() { uint16_t a; uint32_t len; char temp[128]; printf("开始连接云端服务器\r\n"); MQTTPacket_connectData data = MQTTPacket_connectData_initializer;//配置部分可变头部的值 buflen = sizeof(buf); memset(buf,0,buflen); memset(ClientID,0,128);//客户端ID的缓冲区全部清零 sprintf(ClientID,"%s|securemode=3,signmethod=hmacsha1|",DEVICENAME);//构建客户端ID,并存入缓冲区 memset(Username,0,128);//用户名的缓冲区全部清零 sprintf(Username,"%s&%s",DEVICENAME,PRODUCTKEY);//构建用户名,并存入缓冲区 Username_len = strlen(Username); memset(temp,0,128);//临时缓冲区全部清零 sprintf(temp,"clientId%sdeviceName%sproductKey%s",DEVICENAME,DEVICENAME,PRODUCTKEY);//构建加密时的明文 utils_hmac_sha1(temp,strlen(temp),Password,DEVICESECRE,DEVICESECRE_LEN);//以DeviceSecret为秘钥对temp中的明文,进行hmacsha1加密,结果就是密码,并保存到缓冲区中 Password_len = strlen(Password);//计算用户名的长度 printf("ClientId:%s\r\n",ClientID); printf("Username:%s\r\n",Username); printf("Password:%s\r\n",Password); //【重要参数设置】可修改版本号、保活时间 data.MQTTVersion = MQTTVERSION; //MQTT协议版本号 data.clientID.cstring = ClientID; //客户端标识,用于区分每个客户端xxx为自定义,后面为固定格式 data.keepAliveInterval = KEEPALIVEINTERVAL; //保活计时器,定义了服务器收到客户端消息的最大时间间隔,单位是秒 data.cleansession = 1; //该标志置1服务器必须丢弃之前保持的客户端的信息,将该连接视为“不存在” data.username.cstring = Username; //用户名 DeviceName&ProductKey data.password.cstring = Password; //密码,工具生成 len = MQTTSerialize_connect(buf, buflen, &data);//构造连接的报文 transport_sendPacketBuffer(0,buf, len);//发送连接请求 unsigned char sessionPresent, connack_rc; a=0; while(MQTTPacket_read(buf, buflen, transport_getdata) != CONNACK || a>1000)//等待胳回复 { HAL_Delay(10);//必要的延时等待 a++;//超时计数加1 } if(a>1000)NVIC_SystemReset();//当计数超时,则复位单片机 while(MQTTDeserialize_connack(&sessionPresent, &connack_rc, buf, buflen) != 1 || connack_rc != 0); if(connack_rc != 0) { printf("连接回复:%uc\r\n",connack_rc); } printf("连接成功!\r\n"); return 0;//执行成功返回0 } void IOT_ping(void)//发送心跳包PING(保持与云服务器的连接) { uint32_t len; len = MQTTSerialize_pingreq(buf, buflen); //计算数据长度 transport_sendPacketBuffer(0, buf, len); //发送数据 HAL_Delay(200);//必要的延时等待 printf("发送心跳包Ping... "); } uint8_t IOT_subscribe(void)//subscribe主题订阅(订阅成功后才能接收订阅消息) { uint32_t len; int req_qos = TOPIC_QOS; MQTTString topicString = MQTTString_initializer;//定义Topic结构体并初始化 topicString.cstring = TOPIC_SUBSCRIBE; len = MQTTSerialize_subscribe(buf, buflen, 0, MSGID, 1, &topicString, &req_qos);//订阅发送数据编码 transport_sendPacketBuffer(0, buf, len); HAL_Delay(100);//必要的延时等待 if(MQTTPacket_read(buf, buflen, transport_getdata) == SUBACK) //等待订阅回复 { unsigned short submsgid; int subcount; int granted_qos; MQTTDeserialize_suback(&submsgid, 1, &subcount, &granted_qos, buf, buflen);//回复的订阅确认数据解码 if(granted_qos != 0) //qos不为0表示订阅成功 { printf("订阅成功 GrantedQoS=%d\r\n", granted_qos); return 0; //订阅成功 } } printf("订阅失败\r\n"); return 1; //订阅失败 } uint8_t IOT_publish(char* payload)//publish主题发布(参数是发布信息内容,用双引号包含) { uint32_t len; MQTTString topicString = MQTTString_initializer;//定义Topic结构体并初始化 topicString.cstring = TOPIC_PUBLISH; int payloadlen = strlen(payload);//用函数计算发布信息内容的长度 printf("发布信息:%.*s\r\n", payloadlen, payload); //将要发送的信息payload通过MQTTSerialize_publish编码后用transport_sendPacketBuffer发送给云服务器 len = MQTTSerialize_publish(buf, buflen, 0, 0, 0, 0, topicString, (unsigned char*)payload, payloadlen);//发布数据编码 transport_sendPacketBuffer(0, buf, len); //发送编码好的最终数据 HAL_Delay(100);//必要的延时等待 return 1; }
main.c
#include "../../icode/aliyun/esp8266/esp8266.h" #include "../../icode/aliyun/mqtt/MQTTPacket.h" #include "../../icode/aliyun/mqtt/transport.h" #include "../../icode/aliyun/iot/iot.h" #include "../../icode/tim/tim.h" int main(void) { uint16_t a=0,b=0; int t,qos,payloadinlen; //为下面即将解析的消息定义所需变量 unsigned char dup,retained; unsigned short msgid; unsigned char* payloadin; MQTTString receiveTopic; while(esp8266_Connect_IOTServer());//AT指令连接TCP连接云服务器(IP和端口参数在esp8266.h文件内修改设置) while(IOT_connect());//用MQTT协议+三元组信息连接阿里云物联网平台(三元组参数在iot.h文件内修改设置) printf("订阅云服务器\r\n"); HAL_Delay(100);//等待 IOT_subscribe();//主题订阅(订阅成功后才能接收订阅消息) a=0xFFF0; //强制发送心跳包的计数溢出,立即重发心跳包 LED_1(0);//LED状态初始化 关 LED_2(0); while (1) { //循环发送心跳包,以保持设备在线 HAL_Delay(10);//主循环的间隔延时(防止刷新过快) a++;//计算加1 if(a>1000){ //每1000*10毫秒延时发送一次Ping心跳包(保保持与云服务器的连接) a=0;//计算标志清0 IOT_ping();//发送Ping心跳包 if(MQTTPacket_read(buf, buflen, transport_getdata)==PINGRESP){//判断心跳包是不回复确认 printf("心跳成功\r\n"); //回复0xD0,0x00时表示心跳成功的回复 }else { printf("心跳失败\r\n");//无回复表示失败 BUZZER_SOLO1();//蜂鸣器输出单音(提示心跳失败) a=0xFFF0; //强制发送心跳包的计数溢出,立即重发心跳包 b++;//重启计数加1 if(b>20) //如果快速发送心跳包20次后无回复,则复位WIFI模块重新连接 { while(esp8266_Connect_IOTServer());//AT指令连接TCP连接云服务器(IP和端口参数在esp8266.h文件内修改设置) while(IOT_connect());//用MQTT协议+三元组信息连接阿里云物联网平台(三元组参数在iot.h文件内修改设置) a=0;b=0;//计算标志清0 } } USART3_RX_STA = 0;//串口3接收标志位清0 } //接收云端的订阅消息 if(USART3_RX_STA&0x8000) //判断云服务器发布的消息 { switch (USART3_RX_BUF[0]/16){//判断接收到的报文类型 case PUBLISH: BUZZER_SOLO1();//蜂鸣器输出单音 t = MQTTDeserialize_publish( //对接收的MQTT原始数据进行解码(返回1表示成功,其他值表示错误) &dup, //【得出】重发标志位(0首发,1早前报文的重发) &qos, //【得出】服务质量等级(0最多分发一次,1至少分发一次,2只分发一次) &retained, //【得出】保留位参数 &msgid, //【得出】消息ID &receiveTopic, //【得出】订阅主题名 &payloadin, //【得出】消息内容 &payloadinlen, //【得出】消息长度 USART3_RX_BUF, USART3_RX_STA&0x7FFF); //【输入】原始数据缓存(数组+数量) if(t){//如果数据正确 printf("接收到主题:%.*s ", receiveTopic.lenstring.len, receiveTopic.lenstring.data);//显示接收主题 printf("消息内容:%.*s ", payloadinlen, payloadin);//显示消息内容的字符串 printf("QoS:%d\r\n", qos);//显示接收QoS USART3_RX_STA = 0;//标志位清0 //数据控制开发板的程序 if(strstr((const char*)payloadin,(const char*)"LED1 ON"))//比对信息内容是不是LED1 ON { LED_1(1); IOT_publish("LED1 ON OK!");//publish主题发布(发送到云平台) }else if(strstr((const char*)payloadin,(const char*)"LED1 OFF"))//同上 { LED_1(0); IOT_publish("LED1 OFF OK!");//publish主题发布(发送到云平台) }else if(strstr((const char*)payloadin,(const char*)"LED2 ON"))//同上 { LED_2(1); IOT_publish("LED2 ON OK!");//publish主题发布(发送到云平台) }else if(strstr((const char*)payloadin,(const char*)"LED2 OFF"))//同上 { LED_2(0); IOT_publish("LED2 OFF OK!");//publish主题发布(发送到云平台) }else if(strstr((const char*)payloadin,(const char*)"RELAY1 ON"))//同上 { RELAY_1(1); IOT_publish("RELAY1 ON OK!");//publish主题发布(发送到云平台) }else if(strstr((const char*)payloadin,(const char*)"RELAY1 OFF"))//同上 { RELAY_1(0); IOT_publish("RELAY1 OFF OK!");//publish主题发布(发送到云平台) } }else{ printf("接收订阅消息时出错\r\n");//接收错误时的显示 } break; case CONNACK: //连接报文确认 //插入您的处理程序(也可空置) break; case SUBACK: //订阅请求报文确认 //插入您的处理程序(也可空置) break; case UNSUBACK: //取消订阅报文确认 //插入您的处理程序(也可空置) break; default: //冗余语句 break; } USART3_RX_STA = 0;//串口3接收标志位清0 } //按键操作 if(KEY_1())//按下KEY1判断【连接云服务器并订阅主题】 { BUZZER_SOLO2();//提示音 // while(esp8266_Connect_AP());//连接AP无线路由器热点(热点参数在esp8266.h。WIFI模块已保存热点时可屏蔽) while(esp8266_Connect_IOTServer());//连接TCP连接云服务器(IP和端口参数在esp8266.h文件内修改设置) while(IOT_connect());//用MQTT协议+三元组信息连接阿里云物联网平台(三元组参数在iot.h文件内修改设置) printf("订阅云服务器\r\n"); IOT_subscribe();//主题订阅(订阅成功后才能接收订阅消息) HAL_Delay(100);//等待 } if(KEY_2())//按下KEY2判断【向服务器发布信息】 { BUZZER_SOLO2();//提示音 IOT_publish("TEST www.doyoung.net");//publish主题发布(参数是发布信息内容,用双引号包含) HAL_Delay(100);//等待 } } }