Arduino rs485 modbus пример

Arduino & Modbus

В предыдущей статье мы соединили открытую платформу домашней автоматизации OpenHAB с контроллером Arduino использовав очень простой, текстовый протокол. Но это решение поставит нас в тупик, если мы захотим подключить наш контроллер к другой системе, что же делать?

Modbus — самый известный и распространенный стандарт в промышленной автоматизации, его поддерживают миллионы устройств по всему миру, эти устройства легко интегрируется в единую сеть и стыкуются с огромным количеством готового программного обеспечения. Попробуем использовать его в нашем проекте?

Что нам необходимо знать об этом стандарте?
Протокол Modbus использует последовательные линии связи (например, RS232, RS485), а протокол Modbus TCP рассчитан на передачу данных по сетям TCP/IP.
Протокол Modbus имеет два режима передачи RTU и ASCII, в режиме ASCII каждый байт передается как два ASCII символа его шестнадцатеричного представления.
В сети Modbus есть только один ведущий, который с заданным интервалом опрашивает несколько ведомых устройств, каждое из которых имеет свой уникальный адрес от 1 до 254, адрес 0 широковещательный и на него отвечают все устройства, так как ведущий в сети один у него нет своего адреса.
В спецификации Modbus определено два типа данных, один бит и 16 битное слово. Данные организованны в четыре таблицы с 16 битной адресацией ячеек, адресация в таблицах начинается с 0. Для доступа к данным из разных таблиц предназначены отдельные команды.

Discrete Inputs 1 бит только чтение
Coils 1 бит чтение и запись
Input Registers 16 бит только чтение
Holding Registers 16 бит чтение и запись

Как нам подключить Modbus устройство к OpenHAB? За это отвечает модуль Modbus Tcp Binding, этот модуль работает в режиме ведущего и обеспечивает подключение нескольких ведомых устройств через последовательный порт или TCP/IP сеть.
Для того чтобы связать с ним Arduino нам необходимо реализовать в контроллере ведомое Modbus устройство, воспользуемся для этого библиотекой Modbus-Master-Slave-for-Arduino.

Скачаем библиотеку и создадим скетч, скопировав следующий код в редактор программ. При желании можно просто скачать проект с необходимыми файлами из репозитория.

Рассмотрим на примере нашего скетча основные шаги необходимые для работы с этой библиотекой.

Все функции библиотеки реализованы в одном файле ModbusRtu.h.
Для взаимодействия с ней, в программе нужно создать объект, задав в его конструкторе Modbus адрес, номер последовательного порта, номер выхода, управляющего передачей (для RS485)

Modbus slave(ID, 0, 0);

Затем определить массив регистров Modbus

uint16_t au16data;

После этого, при старте программы настроить последовательный порт ведомого

slave.begin(19200);

В основном цикле программы необходимо вызывать функцию обработки Modbus сообщений

state = slave.poll(au16data, 11);

И после этого можно обработать полученные данные и сохранить необходимые переменные в регистрах Modbus.

#include «ModbusRtu.h» #define ID 1 // адрес ведомого #define btnPin 2 // номер входа, подключенный к кнопке #define stlPin 13 // номер выхода индикатора работы // расположен на плате Arduino #define ledPin 12 // номер выхода светодиода //Задаём ведомому адрес, последовательный порт, выход управления TX Modbus slave(ID, 0, 0); boolean led; int8_t state = 0; unsigned long tempus; // массив данных modbus uint16_t au16data; void setup() { // настраиваем входы и выходы io_setup(); // настраиваем последовательный порт ведомого slave.begin( 19200 ); // зажигаем светодиод на 100 мс tempus = millis() + 100; digitalWrite(stlPin, HIGH ); } void io_setup() { digitalWrite(stlPin, HIGH ); digitalWrite(ledPin, LOW ); pinMode(stlPin, OUTPUT); pinMode(ledPin, OUTPUT); pinMode(btnPin, INPUT); } void loop() { // обработка сообщений state = slave.poll( au16data, 11); // если получили пакет без ошибок — зажигаем светодиод на 50 мс if (state > 4) { tempus = millis() + 50; digitalWrite(stlPin, HIGH); } if (millis() > tempus) digitalWrite(stlPin, LOW ); //обновляем данные в регистрах Modbus и в пользовательской программе io_poll(); } void io_poll() { //Копируем Coil в Discrete au16data = au16data; //Выводим значение регистра 1.3 на светодиод digitalWrite( ledPin, bitRead( au16data, 3 )); //Сохраняем состояние кнопки в регистр 0.3 bitWrite( au16data, 3, digitalRead( btnPin )); //Копируем Holding в Input au16data = au16data; au16data = au16data; au16data = au16data; //Сохраняем в регистры отладочную информацию au16data = slave.getInCnt(); au16data = slave.getOutCnt(); au16data = slave.getErrCnt(); }

Стандарт предусматривает отдельную таблицу для каждого типа данных, но особенностью применённой библиотеки является то, что все регистры хранятся в одном массиве, поэтому структура регистров контроллера будет выглядеть следующим образом:

Регистр Тип Название Доступ
au16data discrete 0:DT0; 1:DI1; 2:DT2; 3:BTN только чтение
au16data coil 0:CL0; 1:CL1; 2:CL2; 3:LED чтение и запись
au16data input IN0 только чтение
au16data input IN1 только чтение
au16data input IN2 только чтение
au16data holding HLD0 чтение и запись
au16data holding HLD1 чтение и запись
au16data holding HLD2 чтение и запись

Для демонстрации работы с разными регистрами, в процессе работы программы данные из регистра с типом coil будут скопированы в регистр с типом discrete, а из регистров с типом holding в регистры с типом input. Кроме этого состояние кнопки будет сохранено в третий бит регистра au16data (discrete), а значение третьего бита регистра au16data (coil) выведено на светодиод.

Доработаем макет контроллера, который был собран для предыдущих экспериментов, переключим светодиод с 13 на 12 вывод. Обычно на плате самого Arduino уже есть светодиод, подключенный к 13 выводу, в нашей программе он станет индикатором статуса работы. Теперь подключим USB кабель к компьютеру, скомпилируем и загрузим программу в контроллер.

