GSM-система контроля за автоматическим поливом

Dron
Offline
Зарегистрирован: 17.06.2016

Доброго всем времени суток.

Хотел бы поделится со всеми одной своей разработкой. Может данная информация окажется кому-то полезной.

Dron
Offline
Зарегистрирован: 17.06.2016

На своей даче я реализовал автоматическую систему полива. Реализована она на готовых электроных таймерах. Один таймер включает насос типа "малыш", погруженный в колодец. Вода поступает в бочку. Бочка - 200 литров. Набор воды обычно происходит через день с 21:00 до 21:20. Другой таймер открывает соленойдный клапан, и подогретая окружающей средой вода поступает в систему капельного полива. Открытие клапана происходит ежедневно с 15:00 до 20:30. И все бы хорошо, но система питается от 220VAC. Бывают отключения электричества. Может засориться магистральный фильтр.  И пр... Одним словом, необходимо отслеживать изменения уровня воды в бочке. Было принято решение разработать устройство. Вот оно:

Dron
Offline
Зарегистрирован: 17.06.2016

Устройство состоит из четырех модулей:

- Arduino Nano;

- GSM-модуль SIM800L;

- блок питания 220VAC/5VDC;

- понижающий DC-регулятор.

Выходное напряжение DC-регулятора установлено на уровне 4.18 вольт. От него запитывается SIM800L. При этом уровне напряжения SIM (по АТ запросу) выдает заряд аккумулятора около 100%. Вообще, от этого регулятора SIM работает очень стабильно. 

Arduino и SIM в любой момент могут быть извлечены из устройства (просто, для удобства)

Хотелось бы отметить, что у Arduino Nano есть один несомненно большой плюс. Это наличие готового USB разъема. Нет необходимоти тащить ноут на дачу. Программу можно корректировать с телефона через ArduinoDroid. Можно отследить ход выполнения программы через Serial (опять же с телефона).

На первых трех аналоговых входах реализован кондуктометрический датчик. К ним и к GND подключаются электроды на разных уровнях в бочке. GND является общим электродом и подключается на самое дно бочки. Электроды на входах А0, А1 и А2 подключаются на нижнем, среднем и верхнем уровнях бочки соответственно. Как всем нам известно, значения на аналоговых входах может меняться от 0 до 1024. Значение более 1020 означает, что данный уровень водой не достигнут. Пороговое значение, при котором уровень считается достигнутым в программе установлен равным 800 (исходя из экспериментов проведенных в ванной). После установки устройства на месте, пороговое значение будет скорректированно для каждого уровня.

И так... Каков функционал данного устройства... При изменении уровня в бочке, оно звонит "хозяину". При этом мы не знаем, увеличился уровень или уменьшился. Да, конечно, можно было бы просто послать SMS-ку, и указать в ней что да как... Но исходящие SMS платны. Я не жадный, но дело принципа. Дача находится в 30-ти километрах от дома. Но это уже другой регион. Соответственно, роуминг. На этом наши сотовые операторы незаслуженно (я так считаю) наживаются. Поэтому, вместо того, чтоб слать SMS-ки, устройство нам будет звонить, а мы будем этот звонок игнорировать. Мы лучше сами позвоним на устройство, переключимся на телефоне на DTMF-приемник и в тоновом режиме примем текущие значения уровней в бочке:

Судя по картинке, бочка пуста (полив прошел успешно).

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

Данный способ передачи информации может быть использован и для других целей (показания датчиков и пр.). Так же в тоновом режиме можно удаленно управлять устройствами с телефона.

А так выглядит Serial Monitor, если устройство функционирует нормально:

А вот и программа:

#include <SoftwareSerial.h>
SoftwareSerial mySerial(2, 3); // RX, TX
int ch = 0;
String val = "";  //переменная для приема данных от GSM-модуля
int level_1=0;    //переменная 1-го уровня
int level_2=0;    //переменная 2-го уровня
int level_3=0;    //переменная 3-го уровня

