• 基于STM32+华为云IOT设计的智能门禁系统


    一、项目介绍

    智能门禁系统是一种应用物联网技术的智能化安防系统,提供安全高效的门禁管理和远程监控功能。传统的门禁系统通常使用磁卡、密码或钥匙等方式进行开锁,但存在易丢失、易复制、操作繁琐等问题。为了解决这些问题,并提高门禁安全性和便捷性,这里介绍了一款基于STM32F103和华为云IOT的智能门禁系统的设计方案。

    系统的核心控制器采用了STM32F103单片机,具有丰富的外设接口和强大的处理能力,能够实现对门禁设备的精确控制和数据处理。为了提供多种开锁方式,选择了RFID-RC522读写模块和4x4电容矩阵键盘作为输入设备,用户可以通过刷卡或输入密码来开启门禁。采用了28BYJ-48步进电机作为门锁控制装置,能够实现对门锁的精确控制。

    为了方便用户与门禁系统进行交互,使用了SPI接口的0.96寸OLED显示屏和蜂鸣器,可以显示门锁状态信息和人机交互提示音。此外,为了实现远程控制和消息推送功能,选择了ESP8266 WiFi模块连接华为云物联网平台,用户可以通过手机APP实现远程开锁,并且可以随时监控门锁状态。当门锁打开或关闭成功时,手机APP会收到通知提醒,以增加用户的使用安全性和便捷性。

    通过将STM32F103和华为云IOT相结合,可以实现智能门禁系统在数据传输、远程控制和消息推送等方面的高效、稳定和安全性能。该系统不仅提供了多种开锁方式和人机交互功能,还具备远程监控和消息提醒的智能化特点,适用于住宅小区、学校、企事业单位等场所的门禁管理需求。

    image-20230801151934943

    image-20230801151925200

    二、硬件选型

    在智能门禁系统的硬件选型中,选择了以下模块:

    【1】主控制器:STM32F103单片机。它是一款高性能、低功耗的ARM Cortex-M3内核微控制器,具有丰富的外设接口和强大的计算和控制能力,非常适合用于门禁系统的控制和数据处理。

    【2】RFID读写模块:RFID-RC522。这个模块基于13.56MHz的射频识别技术,可以实现对RFID卡片的读写操作,通过刷卡方式进行门禁的开启和关闭。

    【3】门锁控制:28BYJ-48步进电机。该步进电机结构简单、体积小巧,能够实现对门锁的准确控制,提供开启和关闭门锁的功能。

    【4】密码输入设备:4x4电容矩阵键盘。这种键盘采用电容触摸技术,具有防水、灵敏度高的特点,用户可以通过输入密码来开启门禁。

    【5】显示屏:SPI接口的0.96寸OLED显示屏。该显示屏具有高亮度、高对比度和低功耗的特点,可以显示门锁状态信息和人机交互提示。

    【6】网络连接模块:ESP8266-WIFI。这个模块支持WiFi通信标准,能够实现与华为云物联网平台的连接,实现远程控制和消息推送功能。

    【7】门状态检测:红外磁感应模块。通过红外磁感应技术,可以检测门是否打开或关闭,提供准确的门状态信息。

    选择的这些硬件可以满足智能门禁系统的各项功能需求。主控制器STM32F103为整个系统提供了稳定可靠的控制和处理能力,RFID读写模块和密码键盘提供了多种开锁方式,步进电机实现了精确的门锁控制,显示屏和蜂鸣器提供了人机交互功能,ESP8266-WIFI模块连接华为云实现了远程控制和消息推送,红外磁感应模块检测门状态,使得整个智能门禁系统更加智能、安全和便捷。

    三、系统设计思路

    智能门禁系统的设计思路:

    【1】硬件连接与集成:各个硬件组件(如主控制器、RFID读写模块、步进电机等)需要进行正确的连接和接口配置。通过连接线将各个硬件模块与主控制器相连,并设置正确的引脚连接和寄存器配置,以确保它们能够正常工作并实现相应的功能。

    【2】数据采集与处理:系统通过RFID读写模块读取用户刷卡信息或通过电容矩阵键盘获取用户输入的密码信息。主控制器接收到这些数据后,进行相应的数据处理,例如验证刷卡信息或密码是否正确。如果验证通过,则进行下一步的门禁控制流程。

    【3】门锁控制与状态检测:主控制器通过控制步进电机来实现门锁的精确控制。当用户验证成功后,主控制器发送指令给步进电机,使其旋转相应的步数,从而打开或关闭门锁。同时,主控制器还使用红外磁感应模块来检测门的状态,以确保门锁的准确控制。

    【4】用户界面与人机交互:系统配备了0.96寸OLED显示屏和蜂鸣器,通过显示屏可以向用户展示门锁的状态信息和相关提示,如开锁成功、密码错误等。蜂鸣器则可以发出不同的提示音,提供与用户的交互反馈。

    【5】远程控制与消息推送:通过ESP8266-WIFI模块与华为云物联网平台连接,使得用户可以通过手机APP进行远程开锁操作。用户在手机APP上输入开锁指令后,APP会将指令发送到华为云平台,再由云平台将指令传输给物联网设备(智能门禁系统)。主控制器接收到开锁指令后,执行相应的控制动作,并将结果通过云平台发送回手机APP,实现远程控制和消息推送功能。

    整个系统的设计思路是将各个硬件组件协调配合,通过主控制器进行数据处理和门锁控制,并提供人机交互界面和远程控制功能,以实现智能门禁系统的安全、高效和便捷的门禁管理和远程监控。

    四、华为云物联网平台部署

    华为云官网: https://www.huaweicloud.com/

    打开官网,搜索物联网,就能快速找到 设备接入IoTDA

    image-20230801151116995

    4.1 物联网平台介绍

    华为云物联网平台(IoT 设备接入云服务)提供海量设备的接入和管理能力,将物理设备联接到云,支撑设备数据采集上云和云端下发命令给设备进行远程控制,配合华为云其他产品,帮助我们快速构筑物联网解决方案。

    使用物联网平台构建一个完整的物联网解决方案主要包括3部分:物联网平台、业务应用和设备。

    物联网平台作为连接业务应用和设备的中间层,屏蔽了各种复杂的设备接口,实现设备的快速接入;同时提供强大的开放能力,支撑行业用户构建各种物联网解决方案。

    设备可以通过固网、2G/3G/4G/5G、NB-IoT、Wifi等多种网络接入物联网平台,并使用LWM2M/CoAP、MQTT、HTTPS协议将业务数据上报到平台,平台也可以将控制命令下发给设备。

    业务应用通过调用物联网平台提供的API,实现设备数据采集、命令下发、设备管理等业务场景。

    image-20230801151124332

    image-20230801151130369

    4.2 开通物联网服务

    地址: https://www.huaweicloud.com/product/iothub.html

    image-20230801151136153

    点击总览,查看接入信息。 我们当前设备准备采用MQTT协议接入华为云平台,这里可以看到MQTT协议的地址和端口号等信息。

    image-20230801151202953

    总结:

    端口号:   MQTT (1883)| MQTTS (8883)   
    接入地址: a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
    
    • 1
    • 2

    根据域名地址得到IP地址信息:

    Microsoft Windows [版本 10.0.19045.2965]
    (c) Microsoft Corporation。保留所有权利。
    
    C:\Users\11266>ping a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
    
    正在 Ping a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com [121.36.42.100] 具有 32 字节的数据:
    来自 121.36.42.100 的回复: 字节=32 时间=38ms TTL=94
    来自 121.36.42.100 的回复: 字节=32 时间=37ms TTL=94
    来自 121.36.42.100 的回复: 字节=32 时间=38ms TTL=94
    来自 121.36.42.100 的回复: 字节=32 时间=36ms TTL=94
    
    121.36.42.100 的 Ping 统计信息:
        数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
    往返行程的估计时间(以毫秒为单位):
        最短 = 36ms,最长 = 38ms,平均 = 37ms
    
    C:\Users\11266>
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    image-20230801151212765

    MQTT协议接入端口号有两个,1883是非加密端口,8883是证书加密端口,单片机无法加载证书,所以使用1883端口比较合适。 接下来的ESP8266就采用1883端口连接华为云物联网平台。

    4.3 创建产品

    (1)创建产品

    点击产品页,再点击左上角创建产品。

    image-20230801151220860

    (2)填写产品信息

    根据自己产品名字填写。

    image-20230801151228433

    (3)产品创建成功

    image-20230801151234528

    (4)添加自定义模型

    产品创建完成之后,点击进入产品详情页面,翻到最下面可以看到模型定义。

    这个模型就是定义自己设备接下来需要向服务器上传那些数据类型。根据自己的数据类型进行编写。

    先点击自定义模型。

    image-20230801151243343

    再创建一个服务ID。

    image-20230801151250227

    接着点击新增属性。

    image-20230801151255512

    image-20230801151300240

    4.4 添加设备

    产品是属于上层的抽象模型,接下来在产品模型下添加实际的设备。添加的设备最终需要与真实的设备关联在一起,完成数据交互。

    (1)注册设备

    image-20230801151308625

    (2)根据自己的设备填写

    image-20230801151315386

    (3)保存设备信息

    创建完毕之后,点击保存并关闭,得到创建的设备密匙信息。该信息在后续生成MQTT三元组的时候需要使用。

    image-20230801151322328

    (4)设备创建完成

    可以点击设备进入到设备详情页面。

    image-20230801151331651

    4.5 MQTT协议主题订阅与发布

    (1)MQTT协议介绍

    当前的设备是采用MQTT协议与华为云平台进行通信。

    MQTT是一个物联网传输协议,它被设计用于轻量级的发布/订阅式消息传输,旨在为低带宽和不稳定的网络环境中的物联网设备提供可靠的网络服务。MQTT是专门针对物联网开发的轻量级传输协议。MQTT协议针对低带宽网络,低计算能力的设备,做了特殊的优化,使得其能适应各种物联网应用场景。目前MQTT拥有各种平台和设备上的客户端,已经形成了初步的生态系统。

    MQTT是一种消息队列协议,使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合,相对于其他协议,开发更简单;MQTT协议是工作在TCP/IP协议上;由TCP/IP协议提供稳定的网络连接;所以,只要具备TCP协议栈的网络设备都可以使用MQTT协议。 本次设备采用的ESP8266就具备TCP协议栈,能够建立TCP连接,所以,配合STM32代码里封装的MQTT协议,就可以与华为云平台完成通信。

    华为云的MQTT协议接入帮助文档在这里: https://support.huaweicloud.com/devg-iothub/iot_02_2200.html

    image-20230801151337395

    业务流程:

    image-20230801151342879

    (2)华为云平台MQTT协议使用限制

    描述限制
    支持的MQTT协议版本3.1.1
    与标准MQTT协议的区别支持Qos 0和Qos 1支持Topic自定义不支持QoS2不支持will、retain msg
    MQTTS支持的安全等级采用TCP通道基础 + TLS协议(最高TLSv1.3版本)
    单帐号每秒最大MQTT连接请求数无限制
    单个设备每分钟支持的最大MQTT连接数1
    单个MQTT连接每秒的吞吐量,即带宽,包含直连设备和网关3KB/s
    MQTT单个发布消息最大长度,超过此大小的发布请求将被直接拒绝1MB
    MQTT连接心跳时间建议值心跳时间限定为30至1200秒,推荐设置为120秒
    产品是否支持自定义Topic支持
    消息发布与订阅设备只能对自己的Topic进行消息发布与订阅
    每个订阅请求的最大订阅数无限制

    (3)主题订阅格式

    帮助文档地址:https://support.huaweicloud.com/devg-iothub/iot_02_2200.html

    image-20230801151350067

    image-20230801151355027

    对于设备而言,一般会订阅平台下发消息给设备 这个主题。

    设备想接收平台下发的消息,就需要订阅平台下发消息给设备 的主题,订阅后,平台下发消息给设备,设备就会收到消息。

    如果设备想要知道平台下发的消息,需要订阅上面图片里标注的主题。

    以当前设备为例,最终订阅主题的格式如下:
    $oc/devices/{device_id}/sys/messages/down
    
    最终的格式:
    $oc/devices/6419627e40773741f9fbdac7_dev1/sys/messages/down
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (4)主题发布格式

    对于设备来说,主题发布表示向云平台上传数据,将最新的传感器数据,设备状态上传到云平台。

    这个操作称为:属性上报。

    帮助文档地址:https://support.huaweicloud.com/usermanual-iothub/iot_06_v5_3010.html

    image-20230801151401246

    根据帮助文档的介绍, 当前设备发布主题,上报属性的格式总结如下:

    发布的主题格式:
    $oc/devices/{device_id}/sys/properties/report
     
    最终的格式:
    $oc/devices/6419627e40773741f9fbdac7_dev1/sys/properties/report
    发布主题时,需要上传数据,这个数据格式是JSON格式。
    
    上传的JSON数据格式如下:
    
    {
      "services": [
        {
          "service_id": <填服务ID>,
          "properties": {
            "<填属性名称1>": <填属性值>,
            "<填属性名称2>": <填属性值>,
            ..........
          }
        }
      ]
    }
    根据JSON格式,一次可以上传多个属性字段。 这个JSON格式里的,服务ID,属性字段名称,属性值类型,在前面创建产品的时候就已经介绍了,不记得可以翻到前面去查看。
    
    根据这个格式,组合一次上传的属性数据:
    {"services": [{"service_id": "stm32","properties":{"DS18B20":18,"motor_water":1,"motor_oxygen":1,"temp_max":10,"water_hp":130,"motor_food":0,"time_food":0,"oxygen_food":3}}]}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    4.6 MQTT三元组

    MQTT协议登录需要填用户ID,设备ID,设备密码等信息,就像我们平时登录QQ,微信一样要输入账号密码才能登录。MQTT协议登录的这3个参数,一般称为MQTT三元组。

    接下来介绍,华为云平台的MQTT三元组参数如何得到。

    (1)MQTT服务器地址

    要登录MQTT服务器,首先记得先知道服务器的地址是多少,端口是多少。

    帮助文档地址:https://console.huaweicloud.com/iotdm/?region=cn-north-4#/dm-portal/home

    MQTT协议的端口支持1883和8883,它们的区别是:8883 是加密端口更加安全。但是单片机上使用比较困难,所以当前的设备是采用1883端口进连接的。

    根据上面的域名和端口号,得到下面的IP地址和端口号信息: 如果设备支持填写域名可以直接填域名,不支持就直接填写IP地址。 (IP地址就是域名解析得到的)

    华为云的MQTT服务器地址:114.116.232.138
    域名:7445c6bcd3.st1.iotda-device.cn-north-4.myhuaweicloud.com
    华为云的MQTT端口号:1883
    
    • 1
    • 2
    • 3

    注意!!!! 具体要看这里:

    image-20230801151408871

    (2)生成MQTT三元组

    华为云提供了一个在线工具,用来生成MQTT鉴权三元组: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

    打开这个工具,填入设备的信息(也就是刚才创建完设备之后保存的信息),点击生成,就可以得到MQTT的登录信息了。

    下面是打开的页面:

    image-20230801151418248

    填入设备的信息: (上面两行就是设备创建完成之后保存得到的)

    得到三元组之后,设备端通过MQTT协议登录鉴权的时候,填入参数即可。

    ClientId 6419627e40773741f9fbdac7_dev1_0_0_2023032108
    Username 6419627e40773741f9fbdac7_dev1
    Password 861ac9e6a579d36888b2aaf97714be7af6c77017b017162884592bd68b086a6e
    
    • 1
    • 2
    • 3

    4.7 模拟设备登录测试

    经过上面的步骤介绍,已经创建了产品,设备,数据模型,得到MQTT登录信息。 接下来就用MQTT客户端软件模拟真实的设备来登录平台。测试与服务器通信是否正常。

    (1)填入登录信息

    打开MQTT客户端软件,对号填入相关信息(就是上面的文本介绍)。然后,点击登录,订阅主题,发布主题。

    image-20230801151429390

    (2)打开网页查看

    完成上面的操作之后,打开华为云网页后台,可以看到设备已经在线了。

    点击详情页面,可以看到上传的数据。

    到此,云平台的部署已经完成,设备已经可以正常上传数据了。

    4.8 获取设备影子数据(API接口)

    帮助文档:https://support.huaweicloud.com/api-iothub/iot_06_v5_0079.html

    设备影子介绍:

    设备影子是一个用于存储和检索设备当前状态信息的JSON文档。
    每个设备有且只有一个设备影子,由设备ID唯一标识
    设备影子仅保存最近一次设备的上报数据和预期数据
    无论该设备是否在线,都可以通过该影子获取和设置设备的属性
    
    • 1
    • 2
    • 3
    • 4

    简单来说:设备影子就是保存,设备最新上传的一次数据。

    我们设计的软件里,如果想要获取设备的最新状态信息,就采用设备影子接口。

    如果对接口不熟悉,可以先进行在线调试:https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=ShowDeviceShadow

    在线调试接口,可以请求影子接口,了解请求,与返回的数据格式。

    image-20230801151441245

    设备影子接口返回的数据如下:

    {
     "device_id": "6419627e40773741f9fbdac7_dev1",
     "shadow": [
      {
       "service_id": "stm32",
       "desired": {
        "properties": null,
        "event_time": null
       },
       "reported": {
        "properties": {
         "DS18B20": 18,
         "motor_water": 1,
         "motor_oxygen": 1,
         "temp_max": 10,
         "water_hp": 130,
         "motor_food": 0,
         "time_food": 0,
         "oxygen_food": 3
        },
        "event_time": "20230321T081126Z"
       },
       "version": 0
      }
     ]
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    4.9 修改设备属性(API接口)

    地址: https://support.huaweicloud.com/api-iothub/iot_06_v5_0034.html

    接口说明

    设备的产品模型中定义了物联网平台可向设备下发的属性,应用服务器可调用此接口向指定设备下发属性。平台负责将属性以同步方式发送给设备,并将设备执行属性结果同步返回。
    
    • 1

    修改设备属性的接口,可以让服务器给设备下发指令,如果需要控制设备。

    在线调试地址:

    https://apiexplorer.developer.huaweicloud.com/apiexplorer/doc?product=IoTDA&api=UpdateProperties

    修改设备属性是属于同步命令,需要设备在线才可以进行调试,先使用MQTT客户端登录服务器,模拟设备上线。

    然后进行调试,测试数据远程下发给设备。

    【1】利用MQTT客户端先登录设备 (这是同步命令,必须在线才能调试)

    image-20230801151450774

    【2】点击调试

    image-20230801151457386

    {"services":{"temp_max":100}}
    
    • 1

    【4】可以看到,MQTT客户端软件上已经收到了服务器下发的消息

    image-20230801151505222

    由于是同步命令,服务器必须要收到设备的响应才能顺利完成一个流程,设备响应了服务器才能确定数据下发成功。

    五、代码设计

    5.1 ESP8266模块介绍

    ESP8266-WIFI是一款由乐鑫科技开发的低成本Wi-Fi芯片,可与其他微控制器或单片机配合使用,实现无线通讯功能。该芯片集成了一个高性能的32位Tensilica L106 RISC处理器,以及一组内置的WIFI模块和电源管理模块,能够在低功耗下提供稳定的无线网络连接。

    以下是ESP8266与MQTT协议相关常用的AT指令介绍:

    【1】AT+CIPSTART:建立TCP连接。

    在使用MQTT协议时,需要先建立TCP连接。AT+CIPSTART可以实现与MQTT服务器的TCP连接,格式如下:

    AT+CIPSTART="TCP","MQTT服务器地址",MQTT服务器端口号
    
    • 1

    【2】AT+CIPSEND:发送数据。

    在建立TCP连接之后,可以使用AT+CIPSEND发送数据,包括MQTT协议的各种命令。格式如下:

    AT+CIPSEND=
    
    
    • 1
    • 2

    其中,表示数据长度,表示MQTT协议的命令,例如CONNECT、PUBLISH、SUBSCRIBE等。

    【3】AT+CIPCLOSE:关闭TCP连接。在使用完MQTT协议之后,可以使用AT+CIPCLOSE关闭TCP连接,格式如下:

    AT+CIPCLOSE
    
    • 1

    【4】AT+CIPSTATUS:查询TCP连接状态。使用AT+CIPSTATUS可以查询当前TCP连接的状态,格式如下:

    AT+CIPSTATUS
    
    • 1

    返回值包括:STATUS、TCP连接状态;LINK ID、TCP连接ID;IP、TCP连接的IP地址;PORT、TCP连接端口号。

    【5】AT+MQTTCONN:连接MQTT服务器。使用AT+MQTTCONN可以连接MQTT服务器,格式如下:

    AT+MQTTCONN=,,,,,,,,
    
    • 1

    其中,表示客户端ID;表示用户名;表示密码;表示心跳包间隔时间;表示清除会话标志;表示遗言主题;表示遗言消息;表示遗言QoS等级;表示遗言消息是否保留。

    【6】AT+MQTTPUB:发布消息。使用AT+MQTTPUB可以发布MQTT消息,格式如下:

    AT+MQTTPUB=,,,
    
    • 1

    其中,表示MQTT主题;表示消息负载;表示消息QoS等级;表示消息是否保留。

    【7】AT+MQTTSUB:订阅主题。使用AT+MQTTSUB可以订阅MQTT主题,格式如下:

    AT+MQTTSUB=,
    
    • 1

    其中,表示MQTT主题;表示消息QoS等级。

    5.2 连接云端代码

    以下是STM32F103+ESP8266通过MQTT协议登录MQTT物联网服务器,实现主题订阅、发布的示例代码,其中利用了ESP8266-WIFI的AT指令实现与MQTT服务器的通信。

    需要在STM32F103上配置UART串口,以实现与ESP8266的通信。在STM32F103上编写AT指令的发送和接收函数,将其集成到工程中。

    代码如下:

    #include "stm32f1xx_hal.h"
    #include 
    #include 
    
    /* 定义ESP8266的UART串口 */
    UART_HandleTypeDef huart1;
    
    /* 定义MQTT服务器地址、端口号、用户名和密码等信息 */
    const char* mqtt_server = "mqtt.example.com";
    const int mqtt_port = 1883;
    const char* mqtt_username = "username";
    const char* mqtt_password = "password";
    
    /* 定义MQTT主题 */
    const char* mqtt_topic = "test/topic";
    
    /* 定义回调函数,处理MQTT消息 */
    void messageArrived(MessageData* data)
    {
        printf("Message arrived: topic=%.*s, payload=%.*s\n", 
               data->topicName->lenstring.len, 
               data->topicName->lenstring.data, 
               data->message->payloadlen, 
               data->message->payload);
    }
    
    /* 发送AT指令并等待响应 */
    int ESP8266_SendATCommand(char* cmd, char* response, int timeout)
    {
        HAL_UART_Transmit(&huart1, (uint8_t*)cmd, strlen(cmd), timeout);
    
        int len = 0;
        int len_read = 0;
        HAL_Delay(100);
        while (1) {
            if (HAL_UART_Receive(&huart1, (uint8_t*)response + len, 1, timeout) == HAL_OK) {
                len++;
                if (strstr(response, "OK") != NULL) {
                    return len;
                }
                if (strstr(response, "ERROR") != NULL) {
                    return -1;
                }
                if (len >= 1024) {
                    return -1;
                }
            }
        }
    }
    
    /* 连接MQTT服务器 */
    int MQTT_Connect(void)
    {
        char cmd[1024];
        char response[1024];
    
        /* 设置ESP8266为透传模式 */
        ESP8266_SendATCommand("AT+CIPMODE=1\r\n", response, 1000);
        ESP8266_SendATCommand("AT+CIPMUX=0\r\n", response, 1000);
    
        /* 建立TCP连接 */
        sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", mqtt_server, mqtt_port);
        ESP8266_SendATCommand(cmd, response, 5000);
    
        /* 连接MQTT服务器 */
        sprintf(cmd, "AT+CIPSEND=%d\r\n", 33 + strlen(mqtt_username) + strlen(mqtt_password));
        ESP8266_SendATCommand(cmd, response, 1000);
        sprintf(cmd, "CONNECT\n\rclient_id:ESP8266\n\ruser:%s\n\rpassword:%s\n\r\n\r", mqtt_username, mqtt_password);
        ESP8266_SendATCommand(cmd, response, 5000);
    
        /* 订阅MQTT主题 */
        sprintf(cmd, "AT+CIPSEND=%d\r\n", 10 + strlen(mqtt_topic));
        ESP8266_SendATCommand(cmd, response, 1000);
        sprintf(cmd, "SUBSCRIBE\n\r%d\n\r0\n\r\n\r", strlen(mqtt_topic));
        ESP8266_SendATCommand(cmd, response, 5000);
    
        return 0;
    }
    
    /* 发布MQTT消息 */
    void MQTT_Publish(const char* message)
    {
        char cmd[1024];
        char response[1024];
    
        sprintf(cmd, "AT+CIPSEND=%d\r\n", 8 + strlen(mqtt_topic) + strlen(message));
        ESP8266_SendATCommand(cmd, response, 1000);
        sprintf(cmd, "PUBLISH\n\r%d\n\r%s\n\r\n\r%s\n\r", strlen(mqtt_topic), mqtt_topic, message);
        ESP8266_SendATCommand(cmd, response, 5000);
    }
    
    /* 主程序 */
    int main(void)
    {
        /* 连接MQTT服务器 */
        if (MQTT_Connect() != 0) {
            while (1) {
                /* 连接失败,进入死循环 */
            }
        }
    
        /* 发布MQTT消息 */
        MQTT_Publish("Hello, MQTT!");
    
        /* 等待接收MQTT消息 */
        while (1) {
            /* 读取ESP8266接收缓冲区中的数据 */
            char response[1024];
            int len = ESP8266_SendATCommand("AT+CIPRXGET=2,1024\r\n", response, 1000);
            if (len > 0) {
                printf("Received data: %.*s\n", len, response);
                /* TODO: 解析并处理MQTT消息 */
            }
    
            /* 延时一段时间 */
            HAL_Delay(100);
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120

    代码实现了连接MQTT服务器、订阅主题、发布消息等功能。

    5.3 OLED显示屏代码

    以下是使用STM32F103ZET6控制0.96寸SPI接口的OLED显示屏的代码:

    cCopy Code#include "stm32f10x.h"
    #include "spi.h" // SPI库头文件
    
    // OLED相关定义
    #define OLED_CMD  0 // 命令模式
    #define OLED_DATA 1 // 数据模式
    
    #define OLED_ADDR_START 0xB0 // OLED起始地址
    #define OLED_PAGE_START 0x00 // 第一个页的地址
    #define OLED_COLUMN_START 0x10 // 列起始地址
    
    // 初始化OLED显示屏
    void OLED_Init(void)
    {
      // 初始化SPI接口
      SPI_Init();
      
      Delay(100);
      
      // 向OLED发送初始化命令和数据
      
      // 模式设置:设置4位数据线,扩展指令集
      OLED_WriteCmd(OLED_CMD, 0x21);
      // 对比度设置,默认为0x7F
      OLED_WriteCmd(OLED_CMD, 0x81);
      OLED_WriteCmd(OLED_CMD, 0x7F);
      // 设置显示模式:正常显示
      OLED_WriteCmd(OLED_CMD, 0xA4);
      // 设置偏置电压
      OLED_WriteCmd(OLED_CMD, 0xA8);
      OLED_WriteCmd(OLED_CMD, 0x3F);
      // 设置显示方向:从左到右,从上到下
      OLED_WriteCmd(OLED_CMD, 0xC8);
      // 设置扫描地段,从COM0到COM[N-1]
      OLED_WriteCmd(OLED_CMD, 0xDA);
      OLED_WriteCmd(OLED_CMD, 0x12);
      // 设置时钟分频因子
      OLED_WriteCmd(OLED_CMD, 0xD5);
      OLED_WriteCmd(OLED_CMD, 0x80);
      // 设置显示开启
      OLED_WriteCmd(OLED_CMD, 0xAF);
    }
    
    // 写入命令或数据到OLED显示屏
    void OLED_WriteCmd(uint8_t mode, uint8_t cmd)
    {
      if (mode == OLED_CMD) {
        GPIO_ResetBits(GPIOA, GPIO_Pin_4); // 将DC引脚置低,进入命令模式
      } else {
        GPIO_SetBits(GPIOA, GPIO_Pin_4); // 将DC引脚置高,进入数据模式
      }
      
      GPIO_ResetBits(GPIOA, GPIO_Pin_3); // 将CS引脚置低,选中OLED
      
      SPI_SendData(cmd); // 发送命令或数据
      
      while (SPI_GetFlagStatus(SPI1, SPI_FLAG_TXE) == RESET);
      
      GPIO_SetBits(GPIOA, GPIO_Pin_3); // 将CS引脚置高,释放OLED
    }
    
    // 在指定位置显示一个数字
    void OLED_ShowNumber(uint8_t x, uint8_t y, uint32_t number)
    {
      char str[11]; // 最大显示10位数字
      
      sprintf(str, "%lu", number); // 将数字转换为字符串
      
      OLED_ShowString(x, y, str); // 在指定位置显示字符串
    }
    
    // 在指定位置显示一个字符串
    void OLED_ShowString(uint8_t x, uint8_t y, char *str)
    {
      uint8_t i = 0;
      
      while (str[i] != '\0') {
        OLED_ShowChar(x, y, str[i]); // 在指定位置显示一个字符
        x += 6; // 移动到下一列
        i++;
      }
    }
    
    // 在指定位置显示一个字符
    void OLED_ShowChar(uint8_t x, uint8_t y, char ch)
    {
      uint8_t i, j;
      
      for (i = 0; i < 8; i++) { // 8行
        OLED_WriteCmd(OLED_CMD, OLED_ADDR_START + y); // 指定页
        OLED_WriteCmd(OLED_CMD, OLED_COLUMN_START + x); // 指定列
        
        for (j = 0; j < 6; j++) { // 6列
          OLED_WriteCmd(OLED_DATA, ASCII[ch - 32][j]);
        }
        
        y++;
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99

    在这段代码中,定义了OLED相关的常量和函数,包括OLED命令模式和数据模式、OLED起始地址、页起始地址、列起始地址等。在OLED_Init()函数中进行了OLED的初始化过程,包括发送相应的初始化命令和数据给OLED显示屏。在OLED_WriteCmd()函数中,根据传入的命令模式(OLED_CMD)或数据模式(OLED_DATA),将DC引脚设置为相应的状态,并通过SPI接口将命令或数据发送给OLED显示屏。在OLED_ShowNumber()OLED_ShowString()OLED_ShowChar()函数中,通过调用OLED_WriteCmd()发送相应的指令和数据,实现在指定位置显示数字、字符串和字符。

    5.4 步进电机控制代码

    以下是STM32F103+28BYJ40步进电机实现角度控制和正反转控制的代码,使用了STM32F1xx_HAL库来控制GPIO口和定时器,实现精准的角度控制和正反转控制。

    #include "stm32f1xx_hal.h"
    #include 
    
    /* 定义步进电机的4个控制引脚 */
    #define IN1_Pin GPIO_PIN_0
    #define IN1_GPIO_Port GPIOA
    #define IN2_Pin GPIO_PIN_1
    #define IN2_GPIO_Port GPIOA
    #define IN3_Pin GPIO_PIN_2
    #define IN3_GPIO_Port GPIOA
    #define IN4_Pin GPIO_PIN_3
    #define IN4_GPIO_Port GPIOA
    
    /* 定义步进电机的步数数组 */
    uint8_t step_sequence[8] = {0b0001, 0b0011, 0b0010, 0b0110, 0b0100, 0b1100, 0b1000, 0b1001};
    
    /* 定义当前步进电机的位置和方向 */
    int current_step = 0;  // 当前步数
    int direction = 1;  // 1为顺时针,-1为逆时针
    
    /* 定义角度控制参数 */
    float angle_per_step = 1.8;  // 每步角度
    int total_steps = 2048;  // 总步数
    
    /* 定义定时器中断处理函数,控制步进电机的运动 */
    void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
    {
        /* 更新步进电机的位置 */
        current_step += direction;
        if (current_step >= total_steps) {
            current_step = 0;
        } else if (current_step < 0) {
            current_step = total_steps - 1;
        }
    
        /* 控制步进电机的旋转方向 */
        int sequence_index = current_step % 8;
        GPIO_PinState in1_state = (step_sequence[sequence_index] & 0b0001) ? GPIO_PIN_SET : GPIO_PIN_RESET;
        GPIO_PinState in2_state = (step_sequence[sequence_index] & 0b0010) ? GPIO_PIN_SET : GPIO_PIN_RESET;
        GPIO_PinState in3_state = (step_sequence[sequence_index] & 0b0100) ? GPIO_PIN_SET : GPIO_PIN_RESET;
        GPIO_PinState in4_state = (step_sequence[sequence_index] & 0b1000) ? GPIO_PIN_SET : GPIO_PIN_RESET;
        HAL_GPIO_WritePin(IN1_GPIO_Port, IN1_Pin, in1_state);
        HAL_GPIO_WritePin(IN2_GPIO_Port, IN2_Pin, in2_state);
        HAL_GPIO_WritePin(IN3_GPIO_Port, IN3_Pin, in3_state);
        HAL_GPIO_WritePin(IN4_GPIO_Port, IN4_Pin, in4_state);
    }
    
    /* 初始化定时器 */
    void TIM_Init(void)
    {
        TIM_HandleTypeDef htim;
    
        /* 使能定时器时钟 */
        __HAL_RCC_TIM2_CLK_ENABLE();
    
        /* 配置定时器 */
        htim.Instance = TIM2;
        htim.Init.Prescaler = 7199;  // 72MHz / 7200 = 10kHz
        htim.Init.CounterMode = TIM_COUNTERMODE_UP;
        htim.Init.Period = 99;  // 10kHz / 100 = 100Hz
        htim.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
        HAL_TIM_Base_Init(&htim);
    
        /* 启动定时器 */
        HAL_TIM_Base_Start_IT(&htim);
    }
    
    /* 控制步进电机旋转 */
    void StepMotor_Rotate(int angle, int clockwise)
    {
        /* 计算需要旋转的步数和方向 */
        int steps = angle / angle_per_step;
        direction = clockwise ? 1 : -1;
    
        /* 控制步进电机旋转 */
        for (int i = 0; i < steps; i++) {
            HAL_Delay(1);
        }
    }
    
    /* 主程序 */
    int main(void)
    {
        /* 初始化定时器 */
        TIM_Init();
    
        /* 控制步进电机旋转 */
        StepMotor_Rotate(90, 1);  // 顺时针旋转90度
        StepMotor_Rotate(180, 0);  // 逆时针旋转180度
    
        while (1) {
            /* 循环等待 */
        }
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96

    IN1_Pin到IN4_Pin为步进电机的控制引脚,step_sequence为步进电机的步数数组,current_step为当前步数,direction为旋转方向,angle_per_step为每步的角度,total_steps为总步数。在HAL_TIM_PeriodElapsedCallback函数中,根据当前步数和旋转方向控制步进电机的旋转方向,使用HAL_GPIO_WritePin函数控制引脚输出状态。在StepMotor_Rotate函数中,根据传入的角度和旋转方向计算需要旋转的步数和方向,然后循环延时控制步进电机旋转。通过调用StepMotor_Rotate函数,可以实现步进电机的角度控制和正反转控制。

    六、总结

    智能门禁系统是一种基于STM32F103主控制器和华为云物联网平台的安全、便捷的门禁控制方案。通过整合多种硬件模块和网络通信技术,该系统实现了多种开锁方式(密码输入、RFID刷卡、手机APP远程开锁),并且具备远程监控和消息推送功能。

    在硬件选型上,刷卡模块选择了RFID-RC522,采用28BYJ-48步进电机作为门锁控制器,4x4电容矩阵键盘用于密码输入,SPI接口的0.96寸OLED显示屏用于门锁状态信息显示,而ESP8266-WIFI模块则实现了联网功能。此外,红外磁感应模块用于检测门的状态。

    整个系统的设计使得门禁更加安全可靠,用户可以通过多种方式进行开锁操作,包括输入密码、刷卡以及远程开锁。同时,系统可以实时监控门锁状态,并在门锁打开或关闭时通过手机APP向用户发送通知提醒,提高了使用者的安全性和便利性。

    基于STM32F103和华为云物联网平台的智能门禁系统充分利用了现代科技的优势,提供了安全、便捷的门禁控制解决方案。除了基本的门禁功能外,该系统还具备远程控制和实时监控的能力,为用户带来更加智能化的门禁体验,并满足了现代人们对于安全、便捷生活的需求。

  • 相关阅读:
    LCR 012.寻找数组的中心下标
    Day12--渲染二级和三级分类列表
    【Linux】基本指令
    8.11-分析工具、8.12-数据字典、8.13-数据流图 8.14-设计工具
    配置vscode免密登录本地Ubuntu
    抑制细胞代谢紊乱的抑制剂
    RSS订阅
    数据结构-红黑树
    荐书 | 《考试脑科学》:这样学习才能事半功倍
    图像语义分割 pytorch复现U2Net图像分割网络详解
  • 原文地址:https://blog.csdn.net/xiaolong1126626497/article/details/133203624