Как СМСнуть обогревателю?

Winter is coming

Winter is coming, господа, а посему нужно начинать готовиться. Как говорится, готовь скетч с обогревателем летом, а скетч с кондиционером – зимой.

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

Подключаем безлимитный тариф SMS и вперед, к прогрессу.

Детали

Для создания нашего устройства нам потребуются:

  • Собственно, обогреватель, самый простой 

 

  • GPRS Shield (поиск на AliExpress)

 

  • Arduino UNO или Leonardo, или любая другая плата на ваш вкус

  • Troyka Shield. Уж больно он мне нравится. Все датчики подключаются легко и непринужденно.

  •  Relay Shield. Любое реле. Опять же – на ваш вкус

  

  •  DHT11

  • Соединительные провода. Тут все немного сложнее.  Если хочется собрать готовое устройство, то термостат желательно сделать выносной. Для этого нужно сделать провода достаточно длинными, а можно соорудить беспроводной термостат. В целях обучения беспроводной связи я выбрал второй вариант.

  • Пара — приемник и передатчик 433 МГц (Поиск на AliExpress)

  • Arduino Pro MINI

Описание устройства

Наше устройство будет состоять из двух частей – термостата и центрального контроллера. Термостат будет посылать данные о температуре на центральное устройство, которое, в свою очередь, будет управлять реле и общаться со смартфоном посредством GPRS Shield.

Со смартфона можно будет задать температуру, которую нужно поддерживать и режим работы устройства, а также узнать состояние и температуру.

Термостат

Начнем, пожалуй, с создания термостата. Наша задача – считывать показания температуры с DHT11 и передавать их на центральное устройство каждый фиксированный промежуток времени.

Задача поставлена – приступаем к выполнению. Соберем макет термостата.

Для эксперимента, соберем  макет центрального контроллера с единственной функцией – принятие сообщений от термостата. Это вид сверху. Под Troyka Shield находятся GPRS Shield и Arduino.

Может возникнуть вопрос – “А почему у приемника четыре пина, хотя используются только три?” Отвечаю – все просто, пин Data продублирован.

Теперь посмотрим на код передатчика.

 //Подключаем библиотеки  #include <TroykaDHT11.h> #include <VirtualWire.h>  //Определяем пин DHT11  DHT11 dht(11);  void setup()  {    Serial.begin(9600);    dht.begin();   //Светодиод для индикации   pinMode(13,OUTPUT);   //требуется для DR3100   vw_set_ptt_inverted(true);    //Обозначаем пин, к которому подключили приемник   vw_set_rx_pin(12);   //Установим скорость передачи   vw_setup(4000);   }  void loop() {    //Получаем температуру с DHT11   String temp;   dht.read();   temp = dht.getTemperatureC();   Serial.println(temp);   //Приводим температуру к нужному для отправки виду   char msg[10];   temp.toCharArray(msg, 10);   Serial.println(msg);   //Отправляем сообщение   digitalWrite(13, HIGH);   vw_send((uint8_t *)msg, strlen(msg));   vw_wait_tx();   digitalWrite(13, LOW);   //Ждем немного   delay(5000);   }

А вот код для приемника.

 //Подключаем библиотеку  #include <VirtualWire.h>  //Создаем массив для передачи температуры  char temp[3];  void setup() {     Serial.begin(9600);      //требуется для DR3100     vw_set_ptt_inverted(true);      //Обозначаем пин, к которому подключили приемник     vw_set_rx_pin(12);     //Установим скорость передачи     vw_setup(4000);       //Светодиод для индикации     pinMode(13, OUTPUT);     //Стартуем     vw_rx_start();         Serial.println("Setup"); }     void loop() {   uint8_t buf[VW_MAX_MESSAGE_LEN]; // Буфер для сообщения   uint8_t buflen = VW_MAX_MESSAGE_LEN; // Длина буфера    if (vw_get_message(buf, &buflen)) // Если принято сообщение   {   Serial.println("Received:");    digitalWrite(13, HIGH);   //По символу записываем принятое сообщение    for(int i = 0; i < buflen; i++)   {      temp[i] = buf[i];   }   //Выводим сообщение   Serial.print("Temperature is ");   Serial.print(temp);   Serial.println(" C");   digitalWrite(13, LOW);  } } 

Как это работает?

