Мониторинг состояния реки: Arduino-система контроля качества воды
Компоненты и расходные материалы
| Arduino MKR1000 (MKR FOX 1200) | × | 1 |
| Датчик уровня воды | × | 1 |
| pH-датчик | × | 1 |
| Светодиод (белый) | × | 1 |
| Резистор 220 Ом | × | 2 |
| Фоторезистор | × | 1 |
| Провода-перемычки | × | 1 комплект |
Необходимые инструменты
- Паяльник
- Пилка по металлу
Приложения и онлайн-сервисы
- Arduino Web Editor
- Sigfox
- Wia
О проекте
Обзор
Река или озеро — отличное место для отдыха. Но если вода загрязнена, вся радость пропадает. Многие люди зависят от речной воды для скота и не могут позволить давать животным грязную воду.
Представьте: вы живете у реки. Не хотите просыпаться ночью от наводнения. Или сосед сбрасывает бензин в реку — вы не хотите, чтобы животные заболели.
MyRiver решает все эти проблемы. Устройство работает в изолированных зонах благодаря Sigfox, так что доступно всем. Оно измеряет pH воды, уровень загрязнения инновационным методом и уровень воды. Мониторинг в реальном времени на телефоне или компьютере через Wia. Мгновенные уведомления при проблемах.
Функциональность
Устройство на базе Arduino MKR FOX, рассчитано на удаленную работу. Размещается в реке/озере, мониторится откуда угодно. Благодаря низкопотребляющему режиму SAM D работает долго от 2 АА батареек.
Измерения каждые 30 минут, затем сон (минимум 20 мин).
MKR FOX считывает:
- pH реки — pH-датчик;
- Уровень воды — датчик уровня;
- Загрязнение нерастворимыми веществами — светодиод светит на фоторезистор; если показания низкие, в воде твердое вещество (нефть, бензин, ртуть и др.).
Данные отправляются в Sigfox → Wia → обработка и визуализация. Пользователь получает уведомления.
Буфер (12 байт) кодируется в HEX для Sigfox. Wia конвертирует обратно в строку, парсит в значения датчиков, обрабатывает, шлет уведомления при выходе за нормы.
Read Sensors— считывает pH, уровень воды, фоторезистор в переменные;Format Buffer— объединяет значения в 12-байтовый буфер для Sigfox;Parse Data— отправляет буфер в Sigfox.
MKR FOX может слать только 12 байт через Sigfox. Максимальное сырое значение каждого датчика — 1024 (10-битный АЦП), поэтому каждый датчик получает 4 байта (1/3 буфера). Arduino конвертирует целые в строки, дописывает ведущие нули до 4 символов, склеивает в 12-байтовую строку.
Пример: фоторезистор = 620 → 0620; уровень воды = 24 → 0024. Объединяются в буфер и отправляются.
Устройство в действии
Фото функциональности (лучше смотреть видео выше).
Преимущества
Пользователь получает:
- Простой мониторинг уровня воды, pH и загрязнения из любой точки в любое время;
- Мгновенные уведомления о загрязнении (растворимых/нерастворимых веществ) или наводнении;
- Простота использования и монтажа;
- Работа в любой стране с покрытием Sigfox.
Сборка проекта
Шаг 1: Необходимое оборудование
Все компоненты паяются вместе. Материалы:
- Arduino MKR FOX 1200
- pH-датчик
- Датчик уровня воды
- Фоторезистор
- Белый светодиод
- 2 резистора 220Ω
- Провода-перемычки
Шаг 2: Подключение схемы
Схема может показаться запутанной (компоненты не на макетке). При сложностях скачайте Fritzing-файл и перемещайте провода на экране.
Как должна выглядеть готовая схема:
Подготовка MKR FOX
Подключение батарейного отсека и антенны. Батарейный отсек подключается ПОСЛЕ датчиков. Остальная проводка — в разделе Final.
Шаг 3: Разбор кода
3 основные части:
- Read Sensors
- Format Buffer
- Parse Data
Read Sensors
struct GetValue {
int riverLevel() {
const int val = analogRead(A2);
if(proDebug) { Serial.print(" River Level "); Serial.println(val); }
return val;
}
int riverPh() {
const int val = (analogRead(A1) / 5 * 3.3);
if(proDebug) { Serial.print(" River Ph "); Serial.println(val); }
return val;
}
int riverPol() {
const int val = analogRead(A3);
if(proDebug) { Serial.print(" River Pollution "); Serial.println(val); }
return val;
}
};
Структура с методами для каждого датчика.
Format Buffer
String data(int level, int ph, int pol) {
int values[3] = {level, ph, pol};
String newValues;
for(int i = 0; i < 3; i++) {
if(values[i] < 10) { newValues += "000"; Serial.println("Option 1"); }
else if(values[i] < 100) { newValues += "00"; Serial.println("Option 2"); }
else if(values[i] < 1000) { newValues += "0"; Serial.println("Option 3"); }
newValues += values[i];
Serial.print(" Run "); Serial.print(i); Serial.print("/3 "); Serial.println(newValues);
}
return newValues;
}
Цикл добавляет ведущие нули до 4 символов на значение, склеивает в строку.
Parse Data
void parseData(String data) {
SigFox.beginPacket();
SigFox.print(data);
int ret = SigFox.endPacket();
}
Отправка буфера в Sigfox.
Шаг 4: Настройка интеграции Wia + Sigfox
Wia — профессиональная IoT-платформа: виджеты на вебе/мобильном, блок-схемы (Flows) для обработки данных. Данные = события (events).
Нужен аккаунт Sigfox и Wia. Приложение Wia на iOS/Android для уведомлений.
Настройка вебхука в Sigfox (много скриншотов в оригинале):
- Type:
Data Uplink - Channel:
URL - Custom Payload Configuration: не заполнять
- URL Pattern:
https://api.wia.io/v1/events - HTTP Method:
POST - Header:
Authorisation: Bearer <ваш_токен> - Content-Type:
application/json - Body: код из ссылки (в оригинале)
- OK
Шаг 5: Настройка Flow в Wia
Flow конвертирует HEX → String, парсит 3 значения, конвертирует: загрязнение и уровень → «High»/«OK», pH → реальное значение pH. Уведомляет при выходе за нормы.
Пошаговая инструкция со скриншотами (в оригинале много изображений). Ниже код функций Wia (копируйте в Functions).
Wia Functions
processPayload
if(input.body) {
output.body.name = "value";
output.body.data = toByteArrayStr(input.body.data.sigfoxData);
}
function toByteArrayStr(sigfoxData) {
let result = Buffer(sigfoxData, 'hex');
return result.toString('utf8');
}
processPollution
if(input.body.data) {
let data = input.body.data;
if(data == "POLLUTED") {
output.body.name = "waterLevel";
output.body.data = data;
} else { while(1); }
}
processPh
let minVal = 6.5; let maxVal = 8.5;
if(input.body.data) {
let data = input.body.data;
if(data > maxVal || data < minVal) {
output.body.name = "waterPh";
output.body.data = data;
} else { while(1); }
}
processLevel
if(input.body.data) {
let data = input.body.data;
if(data == "HIGH") {
output.body.name = "waterLevel";
output.body.data = data;
} else { while(1); }
}
riverPollution
let minVal = 200; let maxVal = 0;
if(input.body.data.sigfoxData) {
let data = input.body.data.sigfoxData;
let processedData = toByteArrayStr(data);
let newData = cutString(processedData);
output.body.name = "waterPollution";
output.body.data = processData(newData);
}
function toByteArrayStr(sigfoxData) {
let result = Buffer(sigfoxData, 'hex');
return result.toString('utf8');
}
function cutString(data) {
let newVal = data[8]; newVal += data[9]; newVal += data[10]; newVal += data[11];
let finalVal = parseInt(newVal);
return finalVal;
}
function processData(data) {
if(data < minVal) return "POLLUTED"; else return "OK";
}
riverPh
let minVal = 300; let maxVal = 600;
if(input.body.data.sigfoxData) {
let data = input.body.data.sigfoxData;
let processedData = toByteArrayStr(data);
let newData = cutString(processedData);
let finalVal = (newData / 73.1428571);
let valueToSend = finalVal.toFixed(2);
output.body.name = "waterPh";
output.body.data = valueToSend;
}
function toByteArrayStr(sigfoxData) {
let result = Buffer(sigfoxData, 'hex');
return result.toString('utf8');
}
function cutString(data) {
let newVal = data[4]; newVal += data[5]; newVal += data[6]; newVal += data[7];
let finalVal = parseInt(newVal);
return newVal;
}
riverLevel
let minVal = 0; let maxVal = 200;
if(input.body.data.sigfoxData) {
let data = input.body.data.sigfoxData;
let processedData = toByteArrayStr(data);
let newData = cutString(processedData);
output.body.name = "waterLevel";
output.body.data = processData(newData);
}
function cutString(data) {
let newVal = data[0]; newVal += data[1]; newVal += data[2]; newVal += data[3];
let finalVal = parseInt(newVal);
return finalVal;
}
function toByteArrayStr(sigfoxData) {
let result = Buffer(sigfoxData, 'hex');
return result.toString('utf8');
}
function processData(data) {
if(data > maxVal) return "HIGH"; else return "OK";
}
Wia Emails (шаблоны уведомлений)
riverPollutionEmail
Automatic Messaging System Data Warning - myWater device ${trigger.source.device.id} detected a problem in the stream. Warning - Water Is Polluted by Insoluble Substances. Information Device ${trigger.source.device.id} Raw Data ${input.body.data}
riverPhEmail
Automatic Messaging System Data Warning - myWater device ${trigger.source.device.id} detected a problem in the stream. Warning - Water Ph is not Appropriate. Water is Contaminated. Information Device ${trigger.source.device.id} Raw Data ${input.body.data}
riverLevelEmail
Automatic Messaging System Data Warning - myWater device ${trigger.source.device.id} detected a problem in the stream. Warning - Water Level is too High, River may be flooding. Information Device ${trigger.source.device.id} Raw Data ${input.body.data}
Настройка переменных
При получении данных Wia обрабатывает их: если значение в пределах мин/макс — безопасно. Лимиты pH и уровня заданы, но порог фоторезистора нужно определить экспериментально:
void setup() {
pinMode(1, OUTPUT);
Serial.begin(9600);
while(!Serial) {};
digitalWrite(1, HIGH);
}
void loop() {
const int val = analogRead(A3);
Serial.println(val);
delay(5000);
}
Переменная proDebug — если включена, нужен USB-PC и открытый Serial Monitor (для отладки). По умолчанию false для полевой работы.
Библиотеки
- Sigfox (c) 2016 Arduino LLC, LGPL
- ArduinoLowPower (c) 2016 Arduino LLC, LGPL
Финал
Подключите Arduino к ПК, залейте скетч, убедитесь что батарейный отсек подключен и схема исправна. Далее — корпус.
Корпус из пластиковой бутылки — экологично и дешево. Устройство выглядит так:
Фото процесса изготовления корпуса (листайте в оригинале).
Установка в реку: идете на реку во время высокой воды (прилива) — критично, чтобы устройство не ложно срабатывало на прилив как на наводнение.
Заклейте горлышко бутылки скотчом против проникновения воды.
Тестировали в грязной реке — все тревожки сработали верно.
При установке зонда: вода должна быть у низа датчика уровня, но полностью покрывать фоторезистор. Закрепите на берегу скотчем или болтами.
Предыстория
Смотришь в окно на когда-то красивую реку, а там зеленая слизь — хочется переехать. А если река затопит дом ночью — точно переедешь.
Я подумал над этими проблемами и как решить их одним устройством — родился MyRiver.
В городе у реки — предупредит, если сосед сльет масло. На ферме — предупредит, что вода грязная перед поем скота, и даст время увести скот перед наводнением.
Только pH недостаточно: нерастворимые вещества (ртуть, металлы) не меняют pH. Придумали новый метод уникальный для проекта: фоторезистор измеряет свет от LED, проходящий сквозь воду. Любая примесь меняет пропускание света.
Код
MyRiver — полный код
Полный скетч доступен в разделе Code.
Схемы
Fritzing-файл: schematics_lXO5SiusZO.fzz
Производственный процесс
- Понимание кислородного (O2) датчика: роль, размещение и признаки отказа
- Создайте домашний монитор температуры на Raspberry Pi с MCP9808, InfluxDB и Grafana
- Датчик эмоций и ЭЭГ: низкобюджетное железо для детекции эмоций в реальном времени
- Устройство точного измерения давления и расхода воздуха на аналоговом датчике
- Проект с датчиком DHT11: индикация температуры светодиодами и пьезо-динамиком на Arduino
- UnifiedWater v1: полный набор датчиков качества воды на Arduino MKR GSM
- 3D RGB Arduclock — Bluetooth-часы на адресных LED с RTC
- Простой ИК-датчик препятствий на Arduino: ИК-светодиод и фотодиод
- Интерактивная игра с джойстиком и LED-обратной связью на Arduino
- Find Me: Умный поисковик вещей на Arduino и Bluetooth