Связь двух ардуин по 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)
Спасибо, я попробую, как доберусь и отпишусь тут о результате. Вы пишите, что скорость отправки низкая. А может увеличить ее? Если да, то до скольки?