尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

微信小程序+ESP32+DHT11+LED 远程控制完整项目实战

微信小程序+ESP32+DHT11+LED 远程控制完整项目实战 简介这是一套面向物联网初学者与嵌入式开发爱好者的微信小程序ESP32软硬件协同实践资源聚焦智能家居控制场景解决温湿度远程监测与LED设备无线开关的核心需求。资源包共470个文件涵盖98个JavaScript逻辑文件含mqtt.min.js等通信模块、74个WXML页面结构、80个WXSS样式、73个JSON配置如project.config.json、app.json及95个TypeScript类型定义完整支撑小程序端开发313KB轻量级压缩包便于快速部署与学习。已有2431人下载学习资源结构清晰pages目录组织多级交互页面utils封装网络请求与数据处理dist为可直接上传的构建产物app.js与app.json实现全局生命周期与路由管理。读者可直接复用该架构快速搭建基于MQTT或HTTP协议的ESP32远程控制系统并深入理解传感器数据上云、小程序状态同步及硬件指令下发的全链路实现逻辑。 手头有个微信小程序远程控制硬件的老项目压缩包名叫“Wechat_esp32_DHT11_LED.zip”一直躺在移动硬盘里。最近整理文件翻出来发现当初踩过的坑、调通的代码、理清的思路还挺有价值。网上能搜到的资料大多是零散片段要么只讲ESP32端要么只讲小程序端很少有把一个完整闭环串起来讲的。所以我干脆花了点时间把整个项目重新梳理了一遍把从硬件原理图到小程序UI、从固件逻辑到联调排障的完整链路都写出来。这个项目说白了就是微信小程序作为人机交互层ESP32作为设备控制层DHT11负责采集温湿度LED灯作为被控执行器。整套系统跑通之后你在任何地方打开微信就能看到传感器数据手指点一下开关就能控制远端的灯亮灭。听起来简单但真正把这条链路打通需要跨好几个领域硬件电路设计、嵌入式固件开发、无线通信协议、小程序前端开发。我尽量按当初做项目的实际顺序来讲每一步都写清楚为什么这么做方便你直接照着复现。1. 项目拆解从压缩包名看整个系统的设计蓝图1.1 四个关键词背后的功能矩阵先看这个压缩包的名字Wechat_esp32_DHT11_LED。拆开看就是四个关键词Wechat微信小程序、esp32主控芯片、DHT11温湿度传感器、LED被控设备。这四个词正好对应一个典型物联网项目的四层架构应用层微信、网络层ESP32内置WiFi/蓝牙、感知层DHT11、执行层LED。我在最初规划这个项目的时候其实纠结过要不要把传感器和执行器都放在一个板子上。后来想通了这个项目的核心价值是验证“微信小程序-ESP32-传感器/执行器”这条控制链路所以DHT11和LED必须同时挂上缺一个都构不成完整的闭环。举个实际场景你可以在办公室里通过小程序查看家里的温湿度如果温度过高顺手远程把风扇或者灯光打开。这样DHT11和LED就形成了一个有实际意义的组合而不只是两个孤立的硬件模块。1.2 技术选型为什么是ESP32而不是Arduino Uno或STM32做过硬件的人都知道选主控芯片是项目启动的第一个决策点。我手上当时有Arduino Uno、STM32F103C8T6、ESP32三块板子最终选了ESP32原因很直接原生无线能力ESP32自带WiFi和蓝牙双模而Arduino Uno需要外接ESP8266模块或者HC-05蓝牙模块才能实现无线通信多一个模块就多一分调试负担。微信小程序正好支持BLE蓝牙ESP32的蓝牙功能可以直接和小程序对接不需要额外硬件。性能和内存ESP32是双核240MHz520KB SRAM4MB Flash跑蓝牙协议栈加传感器驱动加LED控制绰绰有余。STM32F103虽然性能也不错但同样需要外接蓝牙模块而且用标准库或者HAL库写蓝牙协议栈的适配层工作量明显更大。Arduino生态这是我选ESP32最核心的原因。ESP32在Arduino IDE下有非常成熟的支持DHT11这类传感器有现成的库BLE通信也有官方提供的BluetoothSerial和BLEDevice库上手门槛比STM32低很多。你不需要深入研究ESP-IDF底层的复杂机制也能快速搭出一个能跑的原型。3.3V逻辑电平DHT11和大多数LED模块都支持3.3V供电和逻辑ESP32的GPIO刚好全部兼容不需要做电平转换电路硬件设计简单不少。当然ESP32也不是没有缺点它的ADC线性度一般不适合做高精度模拟采集GPIO数量相比STM32同价位型号偏少。但对于这个项目来说DHT11只占用1个GPIOLED只用1个GPIO绰绰有余。1.3 通信方案取舍BLE蓝牙还是WiFi连接这是整个项目里我最纠结的一个选择因为微信小程序同时支持BLE和WiFi两种通信方式选错了后面会走很多弯路。我当时的分析逻辑如下BLE蓝牙方案的优缺点优点连接速度快功耗低ESP32在BLE模式下电流大概几十mA远低于WiFi的几百mA不需要外部服务器中转小程序直接和ESP32点对点通信数据不经过第三方隐私性好。缺点通信距离短空旷环境10-15米左右隔一堵墙就明显衰减只能在小程序主动打开的时候才能通信没法实现后台推送。另外微信小程序对BLE的操作有一堆平台限制比如iOS和Android的行为不一致需要在代码里做兼容处理。WiFi方案的优缺点优点通信距离远只要在同一局域网内数据可以通过服务器中转能够实现远程控制哪怕你在地球另一端只要ESP32联网了就能控制。缺点需要配置WiFi账号密码要么通过网页配网要么用SmartConfig对用户不够友好功耗高不适合电池供电而且如果要实现真正的跨网络远程控制需要自己搭MQTT服务器、HTTP服务器或者用微信云开发代码量和工作量会显著增加。我最终选了BLE原因很朴素项目刚起步时最怕的就是链路太长出了问题不知道怎么排查。BLE的链路最短小程序直接发指令给ESP32ESP32执行完直接回传数据。没有服务器、没有配网流程、没有路由器转发每一步都可以用手机和串口助手快速验证。如果一开始就上WiFi加MQTT加云开发调试成本至少翻三倍。先把BLE这条链路跑通后续再横向扩展成WiFi方案会容易很多。提示如果你想做的是真正的“随时随地远程控制”BLE确实不够用。建议先按本文把BLE链路跑通再在ESP32端加一个WiFi配网模块走MQTT方案小程序改用云开发。这样渐进式地做既不会一上来就被复杂架构劝退又能最终实现远程控制。2. 硬件设计DHT11测量电路与LED驱动电路的完整搭建2.1 DHT11引脚定义与上拉电阻的必要性DHT11是典型的单总线数字温湿度传感器只有3个引脚需要接线VCC供电、DATA数据、GND地线。我用的模块是四针版本多了一个NC空脚模块上已经帮我把上拉电阻和去耦电容焊好了但如果你用的是裸传感器就需要自己在电路里补上关键元件。先说上拉电阻。DHT11的数据线是漏极开路输出的这意味着它本身只能把电平拉低不能主动拉高。为了让数据线处于空闲状态时是高电平必须在DATA引脚和VCC之间接一个上拉电阻。电阻值一般选4.7kΩ到10kΩ之间。我常用10kΩ因为DHT11的通信速率本身不高最大20kbps10kΩ的上拉电阻响应速度完全够用而且功耗更低。如果你手头只有4.7kΩ也完全没问题这两个值在DHT11的数据手册里都是被允许的。接线方式如下VCC - 3.3V或者5VDHT11工作电压范围是3.3V到5.5VDATA - ESP32 GPIO4我选了GPIO4因为它在ESP32 DevKit上默认没被占用GND - GNDDATA和VCC之间接一个10kΩ电阻再说去耦电容。任何数字芯片在电平翻转的瞬间都会产生电流尖峰如果供电线阻抗偏高这个尖峰就会引起电源电压跌落轻则数据出错重则芯片复位。所以DHT11的VCC和GND之间应该接一个100nF0.1uF的陶瓷电容靠近DHT11的供电引脚放置。这会显著提升读取的稳定性。我自己第一次搭电路的时候没加这个电容结果就是数据偶尔会读出一个固定的错误值“255”排查了很久才意识到是供电不稳导致的。2.2 LED驱动电路设计限流电阻计算与三极管开关LED在硬件里看起来最简单实际上翻车最多。核心问题就是ESP32的GPIO输出能力有限不能直接驱动大功率LED而且LED本身是电流型器件必须串联限流电阻否则会烧毁。ESP32的GPIO在3.3V高电平输出时的最大拉电流大约在12mA左右灌电流低电平吸收电流能做到更高一些但也不建议超过20mA。这个项目里我用的是一颗普通5mm直插红色LED额定工作电流10mA正向压降约1.8V。限流电阻的计算公式是R (VCC - V_LED) / I_LED (3.3V - 1.8V) / 0.01A 150Ω所以串联一个150Ω的电阻就能把电流限制在10mA左右。电阻的功率是P I² * R 0.01² * 150 0.015W远小于常见的1/8W电阻额定功率所以不用考虑散热问题。实际焊接时我用了220Ω的电阻因为手边刚好有算下来电流只有6.8mA亮度稍微暗一点但完全够用。如果你要驱动的是功率更大的LED灯珠比如1W以上的那就不能用GPIO直接带了。1W灯珠的工作电流是300mA左右远超GPIO能力需要加一个三极管或者MOSFET做开关。以最常见的NPN三极管S8050为例接法是这样的GPIO通过1kΩ电阻接三极管基极发射极接地集电极接LED的阴极LED的阳极通过限流电阻接5V电源。GPIO输出高电平时三极管导通LED点亮GPIO输出低电平时三极管截止LED熄灭。这个电路的本质是用小电流控制大电流GPIO只提供毫安级的基极电流真正的负载电流由电源通过三极管提供。2.3 原理图绘制与PCB设计的几个实用经验这个项目我用嘉立创EDA画的原理图和PCB毕竟免费又方便直接下单打样。画原理图时有几点经验值得分享DHT11的元件库嘉立创EDA的元件库里直接搜“DHT11”有两类封装一类是裸传感器四针直插另一类是模块三针或四针带排针。如果你打算直接用模块选带排针的那种就行如果想把传感器集成到PCB上选裸传感器封装需要自己补上10kΩ上拉电阻和100nF电容。LED限流电阻不要忘很多人画原理图时LED直接接GPIO等画PCB时才发现没有位置放电阻。嘉立创EDA的电路检查ERC功能会提示未连接的引脚或者网络短路但不会主动帮你检查“LED有没有串电阻”这种逻辑问题需要自己养成熟练的审查习惯。电源走线加宽ESP32在WiFi开启的瞬间电流会冲到几百mA如果电源线走得太细压降会导致ESP32重启。我一般把电源线的宽度设置成1mm以上信号线用0.3mm就够。引出调试接口就算你的PCB做得很小很紧凑也建议把ESP32的EN、GPIO0、TX、RX、3V3、GND这几个引脚引到排针上。理由很简单ESP32烧录需要进入下载模式GPIO0拉低后复位而且你几乎100%会在联调时需要用串口打印日志排查问题。没有调试接口每次烧录都要飞线心态会崩。PCB布线的时候注意一点DHT11的数据线尽量短不要和电源线或者LED驱动线平行走得太长。虽然DHT11是低速设备但数据线一旦受到干扰读回来的数据就会间歇性错误而且这种错误非常难排查因为频率不高容易让人觉得是程序逻辑的问题。如果布线空间实在紧张可以在数据线上加一个10pF到33pF的对地电容做滤波能有效减少毛刺。3. 开发环境搭建Arduino IDE ESP32离线包避免被网络问题劝退3.1 选择Arduino框架而不是ESP-IDF的理由ESP32的原生开发框架是乐鑫官方的ESP-IDF功能强大、性能极致但学习曲线确实陡峭。你需要面对的是命令行工具链、menuconfig配置、分区表设计、CMake构建系统……这些对于资深嵌入式工程师来说没问题但对于一个主要目标是想快速验证“微信小程序控制ESP32”的项目来说成本太高了。Arduino框架的价值在于抽象和封装。它把WiFi、蓝牙、GPIO、I2C、SPI等等底层接口都封装成统一的API让你不用关心寄存器配置和中断向量表。DHT11有专门的库BLE有现成的库。我可以在半天内把ESP32端的固件框架搭出来把时间花在真正需要思考的通信协议和逻辑设计上而不是陷在底层驱动里。当然Arduino框架也不是没有缺点代码执行效率低一些内存占用高一些对底层资源的精细控制能力弱一些。但对于DHT11低速传感器和LED简单的开关控制来说这些缺点完全不影响使用反而换来的是开发效率的大幅提升和小白友好的调试体验。做项目要分清主次能用现成的轮子就绝对不重复造轮子。3.2 Arduino IDE版本选择与ESP32离线包安装我用的是Arduino IDE 2.x版本界面比1.x现代很多自带串口监视器、自动补全和代码格式化功能用了就回不去1.x了。但2.x也有一个让人抓狂的问题国内网络环境下安装ESP32开发板包经常失败或者速度慢到让人怀疑人生。解决办法就是使用离线安装包。网上有不少热心人整理好的ESP32离线包核心思路是把原本需要从乐鑫GitHub仓库下载的所有工具链和库打包成压缩包解压后直接放到Arduino的硬件目录里跳过在线下载这一步。具体操作步骤是先安装好Arduino IDE 2.x并正常打开一次让它生成默认的配置目录。下载ESP32离线包我用的版本是3.x对应Arduino IDE 2.x压缩包解压后是一个名为“esp32”的文件夹。把这个文件夹放到Arduino硬件目录下Windows一般是C:\Users\你的用户名\Documents\Arduino\hardware\espressif\如果没有这个路径就手动创建。重启Arduino IDE在“开发板管理器”里你就能看到ESP32相关的板卡已经出现了不用再点安装按钮。这里有个容易踩的坑Arduino IDE 2.x的软件包目录和1.x不一样。1.x的硬件目录在Arduino\hardware下面2.x则默认使用Documents\Arduino\hardware。如果你以前装过1.x的ESP8266或者ESP32包别把它们混在一起两个版本的包最好不要共享同一个目录否则容易造成库文件版本冲突。3.3 板卡选择与首次烧录排查在“工具”菜单里选择板卡时我选的是“ESP32 Dev Module”因为这是最通用的选项。如果你用的是ESP32-S3就要选“ESP32S3 Dev Module”。注意这里的板卡型号和芯片型号要严格对应选错了会导致编译失败或者烧录进去不工作。首次烧录时最容易遇到的问题是“A fatal error occurred: Failed to connect to ESP32: Wrong boot mode detected”。这个错误的意思是ESP32没有进入下载模式。解决办法很简单按住开发板上的BOOT按键不放然后点击Arduino IDE的上传按钮等串口输出显示“Connecting…”再松开BOOT按键。如果开发板没有BOOT按键比如一些非官方的最小系统板就需要外接导线把GPIO0接地然后按一下EN引脚复位。进入下载模式的条件就是GPIO0为低电平且芯片被复位。我把烧录时常用的几种模式整理成表格方便你对照模式GPIO0状态操作方式用途下载模式低电平按住BOOT再复位或GPIO0接地后复位烧录程序运行模式高电平正常上电/复位运行已烧录的程序日志输出任意正常运行连接串口即可查看调试信息烧录成功后建议先在ESP32端写一个最简单的Blink示例程序让板载LED每隔1秒闪烁一次。这个程序能同时验证三件事芯片是否正常工作、上传流程是否正确、串口打印是否连通。如果这一步都没问题再开始做DHT11和BLE的代码排查问题时会轻松很多。4. ESP32固件开发DHT11读取、LED控制与BLE通信的核心逻辑4.1 DHT11驱动库选择与数据校验机制DHT11的驱动程序在网络上有非常多版本我最终选的是Adafruit官方维护的DHT sensor library配合Adafruit Unified Sensor库一起使用。选这个库的理由是代码维护活跃、接口统一、对DHT11/DHT22/DHT21等多种传感器都兼容。安装方式很简单Arduino IDE的“库管理器”搜索“DHT sensor library”安装Adafruit的版本它还会自动安装依赖的Adafruit Unified Sensor库。DHT11的读取逻辑本身其实不复杂主机发送一个开始信号DHT11响应后通过单总线协议返回40位数据。这40位由5个字节组成格式是8位湿度整数部分 8位湿度小数部分 8位温度整数部分 8位温度小数部分 8位校验和。校验和的计算方法是前四个字节相加取低8位如果等于第五个字节说明数据有效。代码实现上Adafruit库已经帮你处理好了这些底层的时序逻辑你只需要调用dht.readTemperature()和dht.readHumidity()就能拿到数据。但有个关键点必须注意DHT11的采样频率是1Hz也就是说两次读取的时间间隔必须大于1秒。如果你频繁调用读取函数DHT11会一直忙着响应上一次的请求导致返回NAN无效数据。我实际测试下来读取间隔设在1.5秒到2秒最稳。4.2 LED控制的两种模式开关控制与PWM调光LED控制在这类项目里看起来是最简单的部分但深入做下去也有一些设计空间。最基础的实现是直接用digitalWrite(ledPin, HIGH)点亮、digitalWrite(ledPin, LOW)熄灭这是最简单的开关控制适合验证通信链路。但如果想让体验更好可以做PWM调光控制通过改变PWM占空比来调节LED亮度。ESP32的PWM实现和传统Arduino的analogWrite()不同ESP32使用LEDCLED Control外设需要先配置通道channel和频率再设置占空比。核心代码大概是ledcSetup(0, 5000, 8); // 通道05kHz频率8位分辨率0-255 ledcAttachPin(ledPin, 0); // 将ledPin绑定到通道0 ledcWrite(0, 128); // 设置占空比50%半亮状态这里一个重要参数是PWM频率。人眼对低频率PWM的闪烁非常敏感一般500Hz以上就看不出闪烁了。功率LED的驱动频率通常要做到1kHz以上因为频率太低会导致LED出现可感知的频闪。8位分辨率0-255对于这个项目来说足够细腻了如果你需要更平滑的调光效果可以改成12位分辨率0-4095。在选LED控制引脚时有一个容易忽视的细节ESP32的GPIO并不是所有引脚都适合做PWM输出。GPIO34、GPIO35、GPIO36、GPIO39这几个引脚是输入模式不能用ledcAttachPin()绑定。我第一次做的时候把这几个引脚用作LED控制结果一直亮不起来查了引脚定义才恍然大悟。所以选引脚前一定要先查ESP32的引脚功能表确认GPIO支持数字输出和LEDC功能。4.3 BLE通信核心GATT服务与特征值的合理设计BLE通信是整个项目里技术含量最高的部分也是微信小程序和ESP32之间能正常对话的关键。BLE通信的核心是GATTGeneric Attribute Profile协议它的模型可以理解为服务Service包含特征Characteristic特征值就是实际传输数据的载体。把数据看作文件的话服务就是文件夹特征值就是文件。在这个项目中我定义了1个Service和2个CharacteristicService UUID:0000FFE0-0000-1000-8000-00805F9B34FBCharacteristic 1控制LEDUUID0000FFE1-0000-1000-8000-00805F9B34FB属性为Write可写用于接收微信小程序发来的LED控制指令。Characteristic 2读取温湿度UUID0000FFE2-0000-1000-8000-00805F9B34FB属性为Notify通知用于ESP32主动向小程序推送温湿度数据。Service和Characteristic的UUID可以用标准蓝牙SIG定义的但为了项目独立性和避免和小程序端的系统自带服务冲突我用的是自定义16位UUID前缀补足标准的128位UUID格式。在微信小程序里要精确匹配这个UUID省去扫描多个服务再筛选的麻烦。ESP32端初始化和处理BLE回调的核心代码结构如下#include BLEDevice.h #include BLEServer.h #include BLEUtils.h #include BLE2902.h BLECharacteristic *pCharacteristic; class MyCallbacks: public BLECharacteristicCallbacks { void onWrite(BLECharacteristic *pCharacteristic) { String value pCharacteristic-getValue(); if (value.length() 0) { if (value[0] 1) { digitalWrite(ledPin, HIGH); } else if (value[0] 0) { digitalWrite(ledPin, LOW); } } } }; void setupBLE() { BLEDevice::init(ESP32_DHT11_LED); BLEServer *pServer BLEDevice::createServer(); BLEService *pService pServer-createService(0000FFE0-0000-1000-8000-00805F9B34FB); pCharacteristic pService-createCharacteristic( 0000FFE1-0000-1000-8000-00805F9B34FB, BLECharacteristic::PROPERTY_WRITE ); pCharacteristic-setCallbacks(new MyCallbacks()); pCharacteristic-addDescriptor(new BLE2902()); BLECharacteristic *pNotifyCharacteristic pService-createCharacteristic( 0000FFE2-0000-1000-8000-00805F9B34FB, BLECharacteristic::PROPERTY_NOTIFY ); pNotifyCharacteristic-addDescriptor(new BLE2902()); pService-start(); BLEAdvertising *pAdvertising pServer-getAdvertising(); pAdvertising-addServiceUUID(pService-getUUID()); pAdvertising-start(); }这里有两个配置必须注意。第一个是BLE2902描述符如果特征属性是Notify就必须添加BLE2902描述符否则手机端无法订阅通知。第二个是广播设置addServiceUUID()可以让ESP32的广播包里包含Service的UUID信息这样微信小程序在扫描附近蓝牙设备时可以直接通过UUID过滤出这个设备不需要扫描完所有设备再逐个连接查看。在消息协议的格式上我最初用的是纯文本协议比如“1”和“0”因为简单直观。联调跑通之后我把它换成了简短的JSON格式控制指令是{cmd:led,state:1}温度上报是{temp:26.5,hum:56.8}。这样做的原因是项目后续扩展性更好比如要加OLED显示或者再加几个传感器每个指令都能在JSON里增加字段而不用改协议结构。BLE传输的数据量很小JSON多出来的几个字节完全在可接受范围内。如果你追求极致性能可以定义紧凑的字节数组协议但对于一个传感器数据量级的项目JSON的清晰易读带来的收益远大于那一点点带宽开销。4.4 温湿度上报策略与防抖设计温湿度传感器读到的数据什么时候推送给小程序这是一个看似简单但实际影响很大的设计决策。我当时有两种方案一是小程序主动请求时立即上报二是ESP32定时主动推送。第一种方案实现简单但问题是每次获取数据都需要小程序发一条指令延迟取决于请求频率而且频繁发指令也会增加功耗。第二种方案更优雅ESP32每隔2秒读取一次DHT11如果数据发生变化或者每隔固定时间就通过Notify特征主动推送到小程序。小程序的蓝牙接收端做监听处理收到数据就刷新UI。我最终采用的方案是第二种的变体每隔5秒推送一次的定时推送 温度或湿度变化超过1个单位时立即推送。这样既保证了数据的实时性又避免了无意义的数据轰炸。而且防抖设计在这里很重要DHT11的数据在有效范围内虽然有波动但不至于每次读取都有明显变化如果每次读取都推送小程序端的UI刷新会抖动得很难看。加一个变化阈值判断界面就稳定多了。5. 微信小程序端开发蓝牙控制与数据展示的完整链路5.1 小程序框架选择与项目结构搭建微信小程序端我用的原生框架没有引入uni-app或者Taro。原因很简单这个项目的核心是验证BLE通信链路用原生框架能最快找到微信官方蓝牙API的对应关系遇到问题也更容易搜索到解决方案。如果你要跨端复用代码再考虑后来引入跨端框架。小程序项目结构主要包含四大块页面文件WXML、样式文件WXSS、逻辑文件JS、配置文件JSON。页面结构上我做了两个tab一个“控制中心”显示温湿度数据和LED开关一个“设备连接”负责扫描、连接、断开蓝牙设备。在小程序的app.json里有一项配置非常重要permission字段。如果你的小程序涉及蓝牙功能必须在JSON里声明permission: {scope.bluetooth: {desc: 用于连接和控制ESP32蓝牙设备}}同时在调用蓝牙API之前向用户发起权限请求。不写这个声明在iOS上会直接导致蓝牙API调用失败并报错“getAppAuthorizeSetting:fail”。5.2 BLE连接流程从扫描到订阅通知的完整步骤微信小程序的BLE操作API设计得相当清晰但流程比较长容易踩坑。我整理了一遍完整流程每个步骤尽量说清楚它要解决的问题第一步初始化蓝牙适配器wx.openBluetoothAdapter({ success: () { this.startScan(); }, fail: (err) { // 常见错误码10001表示未初始化蓝牙10002表示用户未授权 } });这一步是让手机蓝牙硬件进入可操作状态。注意wx.openBluetoothAdapter需要用户在微信里授权“使用蓝牙”权限如果用户拒绝了需要通过弹窗引导用户去设置页开启。第二步扫描附近蓝牙设备wx.startBluetoothDevicesDiscovery({ allowDuplicatesKey: false, interval: 1000, success: () { // 在onBluetoothDeviceFound回调中获取设备按名称过滤 } });扫描设备时不要在每个回调里直接处理而是在wx.onBluetoothDeviceFound监听回调里做过滤判断。根据ESP32广播的名称“ESP32_DHT11_LED”直接过滤能大幅减少干扰设备的数量。扫描到设备后记录下deviceId这是蓝牙设备地址停止扫描然后发起连接。第三步连接设备并获取服务和特征值wx.createBLEConnection({ deviceId, timeout: 10000, success: () { wx.getBLEDeviceServices({ deviceId, success: (res) { // 遍历res.services找到自己定义的Service UUID }}); }});连接成功后需要先获取设备上所有的Service列表然后在里面找到自己定义的Service UUID。找到Service之后再获取这个Service下面的所有特征值列表找到Write特征和Notify特征。这一步容易出错的地方是getBLEDeviceCharacteristics方法需要传入serviceId参数这个参数必须是你从getBLEDeviceServices返回结果中取到的标准UUID字符串大小写要和ESP32端定义的完全一致。第四步启用Notify监听wx.notifyBLECharacteristicValueChange({ deviceId, serviceId, characteristicId: notifyCharacteristicId, state: true, success: () { // 在onBLECharacteristicValueChange回调中接收温湿度数据 } });state: true表示启用通知监听这样ESP32主动推数据时微信小程序端的onBLECharacteristicValueChange回调就能收到。这个回调里拿到的value是一个ArrayBuffer需要先转成字节数组再拼接成字符串最后解析JSON。第五步发送控制指令wx.writeBLECharacteristicValue({ deviceId, serviceId, characteristicId: writeCharacteristicId, value: this.stringToBuffer({cmd:led,state:1}), success: () { wx.showToast({ title: 指令已发送, icon: success }); } });wx.writeBLECharacteristicValue同样要求value是ArrayBuffer类型所以需要把字符串转成ArrayBuffer。这里有一个微信小程序的经典限制每次写入数据最大长度是20字节超过会被截断。我第一次提交的JSON指令长度超过了20字节结果ESP32端接收到的数据不完整指令解析失败。解决方法是把短的指令放在一个20字节以内的二进制格式里或者用分段写入的方案。最简单的办法是精简指令字段把{cmd:led,state:1}压到{c:l,s:1}或者直接用1和0两个字符传输定义好对应关系。我最终采取的是20字节以内的二进制格式既简洁又不会触发长度限制代价是协议可读性差一些所以我在代码里加了一条很详细的注释说明每个字节的含义。5.3 温湿度实时展示与LED开关的UI设计UI设计上我没有用花哨的图表库而是用原生的WXML和WXSS画了一个简洁的控制面板顶部两个大卡片显示温度和湿度中间是一个大大的圆形开关按钮控制LED。温度数值的刷新逻辑就是监听onBLECharacteristicValueChange回调收到的数据解析成JSON后直接setData到页面变量上。这里有个细节微信小程序setData的频率过高会导致页面卡顿。ESP32每5秒推送一次温湿度数据按理说不会卡但如果你在回调里做了大量字符串拼接或者页面动画就可能会丢帧。我实测下来发现直接在回调里用setData更新温度文本是没问题的但如果在回调里同时触发一个跑马灯动画FPS就会明显下降。所以尽量保持setData的数据量小、频率低。LED开关按钮我用了微信小程序的switch组件绑定bindchange事件切换时发送对应的控制指令。为了让用户有即时的操作反馈我会在点击按钮后先本地乐观更新UI状态等收到ESP32的确认消息或者超时再纠正状态。这样做的好处是界面响应快缺点是如果ESP32没接收到指令UI状态会短暂地与实际不符但可以通过一个2秒的定时器来超时回滚挽回体验。5.4 断线重连与异常状态处理实际使用中蓝牙连接不可能100%稳定。手机走远了会断开ESP32断电会断开微信小程序退到后台再回来也可能断开。如果不做断线重连用户每次都要重新扫描设备并手动连接体验会差很多。我的处理策略是监听wx.onBLEConnectionStateChange回调一旦发现连接断开弹出一个Toast提示“设备已断开”同时页面状态自动切换到“已断开”模式——LED开关按钮变成灰色不可点击温湿度数据显示为上次的值并标注“离线数据”。然后启动一个定时器每3秒自动尝试重连一次重连成功后清除定时器页面自动恢复到正常状态。重连逻辑的代码不复杂但要注意一个细节重连之前必须重新打开蓝牙适配器因为某些Android机型在连接断开后适配器也会自动关闭。如果不重新调用wx.openBluetoothAdapter就直接createBLEConnection大概率会失败。6. 联调实战问题排查链路、避坑指南与稳定性优化6.1 联调第一道坎DHT11读数始终为NAN我是在所有代码都写完、信心满满地打开串口监视器和微信小程序准备看数据的时候遭遇了第一次暴击串口打印的温湿度一直显示“Nan”和“nan”。排查过程是这样的第一步检查接线。用万用表测量DHT11的VCC和GND两端电压是3.3V正常。DATA引脚和GPIO4之间的通断正常。第二步排除供电问题。我前面提到过DHT11这种传感器对供电纹波比较敏感于是我在VCC和GND之间焊了一个100nF陶瓷电容再测还是NAN。第三步回到代码层面。在初始化代码里我的读取间隔设在了10秒理论上没问题。然后我用Adafruit库自带的例子测试发现一个细节dht.begin()之后需要等待至少250ms才能进行第一次读取如果立即读取返回的就是无效数据。我之前在setup()里调用了dht.begin()然后马上在loop()里读取正好踩了这个时序坑。修正方法是在begin()之后加一个delay(300)然后所有读取都至少间隔2秒。改完之后温度数据稳定输出了。这个问题的本质是DHT11上电后需要一段稳定时间才能正确响应数据请求。很多库的示例程序没有强调这点但实际使用时必须考虑。6.2 联调第二道坎微信小程序扫描不到ESP32设备代码写好了硬件接线也没问题小程序端却怎么都扫描不到“ESP32_DHT11_LED”这个名字的设备。排查过程如下第一步用手机系统自带的蓝牙设置页查看附近设备果然能看到“ESP32_DHT11_LED”。这说明ESP32的BLE广播本身是正常的问题出在小程序端。第二步检查小程序端的扫描代码。我开始怀疑是wx.openBluetoothAdapter后的时序问题蓝牙适配器初始化需要几十毫秒如果在适配器完全初始化之前就调wx.startBluetoothDevicesDiscovery扫描可能不正常。于是我在完成回调里加了一个延迟再启动扫描。第三步还是不行。继续检查发现可能是权限和配置的问题。我在手机上查看微信小程序的设置确认蓝牙权限已经打开。然后我重新审视了一个关键点iOS和Android对蓝牙扫描的行为不一样。iOS上如果你的小程序没有在app.json中声明permission.scope.bluetooth系统会直接拒绝蓝牙访问Android上则是在运行时需要动态申请权限。我在JSON里补全了权限声明同时在调用蓝牙API前用wx.authorize请求授权。三步走完之后扫描正常设备出现了。其实这个问题的主要原因是权限声明缺失加一行JSON配置就好了但很多人没注意到就会卡很久。6.3 联调第三道坎数据能收到但偶尔出现乱码基本链路打通后我发现温湿度数据偶尔会出现乱码比如温度显示成“-24.5”湿度显示成“103”。这种间歇性的问题最折磨人因为不是每次都出现很难定位。我先是怀疑BLE通信丢包。BLE协议栈底层有完善的错误检测机制理论上单次通知的数据如果校验失败会在链路层重传应用层收到的数据应该是完整的。所以丢包导致乱码的可能性不大。然后我怀疑是小程序端的ArrayBuffer转字符串逻辑有问题。BLE传输的数据是UTF-8编码的如果我在拼接过程中对不完整的数据做了String.fromCharCode转换就可能出现乱码。仔细检查发现我的Notify回调接收到的value可能是分帧的某次推送的数据可能超过BLE底层单帧大小被拆成多帧到达。而我的代码每次收到一帧数据就立即解析导致解析到半截JSON就报错了。解决方案是在Notify回调里做数据拼接维护一个接收缓冲区每次收到数据先追加到缓冲区然后尝试用JSON.parse解析如果能解析成功就处理数据并清空缓冲区解析失败就继续等待下一帧。这样就保证了数据包完整性。6.4 功耗与稳定性优化让项目真正可以长期运行联调跑通、功能全部正常之后我开始考虑这个项目如果长期运行会遇到什么问题。毕竟不能让它像实验室玩具一样只能跑几分钟。首先是功耗问题。ESP32在BLE连接保持状态下电流大约在30mA到50mA之间如果全部用默认配置时间久了还是有一定的功耗负担。可以做的优化包括启用ESP32的Modem Sleep模式在保持BLE连接的同时降低功耗关掉不需要的外设比如把ADC和触摸外设关掉降低BLE广播间隔在已连接状态下广播数据本身是无关的。不过需要注意的是Modem Sleep模式对BLE连接有一定影响如果设置不当会导致连接不稳定需要在实际测试中找到合适的平衡点。其次是看门狗问题。ESP32默认是开启任务看门狗的如果你的主循环里有耗时过长的阻塞操作看门狗超时后会触发系统重启。DHT11的读取本身不耗时但如果在错误的状态下读取比如DHT11掉线库会重试多次最长可能阻塞几十毫秒。正常情况下没问题但如果你在同一个循环里做了太多事情就有可能触发看门狗。我在实际测试中曾经因为在一个循环里同时做DHT11读取和BLE广播设置两个操作叠加导致偶尔看门狗复位。解决方法是把BLE广播设置放在初始化阶段完成不在主循环里频繁修改广播数据。第三是数据稳定性的长期测试。DHT11在潮湿环境下的读数是否准确、在长引线条件下是否会因为线路电容影响时序这些都需要在实际摆放位置做长时间测试才能知道。我后来做的一个比较有价值的优化是连续读取三次DHT11数据取中位数作为最终上报值有效滤除了偶发的异常值。代价是每次上报前多花几百毫秒但对于监控场景完全可以接受。6.5 扩展思路从BLE到WiFi远程控制的升级路径BLE方案跑通之后整个项目算是一个完整的闭环但只要用过一次就会发现它有个天然边界手机必须离ESP32足够近。如果你想真正实现远程控制就需要往WiFi方案扩展。我已经理清了一条可行的升级路径在这里分享给大家参考。第一种方案是局域网内控制。ESP32连接家里路由器小程序也连接同一个WiFi通过HTTP请求给ESP32发送指令。实现简单但只能在家里的网络环境下用。第二种方案是MQTT加云服务器。ESP32连接MQTT Broker比如EMQX自建或者用阿里云、腾讯云的物联网套件小程序端通过微信云开发的云函数调用MQTT API发布指令。这是目前做物联网远程控制的主流方案跨网可用但需要自己搭一套服务端维护成本高一些。第三种方案是微信小程序云开发加HTTP隧道。ESP32定期请求云函数获取指令云函数同时提供API让小程序下发指令。这个方案不需要长连接和MQTT协议逻辑简单但实时性差一些如果你要控制LED灯这种对响应速度有一定要求的设备体验会差一点。如果你是跟着这篇文章一步步做下来的我强烈建议在完善了BLE方案之后再挑战一下MQTT方案你会发现两者在架构上的差异非常大收获也会完全不同。7. 写在最后的经验沉淀这个项目带给我的几件事项目做到这里代码可以交付Demo可以演示但真正让我觉得有价值的是这段经历沉淀下来的几个认知最后拿出来和大家聊聊。第一把一个完整闭环做通比做很多个单点小实验更有意义。以前我也玩过很多硬件模块每个模块单独点亮的时候成就感满满但它们之间是割裂的。这次从DHT11到LED到BLE到小程序每个环节都是下一个环节的输入任何一个点断了整个系统就瘫痪。这种系统性的思考方式是任何单点教程都教不了你的。第二遇到问题别急着翻重写先想想信息链路里哪一环丢了。按照这次的实战经验硬件项目最常见的崩溃点不在“代码逻辑”而在“时序、电平、供电、协议匹配”这类底层细节上。每次报错都去检查这几个方面比盲目改代码有效率的得多。第三小项目的成功关键在于控制变量。我这次深有体会你不可能在一个晚上同时排掉所有的问题。最好的方式是先在串口监视器里确认DHT11读数正常再让微信小程序能够扫描到ESP32再让二者通信成功。每一步都有明确的验收标准缩小排查范围问题定位自然就快了。最后再分享一个小技巧ESP32端一定要留一个串口打印日志的接口。联调的时候把收到的每一条BLE指令、每一次温湿度上报、每一次异常事件都打印出来。没有日志你会像盲人摸象一样在黑暗里摸索有了日志问题基本上能在十分钟内定位到原因。后续我还计划在这个基础上加一个OLED显示屏让ESP32本地就能显示温湿度同时把数据通过MQTT上报到云端记录历史曲线。如果你也在做类似的项目欢迎在社区里找我交流讨论毕竟一个人闷头做项目很容易撞在同一个坑里。本文还有配套的精品资源点击获取
返回列表