文章目录[隐藏]
前言
本次实验使用的例程来自github,仅做了少量改动
附上github源码
点这里
一、所用器件
(1)STM32F103C8T6
(2)转串口模块(CH340)
(3)esp8266-01s
(4)气体检测模块_MQ
二、代码分析
(1)接线
STM32 | ESP8266 |
---|---|
PA2 | RX |
PA3 | TX |
PA4 | 复位 |
3V3 | VCC |
GND | GND |
STM32 | DHT11 |
---|---|
PA6 | DATA |
STM32 | MQ气体传感器 |
---|---|
PA7 | AO |
STM32 | 转串口模块 |
---|---|
PA9 | RX |
PA10 | TX |
(2)代码
<stm32f10x_it.c>:中断处理函数
<usart1.c>:与上位机通信
<usart2.c>:与ESP8266通信,串口2接收中断处理上位机发送来的数据
<timer2.c>:定时器2中断用来发送温湿度数据,10S
<timer3.c>:定时器3中断用来发送心跳包(ping,用于保持和服务器连接,长时间没给服务器发送数据会被踢下线),2s和30s两种模式
<timer4.c>:将串口2接收到的服务器数据依次存放在MQTT接收缓存数组中,50m
(这里仅写出重要的或换一台设备连接时需要改动的地方)
<mqtt.h>
(产品ID、设备ID、鉴权信息)这三个信息需要改动。后面会讲从哪里看这些信息
在创建onenet步骤中的第(6)步、第(10)步。
#define PRODUCTID "461722" //产品ID
#define PRODUCTID_LEN strlen(PRODUCTID) //产品ID长度
#define DEVICEID "795884401" //设备ID
#define DEVICEID_LEN strlen(DEVICEID) //设备ID长度
#define AUTHENTICATION "1234" //鉴权信息
#define AUTHENTICATION_LEN strlen(AUTHENTICATION) //鉴权信息长度
#define S_TOPIC_NAME "topic_one" //需要订阅的主题
#define P_TOPIC_NAME "topic_two"
#define Data_TOPIC_NAME "$dp"
<wifi.h>
(路由器SSID :你的电脑当前连接的wifi ID名 )
(路由密码:你的电脑当前连接的wifi 密码)
#define SSID "xxxx" //路由器SSID名称
#define PASS "xxxxx" //路由器密码
<main.h>
char *cmdLED_On = "LEDON"; //LED打开
char *cmdLED_Off = "LEDOFF"; //LED关闭
char *cmdDHT11_On = "DHTON"; //温湿度数据传输打开
char *cmdDHT11_Off = "DHTOFF"; //温湿度数据传输关闭
char *ledFlag = "LEDOFF"; //LED状态
int dhtFlag = 0; //温湿度数据传输状态
int main(void)
{
Delay_Init(); //延时功能初始化
Usart1_Init(9600); //串口1功能初始化,波特率9600
Usart2_Init(115200); //串口2功能初始化,波特率115200
TIM4_Init(500,7200); //TIM4初始化,定时时间 500*7200*1000/72000000 = 50ms
LED_Init(); //LED初始化
DHT11_Init(); //初始化DHT11
Adc_Init();
WiFi_ResetIO_Init(); //初始化WiFi的复位IO
IoT_Parameter_Init(); //初始化云IoT平台MQTT服务器的参数
while(1)
{
// connectFlag=1同服务器建立了连接
if(connectFlag == 1)
{
/*-------------------------------------------------------------*/
/* 处理发送缓冲区数据 */
/*-------------------------------------------------------------*/
if(MQTT_TxDataOutPtr != MQTT_TxDataInPtr) //if成立的话,说明发送缓冲区有数据了
{
//3种情况可进入if
//第1种:0x10 连接报文
//第2种:0x82 订阅报文,且connectPackFlag置位,表示连接报文成功
//第3种:subcribePackFlag置位,说明连接和订阅均成功,其他报文可发
if((MQTT_TxDataOutPtr[2] == 0x10)||((MQTT_TxDataOutPtr[2] == 0x82)&&(connectPackFlag == 1))||(subcribePackFlag == 1))
{
if (MQTT_TxDataOutPtr[2] == 0x30)
{
u1_printf("发送数据:0x%x,单片机数据推送至服务器\r\n", MQTT_TxDataOutPtr[2]);
u1_printf("\r\n");
}
else
{
u1_printf("发送数据:0x%x\r\n", MQTT_TxDataOutPtr[2]);//串口提示信息
}
MQTT_TxData(MQTT_TxDataOutPtr); //发送数据
MQTT_TxDataOutPtr += TBUFF_UNIT; //指针下移
if(MQTT_TxDataOutPtr == MQTT_TxDataEndPtr) //如果指针到缓冲区尾部了
{
MQTT_TxDataOutPtr = MQTT_TxDataBuf[0]; //指针归位到缓冲区开头
}
}
}
/*-------------------------------------------------------------*/
/* 处理接收缓冲区数据 */
/*-------------------------------------------------------------*/
if(MQTT_RxDataOutPtr != MQTT_RxDataInPtr) //if成立的话,说明接收缓冲区有数据了
{
u1_printf("接收到数据:");
/*-----------------------------------------------------*/
/* 处理CONNACK报文 */
/*-----------------------------------------------------*/
//if判断,如果第一个字节是0x20,表示收到的是CONNACK报文
//接着我们要判断第4个字节,看看CONNECT报文是否成功
if(MQTT_RxDataOutPtr[2] == 0x20)
{
switch(MQTT_RxDataOutPtr[5])
{
case 0x00 : u1_printf("CONNECT报文成功\r\n"); //串口输出信息
connectPackFlag = 1; //CONNECT报文成功,订阅报文可发
break; //跳出分支case 0x00
case 0x01 : u1_printf("连接已拒绝,不支持的协议版本,准备重启\r\n"); //串口输出信息
connectFlag = 0; //connectFlag置零,重启连接
break; //跳出分支case 0x01
case 0x02 : u1_printf("连接已拒绝,不合格的客户端标识符,准备重启\r\n");//串口输出信息
connectFlag = 0; //connectFlag置零,重启连接
break; //跳出分支case 0x02
case 0x03 : u1_printf("连接已拒绝,服务端不可用,准备重启\r\n"); //串口输出信息
connectFlag = 0; //connectFlag置零,重启连接
break; //跳出分支case 0x03
case 0x04 : u1_printf("连接已拒绝,无效的用户名或密码,准备重启\r\n"); //串口输出信息
connectFlag = 0; //connectFlag置零,重启连接
break; //跳出分支case 0x04
case 0x05 : u1_printf("连接已拒绝,未授权,准备重启\r\n"); //串口输出信息
connectFlag = 0; //connectFlag置零,重启连接
break; //跳出分支case 0x05
default : u1_printf("连接已拒绝,未知状态,准备重启\r\n"); //串口输出信息
connectFlag = 0; //connectFlag置零,重启连接
break; //跳出分支case default
}
}
//if判断,第一个字节是0x90,表示收到的是SUBACK报文
//接着我们要判断订阅回复,看看是不是成功
else if(MQTT_RxDataOutPtr[2] == 0x90)
{
switch(MQTT_RxDataOutPtr[6])
{
case 0x00 :
case 0x01 :
u1_printf("订阅成功\r\n"); //串口输出信息
subcribePackFlag = 1; //subcribePackFlag置1,表示订阅报文成功,其他报文可发送
pingFlag = 0; //pingFlag清零
TIM3_ENABLE_30S(); //启动30s的PING定时器
Send_Data();
TIM2_ENABLE_10S();
break; //跳出分支
default:
u1_printf("订阅失败,准备重启\r\n");//串口输出信息
connectFlag = 0; //connectFlag置零,重启连接
break; //跳出分支
}
}
//if判断,第一个字节是0xD0,表示收到的是PINGRESP报文
else if(MQTT_RxDataOutPtr[2] == 0xD0)
{
u1_printf("PING报文回复\r\n"); //串口输出信息
if(pingFlag == 1)
{ //如果pingFlag=1,表示第一次发送
pingFlag = 0; //要清除pingFlag标志
}
else if(pingFlag > 1)
{ //如果pingFlag>1,表示是多次发送了,而且是2s间隔的快速发送
pingFlag = 0; //要清除pingFlag标志
TIM3_ENABLE_30S(); //PING定时器重回30s的时间
}
}
//if判断,如果第一个字节是0x30,表示收到的是服务器发来的推送数据
//我们要提取控制命令
else if((MQTT_RxDataOutPtr[2] == 0x30))
{
u1_printf("服务器等级0推送\r\n"); //串口输出信息
MQTT_DealPushdata_Qs0(MQTT_RxDataOutPtr); //处理等级0推送数据
}
MQTT_RxDataOutPtr += RBUFF_UNIT; //指针下移
if(MQTT_RxDataOutPtr == MQTT_RxDataEndPtr) //如果指针到缓冲区尾部了
{
MQTT_RxDataOutPtr = MQTT_RxDataBuf[0]; //指针归位到缓冲区开头
}
}//处理接收缓冲区数据的else if分支结尾
/*---------------------------------------------------------------------------------------------------------------------*/
/* 处理命令缓冲区数据 */
/*---------------------------------------------------------------------------------------------------------------------*/
if(MQTT_CMDOutPtr != MQTT_CMDInPtr) //if成立的话,说明命令缓冲区有数据了
{
u1_printf("命令:%s\r\n",&MQTT_CMDOutPtr[2]); //串口输出信息
if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED_On,strlen(cmdLED_On)))//判断指令,如果是CMD1
{
ledFlag = "LEDON";
LED_On(); //LED开启
}
else if(!memcmp(&MQTT_CMDOutPtr[2],cmdLED_Off,strlen(cmdLED_Off))) //判断指令,如果是CMD11
{
ledFlag = "LEDOFF";
LED_Off(); //LED关闭
}
else if(!memcmp(&MQTT_CMDOutPtr[2],cmdDHT11_On,strlen(cmdDHT11_On)))//判断指令,如果是CMD3
{
dhtFlag = 1; //dataFlag置1,表示处于采集状态了
TIM2_ENABLE_10S(); //定时器2,,10s间隔采集温湿度
}
else if(!memcmp(&MQTT_CMDOutPtr[2],cmdDHT11_Off,strlen(cmdDHT11_Off)))//判断指令,如果是CMD3
{
dhtFlag = 0; //dataFlag置0,表示处于停止状态了
TIM_Cmd(TIM2,DISABLE); //判断2路开关状态和采集状态,并发布给服务器
}
//不做处理,后面会发送状态
else u1_printf("未知指令\r\n"); //串口输出信息
MQTT_CMDOutPtr += CBUFF_UNIT; //指针下移
if(MQTT_CMDOutPtr == MQTT_CMDEndPtr) //如果指针到缓冲区尾部了
MQTT_CMDOutPtr = MQTT_CMDBuf[0]; //指针归位到缓冲区开头
Send_Data();//发送控制数据
//处理命令缓冲区数据的else if分支结尾
} //connectFlag=1的if分支的结尾
}
/*--------------------------------------------------------------------*/
/* connectFlag=0同服务器断开了连接,我们要重启连接服务器 */
/*--------------------------------------------------------------------*/
else
{
u1_printf("需要连接服务器\r\n"); //串口输出信息
TIM_Cmd(TIM4, DISABLE); //关闭TIM4
TIM_Cmd(TIM3, DISABLE); //关闭TIM3
WiFi_RxCounter = 0; //WiFi接收数据量变量清零
memset(WiFi_RX_BUF, 0, WiFi_RXBUFF_SIZE); //清空WiFi接收缓冲区
if(WiFi_Connect_IoTServer() == 0) //如果WiFi连接云服务器函数返回0,表示正确,进入if
{
u1_printf("建立TCP连接成功\r\n"); //串口输出信息
connectFlag = 1; //connectFlag置1,表示连接成功
WiFi_RxCounter = 0; //WiFi接收数据量变量清零
memset(WiFi_RX_BUF, 0, WiFi_RXBUFF_SIZE); //清空WiFi接收缓冲区
MQTT_Buff_Init(); //初始化发送缓冲区
}
}
}
}
<stm32f10x_it.c>
所发送给mqtt的报文是在中断里面发送的
/*-------------------------------------------------*/
/*函数名:定时器2中断服务函数 */
/*参 数:无 */
/*返回值:无 */
/*-------------------------------------------------*/
extern u16 ADC_Val;
void TIM2_IRQHandler(void)
{
char humidity; //定义一个变量,保存湿度值
char temperature; //定义一个变量,保存温度值
char head1[3];
char temp[50]; //定义一个临时缓冲区1,不包括报头
char tempAll[100]; //定义一个临时缓冲区2,包括所有数据
int dataLen = 0; //报文长度
ADC_Val_Disp(10,20); //气敏检测
if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
DHT11_Read_Data(&temperature,&humidity);//读取温湿度值
memset(temp, 0, 50); //清空缓冲区1
memset(tempAll, 0, 100); //清空缓冲区2
memset(head1, 0, 3); //清空MQTT头
sprintf(temp,"{\"MQ\":\"%d\",\"TM\":\"%d\",\"HM\":\"%d\"}",
(ADC_Val/100), temperature, humidity);//构建报文
head1[0] = 0x03; //固定报头
head1[1] = 0x00; //固定报头
head1[2] = strlen(temp); //剩余长度
sprintf(tempAll, "%c%c%c%s", head1[0], head1[1], head1[2], temp);
u1_printf("\r\n"); //串口显示相关数据
u1_printf("%s\r\n", tempAll + 3);
dataLen = strlen(temp) + 3;
MQTT_PublishQs0(Data_TOPIC_NAME,tempAll, dataLen);//添加数据,发布给服务器
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
三、OneNet创建一个设备
(1)百度搜索onenet
(2)进入官网
(3)右上角:“登录”,之后点击“控制台”,进入之后把鼠标放到 “全部产品服务” 选项上 选择多协议接入
(4)进入之后点击添加产品
(5)填写信息
联网方式选择 wifi 其他的随便填就好。
(6)创建好产品之后会出现这个界面
代码中所需的 产品id 就有了
(由于我没有实名,所以只能创建一个产品,这里我有设备是因为是我之前创建的,正常第一次的话是没有的。)
(7)点击设备列表 ->添加设备
(8)设备信息随便填就行
(9)创建好之后的界面
(10)点击 “详情”
这里我们代码中所需要的信息:
设备id
鉴权信息 就有了
(11)点击数据流模板 添加数据流
这里你添加的数据流名称就是你在后面添加应用的时候需要选中的数据流;
并且你的数据流名称要与程序中发送的报文内容一致。
(12)点击应用管理
(13)添加应用
(信息随便填)
(14)进入自己的应用
开始编辑应用
(15)添加器件 比如仪表盘
可以看到左边有很多可选择的器件,点击放下的器件,右边会有许多信息要选择,其中就有数据流。其他的器件等等自己摸索吧。
至此所有的配置就配置完了,当连接到onenet,进行数据连接之后再应用这里就可以看到数据的变化。
四、现象
接上串口,打开串口助手(9600),上电之后,可以看到串口助手中会显示连接的调试信息,onenet移动端也可以显示连接情况,连接之后,检测到的数据可以上传到onenet中。
五、链接
链接:https://pan.baidu.com/s/1JB29JXNqvEnOf6rqMJLn8w
提取码:A0G4
版权声明:本文为CSDN博主「肌肉猛男ing」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_51013944/article/details/121090085
暂无评论