Управление Реле по MQTT на Arduino
- Войдите на сайт для отправки комментариев
Пнд, 25/07/2016 - 12:41
Такая проблема, работаю над скетчем для Arduino UNO + Ethernet Shild W5100, который будет управлять реле по протоколу MQTT программно через систему автоматизации Умного дома MajorDoMo, или вручную с выключателей-кнопок + отображать в системе MajorDoMo состояние реле (Вкл./Выкл., 0 или 1) для каждой релюшки. Только пока не могу понять как сделать управление Вкл./Выкл. с отправкой статуса о реле через топики по MQTT и выключателей.
Уже долго мучаюсь над кодом, помогите пожалуйста.
/* Скетч для управления Реле через кнопку-выключатель, либо через MajorDoMo по MQTT с обратной связью и сохранением статуса реле при откл. питания... */ #include <SPI.h> // Библиотека SPI шины #include <Ethernet.h> // Ethernet библиотека #include <PubSubClient.h> // Библиотека MQTT #include <EEPROM.h> // Библиотека EEPROM #define Relay1 2 // Реле №1 #define Relay2 3 // Реле №2 #define Relay3 4 // Реле №3 #define Relay4 5 // Реле №4 #define BUTTON_1 6 // Кнопка-выключатель №1 #define BUTTON_2 7 // Кнопка-выключатель №1 #define BUTTON_3 8 // Кнопка-выключатель №1 #define BUTTON_4 9 // Кнопка-выключатель №1 boolean StatusRelay1 = true; // Статус Реле №1 boolean btnPress1 = false; // Объявляем что кнопка №1 не нажата 0 boolean LastBtnStat1 = false; // Временная переменная для хранения статуса boolean StatusRelay2 = true; // Статус Реле №2 boolean btnPress2 = false; // Объявляем что кнопка №2 не нажата 0 boolean LastBtnStat2 = false; // Временная переменная для хранения статуса boolean StatusRelay3 = true; // Статус Реле №3 boolean btnPress3 = false; // Объявляем что кнопка №3 не нажата 0 boolean LastBtnStat3 = false; // Временная переменная для хранения статуса boolean StatusRelay4 = true; // Статус Реле №4 boolean btnPress4 = false; // Объявляем что кнопка №4 не нажата 0 boolean LastBtnStat4 = false; // Временная переменная для хранения статуса int REL1; // Переменные для вычислений с Реле... int REL2; int REL3; int REL4; int Address1 = 0; // Адреса EEPROM для хранения значений int Address2 = 2; int Address3 = 4; int Address4 = 6; // Задаём mac и ip адреса в Локальной сети byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; IPAddress ip{192, 168, 1, 74}; //ip Адрес Ethernet Shild'a Arduino IPAddress server{192, 168, 1, 70}; //ip Адрес для MQTT Брокера // Шапка Функции Callback для объявления и Инициализации PubSubClient void callback(char* topic, byte* payload, unsigned int length); EthernetClient ethClient; // Инициализируем Ethernet клиент PubSubClient client(server, 1883, callback, ethClient); // Инициализируем MQTT клиент // Функция Callback void callback(char* topic, byte* payload, unsigned int length) { payload[length] = '\0'; Serial.print(topic); Serial.print(" "); String strTopic = String(topic); String strPayload = String((char*)payload); Serial.println(strPayload); if (strTopic == "home/data/status/Relay/1") { if (strPayload == "ON") { REL1 = 1; } else if (strPayload == "OFF") { REL1 = 0; } Serial.print("REL1 = "); Serial.println(REL1); } else if (strTopic == "home/data/status/Relay/2") { if (strPayload == "ON") { REL2 = 1; } else if (strPayload == "OFF") { REL2 = 0; } Serial.print("REL2 = "); Serial.println(REL2); } else if (strTopic == "home/data/status/Relay/3") { if (strPayload == "ON") { REL3 = 1; } else if (strPayload == "OFF") { REL3 = 0; } Serial.print("REL3 = "); Serial.println(REL3); } else if (strTopic == "home/data/status/Relay/4") { if (strPayload == "ON") { REL4 = 1; } else if (strPayload == "OFF") { REL4 = 0; } Serial.print("REL4 = "); Serial.println(REL4); } } void setup() { pinMode(Relay1, OUTPUT); // Задаём Реле №1 как Выход pinMode(BUTTON_1, INPUT); // Задаём кнопку №1 как Вход pinMode(Relay2, OUTPUT); pinMode(BUTTON_2, INPUT); pinMode(Relay3, OUTPUT); pinMode(BUTTON_3, INPUT); pinMode(Relay4, OUTPUT); pinMode(BUTTON_4, INPUT); Ethernet.begin(mac, ip); // Инициализируем mac, ip Serial.begin(9600); // Задаём скорость порта в БОД'ах. Serial.println(F("Relay Test!")); /* Тестовое сообщ. при откр. Монитора порта. Так же обёртываем сообщения в макрос F() для экономии ОЗУ */ //цикл считывания значения из EEPROM для реле №1 if ( EEPROM.read(Address1) ) { digitalWrite(Relay1, HIGH); } else { digitalWrite(Relay1, LOW); } //цикл считывания значения из EEPROM для реле №2 if ( EEPROM.read(Address2) ) { digitalWrite(Relay2, HIGH); } else { digitalWrite(Relay2, LOW); } //цикл считывания значения из EEPROM для реле №3 if ( EEPROM.read(Address3) ) { digitalWrite(Relay3, HIGH); } else { digitalWrite(Relay3, LOW); } //цикл считывания значения из EEPROM для реле №4 if ( EEPROM.read(Address4) ) { digitalWrite(Relay4, HIGH); } else { digitalWrite(Relay4, LOW); } if (client.connect("RelayClient")) { Serial.println("Online"); // client.publish("home/data/status/Relay/1", "OFF"); // client.publish("home/data/status/Relay/2", "OFF"); client.subscribe("home/data/status/Relay/#"); } } // Функция для кнопки №1 void Button1() { btnPress1 = digitalRead(BUTTON_1); StatusRelay1 = digitalRead(Relay1); if (btnPress1 && LastBtnStat1) { delay(35); // Задержка для защиты от дребезга btnPress1 = digitalRead(BUTTON_1); } LastBtnStat1 = btnPress1; } // Функция для кнопки №2 void Button2() { btnPress2 = digitalRead(BUTTON_2); StatusRelay2 = digitalRead(Relay2); if (btnPress2 && LastBtnStat2) { delay(35); // Задержка для защиты от дребезга btnPress2 = digitalRead(BUTTON_2); } LastBtnStat2 = btnPress2; } // Функция для кнопки №3 void Button3() { btnPress3 = digitalRead(BUTTON_3); StatusRelay3 = digitalRead(Relay3); if (btnPress3 && LastBtnStat3) { delay(35); // Задержка для защиты от дребезга btnPress3 = digitalRead(BUTTON_3); } LastBtnStat3 = btnPress3; } // Функция для кнопки №4 void Button4() { btnPress4 = digitalRead(BUTTON_4); StatusRelay4 = digitalRead(Relay4); if (btnPress4 && LastBtnStat4) { delay(35); // Задержка для защиты от дребезга btnPress4 = digitalRead(BUTTON_4); } LastBtnStat4 = btnPress4; } // Функция для переподключения соединения с Брокером void reconnect() { // Повторяем, пока не переподключимся... while (!client.connected()) { // Логическое НЕ "!" - Проверяем, если клиент не Законнектен.... // Попытка подключиться if (client.connect("RelayClient")) { // Если DHTClient клиент подключен... Serial.println(F("Connected!")); // Выводим сообщ., что подключено! } else { Serial.print(F("Failed connected - ")); // Ощибка соединения Serial.println(F("Jdem 5 seconds")); // Ждём 5 секунд delay(5000); } } } void loop() { if (!client.connect("RelayClient")) { // Если DHTClient клиент НЕ подключен... reconnect(); // Запускаем функцию переподключения } Button1(); Button2(); Button3(); Button4(); //Реле 1 if (REL1 == 0) { digitalWrite(Relay1, LOW); Serial.println(F("Relay1 - off")); EEPROM.write(Address1, 0); //сохраняем как Off client.publish("home/data/status/Relay/1", "0"); } else if (REL1 == 1) { digitalWrite(Relay1, HIGH); Serial.println(F("Relay1 - on")); EEPROM.write(Address1, 1); //сохраняем как On client.publish("home/data/status/Relay/1", "1"); } delay(900); //Реле 2 if (REL2 == 0) { digitalWrite(Relay2, LOW); Serial.println(F("Relay2 - off")); EEPROM.write(Address2, 0); //сохраняем как Off client.publish("home/data/status/Relay/2", "0"); } else if (REL2 == 1) { digitalWrite(Relay2, HIGH); Serial.println(F("Relay2 - on")); EEPROM.write(Address2, 1); //сохраняем как On client.publish("home/data/status/Relay/2", "1"); } delay(900); //Реле 3 if (REL3 == 0) { digitalWrite(Relay3, LOW); Serial.println(F("Relay3 - off")); EEPROM.write(Address3, 0); //сохраняем как Off client.publish("home/data/status/Relay/3", "0"); } else if (REL3 == 1) { digitalWrite(Relay3, HIGH); Serial.println(F("Relay3 - on")); EEPROM.write(Address3, 1); //сохраняем как On client.publish("home/data/status/Relay/3", "1"); } delay(900); //Реле 4 if (REL4 == 0) { digitalWrite(Relay4, LOW); Serial.println(F("Relay4 - off")); EEPROM.write(Address4, 0); //сохраняем как Off client.publish("home/data/status/Relay/4", "0"); } else if (REL4 == 1) { digitalWrite(Relay4, HIGH); Serial.println(F("Relay4 - on")); EEPROM.write(Address4, 1); //сохраняем как On client.publish("home/data/status/Relay/4", "1"); } delay(900); client.loop(); }
Переделал скетч, стали кнопки теперь работать, но вот как управлять по MQTT, не могу погнать.
Мало кому будет интересно копаться в длинных портянках, выискивая ваши ошибки. Если что-то не работает, локализуйте проблемный участок до нескольких строк, снабжайте код всеми необходимым пояснениями и только в таком виде формулируйте вопрос.
Тоже долго мучаюсь. Ты не закончил код? может поделишься ?
Отбросил эту идею, т.к. это накладно обходится: ардуино + Ethernet шилд + тянуть везде по дому витую пару.
Решил сделать по Wi-Fi через ESP8266, это дешевле и удобнее.
Но некоторые проекты именно через Arduino решил сделать.
Доброго дня. Я пошел по иному пути. Вот Код.
Этими данными управляю и получаю на разных устройствах.
Спасибо! Думаю этот код ещё мне пригодится для каких либо проектов. :)
Не могли бы помочь разобраться в коде.
Я так понял: нет поддержки постоянного нажатия кнопки?
да, вот сейчас переделал код чутка в Классе Sweeper по другому. Но чёт не то всё же....
Я бы сделал следующее: Отдельная функция проверяет нажатие кнопки (разовое или постоянное), учти, что по ИК идет не 1 постоянно, а поток знаков, которые надо делить на код или прописать как "0xFFC23D0xFFC23D0xFFC23D0xFFC23D.....". Я с таким сталкивался. В порт выведи инфу о приходе данных.
А почему именно ИК? Под такие задачи лучше RF/
У меня рабочий, почти готовый проект управления напольным вентилятором. Всё работает супер, кроме поворотов и автоповоротов вентилятора.
Для таких целей, я купил Вот такой прибор. И под него сделал: старенький холодильник, мультиварку, свет в прихожей и увлажнитель воздуха.
Есть готовый скрипт по проверке кнопок, могу выставить. Там дребезг (программно решен) и нажатие на удержание.
Добрый день.
Можете детально разжевать, как получать данные по mqtt? С отправкой проблем нет, а вот принять ничего не выходит.
например пишу "client.subscribe(relays_topic2);" и по идее теперь мне должны приходить сообщения, но куда? в какую переменную и в каком типе (char, int)?
подскажите пожалуйста на каком нибудь куске кода, буду благодарен.
Доброго дня. Вот простой, увлажнитель воздуха на MQTT, Но! Как показала практика, можно и в один топик кидать, лишь бы ЭХО не было. Забыл, допишу, что GPIO0 - подключет к реле, а реле к ПЛЮСУ +3,3В, если стандартно, через МИНУС, то модуль входит в режим программирования, ОН видит любой потенциал!!
GPIO0 - подключет к реле, а реле к ПЛЮСУ +3,3В
Жестоко. У ESP выходной ток совсем малые (по памяти 6мА) а Вы к нему реле. Наверно и без обратного диода.
Да. И поверьте все работает. Нагружал до 60мА. Сейчас все работает в связке с 24В. Управление идет через 4N35 . Схема:
Реле подключал ssr-25da. Там по току и напряжению есть запас. Работает отлично - управляет: светом, вытяжкой, холодильником и т.д... :)
Сейчас брокер MQTT стоит на Orange Pi PC2
Так судя по схеме не реле к GPIO0 а оптрон подключен. Без токоограничительного резистора 8) Мрак.
///Нагружал до 60мА.
Кого Вы нагружали ;) По доке у ESP нет такого тока на выходах и близко.
Верю, Вы правы на все 100%. Для пробы и не жалости ESP, 5В реле от ардуино, просто эксепимент. А вот SSR-25 работает до сих пор, как дистанционное управление. AC-DC (3.3В) + SSR-25 +ESP01.
Так чего ж там SSR-25 не работало бы, у него входной ток 4мА при 3В. Оптрон кстати лишний во всех случаях. Для SSR-25 вобще, для "5В реле от ардуино" (че за зверь такой - ХЗ, хрустальный шар говорит что электромагнитное) заменить на транзистор.
Все просто. SSR - коммутирует только 220В переменки. А мне надо 24В постоянки. Схемотехника, такая нежная штука, что приходится учитывать все нюансы как и человеческий фактор (женские ручки дома :) ). Но это не та тема. Сейчас все перевожу на полевики.
Все просто. SSR - коммутирует только 220В переменки. А мне надо 24В постоянки.
Однофазные твердотельные реле серии SSR представлены несколькими типами:
Схемотехника, такая нежная штука, что приходится учитывать все нюансы как и человеческий фактор (женские ручки дома :) ). Но это не та тема. Сейчас все перевожу на полевики.
Что было под рукой на том и собрал. А гльваника есть, там нет общего минуса. Давай не будем углубляться в философию «споделок».Все делается из подручных материалов. А вылизывание проекта, это уже дело времени и средств.
Что было под рукой на том и собрал. А гльваника есть, там нет общего минуса.
Ага. Аж два раза )))
Схема проще не придумаешь:
https://mysku.ru/blog/aliexpress/36460.html
Давай не будем углубляться в философию «споделок».Все делается из подручных материалов. А вылизывание проекта, это уже дело времени и средств.
Причем тут философия. Просто я указал Вам на лишний элемент схемы на SSR (кстати у него внутри свой оптрон еще имеется). И на невозможность подключения электромагнитного реле напрямую к ESP за превышения тока. Здесь нужен транзисторный ключ.
Все правильно. Вы все верно написали.
Почему я пытаюсь доказать, что есть развязка. логический "0" это не прямой "-3.3В". Подключение к GPIO0 идет через +3.3В. Если использовать подключение как GPIO0 == "1" , то модуль встает в программирование и ни какое сопротивление не спасет Вас от потенциала. Да, допускаю, что ключи формирования логических "0" и "1" - это практически минус и плюс напряжения питания. Тогда встает вопрос, откуда берется ограничение тока нагрузки, от ключей управления? Да. Ну, а дальше смотрим схему микросхемы 8266.
Ладно, это лирика. MQTT - удобно и просто. С этого вопроса все и началось.
Ох и бардак в мыслях )))
Забудьте "-3.3В." их в этой схеме нет, та точка которая у Вас так названа - это 0. Она ж и логический ноль. Если бы было -3.3В то вольтмер между -3.3 и +3.3 показал бы 6.6В, это не наш случай, такое бывает называется двухполярным питанием.
И "Подключение к GPIO0 идет через +3.3В. " тоже забудьте...
Я к счастю знаком с этой проблемой не по наслышке (например http://arduino.ru/forum/apparatnye-voprosy/polzuet-li-kto-wifi-moduli-esp8266-podelites-vpechatleniyami?page=20#comment-260721 ), потому понимаю что вы стараетесь сказать.
А именно, при старте модуля необходима подтяжка GPIO0 (и GPIO2 тоже) к 1, потому чтоб управляемое устройство не срабатывало при старте из-за подтяжки оно должно быть активным по 0. Может это кому и непревычно, но вполне нормально.
ПС. заодно и тут глянте http://arduino.ru/forum/apparatnye-voprosy/polzuet-li-kto-wifi-moduli-esp8266-podelites-vpechatleniyami?page=20#comment-260968
В строках:
первая часть кода с отправкой работает (отправляет на сервер нормально), а то что закомментированана нет (не принимает с сервера), подскажите, что я не так делаю?
Может я с утра слеповат. А где сама функция по приему топика. В 16 топике посмотрите.
По
void
callback(
const
MQTT::Publish& pub){
Можете подробнее расписать?
я никак не пойму как устроена библиотека pubsubclient.h на получение данных.
Отправить данные проблем не возникает, но вот получить никак не выходит.
Помогите, пожалуйста.
вставляю этот Ваш код
и получаю море ошибок компиляции
Вот тут есть видео, можно посмотреть и сразу все понять.
Добрый день. Помогите пожалуйста разобраться. Использую gprs модем А6 и uno. Хочу включать и выключать светодиод через mqtt клиент. Соединение настраиваю, подключаюсь к нету, к mqtt серверу - тут проблем нет. Но когда отправляю "1" происходит постоянное включение-выключение светодиода (моргает), в клиенте на телефоне отображается постоянное переключение с"1" на "0". Прошу помощи! Вторую неделю бьюсь, а результата нет.
Делай разный топик. Mytest/Myled - отправить а Mytest/Myled1 - принять, А так, у тебя цикл, время которого исчисляется запросом к брокеру.
Или, можно попробовать, без сохранения данных на брокере.
Спасибо! сегодня вечером попробую.
P.S. попытался. получилось.Спасибо за помощь!
ZhenyaRUS39, добрый день. Точно такая же проблема. Вам удалось ее побороть? Грешу на разные библиотеки pubsubclient.h.
ZhenyaRUS39, добрый день. Точно такая же проблема. Вам удалось ее побороть? Грешу на разные библиотеки pubsubclient.h.
А какую библиотеку pubsubclient использовали? Их существует две, причем разных версий. Ту, что через менеджер библиотек подключается?
Charly_big - не советую повторять код из предыдущего сообщения, он кривой и даже более - код опасен для контроллера.
ZhenyaRUS39 - в коде куча ошибок, не надо его никому советовать.
Вот это вообще феерично: в 96 строке вы читаете Number из ЕПРОМ. В строке 111 безальтернативно приравниваете его шести (спрашивается, зачем тогда читали из памяти?). В строке 173 прибавляете к Number единицу, записываете в ЕПРОМ (на каждом цикле? - офигели?!) но потом loop кончается и снова переходит к началу. И снова сначала читаем из ЕПРОМ, выкидываем это значение, выставляем Number = 6 и тд...
Евгений, Вы хоть чуть-чуть логику кода понимаете? или вы его тупо списали где-то в инете? Мало того, что в каждом прогоне loop у Вас Number всегда 6. так еще вы на каждом лупе пишете в его ЕПРОМ. Вы в курсе, что ресурс записи в ЕПРОМ совсем небольшой? Если прикинуть, что у вас проход основного цикла занимает около 4х секунд - значит при ресурсе ЕПРОМ 100 000 записей ваш ЕПРОМ может начать глючить уже на пятые сутки.
Решил проблему. Дело было в библиотеке. Скачал рабочий скетч и библиотеку по ссылке под видео https://youtu.be/q36C4rYAKVA
Полностью согласен с b707 . Только загубит память еще быстрее, НА СТАДИИ ОТЛАДКИ :-) . Если и хранить данные, то есть 4 метра для этого. Создайте файлик текстовый, туда и звписывайте. А если в лом, то используя MQTT, можно и в самом брокере - но это извращение, но как пример - можно.
Этот код сюда вставлен только для показа взаимодействия в области MQTT (вопрос был об этом).
Код не завершен. Запись в EPROM нужна т.к. это будет устройство, которое будет засыпать и просыпаться раз в 20 минут прогнять цикл и снова спать(а т.к. во время сна на контроллере не будет питания, то единственно место записи EPROM), и лишь на 6й цикл просыпания будет подключаться к Wi-fi для обмена данных сервером.
Создавать файл и записывать и читать с него я не умею.
Если есть примеры опысывающие, как записывать в файл в память, то прошу поделиться.
Если есть примеры опысывающие, как записывать в файл в память, то прошу поделиться.
Примеры есть в примерах Arduino IDE - открываете среду, идёте в пункт меню "Примеры" и - там примеры!
Создавать файл и записывать и читать с него я не умею.
Читать-то вы, надеюсь, умеете? Прочитайте ЕЩЕ РАЗ то что я вам написал. Во-первых - в вашем коде ВСЕГДА шестой цикл. Во-вторых - я вам обяснил, почему НЕЛЬЗЯ постоянно писать в ЕЕПРОМ. Тем более, что вы пишете туда ошибочные данные. В=третьих и в четвертых - вы, видимо, вообще крайне слабо представляете себе, как пишутся программы на ардуино - при засыпании контроллера на нем остается питание и данные в еепром писать необязательно, и в ЕЕПРОМ не пишут файлы!!!!
...и в ЕЕПРОМ не пишут файлы!!!!
А вот интересно, почему?
А то у меня тут как раз появилсь идея создать в EEPROM простенькую файловую систему.
...и в ЕЕПРОМ не пишут файлы!!!!
А вот интересно, почему?
А то у меня тут как раз появилсь идея создать в EEPROM простенькую файловую систему.
Доброго дня. Можно конечно сделать всё. А как же ресурс железяки. Надо и даташит читать. Раз идет тема про MQTT, а следовательно о ESP8266 (пока), то к ней незря приклеили 4 метра памяти. На ней хранить можно и файловую систему делать, но посточнная запись (не чтение) то же убийственна. Один из долговечных инструментов - CD карточка. Но и она может через годик сдохнуть. (Есть опыт с SQL - постоянный опрос и запись данных).
...ЕЕПРОМ...
Можно конечно сделать всё. А как же ресурс железяки... Один из долговечных инструментов - CD карточка. Но и она может через годик сдохнуть.
Вообщето ресурс EEPROM где-то на порядок больше ресурса flash. (если, конечно, имелась в виду SD, у CD ресурс еще меньше)
С учетом, что стоит 25q32fvsig, в даташите - W25Q32: 32 Мбит/4 Мбайт (4,194,304) .... До 100 тыс. циклов стирания/записи
PS - хотя по ДАТАШИТ написано, что БОЛЕЕ "More than 100,000 erase/program cycles"