Пора приступать к испытаниям. Значительно облегчает работу на этом этапе эмулятор Modbus мастер-устройства, в сети есть несколько хороших, при этом бесплатных программ, вот некоторые из них:
www.focus-sw.com/fieldtalk/modpoll.html
qmodbus.sourceforge.net/
www.mikont.com/products/EAT-Console.html

Среди них можно отметить утилиту EAT-Console которая позволяет не только управлять и опрашивать Modbus устройства, но и отображает данные в графическом виде, что очень удобно при отладке работы с различными датчиками, например датчиками влажности, давления и температуры. Перед началом работы с программой и её конфигуратором рекомендую ознакомиться с документацией.

Для установки эмулятора нужно скачать архив и распаковать его в папку C:arduinoEATConsole, затем открыть страницу загрузки Eclipse, скачать Eclipse IDE for Java Developers и распаковать его в папку C:arduinoeclipse, после этого скопировать файлы из папки C:arduinoEATConsoleeclipseplugins в папку C:arduinoeclipseplugins.

Для создания конфигурации необходимо запустить C:arduinoeclipseeclipse.exe, создать пустой проект, скопировать в него пустой файл C:arduinoEATConsolemenu.ptmenu и добавить в редакторе пункты в соответствии со следующей таблицей. Если же вы скачали проект из репозитория, то в нём, в папке EATConsole уже есть подготовленный файл menu.ptmenu.

Type — тип элемента меню EATConsole.
Address — адрес регистра данных.
Bit – номер бита в регистре.
Name – название элемента меню.
Point – количество десятичных знаков после точки.
Slave – Modbus адрес контроллера.

Теперь сохраним и скопируем файл menu.ptmenu в каталог C:arduinoEATConsole, для этого можно щёлкнуть правой кнопкой мыши на файле прямо в Eclipse, выбрать в контекстном меню пункт “Copy”, а затем вставить в проводнике в папку C:arduinoEATConsole.

После этого запустим C:arduinoEATConsoleEATConsole.exe, настроим последовательное соединение, выбрав пункт меню ФайлНастройки, в диалоговом окне укажем номер порта, скорость 19200, 8 бит данных, 1 стоповый бит.

*Программа работает с портами с 1 по 8 и если USB переходник Arduino встал на порт с большим номером, придётся открыть диспетчер устройств Windows и изменить номер порта для него.

Когда все настройки будут введены, нажмите кнопку “Установить”, сразу после этого программа начнёт опрос устройства и если что-то пошло не так появится сообщение – НЕТ СВЯЗИ. Если же всё было сделано правильно и связь есть в чём можно убедиться по миганию индикатора статуса (светодиод на выводе 13), то пора приступить к испытаниям нашего контроллера.

Попробуем поменять значение в регистрах HLD0…HLD2 и СL0…СL2, при этом должно поменяться значение в соответствующем регистре IN0..IN2 и DT0..DT2, затем нажмём на кнопку макета, при этом должно поменяться значение в поле BTN, щёлкнем по полю LED, при этом должен загореться или потухнуть светодиод.

Что мы получили в результате нашей работы:

1 познакомились с азами протокола Modbus;
2 создали скетч, который превращает Arduino в Modbus slave устройство и позволяет читать и записывать несколько Modbus регистров с разными типами данных;
3 протестировали обмен с контроллером при помощи эмулятора функций Modbus master устройства, для которого создали конфигурацию соответствующую структуре регистров контроллера.

Выводы
Библиотека Modbus-Master-Slave-for-Arduino проста в использовании, позволяет создать ведомое Modbus устройство, которое корректно работает с программным эмулятором. Скомпилированный пример занимает немногим более 5кб памяти программ, так что в контроллере остаётся достаточно места для добавления необходимого функционала.

Стандарт Modbus открыт и популярен, но в нём есть ряд недостатков — в стандарте определено только два типа данных,
протокол требует постоянного обмена между ведущим и ведомыми устройствами, конфигурировать систему приходится вручную.

Имея некоторые недостатки, протокол вполне пригоден для использования в контроллере систем домашней автоматизации, особенно если необходима стыковка с различным программным обеспечением.

В следующий раз займёмся подключением контроллера к платформе OpenHAB.

В уроке представлю библиотеку для разработки программного обеспечения ведомого контроллера Ардуино в сетях с протоколом ModBus RTU. В качестве примера реализую связь контроллера с компьютером через интерфейс UART.

Предыдущий урок Список уроков Следующий урок

В уроке 48 мы разработали простой локальный контроллер и подключили его к компьютеру через стандартный интерфейс UART. Обмен данными происходил по специализированному протоколу, созданному специально для нашего контроллера.

В предыдущем уроке я подробно рассказал о протоколе ModBus RTU, ставшем стандартным протоколом обмена данными в малых распределенных вычислительных системах. Считается, что любой контроллер должен поддерживать работу в сети ModBus.

Давайте и наш контроллер из урока 48 подключим к компьютеру по этому протоколу. Для этого я разработал простую библиотеку. Прежде всего, подробно опишу ее функции и возможности.

Библиотека Tiny_ModBusRTU_Slave.

Библиотеку Tiny_ModBusRTU_Slave можно загрузить по этой ссылке:

Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!

Библиотека позволяет простыми средствами реализовать программное обеспечение ведомого контроллера с протоколом ModBus RTU. Может быть использована совместно с интерфейсами UART, RS-485, RS-422, RS-232 и т.п. Поддерживает управление передатчиком в шинных интерфейсах с третьим состоянием, подобных RS-485.

Слово ”Tiny” (крошечный) в названии библиотеки подчеркивает, что реализован минимальный набор функций. Всего 3 кода функции работы с регистрами хранения. Тем не менее, большинство контроллеров поддерживает именно этот набор операций.

