当前位置: 首页 > news >正文

DIY-ESP8266移动PM2.5传感器-带屏幕-APP

本教程将指导您制作一台专业级的空气质量检测仪。这个项目使用经济实惠的ESP8266和PMS5003传感器,配合OLED显示屏,不仅能实时显示PM2.5数值,还能通过手机APP随时查看数据。总成本70元,相比几百的用的便宜,用的心理踏实。

功能特点

  • 实时检测PM2.5和PM10浓度
  • OLED屏幕直观显示数据
  • WiFi无线连接,支持远程数据查看
  • 手机APP实时监控
  • 支持数据历史记录和趋势分析
  • 24小时持续监测

成品
在这里插入图片描述

网页
在这里插入图片描述

APP
在这里插入图片描述

硬件需求

[upl-image-preview url=https://forum.thingspanel.cn/assets/files/2024-12-20/1734656701-50998-image.png]

ESP8266开发板(如NodeMCU、Wemos D1 mini等)10元
PMS5003/PMS7003系列颗粒物传感器 50元
0.96寸OLED显示屏(I2C接口,128x64分辨率)9元
若干杜邦线

接线说明

PMS传感器接线(5V供电):

VCC → ESP8266的VIN(5V)
GND → ESP8266的GND
TX → D6(GPIO12)
RX → D5(GPIO14)

OLED显示屏接线:

VCC → 3.3V
GND → GND
SDA → D2(GPIO4)
SCL → D1(GPIO5)

准备工作

Arduino IDE配置:

安装ESP8266开发板支持
安装必要的库:

WiFiManager(用于WiFi配置)
PubSubClient(MQTT客户端)
Adafruit GFX和Adafruit SSD1306(OLED驱动)
ArduinoJson(JSON数据处理)

使用说明

首次使用:

给设备上电后,会创建一个名为"AP"的WiFi热点
用手机连接该热点
在弹出的配置页面中设置WiFi信息
设备会自动连接配置好的WiFi

正常使用:

设备每3秒更新一次显示屏数据
每10秒向MQTT服务器发送一次数据
OLED屏幕显示实时PM2.5数值

代码

#include <ESP8266WiFi.h>
#include <WiFiManager.h>
#include <PubSubClient.h>
#include <SoftwareSerial.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <ArduinoJson.h>// OLED显示屏设置
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);// PMS传感器串口设置
#define PMS_RX 12  // D6
#define PMS_TX 14  // D5
SoftwareSerial pmsSensor(PMS_RX, PMS_TX);// MQTT设置
const char* mqtt_server = "47.115.210.16";
const int mqtt_port = 1883;
const char* mqtt_username = "cc1fb73f-ec43-3b37-522";
const char* mqtt_password = "b5c6d62";
const char* mqtt_client_id = "mqtt_d59ea842-b79";
const char* mqtt_topic = "devices/telemetry";// 时间设置
unsigned long lastMQTTPublish = 0;
const long MQTT_PUBLISH_INTERVAL = 10000;  // MQTT发布间隔10秒
const long DISPLAY_UPDATE_INTERVAL = 3000;  // 显示更新间隔3秒WiFiClient espClient;
PubSubClient client(espClient);// PMS数据结构
struct PMS_data {uint16_t pm1_0;uint16_t pm2_5;uint16_t pm10_0;uint16_t pm1_0_std;   // 添加标准颗粒物浓度uint16_t pm2_5_std;uint16_t pm10_0_std;bool valid;
} pms_data;void setup() {Serial.begin(115200);Serial.println("\n启动中...");// 初始化I2CWire.begin(4, 5);  // SDA = GPIO4 (D2), SCL = GPIO5 (D1)// 初始化OLEDif(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {Serial.println("OLED初始化失败");} else {Serial.println("OLED初始化成功");display.clearDisplay();display.setTextColor(SSD1306_WHITE);showInitScreen();}// 初始化PMS传感器Serial.println("初始化PMS传感器...");pmsSensor.begin(9600);pms_data.valid = false;// 配置WiFiWiFiManager wifiManager;if(!wifiManager.autoConnect("AP")) {Serial.println("配置失败,重启");ESP.restart();}// 配置MQTTclient.setServer(mqtt_server, mqtt_port);
}void loop() {static unsigned long lastDisplayUpdate = 0;unsigned long currentMillis = millis();if (!client.connected()) {reconnectMQTT();}client.loop();if (currentMillis - lastDisplayUpdate >= DISPLAY_UPDATE_INTERVAL) {if (readPMSdata()) {updateDisplay();}lastDisplayUpdate = currentMillis;}if (pms_data.valid && currentMillis - lastMQTTPublish >= MQTT_PUBLISH_INTERVAL) {publishData();lastMQTTPublish = currentMillis;}
}bool readPMSdata() {uint8_t buffer[32];uint16_t sum = 0;// 清空接收缓冲区while(pmsSensor.available()) {pmsSensor.read();}// 等待数据delay(1000);if (pmsSensor.available() < 32) {return false;}// 检查帧头if (pmsSensor.read() != 0x42 || pmsSensor.read() != 0x4D) {return false;}// 读取剩余数据pmsSensor.readBytes(buffer, 30);// 计算校验和sum = 0x42 + 0x4D;for (int i = 0; i < 28; i++) {sum += buffer[i];}// 验证校验和if (sum != ((buffer[28] << 8) | buffer[29])) {Serial.println("校验和错误");return false;}// 打印原始数据以供调试Serial.println("Raw data:");for(int i = 0; i < 30; i++) {Serial.printf("%02X ", buffer[i]);if((i + 1) % 8 == 0) Serial.println();}Serial.println();// 标准颗粒物浓度pms_data.pm1_0_std = (buffer[4] << 8) | buffer[5];    // 数据1:PM1.0标准pms_data.pm2_5_std = (buffer[6] << 8) | buffer[7];    // 数据2:PM2.5标准pms_data.pm10_0_std = (buffer[8] << 8) | buffer[9];   // 数据3:PM10标准// 大气环境浓度pms_data.pm1_0 = (buffer[10] << 8) | buffer[11];    // 数据4:PM1.0大气环境pms_data.pm2_5 = (buffer[12] << 8) | buffer[13];    // 数据5:PM2.5大气环境pms_data.pm10_0 = (buffer[14] << 8) | buffer[15];   // 数据6:PM10大气环境Serial.println("解析后的数据:");Serial.printf("标准颗粒物浓度 - PM1.0: %d, PM2.5: %d, PM10: %d\n", pms_data.pm1_0_std, pms_data.pm2_5_std, pms_data.pm10_0_std);Serial.printf("大气环境浓度 - PM1.0: %d, PM2.5: %d, PM10: %d\n", pms_data.pm1_0, pms_data.pm2_5, pms_data.pm10_0);// 验证数据合理性(暂时注释掉范围检查,看看实际数据)// if (pms_data.pm2_5 > 1000 || pms_data.pm1_0 > 1000 || pms_data.pm10_0 > 1000) {//     Serial.println("数据超出合理范围");//     return false;// }pms_data.valid = true;// 打印调试信息Serial.printf("PM1.0: %d, PM2.5: %d, PM10: %d\n", pms_data.pm1_0, pms_data.pm2_5, pms_data.pm10_0);return true;
}void updateDisplay() {display.clearDisplay();// 标题 - 确保不会折行display.setTextSize(1);display.setTextColor(SSD1306_WHITE);const char* title = "AIR QUALITY";int16_t titleWidth = strlen(title) * 6;display.setCursor((SCREEN_WIDTH - titleWidth) / 2, 2);display.print(title);// 单条分隔线display.drawLine(0, 12, SCREEN_WIDTH, 12, SSD1306_WHITE);// PM2.5值显示String pm25Str = String(pms_data.pm2_5);int16_t valueX = SCREEN_WIDTH - (pm25Str.length() * 12) - 8;  // 右边距离8像素int16_t valueY = 22;// PM2.5标签display.setTextSize(1);display.setCursor(8, valueY + 4);display.print("PM2.5");// PM2.5数值(反色显示)int16_t boxWidth = pm25Str.length() * 12 + 4;int16_t boxHeight = 18;display.fillRect(valueX - 2, valueY - 1, boxWidth, boxHeight, SSD1306_WHITE);display.setTextColor(SSD1306_BLACK);display.setTextSize(2);display.setCursor(valueX, valueY);display.print(pm25Str);// PM10显示(保持在一行内)display.setTextColor(SSD1306_WHITE);display.setTextSize(1);// PM10标签和值int16_t pm10Y = 48;display.setCursor(8, pm10Y);display.print("PM10");String pm10Str = String(pms_data.pm10_0);int16_t pm10ValueX = SCREEN_WIDTH - (pm10Str.length() * 6) - 35;  // 预留单位的空间display.setCursor(pm10ValueX, pm10Y);display.print(pm10Str);// 单位display.setCursor(SCREEN_WIDTH - 30, pm10Y);display.print("ug/m3");display.display();
}void publishData() {StaticJsonDocument<200> doc;doc["pm25"] = pms_data.pm2_5;doc["pm10"] = pms_data.pm10_0;char buffer[200];serializeJson(doc, buffer);if (client.publish(mqtt_topic, buffer)) {Serial.println("MQTT数据发送成功");Serial.println(buffer);} else {Serial.println("MQTT数据发送失败");}
}void reconnectMQTT() {while (!client.connected()) {Serial.println("尝试MQTT连接...");if (client.connect(mqtt_client_id, mqtt_username, mqtt_password)) {Serial.println("MQTT已连接");} else {Serial.print("MQTT连接失败, rc=");Serial.println(client.state());delay(2000);}}
}void showInitScreen() {display.clearDisplay();// 计算文本位置以实现居中显示int16_t startY = (SCREEN_HEIGHT - 32) / 2;  // 32是两行文本的总高度(16px每行)display.setTextSize(2);// 显示 "Air PM2.5"const char* line1 = "Air PM2.5";int16_t line1Width = strlen(line1) * 12;  // 每个字符宽度约12像素display.setCursor((SCREEN_WIDTH - line1Width) / 2, startY);display.println(line1);// 显示 "Monitor"const char* line2 = "Monitor";int16_t line2Width = strlen(line2) * 12;display.setCursor((SCREEN_WIDTH - line2Width) / 2, startY + 16);display.println(line2);display.display();delay(2000);
}

