====== GPRS-логгер для теплицы ====== {{ :projects:gprslogger:gprslogger_overview.jpg?nolink&700 |}} * Платформы: Iskra Neo * Языки программирования: Arduino (C++) * Тэги: GPRS, SIM900, теплица, мониторинг погоды, конструктор ПВХ. ===== Что это? ===== Каждый садовод и дачник знает, чтобы собрать хороший урожай, необходимо уйму времени провести на огороде. Но вряд ли кому-нибудь придёт в голову находиться круглые сутки возле любимой грядки. А ведь так хочется, находясь дома или на работе, быть уверенным, что твоим помидоркам ничего не угрожает. Одним из инструментов для решения этой задачи является мобильный Интернет. В этой статье мы расскажем как собрать устройство, способное осуществлять постоянный мониторинг окружающей среды, считывая данные с различных датчиков, и передавать их на популярный сервис «[[http://narodmon.ru/|народный мониторинг]]», используя GPRS. ===== Что нам понадобится? ===== {{ :projects:gprslogger:gprslogger_parts.jpg?nolink&700 |}} - [[amp>product/iskra-neo?utm_source=proj&utm_campaign=gprslogger&utm_medium=wiki | Iskra Neo]] - [[amp>product/arduino-gprs-shield?utm_source=proj&utm_campaign=gprslogger&utm_medium=wiki | GPRS Shield]] - [[amp>product/arduino-troyka-shield?utm_source=proj&utm_campaign=gprslogger&utm_medium=wiki | Troyka Shield]] - [[amp>product/soil-moisture-sensor?utm_source=proj&utm_campaign=gprslogger&utm_medium=wiki | Датчик влажности почвы]] - [[amp>product/gas-sensor-mq2?utm_source=proj&utm_campaign=gprslogger&utm_medium=wiki | Датчик газа MQ2 ]] - [[amp>product/troyka-light-sensor?utm_source=proj&utm_campaign=gprslogger&utm_medium=wiki | Датчик освещённости (Troyka-модуль) ]] - [[amp>product/temperature-humidity-sensor-dht11?utm_source=proj&utm_campaign=gprslogger&utm_medium=wiki | Датчик температуры и влажности DHT11 ]] - [[amp>product/usb-cable-micro?utm_source=proj&utm_campaign=gprslogger&utm_medium=wiki | Кабель USB (A — Micro USB)]] - Крепёжные элементы: винты, саморезы, гайки - SIM-карта - Конструктор ПВХ ===== Как собрать? ===== - Возьмите платформу Iskra Neo и нижнюю панель кубa (7x7), соедините её с помощью винтов и гаек, так чтобы гайки располагались между панелью и платформой.{{ :projects:gprslogger:gprslogger_build1.jpg?nolink&700 |}} - Вставьте сим-карту в GPRS Shield и установите его сверху на платформу Iskra Neo.{{ :projects:gprslogger:gprslogger_build2.jpg?nolink&700 |}} - Боковые панели куба (7x7) соедините с нижней панелью (7x7). Установите Troyka Shield сверху на GPRS Shield.{{ :projects:gprslogger:gprslogger_build3.jpg?nolink&700 |}} - Возьмите панель куба (7x5) и, спользуя винты или саморезы, закрепите на ней датчик газа MQ2 и датчик температуры и влажности DHT11.{{ :projects:gprslogger:gprslogger_build4.jpg?nolink&700 |}} - Используя 3-проводные шлейфы, подключите через Troyka Shield датчик газа MQ2 к аналоговому пину ''A2'', а цифровой датчик DHT11 к цифровому пину ''11''. Далее панель куба (7x5) с установленными датчиками закрепите между боковыми панелями куба, так чтобы датчики были направлены внутрь.{{ :projects:gprslogger:gprslogger_build5.jpg?nolink&700 |}} - Возьмите переднюю панель куба (6x7) и соедините её с нижней панелью и панелью с датчиками, заранее откусив бокорезами секции, мешающие выводу антенны. Прикрутите антенну в SMA-разъём.{{ :projects:gprslogger:gprslogger_build6.jpg?nolink&700 |}} - Возьмите датчик освещённости, прикрутите его к панели для крепления Troyka-модулей, и с помощью четырёх панелей конструктора (3x2) соорудите «башенку».{{ :projects:gprslogger:gprslogger_build7.jpg?nolink&700 |}} - В продолжение установите «башенку» с датчиком на верхнюю панель куба (7x7), и установите получившуюся конструкцию на левую, правую и заднюю панели. Подключите датчик освещённости через стандартный 3-проводные шлейф через Troyka Shield к аналоговому пину ''A4''.{{ :projects:gprslogger:gprslogger_build8.jpg?nolink&700 |}} - Заднюю панель куба (7x3) закрепите между левой и правой панелями внизу, напротив разъёмов USB и внешнего питания платформы Iskra Neo, заранее отделив бокорезами секции, мешающие ей устойчиво зафиксироваться.{{ :projects:gprslogger:gprslogger_build9.jpg?nolink&700 |}} - Подключите датчик влажности почвы через 3-проводной шлейф через Troyka Shield к аналоговому пину ''A0''. В результате должна получиться такая схема:{{ :projects:gprslogger:gprslogger_scheme.png?nolink&700 |}} - Возьмите заднюю панель куба (7x2) и установите её между левой и правой панелями в верхней части. В заключение возьмите панель крестиков (1x6) и установите между задними панелями (7x3) и (7x2).{{ :projects:gprslogger:gprslogger_build10.jpg?nolink&700 |}} ===== Алгоритм ===== * Сразу после подачи питания проверяем есть ли связь с GPRS устройством. * Если связи нет, повторяем запрос снова и ждём подтверждения связи. * Пытаемся установить GPRS-соединение с заданными настройками. * Если GPRS-соединения нет, повторяем попытку снова и ждём подтверждения. * Пытаемся подключиться к серверу. * Если подключение нет, повторяем запрос снова и ждём подтверждения сервера. * Считываем значения с датчиков. * Формируем и оправляем TCP-запрос в специальном формате для «народного мониторинга» * Повторяем весь выше описанный цикл каждые 5 минут. ===== Исходный код ===== // библиотека для работы с GPRS устройством #include // библиотека для эмуляции Serial-порта // она нужна для работы библиотеки GPRS_Shield_Arduino #include // библиотека для работы с датчиком DHT11 #include // даём разумное имя для пина к которому подключен датчик DHT11 #define DHT11_PIN 11 // даём разумное имя для пина к которому подключен датчик влажности почвы #define MOISTURE_PIN A0 // даём разумное имя для пина к которому подключен датчик уровня CO2 #define MQ2_PIN A2 // даём разумное имя для пина к которому подключен датчик уровня освещённости #define LIGHT_PIN A4 // IMEI GPRS Shield, он указан на лицевой стороне шилда // по IMEI устройство будет идентифицироваться в проекте // поэтому он должен быть уникальным #define IMEI "868204005187692" // часть запроса в специальном формате для народного мониторинга, содержащая: // IMEI устройства, название фирмы и GPS-координаты #define CLIENT "#"IMEI"#Амперка#55.7467#37.6627#2.0\r\n" // интервал между отправками данных в миллисекундах (5 минут) #define INTERVAL 300000 // размер массива, содержащий TCP-запрос #define LEN 370 // буфер для отправки данных на народный мониторинг // согласно установленной сервисом форме char tcpBuffer[LEN]; // переменная для хранения времени работы программы // с последнего запуска отправки данных на сервер unsigned long previousMillis = 0; // переменная температуры воздуха int temp = 0; // переменная влажности воздуха int humi = 0; // переменная влажности почвы int moisture = 0; // переменная уровня CO2 int mq2 = 0; // переменная уровня освещённости int light = 0; // создаём объект класса dht11 dht11 DHT; // создаём объект класса GPRS и передаём в него объект Serial1 GPRS gprs(Serial1); // можно указать дополнительные параметры — пины PK и ST // по умолчанию: PK = 2, ST = 3 // GPRS gprs(Serial1, 2, 3); void setup() { // открываем последовательный порт для мониторинга действий в программе Serial.begin(9600); // открываем Serial-соединение с GPRS Shield Serial1.begin(9600); } void loop() { // включаем GPRS шилд gprs.powerOn(); // проверяем есть ли связь с GPRS устройством while (!gprs.init()) { // если связи нет, ждём 1 секунду // и выводим сообщение об ошибке // процесс повторяется в цикле // пока не появится ответ от GPRS устройства delay(1000); Serial.print("GPRS Init error\r\n"); } // вывод об удачной инициализации GPRS Shield Serial.println("GPRS init success"); delay(3000); // пытаемся установить GPRS-соединение // с заданными настройками, которые предоставляются операторами связи while (!gprs.join("internet.beeline.ru", "beeline", "beeline")) { // если GPRS-соединения нет // выводим сообщение об ошибке и ждём 1 секунду // процесс повторяется в цикле // пока не появится положительный ответ от GPRS устройства Serial.println("Gprs join network error"); delay(1000); } // выводим сообщение об удачной установке GPRS-соединения Serial.println("GPRS OK"); // получаем и выводим локальный IP адрес Serial.print("IP Address is "); Serial.println(gprs.getIPAddress()); // пытаемся подключиться к серверу // указывая тип соединения, адрес сервера и номер порта while (!gprs.connect(TCP, "narodmon.ru", 8283)) { // если сервер не отвечает или отвечает ошибкой // выводим сообщение об ошибке и ждём 1 секунду // процесс повторяется в цикле // пока не появится положительный ответ от сервера Serial.println("Connect error"); delay(1000); } // выводим сообщение об удачном подключении к серверу Serial.println("Connect success"); // вызываем функцию считывания всех показателей с датчиков readSensors(); // выводим показания датчиков в последовательный порт serialPrint(); // вызываем функцию, которая формирует и отправляет tcp-запрос // в специальном формате для «народного мониторинга» tcpRequest(); // разрываем все GPRS-соединения gprs.close(); // деактивируем интерфейс GPRS gprs.disconnect(); // выводим сообщение об удачном завершении операции Serial.println("OK"); // выключаем GPRS-шилд gprs.powerOff(); // проверяем не прошел ли нужный интервал времени while (millis() - previousMillis < INTERVAL) { // ждём 5 минут } //если прошел, то сохраняем текущее время previousMillis = millis(); } // функция записи данных с датчиков в массив // в специальном формате для «народного мониторинга» void tcpRequest() { /* помните, что при выполнении операций с массивами символов, например strcat(str1, str2); контроль нарушения их границ не выполняется, поэтому программист должен сам позаботиться о достаточном размере массива str1, позволяющем вместить как его исходное содержимое, так и содержимое массива str2 */ // добавляем к строке tcpBuffer строку CLIENT strcat(tcpBuffer, CLIENT); // функция добавления в TCP-запрос значения температуры воздуха tcpTemp(); // функция добавления в TCP-запрос значения влажности воздуха tcpHumi(); // функция добавления в TCP-запрос значения влажности почвы tcpMoisture(); // функция добавления в TCP-запрос состояния уровня CO2 tcpGas(); // функция добавления в TCP-запрос значения освещённости tcpLight(); // добавляем к строке tcpBuffer два символа «##», // которые свидетельствуют об окончании запроса strcat(tcpBuffer, "##"); // отправляем массив TCP-запроса на сервис «народного мониторинга» gprs.send(tcpBuffer); // очищаем буфер clearTcpBuffer(); } // Функция добавление в TCP-запрос данные о температуре воздуха void tcpTemp() { // переменная для символьного представления // значения температуры воздуха char s_temp[8]; // преобразуем целое число 10 системы исчисления // из переменной temp в строковое представление в массив s_temp[] itoa(temp, s_temp, 10); // добавляем к буферу символы «.00», для дробной части strcat(s_temp, ".00"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу уникальный серийный номера датчика // получаем его путём добавления к IMEI GPRS-шилда названия датчика strcat(tcpBuffer, IMEI); strcat(tcpBuffer, "T01"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу строку s_temp strcat(tcpBuffer, s_temp); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу время актуальности показаний // если показания датчиков передаются немедленно, // то данный параметр передавать не надо strcat(tcpBuffer, " "); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу названия датчика strcat(tcpBuffer, "Датчик температуры"); strcat(tcpBuffer, "\r\n"); } // Функция добавление в TCP-запрос данные о влажности воздуха void tcpHumi() { // переменная для символьного представления // значения влажности воздуха char s_humi[8]; // преобразуем целое число 10 системы исчисления // из переменной humi в строковое представление в массив s_humi[] itoa(humi, s_humi, 10); // добавляем к буферу символы «.00», для дробной части strcat(s_humi, ".00"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу уникальный серийный номера датчика // получаем его путём добавления к IMEI GPRS-шилда названия датчика strcat(tcpBuffer, IMEI); strcat(tcpBuffer, "H01"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу строку s_humi strcat(tcpBuffer, s_humi); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу время актуальности показаний // если показания датчиков передаются немедленно, // то данный параметр передавать не надо strcat(tcpBuffer, " "); strcat(tcpBuffer, "#"); // добавляем к буферу названия датчика strcat(tcpBuffer, "Датчик влажности"); strcat(tcpBuffer, "\r\n"); } void tcpMoisture() { // переменная для символьного представления // значения влажности почвы char s_moisture[8]; // преобразуем целое число 10 системы исчисления // из переменной moisture в строковое представление в массив s_moisture[] itoa(moisture, s_moisture, 10); // добавляем к буферу символы «.00», для дробной части strcat(s_moisture, ".00"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу уникальный серийный номера датчика // получаем его путём добавления к IMEI GPRS-шилда названия датчика strcat(tcpBuffer, IMEI); strcat(tcpBuffer, "H02"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу строку s_moisture strcat(tcpBuffer, s_moisture); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу время актуальности показаний // если показания датчиков передаются немедленно, // то данный параметр передавать не надо strcat(tcpBuffer, " "); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу названия датчика strcat(tcpBuffer, "Датчик влажности почвы"); strcat(tcpBuffer, "\r\n"); } void tcpGas() { // переменная для символьного представления значения CO2 char s_mq2[8]; // преобразуем целое число 10 системы исчисления // из переменной mq2 в строковое представление в массив s_mq2[] itoa(mq2, s_mq2, 10); // добавляем к буферу символы «.00», для дробной части strcat(s_mq2, ".00"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу уникальный серийный номера датчика // получаем его путём добавления к IMEI GPRS-шилда названия датчика strcat(tcpBuffer, IMEI); strcat(tcpBuffer, "MQ2"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу строку s_gas strcat(tcpBuffer, s_mq2); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу время актуальности показаний // если показания датчиков передаются немедленно, // то данный параметр передавать не надо strcat(tcpBuffer, " "); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу названия датчика strcat(tcpBuffer, "Датчик CO2"); strcat(tcpBuffer, "\r\n"); } void tcpLight() { // переменная для символьного представления значения освещённости char s_light[8]; // преобразуем целое число 10 системы исчисления // из переменной light в строковое представление в массив s_light[] itoa(light, s_light, 10); // добавляем к буферу символы «.00», для дробной части strcat(s_light, ".00"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу уникальный серийный номера датчика // получаем его путём добавления к IMEI GPRS-шилда названия датчика strcat(tcpBuffer, IMEI); strcat(tcpBuffer, "L01"); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу строку s_light strcat(tcpBuffer, s_light); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу время актуальности показаний // если показания датчиков передаются немедленно, // то данный параметр передавать не надо strcat(tcpBuffer, " "); // добавляем к буферу разделительный символ «#» strcat(tcpBuffer, "#"); // добавляем к буферу названия датчика strcat(tcpBuffer, "Датчик освещения"); strcat(tcpBuffer, "\r\n"); } // функция считывания показателей с датчиков void readSensors() { // считывание данных с датчика DHT11 DHT.read(DHT11_PIN); // присваивание переменной temp значения температуры воздуха temp = DHT.temperature; // присваивание переменной humi значения влажности воздуха humi = DHT.humidity; // считывание значения с датчика влажности почвы moisture = analogRead(MOISTURE_PIN); // считывание значения с датчика уровня CO2 mq2 = analogRead(MQ2_PIN); // считывание значения с датчика уровня освещённости light = analogRead(LIGHT_PIN); // преобразовываем аналоговое 10-битное значение // датчика влажности почвы в диапазон (от 0% до 100%) moisture = map(moisture, 0, 1023, 0, 100); // преобразовываем аналоговое 10-битное значение // датчика уровня CO2 в диапазон (от 0ppm до 8000ppm) mq2 = map(mq2, 0, 1023, 0, 8000); // преобразовываем аналоговое 10-битное значение // датчика уровня освещённости в диапазон (от 0Lx до 2000Lx) light = map(light, 0, 1023, 2000, 0); } void clearTcpBuffer() { for (int t = 0; t < LEN; t++) { // очищаем буфер, // присваивая всем индексам массива значение 0 tcpBuffer[t] = 0; } } // функция вывода значения датчиков в последовательный порт void serialPrint() { Serial.print("temp = "); Serial.println(temp); Serial.print("humi = "); Serial.println(humi); Serial.print("moisture = "); Serial.println(moisture); Serial.print("CO2 = "); Serial.println(mq2); Serial.print("light = "); Serial.println(light); Serial.println(""); } Для работы выше приведённого скетча вам понадобятся две библиотеки: * [[https://github.com/amperka/gprs-shield|Библиотека SIM900 для удобства работы с платой]] * Библиотека {{:projects:gprslogger:dht11.rar|DHT11}} позволяющая считывать данные с датчика DHT11. ===== Регистрация GPRS-логгера в «Народном мониторинге» ===== Как же нам проверить показания датчиков после того, как мы отправили их на сервер «Народного мониторинга? Помимо хранение данных с различных датчиков, «Народный мониторинг» позволяет видеть их в реальном времени через Интернет с привязкой к карте, а также строить на их основании графики и таблицы. При чем загружать на сайт можно самые разные данные: это может быть информация о климатических условиях или, например, данные о напряжении в сети, потребляемой энергии, даже фото и видео с веб-камер. Для просмотра значений с датчиков необходимо выполнить следующее: - Зайдите в браузере на сервис «[[http://narodmon.ru/|народный мониторинг]]».{{ :projects:gprslogger:gprslogger_map.png?nolink&683 |}} - Зарегистрируйтесь и пройдите авторизацию. - Добавьте в список новое устройство: Датчики Добавить моё устройство и введите IMEI вашего GPRS Shield. После этого откроется меню со списком датчиков, где можно будет просмотреть данные за определённый промежуток времени, построить на их основании графики или же вывести значения в таблицу.{{ :projects:gprslogger:gprslogger_map2.png?nolink&683 |}} ===== Демонстрация работы устройства ===== {{youtube>KDrEfaBqe6Y?large}} ===== Что дальше? ===== Данное устройство можно использовать в самых разнообразных целях. Например если к нему подключить автономное питание, можно узнавать если ли в доме электричество, а с помощью [[amp>collection/proximity-sensors?utm_source=proj&utm_campaign=gprslogger&utm_medium=wiki | датчиков пространства]] предостеречь ваш дом от проникновения недоброжелателей.