Библиотека выполняет свои функции параллельным процессом. Все операции обмена по сети происходят в прерывании по таймеру. В основном цикле программа просто использует таблицу регистров ModBus.

Библиотека Tiny_ModBusRTU_Slave имеет конструктор и всего одну функцию.

class Tiny_ModBusRTU_Slave {
public:
Tiny_ModBusRTU_Slave(byte adress, byte timeOut, unsigned int * holdingRegTable, unsigned int lengthTable); // конструктор
Tiny_ModBusRTU_Slave(byte adress, byte timeOut, unsigned int * holdingRegTable, unsigned int lengthTable, byte directPin); // конструктор
void update(); // загрузка данных
boolean flagRead; // признак данные были считаны
boolean flagWrite; // признак данные были записаны
};

Конструктор.

Tiny_ModBusRTU_Slave(byte adress, byte timeOut, unsigned int * holdingRegTable, unsigned int lengthTable, byte directPin)

Создает объект со следующими параметрами:

  • address – адрес ведомого устройства в сети. Может иметь значение от 1 до 247.
  • timeOut – время паузы (тишины) перед передачей ответа. Значение должно быть не менее времени, необходимого на передачу 3,5 байта. Вычисляется, как timeOut * период вызова функции update().
  • holdingRegTable – указатель на массив (имя массива) – таблицы регистров хранения.
  • lengthTable – размер таблицы регистров хранения.
  • directPin – номер вывода разрешения работы передатчика ведомого устройства. Используется для шинных интерфейсов, у которых передатчик имеет три состояния. Например, RS-485. Параметр не обязательный. При его отсутствии вывод не используется.

Tiny_ModBusRTU_Slave slave(1, 12, regTable, 12); // создаем объект, адрес 1, таймаут 6 мс, массив regTable, размер 12

Метод.

update() – загрузка данных. Единственная функция класса. В ней происходит все управление обменом. Функция должна вызываться регулярно в параллельном процессе, например, в прерывании по таймеру.

// обработчик прерывания 500 мкс
void timerInterrupt() {
slave.update();
}

Метод update() полностью контролирует обмен данными по сети. Программа основного цикла работает только с массивом regTable (таблица регистров хранения). Данные массива доступны ведущему устройству сети для чтения и записи.

На всякий случай я добавил 2 признака, сообщающие об обращении ведущего устройства к регистрам хранения.

  • flagRead – признак чтения данных. Активное состояние (true) говорит о том, что ведущее устройство прочитало хотя бы один регистр хранения. Признак должен сбрасываться в основной программе при обработке.
  • flagWrite – признак записи данных. Активное состояние сообщает о том, что произошла запись хотя бы одного регистра. Т.е. данные таблицы были изменены. Признак должен сбрасываться в основной программе.

Библиотека поддерживает коды функций.

Код функции Название Описание
03 READ HOLDING REGISTERS Чтение значений одного или нескольких регистров хранения
06 FORCE SINGLE REGISTER Запись в один регистр хранения
16 FORCE MULTIPLE REGISTERS Последовательная запись нескольких регистров хранения

Обрабатываются следующие коды ошибок.

Код ошибки Название Описание
01 ILLEGAL FUNCTION Код функции не поддерживается в контроллере
02 ILLEGAL DATA ADRESS Недопустимый адрес данных

Библиотека поддерживает широковещательный обмен, при котором ведущее устройство формирует адрес равный 0.

Максимальное число байтов одного кадра – 64 байта.

Применение библиотеки Tiny_ModBusRTU_Slave.

Для практической реализации программы ведомого Ардуино контроллера необходимо:

  • подключить библиотеку Tiny_ModBusRTU_Slave;
  • создать массив – таблицу регистров;
  • создать объект Tiny_ModBusRTU_Slave;
  • задать параметры UART (класс Serial);
  • реализовать прерывание по таймеру и в его обработчик включить функцию update().

#include <Tiny_ModBusRTU_Slave.h>
#include <TimerOne.h>

unsigned int regTable;
Tiny_ModBusRTU_Slave slave(1, 8, regTable, 10); // создаем объект, адрес 1, таймаут 4 мс, массив regTable, размер 10

void setup() {
Timer1.initialize(500); // инициализация таймера 1, период 500 мкс
Timer1.attachInterrupt(timerInterrupt, 500); // задаем обработчик прерываний
Serial.begin(9600);
}

void loop() {
}

// обработчик прерывания
void timerInterrupt() {
slave.update();
}

В этом скетче цикл loop() остался пустым. Тем не менее, программа будет работать. Если подключить плату к сети ModBus, то ведущий контроллер сможет записывать и считывать данные из массива regTable. Другое дело, что больше контроллер ничего не делает. Получилось сетевое запоминающее устройство на 10 регистров.

Давайте загрузим этот простой скетч и проверим работу ведомого контроллера.

Для проверки я воспользовался эмулятором ведущего устройства ModBus. Таких программ в интернете очень много. Но то, что нужно я нашел с большим трудом.

Какие-то эмуляторы ModBus платные, другие сложные, требуют установки дополнительных программ. Некоторые допускают работу только с одним регистром. Попался мне эмулятор, который вроде меня устроил. Потом оказалось, что он при каждой посылке вырабатывает импульс на сигнале DTR конвертера USB-USRT и сбрасывает плату Ардуино. Понял я это не сразу.

В конце концов, я нашел эмулятор ведущего устройства ModBus RTU, который устроил меня полностью. Это эмулятор QModBus. Описывать его не буду. В программе все просто и интуитивно понятно.

Эмулятор QModBus опубликован по ”Универсальной общественной лицензии GNU GPL”, что допускает свободное распространение программы. Поэтому выкладываю программу на сайте: QModBus-0.2.1-win32.

С помощью QModBus я проверил все коды функции библиотеки, записывал и считывал разные данные, убедился, что правильно отрабатываются коды ошибок.

Реализация локального контроллера ModBus RTU.

Я использовал локальный контроллер из урока 48 без каких-либо изменений аппаратной части.

Повторю его схему.

Еще раз покажу, как он выглядит.

