基于Arduino与PurpleAir API的空气质量可视化物联网终端制作

发布时间:2026/6/4 20:23:35

基于Arduino与PurpleAir API的空气质量可视化物联网终端制作 1. 项目概述一个能“看见”空气的设备最近几年空气质量成了我们生活中一个绕不开的话题。无论是北方的雾霾还是特定季节的山火烟尘都让“今天空气怎么样”变成了出门前的一个例行检查。我自己就经常反复刷新手机上的空气质量App纠结着到底能不能开窗通风或者带孩子去楼下玩一会儿。这种被动查询的方式总让人觉得信息获取不够直观和及时。于是我就琢磨着能不能做一个放在家里的小玩意儿像温度计一样不用我主动去查它就能无声无息地告诉我当前的空气状况。这就是我做这个空气质量状态LED显示器的初衷。它的核心思路很简单用一个联网的微控制器定时从网络上获取你所在区域的实时空气质量数据然后通过一组彩色LED灯用不同的颜色把空气状况直观地显示出来。绿色代表优黄色代表良红色可能就提示你该注意了。这个项目非常适合对物联网、Arduino编程或者环境监测感兴趣的爱好者。你不需要是电子工程专家只要会基础的焊接和代码上传就能亲手搭建一个属于自己的环境信息终端。它用到的核心部件——Arduino Feather M0 WiFi和WS2812B LED灯带都是创客圈里非常流行且文档丰富的模块学习成本低可玩性高。最终你会得到一个既实用又有成就感的桌面小装置。2. 核心硬件选型与设计思路2.1 为什么选择Arduino Feather M0 WiFi在开始动手之前硬件选型是第一步也是最关键的一步。市面上主控板很多从经典的Arduino Uno到功能强大的ESP32我最终选择了Adafruit的Feather M0 WiFi主要基于以下几点考量第一高度集成与低功耗。Feather M0 WiFi的核心是一颗ATSAMD21微控制器和一块ATWINC1500 WiFi模块。这意味着Wi-Fi功能是板载的我们不需要再外接一个ESP-01之类的模块大大简化了电路连接和供电设计。同时ATSAMD21芯片本身功耗控制得不错结合板载的锂电池充电管理电路可以让整个系统依靠一块小容量锂电池运行数小时实现一定程度的便携性或作为断电备用。第二生态友好与易用性。Adafruit为其产品提供了极其完善的软件库Library和教程。对于这个项目我们需要用到WiFi连接、HTTP请求获取API数据和NeoPixel控制WS2812B LED这三个核心功能。Adafruit为它们都提供了经过充分测试的库并且这些库在Feather M0上兼容性很好能避免很多底层驱动问题。第三“Feather”形态因子。Feather系列采用了统一的引脚排列和尺寸这带来了两个好处一是可以使用“堆叠式”排针方便像叠罗汉一样连接其他Feather扩展板虽然本项目未使用二是其引脚布局清晰电源、地线、数字模拟引脚分区明确在面包板或焊接时不容易接错。注意如果你手头有NodeMCUESP8266或ESP32开发板它们是完全可行的替代方案甚至性价比更高。选择Feather M0更多是出于我个人对Adafruit生态的偏好以及对3.3V系统一致性的考虑LED和主控均为3.3V无需电平转换。2.2 传感器数据的来源PurpleAir API vs. 物理传感器这是一个重要的设计决策是直接连接一个本地的PM2.5传感器模块还是去获取网络上的数据我选择了后者即通过PurpleAir的API获取数据。使用PurpleAir API的优势成本与复杂度大幅降低一个精度尚可的激光PM2.5传感器模块如攀藤PMS5003价格在百元以上且需要处理串口通信、数据解析还要考虑传感器的预热、维护以及定期校准。而调用API几乎是零硬件成本。数据代表范围更广单个传感器只能反映设备所在“点”的空气质量。而PurpleAir社区网络由成千上万个传感器组成选择你家附近的一个户外传感器其数据更能代表你所在“区域”的整体室外空气状况这对于决定是否开窗更有参考价值。免维护你无需担心自己的传感器积灰、损坏或校准漂移。PurpleAir的维护由传感器所有者负责。当然也有其局限性依赖网络与API服务设备必须连接互联网并且PurpleAir的服务必须可用。如果API变更或收费项目可能需要调整。数据非本地实时测量存在几分钟的数据延迟且无法监测纯室内环境除非你附近有公开的室内传感器。基于这些分析本项目的定位就很清晰了它是一个低成本、低复杂度、用于获取社区化室外空气质量参考数据并予以直观显示的“信息终端”而非高精度的科学监测仪器。这个定位决定了我们后续的所有技术实现路径。2.3 显示单元的选择Adafruit Jewel 7显示部分我选择了Adafruit Jewel 7它本质上就是一个集成了7颗WS2812B LED的圆形小板。WS2812B是一种智能控制LED每个灯珠内部都集成了驱动芯片只需要一根数据线就能控制无数个灯珠的颜色和亮度极大地简化了布线。选择Jewel 7的原因如下尺寸与形态小巧的圆形设计非常适合嵌入到各种外壳中7颗灯珠可以设计出多种显示模式如整体单色、呼吸灯、旋转动画等。驱动简单仅需一个GPIO引脚数字引脚控制对主控板引脚资源占用少。亮度与色彩WS2812B可显示全彩颜色足以用丰富的色彩梯度来表征空气质量指数AQI的不同等级。这里有一个实操心得WS2812B的工作电压是5V但数据信号电压要求是3.3V-5V。Feather M0的GPIO输出是3.3V。虽然很多情况下3.3V信号也能驱动5V供电的WS2812B但为了稳定性最佳实践是进行电平转换。不过Adafruit的NeoPixel库针对3.3V逻辑的ARM芯片如Feather M0做了优化在短距离、灯珠数量少如只有7个的情况下直接连接通常也能稳定工作。为了简化本项目采用了直接连接。如果未来扩展灯带电平转换模块如74AHCT125是必需的。3. 系统搭建与电路连接详解3.1 物料清单与工具准备除了核心的主控板和LED你还需要准备以下物料和工具电子部分Adafruit Feather M0 WiFi (含排针如需焊接)Adafruit Jewel 7 (7颗WS2812B LED)3.7V 锂电池例如350mAh或更大容量用于便携供电。Micro USB数据线用于编程和充电。杜邦线跳线若干建议使用公-公跳线用于连接。如果需要焊接排针、细导线红、黑、绿或其他颜色用于区分、焊锡、焊台。结构与外壳部分一个圆形罐子/盒子作为主体外壳。燕麦罐、茶叶罐、塑料食品盒都可以直径最好能盖住LED灯板并留出扩散空间。半透光扩散材料这是让显示效果从“刺眼的点光源”变为“柔和的面光源”的关键。羊皮纸是绝佳选择它廉价、易得、透光均匀且基本不偏色。描图纸、磨砂亚克力板也是不错的替代品。固定与支撑材料热熔胶枪和胶棒、双面泡棉胶、一小段硬纸管如厕纸芯用于内部支撑。工具剪刀、美工刀、尺子、打火机处理线头。3.2 电路连接步骤与原理整个系统的电路连接非常简单本质上只有三条线连接Feather和Jewel。焊接可选但推荐如果你希望成品更牢固建议将三根不同颜色的导线建红、黑、绿焊接到Jewel 7的焊盘上。焊盘通常标有5V、GND和DI数据输入。焊接时动作要快避免过热损坏LED芯片。连接电源红线将来自Jewel 7的红线5V连接到Feather M0的USB引脚或BAT引脚。这里有个关键点Feather M0的USB引脚在通过USB供电时约为5V而BAT引脚是电池电压约3.7V-4.2V。WS2812B的理想工作电压是5V。如果使用电池供电接BATLED的亮度会稍暗颜色可能略有偏差但对于本项目是可接受的。如果追求最佳显示效果可以连接一个微型5V升压模块但会增加复杂度。本项目为求简洁直接连接至USB引脚当USB供电时或BAT引脚电池供电时。连接地线黑线将来自Jewel 7的黑线GND连接到Feather M0上任意的GND引脚。确保共地这是电路正常工作的基础。连接数据线绿线将来自Jewel 7的绿线DI连接到Feather M0的一个数字IO引脚例如引脚9。这个引脚将在代码中被定义为NeoPixel的数据引脚。连接电池将锂电池的JST插头插入Feather M0的电池接口。板载的充电管理芯片会在USB插入时自动为电池充电。连接示意图文字描述Adafruit Feather M0 WiFi USB 引脚 (5V) ------ Jewel 7 的 5V 焊盘 (红线) GND 引脚 ------ Jewel 7 的 GND 焊盘 (黑线) 引脚 9 ------ Jewel 7 的 DI 焊盘 (绿线) BAT 接口 ------ 3.7V 锂电池重要注意事项在通电状态下绝对不要热插拔WS2812B的数据线或更改电路连接这极易因瞬时电流冲击而烧毁LED芯片。务必先断开电源拔掉USB或电池再进行操作。4. 软件编程从获取数据到点亮LED4.1 开发环境配置与库安装首先确保你已安装Arduino IDE。接着我们需要添加对Adafruit硬件和库的支持。添加开发板支持打开Arduino IDE进入文件 - 首选项在“附加开发板管理器网址”中填入https://adafruit.github.io/arduino-board-index/package_adafruit_index.json然后打开工具 - 开发板 - 开发板管理器搜索“Feather M0”找到并安装“Adafruit SAMD Boards”。安装必要的库打开工具 - 管理库。我们需要安装以下库Adafruit NeoPixel用于控制WS2812B LED。Adafruit ATWINC15x0这是Feather M0 WiFi的Wi-Fi驱动库。ArduinoJson版本6.x或7.x这是处理PurpleAir API返回的JSON数据的关键库。务必安装较新版本。4.2 获取并配置PurpleAir API密钥与传感器IDPurpleAir的API访问方式有过变更。现在需要申请一个免费的“Read Key”。申请API Key访问 PurpleAir API 密钥申请页面。注册/登录后你可以创建一个“Read Key”。这个密钥将用于在你的代码中验证API请求。查找传感器ID打开PurpleAir地图。放大到你所在的区域并在地图图层设置中关闭“室内传感器”显示只保留室外传感器。点击离你最近或你认为最有代表性的一个室外传感器图标。在弹出的信息框中找到“传感器ID”或“ID”字段。通常是一个6位或7位的数字。请务必记录下这个数字。4.3 代码结构与核心逻辑解析完整的代码较长这里我将拆解核心逻辑部分。你需要创建两个文件主程序文件例如PurpleAir_AQI_Display.ino和一个配置文件arduino_secrets.h。arduino_secrets.h配置文件// 将此文件重命名为 arduino_secrets.h #define SECRET_SSID 你的Wi-Fi名称 #define SECRET_PASS 你的Wi-Fi密码 #define PURPLE_AIR_API_KEY 你的PurpleAir API Read Key #define PURPLE_AIR_SENSOR_ID 你的传感器ID // 注意是字符串类型这个文件用于存放你的敏感信息避免将它们上传到公开的代码仓库。主程序核心逻辑简化版#include Adafruit_NeoPixel.h #include WiFi101.h // Feather M0 WiFi 使用此库 #include ArduinoJson.h // 包含你的密钥 #include arduino_secrets.h // 网络参数 char ssid[] SECRET_SSID; char pass[] SECRET_PASS; int status WL_IDLE_STATUS; // PurpleAir 参数 String sensorId PURPLE_AIR_SENSOR_ID; String apiKey PURPLE_AIR_API_KEY; // NeoPixel 参数 #define LED_PIN 9 #define LED_COUNT 7 Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB NEO_KHZ800); // AQI 颜色映射 (基于美国AQI标准简化版) uint32_t aqiColors[] { strip.Color(0, 255, 0), // 绿色 优 (0-50) strip.Color(255, 255, 0), // 黄色 良 (51-100) strip.Color(255, 165, 0), // 橙色 轻度污染 (101-150) strip.Color(255, 0, 0), // 红色 中度污染 (151-200) strip.Color(128, 0, 128), // 紫色 重度污染 (201-300) strip.Color(128, 0, 0) // 褐红色 严重污染 (301) }; void setup() { Serial.begin(115200); strip.begin(); strip.show(); // 初始化时关闭所有LED // 连接Wi-Fi connectToWiFi(); } void loop() { // 1. 从PurpleAir获取PM2.5数据 float pm25 fetchPM25FromPurpleAir(); // 2. 将PM2.5转换为简化的AQI等级 (0-5) int aqiLevel convertPM25ToAQILevel(pm25); // 3. 根据AQI等级设置LED颜色 uint32_t color aqiColors[aqiLevel]; for(int i0; istrip.numPixels(); i) { strip.setPixelColor(i, color); } strip.show(); // 4. 等待一段时间后再更新例如10分钟 delay(10 * 60 * 1000UL); // 10分钟UL表示无符号长整型 } float fetchPM25FromPurpleAir() { WiFiClient client; String url /v1/sensors/ sensorId ?fieldspm2.5; if (client.connect(api.purpleair.com, 80)) { String request String(GET ) url HTTP/1.1\r\n Host: api.purpleair.com\r\n X-API-Key: apiKey \r\n Connection: close\r\n\r\n; client.print(request); // 等待并解析响应... // 这里需要处理HTTP头找到JSON主体并用ArduinoJson解析出pm2.5字段。 // 具体解析代码略需处理网络延迟和超时。 } client.stop(); return -1.0; // 获取失败返回错误值 } int convertPM25ToAQILevel(float pm25) { // 简化的AQI等级划分 (基于PM2.5浓度 μg/m³) if (pm25 12.0) return 0; // 优 else if (pm25 35.4) return 1; // 良 else if (pm25 55.4) return 2; // 轻度污染 else if (pm25 150.4) return 3; // 中度污染 else if (pm25 250.4) return 4; // 重度污染 else return 5; // 严重污染 } void connectToWiFi() { // 尝试连接Wi-Fi包含重试机制 while (status ! WL_CONNECTED) { status WiFi.begin(ssid, pass); delay(5000); // 等待5秒 } }代码要点解析fetchPM25FromPurpleAir函数这是核心。它构造一个HTTP GET请求到PurpleAir的API端点请求特定传感器sensorId的pm2.5字段。请求头中必须包含X-API-Key。返回的数据是JSON格式需要使用ArduinoJson库来解析。在实际完整代码中你需要仔细处理HTTP响应跳过响应头找到JSON主体然后反序列化。convertPM25ToAQILevel函数这里实现了一个非常简化的AQI转换。实际上完整的AQI计算涉及多个污染物和更复杂的公式。对于显示用途这个基于PM2.5浓度的简化分级已经足够直观。你可以根据中国AQI标准或你更熟悉的标尺来修改阈值。延时策略loop()中使用了delay(10 * 60 * 1000UL)进行10分钟的延时。这意味着设备每10分钟更新一次数据。这是非常必要的因为频繁请求API比如每分钟一次可能会被PurpleAir服务器限流或封禁。对于空气质量显示10-30分钟的更新频率完全足够。错误处理示例代码简化了错误处理。在实际应用中fetchPM25FromPurpleAir函数如果失败应该让LED显示一个特定的错误颜色比如蓝色闪烁并在串口监视器打印调试信息而不是直接使用-1.0这个返回值。4.4 上传代码与初步测试在Arduino IDE中选择开发板为“Adafruit Feather M0”。选择正确的端口。点击上传。上传完成后打开串口监视器波特率115200观察输出。你应该能看到Wi-Fi连接过程和尝试获取API数据的日志。如果一切正常Jewel 7的LED应该会根据你设定的传感器当前的PM2.5值显示出对应的颜色。5. 外壳制作与光效优化5.1 外壳设计与组装一个美观的外壳能让项目从“实验原型”升级为“家居摆件”。准备罐体清洗并晾干你选择的罐子。在罐子底部钻或剪一个小孔用于穿过USB电源线。如果使用电池供电且计划长期放置可以不留孔。制作灯罩/扩散层剪下一块比罐口略大的圆形羊皮纸。找一个大小合适的透明塑料盖如酸奶盖在中心挖一个比Jewel 7略小的圆形孔。用少量胶水如UHU胶或热熔胶将羊皮纸粘贴在塑料盖的内侧覆盖住整个盖子。这样你就得到了一个“夹心”结构的灯罩塑料盖提供支撑羊皮纸负责柔光。内部布局与固定用一小段硬纸管如厕纸芯剪下一截或积木块作为Feather M0和Jewel 7之间的支撑确保LED灯板能朝向罐口。使用双面泡棉胶或热熔胶将Feather M0固定在罐子底部。将支撑物粘在Feather上方再将Jewel 7已焊接好导线粘在支撑物顶部确保其正面朝上对准罐口。将电池妥善放置在罐内空余位置可以用胶带或魔术贴固定。最终组装整理好内部导线避免缠绕。将制作好的灯罩盖在罐口可以用胶带在边缘稍作固定或者如果尺寸合适直接压紧即可。如果使用USB供电将线从底部小孔穿出。5.2 LED光效编程与优化简单的全亮显示有些单调。我们可以利用7颗LED编程实现更优雅的显示效果。方案一呼吸灯效果用呼吸灯亮度平滑脉动来显示当前等级比常亮更柔和也更省电平均电流更低。void breatheLED(uint32_t color, int aqiLevel) { int wait 5; // 呼吸速度 for (int brightness 5; brightness 150; brightness5) { strip.setBrightness(brightness); // 设置整体亮度 for(int i0; istrip.numPixels(); i) { strip.setPixelColor(i, color); } strip.show(); delay(wait); } for (int brightness 150; brightness 5; brightness-5) { strip.setBrightness(brightness); for(int i0; istrip.numPixels(); i) { strip.setPixelColor(i, color); } strip.show(); delay(wait); } } // 在loop中调用 breatheLED(color, aqiLevel) 替代简单的setPixelColor方案二动态扫描效果让LED像进度条或雷达扫描一样动态显示更具科技感。void scanLED(uint32_t color) { for(int i0; istrip.numPixels(); i) { strip.clear(); // 先清空 strip.setPixelColor(i, color); // 点亮当前LED strip.show(); delay(100); // 扫描速度 } } // 可以在获取新数据后短暂播放一次扫描动画然后常亮或呼吸。方案三根据污染等级改变动画模式例如空气质量优时用温和的呼吸灯污染加重时变为急促闪烁的警报模式。void displayAQI(int aqiLevel, uint32_t color) { switch(aqiLevel) { case 0: // 优 breatheLED(color, 100); // 缓慢呼吸 break; case 1: // 良 breatheLED(color, 70); break; case 2: // 轻度污染 solidColorWithSlowBlink(color); // 常亮伴慢闪 break; case 3: // 中度污染及以上 alertBlink(color); // 快速闪烁警报 break; } }实操心得strip.setBrightness()函数非常有用。WS2812B在最高亮度255时非常刺眼尤其在全白模式下电流也很大。建议将亮度设置在50-150之间既能清晰显示又舒适省电。在电池供电时降低亮度能显著延长续航。6. 常见问题排查与进阶优化6.1 问题排查速查表问题现象可能原因排查步骤与解决方案LED完全不亮1. 电源未接通或接反。2. 数据线接错引脚或接触不良。3. 代码中LED引脚定义错误。4. LED损坏。1. 检查USB/电池是否供电用万用表测VCC和GND间电压应为~5V或~3.7V。2. 确认数据线连接到了代码中LED_PIN定义的引脚如引脚9。3. 检查代码strip.begin()和strip.show()是否被执行。4. 运行Adafruit NeoPixel库的示例代码strandtest排除硬件问题。LED显示混乱颜色1. 数据信号时序问题3.3V逻辑驱动5V LED。2. 电源噪声或电压不足。1. 尝试在数据线Feather GPIO到Jewel DI上串联一个100-500欧姆的电阻。2. 确保电源特别是5V能提供足够电流7颗LED全白高亮约需0.3A。尝试用手机充电器通过USB供电测试。3. 在strip.begin()后立即添加一小段延时delay(500)。Wi-Fi无法连接1.arduino_secrets.h中SSID/密码错误。2. 网络为5GHz频段Feather M0只支持2.4GHz。3. 路由器设置了MAC地址过滤。1. 打开串口监视器查看连接状态输出信息。2. 确认Wi-Fi是2.4GHz。在代码中增加重试循环和更详细的错误码打印。3. 检查路由器后台暂时关闭MAC过滤或添加Feather M0的MAC地址可从串口打印获得。无法获取API数据1. API密钥或传感器ID错误。2. 网络连接不稳定。3. PurpleAir API服务暂时不可用或变更。1. 在串口监视器中打印完整的HTTP请求URL和响应代码。尝试在浏览器中手动访问该URL需添加X-API-Key头可用浏览器插件模拟。2. 增加HTTP请求超时时间并加入重试机制。3. 查看PurpleAir API文档确认端点地址和参数格式是否已更新。电池续航极短1. LED亮度设置过高。2. Wi-Fi连接功耗大。3. 电池容量太小或老化。1. 在代码中降低strip.setBrightness()的值如设为50。2. 增加数据更新间隔如从10分钟改为30分钟。在两次更新之间可以考虑让Feather进入深度睡眠模式但这需要更复杂的编程和电路唤醒设计。3. 更换更大容量如1000mAh的锂电池。6.2 进阶优化思路当基础功能实现后你可以考虑以下方向进行升级多传感器数据融合不再只依赖一个PurpleAir传感器而是在代码中查询多个附近传感器的数据然后取平均值或最优/最差值使数据更具代表性。本地AQI计算使用更专业的AQI计算公式例如中国的HJ 633-2012标准综合PM2.5、PM10等污染物浓度如果API提供计算出更准确的AQI指数而不仅仅是PM2.5单一指标。添加本地传感器在设备上集成一个真正的BME280温湿度气压或SGP30TVOC/CO2传感器。这样设备可以同时显示室外的PurpleAir AQI和室内的温湿度/空气质量信息维度更丰富。无线配置与OTA通过构建一个简单的Web配置页面使用Feather M0的Wi-Fi AP模式让设备首次启动时能让你输入Wi-Fi密码和传感器ID而无需修改代码。更进一步可以实现空中升级OTA功能方便后期更新程序。云端数据记录将获取到的数据除了显示外还定期发送到免费的物联网平台如Thingspeak、Blynk或自建的InfluxDB从而绘制出空气质量的历史变化曲线。改变显示载体将LED显示改为一块小型的OLED屏幕可以同时显示PM2.5数值、AQI等级、更新时间等更多信息。这个项目的魅力在于它作为一个起点可以延伸出无数种可能。它扎实地涵盖了物联网项目的几个核心环节硬件互联、网络通信、API调用、数据解析和用户交互。当你完成了第一个能正常运行的版本后那种“让虚拟数据在物理世界发光”的成就感正是创客精神的乐趣所在。

相关新闻