传感器手册

在这里插入图片描述
在这里插入图片描述

相关文章:

DIY-ESP8266移动PM2.5传感器-带屏幕-APP

本教程将指导您制作一台专业级的空气质量检测仪。这个项目使用经济实惠的ESP8266和PMS5003传感器&#xff0c;配合OLED显示屏&#xff0c;不仅能实时显示PM2.5数值&#xff0c;还能通过手机APP随时查看数据。总成本70元&#xff0c;相比几百的用的便宜&#xff0c;用的心理踏实…...

【Canvas与技法】椭圆画法

【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/> <head><title>椭圆的画法 Draft2</title><style type"text/css&quo…...

多核CPU调度是咋搞的?

其实很多情况下都有 这样的疑问 为什么多核CPU用着用着会“躺平”&#xff1f; 为什么手机有 8 核&#xff0c;跑分时性能却不是核心数的翻倍&#xff1f; 答案的钥匙&#xff0c;就藏在多核CPU的调度机制里。 为了更直观地理解&#xff0c;以一个《王者荣耀》游戏服务器为例…...

【Jenkins】pipeline 的基础语法以及快速构建一个 jenkinsfile

Jenkins Pipeline 是 Jenkins 中的一个强大功能&#xff0c;可以帮助你实现自动化构建、测试、部署等流程。Jenkins Pipeline 使用一种名为 Pipeline DSL&#xff08;Domain Specific Language&#xff09;的脚本语言&#xff0c;通常以 Jenkinsfile 形式存在&#xff0c;用于定…...

