Система мониторинга качества воздуха Smart City на базе LoRa
Компоненты и материалы
| Компонент | Количество |
|---|---|
| Arduino UNO | 1 |
| Seeed Base Shield V2 | 1 |
| NodeMCU ESP8266 Breakout Board | 1 |
| Seeed Loudness Sensor (датчик громкости) | 1 |
| Seeed Grove – Air Quality Sensor v1.3 (датчик качества воздуха) | 1 |
| Seeed Grove – Temperature & Humidity Sensor (DHT11) | 1 |
| Reyax LoRa Module (модуль RYLR998) | 1 |
| Seeed Small Solar Panel 80x100mm 1W (солнечная панель) | 1 |
| Seeed LiPo Rider Pro (контроллер заряда LiPo) | 1 |
| Li-Ion аккумулятор 3.7 В (перезаряжаемый) | 1 |
Необходимые инструменты и оборудование
- 3D-принтер (для печати корпуса)
Программное обеспечение и онлайн-сервисы
- Arduino IDE — среда разработки для прошивки микроконтроллеров
- Grandeur — IoT-платформа для управления устройствами и визуализации данных
- Grandeur Canvas — инструмент создания дашбордов с drag-and-drop интерфейсом
Обзор проекта
Smart City и качество воздуха
Согласно Всемирному банку, «умный город» использует технологии для предоставления высокоэффективных городских услуг и создания устойчивой городской среды. Ключевым элементом этой концепции является мониторинг окружающей среды в реальном времени, который позволяет выявлять источники загрязнения и принимать обоснованные управленческие решения — от оптимизации трафика до рекомендаций безопасных пешеходных маршрутов.
Проблема и решение
Загрязнение воздуха в городах несет серьезные риски для здоровья, и своевременные данные критически важны для его снижения. Сети IoT из недорогих сенсорных узлов могут непрерывно фиксировать параметры качества воздуха и метеоусловий. В сочетании с надежным низкоэнергозатратным каналом связи такие сети позволяют городским властям обнаруживать очаги загрязнения и реагировать проактивно.
План проекта
Наше решение отслеживает ключевые экологические показатели — шум, температуру, влажность и качество воздуха — и передает данные беспроводно в облачную платформу. Весь узок питается от солнечной панели мощностью 1 Вт с резервным Li-Ion аккумулятором, обеспечивая автономную работу. Облачный дашборд визуализирует данные в реальном времени, обеспечивая мгновенные инсайты и анализ долгосрочных трендов.
Вместо подключения каждого датчика напрямую к облаку — подхода, увеличивающего энергопотребление и затраты — мы агрегируем данные от нескольких ближайших узлов и передаем их через единый LoRa-шлюз. Дальнодействующие и малоэнергозатратные характеристики LoRa делают его идеальным для плотных городских развертываний.
В качестве бэкенд-платформы выбрана Grandeur благодаря удобным SDK для Arduino, NodeMCU и Raspberry Pi, а также визуальному конструктору дашбордов.
Солнечная энергия обеспечивает устойчивое, не требующее обслуживания питание для удаленных узлов, а Li-Ion аккумулятор гарантирует работу в периоды низкой инсоляции.
Сборка сенсорного узла
Узел интегрирует Grove-датчик качества воздуха, датчик громкости и датчик температуры/влажности DHT11 — все они подключаются к Seeed Base Shield без пайки, что упрощает сборку и обслуживание.
Питание обеспечивается солнечной панелью 1 Вт и контроллером заряда Seeed LiPo Rider Pro, который включает 5-вольтовый повышающий преобразователь для поддержания рабочего напряжения Arduino.
Беспроводную передачу осуществляет трансивер Reyax RYLR998 LoRa. Поскольку модуль работает с логикой 3.6 В, простой делитель напряжения защищает 5-вольтную линию TX Arduino.
Ниже представлена схема соединений, связывающая LoRa-модуль с Base Shield через кастомный адаптер.
После сборки проверьте связь, отправив AT-команду с Arduino на LoRa-модуль.
Загрузка и тестирование
Загрузите предоставленный скетч в Arduino, затем откройте монитор последовательного порта для подтверждения корректной передачи показаний датчиков.
Монтаж в корпус
Распечатайте STL-файлы из раздела вложений. После проверки совместимости разместите компоненты внутри корпуса, зафиксировав их двусторонним скотчем или термоклеем. Установите солнечную панель на крышку и проложите кабели питания через технологическое отверстие.
После фиксации всех компонентов узел готов к полевому развертыванию.
Настройка Grandeur
Grandeur — это IoT-бэкенд, связывающий устройства с пользователями и предоставляющий дашборд в реальном времени. Выполните следующие шаги для настройки проекта:
- Создайте новый проект на grandeur.dev.
- Зарегистрируйте учетную запись пользователя — она понадобится для привязки устройств.
- Определите модель устройства со следующей JSON-схемой:
{"temp":0, "humid":0, "air":0, "noise":0} - Создайте экземпляр устройства, скопируйте его уникальный ID и токен доступа — они понадобятся для прошивки Arduino.
- Привяжите устройство к пользователю через дашборд или API (метод
pairDevice()). - Создайте дашборд в Canvas: добавьте четыре виджета-графика — для температуры, влажности, уровня шума и качества воздуха — и свяжите их с соответствующими переменными.
После авторизации Canvas пользователи смогут входить в систему и просматривать данные в реальном времени. API-ключ и токен устройства потребуются для прошивки шлюза.
Настройка IoT LoRa-шлюза
NodeMCU выступает в роли LoRa-шлюза: он принимает UART-данные от сенсорного узла и передает их в Grandeur через Wi-Fi. Хотя для этой роли можно использовать Raspberry Pi, NodeMCU предлагает компактное и недорогое решение, полностью удовлетворяющее требованиям проекта по пропускной способности и задержкам.
Прошивка шлюза
Прошивка разбирает полученный от LoRa-модуля запятыми-разделенный пакет, преобразует каждое значение в float и отправляет в Grandeur через предоставленный SDK. Ниже приведена основная логика извлечения и передачи данных.
void process_received_data() {
start_pos = inputString.indexOf(start_val);
end_pos = inputString.indexOf(end_val);
String data_string = inputString.substring(start_pos + 1, end_pos - 1);
int firstCommaIndex = data_string.indexOf(',');
int secondCommaIndex = data_string.indexOf(',', firstCommaIndex + 1);
int thirdCommaIndex = data_string.indexOf(',', secondCommaIndex + 1);
String temperature = data_string.substring(0, firstCommaIndex);
String humidity = data_string.substring(firstCommaIndex + 1, secondCommaIndex);
String noise_level = data_string.substring(secondCommaIndex + 1, thirdCommaIndex);
String air_quality = data_string.substring(thirdCommaIndex + 1);
temp = temperature.toFloat();
humid = humidity.toFloat();
noise = noise_level.toFloat();
air = air_quality.toFloat();
inputString = "";
stringComplete = false;
}
Визуализация данных
Программный код
Сенсорный узел (Arduino)
#include "AirQuality.h" // Библиотека Grove air quality
#include "Arduino.h"
#include "DHT.h"
#define DHTPIN 4 // Цифровой пин для DHT11
#define DHTTYPE DHT11 // Модель датчика
DHT dht(DHTPIN, DHTTYPE);
AirQuality airqualitysensor;
int current_quality = -1;
const int sampleWindow = 50; // Окно выборки 50 мс (20 Гц)
unsigned int sample;
float noise;
float temp, humid;
int airQuality;
void setup() {
Serial.begin(115200); // UART для LoRa
airqualitysensor.init(14);
dht.begin();
}
void loop() {
noise = calculate_sound_in_db();
airQuality = calculate_air_quality();
calculate_temp_humid();
String values = String(temp) + "," + String(humid) + "," + String(noise) + "," + String(airQuality);
String cmd = "AT+SEND=0," + String(values.length()) + "," + values;
Serial.println(cmd);
delay(15000);
}
float calculate_sound_in_db() {
unsigned long startMillis = millis();
float peakToPeak = 0;
unsigned int signalMax = 0;
unsigned int signalMin = 1024;
while (millis() - startMillis < sampleWindow) {
sample = analogRead(A1);
if (sample < 1024) {
if (sample > signalMax) signalMax = sample;
else if (sample < signalMin) signalMin = sample;
}
}
peakToPeak = signalMax - signalMin;
float db = map(peakToPeak, 0, 1000, 48, 120);
return db;
}
int calculate_air_quality() {
current_quality = airqualitysensor.slope();
return current_quality;
}
void calculate_temp_humid() {
float h = dht.readHumidity();
float t = dht.readTemperature();
if (isnan(h) || isnan(t)) return;
temp = t;
humid = h;
}
ISR(TIMER1_OVF_vect) {
if (airqualitysensor.counter == 61) {
airqualitysensor.last_vol = airqualitysensor.first_vol;
airqualitysensor.first_vol = analogRead(A0);
airqualitysensor.counter = 0;
airqualitysensor.timer_index = 1;
PORTB = PORTB ^ 0x20;
} else {
airqualitysensor.counter++;
}
}
Шлюз (NodeMCU / Arduino)
/* Подключение SDK и WiFi-библиотеки */
#include <Grandeur.h>
#include <ESP8266WiFi.h>
#include <SoftwareSerial.h>
SoftwareSerial LORA(D7, D8);
/* Конфигурация */
String deviceID = "your_device_id";
String apiKey = "your_api_key";
String token = "your_device_token";
/* Wi-Fi учетные данные */
String ssid = "your_wifi_ssid";
String password = "your_wifi_password";
/* Объекты проекта и устройства */
Grandeur::Project project;
Grandeur::Project::Device device;
/* Состояние подключения и таймер */
int connected = 0;
uint32_t lastUpdate = 0;
String inputString = "";
boolean stringComplete = false;
// Разделители пакета (задаются на передающем узле)
char start_val = ':';
char end_val = '%';
int start_pos = 0;
int end_pos = 0;
float temp = 0;
float humid = 0;
float air = 0;
float noise = 0;
/* Колбэк состояния подключения */
void onConnection(bool status) {
switch (status) {
case CONNECTED:
Serial.println("Device is connected to the cloud.");
connected = 1;
lastUpdate = millis();
return;
case DISCONNECTED:
Serial.println("Device is disconnected from the cloud.");
connected = 0;
return;
}
}
/* Подключение к Wi-Fi */
void connectWiFi() {
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.println(WiFi.localIP());
}
/* Колбэк подтверждения обновления данных */
void dataCallback(const char* code, Var res) {
if (code == "DEVICE-DATA-UPDATED") {
double temp_data = (double) res["update"]["temp"];
double humid_data = (double) res["update"]["humid"];
double noise_data = (double) res["update"]["noise"];
double air_data = (double) res["update"]["air"];
Serial.printf("Temperature: %f Humidity: %f Noise Level: %f Air Quality: %f\n",
temp_data, humid_data, noise_data, air_data);
return;
}
Serial.println("Failed to Update DATA");
return;
}
/* Отправка обновлений в облако */
void sendUpdate() {
Var data;
data["temp"] = temp;
data["humid"] = humid;
data["noise"] = noise;
data["air"] = air;
device.data().set("temp", temp, dataCallback);
device.data().set("humid", humid, dataCallback);
device.data().set("noise", noise, dataCallback);
device.data().set("air", air, dataCallback);
}
void setup() {
Serial.begin(9600);
LORA.begin(115200);
inputString.reserve(200);
connectWiFi();
project = grandeur.init(apiKey, token);
device = project.device(deviceID);
project.onConnection(onConnection);
}
void loop() {
serialEvent();
project.loop(WiFi.status() == WL_CONNECTED);
if (stringComplete) {
process_received_data();
if (connected) sendUpdate();
}
}
void serialEvent() {
while (LORA.available()) {
char inChar = (char)LORA.read();
if (inChar == '\n') {
stringComplete = true;
} else {
inputString += inChar;
}
}
}
Производственный процесс
- Крошечные датчики PM2.5 и VOC обеспечивают мониторинг личного качества воздуха в реальном времени
- Bosch Sensortec представляет AI-сенсор качества воздуха BME688 для снижения риска передачи вирусов
- Renesas расширяет платформу датчика качества воздуха ZMOD4410 встроенным ИИ для улучшенного обнаружения запахов
- Измерение качества воздуха с OpenSensors: руководство по Arduino и Python
- DIY монитор качества воздуха: экономичный детектор, соответствующий стандартам EPA
- Солнечный датчик качества воздуха на Helium: руководство по проектированию и развёртыванию
- IoT-датчики революционизируют мониторинг загрязнения воздуха и охрану общественного здоровья
- IoT-мониторинг качества воздуха: безопасность, продуктивность и устойчивое развитие
- Промышленный IoT-мониторинг качества воздуха: повышение продуктивности и снижение затрат
- Продвинутый углеродный датчик NO₂ улучшает мониторинг качества воздуха в помещениях