Самодельный GSM контроллер отопления на базе Arduino UNO и модуля SIM800L

Здравствуйте!

Появилась потребность изготовить бюджетный контроллер отопления. Да такой, чтобы можно было управлять им удаленно с помощью SMS команд, и получать данные о температуре влажности и состоянии сети 220 вольт.

Схема самодельный GSM контроллер для отопления

Для проекта понадобятся:

Плата Arduino UNO,

GSM модуль SIM800L, Провода,

Твердотельное реле G3MB-202P,

Модуль зарядки LI-ION батарей от MICROUSB,

LI-ION батарея на 3.7 вольта с емкостью от 1000 миллиампер,

Зарядка от телефона, или блок питания на 5 вольт, и от 500 до 1000 миллиампер,

Датчик температуры и влажности DHT11,

Датчик температуры LM35,

4 сопротивления на 250-500 ом, 2k, 2.4k и 10k

Для индикации любой маломощный светодиод,

Еще понадобится: активная сим карта с положительным балансом на счету.

Вилка, розетка, и коробка в корой все поместится.

Коробку для корпуса лучше использовать из негорючего пластика!

Материалы для самодельный GSM контроллер для отопления

Рассмотрим скетч!

В 16 строке скетча, необходимо вписать номера телефонов, с которых будет разрешено принимать SMS команды.

Также в 245 строке необходимо указать номер, на который будут приходить данные о температуре, влажности, состоянии сети 220 вольт и какую программу выполняет контроллер.

Я прописал в этот скетч 8 SMS команд.

Часть из них служат для переключения на интересующую программу, а остальные для запроса отчета, и настройки климат контроля.

Более подробно я рассказываю на видео, которое закреплено в конце этой статьи


//Начало скетча


#include "SoftwareSerial.h"
#include "SimpleDHT.h"
int p8 = 11, t_1 = 0, pinDHT11 = 15, temperature = 0, humidity = 0, msgphone = 0;
SimpleDHT11 dht11(pinDHT11);
SoftwareSerial SIM800(2, 3); //программные RX, TX для связи с модулем SIM800L
unsigned long timing1, timing2, timing3, timing4, timing5, timing6, timing7, timing8, timing9;
// Перемен. для хранения точки отсчета для таймеров
int j = 0, k = 0, a = 0, x = 9, x2 = 20, v = 0;
String _response = "";                          // Переменная для хранения ответа модуля
long lastUpdate = millis();                     // Время последнего обновления
long updatePeriod = 60000;                      // Проверять каждую минуту

String phones = "+xxxxxxxxxxxx, +xxxxxxxxxxxx"; //Список разрешенных номеров
                   //номера вписываются в международном формате начиная с +