工作中如何提高技术实力?

点击“硬核王同学”&#xff0c;选择“关注/三连” 福利干货第一时间送达 大家好&#xff0c;我是硬核王同学。 其实这个问题困扰了我很久啊&#xff0c;不知道你们有没有跟我一样。 如何在工作中如何提高技术实力&#xff1f; 随着时间的增加&#xff0c;越来越觉得工作上…...

画图,matlab,

clear;close all;clc;tic;dirOutput dir(*.dat); % 罗列所有后缀-1.dat的文件列表&#xff0c;罗列BDDATA的数据 filenames string({dirOutput.name}); % 提取文件名%% 丢包统计 FILENAMES [""]; LOSS_YTJ [ ]; LOSS_RAD [ ]; LOSS_ETH [ ]…...

Java虚拟机类加载(解析阶段)[虚方法表的生成以及其存在意义]

class字节码文件中的常量池结构详解-CSDN博客Java虚拟机类加载(解析阶段)-CSDN博客...

电子元器件与电路之-MOS管的介绍和作用

一、基本概念 MOS 管&#xff0c;或MOSFET&#xff0c;全称是Metal-Oxide-Semiconductor Field-Effect Transistor&#xff08;金属 - 氧化物 - 半导体场效应晶体管&#xff09;。和三极管利用电流控制电流不同&#xff0c;它是一种利用电场效应来控制电流的半导体器件。和三级…...

python实现word转html

目录 使用mammoth库 使用spire.doc库 使用mammoth库 mammoth库支持将word转为HTML和markdown格式的文件。 import mammothdef word_html(word_file):html_save_name fr{word_file.split(.)[0]}.htmlwith open(word_file, rb) as f:data mammoth.convert_to_html(f)with o…...

nginx模块ngx-fancyindex 隐藏标题中的 / 和遇到的坑

首先下载nginx源码&#xff0c;编译时加上 --add-module/usr/local/src/ngx-fancyindex/ 例如 &#xff1a; ./configure --prefix/usr/local/nginx --with-select_module --with-poll_module --with-threads --with-file-aio --with-http_ssl_module --with-http_v2_module…...

