Winter is coming
Winter is coming, господа, а посему нужно начинать готовиться. Как говорится, готовь скетч с обогревателем летом, а скетч с кондиционером – зимой.
Наша задача – сделать умный обогреватель. Сейчас уже существуют обогреватели с термостатами, но нам важна сама идея. К тому же не каждый может похвастать тем, что может спросить у своего обогревателя температуру или попросить подогреть комнату ко времени прихода с работы, учебы.
Подключаем безлимитный тариф SMS и вперед, к прогрессу.
Детали
Для создания нашего устройства нам потребуются:
- Собственно, обогреватель, самый простой
- GPRS Shield (поиск на AliExpress)
- Arduino UNO или Leonardo, или любая другая плата на ваш вкус
- Troyka Shield. Уж больно он мне нравится. Все датчики подключаются легко и непринужденно.
- Relay Shield. Любое реле. Опять же – на ваш вкус
- Соединительные провода. Тут все немного сложнее. Если хочется собрать готовое устройство, то термостат желательно сделать выносной. Для этого нужно сделать провода достаточно длинными, а можно соорудить беспроводной термостат. В целях обучения беспроводной связи я выбрал второй вариант.
- Пара - приемник и передатчик 433 МГц (Поиск на AliExpress)
Описание устройства
Наше устройство будет состоять из двух частей – термостата и центрального контроллера. Термостат будет посылать данные о температуре на центральное устройство, которое, в свою очередь, будет управлять реле и общаться со смартфоном посредством 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 = 1\r"); 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 Кб)
Комментарии (11) | Я собрал (0) | Подписаться
Для добавления Вашей сборки необходима регистрация
[Автор]
Кстати, первый ответ по запросу "Troyka shield" является ссылкой на сайт, где можно приобрести Тройку.
Аналогом на AliExpress будет являться Sensor Shield.
Я вообще не программист. Я инженер эл.монтажник. У меня автономное отопление на простых аналоговых термостатах. Прочитав ваш материал захотелось использовать ваш проект, если вы не против, но как одновременно управлять обогревателями в двух комнатах и эл. бойлером так и не дошло. И сколько чего покупать из модулей, плат и датчиков не сообразил, если подскажите буду благодарен. Спасибо.