一、项目简介
基于 STM32F103C8T6 单片机和 ESP8266 Wi-Fi 模块,旨在实现通过 Wi-Fi 连接阿里云物联网平台,进行数据上传和远程控制
- STM32F103C8T6:作为核心控制单元,负责系统的运算、数据处理和与外设的交互。STM32F103C8T6 具有强大的计算能力、丰富的外设接口和稳定的性能,适合嵌入式系统的开发。
- ESP8266 Wi-Fi 模块:负责与阿里云物联网平台进行网络通信。ESP8266 是一个低功耗的 Wi-Fi 模块,通过 UART 与 STM32F103C8T6 进行连接,并通过 Wi-Fi 将数据传输到阿里云。
- 外部传感器/执行器(可选):根据应用场景,用户可以选择连接温湿度传感器、光照传感器等,并通过 STM32F103C8T6 获取数据或控制执行器进行远程操作。
- 串口通信(UART):STM32F103C8T6 与 ESP8266 通过 UART 接口进行数据传输。STM32 发送指令给 ESP8266,ESP8266 负责通过 Wi-Fi 网络与阿里云进行通信。
- MQTT 协议:ESP8266 使用 MQTT 协议与阿里云物联网平台进行数据交互。通过 MQTT 协议,STM32 可以将传感器数据上传到阿里云,或接收来自阿里云的指令进行远程控制。
二、阿里云设置
1.通过百度搜索阿里云IOT
![](https://i-blog.csdnimg.cn/direct/239e91c88e4a476abaf71de6a837502f.png)
2.打开后进行注册登录,然后进入控制台界面
![](https://i-blog.csdnimg.cn/direct/5fd5a8f731af4c789b476b5fbec9b8e0.png)
3.新建公共实例
新建一个公共实例,地区选择离自己近的地方即可其他默认,我这边选择的是华东2(上海)
![](https://i-blog.csdnimg.cn/direct/2bb9aec27c484cc2944f93e2175b12ac.png)
4.然后选择新建一个产品
![](https://i-blog.csdnimg.cn/direct/afadac33622c4036a0bb4bacb8db9365.png)
![](https://i-blog.csdnimg.cn/direct/157e46524743449c9d8f1a013e0bd7a5.png)
5.创建产品完成后再去添加一个设备
![](https://i-blog.csdnimg.cn/direct/738677e5631b4c238c6e384e6ee2a303.png)
![](https://i-blog.csdnimg.cn/direct/67a0140b3f074ea1b4a48e4ab33155ee.png)
![](https://i-blog.csdnimg.cn/direct/58f3c14a570b465c82b3bd257813a450.png)
![](https://i-blog.csdnimg.cn/direct/7628a2f45e244bfaa0a4fca291fef74b.png)
6.点击查看设备
然后查看三要素,把他们复制到文本文件中,后续需要使用。
![](https://i-blog.csdnimg.cn/direct/801adfc938c74c428d5aa9850c86b21f.png)
7.点击产品进行查看
创建两个(根据自己的需求)自定义物模型
![](https://i-blog.csdnimg.cn/direct/48a8b4ec3827403ca619b090276fbc1d.png)
![](https://i-blog.csdnimg.cn/direct/6cc77296c3e84242847a8eca06d1bc8d.png)
![](https://i-blog.csdnimg.cn/direct/135f843629484015b531b882eb75b3f9.png)
![](https://i-blog.csdnimg.cn/direct/eec660599c10480ab8a73efae0d4adac.png)
![](https://i-blog.csdnimg.cn/direct/7fa62bc030a94fd0b993beb8108958eb.png)
![](https://i-blog.csdnimg.cn/direct/22c7a758d401452e8a27d533d8c8a4bf.png)
![](https://i-blog.csdnimg.cn/direct/36ebd7fea6924e938821c83a5888498d.png)
创建完成后记录下标识符后面需要用到,然后点击发布上线即可。
三、固件烧录
1.硬件连接
我这边使用的是ESP8266的nodemcu,自带CH340所以连接电源即可,如果只是最小的模块可以通过CH340连接到电脑上方式如下:
|-------|---------|
| CH340 | ESP8266 |
| 3.3V | VCC |
| RX | TX |
| TX | RX |
| GND | GND |
2.软件获取
烧录固件需要到烧录软件和一个固件,把链接放到下面自取即可。
烧录工具:通过网盘分享的文件:flash_download_tool_3.9.2_0.zip
链接: https://pan.baidu.com/s/1J93R1XedIUha_uL-9dx2pA 提取码: va4c
烧录固件:通过网盘分享的文件:1471_esp8266-at_mqtt-1m.zip
链接: https://pan.baidu.com/s/1GfZX8YfslurIw1rKer0JTw 提取码: y5yt
3.软件设置
1.解压完成后直接点击exe文件运行即可
![](https://i-blog.csdnimg.cn/direct/5ffd3dde123041288745ea42462527da.png)
2.选择ESP8266和develop
![](https://i-blog.csdnimg.cn/direct/71c71d1635bc48efb257b766b26d61f2.png)
3.选择固件
选择刚才下载的固件,地址填0x0,具体设置如图所示,选择自己的串口,波特率选择最大即可
![](https://i-blog.csdnimg.cn/direct/0c0111803a004ab4af0625667d05e569.png)
4.开始烧录
我用的nodemcu不需要按任何按键都可以下载,但有其他型号的板子下载起来可能比较复杂,如正点原子的开发板需要IO1接地才可以下载,以及一些板子需要一直按着BOOT0才可以下载。
正点原子的板子下载时IO1和GND以及VCC要连接在一个电源上不可以分开连接,否则会下载失败。
![](https://i-blog.csdnimg.cn/direct/9c127b245d9e46cb8bb987ecdf716bc7.png)
5.烧录完成
烧录完成后点击reset复位就可以进行 AT指令测试了
![](https://i-blog.csdnimg.cn/direct/c300a51547fd4890b0638622733c4e09.png)
四、AT指令测试
AT指令集 这里列举了常用的AT指令集,具体指令的意思请自行查询。
打开XCOM或者其他的串口软件,发送AT指令
![](https://i-blog.csdnimg.cn/direct/0a289d9ab3b745b58950740cfc276003.png)
1.发送AT
复位后发送AT返回OK说明通讯没有问题
![](https://i-blog.csdnimg.cn/direct/1316f58e537244b2aa544fb976df845e.png)
2.设置波特率
发送AT+UART=115200,8,1,0,0设置波特率
然后发送**AT+UART?**查看波特率是否设置成功
![](https://i-blog.csdnimg.cn/direct/fefe31a78eee4bfa8bd93a2cde39f845.png)
3.发送AT+RESTORE重新启动模块
![](https://i-blog.csdnimg.cn/direct/237c33669ef64e30a8b49df93018d03a.png)
4.发送AT+CWMODE=1配置WIFI模式
![](https://i-blog.csdnimg.cn/direct/5b090521adf44f69962f2a57f6e169e8.png)
5.发送**AT+CWJAP="SSID","SSID_password"**连接wifi
输入自己WIFI的账号密码即可,用手机热点是注意使用2.4GHz的频段,现在的手机都是5G,但是该模块不支持5G,可能会导致连接失败。
例:AT+CWJAP="UFI-EB37","1234567890"
![](https://i-blog.csdnimg.cn/direct/2f7bbf3f588540b4b68e1c801f49d2f5.png)
6.发送AT+MQTTUSERCFG=0,1,"NULL","name","password",0,0,""
这里的name就是之前复制下来的username,password就是passwd,复制粘贴过来即可
例:AT+MQTTUSERCFG=0,1,"NULL","Test&io5xsizC2D2","0620ed20f70f7bdf05637aa1b01292c2945760119bb7dd34da0aeacef84c7529",0,0,""
![](https://i-blog.csdnimg.cn/direct/ec75631f44404ca497f3955e4718270e.png)
![](https://i-blog.csdnimg.cn/direct/eaac109dbb8048ed9fdd0ea2211d9f5f.png)
7.发送AT+MQTTCLIENTID=0,"ClienID"
设置MQTT ID,要在ID信息每一个","前加"\"进行转义 把之前复制的clientld拿过来使用
例:
原来的ClienID为:io5xsizC2D2.Test|securemode=2,signmethod=hmacsha256,timestamp=1736230153434|需要发送的AT指令为:AT+MQTTCLIENTID=0,"io5xsizC2D2.Test|securemode=2\,signmethod=hmacsha256\,timestamp=1736216781469|"
列出两个ID请注意查看区别,方便修改自己的ID
![](https://i-blog.csdnimg.cn/direct/703ecea178a6412abc67bbf8d2a01b63.png)
![](https://i-blog.csdnimg.cn/direct/2e09406b8cd14f2287bf9e0d5e7cfcc2.png)
8.发送AT+MQTTCONN=0,"域名",1883,1
发送MQTT域名,域名获取 ,将之前的mqttHostUrl复制过来使用。
例:AT+MQTTCONN=0,"iot-06z00gjlxx4sqaa.mqtt.iothub.aliyuncs.com",1883,1
![](https://i-blog.csdnimg.cn/direct/9d991518bcd74e82bca7f7e7f2d564f5.png)
![](https://i-blog.csdnimg.cn/direct/8b5dc6b66f6e4527bc0839de9ea86b48.png)
此时可以看到设备显示在线状态
![](https://i-blog.csdnimg.cn/direct/99d295460d6f4e9b82c9ca6b9bd92db5.png)
9.发送AT+MQTTSUB=0,"订阅topic",1
订阅主题,根据如图所示步骤找到订阅的topic
![](https://i-blog.csdnimg.cn/direct/21437783c5c64bbdb9189ef7c556d902.png)
/io5xsizC2D2/${deviceName}/user/get,此时我们看到还需要一个devicename
然后打开我们的设备界面就可以找到我们所需要的devicename了
例:AT+MQTTSUB=0,"/io5xsizC2D2/Test/user/get",1
![](https://i-blog.csdnimg.cn/direct/890f3ae089c6417c9ece9cd1a6519efd.png)
发送完成后点击设备,查看设备中的topic列表一栏,就可以看到刚才订阅的topic出现在设备上了。
![](https://i-blog.csdnimg.cn/direct/332ce1eefa654e3692ad22d21d86b6f7.png)
10.命令下发
点击设备的topic列表 ,点击发送消息,输入123456789,然后查看我们的xcom就可以看到已经成功接收到了。
![](https://i-blog.csdnimg.cn/direct/973b928376de4bc7a6caa793fd282e03.png)
![](https://i-blog.csdnimg.cn/direct/f5e24c433f2e4a36a1eeb6cafcc27549.png)
![](https://i-blog.csdnimg.cn/direct/0b49574a9c8444dfa8253ad3d1c0cfde.png)
11.数据上传
发送AT+MQTTPUB=0,"发布topic","AJson格式内容",1,0 发布主题
json数据格式,{\"params\":{\"temperature\":37.5}},其中temperature为属性的标识符
![](https://i-blog.csdnimg.cn/direct/9dae785fee3a458785901c78868210ab.png)
![](https://i-blog.csdnimg.cn/direct/98f20b4ec5df4ba0bd51c40338b2f47a.png)
![](https://i-blog.csdnimg.cn/direct/554f7c7d590f49c687b020999005ecbf.png)
例:
AT+MQTTPUB=0,"/sys/io5xsizC2D2/Test/thing/event/property/post","{\"params\":{\"temperature\":37.5}}",1,0
发送完成后我们可以看到阿里云成功显示温度37.5℃
12.日志查看
选择日志服务我们可以看到所发的消息,以及可以看到发送失败时报错的代码,然后根据代码去阿里云手册中找到该错误的含义,便于排查错误。
![](https://i-blog.csdnimg.cn/direct/ebdfc5e4c5a34396a53cffb9b558a976.png)
五、串口测试
1.硬件连接
|---------------|---------|
| STM32F103C8T6 | ESP8266 |
| 5V | VCC |
| GND | GND |
| PA9 | RX |
| PA10 | TX |
使用这种小板子时记得外接电源,否则会因为电源不足而供不上电。
2. 串口文件
usart1.c
cpp
#include "sys.h"
#include "usart.h"
#if 1
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
void _sys_exit(int x)
{
x = x;
}
//重定义fputc函数
int fputc(int ch, FILE *f)
{
while((USART1->SR&0X40)==0);//循环发送,直到发送完毕
USART1->DR = (u8) ch;
return ch;
}
#endif
#if EN_USART1_RX //如果使能了接收
//串口1中断服务程序
//注意,读取USARTx->SR能避免莫名其妙的错误
u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.
//接收状态
//bit15, 接收完成标志
//bit14, 接收到0x0d
//bit13~0, 接收到的有效字节数目
u16 USART_RX_STA=0; //接收状态标记
void uart_init(u32 bound){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1|RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
//USART1_TX GPIOA.9
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9; //PA.9
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.9
//USART1_RX GPIOA.10初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;//PA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIOA.10
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = bound;//串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStructure.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART1, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启串口接受中断
USART_Cmd(USART1, ENABLE); //使能串口1
}
void USART1_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART1); //读取接收到的数据
if((USART_RX_STA&0x8000)==0)//接收未完成
{
if(USART_RX_STA&0x4000)//接收到了0x0d
{
if(Res!=0x0a)USART_RX_STA=0;//接收错误,重新开始
else USART_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART_RX_STA|=0x4000;
else
{
USART_RX_BUF[USART_RX_STA&0X3FFF]=Res ;
USART_RX_STA++;
if(USART_RX_STA>(USART_REC_LEN-1))USART_RX_STA=0;//接收数据错误,重新开始接收
}
}
}
}
}
#endif
usart1.h
cpp
#ifndef __USART_H
#define __USART_H
#include "stdio.h"
#include "sys.h"
#define USART_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART1_RX 1 //使能(1)/禁止(0)串口1接收
extern u8 USART_RX_BUF[USART_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART_RX_STA; //接收状态标记
//如果想串口中断接收,请不要注释以下宏定义
void uart_init(u32 bound);
#endif
3.ESP8266文件
esp8266.c
里面的wifi名称密码,以及MQTT三要素要根据自己的阿里云进行修改。
cpp
#include "esp8266.h"
#include "usart.h"
#include "stm32f10x_usart.h"
#include "delay.h"
#include <stdio.h>
#include <string.h>
#include "esp8266.h"
void ESP8266_Init(void)
{
// u16 t;
// u16 len;
// u16 times=0;
delay_init(); //延时函数初始化
//NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
//uart_init(115200); //串口初始化为11520
//重启ESP
printf("AT+RESTORE\r\n");
delay_ms(1000);
delay_ms(1000);
//wifi模式设置
printf("AT+CWMODE=1\r\n");
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
//wifi名称,WiFi密码
printf("AT+CWJAP=\"UFI-EB37\",\"1234567890\"\r\n");
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
//MQTT名称,MQTT密码
printf("AT+MQTTUSERCFG=0,1,\"NULL\",\"ESP8266&io5xsNZLaxr\",\"ec2ac185df0828bd3d5f68fc840eed580ef660c88320010d71c049eeda719047\",0,0,\"\"\r\n");
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
//MQTTID
printf("AT+MQTTCLIENTID=0,\"io5xsNZLaxr.ESP8266|securemode=2\\,signmethod=hmacsha256\\,timestamp=1735989723311|\"\r\n");
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
//MQTT域名
printf("AT+MQTTCONN=0,\"iot-06z00gjlxx4sqaa.mqtt.iothub.aliyuncs.com\",1883,1\r\n");
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
//订阅消息
printf("AT+MQTTSUB=0,\"/sys/io5xsNZLaxr/ESP8266/thing/deviceinfo/update\",1\r\n");
delay_ms(1000);
delay_ms(1000);
delay_ms(1000);
memset(USART_RX_BUF,0,500);
USART_RX_STA=0;
}
esp8266.h
cpp
#ifndef _ESP8266_H
#define _ESP8266_H
#include "stm32f10x.h"
#include "delay.h"
#include "usart.h"
void ESP8266_Init(void);
#endif
4.主函数
我这边要做一个智能秤所以上传了一个称重传感器的数据,上述连接到阿里云后上传数据只需要这句话即可:printf("AT+MQTTPUB=0,\"/sys/io5xsNZLaxr/ESP8266/thing/event/property/post\",\"{\\\"params\\\":{\\\"Weight\\\":%d}}\",1,0\r\n",weight);
main.c
cpp
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
delay_init();
HX711_GPIO_Init();
uart_init(115200);
ESP8266_Init(); //ESP初始化
Get_Tare();
OLED_Init();
OLED_Clear();
OLED_ShowCH(2,0,"失重-智能迷你秤");
OLED_ShowCH(20,4,"重量: 0g");
while(1)
{
Get_Weight();
if(medleng == 0) //缓存的第1个元素,直接放入,不需要排序
{
buffer[0] = weight; medleng = 1;
}
else //插入排序算法,按从小到大的顺序排列
{
for(int i = 0; i < medleng; i ++)
{
if( buffer[i] > weight) // 轮询到的当前元素>AD值,则交换它们的值,xd为中间变量存放位置
{
xd = weight; weight = buffer[i]; buffer[i] = xd;
}
}
buffer[medleng] = weight; //把轮询出较大的数放入缓存的后面.
medleng++;
}
if(medleng >= MEDIAN_LEN) //ADC采样的数据个数达到中值滤波要求的数据个数
{
weight = buffer[MEDIAN]; //最终重量取中值滤波数组的中间值
medleng = 0;
OLED_ShowNum(60,4,weight,4,0);
printf("weight:%d g\r\n",weight);
printf("AT+MQTTPUB=0,\"/sys/io5xsNZLaxr/ESP8266/thing/event/property/post\",\"{\\\"params\\\":{\\\"Weight\\\":%d}}\",1,0\r\n",weight);
delay_ms(500);
}
}
}
打开阿里云我们可以看到数据上传成功,都是0的原因是我还没有称重传感器~~
![](https://i-blog.csdnimg.cn/direct/4ddf430bb1e7452493414907cb66044c.png)