第二十四天 循环神经网络(RNN)LSTM与GRU

LSTM&#xff08;长短期记忆网络&#xff09;和GRU&#xff08;门控循环单元&#xff09;是两种流行的循环神经网络变体&#xff0c;它们被设计来解决传统RNN在处理长序列数据时遇到的梯度消失和梯度爆炸问题。这两种网络都通过引入门控机制来控制信息的流动&#xff0c;从而能…...

RocketMQ如何保证消息顺序?

大家好&#xff0c;我是锋哥。今天分享关于【RocketMQ如何保证消息顺序?】面试题。希望对大家有帮助&#xff1b; RocketMQ如何保证消息顺序? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 RocketMQ 作为一个分布式消息中间件&#xff0c;提供了高吞吐、低延迟的…...

LabVIEW实现GSM/GPRS通信

目录 1、GSM/GPRS通信原理 2、硬件环境部署 3、程序架构 4、前面板设计 5、程序框图设计 6、测试验证 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联…...

关于如何做技术文档

在技术的浩瀚海洋中&#xff0c;一份优秀的技术文档宛如精准的航海图。它是知识传承的载体&#xff0c;是团队协作的桥梁&#xff0c;更是产品成功的幕后英雄。然而&#xff0c;打造这样一份出色的技术文档并非易事。你是否在为如何清晰阐释复杂技术而苦恼&#xff1f;是否纠结…...

基于多尺度动态卷积的图像分类

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…...

RK3576 介绍

RK3576 介绍 1 介绍1.1 概述1.2 RK3576、RK3588、RK3568 和 RK3399 的参数对比 2 DataSheet2.1 RK35762.2 RK35882.3 RK35682.4 RK3399 参考 1 介绍 1.1 概述 ARM 64位高性能八核通用处理器&#xff0c;丰富的PCIE/USB3.0/SATA/GMAC等各类高速及CAN FD/DSMC/UART/SPI/I2C/I3C…...

如何评估并持续优化AI呼出机器人的使用效果

如何评估并持续优化AI呼出机器人的使用效果 作者&#xff1a;开源呼叫中心FreeIPCC 随着人工智能技术的发展&#xff0c;AI呼出机器人在企业中的应用越来越广泛。这些智能系统不仅提高了工作效率、降低了成本&#xff0c;还改善了客户体验。然而&#xff0c;要确保AI呼出机器…...

Ubuntu上如何部署Nginx?

环境&#xff1a; Unbuntu 22.04 问题描述&#xff1a; Ubuntu上如何部署Nginx&#xff1f; 解决方案&#xff1a; 在Ubuntu上部署Nginx是一个相对简单的过程&#xff0c;以下是详细的步骤指南。我们将涵盖安装Nginx、启动服务、配置防火墙以及验证安装是否成功。 1. 更新…...

制造业4.0:AI与机器人如何重塑生产线

引言&#xff1a;从传统到未来的转型 在轰鸣的生产线上&#xff0c;传统制造业曾以规模化生产和成本效益为核心竞争力&#xff0c;推动了全球工业化进程。然而&#xff0c;面对现代市场的多样化需求和激烈竞争&#xff0c;这种模式正暴露出越来越多的局限性&#xff1a;产能过剩…...

ChatGPT与领域特定语言的集成

用ChatGPT做软件测试 领域特定语言&#xff08;Domain-Specific Language&#xff0c;DSL&#xff09;是一种编程语言&#xff0c;专门设计用于满足特定领域或问题领域的需求。它是一种定制的语言&#xff0c;通常包括特定领域的专业术语以及相应的语法规则。DSL的设计旨在让领…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

Cursor实现用excel数据填充word模版的方法

cursor主页&#xff1a;https://www.cursor.com/ 任务目标&#xff1a;把excel格式的数据里的单元格&#xff0c;按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例&#xff0c;…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

椭圆曲线密码学(ECC)

一、ECC算法概述 椭圆曲线密码学&#xff08;Elliptic Curve Cryptography&#xff09;是基于椭圆曲线数学理论的公钥密码系统&#xff0c;由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA&#xff0c;ECC在相同安全强度下密钥更短&#xff08;256位ECC ≈ 3072位RSA…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

MODBUS TCP转CANopen 技术赋能高效协同作业

在现代工业自动化领域&#xff0c;MODBUS TCP和CANopen两种通讯协议因其稳定性和高效性被广泛应用于各种设备和系统中。而随着科技的不断进步&#xff0c;这两种通讯协议也正在被逐步融合&#xff0c;形成了一种新型的通讯方式——开疆智能MODBUS TCP转CANopen网关KJ-TCPC-CANP…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...