Программу, конечно, надо менять.

Прежде всего, необходимо выбрать назначение регистров хранения ModBus. Я задал адреса в подряд.

Номер регистра Формат числа Параметр
0 float Температура
1
2 float Напряжение
3
4 бит Состояние кнопки (мл. бит)
5 бит Состояние светодиода (мл. бит)

Напомню, что регистры в протоколе ModBus шестнадцатиразрядные. Получилось 6 регистров.

Осталось взять предыдущий скетч, добавить в основной цикл обработку датчиков и загрузку измеренных значений в регистры хранения.

Я использовал программу из урока 48. Добавил к ней объект Tiny_ModBusRTU_Slave.

Скетч можно загрузить по этой ссылке:

Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!

Начальную проверку я выполнил с помощью того же эмулятора QModBus.

Считывал 6 регистров хранения и убедился, что передача происходит без ошибок, данные меняются. Осмысленной получилась проверка только кнопки и управление светодиодом.

Программа верхнего уровня.

Мы используем числовой протокол. Поэтому показания датчиков выглядят как бессмысленный набор чисел. Для того, чтобы увидеть температуру и напряжение в ”человеческом виде” я написал программу верхнего уровня. Вернее я изменил протокол обмена программы UART_Arduino_PC из урока 48. И с помощью нее окончательно проверил работу локального контроллера.

Все работает идеально. Ни единой ошибки.

Программу верхнего уровня можете загрузить по ссылке:

Зарегистрируйтесь и оплатите. Всего 25 руб. в месяц за доступ ко всем ресурсам сайта!

Сравнение производительности протокола ModBus и специализированного протокола из урока 48.

В предыдущем уроке я сравнивал протокол ModBus со специализированными протоколами обмена данными в общем виде, без конкретных чисел. Теперь можно посчитать производительность обоих протоколов для одного и того же устройства.

Для обмена информацией с локальным контроллером по специализированному протоколу из урока 48 нам требовалось 15 байтов.

Для передачи той же самой информации по протоколу ModBus я насчитал 39 байтов. И еще паузы между кадрами.

Производительность сети ModBus более чем в два раза ниже. Это расплата за применение универсального протокола.

Библиотеку Tiny_ModBusRTU_Slave я буду использовать в дальнейших уроках для организации проводной сети между платами Ардуино. Проверить ее работу в сети ModBus с чужими контроллерами у меня нет возможности. Но испытание совместно с эмулятором ModBus ошибок в библиотеке не выявило.

Подключение

Подключается это к Arduino относительно просто.

Первый вопрос, который предстоит решить, это что физический интерфейс модуля является штырьковый контакт 4×2. Это не облегчает подключение к Arduino или макетной плате. К счастью для меня, у меня есть несколько 5 × 2 секционных коннекторов, которые я уже разработал, для других целей. Сложностью лишь является то, что распиновка должна быть в обратном направлении.

Второй вопрос в том, что этот модуль напряжение питания 3.3В. К счастью его входы 5В, а его выходы, достаточны для входа Arduino. Осталось только помнить что нам нужно получить 3,3В. Для Arduino это не проблема, потому что она имеет 3,3 пин, но не все Arduino-совместимые платы имеют его, поэтому иногда мне нужно регулировать напряжение питания в 3.3В.

Сигнал RF модуль Breakout
Board
Arduino
GND 1 2 GND
VCC 2 1 3V3
CE 3 4 8
CSN 4 3 9
SCK 5 6 13
MOSI 6 5 11
MISO 7 8 12
IRQ 8 7 2*

*Обратите внимание, что я никогда не подключаю IRQ пин в моей установке, но может кому то будет нужно.

В подключении на фото выше, у меня есть пара устройств. Слева, у меня Arduino Pro Mini (нет на фото), подключенный к плате с помощью 10-контактного кабеля используя по 5 × 2 коннектор. На этом устройстве есть регулятор 3V3 (без обвязки — позор мне!), И радио-модуль подключенный на другой стороне с помощью другого 5 × 2 шлейфа. Проводами сделано подключение, перечисленное в таблице чуть выше.

Справа находится сборка Arduino с пользовательским прототипом макетной платы. Поскольку в нем 3V3 встроенная, здесь легче подключение модуль. Опять же, радио подключен через короткий 10-контактный кабель.

Библиотека RF24

Получить библиотеку из GitHub: github:RF24.

Во-первых, стоит взглянуть на код, который там. Лучшее, что я нашел это nRF24L01 на Arduino Playground. Этот код дал неоценимую помощь в освоении модуля что заняло бы много времени. И особенно ценю простые примеры автора.

Тем не менее, я решил написать свою собственную библиотеку, потому что я имел ряд целей, которые не были выполнены этой библиотекой.

  • Использование стандартной SPI библиотеки. Пользователям будут проще и быстрее если библиотека опирается на официальную SPI библиотеку входящую в состав дистрибутива.
  • Protected internals. Как хорошее правило дизайна, важно, чтобы внутреннее устройство класса было скрыто от пользователей, и сосредоточено на предоставлении богатого внешнего интерфейса. Это дает возможность легче реорганизовать библиотеку, не нарушая ничего, что в данный момент используется.
  • Готовность к сложной топологии. Одно устройство может общаться со многими устройствами одновременно, однако это другая библиотека, предназначена для общения 1-к-1. В конечном счете я хочу, чтобы была возможность построить ячеистую сеть, поэтому библиотека должна быть готова к этому.
  • Полное соответствие спецификации. Я хотел убедиться, что библиотека ведет себя именно так, как говорит даташит.
  • Подобный интерфейс. Чтобы облегчить роботу пользователей, я хотел чтобы библиотека работала также хорошо как и стандартные библиотеки, насколько это возможно, но не скрывая мощности микросхемы.

Вот документации для публичных интерфейсов драйвера:

Конструктор. Создает новый экземпляр этого драйвера. Перед использованием необходимо создать экземпляр и передать уникальные пины, к которым этот чип подключен.

