Промышленное производство
Промышленный Интернет вещей | Промышленные материалы | Техническое обслуживание и ремонт оборудования | Промышленное программирование |
home  MfgRobots >> Промышленное производство >  >> Manufacturing Technology >> Производственный процесс

Мультисенсорная погодная станция на Arduino MKR1000: молнии, газ, УФ, температура, влажность, давление

Компоненты и материалы

Arduino MKR10001 шт.
Датчик молний MOD-1016 (AS3935)1 шт.
Газовый датчик DFRobot MQ-9 (CO/горючие газы)1 шт.
Датчик влажности и температуры Adafruit HTU21D-F1 шт.
Датчик давления/высоты/температуры Adafruit BMP085/BMP1801 шт.
УФ-датчик SparkFun ML85111 шт.
RTC ChronoDot v2.1 (DS3231)1 шт.
SparkFun Photon Weather Shield1 шт.
NeoPixel (адресные светодиоды)1 шт.
Макетная плата, перемычкинабор

О проекте

Автор хотел построить полноценную погодную станцию с расширенным набором параметров: не только температура/влажность/давление, но и молнии, УФ-индекс, газы. Проблема обычных Arduino — мало аппаратных прерываний. MKR1000 решает это: у него 8 аппаратных прерываний!

Мультисенсорная погодная станция на Arduino MKR1000: молнии, газ, УФ, температура, влажность, давление

Выбор датчиков

Датчики делятся на три типа интерфейсов:

MKR1000 имеет достаточно выводов для всех типов.

Важно про напряжение: MKR1000 работает на 3.3В логике. Если датчик на 5В — нужны уровнесдвигатели (level shifters). Все датчики автора — 3.3В-совместимые, куплены у Adafruit/SparkFun ($5–20, газовые $10–20). MOD-1016 (AS3935) — $26 с антенной.

Распределение пинов MKR1000

Автор составил таблицу назначений (также в коде как комментарий):

ПинНазначение
A0Зарезервирован (DAC)
A1ML8511 УФ-датчик
A2Световой датчик (видимый)
A3TMP36 локальная температура
A4MQ-9 газовый датчик
A5Датчик влажности почвы
A6Анемометр (скорость ветра)?
0Кнопка (HW INT)
1Датчик молний AS3935 (HW INT)
2Анемометр (прерывание на оборот) (HW INT)
3Дождемер (HW INT)
4,5Свободны
6Пьезо-зумер (общий с встроенным LED)
7NeoPixel вывод
11,12I2C SDA, SCL

Начало работы: по одному датчику

Для новичков: подключайте и тестируйте датчики по одному. Загружайте примеры кода, ставьте библиотеки, меняйте номера пинов на свои. Когда каждый работает отдельно — объединяйте в общий скетч.

Автор использовал внешний RTC ChronoDot, так как встроенный RTC MKR1000 не удерживал время от батарейки.

Мультисенсорная погодная станция на Arduino 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 за подсказку.

Мультисенсорная погодная станция на Arduino MKR1000: молнии, газ, УФ, температура, влажность, давление

Советы по аналоговым датчикам

Советы по I2C

Мультисенсорная погодная станция на Arduino MKR1000: молнии, газ, УФ, температура, влажность, давление

Что делать с данными?

Сейчас вывод в 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 MKR1000: молнии, газ, УФ, температура, влажность, давление

Код

Основной скетч мониторинга (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, поэтому нарисовал принципиальную схему вручную.

Мультисенсорная погодная станция на Arduino MKR1000: молнии, газ, УФ, температура, влажность, давление

Производственный процесс

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