MKR1000 多模态环境传感器平台:闪电、气体、紫外、温湿度、气压全监测
组件与物料清单
| 组件 | 数量 |
|---|---|
| Arduino MKR1000 | 1 |
| MOD-1016 闪电探测器 (AS3935) | 1 |
| DFRobot MQ-9 可燃气体传感器 | 1 |
| Adafruit HTU21D-F 温湿度传感器 | 1 |
| Adafruit BMP085/BMP180 气压/海拔/温度传感器 | 1 |
| SparkFun ML8511 紫外线传感器 | 1 |
| ChronoDot RTC 实时时钟模块 | 1 |
| SparkFun Photon Weather Shield(备用扩展板) | 1 |
| Adafruit NeoPixel 灯珠 | 1 |
| 面包板、跳线若干 | 1 |
项目背景
我热爱观测天气,想自建一套能长期记录、绘图分析的气象站。普通气象站只能看 "当下" 或过去一小时数据,我要做更多。MKR1000 拥有 8 个硬件中断引脚,解决了传统 Arduino 中断资源不足的痛点,让我能同时接入:雨量计、风速计、闪电探测器等中断型传感器。

传感器分类与选型建议
- 模拟量输出: 光照(含紫外/红外)、风向、气体、称重…
- I²C 总线: 温度、湿度、气压、加速度计、陀螺仪…
- 中断型: 雨量翻斗、风速计(每转一个脉冲)、闪电探测、定时器…
MKR1000 丰富的 I/O 可同时满足以上所有类型。注意电压兼容: MKR1000 为 3.3V 逻辑,若传感器为 5V 需加电平转换模块。
引脚分配规划
建议先在纸上规划,再写入代码注释备忘。我的分配方案:
- A0:预留 DAC
- A1:ML8511 UV 传感器
- A2:TEMT6000 可见光传感器
- A3:TMP36 本地温度传感器
- A4:MQ-9 可燃气体传感器
- A5:土壤湿度传感器
- A6:风速传感器?
- D0 (中断):按键
- D1 (中断):AS3935 闪电探测器
- D2 (中断):风速计脉冲
- D3 (中断):雨量翻斗
- D4、D5:预留
- D6 (共板载 LED):蜂鸣器
- D7:NeoPixel 数据线
- D11/D12:I²C SDA/SCL
入门建议:逐个调试传感器
新手建议:一次只接一个传感器,跑通官方例程并读出串口数据后,再接入下一个。所有传感器单独验证通过后,再集成到主程序。
笔者保留了 I²C 接口的 ChronoDot RTC(DS3231),因为 MKR1000 自带 RTC 电池供电下走时不准。面包板上橙色跳线分别给各传感器供电,调试时拔掉即可屏蔽该传感器,非常方便。

库兼容性避坑指南
部分老旧库在新版 Arduino IDE (1.6.5+) 中因 ATOMIC_* 宏被移除而编译报错。遇到橙色报错先判断是库还是自身代码问题,搜索 "arduino + 报错信息" 通常能找到修补方案。推荐优先购买 Adafruit/SparkFun 官方模块,其库维护及时。
WiFi 与加密提示
MKR1000 内置加密引擎可保障物联网数据传输安全,但本项目暂未实现上云。关键提示: 必须使用最新版 WiFi101 库,否则无法识别板载 WiFi 模块。
模拟传感器读数技巧
- 简单模拟输出:
analogRead()后用map()或数学公式换算物理量。 - 电阻式元件:通常需分压电路,可加电位器微调。
- TMP36 等需换算公式:电压 → 摄氏度 → 华氏度。
- 微弱信号传感器(称重、土壤湿度、加速度):建议买带运放的成品模块。
I²C 总线使用技巧
- 需 SDA(数据)、SCL(时钟) 两根线,主从轮流发送数据。
- 每设备有唯一 7-bit 地址,避免地址冲突。若必须用同地址多设备,需额外加使能引脚切换。
- 总线最远端需加上拉电阻(通常 4.7kΩ),部分模块已板载。
- 笔者经验:在 SDA/SCL 串联 220Ω 电阻(3.3V 系统)或 330Ω(5V 系统)可显著提升通讯稳定性。