Параметры:

  • _cepin : контакт модуля Chip Enable
  • _cspin : контакт модуля Chip Select

>begin

Начало работы чипа. Вызываем его в setup(), перед вызовом любых других методов.

>setChannel

Набор каналов связи RF.

Параметры:

  • channel : Радиочастотный канал для общения, 0-127

Пример

В комплекте с библиотекой я составил примеры, чтобы запустить этих ребят, pingpair пример.

Физическое переключение режимов

Одна деталь, я решил написать один код для устройств передачи и приема. Это значительно упрощает логику. Для этого я использовал свободный пин устройства, для переключения между двумя режимами. Я называю их «roles». Существует передатчик «tole» и приемник «role». Пин 7 соединенный с землей задействует один режим, соединенный с питанием — второй.

// // Управление режимами // // Настройка адреса и режима. Этот скетч использует один и то же // код для всех устройств в этой системе // Это сильно упрощает тестирование. Устройство само определяет в // каком оно режиме // // Это делается через addr_pin. Установите его в низкий уровень для адреса # 0, высокой # 1. // // Этим скетчем поддерживаются разные режимы typedef enum { role_rx = 1, role_tx1, role_end } role_e; // Удобные для отладки названия режимов const char* role_friendly_name = { «invalid», «Receive», «Transmit»}; // Режимы какие имеет каждый из возможных аппаратных адресов const role_e role_map = { role_rx, role_tx1 }; // Текущий режим role_e role; void <b>setup</b>(void) { // // Адрес и режим // // установка номера пина pinMode(addr_pin, INPUT); digitalWrite(addr_pin,HIGH); delay(20); // Просто чтобы получить точное чтение с адреса пина // прочитать значение пина, устанавливаем адрес и режим node_address = digitalRead(addr_pin) ? 0 : 1; role = role_map;

НАСТРОЙКА Radio

Первичные настройки модулей …

// // Установка и настройка rf radio // radio.begin(); // Устанавливаем канал (по желанию) radio.setChannel(1); // Установка размера данных (не обязательно, но рекомендуется) // Библиотека использует фиксированный размер данных, так что если вы не установите, // то будет установлено автоматически. radio.setPayloadSize(sizeof(unsigned long)); // // Открытие канала на другие узлы для связи (обязательно) // // Этот простой скетч открывает два канала для общения этих двух узлов // туда и обратно // Открываем «наш» канал для записи radio.openWritingPipe(pipes); // Мы открываем ‘Другой’ канал для чтения, в позиции №1 (мы можем иметь до 5 каналов открытых для чтения) int other_node_address; if (node_address == 0) other_node_address = 1; else other_node_address = 0; radio.openReadingPipe(1,pipes); // // Начинаем слушать // radio.startListening(); // // Выводим конфигурацию RF-модуля для отладки // radio.print_details(); }

Режим передачи

Блок передатчика отправляет текущее millis() ждет тайм-аут или ответ от другого модуля, получает ответ, измеряет разницу, и выводит результат.

