Мультисенсорная погодная станция на Arduino MKR1000: молнии, газ, УФ, температура, влажность, давление
Компоненты и материалы
| Arduino MKR1000 | 1 шт. |
| Датчик молний MOD-1016 (AS3935) | 1 шт. |
| Газовый датчик DFRobot MQ-9 (CO/горючие газы) | 1 шт. |
| Датчик влажности и температуры Adafruit HTU21D-F | 1 шт. |
| Датчик давления/высоты/температуры Adafruit BMP085/BMP180 | 1 шт. |
| УФ-датчик SparkFun ML8511 | 1 шт. |
| RTC ChronoDot v2.1 (DS3231) | 1 шт. |
| SparkFun Photon Weather Shield | 1 шт. |
| NeoPixel (адресные светодиоды) | 1 шт. |
| Макетная плата, перемычки | набор |
О проекте
Автор хотел построить полноценную погодную станцию с расширенным набором параметров: не только температура/влажность/давление, но и молнии, УФ-индекс, газы. Проблема обычных Arduino — мало аппаратных прерываний. MKR1000 решает это: у него 8 аппаратных прерываний!

Выбор датчиков
Датчики делятся на три типа интерфейсов:
- Аналоговые: свет (в т.ч. ИК/УФ), направление ветра, газы, тензодатчики…
- I2C: температура, влажность, барометр, акселерометр, гироскоп…
- Прерывания: дождемеры (kipper), анемометры (ветромеры), датчики молний, таймеры…
MKR1000 имеет достаточно выводов для всех типов.
Важно про напряжение: MKR1000 работает на 3.3В логике. Если датчик на 5В — нужны уровнесдвигатели (level shifters). Все датчики автора — 3.3В-совместимые, куплены у Adafruit/SparkFun ($5–20, газовые $10–20). MOD-1016 (AS3935) — $26 с антенной.
Распределение пинов MKR1000
Автор составил таблицу назначений (также в коде как комментарий):
| Пин | Назначение |
|---|---|
| A0 | Зарезервирован (DAC) |
| A1 | ML8511 УФ-датчик |
| A2 | Световой датчик (видимый) |
| A3 | TMP36 локальная температура |
| A4 | MQ-9 газовый датчик |
| A5 | Датчик влажности почвы |
| A6 | Анемометр (скорость ветра)? |
| 0 | Кнопка (HW INT) |
| 1 | Датчик молний AS3935 (HW INT) |
| 2 | Анемометр (прерывание на оборот) (HW INT) |
| 3 | Дождемер (HW INT) |
| 4,5 | Свободны |
| 6 | Пьезо-зумер (общий с встроенным LED) |
| 7 | NeoPixel вывод |
| 11,12 | I2C SDA, SCL |
Начало работы: по одному датчику
Для новичков: подключайте и тестируйте датчики по одному. Загружайте примеры кода, ставьте библиотеки, меняйте номера пинов на свои. Когда каждый работает отдельно — объединяйте в общий скетч.
Автор использовал внешний RTC ChronoDot, так как встроенный RTC MKR1000 не удерживал время от батарейки.

Оранжевые перемычки — 3.3В питание датчиков. Легко отключать ненужный датчик, выдергивая перемычку.
Нюансы библиотек
Некоторые библиотеки не работают с MKR1000 / Arduino IDE 1.6.7 «из коробки». Проблема — старые макросы ATOMIC_* из AVR libc (убраны в IDE 1.6.5). Нужно патчить библиотеку или искать обновлённую версию. Автор рекомендует покупать модули у Adafruit/SparkFun — они поддерживают библиотеки.
WiFi и шифрование
MKR1000 имеет встроенный WiFi и криптографический ускоритель (ATECC508A) для безопасной передачи данных. В этом проекте WiFi не используется (версия 2), но совет: обязательно обновите библиотеку WiFi101 до последней версии, иначе модуль не определится. Спасибо Charif Mahmoudi за подсказку.

Советы по аналоговым датчикам
- Аналоговый выход читается
analogRead(). Обычно нуженmap()или формула. - Резистивные датчики требуют дополнительную схему (делитель напряжения).
- TMP36: напряжение → °C:
(V - 0.5) * 100. Для °F:C * 9/5 + 32. - Датчики с малыми сигналами (тензодатчики, влажность почвы) — лучше купить модуль со встроенным усилителем.
Советы по I2C
- I2C = 2 провода (SDA данные, SCL такт) + GND + VCC.
- У каждого устройства свой 7-битный адрес. Избегайте дублей адресов.
- Нужны подтягивающие резисторы на SDA/SCL (на самом дальнем устройстве). Многие модули уже имеют их на плате.
- Полезно добавить последовательные резисторы 220 Ом (для 3.3В) в линии SDA и SCL — повышает надёжность.