Начнем с передатчика. Arduino Pro MINI запрашивает значение температуры с DHT11. Получив температуру, MINI готовит пакет для передачи. Для передатчика важна разрядность, поэтому в коде присутствует странная запись — uint8_t *

Не нужно пугаться —  это другой и более правильный вид записи типа данных byte или unsigned char.

Вот небольшая таблица такой записи типов данных.

 int8_t    |  char    |  от -128 до 127
 uint8_t   |  byte, unsigned char |  от 0 до 255
 int16_t   |  int    |  от -32768 до 32767
 uint16_t  |  unsigned int, word  |  от 0 до 65535 
 int32_t   |  long    |  от  -2147483648 до 2147483647 
 uint32_t  |  unsigned long    |  от 0 до 4294967295

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

Приемник отслеживает эфир на наличие сообщений и, если сообщение пришло, выводит пришедшее значение температуры в Serial Monitor .

Умный обогреватель

Пришло время заняться центральным устройством. Корпус устройства и конечное выполнение зависит только от ваших возможностей и фантазии, поэтому я лишь приведу схему, по которой можно собрать устройство. Под Troyka Shield также находятся GPRS Shield и Arduino.

Если кто не знает, как подключать нагрузку к реле – вот схема. Ну и для общего образования:

  • Коричневый провод – это фаза, синий – ноль.
  • По-хорошему, нужно всегда размыкать фазу, а не ноль. Если выключатель будет размыкать ноль, то может ударить током, если притронутся к фазе, а если это сеть с большим напряжением, то исход может быть летальным. Вы там осторожнее.

 