int level_11=0;    //промежуточная переменная 1-го уровня
int level_22=0;    //промежуточная переменная 2-го уровня
int level_33=0;    //промежуточная переменная 3-го уровня

String Level="AT+VTS="; //переменная формирования АТ команд для передачи в  GSM-модуль
String L_1="";
String L_2="";
String L_3="";

unsigned long time;
unsigned long time1=0;

void setup() {
digitalWrite(A0, 1);  //включаем подтягивающий резистор аналогового входа А0 (уровень 1)
digitalWrite(A1, 1);  //включаем подтягивающий резистор аналогового входа А1 (уровень 2)
digitalWrite(A2, 1);  //включаем подтягивающий резистор аналогового входа А2 (уровень 3)

  if(analogRead(A0)>800) level_1=level_11=0; else level_1=level_11=1; //устанавливаем порог для уровня 1 раным 800
  if(analogRead(A1)>800) level_2=level_22=0; else level_2=level_22=1; //устанавливаем порог для уровня 2 раным 800
  if(analogRead(A2)>800) level_3=level_33=0; else level_3=level_33=1; //устанавливаем порог для уровня 3 раным 800

delay(20000);           //время на инициализацию модуля
Serial.begin(19200);    //открываем COM-порт
mySerial.begin(19200);  //открываем порт GSM-модуля
mySerial.println("AT+CLIP=1");  //включаем АОН
delay(100);
mySerial.println("AT+VTD=4");  //устанавливаем длительность в тоновом режиме равной 0,4сек
delay(100);

mySerial.println("ATD+79ХХХХХХХХХ;");  //звоним на свой номер
delay(16000); 
mySerial.println("ATH0");  //разрываем связь через 16 сек
delay(100);
}

void loop() {

L_1=String(analogRead(A0)); //переводим "цифру" в String 
L_2=String(analogRead(A1)); //переводим "цифру" в String
L_3=String(analogRead(A2)); //переводим "цифру" в String
  
  if (mySerial.available()) {       //если GSM модуль что-то послал нам,
    while (mySerial.available()) {  //сохраняем входную строку в переменную val
      ch = mySerial.read();
      val += char(ch);
      delay(10);
    }
    if (val.indexOf("RING") > -1) {           //если звонок обнаружен, то проверяем номер
      if (val.indexOf("79ХХХХХХХХХ") > -1) {  //если номер звонящего наш
        Serial.println("--- MASTER RING DETECTED ---");
        digitalWrite(13, HIGH);   //включаем светодиод 
        mySerial.println("ATA");  //устанавливаем соединение
        delay(1000);              //задержка для включения DTMF-ресивера на телефоне
        mySerialLevel();          //отправляем значения уровней на DTMF-ресивер 
        mySerial.println("ATH0"); //разрываем связь

        digitalWrite(13, LOW);    //выключаем светодиод 
        }
    } else
      Serial.println(val);  //печатаем в монитор порта пришедшую строку
    val = ""; //обнуляем переменную val
  }
  

time = millis();

if((time-time1)>=5000){   //раз в 5 секунд выводим значения уровней на монитор
  
    Serial.print(L_1);    //выводим на монитор значение уровня 1
    Serial.print("\t");   //табуляция
  
    Serial.print(L_2);    //выводим на монитор значение уровня 2
    Serial.print("\t");   //табуляция
  
    Serial.print(L_3);    //выводим на монитор значение уровня 3
    Serial.print("\t");   //табуляция
  
    SerialLevel();        //выводим на монитор сформированную АТ команду
  
    //Serial.print(time); //выводим на монитор значение времени
  
    Serial.println("");   //перенос строки
                            
    time1=time;                      
  }
  
CheckLevel(); //проверяем значения уровеней
//в случае изменения звоним на свой номер

}



