GPRS-логгер для теплицы

  • Платформы: Iskra Neo
  • Языки программирования: Arduino (C++)
  • Тэги: GPRS, SIM900, теплица, мониторинг погоды, конструктор ПВХ.

Что это?

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

Одним из инструментов для решения этой задачи является мобильный Интернет. В этой статье мы расскажем как собрать устройство, способное осуществлять постоянный мониторинг окружающей среды, считывая данные с различных датчиков, и передавать их на популярный сервис «народный мониторинг», используя GPRS.

Что нам понадобится?

Как собрать?

  1. Возьмите платформу Iskra Neo и нижнюю панель кубa (7x7), соедините её с помощью винтов и гаек, так чтобы гайки располагались между панелью и платформой.
  2. Вставьте сим-карту в GPRS Shield и установите его сверху на платформу Iskra Neo.
  3. Боковые панели куба (7x7) соедините с нижней панелью (7x7). Установите Troyka Shield сверху на GPRS Shield.
  4. Возьмите панель куба (7x5) и, спользуя винты или саморезы, закрепите на ней датчик газа MQ2 и датчик температуры и влажности DHT11.
  5. Используя 3-проводные шлейфы, подключите через Troyka Shield датчик газа MQ2 к аналоговому пину A2, а цифровой датчик DHT11 к цифровому пину 11. Далее панель куба (7x5) с установленными датчиками закрепите между боковыми панелями куба, так чтобы датчики были направлены внутрь.
  6. Возьмите переднюю панель куба (6x7) и соедините её с нижней панелью и панелью с датчиками, заранее откусив бокорезами секции, мешающие выводу антенны. Прикрутите антенну в SMA-разъём.
  7. Возьмите датчик освещённости, прикрутите его к панели для крепления Troyka-модулей, и с помощью четырёх панелей конструктора (3x2) соорудите «башенку».
  8. В продолжение установите «башенку» с датчиком на верхнюю панель куба (7x7), и установите получившуюся конструкцию на левую, правую и заднюю панели. Подключите датчик освещённости через стандартный 3-проводные шлейф через Troyka Shield к аналоговому пину A4.
  9. Заднюю панель куба (7x3) закрепите между левой и правой панелями внизу, напротив разъёмов USB и внешнего питания платформы Iskra Neo, заранее отделив бокорезами секции, мешающие ей устойчиво зафиксироваться.
  10. Подключите датчик влажности почвы через 3-проводной шлейф через Troyka Shield к аналоговому пину A0. В результате должна получиться такая схема:
  11. Возьмите заднюю панель куба (7x2) и установите её между левой и правой панелями в верхней части. В заключение возьмите панель крестиков (1x6) и установите между задними панелями (7x3) и (7x2).

Алгоритм

  • Сразу после подачи питания проверяем есть ли связь с GPRS устройством.
    • Если связи нет, повторяем запрос снова и ждём подтверждения связи.
  • Пытаемся установить GPRS-соединение с заданными настройками.
    • Если GPRS-соединения нет, повторяем попытку снова и ждём подтверждения.
  • Пытаемся подключиться к серверу.
    • Если подключение нет, повторяем запрос снова и ждём подтверждения сервера.
  • Считываем значения с датчиков.
  • Формируем и оправляем TCP-запрос в специальном формате для «народного мониторинга»
  • Повторяем весь выше описанный цикл каждые 5 минут.

Исходный код

GPRS_Logger.ino
// библиотека для работы с GPRS устройством
#include <GPRS_Shield_Arduino.h>
 
// библиотека для эмуляции Serial-порта
// она нужна для работы библиотеки GPRS_Shield_Arduino
#include <SoftwareSerial.h>
 
// библиотека для работы с датчиком DHT11
#include <dht11.h>
 
// даём разумное имя для пина к которому подключен датчик 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("");
}

Для работы выше приведённого скетча вам понадобятся две библиотеки:

Регистрация GPRS-логгера в «Народном мониторинге»

Как же нам проверить показания датчиков после того, как мы отправили их на сервер «Народного мониторинга? Помимо хранение данных с различных датчиков, «Народный мониторинг» позволяет видеть их в реальном времени через Интернет с привязкой к карте, а также строить на их основании графики и таблицы. При чем загружать на сайт можно самые разные данные: это может быть информация о климатических условиях или, например, данные о напряжении в сети, потребляемой энергии, даже фото и видео с веб-камер.

Для просмотра значений с датчиков необходимо выполнить следующее:

  1. Зайдите в браузере на сервис «народный мониторинг».
  2. Зарегистрируйтесь и пройдите авторизацию.
  3. Добавьте в список новое устройство: Датчики Добавить моё устройство и введите IMEI вашего GPRS Shield.

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

Демонстрация работы устройства

Что дальше?

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