Запрограммируем устройство. Есть готовая библиотека для работы с GPRS Shield, поэтому передача сообщений становится достаточно тривиальной. Скачать библиотеку можно тут. Один минус – функция отправки SMS не принимает SMS в String, поэтому нам нужно переводить String в char array с помощью функции string.toCharArray(char, number);

 //Подключаем необходимые библиотеки //Для GPRG Shield  #include <GPRS_Shield_Arduino.h> #include <SoftwareSerial.h>  //Для радиоприемника  #include <VirtualWire.h>  //Определяем пин, к которому подключено реле  #define RELAY 8  //Текущее состояние реле   bool stateRelay = false;  //Переменная для определения режима работы  int mode = 0;  //Переменная для ручного управления обогревателем  int m_temp = 0;  //Переменные для температуры //Температура в данный момент  int curr_temp = 0;  //Температура срабатывания   int trig_temp = 0;  //Нежелательная температура  int danger_temp = 36;  // создаём объект класса GPRS  GPRS gprs;  //Определяем номер, на который будем посылать сообщения //Нужно ввести свой номер  #define PHONE_NUMBER "+XXXXXXXXXXX"  //Описываем сообщения для отправки  #define HELLO_MESSAGE "Hello from GPRS Shield! Please, set trigger temperature." #define MODE_MESSAGE "Send mode's name." #define READY_MESSAGE "I'm ready!" #define INC_TRIG "Incorrect trigger value! You only can set - 20, 25 and 28." #define INC_MODE "Incorrect mode value! You can only set - Auto and Manual" #define DANGER_MESSAGE "Current temperature is bigger than 38C. Relay is off." #define ERROR_SMS "Incorrect command. You can only ask for state, set trigger temperature or set mode" #define HEATERON "OK, heater is on." #define HEATEROFF "OK, heater is off." #define AUTOMODE "OK, auto mode." #define MANUALMODE "OK, manual mode." #define OKTEMP "OK, trigger temperature changed"  String helloText = "Hello from GPRS Shield!"; String tempText = "Tempreature is "; String termValue = String(curr_temp); String degree = " C."; String heaterOn = "Heater is On."; String heaterOff = "Heater is Off."; String message_to_send; char char_message_to_send[60];  // текст сообщения char message[160]; // номер, с которого пришло сообщение char phone[16]; // дата отправки сообщения char datetime[24];  void setup() {   //Определяем пины реле, светодиода, приемника      pinMode(RELAY, OUTPUT);   pinMode(13, OUTPUT);   digitalWrite(RELAY, LOW);   vw_set_ptt_inverted(true);   vw_set_rx_pin(12);    //Устанавливаем связь    Serial.begin(9600);   vw_setup(4000);   vw_rx_start();     Serial.println("Start");   gprs.powerUpDown();      while (!gprs.init())     {     delay(1000);     Serial.println("Connecting");    }     //Отправляем запрос температуры   Serial.println("Connected");  gprs.sendSMS(PHONE_NUMBER, HELLO_MESSAGE);  Serial.println("Hello message sent");    while(trig_temp == 0)  {   Serial.println("Waiting for trigger temperature");     if (gprs.ifSMSNow())     {     gprs.readSMS(message, phone, datetime);     sms_temp(message);    }     delay(1000);  }    Serial.println("Trigger temperature assigned. Ask for mode name.");   //Отправляем запрос режима     gprs.sendSMS(PHONE_NUMBER, MODE_MESSAGE);     while(mode == 0)  {    Serial.println("Waiting for mode");    if (gprs.ifSMSNow())     {     gprs.readSMS(message, phone, datetime);     sms_mode(message);       }    delay(1000);  }   Serial.println("Mode assigned");    gprs.sendSMS(PHONE_NUMBER, READY_MESSAGE); }  void loop() {     //Ждем сообщения          if (gprs.ifSMSNow())     {     gprs.readSMS(message, phone, datetime);     Serial.println("Message");     inc_sms(message);    }    //Получаем текущую температуру с термостата        inc_temp();        //Проверяем надобность включения реле        relay(); }  //Включаем или выключаем реле  void relay() {   if(curr_temp < trig_temp && mode == 1)   {     digitalWrite(RELAY, HIGH);       stateRelay = true;     delay(3000);   }    if(curr_temp >= trig_temp && mode == 1)    {     digitalWrite(RELAY, LOW);     stateRelay = false;     delay(3000);   }   if(m_temp == 1 &&  mode == 2 && curr_temp < danger_temp)   {      digitalWrite(RELAY, HIGH);        stateRelay = true;   }   if(m_temp == 0 &&  mode == 2 || curr_temp >= danger_temp)   {      digitalWrite(RELAY, LOW);        stateRelay = false;      if(curr_temp >= danger_temp)      {       Serial.println("DANGER!");       gprs.sendSMS(PHONE_NUMBER, DANGER_MESSAGE);       delay(60000);      }   } }  //Проверяем входящее SMS  void inc_sms(char f_message[]) {   if(strcmp(f_message, "State") == 0)   {     Serial.println("Calling for state function");     state();   }   else if(strcmp(f_message, "HeaterOn") == 0)   {     m_temp = 1;     gprs.sendSMS(PHONE_NUMBER, HEATERON);   }   else if(strcmp(f_message, "HeaterOff") == 0)   {     m_temp = 0;     gprs.sendSMS(PHONE_NUMBER, HEATEROFF);   }   else if(strcmp(f_message, "Auto") == 0)   {     mode = 1;     gprs.sendSMS(PHONE_NUMBER,AUTOMODE);   }   else if(strcmp(f_message, "Manual") == 0)   {     mode = 2;     gprs.sendSMS(PHONE_NUMBER, MANUALMODE);   }   else if(strcmp(f_message, "20") == 0)  {   trig_temp = 20;   gprs.sendSMS(PHONE_NUMBER, OKTEMP);  }  else if(strcmp(f_message, "25") == 0)  {   trig_temp = 25;   gprs.sendSMS(PHONE_NUMBER, OKTEMP);  }  else if(strcmp(f_message, "28") == 0)  {   trig_temp = 28;   gprs.sendSMS(PHONE_NUMBER, OKTEMP);  }   else    {     Serial.println("Error");     gprs.sendSMS(PHONE_NUMBER, ERROR_SMS);        } }    //Устанавливаем режим  void sms_mode(char f_message[]) {   if(strcmp(f_message, "Auto") == 0)   {     mode = 1;   }    else if(strcmp(f_message, "Manual") == 0)   {     mode = 2;   }   else   {     gprs.sendSMS(PHONE_NUMBER,INC_MODE);   } }      /*     Температура, которую нужно поддерживать.     Я не буду делать так, чтобы можно было установить     любую температуру срабатывания.     Я выберу три - 20, 25 и 28.     Вы можете выбирать режим на свое усмотрение.   */   void sms_temp(char f_message[]) {  if(strcmp(f_message, "20") == 0)  {   trig_temp = 20;  }  else if(strcmp(f_message, "25") == 0)  {   trig_temp = 25;  }  else if(strcmp(f_message, "28") == 0)  {   trig_temp = 28;  }  else  {   gprs.sendSMS(PHONE_NUMBER,INC_TRIG);  } }  //Считываем температуру  void inc_temp() {   uint8_t buf[VW_MAX_MESSAGE_LEN]; // Буфер для сообщения   uint8_t buflen = VW_MAX_MESSAGE_LEN; // Длина буфера   char temp[3];      if (vw_get_message(buf, &buflen)) // Если принято сообщение   {   Serial.println("Received:");    digitalWrite(13, HIGH);   for(int i = 0; i < buflen; i++)   {      temp[i] = buf[i];   }   curr_temp = atoi(temp);   Serial.print("Temperature is ");   Serial.print(temp);   Serial.println(" C");   digitalWrite(13, LOW);  } }  //Готовим и отправляем SMS со статусом  void state() {   if(stateRelay)   {     message_to_send = String(helloText + tempText + termValue + degree + heaterOn);     message_to_send.toCharArray(char_message_to_send, 100);     Serial.println("Sending");     gprs.sendSMS(PHONE_NUMBER, char_message_to_send);     Serial.println("Sent");   }   else if(!stateRelay)   {     message_to_send = String(helloText + tempText + termValue + degree + heaterOff);     message_to_send.toCharArray(char_message_to_send, 100);     Serial.println("Sending");     gprs.sendSMS(PHONE_NUMBER, char_message_to_send);     Serial.println("Sent");   } } 