/***функция формирования AT+VTS команды для отслеживания в Serial ***/
void SerialLevel(){
  Level+=char(34);

  Level+=L_1[0];
  Level+=',';
  Level+=L_1[1];
  Level+=',';
  
  if(L_1[2]>47&&L_1[2]<58) {
    Level+=L_1[2];
    Level+=','; 
  }
  if(L_1[3]>47&&L_1[3]<58) {
    Level+=L_1[3];
    Level+=','; 
  }
  Level+='#';
  Level+=',';  
  Level+=L_2[0];
  Level+=',';
  Level+=L_2[1];
  Level+=',';

  if(L_2[2]>47&&L_2[2]<58) {
    Level+=L_2[2];
    Level+=','; 
  } 
  
  if(L_2[3]>47&&L_2[3]<58) {
    Level+=L_2[3];
    Level+=','; 
  } 

  Level+='#';
  Level+=','; 
  Level+=L_3[0];
  Level+=',';
  Level+=L_3[1];  
  if(L_3[2]>47&&L_3[2]<58) {
    Level+=','; 
    Level+=L_3[2];
  } 
  if(L_3[3]>47&&L_3[3]<58) {
    Level+=',';
    Level+=L_3[3];
  } 
  Level+=char(34);
  Serial.print(Level);
  Level="AT+VTS=";
  }

/***функция формирования AT+VTS команды для GSM-модуля ***/
void mySerialLevel(){
  Level+=char(34);

  Level+=L_1[0];
  Level+=',';
  Level+=L_1[1];
  Level+=',';
  
  if(L_1[2]>47&&L_1[2]<58) {
    Level+=L_1[2];
    Level+=','; 
  }
  if(L_1[3]>47&&L_1[3]<58) {
    Level+=L_1[3];
    Level+=','; 
  }
  Level+='#';
  Level+=',';  
  Level+=L_2[0];
  Level+=',';
  Level+=L_2[1];
  Level+=',';

  if(L_2[2]>47&&L_2[2]<58) {
    Level+=L_2[2];
    Level+=','; 
  } 
  
  if(L_2[3]>47&&L_2[3]<58) {
    Level+=L_2[3];
    Level+=','; 
  } 

  Level+='#';
  Level+=','; 
  Level+=L_3[0];
  Level+=',';
  Level+=L_3[1];  
  if(L_3[2]>47&&L_3[2]<58) {
    Level+=','; 
    Level+=L_3[2];
  } 
  if(L_3[3]>47&&L_3[3]<58) {
    Level+=',';
    Level+=L_3[3];
  } 
  Level+=char(34);
  mySerial.println(Level);
  delay(15000);
  //while (1) {if(mySerial.available())break;};
  Level="AT+VTS=";
  }

 /*** функция проверки изменения уровней ***/
 void CheckLevel(){
  if(analogRead(A0)>800) level_1=0; else level_1=1; //проверяем изменение уровня 1
  if(analogRead(A1)>800) level_2=0; else level_2=1; //проверяем изменение уровня 2
  if(analogRead(A2)>800) level_3=0; else level_3=1; //проверяем изменение уровня 3
  if(level_1!=level_11||level_2!=level_22||level_3!=level_33){  // если один из уровней изменился

    mySerial.println("ATD+79ХХХХХХХХХ;");  //звоним на свой номер
    delay(16000); 
    mySerial.println("ATH0");  //разрываем связь через 16 сек
    delay(60000);     //делаем задержку на "дребезг зеркала воды"
    level_11=level_1; //запоминаем текущее значение уровня 1 в промежуточной переменной
    level_22=level_2; //запоминаем текущее значение уровня 2 в промежуточной переменной
    level_33=level_3; //запоминаем текущее значение уровня 3 в промежуточной переменной

  };

}

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

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

нельзя просто так сунуть выводы проца без защиты в бак или в грунт.. статика и другого рода наводки напряжения выведут из строя проц.. рано или поздно это случится.

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