Что делать с данными?
Сейчас вывод в Serial Monitor. Можно добавить LCD, SD-карту или отправку в облако через WiFi. Идея: несколько станций с GPS — триангуляция ударов молний по амплитуде!
Примечание 2017-11-29: Аппаратные прерывания на MKR1000 не заработали. Автор готовит материалы для класса 9-го класса (13–14 лет) на базе Adalogger M0 + SD-карта + WiFi. Сайт класса: arduinoclass.pbworks.com
Идея визуализации: лампа IKEA SODERSVIK с NeoPixel вместо белых LED — цвет = температура, анимация = ветер, IR-пульт для переключения режимов.

Код
Основной скетч мониторинга (Arduino)
/* RTC-Weather-Sensors_v6_MKR1000 by Zonker Harris Spring 2016 */
#include
#include
#include
#include
// NeoPixel и Lightning библиотеки требуют обновления прерываний — временно отключены
// #include
Adafruit_BMP085_Unified bmp = Adafruit_BMP085_Unified(10180);
Adafruit_HTU21DF htu = Adafruit_HTU21DF();
int UVOUT = A1;
int lightOut = A2;
int tmp36out = A3;
int mq9out = A4;
int sounderPin = 6;
int neopixelPin = 7;
// RTC переменные
int addrRTC = 0x68;
int seconds, seconds1, seconds10;
int minutes, minutes1, minutes10;
int hours, hours1, hours10;
int day, date, month, years;
int temprtc, tempfrtc;
void setup() {
Serial.begin(9600); delay(1000);
Serial.println("i2c Sensor Deck");
pinMode(UVOUT, INPUT); pinMode(lightOut, INPUT); pinMode(tmp36out, INPUT);
pinMode(sounderPin, OUTPUT); pinMode(neopixelPin, OUTPUT);
if (!bmp.begin()) { Serial.println("BMP085/180 not found!"); while(1); }
displaySensorDetails();
if (!htu.begin()) { Serial.println("HTU21D-F not found!"); while(1); }
}
void loop() {
beep(50, 2);
// RTC
Wire.beginTransmission(0x68); Wire.write(0); Wire.endTransmission();
Wire.requestFrom(0x68, 13);
while (Wire.available()) {
seconds = Wire.read(); minutes = Wire.read(); hours = Wire.read();
day = Wire.read(); date = Wire.read(); month = Wire.read(); years = Wire.read();
int a1secs = Wire.read(); int a1mins = Wire.read(); int a1hrs = Wire.read();
int a1daydate = Wire.read(); int a2mins = Wire.read(); int a2hrs = Wire.read();
int a2daydate = Wire.read(); int rtcstatus = Wire.read(); int aginginfo = Wire.read();
temprtc = Wire.read(); tempfrtc = Wire.read();
}
seconds10 = (seconds & 0xF0) >> 4; seconds1 = seconds & 0x0F;
minutes10 = (minutes & 0xF0) >> 4; minutes1 = minutes & 0x0F;
hours10 = ((hours & 0x20) >> 5) * 2 + ((hours & 0x10) >> 4);
hours1 = hours & 0x0F;
years += 2000;
temprtc = (temprtc & 0x7F) + ((tempfrtc & 0xC0) >> 6) * 0.25;
get_date();
Serial.print("ChronoDot - "); Serial.print(hours10); Serial.print(hours1); Serial.print(":");
Serial.print(minutes10); Serial.print(minutes1); Serial.print(":"); Serial.print(seconds10); Serial.print(seconds1);
Serial.print(" 20"); Serial.print(years); Serial.print(" "); Serial.print(month); Serial.print(" "); Serial.print(date);
Serial.print(" \t"); Serial.print(temprtc); Serial.println(" C");
delay(100);
// BMP180
sensors_event_t event; bmp.getEvent(&event);
float BMPtemperature; bmp.getTemperature(&BMPtemperature);
float BMPtempF = BMPtemperature * 1.8 + 32;
Serial.print("Temp: "); Serial.print(BMPtemperature); Serial.print(" C ("); Serial.print(BMPtempF); Serial.print(" F) \t");
if (event.pressure) {
Serial.print("BMP180 - Pres: "); Serial.print(event.pressure); Serial.print(" hPa\t");
float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
Serial.print("Alt: "); Serial.print(bmp.pressureToAltitude(seaLevelPressure, event.pressure)); Serial.println(" m");
} else Serial.println("Sensor error");
delay(100);
// HTU21D-F
float HTUtemperature = htu.readTemperature();
float HTUtempF = HTUtemperature * 1.8 + 32;
Serial.print("HTU21-DF - Temp: "); Serial.print(HTUtemperature); Serial.print(" C (");
Serial.print(HTUtempF); Serial.print(" F)\tHum: "); Serial.print(htu.readHumidity()); Serial.println("%");
delay(100);
// TMP36
int reading = averageAnalogRead(tmp36out);
float tmp36voltage = 0.0032258064516129 * reading; // 3.3V / 1023
float tmp36temperatureC = (tmp36voltage - 0.5) * 100;
Serial.print("TMP36 - temp: "); Serial.print(tmp36temperatureC); Serial.print(" C \t");
float tmp36temperatureF = tmp36temperatureC * 9.0 / 5.0 + 32.0;
Serial.print(tmp36temperatureF); Serial.print(" F, out: "); Serial.print(tmp36voltage); Serial.println("v");
delay(100);
// TEMT6000 видимый свет
int vLevel = averageAnalogRead(lightOut);
float newVOutVolts = 0.0032258064516129 * vLevel;
Serial.print("TEMT6000 out: "); Serial.println(vLevel);
delay(100);
// ML8511 UV
int uvLevel = averageAnalogRead(UVOUT);
float newOutVolts = 0.0032258064516129 * uvLevel;
float uvIntensity = mapfloat(newOutVolts, 0.99, 2.8, 0.0, 15.0);
Serial.print("ML8511 UV out: "); Serial.print(uvLevel); Serial.print(" / UV Intensity (mW/cm^2): "); Serial.println(uvIntensity);
delay(100);
// MQ-9 газ
int MQ9volts = analogRead(mq9out);
Serial.print("MQ-9 Gas: "); Serial.println(MQ9volts, DEC);
delay(100);
Serial.println(""); delay(3500); // цикл ~5 сек
}
// --- Вспомогательные функции ---
int averageAnalogRead(int pinToRead) {
byte numberOfReadings = 8;
unsigned int runningValue = 0;
for (int x = 0; x < numberOfReadings; x++) runningValue += analogRead(pinToRead);
runningValue /= numberOfReadings;
return runningValue;
}
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 duration, int count) {
for (int i = 0; i < count; i++) {
digitalWrite(sounderPin, HIGH); delay(duration);
digitalWrite(sounderPin, LOW); delay(duration);
}
}
// RTC функции (decToBcd, bcdToDec, set_time, get_date и др.) — см. полный код в оригинале
Усреднение аналоговых чтений (переиспользуемый фрагмент)
int averageAnalogRead(int pinToRead) {
byte numberOfReadings = 8;
unsigned int runningValue = 0;
for (int x = 0; x < numberOfReadings; x++) runningValue += analogRead(pinToRead);
runningValue /= numberOfReadings;
return runningValue;
}
I2C-сканер адресов
#include
void setup() { Wire.begin(); Serial.begin(9600); Serial.println("\nI2C 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 == 3) Serial.print(" - AS3935 Lightning");
if (address == 64) Serial.print(" - HTU21D Humidity/Temp");
if (address == 104) Serial.print(" - DS1307 RTC (ChronoDot)");
if (address == 119) Serial.print(" - BMP180 Pressure/Temp");
Serial.println(); nDevices++;
} else if (error == 4) {
Serial.print("Unknown error at 0x"); if (address < 16) Serial.print("0"); Serial.println(address, HEX);
}
}
if (nDevices == 0) Serial.println("No I2C devices\n"); else Serial.println("done\n");
delay(5000);
}
Демо-код ML8511 (адаптирован для 3.3В)
int UVOUT = A1;
void setup() { Serial.begin(9600); pinMode(UVOUT, INPUT); Serial.println("ML8511 example"); }
void loop() {
int uvLevel = averageAnalogRead(UVOUT);
float newOutVolts = 0.0032258064516129 * uvLevel;
float uvIntensity = mapfloat(newOutVolts, 0.99, 2.8, 0.0, 15.0);
Serial.print("ML8511 out: "); Serial.print(uvLevel); Serial.print(" / UV Intensity: "); Serial.println(uvIntensity);
delay(100);
}
// averageAnalogRead и mapfloat — как выше
Схемы
Автор не нашёл все компоненты в Fritzing, поэтому нарисовал принципиальную схему вручную.

Производственный процесс
- Интеграция данных сенсоров в Raspberry Pi: практическое руководство
- Датчик слежения за линией на Raspberry Pi — простой детектор двери/линии KY‑033
- Построение надёжного Temperature & Sensor API на Raspberry Pi с модулем GY‑91
- Создайте автоматизированную аэропоническую систему с Raspberry Pi и датчиком влажности
- Мастерство GPIO Raspberry Pi: подключение PIR-датчика движения на B+/Model 2
- Интеграция импульсного радара Acconeer A111 с Raspberry Pi: практическое руководство
- Подключение, калибровка и программирование PIR-датчика HC‑SR501 с Raspberry Pi
- Python/MicroPython логгер датчиков: бесшовное сохранение данных в Google Sheets
- Передача данных Adafruit 10DOF IMU с Raspberry Pi 2 в Azure Event Hubs на Windows 10 IoT Core
- Корректное выключение и спящий режим для Raspberry Pi — схема управления питанием DIY