【嵌入式项目-1】智能家居管理系统

作者简介和专栏内容见专栏介绍:https://www.nowcoder.com/creation/manager/columnDetail/0eL5bM

麻烦看到贴子的伙伴点点赞大家点赞订阅支持下,提前祝各位offer多多,有问题评论区见~~

来源:牛客网项目描述

通过STM32单片机、ESP8266无线模块和阿里云物联网平台,基于MQTT协议实现上位机对电器的远程控制和实时监测功能。主要工作包括:

  1. 实现单片机通过串口连接ESP8266,使用AT指令自动配置wifi模块并和云服务器建立TCP连接;
  2. 基于MQTT协议实现Connect报文、Subscribe报文、Publish报文的封装、发送和响应,实现设备开关的远程控制;
  3. 通过定时器获取各类传感器(DHT11)实时数据,并上传至云服务器实现实时监测。

背景:这个项目是在参与了学校课程的一项大作业,大概用时一个月的时间,通过STM32单片机、ESP8266无线模块和阿里云物联网平台,基于MQTT协议实现上位机对电器的远程控制和实时监测功能。

思路:整个的重点就在实现单片机和开发板之间的通信和数据交互的处理。这里的数据交互根据MQTT协议实现Connect报文、Subscribe报文、Publish报文的封装、发送和响应,实现设备开关的远程控制;另一方面通过定时器获取各类传感器(DHT11)实时数据,并上传至云服务器实现实时监测。

逻辑图

项目难点与解决方案

  • 环形缓冲区:一开始用数组保存发送的数据和接收的数据,但测试时发现会出现数据覆盖的问题。缓冲区这里使用二维数组和指针来构造和控制大小为7,每一条命令放置在一个格子中,前两位用16进制表示命令长度方便分析和计算。主函数中通过两个指针指向是否相同判断是否存在待发送或待接收的数据,执行后续发送和响应的动作。
  • 多组数据接收:通过USART2中断接收wifi模块返回的信息,云服务器通过wifi返回的是多组数据,需要分隔开依次接收防止覆盖。为了避免阻塞和丢失使用多个二维数组接收缓冲区,利用时间间隔分隔不同命令,故在串口中断中引入计时器time4。若间隔小于40ms说明是同一组数据则不断重置时间,超过40ms说明一组数据发送完毕,进入定时器中断事件,将接收到的数据放入缓冲区,并清空标志位和重置计时。
  • 保活机制:使用中发现一段时间不使用后远程控制失效,发现建立的TCP连接100s后无数据就会断开,为此引入心跳机制(TCP保活机制),使用定时器+Ping报文保活:30s发送一次报文,如果响应成功就维持并修改flag=1,不成功flag++,定时器中检测到flag==1时说明上次未响应,改变发送间隔再重新发送,收到就置0,还没收到继续++,3的话就置connectflag = 0主函数就会重启
  • connectflag:在主函数中根据连接是否成功执行不同操作;在检查返回报文时返回失败可至标志位为0重启连接;在串口2中断中判断接收的数据来自哪里选择放到不同缓冲区

模拟常见问题

为什么缓冲区人工设置前两位放大小,哪里用到了?

项目详述

项目开始前需要准备什么?

  1. 安装Keil5 MDK 并注册(盗版)
  2. 安装器件支持包并在keil中选择:arm芯片型号太多
  3. 安装STLink/jlink驱动:keil5自带:arm/stlink/usbdriver
  4. 安装USB转串口驱动:CH340驱动
  5. stm32F10x固件库放置项目目录:封装了库函数
  6. stm32启动文件.s:程序从启动文件开始
  7. system文件:配置时钟的
  8. 内核寄存器描述文件core.cm3
  9. 库函数:内核/外设库函数放lib
  10. conf, it:库函数包含关系,中断 放user
  11. 建立不同文件夹放不同类文件
  12. 在include path中添加各个文件夹路径
  13. 工程选项define添加宏定义 USE_STDPERIPH_DRIVER

如何进行调试?

写OELD语句进行调试

用串口工具使用printf来调试

  • fputc:重定向c库函数printf 到串口
  • fgetc:重定向c库函数scanf 到串口
  • 禁止使用半主机模式

主函数

初始化:delay + 两个串口引脚 + LED + TIM4 + DHT11 + 云服务器 + NVIC中断

wifi连接前:清空wifi缓冲区,关闭定时器开始连接。

wifi连接后:至连接标志位,清空wifi缓冲区,构建connect和subscribe报文

发送阶段:检查缓冲区固定报头,有相关数据就通过串口发送(mqtt_TxData == u2_TxData),并移动mqtt_TxOutPtr。