String waitResponse() {                  // Функция ожидания ответа и возврата полученного результата
  String _resp = "";                     // Переменная для хранения результата
  long _timeout = millis() + 10000;      // Переменная для отслеживания тайм аута (10 секунд)
  while (!SIM800.available() && millis() < _timeout) {};// Ждем ответа 10 секунд, если пришел ответ или наступил тайм аут, то...
  if (SIM800.available()) {              // Если есть, что считывать
    _resp = SIM800.readString();         //считываем и запоминаем
  } else {                               // Если пришел тайм аут, то
    Serial.println("Timeout...");        //оповещаем об этом и
  } return _resp;                        //возвращаем результат. Пусто, если проблема
}
String sendATCommand(String cmd, bool waiting) {
  String _resp = "";            // Переменная для хранения результата
  Serial.println(cmd);          // Дублируем команду в монитор порта
  SIM800.println(cmd);          // Отправляем команду модулю
  if (waiting) {                // Если необходимо дождаться ответа
    _resp = waitResponse();     // ждем, когда будет передан ответ
    // Если Echo Mode выключен (ATE0), то эти 3 строки можно за комментировать
    if (_resp.startsWith(cmd)) { // Убираем из ответа дублирующуюся команду
      _resp = _resp.substring(_resp.indexOf("\r", cmd.length()) + 2);
    }
    Serial.println(_resp);  // Дублируем ответ в Serial
  } return _resp;           // Возвращаем результат. Пусто, если проблема
}
void parseSMS(String msg) { // Парсим SMS
  String msgheader = "";
  String msgbody = "";
  String msgphone = "";
  msg = msg.substring(msg.indexOf("+CMGR: "));
  msgheader = msg.substring(0, msg.indexOf("\r")); // Выдергиваем телефон
  msgbody = msg.substring(msgheader.length() + 2);
  msgbody = msgbody.substring(0, msgbody.lastIndexOf("OK"));// Выдергиваем текст SMS
  msgbody.trim();
  int firstIndex = msgheader.indexOf("\",\"") + 3;
  int secondIndex = msgheader.indexOf("\",\"", firstIndex);
  msgphone = msgheader.substring(firstIndex, secondIndex);
  Serial.println("Phone: " + msgphone);  // Выводим номер телефона
  Serial.println("Message: " + msgbody); // Выводим текст SMS
  if (msgphone.length() > 6 && phones.indexOf(msgphone) > -1) { // Если телефон в белом списке, то
    if (msgbody == "Pr0") {
      j = 0;
      digitalWrite(13, LOW);
    }
    if (msgbody == "Pr1") {
      j = 1;
      digitalWrite(13, LOW);
    }
    if (msgbody == "Pr2") {
      j = 2;
      digitalWrite(13, LOW);
    }
    if (msgbody == "Pr3") {
      j = 3;
    }
    if (msgbody == "Pr20") {
      j = 4;
      digitalWrite(13, LOW);
    }
    if (msgbody == "Ktr") {
      k = 1;
    }
    if (msgbody == "Klx2+") {
      x2 = x2 + 3;
    }
    if (msgbody == "Klx2-") {
      x2 = x2 - 3;
    }
  } else {
    Serial.println("Unknown phonenumber");
  }
}
void setup() {
  pinMode(13, OUTPUT);
  pinMode(p8, OUTPUT);
  digitalWrite(13, LOW);
  digitalWrite(p8, LOW);
  pinMode(7, INPUT);
  Serial.begin(9600); // Скорость обмена данными с компьютером
  SIM800.begin(9600); // Скорость обмена данными с модемом
  Serial.println("Start!");
  analogReference(INTERNAL);
  sendATCommand("AT", true);                   // Отправили AT для настройки скорости обмена данными
  sendATCommand("AT+CMGDA=\"DEL ALL\"", true); // Удаляем все SMS, чтобы не забивать память
  sendATCommand("AT+CMGF=1;&W", true);         // Включаем текстовый режима SMS (Text mode) и сразу сохраняем значение (AT&W)!
  lastUpdate = millis();                       // Обнуляем таймер
}
bool hasmsg = false;                           // Флаг наличия сообщений к удалению
void loop() {
  int sensorVal = digitalRead(7);
  if (sensorVal == HIGH) {
    v = 220;
  } else {
    v = 0;
  }
  if (millis() - timing9 > 3000) {
    timing9 = millis();
    byte temperature = 0;
    byte humidity = 0;
    dht11.read(&temperature, &humidity, NULL);
    t_1 = analogRead(A0) / x;
    Serial.print("T1 = "); Serial.print(t_1); Serial.print("*C, ");
    Serial.print("T2 = "); Serial.print((int)temperature); Serial.print("*C, ");
    Serial.print((int)humidity); Serial.println("%");
  }
  if (lastUpdate + updatePeriod < millis() ) {                     // Пора проверить наличие новых сообщений
    do {
      _response = sendATCommand("AT+CMGL=\"REC UNREAD\",1", true); // Отправляем запрос чтения непрочитанных сообщений
      if (_response.indexOf("+CMGL: ") > -1) {                     // Если есть хоть одно, получаем его индекс
        int msgIndex = _response.substring( _response.indexOf ("+CMGL: ") + 7, _response.indexOf("\"REC UNREAD\"", _response.indexOf("+CMGL: ")) - 1).toInt();
        char i = 0; // Объявляем счетчик попыток
        do {
          i++; // Увеличиваем счетчик
          _response = sendATCommand("AT+CMGR=" + (String)msgIndex + ",1", true); // Пробуем получить текст SMS по индексу
          _response.trim();                // Убираем пробелы в начале/конце
          if (_response.endsWith("OK")) {  // Если ответ заканчивается на "ОК"
            if (!hasmsg) hasmsg = true;    // Ставим флаг наличия сообщений для удаления
            sendATCommand("AT+CMGR=" + (String)msgIndex, true); // Делаем сообщение прочитанным
            sendATCommand("\n", true);     // Перестраховка - вывод новой строки
            parseSMS(_response);           // Отправляем текст сообщения на обработку
            break; // Выход из do
          } else { // Если сообщение не заканчивается на OK
            Serial.println ("Error");  //ошибка
            sendATCommand("\n", true); // Отправляем новую строку и повторяем попытку
          }
        }
        while (i < 10); break;
      } else {
        lastUpdate = millis();         // Обнуляем таймер
        if (hasmsg) {
          sendATCommand("AT+CMGDA=\"DEL READ\"", true); // Удаляем все прочитанные сообщения
          hasmsg = false;
        } break;
      }
    } while (1);
  }
  if (SIM800.available()) {     // Если модем, что-то отправил
    _response = waitResponse(); // Получаем ответ от модема для анализа
    _response.trim();           // Убираем лишние пробелы в начале и конце
    Serial.println(_response);  // Если нужно выводим в монитор порта
    if (_response.indexOf("+CMTI:") > -1) { // Пришло сообщение об отправке SMS
      lastUpdate = millis() - updatePeriod; // Теперь нет необходимости обрабатывать SMS здесь, достаточно просто сбросить счетчик авто проверки, и в следующем цикле все будет обработано
    }
  }
  if (Serial.available()) {      // Ожидаем команды по Serial
    SIM800.write(Serial.read()); //и отправляем полученную команду модему
  };
  if (j == 0) { //Программа 0
    digitalWrite(p8, HIGH);
  } else {
    digitalWrite(p8, LOW);
  }
  if (j == 1) { //Программа 1
    if (millis() - timing1 > 12600000) { //3 часа 30 минут = 12600000 мс
      timing1 = millis(); timing2 = millis();
      digitalWrite(13, HIGH);
    }
    if (millis() - timing2 > 1800000) { // 30 минут = 1800000 мс
      digitalWrite(13, LOW);
    }
    if (millis() - timing6 > 3000) {
      timing6 = millis();
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW);
    }
  }
  if (j == 2) { //Программа 2
    if (millis() - timing3 > 23400000) { // 6 часа 30 минут = 23400000 мс
      timing3 = millis(); timing4 = millis(); digitalWrite(13, HIGH);
    }
    if (millis() - timing4 > 1800000) {  // 30 минут = 1800000 мс
      digitalWrite(13, LOW);
    }
    if (millis() - timing7 > 3000) {
      timing7 = millis();
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW); delay(300);
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW);
    }
  }
  if (j == 3) {
    digitalWrite(13, HIGH); //Программа 3
    if (millis() - timing8 > 3000) {
      timing8 = millis();
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW); delay(300);
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW); delay(300);
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW);
    }
  } a = j;
  if (j == 4) {
    a = 20; //Программа 4
    if (millis() - timing5 > 3000) {
      timing5 = millis();
      byte temperature = 0;
      byte humidity = 0;
      dht11.read(&temperature, &humidity, NULL);
      if ((int)temperature <= x2) {
        digitalWrite(13, HIGH);
      }
      if ((int)temperature > x2) {
        digitalWrite(13, LOW);
      }
    }
    if (millis() - timing8 > 3000) {
      timing8 = millis();
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW); delay(300);
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW); delay(300);
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW); delay(300);
      digitalWrite(p8, HIGH); delay(100); digitalWrite(p8, LOW);
    }
  } else {
    a = j;
  }
  if (k == 1) {
    delay(300);
    byte temperature = 0;
    byte humidity = 0;
    dht11.read(&temperature, &humidity, NULL); delay(300);
    SIM800.println( "AT+CMGS=\"+xxxxxxxxxx\""); //номер на который будет приходить SMS отчет
    delay(3000);                                //номер вписывается в международном формате начиная с +
    SIM800.print("T1=");
    delay(300); SIM800.print(t_1);
    delay(300); SIM800.print("*C, T2=");
    delay(300); SIM800.print((int)temperature);
    delay(300); SIM800.print("*C, T2-VL=");
    delay(300); SIM800.print((int)humidity);
    delay(300); SIM800.print("% Pr="); delay(300); SIM800.print(a);
    delay(300); SIM800.print(" t=");
    delay(300); SIM800.print(x2);
    delay(300); SIM800.print(" Set=");
    delay(300); SIM800.print(v);
    delay(300); SIM800.print("volt");
    delay(300); SIM800.print((char)26);//команда для отправки SMS
    delay(300); Serial.println("SMS send finish");
  } k = 0;
}


