【创世SD NAND】基于STM32与OneNet平台的智能家居系统设计
实验硬件:STM32F103ZET6;0.96寸OLED(128×64);ESP8266,DHT11;CS创世 SD NAND;LED;KEY
硬件实物图:
效果图:
引脚连接:
OLED模块引脚:
VCC -- 3.3V
GND -- GND
SCL -- PB10
SDA -- PB11
展开全文
ESP8266模块引脚:
VCC -- 3.3V
GND -- GND
RX-- PB10
TX -- PB11
RST -- PB9
EN -- PB7
DHT11传感器引脚:
VCC -- 3.3V
GND -- GND
DATA--PE0
一、物联网
1.1 物联网简介
物联网(Internet of Things,简称IoT)是指通过各种信息传感器、射频识别技术、全球定位系统、红外感应器、激光扫描器等各种装置与技术,实时摘集任何需要监控、 连接、互动的物体或过程,摘集其声、光、热、电、力学、化学、生物、位置等各种需要的信息,通过各类可能的网络接进,实现物与物、物与人的泛在连接,实现对物品和过程的智能化感知、识别和治理。物联网是一个基于互联网、传统电信网等的信息承载体,它让所有能够被独立觅址的普通物理对象形成互联互通的网络 。
总而言之,物联网就是利用现代互联网技术实现端对端的数据互联与掌握。
1.2 物联网开发
目前,物联网开发的形式是多种多样的。总的来说,一般都需借助特定的网络服务平台为基础实现数据的上传与下发(假如只考虑内网是可以不需要的,比如ESP32CAM)。
实力雄厚或者有一定背景的公司通常考虑可能自建网络协议服务器,专属服务自家的物联网产品开发。当然,也有不少企业会抉择借助他人网络服务平台往实现自家的物联网开发。
这里比较闻名的网络服务平台有:中国移动旗下的OneNet、阿里巴巴旗下阿里云以及机智云平台。
平台分析:
机智云:机智云作为物联网开发服务平台的元老,一直致力于完美和搭建快速高效的服务机制,其有一套自己快速开发适配的物联网实现流程。但是,在笔者使用的过程中也存在着一些弊端。比如:
(1)其需要给ESP8266等WIFI模块刷上自家的固件才可使用;
(2)状态极其不稳定,很轻易断联或者死活连不上;
(3)受限于开发模式,对于产品自我开发有一定限制;
OneNet和阿里云平台:这2大平台背靠强大的资源和技术支持,其服务稳定。设定的开发框架也更多样化,可以提供开发者更多的发扬空间。
OneNet服务平台:
阿里云物联网平台:
二、OneNet平台使用
从多元化和产品稳定性方面考虑,作者将以中国移动旗下的OneNet服务平台为案例进行教学讲解。(其实本来企图以机智云出一篇案例的,结果后来发现之前能正常联动的MCU和APP动不动就宕机。后来,干脆直接就以OneNet这个框架更开放的平台为案例教学)
2.1 OneNet预备
1、注册OneNet平台账号(网址:OneNET - 中国移动物联网开放平台 (10086.cn));
2、 登进后抉择掌握台,进进后点击全部产品服务,抉择多协议接进;(我们使用MQTT,既可以上传数据也可以下发数据掌握,而且都是免费的)
3、抉择MQTT(旧版)之后添加产品,按照自己实际需求填写产品内容;
4、点击所创建的产品,添加几个设备(免费版用户上限10个设备)
5、注重设备ID,鉴权信息以及接进方式这3个属性;
6、关于数据流模块可以设置,可以不设置,反正最后通讯正常的情状下会收到需要的数据流;
2.2 OneNet调试
在设置好OneNet平台设备后,其实可以借助该平台自带的API调试工具进行调试检测(前提:下位机已经成果接通了)。
这里的调试使用API函数的介绍和使用可以参考文档中心(一个合格的嵌进式工程师是一定需要学会自己往查看技术支持文档,而且OneNet提供的文档内容还是非常详尽的)。
OneNet技术文档网址:OneNET - 中国移动物联网开放平台 (10086.cn)
网络协议通讯要害函数
服务器或上位机查询读取设备历史数据:
API函数:
请求方式:GET
URL:
服务器或上位机下发主题报文(掌握下位机):
API函数:
请求方式:POST
URL:
以上2个网络通讯的API函数至关重要,就是实现常规情状下OneNet物联网开发的要害性技术支持。(情状答应的条件下,意见读者朋友们往好好研读一下技术文档,将会为之后的开发大大助力)
三、下位机外设驱动
3.1 ESP8266模块
作者摘用的ESP8266模块为ESP8266NodeMCU,是需要进行烧进AT固件,才能实现目的网络通讯。作为常见的物联网开发模块,ESP8266的出现大大降低了物联网开发的难度系数,也普及了物联网的发展。
AT指令最早在蓝牙模块上接触过,所谓AT指令实质上就是一些起掌握造用的特殊字符串。模块可以通过AT指令掌握搭配使用源代码API函数开发,总体开发速度快,难度较低。
不同厂商芯片的AT固件可能有所不同,但是指令基本一致(作者使用的是乐鑫的)。
阐明:由于篇幅有限,这里就不和大家单独详尽介绍AT指令。指令的详尽参数及使用阐明请参考官方文档:ESP8266 AT指令集。
3.2 OLED模块
本项目中0.96寸OLED模块的使用仅为展示DHT11传感器摘集到的温湿度信息,以此来对比是否和服务器端以及上位机APP端的数据一致性。对其使用有不是太了解的读者朋友可以参考,作者另一篇基础教学博客:(2条消息) 【强烈推举】基于stm32的OLED各种展示实现(含动态图)_混分巨兽龙某某的博客-CSDN博客_oled展示图片程序
本项目的代码都是基于作者以前基础教学上的项目代码搭建而成,保证读者朋友可以实现快速复现。
3.3 DHT11模块
本项目中DHT11为下位机MCU摘集四周环境温度和湿度的传感器,当然,条件答应的情状下还可以附加很多环境传感器(比如:烟雾传感器,环境光传感器,二氧化碳传感器等等)。当然得益于OneNet平台的布局,本项目教学的底层逻辑支持读者朋友的自我DIY,实现自主化的物联网产品设计。
DHT11模块驱动参考博客:基于stm32的太空人温湿度时钟项目——DHT11(HAL库)_混分巨兽龙某某的博客-CSDN博客
3.4 KEY和LED
KEY和LED都是源于作者正点原子精英版开发板上自备的(假如和作者同款开发板移植开发将会特殊简单快速),属于最基本的GPIO操作信赖各位应该都是把握的
特殊注重:
(1)这里的KEY按键从设计逻辑上就可以看出应该是需要摘用外部中断的;
(2)KEY按下之后会改变LED的亮灭状态,为了同步上位机此时的LED状态,所以需要触发串口通讯中断(考虑嵌套中断情状时候中断优先级的安顿)。
四、CubeMX配置
1、RCC配置外部高速晶振(精度更高)——HSE;
2、SYS配置:Debug设置成Serial Wire(否则可能导致芯片自锁);
3、TIM2配置:由上面可知DHT11的使用需要us级的延迟函数,HAL库自带只有ms的,所以需要自己设计一个定时器;
4、I2C2配置:作为OLED的通讯方式;
5、UART1和UART3配置:MCU分别与电脑和ESP8266通讯(记得开启串口通信中断);
6、设置KEY0按键PE4为外部中断(依据自己的开发板来确定)
7、GPIO配置:PE0设置为DHT11的DATA端,PE5为LED,并且设置ESP8266的EN和RST(PB7和PB9);
8、时钟树配置
五、代码与解析
5.1 OLED与DHT11模块代码
受篇幅限制OLED与DHT11部分的代码,这里就不展示了。假如有不懂这部分原理与代码的读者朋友可以参考本人的另一篇博客。博客地址:基于stm32的太空人温湿度时钟项目——DHT11(HAL库)_混分巨兽龙某某的博客-CSDN博客
5.2 ESP8266模块代码
ESP8266部分的代码主要是借助串口通讯AT指令与ESP8266模块(刷进AT固件的)与OneNet平台进行信息交互(包含ESP8266初始化、数据发送,指令发送和数据缓存清除等)。
esp8266.h代码:
#ifndef _ESP8266_H_
#define _ESP8266_H_
#include "main.h"
#include "usart.h"
#includestring.h
#includestdio.h
#includestdbool.h
#define ESP8266_WIFI_INFO "AT+CWJAP=\"NJUST\",\"768541ly\"\r\n" //连接上自己的wifi热点:WiFi名和密码
#define ESP8266_ONENET_INFO "AT+CIPSTART=\"TCP\",\"183.230.40.39\",6002\r\n" //连接上OneNet的MQTT
#define OK 0 //接收完成标志
#define OUTTIME 1 //接收未完成标志
void ESP8266_Clear(void); //清空缓存
void ESP8266_Init(void); //esp8266初始化
_Bool ESP8266_SendCmd(char *cmd, char *res);//发送数据
unsigned char *ESP8266_GetIPD(unsigned short timeOut);
void ESP8266_SendData(unsigned char *data, unsigned short len);
#endif
esp8266.c代码:
#include "esp8266.h"
unsigned char ESP8266_Buf[128]; //定义一个数组作为esp8266的数据缓冲区
unsigned short esp8266_cnt = 0, esp8266_cntPre = 0; //定义两个计数值:此次和上一次
unsigned char a_esp8266_buf;
* @brief esp8266初始化
* @param 无
* @retval 无
void ESP8266_Init(void)
ESP8266_Clear();
printf("1. 测试AT启动\r\n"); //AT:测试AT启动
while(ESP8266_SendCmd("AT\r\n", "OK"))
HAL_Delay(500);
printf("2. 设置WiFi模式(CWMODE)\r\n"); //查询/设置 Wi-Fi 模式:设置WiFi模式为Station模式
while(ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK"))
HAL_Delay(500);
printf("3. AT+CWDHCP\r\n"); //启用/禁用 DHCP
while(ESP8266_SendCmd("AT+CWDHCP=1,1\r\n", "OK"))
HAL_Delay(500);
printf("4. 连接WiFi热点(CWJAP)\r\n");
while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP"))
HAL_Delay(500);
printf("5. 建立TCP连接(CIPSTART)\r\n");
while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT"))
HAL_Delay(500);
printf("6. ESP8266 Init OK\r\n");
* @brief 清空缓存
* @param 无
* @retval 无
void ESP8266_Clear(void)
memset(ESP8266_Buf, 0, sizeof(ESP8266_Buf)); //将数组中的元素全部初始化为0,
* @brief 等待接收完成
* @param 无
* @retval OK:表达接收完成;OUTTIME:表达接收超时完成
* 进行循环调用,检测接收是否完成
_Bool ESP8266_WaitRecive(void)
if(esp8266_cnt == 0) //假如当前接收计数为0 则阐明没有处于接收数据中,所以直接跳出,结束函数
return OUTTIME;
if(esp8266_cnt == esp8266_cntPre) //假如上一次的值和这次相同,则阐明接收完毕
esp8266_cnt = 0; //清0接收计数
return OK; //返回接收完成标志
else //假如不相同,则将此次赋值给上一次,并返回接收未完成标志
esp8266_cntPre = esp8266_cnt;
return OUTTIME;
* @brief 发送命令
* @param cmd:表达命令;res:需要检查的返回指令
* @retval 0:表达成功;1:表达失败
_Bool ESP8266_SendCmd(char *cmd, char *res)
unsigned char timeOut = 200;
HAL_UART_Transmit(huart3, (unsigned char *)cmd, strlen((const char *)cmd),0xffff);
while(timeOut--)
if(ESP8266_WaitRecive() == OK) //假如收到数据
printf("%s",ESP8266_Buf);
if(strstr((const char *)ESP8266_Buf, res) != NULL) //假如检索到要害词,清空缓存
ESP8266_Clear();
return 0;
HAL_Delay(10);
return 1;
* @brief 数据发送
* @param data:待发送的数据;len:待发送的数据长度
* @retval 无
void ESP8266_SendData(unsigned char *data, unsigned short len)
char cmdBuf[32];
ESP8266_Clear(); //清空接收缓存
sprintf(cmdBuf, "AT+CIPSEND=%d\r\n", len); //发送命令,sprintf()函数用于将格式化的数据写进字符串
if(!ESP8266_SendCmd(cmdBuf, "")) //收到‘’时可以发送数据
HAL_UART_Transmit(huart3, data, len,0xffff); //发送设备连接请求数据
* @brief 获取平台返回的数据
* @param 等待的时间
* @retval 平台返回的数据,不同网络设备返回的格式不同,需要进行调试,如:ESP8266的返回格式为:"+IPD,x:yyy",x表达数据长度,yyy表达数据内容
unsigned char *ESP8266_GetIPD(unsigned short timeOut)
char *ptrIPD = NULL;
do
if(ESP8266_WaitRecive() == OK) //假如接收完成
ptrIPD = strstr((char *)ESP8266_Buf, "IPD,"); //搜索“IPD”头
if(ptrIPD == NULL) //假如没找到,可能是IPD头的延迟,还是需要等待一会,但不会超过设定的时间
//UsartPrintf(USART_DEBUG, "\"IPD\" not found\r\n");
else
ptrIPD = strchr(ptrIPD, ':'); //找到':'
if(ptrIPD != NULL)
ptrIPD++;
return (unsigned char *)(ptrIPD);
else
return NULL;
HAL_Delay(5); //延时等待
} while(timeOut--);
return NULL; //超时还未找到,返回空指针
* @brief 串口2收发中断回调函数
* @param
* @retval
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
if(esp8266_cnt = 255) //溢出推断,超过一个字节
esp8266_cnt = 0;
memset(ESP8266_Buf,0x00,sizeof(ESP8266_Buf));
HAL_UART_Transmit(huart3, (uint8_t *)"数据溢出", 10,0xFFFF);
else
ESP8266_Buf[esp8266_cnt++] = a_esp8266_buf; //接收数据转存
HAL_UART_Receive_IT(huart3, (uint8_t *)a_esp8266_buf, 1); //再开启接收中断
代码总结:
ESP8266模块的代码基于HAL库实现,主要是利用AT指令往使下位机(STM32+ESP8266)连接上WIFI,并且与OneNet平台进行MQTT协议通信(TCP连接IP地址和对应端口)。
特殊注重:
使用ESP8266进行通讯时,当数据量较大的时候一定要编写缓存清除代码(否则,很有可能出现死机等情状)。当然,这个时候可以搭配CS创世 SD NAND(又喊贴片式 TF卡/贴片式SD卡) 往存储传输的数据流。同时,利用这些保存在SD卡中的数据,可以在下位机制造精美的数据历史信息UI,极大的拓展了产品价值。