接收阶段:检查接收缓冲区中的固定报头对各个报文确认信号作出回应

命令响应:对服务器发来的publish报文进行处理,根据指令完成对应单片机操作。

标志位

  • Connect_flag:初始至0,连接后至1,中间发送需要重启时至0.
  • ConnectPack_flag:Connect确认报文标志位,初始置为0,连接成功置为1。
  • Ping_flag:使用中发现一段时间不使用后远程控制失效,发现建立的TCP连接100s后无数据就会断开,为此引入心跳机制(TCP保活机制),使用定时器+Ping报文保活:30s发送一次报文,如果响应成功就维持并修改flag=1,不成功flag++,定时器中检测到flag==1时说明上次未响应,改变发送间隔再重新发送,收到就置0,还没收到继续++,3的话就置connectflag = 0主函数就会重启。

DHT11相关

引脚怎么接

Data单总线协议,输入输出时需要转换引脚输入输出,GPIO PA5 推挽输出至高低电平即可

具体操作

数据包由 5Byte(40Bit)组成。数据分小数部分和整数部分,一次完整的数据传输为40bit,高位先出。主机只需要读即可。

初始化(DHT11_RST

  • 复位:引脚变输出-> 主机拉低至少18ms(输出低电平)->释放总线(输出高电平)->延时20-40us(复位信号发送完毕)
  • 响应:DHT11检测到复位信号后会自动拉低总线80us再拉高80us表示响应->主机分别在100us内检测是否有低电平信号+高电平信号到达确认是否响应。都有响应表示初始化成功。

读时序(Read_Bit, Read_Byte, Read_Data):以高电平长短定义数据位是0是1。首先拉低电平50us,拉高26-28us表示0;持续70us表示1。

  • Read_Bit:依次检测低电平和高电平后,延迟35us(28 <35<70)检测是0还是1 读取1位
  • Read_Byte:由于高位先出,不断左移读到的数据并和新的数据做 | 运算
  • Read_Data:调用DHT11_RST启动读取,调用read_Byte一次接收5个字节,最后一位校验位,成功后赋值给变量。

定时发送(Sensor_DHT11):此函数调用readdata, 放在time2的中断函数中30s调用一次获取一次传感器数据,按格式封装后放入发送缓冲区(mqtt_PublishQs0:计算剩余长度拼接好publish报文)。

最后1位传送完后,DHT11拉低总线50us表示数据传输完毕,随后总线由上拉电阻拉高进入空闲状态。

函数调用关系

TIM2_IRQHandler(定时器中断函数) -> Sensor_DHT11 -> Read_Data -> DHT11_RST + Read_Byte(Read_Bit)

ESP8266相关

使用指南

工作模式

  • station:作为终端连接其他路由器,不能被其他设备连接(无热点功能的手机)
  • AP:允许其他设备接入,不能连其他路由器。(等价于路由器)
  • AP+Station

透传模式(SerialNet)将本地异步串口通信转换成基于TCP/UDP协议的网络通信。其主要目的是将串行通信的简单设备实现在网络上的通信,而这些设备不需要做任何改变。

引脚怎么接

共8个引脚 3V3接电, GND共地,烧固件时GPIO0接地

USART2_TX-WIFI_RXT USART2_RX-WIFI_TXD 其他不接

把esp当单片机时,就用USB_TTL接他的TX和RX

具体操作

使用USB-TTL (下载器)给ESP8266刷固件

连接STM32后使用AT指令由单片机通过串口发送指令(u2_printf)给esp,根据返回值(WiFi_RX_BUF == USART2_RxBuff:在串口中断中捕获模块的返回值)一步步发送上边8条语句。

WIFI_Config0:对复位后(第一条)返回语句ready的检测,检测到后依次发送上述指令

WIFI_Config:发送指定指令并检测返回值是否为规定值

WIFI_Router & WIFI_ConnectTCP:配置路由器和TCP的输入不统一,且需要的时间较久,故单独处理

WIFI_Connect:依次执行9条指令输出当前状态,返回0为成功

函数调用关系

main -> WIFI_Connect -> 其他所有

串口相关

相关知识

名称

引脚

双工

时钟

电平

设备

USART

TX、RX

全双工

异步

单端

点对点

I2C

SCL、SDA

半双工

同步

单端

多设备

SPI

SCLK、MOSI、MISO、CS

全双工

同步

单端

多设备

CAN

CAN_H、CAN_L

半双工

异步

差分

多设备

USB

DP、DM

半双工

异步

差分

点对点

  • 没有时钟线的通信只能异步:约定好采样频率,帧头帧尾来对齐 波特率:串口通信速率
  • 异步时序要求严格,一般不用软件模拟串口
  • 单端电平必须共地,才能找到相对地的电平
  • USART是个硬件外设,按照串口协议产生和接收电平信号
  • 串口参数与时序
  • 数据的发送和接收都是外设自动完成, 只需要调用send_data和receive_data发送和接收即可uint16_t
  • TX引脚输出定时翻转的高低电平,RX引脚定时读取引脚的高低电平,发送前要加上起始停止校验

八股中可按江协视频补充hex和文本数据包等其他知识点

使用情况

引脚编号

引脚名称

默认复用功能

项目中使用

12

PA2-WIFI_RXT

USART2_TX

连接wifi模块收发报文和wifi配置

13

PA3-WIFI_TXD

USART2_RX

21

PB10

USART3_TX

22

PB11

USART3_RX

30

PA9-RXD

USART1_TX

通过USB_TTL连接电脑用来调试

31

PA10-TXD

USART1_RX

  • 江协那边用的是STLink.,在串口实验部分检测发送时也使用了重定向,不过调用了自己的send_string
  • 串口1实现和电脑的通信,用来调试,没有用到串口接收,所以没有中断调用receive_data
  • TXE是TDR寄存器数据转移到移位寄存器后置位;TC是在TXE置位后,并且数据帧传输完成;在串口中断中使用了

中断

  • 开启RXNE标志位到NVIC的输出:USART_ITConfig(USART2,USART_IT_RXNE,ENABLE); 即RDR寄存器有值,不为空就会产生中断
  • 配置NVIC:分组;通道;使能;优先级

具体操作

开启串口1和GPIO的时钟

  • GPIO结构体:TX为外设输出脚,配置为复用推挽输出;RX为外设输入脚,配置为浮空输入模式(串口波形空闲状态为高电平,不使用下拉输入)
  • 串口结构体:波特率(init会计算对应分频系数并写入BRR寄存器);硬件流控制不开启;串口模式为发送和接收;不要校验;停止位1位;不要校验字长就为8

串口1

  • 实现和电脑通信,为了调试在串口助手显示信息
  • 实现发送数据函数并重定向到串口

串口2

  • 在中断函数中接收wifi发来的数据放至USART2_RxBuff缓冲区中
  • 在串口中断中设置定时器分割不同组数据,并把接收到的数据放到mqtt_RxBuf容器的每一行
  • 主函数中会检测mqtt_RxBuf是否有接收到的数据要处理,有的话就根据信息进行后续操作,最后会移动接收缓冲区指针。(后边来的会覆盖)
  • u2_printf : 向wifi模块发送AT指令
  • u2_TxData:向服务器发送封装好的报文 ,2之后开始,前两个是计数的

函数调用

  • main -> u2_TxData -> USART_SendData
  • USART2_IRQHandler -> USART2_RxBuff
  • WIFI_Config -> u2_printf -> USART_SendByte

OLED相关

相关知识

使用IIC协议进行通信,单片机发送数据并进行应答接收

分辨率为128(128seg)*64(8页),1个seg对应1Byte数据,每页128Byte;

低位在上,高位在下,一个字符占16*8(2页8seg列)

  • 显示字符时调用ascii码表 OLED_WriteData(OLED_F8x16[Char - ' '][i]) 前8位对应页0的seg,后八位对应页1的seg,分两步发送完成字符的显示。
  • 每一步开始前需要移动光标位置OLED_SetCursor到起始对应的页和

剩余60%内容,订阅专栏后可继续查看/也可单篇购买

计算机实习秋招全阶段指南 文章被收录于专栏

作者简介:2个月时间逆袭嵌入式开发,拿下理想汽车-ssp、小米汽车-sp、oppo-sp、迈瑞医疗、三星电子等八家制造业大厂offer~ 专栏内容:涵盖算法、八股、项目、简历等前期准备的详细笔记和模板、面试前中后的各种注意事项以及后期谈薪、选offer等技巧。保姆级全阶段教程帮你获得信息差,早日收到理想offer~

全部评论

相关推荐

女生&nbsp;双非二本&nbsp;专业电子信息&nbsp;学了数电模电&nbsp;c语言&nbsp;数据结构&nbsp;python&nbsp;51单片机还有stm32&nbsp;linux&nbsp;还有一些上位机很多都只有一个大概的了解&nbsp;惭愧&nbsp;学艺不精硬件不感兴趣pcb只会画两层板&nbsp;ad&nbsp;立创都用过&nbsp;原理图能看懂&nbsp;电路设计只做过学校的课业作业四位加法器&nbsp;24制数字钟&nbsp;波形发生器&nbsp;示波器也用过只是来看波形&nbsp;(硬件技能点就这么多了,可能是学过微机原理还有单片机芯片手册也能大概看懂)软件方面&nbsp;stm32当初看正点(感觉不怎么好入门只看学到了串口通信因为系统知识逻辑大纲太乱就没再学习了)现在很久不用只会调用库函数使用模块&nbsp;用stm32参加了电赛国赛&nbsp;只混了个省二&nbsp;而且我在里面也主要是搞open&nbsp;mv&nbsp;的识别还有和stm32的串口通信&nbsp;还有一些坐标转化&nbsp;各功能模块的调用……python就能在用上位机的时候耍耍了&nbsp;最近学校课程是用linux搞树莓派的视觉开发&nbsp;一些人脸识别手势识别什么的疑问是:1,c语言和数据结构有基础但学的不扎实目前在学习…不知道应该学到什么程度2,&nbsp;如果找嵌入式软件之类的工作除了这两门课程还有什么别的需要学习的3,对stm32兴趣不大但是手上有的项目都是用stm32&nbsp;智能送药小车还有国赛那个&nbsp;如果要找暑假着急找实习是不是得先从这两个项目入手&nbsp;先学习stm324,最后&nbsp;直接转纯软不太现实了&nbsp;其实对纯软更有兴趣&nbsp;硬件太挠人了&nbsp;走嵌入式软件开发的话现在学习linux还来得及吗?(手上还有一个师兄遗留的linux&nbsp;ubuntun的项目&nbsp;当初是在里面当论文手&nbsp;这个项目只拿了挑战杯省二)有点迷茫&nbsp;问的有点多&nbsp;请前辈们指教!
点赞 评论 收藏
转发
1.嵌入式从技术来分,分为有操作系统和没操作系统的,有操作系统的是未来的主流。2.嵌入式从职业上分为:一.嵌入式硬件u工程师二.嵌入式驱动工程师三.嵌入式软件工程师四.嵌入式系统工程师嵌入式硬件工程师是最最底层的,通常要接触很多设备,同时还要精通电路图,和电路的设计。说的通俗一点,硬件工程师,把这个项目的各个设备把线给连起来,方便后面的程序编写。嵌入式驱动工程师,就是写这些设备的驱动,你硬件工程师把线连起来了,你得编程吧,你就负责驱动这些设备的程序吧。有些项目,需要在操作系统上驱动设备,那你就要会用这个操作系统,在操作系统上,驱动这些设备,其实大多是都是靠移植。嵌入式软件工程师,非常精通C语言,如果在操作系统上,还得精通数据结构和操作系统,也得会一些常用的算法,有点类似于,让你在安卓或者IOS系统下做一个APP。小编对于嵌入式软件了解的不是很多,有讲错的地方请指出。嵌入式系统工程师,这个如果你很厉害,会写操作系统,那你工资是很高的,但也得付出很多的精力去学习。一般情况下我们都是靠移植,毕竟咋没有华为,苹果,谷歌的实力啊。一般在大公司才会分的这么细,大公司做一个项目,每个人需要干什么会分的很细很细的。举个例子,你们公司要做一个扫地机器人,那么你或者你们这些人组成一个团队,负责扫地机器人的电机驱动的板级支持包编写。那你或者你们这个团队就是负责嵌入式驱动。如果你在小公司,那就说不准了,老板肯定想你会的更多,这样子就不要招更多的人了,但小公司往往工资不比大公司少,毕竟能者多劳,但累确实挺累的。如果读者们还是不清楚嵌入式是什么,或者说嵌入式想学什么,那么小编建议大家学习嵌入式先看一些数电模电,电子专业的书籍,有了一定基础之后,买一块51单片机开发板,从最简单的裸机开始学习,会是一个不错的选择,51单片机学的差不多了,做一个小项目。难一点的基于51单片机的蓝牙指纹开锁系统,简单一点的做一个基于51单片机温度控制系统。51都会了。在买一个32位的学习板,这个跟51比起来,要全部学会还是有点难度的,学的差不多了也可以考虑做一些项目,这个关于嵌入式的学习方向,我以后还会专门写一篇文章来介绍的。&nbsp;&nbsp;c++/嵌入式面经专栏-牛客网 https://www.nowcoder.com/creation/manager/columnDetail/MJNwoM
点赞 评论 收藏
转发
5 16 评论
分享
牛客网
牛客企业服务