1. 项目概述:一个能“感知”天气的健康预警灯
几年前,我被一个反复出现的问题困扰:偏头痛。它总是不期而至,打乱所有计划。后来我发现,我的症状与某些特定的天气变化,尤其是气压的剧烈波动,存在一定的关联。于是我开始定期查看天气预报网站上的“偏头痛指数”预测,这确实有所帮助。但问题在于,我常常忘记去查,或者当头痛真的袭来时,往往为时已晚,只能硬扛。我需要的不是一个需要我主动去“阅读”的复杂应用,而是一个能安静地待在我的视野边缘,一旦预测到风险就主动给我一个温和提醒的“伙伴”。
这就是“Fair Weather Friend”项目的初衷。它本质上是一个物联网环境显示器,核心任务是把复杂的网络数据(比如未来48小时的健康风险预测)提炼成最简单的二元信号:是或否,好或坏。我选择了ESP8266这款小巧但功能强大的Wi-Fi微控制器作为大脑,让它定期从AccuWeather网站抓取特定预测页面,然后通过一个简单的LED灯,用不同的闪烁模式来传达信息。慢速闪烁意味着“未来可能有情况,请留意”,而长时间熄灭、仅偶尔闪烁一下则表示“一切正常,我在待机”。整个过程无需我进行任何操作,它就像房间里一个沉默的哨兵。
这个项目的魅力在于它的普适性和可定制性。虽然我最初用它来监测偏头痛风险,但AccuWeather提供了数十种不同的预测,从过敏指数、关节炎不适度,到高尔夫天气、观星条件、户外活动适宜度等等。你可以轻松地将它改造成一个“明天适合骑行吗”的指示灯,或者一个“今晚夜空晴朗吗”的星空预告器。它剥离了所有冗余信息,只留下最核心的结论,以一种近乎本能的方式融入你的生活环境和日常节奏中。
2. 核心硬件选型与电路设计解析
构建这样一个系统,硬件部分的核心是微控制器、网络模块和输出指示器。我的选择基于几个原则:低成本、易开发、低功耗(至少是间歇性工作),以及最终成品的封装灵活性。
2.1 为什么是ESP8266?
在众多微控制器中,我选择了Adafruit Feather HUZZAH ESP8266。这个决定背后有几个关键考量:
- 高度集成:它将ESP8266 Wi-Fi芯片、USB转串口芯片、稳压电路以及锂电池充电管理电路集成在一块比信用卡还小的板子上。这意味着你不需要额外购买FTDI编程器、电平转换器或充电模块,极大简化了开发和原型制作。
- 灵活的供电方式:它可以通过Micro USB口供电(比如用一个闲置的手机充电器),也可以通过板载的JST PH接口连接锂电池,或者通过引脚接入外部5V电源。这种灵活性让你在最终封装产品时,可以根据安装位置选择最方便的供电方案。
- 丰富的GPIO和社区支持:虽然我们本项目只用一个LED,但ESP8266本身提供了多个GPIO,支持PWM、I2C、SPI等,未来扩展性很强。更重要的是,它在Arduino和MicroPython生态中有极其庞大的社区和库支持,遇到问题很容易找到解决方案。
当然,你也可以使用其他ESP8266开发板,比如NodeMCU或Wemos D1 mini。代码核心逻辑是通用的,主要区别在于引脚定义和供电接口需要稍作调整。Feather HUZZAH的“一站式”体验,对于希望快速实现功能而非纠结于底层电路连接的创作者来说,是更省心的选择。
2.2 最小化电路:LED与电阻的计算
输出部分我追求极简主义,只使用了一个10mm的红色LED和一个220欧姆的限流电阻。为什么是这些参数?
首先,LED的工作电压(正向压降)通常在1.8V至3.3V之间,具体取决于颜色和材料。红色LED一般在1.8V-2.2V。ESP8266的GPIO引脚输出电压为3.3V。我们需要一个电阻来限制流过LED的电流,防止其烧毁,同时确保亮度合适。
计算限流电阻的公式是:R = (Vcc - Vf) / I
Vcc:电源电压,即GPIO高电平输出,为3.3V。Vf:LED正向压降,取典型值2.0V。I:期望的LED工作电流。对于指示用途的LED,5-20mA都常见。为了兼顾亮度和功耗,我选择10mA(0.01A)。
代入公式:R = (3.3V - 2.0V) / 0.01A = 130Ω。 我手边有常见的220Ω电阻,使用它时电流为I = (3.3V - 2.0V) / 220Ω ≈ 5.9mA。这个电流足以让10mm的大尺寸LED在室内清晰可见,同时功耗更低,对系统更友好。这里的一个实操心得是:对于指示灯用途,不必追求最大亮度,在保证可见的前提下,更低的电流意味着更低的整体功耗和更长的器件寿命。
电路连接非常简单:
- LED的正极(长脚)通过220Ω电阻,连接到Feather HUZZAH的GPIO 15引脚(在板子背面有清晰标注)。
- LED的负极(短脚)直接连接到板子的GND(地)引脚。
注意:务必正确区分LED的正负极。接反了不会损坏LED,但也不会点亮。如果不确定,可以用万用表的二极管档测试,或者用3V电池(如CR2032)短暂接触,灯亮时电池正极接触的就是LED正极。
2.3 供电方案与安全细节
我设计了两种供电方式,以适应不同场景:
- USB供电(最简方案):直接使用Micro USB线连接充电宝或手机充电器。这种方式下,你甚至可以不焊接外部LED,而是使用板载的蓝色LED(连接在GPIO 0上),只需在代码中修改
LED_PIN定义为0即可,真正实现“零焊接”原型验证。 - 直流电源插座供电(成品化方案):为了做一个可以长期固定在某个位置、外观更整洁的设备,我增加了一个面板安装的DC桶形插座。我使用的是5V、1A的直流电源适配器(中心正极)。
关键的安全警告:绝对禁止同时连接USB和外部DC电源!Feather HUZZAH板上的电源管理电路虽然优秀,但同时从两个源头供电可能导致不可预料的电压冲突,损坏板载芯片。正确的操作流程是:在通过USB给板子烧录程序时,断开DC电源;在通过DC电源运行时,拔掉USB线。这是一个必须养成的习惯。
在焊接DC插座时,需要辨认引脚:通常,外部的两个小引脚中,外侧的那个是负极(GND),中间的小引脚常作为开关引脚(本例未使用),大的引脚是正极(V+)。将正极连接到板子上标有“USB”的引脚(这是板子的5V输入),负极连接到板子的“GND”引脚。
为了便于将电路板装入最终的外壳(我用的是一个药瓶),我在DC插座和电路板之间增加了一组JST插头插座。这样,我可以把电路部分和带插座的瓶盖分开制作,最后再连接起来,避免了在狭小空间内操作的困难。对于使用标准塑料项目盒的搭建,这一步通常可以省略。
3. 软件逻辑深度剖析与代码实现
项目的软件核心是让ESP8266周期性地执行一个任务循环:连接Wi-Fi -> 获取网页 -> 解析内容 -> 根据结果驱动LED -> 休眠等待下一轮。下面我们拆解每一个环节。
3.1 网络连接与数据获取
代码始于配置你的Wi-Fi凭证和要访问的目标网页。这部分信息需要你根据实际情况修改:
char ssid[] = "你的Wi-Fi网络名称", pass[] = "你的Wi-Fi密码", host[] = "www.accuweather.com", page[] = "/en/us/your-city/your-zip/forecast-type/location-code";page[]的获取需要一点技巧:你需要用浏览器访问AccuWeather网站,搜索你的地点,然后找到你关心的预测类型(比如“Migraine”),并进入该预测的专属页面。此时,浏览器地址栏的URL中,从第一个斜杠(/)开始,一直到末尾的部分,就是你需要的内容。例如:/en/us/beijing/beijing/weather-forecast/123456。注意,不要包含http://或域名。
在loop()函数中,设备首先尝试连接Wi-Fi。此时LED会进入快速闪烁模式(4Hz,10%占空比),提供视觉反馈。连接成功后,它会尝试通过HTTP GET请求获取指定的网页内容。这里使用了一个WiFiClient对象来管理TCP连接。
实操心得:调试是成功之母。在首次运行时,强烈建议通过USB连接电脑,打开Arduino IDE的串口监视器(波特率设为57600)。程序会打印出“WiFi connecting..”、“Contacting server...”等状态信息。如果卡在某个环节,串口输出能帮你快速定位问题是网络连接失败、密码错误,还是URL格式不对。
3.2 网页抓取与信息提取的核心策略
获取到网页数据(通常是超过100KB的HTML代码)后,我们面临最大的挑战:如何从海量文本中精准地找到我们想要的那一小段预测信息?这里我们采用了“多级字符串匹配”的策略,而不是简单地搜索“Migraine”这个词。
为什么不能简单搜索?因为“Migraine”这个词在AccuWeather的页面里可能出现几十次,出现在导航栏、广告、免责声明等无关区域。直接搜索会得到大量误报。更糟糕的是,我发现(至少在项目开发时)他们的页面存在一个Bug:在“当前状况”和“夜间/凌晨”时段,无论实际天气如何,总是显示偏头痛风险高。这是一个必须绕开的“陷阱”。
我们的解决方案:
- 第一级定位(锚点查找):我们首先在HTML源码中寻找可靠的、唯一的“锚点”,来定位到有效的预测区块。通过查看网页源代码,我发现用
<h3>Today</h3>和<h3>Tomorrow</h3>这两个标签包裹的标题非常独特,它们正好标记了“今日”和“明日”预测区域的开始。代码中的matchList0数组就定义了这两个搜索目标。 - 第二级确认(特征确认):一旦通过
multiFind()函数找到了“Today”或“Tomorrow”的锚点,我们就在这个位置之后的HTML流中,继续搜索更具体的特征字符串,例如"Migraine Headache <span>Weather"。这个字符串很可能是预测卡片内的一个特定标签。
只有当两级搜索都成功时,我们才判定“找到了有效的风险预测”。这个if((multiFind(matchList0) >= 0) && client.find("Migraine Headache <span>Weather"))判断语句,就是这个逻辑的体现。这种策略极大地提高了准确性。
multiFind()函数是一个自定义的、可以同时匹配多个字符串的搜索工具。它逐个字节地读取网络数据流,并与列表中的每个字符串进行比对。一旦某个字符串被完全匹配,它就立即返回该字符串的索引号。这种流式处理对于内存有限的微控制器至关重要,因为它不需要将整个网页加载到内存中。
3.3 状态指示与低功耗设计
根据解析结果,程序会设置两个时间变量hi(LED点亮时间)和lo(LED熄灭时间),从而定义LED的闪烁模式:
- 预警模式(慢闪):
hi = 500, lo = 500。LED以1Hz频率(亮0.5秒,灭0.5秒)稳定闪烁,清晰提示用户关注潜在风险。 - 安全模式(待机提示):
hi = 10, lo = 3990。LED每4秒仅快速亮起10毫秒(一个几乎不易察觉的“闪烁”),主要目的是证明设备仍在正常运行,而非死机。 - 工作模式(快闪):在连接Wi-Fi和抓取数据过程中,LED会以更快的频率(1Hz或4Hz)闪烁,指示系统正在活跃工作。
完成一次查询后,程序会调用WiFi.disconnect()断开Wi-Fi连接。这是一个重要的低功耗技巧。ESP8266的Wi-Fi模块在连接状态下功耗较高(约70mA),而断开后深度睡眠的功耗可以降到20μA以下。虽然本项目代码没有使用深度睡眠(因为需要维持PWM来控制LED),但断开Wi-Fi仍能节省可观的电量。如果未来你改造为电池供电,并采用定时唤醒(例如每小时查询一次),那么结合深度睡眠,可以让设备持续工作数周甚至数月。
查询间隔由POLL_INTERVAL定义(默认为15分钟)。对于天气预报这类变化相对缓慢的信息,这个频率完全足够,甚至可以延长到30分钟或1小时,以进一步节省电力和网络资源。
4. 定制化你的专属环境显示器
这个项目的真正力量在于其可定制性。你可以将它从偏头痛预警器,改造成任何你感兴趣的数据的“环境感知器”。
4.1 更换预测类型
AccuWeather提供了丰富的预测类别。要更换类型,你需要:
- 在AccuWeather网站上,进入你所在位置的页面。
- 在预报面板上,点击“+”号添加更多预报类型,例如“Allergy”、“Arthritis”、“Golf”、“Sailing”、“Outdoor DIY Projects”、“Astronomy”等等。
- 点击该预报类型,进入其专属页面。
- 复制浏览器地址栏中从“/”开始的页面路径,替换代码中的
page[]内容。
4.2 修改关键词匹配逻辑
更换预测类型后,网页的HTML结构也会变化。原先搜索"Migraine Headache <span>Weather"可能不再有效。你需要成为临时的“网页结构侦探”:
- 在浏览器中打开你新选择的预报页面。
- 使用浏览器的“查看网页源代码”功能(通常在右键菜单中)。
- 使用源代码查看器的搜索功能(Ctrl+F),寻找能唯一标识“预测存在”或“预测级别”的文本片段。例如,对于过敏指数,你可能会搜索“High”、“Low”、“Moderate”等词,但需要结合其周围的HTML标签来精确定位,比如
<span class="label">High</span>。 - 将你找到的、包含一些HTML标签的独特字符串,替换掉代码中
client.find()函数内的参数。
这个过程被称为“网页抓取”。它的稳定性依赖于目标网站不改变其前端代码结构。如果AccuWeather某天更新了网站设计,你的抓取规则可能会失效,需要重新分析。这是所有基于网页抓取项目所面临的共同挑战。
4.3 硬件扩展与创意封装
LED指示灯只是最基础的输出。你可以发挥创意,让显示方式更具趣味性和装饰性:
- 多彩LED:使用一个RGB LED,用不同颜色代表不同状态。例如,绿色表示“适宜”,红色表示“不适宜”,蓝色表示“数据获取中”。
- NeoPixel灯带:使用Adafruit NeoPixel灯带,可以创造出更丰富的灯光效果,如流水灯、彩虹波纹等,将数据可视化提升到艺术层面。
- 小型显示屏:连接一个OLED或LCD屏幕,在显示简单图标的同时,还能滚动显示具体的预测数值或简短文字。
- 创意外壳:这是赋予项目个性的关键。正如我用旧药瓶来呼应“健康预警”的主题,你可以根据预测内容设计外壳。例如:
- 做一个“骑行天气指示器”,把它装在一个复古的自行车铃铛里。
- 做一个“观星条件指示器”,把它放在一个星空投影仪或小型天文望远镜模型里。
- 做一个“园艺浇水提醒器”,把它封装在一个小洒水壶造型的容器里。
重要安全与伦理提示:本项目及其任何变体,绝不能用作严肃的医疗诊断或治疗工具。无论是偏头痛、过敏还是关节炎预测,都只是基于气象数据的统计可能性提示,存在误差,不能替代专业医疗建议。如果你有健康方面的担忧,首要任务是咨询医生。这个项目更像是一个有趣的个人生活实验和物联网技术实践,其提醒功能应被视为一种辅助性的、参考性的信息,而非决定性依据。
5. 常见问题与故障排除实录
在制作和调试过程中,你可能会遇到一些问题。以下是我在实践中总结的一些常见情况及解决方法。
5.1 编译与上传问题
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| Arduino IDE中找不到“Feather HUZZAH ESP8266”开发板选项。 | ESP8266开发板支持未安装。 | 打开IDE的“文件”->“首选项”,在“附加开发板管理器网址”中添加:http://arduino.esp8266.com/stable/package_esp8266com_index.json。然后在“工具”->“开发板”->“开发板管理器”中搜索“esp8266”并安装。 |
| 上传代码时提示“Failed to connect to ESP8266”或一直等待连接。 | 板子未进入上传模式;驱动问题;端口错误。 | 1. 确保已用USB线连接电脑。2. 在上传前,先按住板上的“FLASH”或“GPIO0”按钮不放,再按一下“RST”按钮复位,然后松开“FLASH”按钮,此时板子进入烧录模式。3. 在“工具”->“端口”中选择正确的COM口(Windows)或/dev/cu.usbserial口(Mac)。4. 检查是否安装了正确的USB驱动(CP210x或CH340)。 |
| 编译错误,提示“WiFiClient”等未定义。 | 编译环境选择错误。 | 在“工具”->“开发板”中,务必选择“Adafruit Feather HUZZAH ESP8266”。确保“Flash Size”设置为“4M (3M SPIFFS)”。 |
5.2 网络与运行问题
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 串口监视器显示“WiFi connecting..”后一直打印“....”,无法连接。 | Wi-Fi SSID或密码错误;信号太弱。 | 1. 仔细检查代码中ssid和pass是否正确,注意大小写和特殊字符。2. 将设备靠近路由器测试。3. 尝试连接手机热点,以排除家庭网络特殊设置(如MAC地址过滤)的问题。 |
| 连接Wi-Fi成功,但提示“Contacting server... failed.”。 | host或pageURL错误;网络无法访问外部网站。 | 1. 双重检查page[]字符串:必须以斜杠开头,不能包含http://或www.。例如:"/en/us/new-york-ny/10013/migraine-weather/3709_pc"。2. 用电脑浏览器访问你拼接的完整网址http://www.accuweather.com + page,确认能打开预测页面。 |
| LED始终只快速闪烁一下,然后长时间熄灭,没有进入慢闪或间歇闪烁循环。 | 网页抓取逻辑失败,可能网页结构已更新。 | 1. 打开串口监视器,查看输出。如果显示“not found”,说明multiFind或client.find没有匹配到内容。2. 你需要按照第4.2节的方法,重新分析目标网页的HTML源代码,更新matchList0中的锚点字符串和client.find中的特征字符串。 |
| LED闪烁模式混乱,或不按预期闪烁。 | 代码中hi和lo的值设置不当;LED引脚接错。 | 1. 检查LED_PIN的定义是否与你实际连接的引脚一致(板载LED是0,我外接的是15)。2. 在“预警模式”和“安全模式”的代码段后,添加串口打印语句,输出当前的hi和lo值,确认逻辑判断正确。 |
5.3 硬件相关问题
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| LED完全不亮。 | 电源未接通;LED或电阻焊接虚焊;正负极接反;引脚定义错误。 | 1. 用万用表检查Feather板子上的3.3V和GND之间是否有电压。2. 检查LED两端电压,在点亮时应接近3.3V。3. 将LED正负极短接到3.3V和GND上,测试LED本身是否完好。4. 确认代码中digitalWrite(LED_PIN, HIGH)是在控制正确的引脚。 |
| 设备工作一段时间后死机或不稳定。 | 电源功率不足;代码逻辑陷入死循环。 | 1. 如果使用USB充电头供电,确保其输出电流不小于1A。劣质或功率不足的充电头可能导致ESP8266在发射Wi-Fi信号时电压跌落而复位。2. 检查代码中所有循环都有超时或退出条件,避免因网络异常导致永久等待。 |
最后,关于网页抓取伦理的提醒:我们的代码设置了POLL_INTERVAL为15分钟,这是一个非常保守和礼貌的访问频率,不会对AccuWeather的服务器造成显著负担。如果你计划大规模部署或更频繁地抓取,请务必尊重网站的robots.txt规则,并考虑使用官方API(如果提供且符合你的需求)。这个项目是出于个人学习和非商业目的,在合理使用原则下探索物联网可能性的一次实践。