А оно как работает?

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

В это же время устройство ждет сообщений от хозяина. Если пришло сообщение о запросе статуса, то устройство отправляет нынешнее значение температуры и состояние реле, если пришло число, то устройство меняет температуру срабатывания, если пришло указание о смене режима – устройство меняет режим работы.

Вот небольшой кусок моего общения с обогревателем.

AT команды

GPRS Shield так же можно управлять с помощью AT команд. Вот небольшой скетч, который показывает, как можно отправить SMS.

 void setup() {  //Включаем GPRS Shield    gprs_On();  //Открываем Serial port для индикации...  Serial.begin(9600);  //...и Serila1 для общения с шилдом  Serial1.begin(9600);  while(!Serial) { }      Serial.println("I'm ready");      //Устанавливаем текстовый режим сообщений          Serial1.print("AT+CMGF = 1r");      delay(3000);      //Указываем номер, на который будем отправлять SMS     //Не забудьте указать свой номер      Serial1.println("AT + CMGS = "+XXXXXXXXXXX"");     delay(300);          // Пишем текст сообщения          Serial1.println("AT Test");     delay(300);          // Отправляем Ctrl+Z, обозначая, что сообщение готово          Serial1.println((char)26);     Serial.println("SMS send!");  }  void loop()  { }  //Функция включения GPRS Shield  void gprs_On() {   // настраиваем пин №2 в режим выхода   pinMode(2, OUTPUT);   // проверяем состояние 3 пина   if (digitalRead(3) != HIGH) {     // если на нём «низкий уровень»     // подаём на пин 2 «высокий уровень»     digitalWrite(2, HIGH);     // ждём 3 секунды     delay(3000);   }   // подаём на пин 2 «низкий уровень»   digitalWrite(2, LOW); }

AT команды используются, если вам не хватает функционала готовых библиотек. С помощью AT команд, например, можно использовать часы реального времени, встроенные в GPRS Shield.

Если хочется общаться с GPRS Shield в режиме реального времени, то можно запустить пример из библиотеки под названием  GPRS_AT_Commands.

Чтобы узнать время у модуля, нужно отправить команду AT+CCLK?. Если вы включили Shield в первый раз или вынули из него батарейку, а потом включили, то ответом будет время, прошедшее после запуска GPRS Shileld.У меня ответом было это.

Для того чтобы установить время, нужно использовать команду AT+CCLK=»YY/MM/DD,HH:MM:SS+ZZ«

Где YY – это год, MM – месяц, DD – день, HH – час, MM – минута, SS – секунда, ZZ – часовой пояс, причем, если сейчас сентябрь, то я должен вписывать 09, а не просто 9.Вот пример.

Не забываем, что если Shield отключить от питания и не установить батарейку, то время сбрасывается.

Для тех, кто хочет залезть глубже, есть справочник AT команд. Прочитать и скачать его можно тут.

 

Итог

Теперь, на “А у нас в квартире газ” можно с гордостью ответить – “А у меня обогреватель доцент кафедры обогревательных наук!”

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

Ну а я с вами прощаюсь – до скорого!