//Конец скетча


Указав свои номера в скетче, загружаю его на плату.

Затем надо подготовить GSM модуль SIM800L. Припаять антенну, делитель напряжения, и еще 3 провода, как показано на схеме ниже.

Делитель напряжения нужен, для того чтобы не спалить GSM модуль. Сигнал входящего высокого уровня, для модуля SIM800L должен быть в пределах от 2.1 до 2.8 вольта!

Делитель напряжения для SIM800L

Затем собираю все как показано на этой схеме.

GSM контроллер отопления на базе Arduino UNO и модуля SIM800L

Я в схеме применил твердотельное реле G3MB-202P, которое рассчитано на нагрузку до 2 Ампер. Этого вполне достаточно для обычного циркуляционного насоса!

Если вы планируете, управлять мощным обогревателем или теплым полом, то используйте более мощное твердотельное реле, на пример SSR-40DA, которое выдерживает до 40 Ампер, или SSR-80DA до 80 Ампер. И провода соответствующего сечения!

Не используйте слишком тонкие провода для питания GSM модуля, так как при запуске модуля, нагрузка может достигать 2 Ампер. При использовании очень тонких проводов, возникает просадка по напряжению, и модуль не сможет найти сеть GSM.

Подробная видео инструкция о том как пользоваться GSM контроллером.

Самодельный GSM контроллер отопления на базе Arduino UNO и модуля SIM800L