void <b>loop</b>(void) { // // Режим передачи. Неоднократное отправление текущего времени // if (role == role_tx1) { // Во-первых, перестать слушать, потому что нам нужно говорить. radio.stopListening(); // Получим время, и отправим его. unsigned long time = millis(); printf(«Now sending %lu…»,time); bool ok = radio.write( &amp;time ); // Теперь, продолжаем слушать radio.startListening(); // Ждем, пока мы не получим ответ, или тайм-аут (250 мс) unsigned long started_waiting_at = millis(); bool timeout = false; while ( ! radio.available() &amp;&amp; ! timeout ) if (millis() — started_waiting_at &gt; 250 ) timeout = true; // Выводим результат if ( timeout ) printf(«Failed, response timed out.\n\r»); else { // Получаем ответ, сравниваем и выводим unsigned long got_time; radio.read( &amp;got_time ); // Выводим printf(«Got response %lu, round-trip delay: %lu\n\r»,got_time,millis()-got_time); } // Повторяем через 1 с. delay(1000); }

режим приема

… А приемник делает противоположное, получив пакет, он отправляет его обратно к другому модулю.

if ( role == role_rx ) { // Если есть готовые данные if ( radio.available() ) { // Повторяем пока не получили все данные unsigned long got_time; boolean done = false; while (!done) { // Получаем данные, все до последнего. done = radio.read( &amp;got_time ); // Выводим это printf(«Got payload %lu…»,got_time); } // Во-первых, перестаем слушать, чтобы могли отправлять radio.stopListening(); // Отправить результат обратно. radio.write( &amp;got_time ); printf(«Sent response.\n\r»); // Теперь, возобновляем прослушивание и ждем следующие пакеты. radio.startListening(); } } }

Arduino и MODBUS

ВНИМАНИЕ! Весь материал, представленный в данной статье размещен только в образовательных и ознакомительных целях, автор данной статьи, равно, как и ресурс её разместивший не несут никакой ответственности за последствия, возникшие в результате использования данного материала!

Идея данной статьи «висела в воздухе» довольно давно. В свое время, работая на довольно высокотехнологичном производстве я порой сталкивался с ситуациями, когда из строя выходили элементы системы, с одной стороны не представляющие собой ничего сложного, а с другой без их нормального функционирования вся система оказывалась неработоспособной, и так, как содержание склада всех необходимых компонентов требовало, одновременно специализированных площадей и омертвления довольно весомого бюджета, приходилось простаивать, неся убытки. Вроде, как, это «проблемы начальства», которое не выделило вовремя средства, но не надо забывать, что «мы все в одной лодке» и недовыполнение объемов производства даже по объективным причинам напрямую сказывается на прибыли, а соответственно и на зарплате, ибо «тумбочка» у этого самого «начальства» отнюдь не резиновая. Кроме пресловутого бюджета есть еще временной фактор – время простоя оборудования, съедающее и без того в этой ситуации ограниченные денежные средства, а заказ нового элемента иногда длится несколько недель, а порой и месяцев, это при начале сотрудничества с компанией-поставщиком всё представляется довольно радужно, обещается «в случае чего выслать самолётом из Европы в течении суток»… Реальность, на самом деле более прозаична и после размещения «сверхсрочного заказа» идет выслушивание отговорок, что «один заболел, второй уехал в командировку, деталь зависла на таможне, перепутали на заводе и мы делаем возврат, и.т.д. и.т.п.»…

Поэтому при знакомстве с Arduino меня порадовало наличие достаточно большого разнообразия датчиков, аналоговые и цифровые входы, ШИМ… в общем все то, что при желании могло выручить не просто эмулируя отсутствующий элемент, но и более-менее успешно его заменяя. Хочу предупредить – я не сторонник замены промышленных элементов «игрушечными», однако иногда ситуация требует. Но вернемся к MODBUS-у. MODBUS — это то самое звено, которое связывает центральный компьютер, управляющий каким-либо оборудованием (либо целой системой, обеспечивающей технологический процесс), со всеми необходимыми датчиками и исполнительными элементами. Если кратко – то это открытый протокол обмена, позволяющий «железякам» общаться на одном языке, естественно, при этом понимая друг друга. Описывать сам протокол не буду, кому интересно – есть файлики в конце статьи, причем довольно толковые. Единственное о чем кратко упомяну – протокол работает по принципу «Ведущий – Ведомый» («Master – Slave»), где Ведущий (один единственный в системе) инициирует запросы и отдает команды, а Ведомые (может быть до 247 устройств в системе, но обычно намного меньше) ждут запрос, обрабатывают его и дают ответ.

Для практического изучения материала данной статьи нам понадобится: Arduino UNO, купить её проще всего можно здесь:http://arduino-kit.com.ua/product_4079.html или здесь: http://arduino-kit.com.ua/product_463.html, 2 светодиода, 2 резистора на 100 Ом и 1 переменный резистор на 10 КОм. 3 куска провода я не считаю, равно, как и 8-пиновую колодку, потому, что провод найдется практически у любого «на хозяйстве», а при должной аккуратности можно обойтись и без колодки. Из программного обеспечения нам понадобится библиотека SimpleModbusSlave.h и программа, которая могла бы эмулировать Ведущее устройство (MODBUS master) на нашем компьютере. В качестве такой программы (предварительно перепробовав массу всего интересного и не очень) я использую ModbusTerminal 2.0.3.9. это, конечно не предел мечты, но работает стабильно, в общем, для моих целей полностью подходит. Да, и еще одно:

Пожалуйста, не проводите нижеописанные эксперименты на компьютерах обеспечивающих управление оборудованием или технологическими процессами, это может привести не только к серьезным убыткам, в том числе и в связи с порчей оборудования, но также и к жертвам!!!

Часть первая, легкая, предварительная.

Для начала соберем нехитрую схему. Схема настолько проста, что предлагаю собрать сразу на весь материал. Длинные ножки светодиодов (аноды) подключаем к 10 и 11 пинам Ардуино, а короткие (катоды) подключаем к контакту GND Ардуино через 100-Омные резисторы. Переменный резистор подключаем согласно схеме на Рисунке 1.


Рисунок 1. Схема подключения переменного резистора.



То, что у меня в итоге получилось хорошо видно на фотографии (Рисунок 2) получилось за 15 минут не торопясь.


Рисунок 2. Собранная схема.



Загружаем скетч из файла Modbus_1_diod:
#include <SimpleModbusSlave.h> // Подключаем библиотеку
enum {D10,HOLDING_REGS_SIZE}; // Описываем регистры
unsigned int holdingRegs; // Определяем массив регистров
void setup(){
modbus_configure(&Serial, 9600, SERIAL_8N2, 1, 2, HOLDING_REGS_SIZE, holdingRegs); // Конфигурируем MODBUS
modbus_update_comms(9600, SERIAL_8N2, 1); // Описываем параметры обмена
pinMode(10, OUTPUT);} // Указываем работу 10-го пина на выход
void loop(){
modbus_update(); // Обновляем данные MODBUS
analogWrite(10, holdingRegs>>2);} // Записываем полученное значение в 10-й пин.

А теперь устанавливаем программу ModbusTerminal и делаем совершенно не сложные настройки: Указываем скорость обмена, порт, адрес устройства и адрес бита управляющего светодиодом. Думаю более понятно описание настроек можно представить визуально.




И если все в порядке, то при изменении значения регистра «0» (в пределах от 0 до 255) соответственно будет меняться яркость светодиода. В моем случае к 10-у пину Ардуино подключен светодиод зеленого цвета.

Часть вторая. Немножко усложним.

А теперь чуть-чуть усложним скетч, при этом вдвое расширим функционал – т.е. будем регулировать свечение сразу двух диодов.
Скетч вырастет совсем немножко – на 2 строчки. Загружаем в Ардуинку скетч из файла Modbus_2_diod:
#include <SimpleModbusSlave.h> // Подключаем библиотеку
enum {D10,D11,HOLDING_REGS_SIZE}; // Описываем регистры
unsigned int holdingRegs;// Определяем массив регистров
void setup(){
modbus_configure(&Serial, 9600, SERIAL_8N2, 1, 13, HOLDING_REGS_SIZE, holdingRegs);// Конфигурируем MODBUS
modbus_update_comms(9600, SERIAL_8N2, 1); // Описываем параметры обмена
pinMode(10, OUTPUT);// Указываем работу 10-го пина на выход
pinMode(11, OUTPUT);}// Указываем работу 11-го пина на выход
void loop(){
modbus_update();// Обновляем данные MODBUS
analogWrite(10, holdingRegs>>2);// Записываем полученное значение в 10-й пин.
analogWrite(11, holdingRegs>>2);}// Записываем полученное значение в 11-й пин.
Видно, что скетч «потяжелел» всего на 2 строчки. Настройки программы ModbusTerminal видно на Рисунке 4.


Рисунок 4. Запись в 2 регистра – нулевой и первый.



Значение регистра «1» в моем случае отвечает за яркость свечения светодиода красного цвета.

Часть третья, заключительная, но не исчерпывающая.

Протокол MODBUS включает в себя очень много интересного и просто описание всех его функций достойно гораздо большего объема, чем эта статья, не говоря уже о прикладных примерах, я же в своей третьей части ограничусь чтением одного единственного регистра при помощи этого протокола. Напоминаю, что ползунок переменного резистора подключен к контакту А0 нашей Ардуинки. Допишем в скетч еще пару строчек и получится код, который можно загрузить из файлаModbus_2_diod_plus_rezistor:
#include <SimpleModbusSlave.h> // Подключаем библиотеку
enum {D10,D11,RS0,HOLDING_REGS_SIZE};// Описываем регистры
unsigned int holdingRegs;// Определяем массив регистров
void setup(){
modbus_configure(&Serial, 9600, SERIAL_8N2, 1, 13, HOLDING_REGS_SIZE, holdingRegs);// Конфигурируем MODBUS
modbus_update_comms(9600, SERIAL_8N2, 1); // Описываем параметры обмена
pinMode(10, OUTPUT);// Указываем работу 10-го пина на выход
pinMode(11, OUTPUT);}// Указываем работу 11-го пина на выход
void loop(){
modbus_update();// Обновляем данные MODBUS
analogWrite(10, holdingRegs>>2);// Записываем полученное значение в 10-й пин.
analogWrite(11, holdingRegs>>2);// Записываем полученное значение в 11-й пин.
holdingRegs = analogRead(A0)/4;// Читаем значение АЦП и записываем его в регистр
delay(1);}// Пауза для подавления возможного «дребезга»
Скетч снова «потяжелел» на 2 строчки. Наверное, без последней строчки (паузы в 1 миллисекунду) можно было бы обойтись, но я умышленное её поставил, чтобы погасить возможный «дребезг» переменного резистора. Что получилось видно на Рисунке 5.


Рисунок 5. Чтение значения регистра 2.



На этом я заканчиваю данную статью, однако в будущем есть планы применить эти скромные наработки для чего-либо более интересного.



Все полезные файлы: 11-all-files.zip

Обзор подготовил Павел Сергеев

Arduino & Modbus

В предыдущей статье мы соединили открытую платформу домашней автоматизации OpenHAB с контроллером Arduino использовав очень простой, текстовый протокол. Но это решение поставит нас в тупик, если мы захотим подключить наш контроллер к другой системе, что же делать?
Modbus — самый известный и распространенный стандарт в промышленной автоматизации, его поддерживают миллионы устройств по всему миру, эти устройства легко интегрируется в единую сеть и стыкуются с огромным количеством готового программного обеспечения. Попробуем использовать его в нашем проекте?
Что нам необходимо знать об этом стандарте?
Протокол Modbus использует последовательные линии связи (например, RS232, RS485), а протокол Modbus TCP рассчитан на передачу данных по сетям TCP/IP.
Протокол Modbus имеет два режима передачи RTU и ASCII, в режиме ASCII каждый байт передается как два ASCII символа его шестнадцатеричного представления.
В сети Modbus есть только один ведущий, который с заданным интервалом опрашивает несколько ведомых устройств, каждое из которых имеет свой уникальный адрес от 1 до 254, адрес 0 широковещательный и на него отвечают все устройства, так как ведущий в сети один у него нет своего адреса.
В спецификации Modbus определено два типа данных, один бит и 16 битное слово. Данные организованны в четыре таблицы с 16 битной адресацией ячеек, адресация в таблицах начинается с 0. Для доступа к данным из разных таблиц предназначены отдельные команды.

Discrete Inputs 1 бит только чтение
Coils 1 бит чтение и запись
Input Registers 16 бит только чтение
Holding Registers 16 бит чтение и запись

Как нам подключить Modbus устройство к OpenHAB? За это отвечает модуль Modbus Tcp Binding, этот модуль работает в режиме ведущего и обеспечивает подключение нескольких ведомых устройств через последовательный порт или TCP/IP сеть.
Для того чтобы связать с ним Arduino нам необходимо реализовать в контроллере ведомое Modbus устройство, воспользуемся для этого библиотекой Modbus-Master-Slave-for-Arduino.
Скачаем библиотеку и создадим скетч, скопировав следующий код в редактор программ. При желании можно просто скачать проект с необходимыми файлами из репозитория.
Рассмотрим на примере нашего скетча основные шаги необходимые для работы с этой библиотекой.
Все функции библиотеки реализованы в одном файле ModbusRtu.h.
Для взаимодействия с ней, в программе нужно создать объект, задав в его конструкторе Modbus адрес, номер последовательного порта, номер выхода, управляющего передачей (для RS485)
Modbus slave(ID, 0, 0);
Затем определить массив регистров Modbus
uint16_t au16data;
После этого, при старте программы настроить последовательный порт ведомого
slave.begin(9600);
В основном цикле программы необходимо вызывать функцию обработки Modbus сообщений

state = slave.poll(au16data, 11);
И после этого можно обработать полученные данные и сохранить необходимые переменные в регистрах Modbus.
#include «ModbusRtu.h» #define ID 1 // адрес ведомого #define btnPin 2 // номер входа, подключенный к кнопке #define stlPin 13 // номер выхода индикатора работы // расположен на плате Arduino #define ledPin 12 // номер выхода светодиода //Задаём ведомому адрес, последовательный порт, выход управления TX Modbus slave(ID, 0, 0); boolean led; int8_t state = 0; unsigned long tempus; // массив данных modbus uint16_t au16data; void setup() { // настраиваем входы и выходы io_setup(); // настраиваем последовательный порт ведомого slave.begin( 9600 ); // зажигаем светодиод на 100 мс tempus = millis() + 100; digitalWrite(stlPin, HIGH ); } void io_setup() { digitalWrite(stlPin, HIGH ); digitalWrite(ledPin, LOW ); pinMode(stlPin, OUTPUT); pinMode(ledPin, OUTPUT); pinMode(btnPin, INPUT); } void loop() { // обработка сообщений state = slave.poll( au16data, 11); // если получили пакет без ошибок — зажигаем светодиод на 50 мс if (state > 4) { tempus = millis() + 50; digitalWrite(stlPin, HIGH); } if (millis() > tempus) digitalWrite(stlPin, LOW ); //обновляем данные в регистрах Modbus и в пользовательской программе io_poll(); } void io_poll() { //Копируем Coil в Discrete au16data = au16data; //Выводим значение регистра 1.3 на светодиод digitalWrite( ledPin, bitRead( au16data, 3 )); //Сохраняем состояние кнопки в регистр 0.3 bitWrite( au16data, 3, digitalRead( btnPin )); //Копируем Holding в Input au16data = au16data; au16data = au16data; au16data = au16data; //Сохраняем в регистры отладочную информацию au16data = slave.getInCnt(); au16data = slave.getOutCnt(); au16data = slave.getErrCnt(); }
Стандарт предусматривает отдельную таблицу для каждого типа данных, но особенностью применённой библиотеки является то, что все регистры хранятся в одном массиве в виде перекрывающихся таблиц, поэтому структура регистров контроллера будет выглядеть следующим образом:

Для демонстрации работы с разными регистрами, в процессе работы программы данные из регистра с типом coil будут скопированы в регистр с типом discrete, а из регистров с типом holding в регистры с типом input. Кроме этого состояние кнопки будет сохранено в третий бит регистра au16data (discrete), а значение третьего бита регистра au16data (coil) выведено на светодиод.
Доработаем макет контроллера, который был собран для предыдущих экспериментов, переключим светодиод с 13 на 12 вывод. Обычно на плате самого Arduino уже есть светодиод, подключенный к 13 выводу, в нашей программе он станет индикатором статуса работы. Теперь подключим USB кабель к компьютеру, скомпилируем и загрузим программу в контроллер.

Пора приступать к испытаниям. Значительно облегчает работу на этом этапе эмулятор Modbus мастер-устройства, в сети есть несколько хороших, при этом бесплатных программ, вот некоторые из них:
www.focus-sw.com/fieldtalk/modpoll.html
qmodbus.sourceforge.net
www.mikont.com/products/EAT-Console.html

Среди них можно отметить утилиту EAT-Console которая позволяет не только управлять и опрашивать Modbus устройства, но и отображает данные в графическом виде, что очень удобно при отладке работы с различными датчиками, например датчиками влажности, давления и температуры. Перед началом работы с программой и её конфигуратором рекомендую ознакомиться с документацией.

Для установки эмулятора нужно скачать архив и распаковать его в папку C:\arduino\EATConsole, затем открыть страницу загрузки Eclipse, скачать Eclipse IDE for Java Developers и распаковать его в папку C:\arduino\eclipse, после этого скопировать файлы из папки C:\arduino\EATConsole\eclipse\plugins в папку C:\arduino\eclipse\plugins.
Для создания конфигурации необходимо запустить C:\arduino\eclipse\eclipse.exe, создать пустой проект, скопировать в него пустой файл C:\arduino\EATConsole\menu.ptmenu и добавить в редакторе пункты в соответствии со следующей таблицей. Если же вы скачали проект из репозитория, то в нём, в папке EATConsole уже есть подготовленный файл menu.ptmenu.
Type — тип элемента меню EATConsole.
Address — адрес регистра данных.
Bit – номер бита в регистре.
Name – название элемента меню.
Point – количество десятичных знаков после точки.
Slave – Modbus адрес контроллера.

Теперь сохраним и скопируем файл menu.ptmenu в каталог C:\arduino\EATConsole, для этого можно щёлкнуть правой кнопкой мыши на файле прямо в Eclipse, выбрать в контекстном меню пункт “Copy”, а затем вставить в проводнике в папку C:\arduino\EATConsole.
После этого запустим C:\arduino\EATConsole\EATConsole.exe, настроим последовательное соединение, выбрав пункт меню Файл\Настройки, в диалоговом окне укажем номер порта, скорость 9600, 8 бит данных, 1 стоповый бит.

*Программа работает с портами с 1 по 8 и если USB переходник Arduino встал на порт с большим номером, придётся открыть диспетчер устройств Windows и изменить номер порта для него.
Когда все настройки будут введены, нажмите кнопку “Установить”, сразу после этого программа начнёт опрос устройства и если что-то пошло не так появится сообщение – НЕТ СВЯЗИ. Если же всё было сделано правильно и связь есть в чём можно убедиться по миганию индикатора статуса (светодиод на выводе 13), то пора приступить к испытаниям нашего контроллера.
Попробуем поменять значение в регистрах HLD0…HLD2 и СL0…СL2, при этом должно поменяться значение в соответствующем регистре IN0..IN2 и DT0..DT2, затем нажмём на кнопку макета, при этом должно поменяться значение в поле BTN, щёлкнем по полю LED, при этом должен загореться или потухнуть светодиод.

Что мы получили в результате нашей работы:
1 познакомились с азами протокола Modbus;
2 создали скетч, который превращает Arduino в Modbus slave устройство и позволяет читать и записывать несколько Modbus регистров с разными типами данных;
3 протестировали обмен с контроллером при помощи эмулятора функций Modbus master устройства, для которого создали конфигурацию соответствующую структуре регистров контроллера.
Выводы
Библиотека Modbus-Master-Slave-for-Arduino проста в использовании, позволяет создать ведомое Modbus устройство, которое корректно работает с программным эмулятором. Скомпилированный пример занимает немногим более 5кб памяти программ, так что в контроллере остаётся достаточно места для добавления необходимого функционала.
Стандарт Modbus открыт и популярен, но в нём есть ряд недостатков — в стандарте определено только два типа данных,
протокол требует постоянного обмена между ведущим и ведомыми устройствами, конфигурировать систему приходится вручную.
Имея некоторые недостатки, протокол вполне пригоден для использования в контроллере систем домашней автоматизации, особенно если необходима стыковка с различным программным обеспечением.
В следующий раз займёмся подключением контроллера к платформе OpenHAB.