Почему стоит заботиться о качестве воздуха (помимо лёгких)

Давайте будем честными — большинство из нас не думает о качестве воздуха, пока не начинает кашлять из-за сильного смога. Но дело в том, что данные о качестве воздуха повсюду, и их можно использовать. Правительствам они нужны для принятия политических решений, городским планировщикам — для развития городов, а обычным людям — чтобы решить, остаться ли сегодня дома или пойти на прогулку.

Проблема? Традиционный мониторинг качества воздуха дорог, требует специального оборудования и часто предоставляет данные только из нескольких фиксированных мест. А что, если я скажу вам, что можно построить сложную систему прогнозирования качества воздуха в реальном времени за долю стоимости, используя IoT-датчики и немного изобретательности?

В следующих нескольких тысячах слов мы как раз и построим такую систему. Мы разработаем систему, которая не просто измеряет качество воздуха — она его прогнозирует. И да, кода будет много.

Понимание архитектуры системы

Прежде чем углубляться в детали, давайте определимся, что мы на самом деле строим. Наша система состоит из трёх взаимосвязанных слоёв: сенсорного (аппаратное обеспечение, собирающее данные), сетевого (подключение и шлюзы) и аналитического (прогнозирование и визуализация).

graph TB A["Датчики качества воздуха
PM2.5 | PM10 | CO2
NO2 | O3 | Температура"] -->|Wi-Fi/LTE| B["Шлюз IoT
Агрегация данных
Локальная обработка"] B -->|HTTPS| C[" облачная платформа
База данных временных рядов
Модели машинного обучения"] C -->|API| D["Веб-панель
Визуализация в реальном времени
Прогнозы"] C -->|WebSocket| E["Мобильное приложение
Пуш-уведомления
Рекомендации по здоровью"] F["Исторические данные
Погодные условия
Данные о трафике"] -.->|Обучение модели| C style A fill:#e1f5ff style B fill:#fff3e0 style C fill:#f3e5f5 style D fill:#e8f5e9 style E fill:#fce4ec

Эта архитектура позволяет нам масштабироваться от мониторинга одного района до целого мегаполиса. Каждый компонент слабо связан, что означает возможность обновления отдельных частей без перестройки всей системы.

Аппаратные компоненты: выбор датчиков

Сердцем любой системы мониторинга качества воздуха является набор датчиков. Теперь мы измеряем не только PM10 — мы охватываем весь спектр. Вот что мы будем использовать:

Датчики частиц измеряют концентрацию взвешенных частиц (PM2.5, PM10). Лазерные датчики пыли, такие как SDS011, обеспечивают отличную точность по разумной цене. Это как глаза вашей системы для обнаружения мельчайших проблем в воздухе.

Датчики газа — это то, где всё становится интереснее. Датчик MQ135 обнаруживает CO2, бензол, NH3 и NOx — всё, что может вызвать недовольство вашей дыхательной системы. Это не лабораторная точность, но удивительно надёжно для экологического мониторинга.

Датчики окружающей среды измеряют температуру, влажность и атмосферное давление. Почему? Потому что качество воздуха не существует в вакууме (буквально — воздух существует в вакууме, но вы понимаете, о чём я). Эти параметры влияют на поведение загрязнителей и имеют решающее значение для точного прогнозирования.

Для микроконтроллера мы будем использовать NodeMCU (ESP8266) или Arduino MKR WiFi 1010. NodeMCU предлагает отличный баланс между подключением, вычислительной мощностью и стоимостью. Он оснащён Wi-Fi, имеет контакты GPIO для подключения датчиков и достаточную память для буферизации локальных данных.

Настройка среды разработки

Прежде чем датчики увидят электроны, давайте подготовим среду разработки. Предполагается, что вы знакомы с Arduino IDE, но если нет, то кривая обучения будет пологой — представьте себе «инструкции IKEA, но понятные».

Сначала установите Arduino IDE, если ещё не сделали этого. Добавьте поддержку платы NodeMCU:

  1. Откройте Arduino IDE и перейдите в «Файл» > «Настройки».
  2. В разделе «Дополнительные URL-адреса менеджера плат» добавьте: http://arduino.esp8266.com/stable/package_esp8266com_index.json.
  3. Перейдите в «Инструменты» > «Плата» > «Менеджер плат» и найдите «ESP8266».
  4. Установите последнюю версию.

Теперь нам нужны библиотеки для наших датчиков. Перейдите в «Файл» > «Включить библиотеку» > «Управление библиотеками» и установите:

  • MQ135 от bestfitme (для газового датчика);
  • SDS011 dust sensor library;
  • DHT sensor library от Adafruit (для измерения температуры и влажности);
  • ArduinoJson от Benoit Blanchon (для работы с JSON).

Сборка сенсорного узла: сборка оборудования

Пришло время физической сборки. Здесь теория встречается с макетной платой и иногда с разочарованием:

Соединения контактов NodeMCU:
- D1 (GPIO5) → контакт данных DHT22
- D2 (GPIO4) → выход MQ135 → A0
- D4 (GPIO2) → контакт RX SDS011
- D3 (GPIO0) → контакт TX SDS011
- GND → общее заземление (DHT, MQ135, SDS011)
- 3.3V → DHT22 VCC, MQ135 VCC, SDS011 VCC
- Vin → внешнее питание 5 В

Почему Vin вместо USB-подключения для питания? Потому что датчики — это энергоёмкие маленькие паразиты, а USB не может обеспечить стабильное напряжение под нагрузкой. Поверьте мне — я узнал это на собственном опыте через обожжённые резисторы и слёзы.

Прошивка: где происходит волшебство

Теперь код. Это прошивка, которая запускается на вашем NodeMCU, собирает данные и связывается с облаком:

#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>
#include <ArduinoJson.h>
#include <DHT.h>
#include <SoftwareSerial.h>
// Конфигурация Wi-Fi
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
const char* cloudServer = "your-cloud-endpoint.com";
// Конфигурация датчиков
#define DHT_PIN D1
#define MQ135_PIN A0
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
SoftwareSerial sdsSerial(D3, D4); // RX, TX для SDS011
// Глобальные переменные
float temperature = 0;
float humidity = 0;
float pm25 = 0;
float pm10 = 0;
int mq135Value = 0;
unsigned long lastTransmission = 0;
const unsigned long TRANSMISSION_INTERVAL = 300000; // 5 минут
// Структура для показаний датчиков
struct SensorReading {
  float temperature;
  float humidity;
  float pm25;
  float pm10;
  int mq135;
  unsigned long timestamp;
};
void setup() {
  Serial.begin(115200);
  delay(100);
  // Инициализация датчиков
  dht.begin();
  sdsSerial.begin(9600);
  Serial.println("\n\nСистема инициализируется...");
  // Подключение к Wi-Fi
  connectToWiFi();
}
void loop() {
  // Проверка подключения к Wi-Fi
  if (WiFi.status() != WL_CONNECTED) {
    connectToWiFi();
  }
  // Чтение датчиков
  readDHTSensor();
  readMQ135Sensor();
  readSDS011Sensor();
  // Передача данных, если интервал истёк
  if (millis() - lastTransmission > TRANSMISSION_INTERVAL) {
    transmitData();
    lastTransmission = millis();
  }
  delay(2000);
}
void readDHTSensor() {
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  if (!isnan(h) && !isnan(t)) {
    humidity = h;
    temperature = t;
    Serial.printf("DHT22 - Температура: %.1f°C, Влажность: %.1f%%\n", temperature, humidity);
  }
}
void readMQ135Sensor() {
  int rawValue = analogRead(MQ135_PIN);
  mq135Value = rawValue;
  // Преобразование в PPM (упрощённо; требуется калибровка для производства)
  float ppm = (rawValue / 1024.0) * 500;
  Serial.printf("MQ135 - Сырое значение: %d, Ожидаемый CO2: %.0f ppm\n", rawValue, ppm);
}
void readSDS0