можно более подробнее прокоментировать часть /***функция формирования AT+VTS команды для отслеживания в Serial ***/ ?

что куда формирует? тоновые сигналы в линию? какого итогового формата?

тоесть должна быть еще вторая часть которая эти сигналы принимает?

дешевая замена гпрс канала, очень здравая мысль.

Dron
Offline
Зарегистрирован: 17.06.2016

Short Circuit пишет:

нельзя просто так сунуть выводы проца без защиты в бак или в грунт.. статика и другого рода наводки напряжения выведут из строя проц.. рано или поздно это случится.

Можно. На используемых аналоговых входах подключены внутренние подтягивающие резисторы (программно). В результате получается замечательный делитель напряжения от внутреннего резистора к плюсу и от внешнего резистора (воды) к GND.

Dron
Offline
Зарегистрирован: 17.06.2016

Данная функция всего лишь отображает в Serial подготовленную АТ команду. В проге есть другая функция, точно такая же, которая в нужный момент отправляет АТ команду в SIM800L через mySerial.

Dron
Offline
Зарегистрирован: 17.06.2016

Dron пишет:

Short Circuit пишет:

нельзя просто так сунуть выводы проца без защиты в бак или в грунт.. статика и другого рода наводки напряжения выведут из строя проц.. рано или поздно это случится.

Можно. На используемых аналоговых входах подключены внутренние подтягивающие резисторы (программно). В результате получается замечательный делитель напряжения от внутреннего резистора к плюсу и от внешнего резистора (воды) к GND.

Это как раз и является тем, что я назвал кондуктометрическим датчиком.

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

Dron пишет:

Short Circuit пишет:

нельзя просто так сунуть выводы проца без защиты в бак или в грунт.. статика и другого рода наводки напряжения выведут из строя проц.. рано или поздно это случится.

Можно. На используемых аналоговых входах подключены внутренние подтягивающие резисторы (программно). В результате получается замечательный делитель напряжения от внутреннего резистора к плюсу и от внешнего резистора (воды) к GND.

ок, спорить не стану, достаточно почитать опыт других "экпериментаторов" которые пожгли входы таким подключением. там нужно ставить: два диода, токоограничивающий резистор, внешнюю подтяжку, это как минимум, ИМХО.

Short Circuit
Short Circuit аватар
Offline
Зарегистрирован: 17.05.2015

идея с передачей инфы на другую сторону неплаха, очень неплоха.

 

Dron
Offline
Зарегистрирован: 17.06.2016

Да, я тоже так считаю. Именно альтернативным способом передачи данных и управления хотелось со всеми поделиться. А насчет делителя напряжения на внутреннем резисторе... Я не первый раз использую этот вариант. Пока (тьфу, тьфу) ни один вход и ни один проц не сгорел. Надеюсь, и не случится. Если вдруг..., сразу отпишусь и сам буду использовать что-то другое.

Dron
Offline
Зарегистрирован: 17.06.2016

Блиныч, сейчас понял, что не так обозвал тему. Очень странно, что я, как человек открывший тему, не могу поменять ее название. С модераторами здесь на форуме, как я понял, проблема. Придется спамить и открыть еще одну тему с той же инфой, но сдругим названием. Мне очень важно мнение людей уже имевших опыт передачи данных в тоновом режиме. Так как некоторые проблемы в передаче данных все же существуют. Тему, пожалуй, назову: "SIM800L, передача данных в тоновом режиме". Short Circuit, присоединяйся.

Dron
Offline
Зарегистрирован: 17.06.2016

Насчет логина, кажется, тоже погорячился. Нужно было назваться не Dron, а как нибудь более красноречиво. Что-нибудь вроде "Дребезг контактов" :))

Dron
Offline
Зарегистрирован: 17.06.2016
ArtAlexis
Offline
Зарегистрирован: 18.06.2018

Поделитесь, как подключали. Особенно питание.

Спасибо!