Связь двух ардуин по rs-485
- Войдите на сайт для отправки комментариев
Доброго времени суток! Прошу сильно не пинать новичка. Проектирую сейчас свой "умный дом".
Вкратце расскажу суть. В каждую комнату проложена витая пара и в каждой организован небольшой щиток, в которой будет распологаться "комнатный" контроллер (пока arduino nano). Далее все "комнатные" контроллеры сводятся в один щиток к главному контроллеру - шлюзу mqtt (это будет arduino mega 2560). Далее Мега будет через Ethetnet-шилд общаться с mqtt-сервером и все данные будут выводиться на отдельный сервер визуализации.
Теперь к проблеме. Мне нужно организовать простую и надежную связь между комнатными контроллерами и главным. Я долго перебирал варианты и остановился на rs-485 (с помощью RS-485 - TTL конвертеров), благо провода уложены уже. Взял ардуино нано (прототип комн контрллера), уно (прототип главного). Пытаюсь реализовать задачу:
1. К нано подключена кнопка, при ее нажатии включается светодиод и в rs-485 канал отправляется сообщение, сигнализирующее, что светодиод включен. Уно принимает это сообщение, расшифровывает и отправляет на mqtt-сервер в соответствующий топик сообщение, что светодиод включен.
2. И в обратном порядке. mqtt генерит сообщение, что нужно выключить светодиод. Уно получает его, отправляет соответствующее сообщение по rs-485. Нано получает его, расшифровывает и гасит светодиод.
Вот мои скетчи:
//MASTER //Подключаем библиотеки #include <SoftwareSerial.h> #include <SPI.h> #include <Ethernet2.h> #include <PubSubClient.h> //Настройки пинов #define DIR 2 // переключатель прием\передача - DE-RE на конвертере TTL-RS-485 //Настройки RS-485 const byte ID = 1; // номер ардуины SoftwareSerial RS485 (7, 8); // назначаем пины RX, TX ардуины //Настройки сети byte mac[] = { 0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; IPAddress ip(192, 168, 31, 200); IPAddress mqttserver(192, 168, 31, 238); //Объекты, переменные, функции Ethernet и MQTT void callback(char* topic, byte* payload, unsigned int length); // шапка функции Callback для объявления и инициализации PubSubClient EthernetClient ethClient; // Инициализируем Ethernet клиент PubSubClient client(mqttserver, 1883, callback, ethClient); // Инициализируем MQTT клиент unsigned long lastReconnectAttempt = 0; // переменная определяет, сколько миллисекунд назад была попытка подключения к MQTT-серверу byte myArray[6] = {5, 6, 0, ID, 0, 0}; // создадим шаблон байтового массива в котором [0 и 1]-стандартное начало, [2]кому будет отсылаться, [3]ID- от кого, [4] порядковый номер сенсора (кнопка, датчик и т.д.), [5] произошедшее на сенсоре событие //Функция обработки входящих соединений - прием данных по подписке void callback(char* topic, byte* payload, unsigned int length) { //преобразуем тему(topic) и значение (payload) в строку payload[length] = '\0'; String strTopic = String(topic); String strPayload = String((char*)payload); //Исследуем что "прилетело" от сервера по подписке: if (strTopic == "/myhome/sw1") { if (strPayload == "off" || strPayload == "0" || strPayload == "false") { Serial.println ("switch off light"); digitalWrite(DIR, HIGH); // включаем передачу RS-485 myArray[2] = 2; // вносим изменения в массив. (2) - номер ардуины, которой отправляем задание myArray[4] = 1; // (1) - номер выключателя, состояние которого нужно изменить myArray[5] = 0; // (0) - как изменить состояние? -выкл. for (int i = 0; i < 6; i++) { // перебираем массив и передаем его по RS-485 Serial.println(myArray[i]); RS485.write(myArray[i]); } } if (strPayload == "on" || strPayload == "1" || strPayload == "true") { Serial.println ("switch on light bedroom"); digitalWrite(DIR, HIGH); // включаем передачу RS-485 myArray[2] = 2; // вносим изменения в массив myArray[4] = 1; myArray[5] = 1; for (int i = 0; i < 6; i++) { // перебираем массив и передаем его по RS-485 Serial.println(myArray[i]); RS485.write(myArray[i]); } } } delay(10); digitalWrite(DIR, LOW); // включаем прием RS-485 } boolean reconnect() { // функция переподключения к mqtt серверу if (client.connect("arduinoClient")) { // попытка подключения к mqtt-серверу // Если удалось подключиться, то сразу подписаться client.subscribe("/myhome/#"); } return client.connected(); //возвращает true если подключение к mqtt есть, false- если его нет } void setup() { Serial.begin(9600); // Запускаем отправку данных в Serial RS485.begin(9600); // Запускаем RS-485 pinMode(DIR, OUTPUT); digitalWrite(DIR, LOW); // включаем прием RS-485 Ethernet.begin(mac, ip); // устанавливаем Ethernet-соединение delay(1000); if (client.connect("arduinoClient")) { // соединяемся с mqtt сервером и если успешно то client.subscribe("/myhome/#"); // подписываемся на топик Serial.println("MQTT connected"); // и выводим сообщение } else Serial.println("MQTT not connected"); // если неуспешно то выводим сообщение } void loop() { // Восстановление MQTT-соединения при обрывах if (!client.connected()) { // если MQTT-соединение разорвалось, каждые 5с пробуем его восстанавливать Serial.println("MQTT disconnected.Attemp to connect"); unsigned long now = millis(); if (now - lastReconnectAttempt > 5000) { lastReconnectAttempt = now; if (reconnect()) { // попытка восстановления mqtt-соединения lastReconnectAttempt = 0; } } } // Обработка входящих сообщений по RS-485 if (RS485.available() >= 6) { // если пришел пакет по RS-485 длиной 6 и более byte kod1 = RS485.read(); // читаем первый пришедший байт и пишем его в переменную kod1 byte kod2 = RS485.read(); // читаем второй пришедший байт и пишем его в переменную kod2 byte id = RS485.read(); // читаем третий пришедший байт и пишем его в переменную id if (kod1 == 5 && kod2 == 6 && id == ID) { // если два первых байта - 5 и 6 и ID адресата совпадает с нашим, то читаем пришедшие данные дальше switch (RS485.read()) { // проверяем от какой ардуины пришел пакет - читаем четвертый байт и выполняем нужный case case 2: Serial.println("Starting task2..."); task_2(); break; } } else RS485.flush(); // если пришел пакет не нам - очищаем буфер RS-485 } client.loop(); } void task_2() { // обрабатываем пакет, пришедший с ардуины №2 switch (RS485.read()) { // проверяем по какому выключателю произошли изменения и далее считываем код изменения case 1: if (RS485.read() == 1) { // формируем mqtt пакет на условие, что на сенсоре 1 произошло изменение на TRUE client.publish("/myhome/sw1", "1"); } else { // формируем mqtt пакет на условие, что на сенсоре 1 произошло изменение на FALSE client.publish("/myhome/sw1", "0"); } break; } }
//SLAVE #include <SoftwareSerial.h> #include "OneButton.h" #define DIR 12 // переключатель прием\передача - DE-RE на конвертере TTL-RS-485 #define BUTTON_1 2 // указываем на каком пине находится первая кнопка #define LED_1 13 int ledValue_1 = LOW; //зададим начальное состояние светодиода const byte ID = 2; // номер ардуины byte myArray[6] = {5, 6, 1, ID, 0, 0}; //создадим шаблон байтового массив в котором [0 и 1]-стандартное начало, [2]кому будет отсылаться, [3]ID- от кого, [4] порядковый номер сенсора (кнопка, датчик и т.д.), [5] произошедшее на сенсоре событие // true значит что кнопка подтянута к плюсу OneButton button1(BUTTON_1, true); SoftwareSerial RS485 (7, 8); // назначаем пины RX, TX ардуины void setup() { Serial.begin(9600); // Запускаем отправку данных в Serial RS485.begin(9600); // Запускаем RS-485 pinMode(DIR, OUTPUT); pinMode(LED_1, OUTPUT); button1.attachClick(click1); // перечисляем функции первой кнопки digitalWrite(DIR, LOW); // включаем прием RS-485 digitalWrite(LED_1, LOW); } void loop() { button1.tick(); // функция постоянно следит за нажатием кнопки 1 delay(10); // Обработка входящих сообщений по RS-485. Принимаем сообщения только от Arduino-Master (ID=1), остальные отбрасываем if (RS485.available()) { // если пришел пакет по RS-485 byte kod1 = RS485.read(); // читаем первый пришедший байт byte kod2 = RS485.read(); // читаем второй пришедший байт byte id = RS485.read(); // читаем третий пришедший байт byte from = RS485.read(); // читаем четвертый пришедший байт if (kod1 == 5 && kod2 == 6 && id == ID && from == 1) { // если два первых байта - 5 и 6, ID адресата совпадает с нашим и данные пришли от Arduino-Master (ID=1), то читаем пришедшие данные дальше switch (RS485.read()) { // проверяем по какому выключателю пришел пакет - читаем пятый байт и выполняем нужный case case 1: if (RS485.read() == 1) { Serial.println("LED_1 vkl..."); digitalWrite(LED_1, HIGH); } else { Serial.println("LED_1 vikl..."); digitalWrite(LED_1, LOW); } break; } } else RS485.flush(); // если пришел пакет не нам - очищаем буфер RS-485 } } void click1() { Serial.println("Button 1 click."); //если свет был выключен, включаем его if ( ledValue_1 == LOW ) { Serial.println("turn on..."); ledValue_1 = HIGH; digitalWrite(DIR, HIGH); // включаем передачу RS-485 myArray[4] = 1; //вносим изменения в массив согласно изменения состояния myArray[5] = 1; for (int i = 0; i < 6; i++) { //перебираем массив и передаем его мастеру по RS-485 Serial.println(myArray[i]); RS485.write(myArray[i]); } } //если свет был включен, будем выключать else { Serial.println("turn off..."); ledValue_1 = LOW; digitalWrite(DIR, HIGH); // включаем передачу RS-485 myArray[4] = 1; //вносим изменения в массив согласно изменения состояния myArray[5] = 0; for (int i = 0; i < 6; i++) { //перебираем массив и передаем его мастеру по RS-485 Serial.println(myArray[i]); RS485.write(myArray[i]); } } digitalWrite(LED_1, ledValue_1); //записываем значение вкл/выкл на пин со светодиодом delay(10); digitalWrite(DIR, LOW); // включаем прием RS-485 }
Теперь что имеем.Жму кнопку на нано. Светодиод загорается, на уно приходит 5 6 1 2 1 1. Уно все разбирает и отправляет в mqtt сообщение "1". Здесь все гладко. А вот если я шлет сообщение mqtt-сервер, то тут проблема. УНО принимает его, формирует команду для нано и отправляет. Но ничего не просходит. Вставлял Serial print,смотрел что видит нано в порту который принимает rs-485. Там должно быть 5 6 2 1 1 0 (когда команда на выкл), но вместо этого я вижу 5 255 255 255 255 255 255 6 255 255 255 255 255 2 1 255 255 255 1 0 255 255 255 . Что то вроде того. То есть данные то пришли, но они разбавлены мусором. Причем это приходит не разом, а частями. Это я заметил, когда вставил Serial print. Выглядит примерно так :
"новое сообщение"
5 255 255 255 255 255 255
"новое сообщение"
6 255 255 255 255 255
"новое сообщение"
2 1 255 255 255
"новое сообщение"
сто пудов вы сильно быстро читаете из порта, у вас скорость отправки 9600 - это гораздо медленней, чем скорость чтения. Поэтому при чтении вы из буфера выбираете истинные занчения, а потом когда они заканчиваются начинаете брать оттуда неизвестно что. Поставьте в строчках 94-96 вашего мастера delay перед каждым чтением и посмотрите на результат (ни у по аналогии сделайте это в коде slave)
Спасибо, я попробую, как доберусь и отпишусь тут о результате. Вы пишите, что скорость отправки низкая. А может увеличить ее? Если да, то до скольки?