Winter is coming
Winter is coming, господа, а посему нужно начинать готовиться. Как говорится, готовь скетч с обогревателем летом, а скетч с кондиционером – зимой.
Наша задача – сделать умный обогреватель. Сейчас уже существуют обогреватели с термостатами, но нам важна сама идея. К тому же не каждый может похвастать тем, что может спросить у своего обогревателя температуру или попросить подогреть комнату ко времени прихода с работы, учебы.
Подключаем безлимитный тариф SMS и вперед, к прогрессу.
Детали
Для создания нашего устройства нам потребуются:
- Собственно, обогреватель, самый простой
- GPRS Shield (поиск на AliExpress)
- Arduino UNO или Leonardo, или любая другая плата на ваш вкус
- Troyka Shield. Уж больно он мне нравится. Все датчики подключаются легко и непринужденно.
- Relay Shield. Любое реле. Опять же – на ваш вкус
- Соединительные провода. Тут все немного сложнее. Если хочется собрать готовое устройство, то термостат желательно сделать выносной. Для этого нужно сделать провода достаточно длинными, а можно соорудить беспроводной термостат. В целях обучения беспроводной связи я выбрал второй вариант.
- Пара — приемник и передатчик 433 МГц (Поиск на AliExpress)
Описание устройства
Наше устройство будет состоять из 2-х частей – термостата и центрального контроллера. Термостат будет посылать данные о температуре на центральное устройство, которое, в свою очередь, будет управлять реле и общаться со смартфоном посредством 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 команд. Прочитать и скачать его можно тут.
Итог
Теперь, на “А у нас в квартире газ” можно с гордостью ответить – “А у меня обогреватель доцент кафедры обогревательных наук!”
Конечно, схему обогревателя можно улучшить. Можно добавить режим, который включает обогреватель по времени, поставить другие датчики и знать влажность и освещенность дома. Можно на термостат поставить регулировщик температуры, которую нужно поддерживать.
Ну а я с вами прощаюсь – до скорого!
Прикрепленные файлы:
- Receiver.zip (5 Кб)