免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

基于ESP32和Flutter的温湿度监控系统:从传感器到APP的物联网开发实践

基于ESP32和Flutter的温湿度监控系统:从传感器到APP的物联网开发实践 简介这是一份面向物联网IoT开发者与Android初学者的温湿度监控手机APP完整源码包覆盖DHT11/DHT22传感器数据采集、单片机处理、无线传输到手机端实时展示与LED控制的完整链路。压缩包共143个文件体量约3.98MB内部含有93张PNG界面素材、17个class编译结果、11个XML布局与项目配置、10个JAR第三方依赖库、3个Java核心源码、1个可直接安装的APK调试包以及工程配置文件等结构清晰可导入Android Studio运行。代码中详细演示了Android前端布局与图表可视化MPAndroidChart、后端RESTful API设计、MySQL/SQLite数据库存储、WebSocket实时刷新、HTTPS/OAuth2.0安全通信并涉及SPI/I2C/MQTT等物联网协议的实际协作同时保留LED开关控制与实时温湿度刷新等功能逻辑。目前已有1615人浏览学习特别适合需要参考完整IoT移动端项目架构的在校学生、软件开发者及竞赛团队。无论是学习硬件接入还是练习APP工程化开发都能从中找到对应参考。 做个温湿度监控手机APP的开发项目是我去年冬天被家里暖气逼出来的决定。客厅温度一直上不去体感冷了才去摸暖气片完全抓瞎。我先后买过两个几十块的温湿度计只能站在跟前看数字夜里温度最低是多少、湿度变化曲线根本查不到更别说在手机APP上远程回看了。后来索性自己动手从传感器到服务器再到手机端完整写了一套温湿度监控系统把整套开发代码沉淀了下来。今天把这套东西完整复盘一遍覆盖硬件采集、HTTP上报、Flutter APP展示、联调排错的全过程。适合刚接触物联网开发、想自己掌握数据而不是被厂商生态绑定的朋友也适合拿来做课程设计或毕业设计的项目骨架。1. 为什么自己搭一套温湿度监控系统而不是买现成设备1.1 现成温湿度计的两类典型痛点不到一百块的温湿度计绝大多数是本地LCD显示数据存在设备内部只能看当前值不能远程访问也没有历史曲线。贵一点的智能温湿度计虽然能连手机APP但走的是厂商自己的云数据格式不公开API更不开放你想把温湿度数据接到自己的服务端或者和家里的智能家居联动基本只能靠逆向和抓包维护成本高随时还可能被厂商停服变成砖头。我自己的需求很明确家里多个房间放采集节点数据必须落到自己的服务器上手机APP能看实时值、24小时曲线、最低最高温湿度后面还想加告警推送。这些需求用市面成品很难同时满足自己写一套反而是最稳妥的。算下来单个节点成本大概五十块以内开发板加传感器比一个联网温湿度计还便宜。1.2 自制系统的能力边界和整体数据流自己开发的好处不是省钱而是数据链路的每一环都可以控制。硬件端想换传感器就换传感器服务端想加接口就加接口APP想改UI就改UI。当然代价也很明确你需要同时碰嵌入式、服务端和移动端三个方向知识面要求比较宽。如果你只是想要一个能用的温湿度计直接买成品如果你想做的是“一套可扩展的环境监控系统”那自己写代码这条路值得走。整套系统的数据流非常朴素温湿度传感器采集数据交给ESP32单片机做简单处理然后通过WiFi以HTTP POST的方式上报到轻量服务端服务端把数据存进数据库并暴露查询接口手机APP定时拉取接口数据渲染成实时卡片和趋势曲线。这个链路里没有复杂消息队列也没有微服务个人项目最重要是先把链路跑通再去谈架构升级。1.3 先定接口再写代码这条经验救了我我一开始犯了个典型的错误先把ESP32端代码写完再回头写服务端最后才写APP结果三端的字段命名各写各的联调时改来改去浪费了大半天。后来我总结出一个固定流程先定义接口文档哪怕只是写在记事本里的几行JSON示例然后服务端先按这个接口mock返回APP端同时开发最后再让硬件端对接真实接口。三步并行但契约先行联调效率高非常多。接口只需要两个一个是POST /api/sensor用于ESP32上报温湿度一个是GET /api/latest?devicexxx用于APP查询某个节点的最新数据。上报的JSON格式如下{ device: living_room, temp: 23.5, humi: 45.2 }服务端返回{code:0}表示成功。这里device字段用来区分不同房间后面扩展多节点部署时全靠它。2. 硬件端采集与上报把温湿度变成手机APP能读的数据2.1 传感器选型SHT30比DHT22省心太多温湿度传感器我前后试过DHT22和SHT30两者的差别在实际使用中非常明显。项目DHT22SHT30温度精度±0.5℃±0.2℃湿度精度±2%~5% RH±2% RH接口单总线时序敏感I2C稳定采样间隔要求建议大于2秒可连续读取价格约10元约15~20元代码复杂度中需要时序库低直接读寄存器DHT22价格便宜但单总线协议对时序要求高换一个开发板或者线长一点就容易读取出错湿度受干扰也比较明显。SHT30是I2C接口接线就三根线VCC、GND、SDA、SCL加一根也行代码稳定得多。如果预算允许我建议直接上SHT30省下来的调试时间远超那几块钱差价。接线方面ESP32的默认I2C引脚是GPIO21对应SDA、GPIO22对应SCLSHT30模块的VCC接3.3VGND接GND。注意模块上的上拉电阻一般已经集成不需要额外处理。2.2 采样逻辑别把原始读数直接上报传感器刚上电时读数不稳定尤其是湿度前十几秒可能跳得离谱。我实测过SHT30刚通电时温度比正常值高两三度因为传感器自身通电后会轻微发热PCB板子测温需要时间稳定。所以采集代码里不能上电就读要等几秒让传感器稳定然后连续采样多次取平均值或中位值。我的做法是连续读5次、每次间隔100毫秒去掉最大值和最小值剩下三个取平均。这样处理之后读数平稳很多基本不需要额外做软件滤波。如果你对精度要求高可以在代码里留一个校准偏移量比如tempOffset -0.3把已知的系统偏差在最终读数里修正掉。2.3 ESP32采集上报代码参考下面是ESP32端基于Arduino框架的采集和上报代码我用的传感器库是Adafruit SHT31Wifi和HTTPClient用ESP32内置库#include WiFi.h #include HTTPClient.h #include ArduinoJson.h #include Wire.h #include Adafruit_SHT31.h Adafruit_SHT31 sht30; const char* ssid your_wifi; const char* password your_password; const char* serverUrl http://192.168.1.100:8080/api/sensor; const char* deviceName living_room; float readStableTemp() { float values[5]; for (int i 0; i 5; i) { values[i] sht30.readTemperature(); delay(100); } // 冒泡排掉最大最小值剩下取平均 for (int i 0; i 4; i) { for (int j 0; j 4 - i; j) { if (values[j] values[j 1]) { float tmp values[j]; values[j] values[j 1]; values[j 1] tmp; } } } return (values[1] values[2] values[3]) / 3.0; } void uploadSensorData(float temp, float humi) { HTTPClient http; http.begin(serverUrl); http.addHeader(Content-Type, application/json); StaticJsonDocument128 doc; doc[device] deviceName; doc[temp] temp; doc[humi] humi; char payload[128]; serializeJson(doc, payload); int httpCode http.POST(payload); if (httpCode ! 200) { Serial.printf(upload failed, code%d\n, httpCode); } http.end(); } void setup() { Serial.begin(115200); Wire.begin(21, 22); sht30.begin(0x44); WiFi.begin(ssid, password); while (WiFi.status() ! WL_CONNECTED) { delay(500); Serial.print(.); } delay(3000); // 上电稳定时间 } void loop() { float t readStableTemp(); float h sht30.readHumidity(); uploadSensorData(t, h); delay(30000); // 30秒上报一次够用了 }这里有几个细节说一下。delay(3000)放在WiFi连接成功之后是为了让传感器从供电波动中恢复稳定。上报周期30秒对家庭环境足够了太频繁反而会让服务端数据库迅速膨胀。如果你放在电池供电的场景上报周期可以拉长到5分钟甚至更长配合ESP32的深度睡眠续航能到几个月。2.4 为什么选HTTP轮询而不是MQTT方案选型的时候我犹豫过MQTT。MQTT是物联网设备通信的主流协议发布订阅模式实时性很高服务端能即时感知设备状态。但实际考虑下来个人项目的节点数量有限设备上报频率又低用HTTP POST简单直接调试也方便出问题用curl就能模拟设备端测试。MQTT需要额外搭建或依赖Broker还要处理主题设计、遗嘱消息、QoS等级这些概念时间成本会明显增加。APP端同理我用的是定时轮询接口每30秒或60秒拉一次最新数据。家庭环境下一分钟内的延迟完全感知不到没必要为了“实时”引入长连接和推送通道徒增复杂度。如果你的项目要求湿度突变时手机立刻弹告警那再用MQTT加WebSocket也不迟但一开始就把链路做简单永远是个人项目的第一原则。3. 手机APP端从拉取数据到绘制温湿度曲线3.1 技术栈选择我直接用Flutter跨平台方案到了手机APP这一层摆在面前的无非是Android原生、iOS原生、Flutter、uni-app这几个方向。我最后选了Flutter原因很简单一套Dart代码同时覆盖Android和iOS温湿度曲线用fl_chart库就能画不需要我在Java和Swift之间切换。而且Flutter的热重载对UI迭代很友好改完布局马上能看到效果调试体验比原生好不少。如果你只是自己用只装Android包那用Kotlin写原生也可以。但考虑到这套监控系统后面想分享给家人用iOS也得有安装包Flutter是时间成本最低的选择。我用的开发环境是Flutter 3.x稳定版IDE用VS CodeAndroid侧targetSdkVersion设的是33。要注意Flutter版本和Android Gradle插件的版本匹配Flutter升级之后如果不跟着升级gradle配置很容易在构建阶段报错。3.2 数据层Dio网络请求加本地缓存APP的数据层我用Dio做网络请求。Dio拦截器里统一设置超时时间避免弱网环境下请求长时间卡住。定义了一个SensorApi类集中管理接口调用代码结构清晰后面加接口也方便import package:dio/dio.dart; class SensorApi { static final Dio _dio Dio(BaseOptions( baseUrl: http://192.168.1.100:8080, connectTimeout: Duration(seconds: 5), receiveTimeout: Duration(seconds: 5), )); static FutureMapString, dynamic fetchLatest(String device) async { final resp await _dio.get( /api/latest, queryParameters: {device: device}, ); return resp.data as MapString, dynamic; } }这个接口返回的JSON长这样{ code: 0, data: { device: living_room, temp: 23.5, humi: 45.2, time: 2025-01-06 22:30:00 } }数据拿到之后不能直接扔我在APP里用shared_preferences把最近一次成功请求的数据缓存到本地。为什么要缓存实际场景里手机可能短暂断网或者服务端短暂不可用有了缓存APP打开时先渲染上一次的数据再静默刷新用户感知是页面永远有内容而不是白屏或者一个错误提示。这个体验细节在物联网APP里很重要因为设备端数据本来就有时间连续性展示旧一秒的数据没有风险。3.3 首页实时卡片和24小时曲线的实现首页UI不需要花哨核心是两大块一块是当前温湿度大数字卡片一块是24小时趋势曲线。卡片部分我直接用了一个Container加圆角背景温度用大字显示湿度排在下面代码不复杂就不占篇幅了。关键是趋势曲线我用fl_chart的LineChart来画。曲线部分的核心是把接口返回的时间点转成FlSpot坐标。x轴用时间戳epoch秒y轴用温度值或湿度值。温度曲线和湿度曲线的数值范围差很多温度可能只在15到30之间波动湿度最高能到80如果共用左Y轴温度变化会被压成一条平线。我处理方式是两条数据线分别配左右两个Y轴温度用左轴湿度用右轴这样两条曲线都能看清趋势。LineChart( LineChartData( lineBarsData: [ LineChartBarData( spots: tempPoints, // ListFlSpotx为时间戳y为温度 color: Color(0xFFFF9800), isCurved: true, dotData: FlDotData(show: false), barWidth: 2, ), LineChartBarData( spots: humiPoints, // ListFlSpotx为时间戳y为湿度 color: Color(0xFF03A9F4), isCurved: true, dotData: FlDotData(show: false), barWidth: 2, ), ], titlesData: FlTitlesData( leftTitles: AxisTitles(sideTitles: SideTitles(showTitles: true)), rightTitles: AxisTitles(sideTitles: SideTitles(showTitles: true)), topTitles: AxisTitles(sideTitles: SideTitles(showTitles: false)), bottomTitles: AxisTitles(sideTitles: SideTitles(showTitles: true)), ), ), )曲线做出来后有一个要处理的问题如果历史数据量很大比如一个月的数据全画上去FlSpot列表可能有几千个点线形图绘制会有明显卡顿。我的方案是只请求最近24小时的数据APP端拿到后再做降采样比如把5分钟的数据聚合成一个点这样24小时最多显示两百多个点手机上画起来毫无压力。3.4 后台刷新冷启动之外最重要的体验APP拉数据的时机我做了两层。第一层是页面可见时立刻拉一次覆盖用户切回APP的场景第二层是定时器后台刷新每60秒请求一次最新数据。这个频率对家庭监控足够数据曲线基本是连续的。Android上实现定时刷新有个坑进程可能被系统回收尤其是国产ROM的后台清理策略很激进。我用的是前台Service加Flutter的Timer.periodic组合方式。Service通过startForeground创建一个常驻通知让系统知道这个APP正在干活降低被回收的概率。如果你不想做常驻通知也可以退一步接受“打开APP时刷新一次”的体验看你自己的取舍。iOS侧因为后台限制更严格我的妥协方案是每次切入前台时从服务端补拉缺失时间段的历史数据而不是试图在后台保持长连接这在实际使用中完全够用。4. 联调阶段反复踩过的坑与定位过程4.1 Android 9之后默认禁止明文HTTP请求第一个坑来得特别快。APP端代码写完连上服务端一请求就直接抛异常错误信息指向cleartext traffic。这是因为targetSdkVersion 28及以上Android默认禁止HTTP明文流量只允许HTTPS。我本地开发环境用的是局域网IP加8080端口没有配HTTPS自然被拦。定位方法很简单抓日志看到CLEARTEXT communication to ... not permitted by network security policy基本就是这个问题。修复方法有两层最省事的是在AndroidManifest.xml的application节点加一行application android:usesCleartextTraffictrue ... 这样整个APP都允许明文HTTP请求。如果要更安全可以用networkSecurityConfig只允许特定域名走明文其他域名强制HTTPS。我的建议是内网测试阶段先用usesCleartextTraffic跑通链路后期上正式环境换成HTTPS再把这一行去掉。4.2 手机锁屏后数据不更新曲线出现断层跑通基础功能后我发现一个问题手机亮屏时数据正常刷新一锁屏过几分钟再打开曲线中间有一段空白。排查下来是Android的Doze省电模式在起作用。屏幕关闭后系统会限制网络访问和CPU调度定时器被延迟甚至暂停网络请求频率大幅降低这不是APP代码的bug而是系统的正常策略。解决方案我试过两种。最简单的是把刷新逻辑放到前台Service里同时给APP加电池优化白名单权限引导用户去系统设置里把APP设为“不受限制”。实测这个组合在多数手机上能让后台刷新稳定运行。更彻底的方案是用WorkManager做周期性任务但系统对周期任务的最小间隔做了限制最短也要15分钟对温湿度监控来说粒度太粗所以我最终采用了前台Service方案。如果你对实时性没这么敏感WorkManager其实更省电也更规范。4.3 传感器读数跳变温度持续偏高硬件端也有一个让我排查了很久的问题同一个房间里SHT30读到的温度比水银温度计高两度左右湿度偶尔还会突然跳高再恢复。刚开始我怀疑是传感器坏了换了一个新的还是同样现象。后来把传感器拿下来裸奔测试才找到原因我把传感器直接放在了ESP32开发板的塑料外壳里板子上的稳压芯片和WiFi模组工作时发热壳内空气不流通热量全被传感器吸收了导致读数虚高。湿度跳变则是空气流通不畅加上传感器表面可能残留助焊剂导致的。解决办法很朴素把传感器引出来用杜邦线连接到板子外部放在通风处不要贴着任何发热元件。改成外接之后温度读数稳定在合理范围湿度跳变也消失了。4.4 时间戳时区不一致导致曲线整体偏移服务端上线后APP曲线出现了一个很隐蔽的问题温度曲线整体平移了8个小时。我一开始以为是数据延迟后来发现是ESP32上报时自己生成了时间戳设备里用的是UTC时间APP端直接渲染出来没有转时区自然差了一个时区。定位清楚后就简单了处理原则是“设备端不生成业务时间服务端统一打点”。ESP32上报时只发设备和数据服务端收到后取当前服务器时间作为这条记录的时间戳。这样无论设备端时钟是否准确、无论设备在哪个时区数据的时间轴都统一和服务端一致。温湿度监控这种低频数据场景完全不需要设备端带时间戳越是简单的方案越不容易出错。4.5 真机调试常见的网络访问问题最后说一个调试环境的小坑。我用Android模拟器调试时APP里访问服务端的地址不能写127.0.0.1因为模拟器里的127.0.0.1指向的是模拟器自己要访问宿主机必须写10.0.2.2。真机调试时又换了新问题真机通过USB连接电脑虽然能装APK但手机的网络和电脑不在同一个网段直接访问电脑IP根本不通。我的解决办法是真机开启USB网络调试通过ADB把手机端口转发到电脑端口adb reverse tcp:8080 tcp:8080执行之后手机APP里访问http://127.0.0.1:8080就能连接到电脑上跑的服务端。这个技巧对局域网环境不稳定或者公司WiFi开了AP隔离的情况特别管用不用折腾网络配置USB插上就能联调。5. 实测效果、优化方向与项目心得5.1 整套系统的实测数据我在家里部署了三个采集节点客厅、卧室、阳台各一个跑了整整两周。用标准的温湿度计做参照SHT30的温度偏差基本在±0.2℃以内湿度偏差在±5%以内精度完全可以接受。三个节点上报成功率在99%以上偶尔一两次失败也是因为路由器重启。APP冷启动到显示曲线大概两秒主要耗时在服务端查询和网络传输页面加载时先出缓存数据再刷新体感上几乎没有等待。功耗方面ESP32以30秒为周期上报实测平均电流在90mA左右长期插USB供电没什么问题。如果改成电池供电我会把上报周期调成5分钟并且让ESP32在两次上报之间进入深度睡眠这样理论待机电流能降到几十微安一节18650电池用几个月不难。5.2 继续优化可以做的几件事这套系统的代码结构留好了扩展位后面有几个明显可以升级的方向。一是告警推送当某个房间温度超过设定阈值或者湿度低于某个值服务端调用服务器酱或邮件接口推送到手机。二是历史数据整理目前数据落在数据库里时间长了会变大可以写个定时任务只保留每小时的一条聚合数据原始数据留一个月。三是多用户支持如果家人也要看数据加一个简单的token鉴权就够了。关于低代码平台我也顺手提一句自己的感受如果你只是想要一个演示用的原型低代码拖拽生成一个温湿度大屏确实快但真到了生产环境数据的格式、采集频率、告警逻辑、设备管理这些细节还得靠正经代码一条条写清楚。低代码不是不能用而是边界要清晰原型验证可以拿它省时间长期维护的监控系统我不建议押在上面。5.3 写代码之外的一点个人体会这套项目做完我最深的感受是真正耗时间的不是某个端的具体代码而是把数据链路上每一环的“想当然”填平。硬件端的传感器发热、APP端的Android明文流量限制、服务端的时间戳统一每一个问题单拎出来都不难但串联起来就是开发周期的大头。希望这份复盘能帮你绕开这些坑让你把时间花在真正有意思的事情上比如把监控数据变成保护家人舒适生活的实用工具。本文还有配套的精品资源点击获取
返回列表