数据可视化构想
目前仅串口输出。未来可接 LCD、SD 卡记录,或利用板载 WiFi 上传云端。设想部署多台设备,通过闪电探测器读数三角定位闪电击中点!
灵感来源:IKEA SODERSVIK 落地灯替换为 NeoPixel 灯珠 + IR 遥控,用灯光可视化风速(云卷云舒)、温度(色温)、12h 温差等气象要素。

核心代码片段
完整监测主程序 (节选关键部分)
/* RTC-Weather-Sensors_v6_MKR1000 by Zonker Harris */
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP085_U.h>
#include <Adafruit_HTU21DF.h>
// NeoPixel 与闪电库因中断向量冲突暂注释,待 IDE 更新后恢复
//#include <Adafruit_NeoPixel.h>
Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10180);
Adafruit_HTU21DF htu = Adafruit_HTU21DF();
// 引脚定义
int UVOUT = A1, lightOut = A2, tmp36out = A3, mq9out = A4;
int sounderPin = 6, neopixelPin = 7;
// ChronoDot RTC 变量 (地址 0x68)
int addrRTC = 0x68;
int seconds, minutes, hours, day, date, month, years;
int temprtc, tempfrtc;
void setup() {
Serial.begin(9600); delay(1000);
pinMode(UVOUT, INPUT); pinMode(lightOut, INPUT); pinMode(tmp36out, INPUT);
pinMode(sounderPin, OUTPUT); pinMode(neopixelPin, OUTPUT);
if (!bmp.begin()) { Serial.println("BMP180 not found"); while(1); }
if (!htu.begin()) { Serial.println("HTU21D not found"); while(1); }
// RTC 首次设置时间后注释掉
// seconds=0; minutes=41; hours=20; day=7; date=3; month=1; years=16; initChrono();
}
void loop() {
beep(50, 2); // 蜂鸣器提示循环开始
// 1. 读取 ChronoDot RTC 时间
Wire.beginTransmission(0x68); Wire.write(0); Wire.endTransmission();
Wire.requestFrom(0x68, 13);
// ... 解析 BCD 码获取时分秒、日期、温度 ...
get_date();
Serial.print("ChronoDot - "); Serial.print(hours); Serial.print(":"); Serial.print(minutes); Serial.print(":"); Serial.println(seconds);
// 2. BMP180 气压/温度/海拔
sensors_event_t event; bmp.getEvent(&event);
float BMPtemperature; bmp.getTemperature(&BMPtemperature);
if (event.pressure) {
Serial.print("BMP180 - Pres: "); Serial.print(event.pressure); Serial.print(" hPa");
float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
Serial.print(" Alt: "); Serial.print(bmp.pressureToAltitude(seaLevelPressure, event.pressure)); Serial.println(" m");
}
// 3. HTU21D 温湿度
float HTUtemperature = htu.readTemperature();
Serial.print("HTU21D - Temp: "); Serial.print(HTUtemperature); Serial.print(" C Hum: "); Serial.print(htu.readHumidity()); Serial.println("%");
// 4. TMP36 模拟温度
int reading = averageAnalogRead(tmp36out);
float voltage = 0.0032258064516129 * reading; // 3.3V/1023
float tempC = (voltage - 0.5) * 100;
Serial.print("TMP36 - temp: "); Serial.print(tempC); Serial.println(" C");
// 5. TEMT6000 可见光
int vLevel = averageAnalogRead(lightOut);
Serial.print("TEMT6000 out: "); Serial.println(vLevel);
// 6. ML8511 UV 强度
int uvLevel = averageAnalogRead(UVOUT);
float uvVolts = 0.0032258064516129 * uvLevel;
float uvIntensity = mapfloat(uvVolts, 0.99, 2.8, 0.0, 15.0); // mW/cm²
Serial.print("ML8511 UV: "); Serial.print(uvLevel); Serial.print(" / UV Intensity: "); Serial.println(uvIntensity);
// 7. MQ-9 可燃气体
int MQ9volts = analogRead(mq9out);
Serial.print("MQ-9 Gas: "); Serial.println(MQ9volts);
Serial.println(); delay(3500); // 约 5 秒一个循环
}
// 多次采样平均滤波
int averageAnalogRead(int pin) {
byte n = 8; unsigned int sum = 0;
for (byte i = 0; i < n; i++) sum += analogRead(pin);
return sum / n;
}
// float 版 map()
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void beep(int dur, int cnt) {
for (int i = 0; i < cnt; i++) { digitalWrite(sounderPin, HIGH); delay(dur); digitalWrite(sounderPin, LOW); delay(dur); }
}
// RTC 辅助函数: initChrono, set_date, get_date, set_time, get_time, decToBcd, bcdToDec 等省略
通用模拟读数平均值函数 (Beerware License)
int averageAnalogRead(int pinToRead) {
byte numberOfReadings = 8;
unsigned int runningValue = 0;
for (byte x = 0; x < numberOfReadings; x++) runningValue += analogRead(pinToRead);
runningValue /= numberOfReadings;
return runningValue;
}
I²C 总线扫描器 (自动识别设备地址)
#include <Wire.h>
void setup() { Wire.begin(); Serial.begin(9600); Serial.println("I2C Scanner"); }
void loop() {
byte error, address; int nDevices = 0;
Serial.println("Scanning...");
for (address = 1; address < 127; address++) {
Wire.beginTransmission(address);
error = Wire.endTransmission();
if (error == 0) {
Serial.print("I2C device at 0x"); if (address < 16) Serial.print("0"); Serial.print(address, HEX);
// 已知设备标注
if (address == 0x03) Serial.print(" - AS3935 Lightning");
if (address == 0x40) Serial.print(" - HTU21D Temp/Hum");
if (address == 0x68) Serial.print(" - DS3231 RTC");
if (address == 0x77) Serial.print(" - BMP180 Pressure");
Serial.println(); nDevices++;
}
}
if (nDevices == 0) Serial.println("No I2C devices");
Serial.println("done"); delay(5000);
}
ML8511 UV 传感器演示代码 (3.3V 系统适配版)
int UVOUT = A1;
void setup() { Serial.begin(9600); pinMode(UVOUT, INPUT); Serial.println("ML8511 example"); }
void loop() {
int uvLevel = averageAnalogRead(UVOUT);
float uvVolts = 0.0032258064516129 * uvLevel;
float uvIntensity = mapfloat(uvVolts, 0.99, 2.8, 0.0, 15.0);
Serial.print("ML8511: "); Serial.print(uvLevel); Serial.print(" / UV: "); Serial.println(uvIntensity);
delay(100);
}
// averageAnalogRead 与 mapfloat 同前
原理图
因 Fritzing 库缺件,绘制了简化连接示意图:

制造工艺
- 树莓派传感器数据融合实战:GPS 与 MPU‑9150 IMU 的 I²C 与 UART 接入
- 树莓派接 KY‑033 循迹/门状态传感器:简易 Python 实现
- 树莓派构建稳健传感器 API:基于 GY‑91 多功能模块的 Flask 实现
- 树莓派自动化气雾栽培系统:湿度传感器智能控制
- 掌握树莓派 GPIO:在 B+/Model 2 上接入 PIR 运动传感器
- 将 Acconeer A111 脉冲雷达与树莓派集成:实用指南
- HC‑SR501 PIR 运动传感器与树莓派接线、校准与 Python 编程全指南
- Python/MicroPython 传感器记录仪:无缝写入 Google Sheets
- 树莓派 2 (Windows 10 IoT Core) 采集 Adafruit 10DOF IMU 数据上传 Azure Event Hubs
- 树莓派优雅关机与休眠模式:DIY电源管理电路
- Portenta H7 对接 MAX6675 热电偶温度传感器教程
- 传感器融合如何提升自主系统的可靠性、安全性与效率