RS-485 на Ардуино

Обсуждаем Arduino, Raspberry Pi и другие электронные компоненты и проекты DIY
tema_po94
Сообщения: 6
Зарегистрирован: 01 июн 2017, 16:07

RS-485 на Ардуино

Сообщение tema_po94 »

Здравствуйте!
Помогите пожалуйста! Я только начал работать с Ардуино... И передо мной стоит следующая задача... реализовать протокол связи между двумя Ардуино Мега 2560 или Уно по принципу мастер-подчиненный. Обшарил весь интернет везде описаны различные вспомагательные библиотеки и нигде практически нет комментариев на этот счет...(
Мне всего то нужно прописать чтение данных и отправку с проверкой контрольной суммы, короче говоря именно по протоколу
Аватара пользователя
Mr.Kubikus
Сотрудник ПАКПАК
Сообщения: 1018
Зарегистрирован: 22 окт 2010, 23:57

Re: RS-485 на Ардуино

Сообщение Mr.Kubikus »

Привет!

Не могли бы вы более подробно описать протокол, который вы хотите реализовать?

Давайте договоримся, что у вас есть два устройства Arduino c драйвером RS485. Ведущее устройство (мастер) обозначим как М, а подчиненное (слейв) как S. Опишите, пожалуйста:
* Какой будет характер взаимодействия устройство: команды, данные или и то и другое?
* Если команды, то сколько их будет?
* Если данные, то в какую сторону они передаются - от M к S или от S к M?

Чем подробнее вы распишите вашу задачу, тем проще будет найти решение!
С уважением, Григорий
GitHub FB ВК
tema_po94
Сообщения: 6
Зарегистрирован: 01 июн 2017, 16:07

Re: RS-485 на Ардуино

Сообщение tema_po94 »

Спасибо, что откликнулись!
Mr.Kubikus писал(а):Какой будет характер взаимодействия устройство: команды, данные или и то и другое?
* Если команды, то сколько их будет?
* Если данные, то в какую сторону они передаются - от M к S или от S к M?
по идее и то и другое, на начальном этапе М отправляет массив данных на S и потом М опрашивает S и проверяет те ли это данные, что он посылал, если те, то хорошо, если нет, то ошибка (это своего рода защита); потом нужно будет с М отправлять команды на S и опрашивать S, получая массив данных (в качестве защиты опять же от ошибок в пересылке)
Команды будут самые простые, необходимо будет через М задавать в S определенные установочные данные и после в S должна выполнятся определенная функция в зависимости от заданной с М команды (...или возможно это не команда, а так же данные??)
проще говоря я с М посылаю на S, допустим коэффициент К=1, когда он приходит на S там исходя из того, что К=1 выполняется определенная функция (если отправляем К=2, то выполняется другая функция) и так же М должен опросить S, а то ли он прислал и ту ли функцию выполняет S

О протоколе:
ModBus – открытый протокол обмена данными в малых локальных сетях. Как правило, используется для передачи данных через интерфейсы RS-232, RS-485, RS-422, в сетях TCP/IP, UDP. Благодаря своей простоте и универсальности ModBus получил широкое распространение и стал де факто стандартом в малых распределенных вычислительных системах. Практически все современные контроллеры поддерживают работу в сетях ModBus.
В сети ModBus контроллеры, как правило, соединены по топологии ”Общая шина”. Взаимодействие контроллеров происходит в соответствии с моделью master-slave (ведущий-ведомый).
В сети есть главное устройство - ведущее. А также несколько подчиненных устройств – ведомые. Обмен может быть инициирован только ведущим устройством.
Транзакция (последовательность операций при обмене данными) состоит из запроса и ответа.
Ведущее устройство может адресовать запрос любому ведомому контроллеру или инициировать широковещательное сообщение, для всех ведомых устройств одновременно.
Ведомое устройство, определив свой адрес в запросе, формирует ответ.
В запросе от ведущего устройства обязательно содержится код функции, т.е. что надо делать. Также в зависимости от функции в запросе могут содержаться данные.

В общем случае кадр запроса (от М к S) имеет следующий формаn:
Пауза
Адрес 8 бит - Содержит адрес ведомого устройства, которому предназначено сообщение от ведущего
Функция 8 бит - Поле функции сообщает адресуемому ведомому устройству, какую операцию выполнить.
Данные N * 8 бит - В поле данных содержится информация, необходимую ведомому контроллеру для выполнения заданной функции или поле содержит данные передаваемые ведомым устройством по запросу ведущего. Длина и формат поля данных зависит от кода функции. В некоторых сообщениях данные могут отсутствовать.
Контрольная сумма 16 бит - Поле контроля целостности данных сообщений. Позволяет проверять кадры на наличие ошибок. Речь идет об ошибках, появляющихся при передаче данных.
Пауза
Аватара пользователя
Mr.Kubikus
Сотрудник ПАКПАК
Сообщения: 1018
Зарегистрирован: 22 окт 2010, 23:57

Re: RS-485 на Ардуино

Сообщение Mr.Kubikus »

Вы привели описание протокола Modbus. Почему бы не использовать соответствующие библиотеки для ведущего и подчиненного устройства?
С уважением, Григорий
GitHub FB ВК
tema_po94
Сообщения: 6
Зарегистрирован: 01 июн 2017, 16:07

Re: RS-485 на Ардуино

Сообщение tema_po94 »

Именно в том и загвоздка, что нужно обойтись без библиотек...
А как в библиотеке реализуется этот протокол не могли бы Вы объяснить по простому примеру (желательно адаптированному к моему)???
Я смотрел примеры и не очень их понял... какая команда за что отвечает... :(
Аватара пользователя
Mr.Kubikus
Сотрудник ПАКПАК
Сообщения: 1018
Зарегистрирован: 22 окт 2010, 23:57

Re: RS-485 на Ардуино

Сообщение Mr.Kubikus »

А в чём проблема с использованием библиотеки? Почему нельзя?
С уважением, Григорий
GitHub FB ВК
tema_po94
Сообщения: 6
Зарегистрирован: 01 июн 2017, 16:07

Re: RS-485 на Ардуино

Сообщение tema_po94 »

мне было дано задание реализовать Modbus протокол для связи двух ардуино по RS-485 как мастер и слейв...
Даже если и использовать библиотеку, то пока не ясно какую и мне в последствии их пояснять нужно будет, а я найти не могу нигде как в той или иной библиотеке реализуется Modbus протокол....
Проще говоря, если даже я буду использовать библиотеку, то проблема состоит в том, что не понятно как с ней работать
tema_po94
Сообщения: 6
Зарегистрирован: 01 июн 2017, 16:07

Re: RS-485 на Ардуино

Сообщение tema_po94 »

Mr.Kubikus писал(а):А в чём проблема с использованием библиотеки? Почему нельзя?
А как в данной библиотеке передавать данные? например массив? и как выполнять, например математические функции??? Я так и не смог разобраться... Помогите пожалуйста!
МАСТЕР

Код: Выделить всё

#include <SimpleModbusMaster.h>
/*
   SimpleModbusMaster позволяет вам общаться с любым подчиненным устройством с использованием протокола Modbus RTU.
  
   Для связи с ведомым вам необходимо создать пакет, который будет содержать всю информацию, необходимую для связи с ведомым.
   Счетчики информации реализованы для дальнейшей диагностики.
   Это переменные, уже реализованные в пакете.
   Вы можете установить и очистить эти переменные по мере необходимости.
   
   Выполняются следующие счетчики информации о модуле:
   
   Request_ - содержит общие запросы к подчиненному
   Success_requests - содержит итоговые успешные запросы
   Failed_requests - общие ошибки кадра, сбои контрольной суммы и отказы буфера
   Retries - содержит количество попыток
   Exception_errors - содержит конкретный счетчик ответов на исключение modbus
   Обычно это незаконная функция, незаконный адрес, недопустимое значение данных или разные ошибки.
  
   И, наконец, есть переменная, называемая «connection_», которая в данный момент содержит текущий статус соединения пакета. Если true, то соединение
   активный. Если false, связь будет остановлена ​​в этом пакете до тех пор, пока программист не установит переменную соединения в true. 
   Причина этого заключается в том, что время, связанное с коммуникацией по протоколу Modbus.
   Каждый неисправный ведомый, который не сообщает, замедляет связь на линии со значением тайм-аута. Например.
   Используя тайм-аут 1500 мс, если у вас 10 подчиненных и 9 из них перестают связываться с задержкой, то нагрузка будет равна 1500 мс * 9 = 13,5 секунд!
   Связь будет автоматически остановлена ​​после истечения срока действия повтора на каждом конкретном пакете.
  
   Вся проверка ошибок, обновление и многозадачность связи происходит в фоновом режиме.
  
   В общем, для связи с подчиненным устройством с использованием модуля Modbus RTU вы запрашиваете информацию, 
   используя конкретный идентификатор подчиненного устройства, запрос функции, начальный адрес
   И, наконец, данные для запроса.
   Поддерживаются функции 1, 2, 3, 4, 15 и 16. В дополнение к этому вещанию (id = 0) поддерживается функция 15 и 16.

   Константы предусмотрены для:
   Функция 1 - READ_COIL_STATUS // чтение состояния катушки
   Функция 2 - READ_INPUT_STATUS // чтение состояния ввода
   Функция 3 - READ_HOLDING_REGISTERS // чтение регистров хранения
   Функция 4 - READ_INPUT_REGISTERS // чтение входных регистров
   Функция 15 - FORCE_MULTIPLE_COILS // Закрепление нескольких катушек
   Функция 16 - PRESET_MULTIPLE_REGISTERS // задает несколько регистров
   
   Заметка:
   Буфер последовательного кольца Arduino - 64 байта или 32 регистра.
   В большинстве случаев вы подключаете Arduino, используя MAX485 или аналогичный.
 
   В запросе функции 3 или 4 мастер попытается прочитать из подчиненного устройства, и поскольку 5 байтов уже используются для ID, FUNCTION, NO OF BYTES и двух байтов CRT, мастер может запросить только 58 байтов или 29 регистров.
 
   В запросе функции 16 мастер попытается записать в подчиненное устройство, и поскольку 9 байтов уже используются для ID, FUNCTION, ADDRESS, NO REGISTERS, NO OF BYTES и двух байтов CRT, мастер может записывать только 54 байта или 27 регистров.
    
   Заметка:
   Использование USB-последовательного преобразователя Максимальные байты, которые вы можете отправить, ограничены его внутренним буфером, который отличается между производителями.
 
   Поскольку предполагается, что в основном вы будете использовать Arduino для подключения без использования USB для последовательного преобразователя, внутренний буфер устанавливается так же, как буфер последовательного кольца Arduino, который составляет 64 байта.
   
   В примере будет использоваться пакет1 для чтения регистра с адреса 0 (значение adc ch0) из подчиненного устройства arduino (id = 1). 
   Затем он будет использовать это значение для регулировки яркости светодиода на выводе 9 с использованием PWM.
   Затем он будет использовать пакет2 для записи регистра (его собственное значение adc ch0) в адрес 1 на ведомом устройства arduino (id = 1), регулирующем яркость светодиода на выводе 9 с использованием PWM.
*/
//////////////////// Информация о портах ///////////////////
#define baud 9600
#define timeout 1000
#define polling 200 // частота сканирования
// Если внутренний регистр повторных попыток пакетов совпадает с подсчитанным числом повторных попыток, связь прекращается в этом пакете. 
// Чтобы снова включить пакет, вы должны установить для переменной «connection» значение true.
#define retry_count 10
// используется для переключения буфера приема/передачи на драйвер
#define TxEnablePin 2
#define LED 9
// Это самый простой способ создания новых пакетов
// Добавьте столько, сколько вы хотите. TOTAL_NO_OF_PACKETS автоматически обновляется.
enum
  {
  PACKET1,
  PACKET2,
  TOTAL_NO_OF_PACKETS //оставить эту последнюю запись
  };
// Создаем массив пакетов для настройки
Packet packets[TOTAL_NO_OF_PACKETS];

// Создаем пакетный вызов для доступа к каждому пакету по отдельности. 
// Это не требуется, вы можете явно получить доступ к массиву. Например. packets [PACKET1].id = 2;
// Это становится утомительным, хотя ...
packetPointer packet1 = &packets[PACKET1];
packetPointer packet2 = &packets[PACKET2];
// Данные, считываемые с ведомого arduino, будут сохранены в этом массиве, если массив инициализирован пакетом.
unsigned int readRegs [1];
// Данные, которые должны быть записаны в подчиненное устройство arduino
unsigned int writeRegs [1];

void setup ()
{
// Функция конструктора пакетов Modbus инициализирует отдельный пакет с назначенными параметрами. 
// Вы всегда можете сделать это явно, используя указатели struct. Первым параметром является адрес рассматриваемого пакета. 
// Фактически этот параметр в Java указывает на адрес переданного объекта. 
// Он имеет следующую форму: modbus_construct (пакет, идентификатор, функция, адрес, данные, регистр)
// Для функций 1 и 2 - количество точек
// Для функций 3, 4 и 16 данных - количество регистров
// Для функции 15 данных - количество катушек
// читаем 1 регистр, начинающийся с адреса 0
modbus_construct(packet1, 1, READ_HOLDING_REGISTERS, 0, 1, readRegs);
// записываем 1 регистр, начиная с адреса 1
modbus_construct(packet2, 1, PRESET_MULTIPLE_REGISTERS, 1, 1, writeRegs);
// P.S. Записи массива регистров выше могут быть разными массивами  
/* Инициализировать настройки связи:
     Параметры (HardwareSerial * SerialPort,
long baud,
unsigned char byteFormat,
unsigned int timeout,
unsigned int polling,
unsigned char retry_count,
unsigned char TxEnablePin,
Packet packets,
unsigned int total_no_of_packets);

     Действующие форматы байтов Modbus:
     SERIAL_8N2: 1 стартовый бит, 8 бит данных, 2 стоповых бита
     SERIAL_8E1: 1 бит запуска, 8 бит данных, 1 бит четности, 1 стоповый бит
     SERIAL_8O1: 1 бит запуска, 8 бит данных, 1 бит четности четности, 1 стоповый бит
     
     Очевидно, вы можете использовать SERIAL_8N1, но это не соответствует спецификациям Modbus. 
    Тем не менее, я протестировал параметр SERIAL_8N1 на различных коммерческих мастерах и рабах, которые, как предполагалось, 
    придерживались этой спецификации и всегда могли общаться ... Go figure.
     
     Они уже определены в глобальном пространстве имен Arduino.
  */
  modbus_configure(&Serial, baud, SERIAL_8N2, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS);
  
  pinMode(LED, OUTPUT);
}

void loop ()
{
  modbus_update();

writeRegs [0] = analogRead(A0); // обновлять данные, которые должны быть записаны в arduino slave
analogWrite(LED, readRegs[0]>>2); // ограничиваем значение adc от ведомого arduino до 255  
/* Вы можете проверить или изменить внутренние счетчики определенного пакета следующим образом:
    packet1->requests; запросы
    packet1->successful_requests; //успешные запросы;
    packet1->failed_requests; //неудачные запросы
    packet1->exception_errors; //ошибки исключения
    packet2->requests;    //запросы
    packet2->successful_requests; //успешные запросы
    packet2->failed_requests;     //неудачные запросы
    packet2->exception_errors;    //ошибки исключения
*/
 }
СЛЭЙВ

Код: Выделить всё

#include <SimpleModbusSlave.h>

/* Этот примерный код получит значение adc ch0 от мастера arduino.
   Затем он будет использовать это значение для регулировки яркости светодиода на выводе 9.
   Значение, полученное от ведущего устройства, будет сохранено в адресе 1 в его собственном адресном пространстве, а именно: holdRegs [].
   
   В дополнение к этому ведомые собственные значения adc ch0 будут сохранены в адресе 0 в своем собственном адресном пространстве, удерживая Regs [] для считываемого мастера. 
   Мастер будет использовать это значение, чтобы изменить яркость собственного светодиода, подключенного к выходу 9.
   
   Метод modbus_update () обновляет массив регистров holdRegs и проверяет связь.

   Заметка:
   Буфер последовательного кольца Arduino - 64 байта или 32 регистра.
   Большую часть времени вы подключаете ардуино к мастеру через последовательный порт с помощью MAX485 или аналогичного.
 
   В запросе функции 3 мастер попытается прочитать из вашего подчиненного устройства, 
   и поскольку 5 байтов уже используются для ID, FUNCTION, NO OF BYTES и двух байтов CRT, мастер может запросить только 58 байтов или 29 регистров.
 
   В запросе функции 16 мастер попытается записать в подчиненное устройство, и поскольку 9 байтов уже используются для 
   ID, FUNCTION, ADDRESS, NO REGISTERS, NO OF BYTES и двух байтов CRT, мастер может записывать только 54 байта или 27 регистров.
 
   Использование USB-последовательного преобразователя Максимальные байты, которые вы можете отправить, ограничены его внутренним буфером, который отличается между производителями.
*/

#define  LED 9  

// Использование команды enum позволяет использовать простой способ для добавления и удаления регистров. 
// Выполнение таким образом экономит ваше # определение размера вашего регистра регистрового массива каждый раз, когда вы хотите добавить больше регистров, 
// и с первого взгляда информирует вас о вашем расположении регистра подчиненных.

//////////////// регистры вашего slave ///////////////////
enum 
{     
// просто добавляем или удаляем регистры...
// Первый регистр начинается с адреса 0
  ADC_VAL,     
  PWM_VAL,        
  HOLDING_REGS_SIZE // оставить это
// общее количество регистров для функций 3 и 16 разделяет один и тот же массив регистров
// то же адресное пространство
};

unsigned int holdingRegs[HOLDING_REGS_SIZE]; // функция 3 и 16 массива регистров
////////////////////////////////////////////////////////////

void setup()
{
  /* parameters(HardwareSerial* SerialPort,
                long baudrate, 
		            unsigned char byteFormat,
                unsigned char ID, 
                unsigned char transmit enable pin, 
                unsigned int holding registers size,
                unsigned int* holding register array)
  */
  
  /* Действующие форматы байтов Modbus:
     SERIAL_8N2: 1 start bit, 8 data bits, 2 stop bits
     SERIAL_8E1: 1 start bit, 8 data bits, 1 Even parity bit, 1 stop bit
     SERIAL_8O1: 1 start bit, 8 data bits, 1 Odd parity bit, 1 stop bit
     
     Очевидно, вы можете использовать SERIAL_8N1, но это не соответствует спецификациям Modbus. 
     Тем не менее, я протестировал параметр SERIAL_8N1 на различных коммерческих мастерах и рабах, которые, как предполагалось, 
     придерживались этой спецификации и всегда могли общаться ... Go figure.
     
     Эти форматы байтов уже определены в глобальном пространстве имен Arduino.
   */
	
  modbus_configure(&Serial, 9600, SERIAL_8N2, 1, 2, HOLDING_REGS_SIZE, holdingRegs);

  // modbus_update_comms (baud, byteFormat, id) не требуется, но позволяет легко обновлять переменные порта и ведомый идентификатор динамически в любой функции.
  modbus_update_comms(9600, SERIAL_8N2, 1);
  
  pinMode(LED, OUTPUT);
}

void loop()
{
  // modbus_update () - единственный метод, используемый в loop (). 
  // Он возвращает общее количество ошибок с момента запуска подчиненного устройства. 
  // Вам не обязательно использовать его, но он полезен для диагностики на ведущем устройстве Modbus.
  
  modbus_update();
  
  holdingRegs[ADC_VAL] = analogRead(A0); // обновлять данные, которые должен быть прочитан мастером для настройки PWM
  
  analogWrite(LED, holdingRegs[PWM_VAL]>>2); // ограничиваем значение adc от мастера arduino до 255

/* Заметка:
      Использование команды enum не требуется. Вы можете установить максимально допустимый размер для holdinRegs [], 
      указав HOLDING_REGS_SIZE с использованием константы, а затем получить доступ с помощью Regs [] по адресу «Индекс».
      То есть
      HoldRegs [0] = analogRead (A0);
      AnalogWrite (LED, holdRegs [1] / 4);
*/
  
}
Аватара пользователя
Mr.Kubikus
Сотрудник ПАКПАК
Сообщения: 1018
Зарегистрирован: 22 окт 2010, 23:57

Re: RS-485 на Ардуино

Сообщение Mr.Kubikus »

Привет!

Исходники, которые вы привели, очень похожи на те, которые мы обсуждали в этом топике. Там мы подробно все разобрали. Посмотрите его внимательно. Если останутся вопросы буду рад помочь.
С уважением, Григорий
GitHub FB ВК
tema_po94
Сообщения: 6
Зарегистрирован: 01 июн 2017, 16:07

Re: RS-485 на Ардуино

Сообщение tema_po94 »

Mr.Kubikus писал(а):Если останутся вопросы буду рад помочь.
Я все же не могу понять как обмениваться данными...
как например прочитать значение переключателя на слэйве и по его его значению включить лампочку на мастере???
Ответить