Здравствуйте!
Появилась потребность изготовить бюджетный контроллер отопления. Да такой, чтобы можно было управлять им удаленно с помощью SMS команд, и получать данные о температуре влажности и состоянии сети 220 вольт.
Для проекта понадобятся:
Плата 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
Для индикации любой маломощный светодиод,
Еще понадобится: активная сим карта с положительным балансом на счету.
Вилка, розетка, и коробка в корой все поместится.
Коробку для корпуса лучше использовать из негорючего пластика!
Рассмотрим скетч!
В 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 вольта!
Затем собираю все как показано на этой схеме.
Я в схеме применил твердотельное реле G3MB-202P, которое рассчитано на нагрузку до 2 Ампер. Этого вполне достаточно для обычного циркуляционного насоса!
Если вы планируете, управлять мощным обогревателем или теплым полом, то используйте более мощное твердотельное реле, на пример SSR-40DA, которое выдерживает до 40 Ампер, или SSR-80DA до 80 Ампер. И провода соответствующего сечения!
Не используйте слишком тонкие провода для питания GSM модуля, так как при запуске модуля, нагрузка может достигать 2 Ампер. При использовании очень тонких проводов, возникает просадка по напряжению, и модуль не сможет найти сеть GSM.
Подробная видео инструкция о том как пользоваться GSM контроллером.