Возникла задача — соединить устройство на микроконтроллере atmega avr с компьютером по rs232. Что бы не изобретать велосипед, в качестве протокола обмена был выбран modbus.
- Поддерживается большинством промышленных (scada) систем.
- Существуют большое количество свободно распространяемых библиотек/программ с открытым исходным кодом.
Для отладки собрал стенд: stk500 c atmega avr подключенный к обычному компьютеру через переходник rs232-usb. Роль мастера (master) будет выполнять ПК, роль ведомого (slave) — avr atmega на stk500.
За основу возьмем:
- Для ведомого (slave) — freemodbus;
- Для ведущего (master) — qmodbus;
freemodbus
freemodbus — реализация ведомого устройства (slave), поддерживает modbus-rtu, modbus-ascii, modbus-tcp, портирован на большое количество архитектур, в том числе и для atmega avr. Скачать исходники можно тут тут ( версия 1.5.0).
Компиляция freemodbus
Скачиваем, разархивируем, заходим в freemodbus-v1.5.0/demo/AVR.
Работа в программе FLProg с протоколом Modbus по интерфейсу RS-485
Запускаем make и… ничего не собирается, получаем ошибку:
make: /opt/gcc-avr/bin/avr-gcc: Command not found make: *** [demo.o] Error 127
Что бы поправить сборку под avr редактируем Makefile, изменяем переменные СС, OBJCOPY, AVRDUDE. В них надо указать путь к соответствующим программам. У меня путь указан в переменной окружения PATH, по этому просто указываю имена программ:
CC=avr-gcc OBJCOPY=avr-objcopy AVRDUDE=avrdude
Далее задаем используемый avr микроконтроллер:
MCU = atmega16
В CFLAGS указываем какой используется кварц — 8 МГц:
-DF_CPU=8000000UL
И из правила сборки по-умолчанию убираем cof и eep, оставляем elf и hex:
all: $(TARGET).elf $(TARGET).hex
Теперь все собирается.
Доступ к данным в modbus
В описании протокола modbus определено четыре типа данных:
- Discrete Inputs: размер — один бит, доступен только на чтение.
- Coils: размер — один бит, доступен на чтение и на запись.
- Input Registers — 16-битный регистр, доступен только на чтение.
- Holding Registers — 16-битный регистр, доступен на чтение и на запись.
Для каждого типа в freemodbus надо реализовать функцию, обеспечивающую доступ к данным.
Для Input Registers:
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs );
Для Holding Registers:
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );
eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode );
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode );
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode );
Для Discrete Inputs:
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete );
eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete );
Протокол MODBUS
Где:
pucRegBuffer — массив в который будет записаны значения регистров при чтении, или из которого будут считаны значения регистров которые надо записать.
usAddress — начальный адрес регистров
usNRegs, usNDiscrete, usNCoils — количество регистров
eMode — чтение или запись
На stk500 есть 8 светодиодов и 8 кнопок, сделаем так что бы управление светодиодами осуществлялось через Holding Register, а состояние кнопок считывалось из Input Register. При при помощи шлейфов PORTВ подключим к светодиодам а PORTA к кнопкам.
Получается у нас будет один регистр хранения (Holding Register) с адресом 1 и один регистр ввода (Input Register) тоже с адресом 1.
У каждого типа регистров в modbus свое адресное пространство, по этому регистры разного типа могут иметь одинаковые адреса.
В файле freemodbus-v1.5.0/demo/AVR/demo.c изменим код функций eMBRegInputCB и eMBRegHoldingCB:
eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) { eMBErrorCode eStatus = MB_ENOERR; if( ( usAddress == 1 ) ( usNRegs == 1 ) ) { *pucRegBuffer++ = 0; *pucRegBuffer++ = ~PINA; } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) { eMBErrorCode eStatus = MB_ENOERR; if( ( usAddress == 1 ) ( usNRegs == 1 ) ) { if( eMode == MB_REG_READ ) { *pucRegBuffer++ = 0; *pucRegBuffer++ = ~PINB; } else { pucRegBuffer++; PORTB = ~(*pucRegBuffer++); } } else { eStatus = MB_ENOREG; } return eStatus; }
В качестве реализаций eMBRegCoilsCB и eMBRegDiscreteCB оставим функции — ‘заглушки’:
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) { return MB_ENOREG; } eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) { return MB_ENOREG; }
eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) < return MB_ENOREG; >eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
Так же надо не забыть настроить PORTB на выход и выставить начальное значение, для этого в функции main добавим строки:
DDRB = 0xFF; //настраиваем PORTB на выход PORTB = 0xFF; //выключаем все светодиоды
DDRB = 0xFF; //настраиваем PORTB на выход PORTB = 0xFF; //выключаем все светодиоды
Настройка подключения modbus
Согласно описанию протокола modbus существует три варианта реализации : RTU, ASCII и TCP.
- modbus ASCII — все данные передаются в текстовом виде, удобно отлаживать, но уменьшатся скорость
- modbus RTU — данные в бинарном виде, скорость выше чем у modbus ASCII
- modbus TCP — для обмена поверх протокола TCP
Наш выбор — modbus RTU.
Для настройки соединения в freemodbus надо вызывать функцию eMBInit:
eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity );
eMBErrorCode eMBInit( eMBMode eMode, UCHAR ucSlaveAddress, UCHAR ucPort, ULONG ulBaudRate, eMBParity eParity );
- eMode — тип реализации, MB_RTU или MB_ASCII
- ucSlaveAddress — адрес устройства на шине modbus
- ucPort — номер CОМ порта, в реализации для avr atmega не имеет значения
- ulBaudRate — скорость обмена, например 38400
- eParity — контроль четности: MB_PAR_NONE — без контроля четности, MB_PAR_ODD — дополнение до нечетности , MB_PAR_EVEN — дополнение до четности.
Пример настройки: протокол modbus RTU, адрес контроллера 10, скорость 38400, включен контроль дополнения до четности.
eStatus = eMBInit( MB_RTU, 10, 0, 38400, MB_PAR_EVEN );
eStatus = eMBInit( MB_RTU, 10, 0, 38400, MB_PAR_EVEN );
Так как modbus TCP и modbus ASCII использовать не будем, отключим ненужные части кода, что бы уменьшить размер программы. Для этого отредактируем файл modbus/include/mbconfig.h, заменим (1) на (0) у дефайнов MB_ASCII_ENABLED и MB_TCP_ENABLED.
/*! brief If Modbus ASCII support is enabled. */ #define MB_ASCII_ENABLED ( 0 ) /*! brief If Modbus RTU support is enabled. */ #define MB_RTU_ENABLED ( 1 ) /*! brief If Modbus TCP support is enabled. */ #define MB_TCP_ENABLED ( 0 )
/*! brief If Modbus ASCII support is enabled. */ #define MB_ASCII_ENABLED ( 0 ) /*! brief If Modbus RTU support is enabled. */ #define MB_RTU_ENABLED ( 1 ) /*! brief If Modbus TCP support is enabled. */ #define MB_TCP_ENABLED ( 0 )
QModBus
QModBus, кросcплатформенная реализация на Qt, компилируется под windows и под linux. Поддерживает modbus-rtu и modbus-ascii. Скачать исходники можно тут (версия 0.2.1 )
Компиляция QModBus
Скачиваем исходники тут. разархивируем. пытаемся собрать
qmake make
И опять компиляция не проходит, выдает предупреждения и ошибки:
WARNING: Failure to find: 3rdparty/libmodbus/modbus.c WARNING: Failure to find: 3rdparty/libmodbus/modbus-data.c WARNING: Failure to find: 3rdparty/libmodbus/modbus-rtu.c WARNING: Failure to find: 3rdparty/libmodbus/modbus-tcp.c WARNING: Failure to find: 3rdparty/libmodbus/modbus.h src/mainwindow.h:30:20: fatal error: modbus.h: No such file or directory compilation terminated.
WARNING: Failure to find: 3rdparty/libmodbus/modbus.c WARNING: Failure to find: 3rdparty/libmodbus/modbus-data.c WARNING: Failure to find: 3rdparty/libmodbus/modbus-rtu.c WARNING: Failure to find: 3rdparty/libmodbus/modbus-tcp.c WARNING: Failure to find: 3rdparty/libmodbus/modbus.h src/mainwindow.h:30:20: fatal error: modbus.h: No such file or directory compilation terminated.
Изменяем файл проекта qmodbus.pro, исправляем перменные SOURCES, HEADERS, INCLUDEPATH:
SOURCES += src/main.cpp src/mainwindow.cpp 3rdparty/qextserialport/qextserialport.cpp 3rdparty/libmodbus/src/modbus.c 3rdparty/libmodbus/src/modbus-data.c 3rdparty/libmodbus/src/modbus-rtu.c 3rdparty/libmodbus/src/modbus-tcp.c HEADERS += src/mainwindow.h 3rdparty/qextserialport/qextserialport.h 3rdparty/qextserialport/qextserialenumerator.h 3rdparty/libmodbus/src/modbus.h INCLUDEPATH += 3rdparty/libmodbus/src 3rdparty/libmodbus 3rdparty/qextserialport
SOURCES += src/main.cpp src/mainwindow.cpp 3rdparty/qextserialport/qextserialport.cpp 3rdparty/libmodbus/src/modbus.c 3rdparty/libmodbus/src/modbus-data.c 3rdparty/libmodbus/src/modbus-rtu.c 3rdparty/libmodbus/src/modbus-tcp.c HEADERS += src/mainwindow.h 3rdparty/qextserialport/qextserialport.h 3rdparty/qextserialport/qextserialenumerator.h 3rdparty/libmodbus/src/modbus.h INCLUDEPATH += 3rdparty/libmodbus/src 3rdparty/libmodbus 3rdparty/qextserialport
qmake make
Теперь сборка работает.
Обмен данными с avr по modbus
Устанавливаем соответствующий COM-порт, скорость 38400, 8 бит данных, контроль дополнения до четности, 1 стоп бит. Slave ID — адрес контроллера avr на шине modbus равен 10.
Читаем состояние кнопок, для этого выбираем команду «Read Input Registers», выставляем адрес регистра = 0, и количество = 1. После нажатия на кнопку Send происходит обмен. Считанное из avr значение отображается в поле Data (на картинке прочитано число 128).
Для управления светодиодам используем команду «Write Single Register». В поле Data надо занести значение, которое будет записано в PORTB. После нажатия Send светодиоды, подключенные к avr, загораются.
Исходники
Архив проекта со всеми изменениями можно скачать тут
Запись опубликована в рубрике Микроконтроллеры avr с метками avr, modbus, qt, uart. Добавьте в закладки постоянную ссылку.
Источник: mainloop.ru
Взаимодействие с устройствами через ModBus RTU в Qt
Недавно мне на аутсорсе предложили выполнить небольшую работу: разработать ПО с графическим интерфейсом для управления источником питания по протоколу ModBus RTU. Не раздумывая я согласился решить поставленную задачу, однако меня ждал “интересный” сюрприз, вызванный моей же невнимательностью. В этой заметке я приведу пример работы с протоколом ModBus RTU в Qt с подробными коментариями, а заодно расскажу о своей досадной оплошности допущенной при решении этой задачи.
Задача и начальные данные
Прежде всего определим задачу: разработать ПО для управления источником питания по протоколу ModBus RTU с возможностью указания адреса устройства, параметров COM-порта, и управлением некоторым количеством параметров (запись и чтение заданных регистров).
Для отладки мне выдали какую-то железяку с поднятым на ней модбасом и возможностью хранить записанные ранее данные. Всё! Больше никакого функционала, полезного действия и смысловой нагрузки она не несёт. В прошлом это была некая плата “универсального интерфейса” – преобразователя различных протоколов обмена данных в привычный UART.
Начальные данные: перед этой задачей я уже делал подобное ПО для этой компании с одним лишь отличием – там управление осуществлялось целой сетью устройств с индивидуальным управлением каждым источником. Таким образом, я рассчитывал значительно упростить себе жизнь используя модули, созданные для решения прошлой задачи.
Поспешишь – людей насмешишь. Или лишний код напишешь…
Народная мудрость….. дополненная мною
Однако, в предыдущей задаче я допустил досадную для меня оплошность, суть которой в том, что я использовал лишь библиотеки для работы с последовательным портом, а реализацию протокола ModBus взял на себя. Почему я так сделал? *смех* В общем: “поспешишь – людей насмешишь!”, или свой код напишешь. Всё уже давно изобретено за нас. На помощь пришла встроенная библиотека, а именно класс QModBusRtuSerialMaster, наследуемый от QModBusDevice. Приступим!
Пишем код!
При реализации были использованы примеры программного кода из открытой документации Qt с ресурса doc.qt.io, которые были адаптированы для решения поставленной мне задачи.
Запускаем среду, создаем проект и приступаем к редактированию файла *.pro. Дополним параметр QT параметрами “serialbus serialport“. Должно получиться нечто такое:
QT += core gui serialbus serialport
Закрываем файл .pro и перейдем к форме главного окна программы QMainWindow. Начнем с заголовочного файла. Прототипы функций я пояснять сейчас не буду, а сделаю это позже, когда будем описывать реализацию класса.
// mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include #include #include class QModbusClient; namespace Ui < class MainWindow; >class MainWindow : public QMainWindow < Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); QModbusDataUnit readRequest(int, int) const; QModbusDataUnit writeRequest(int, int) const; private: Ui::MainWindow *ui; QModbusClient *modbusDevice; private slots: void onStateChanged(int); void readReady(); void prepareWrite(int, int); void prepareRead(int, int); void errorMessage(QString); void on_connectButton_clicked(); >; #endif // MAINWINDOW_H
Далее будем реализовывать интерфейс класса. Конструктор и деструктор класса.
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), modbusDevice(nullptr) // Предварительно инициализируем указатель «нулевым» указателем < ui->setupUi(this); /* * Создаем экземпляр класса QModbusRtuSerialMaster * Связываем сигнал errorOccurred с методом класса errorString(), * обрабатывающим ошибки. */ modbusDevice = new QModbusRtuSerialMaster(this); connect(modbusDevice, errorMessage(modbusDevice->errorString()); >); /* * Также соединяем сигнал изменения состояния объекта modbusDevice * с методом onStateChanged, где мы будем обрабатывать изменения состояния (подключение или отключение порта) */ if(modbusDevice) < connect(modbusDevice, MainWindow::onStateChanged); >> MainWindow::~MainWindow() < /* При завершении работы окна вызываем функцию отключения порта, * если указатель на объект modbusDevice существует. Если соединение * отсутствует, то функция не сделает ничего. * После чего удаляем экземпляр объекта. */ if(modbusDevice) modbusDevice->disconnectDevice(); delete modbusDevice; delete ui; >
Опишем используемые в коде выше методы: errorMessage(..) и onStateChanged(..). Метод errorMessage(…) выводит переданное в него сообщение в статус-баре окна программы и как отладочную информацию. onStateChanged(…) в зависимости от состояния порта (подключен или отключен) изменяет текст кнопки подключения connectButton и также выводит отладочное сообщение.
void MainWindow::errorMessage(QString msg) < /* STATUSBAR_MESSAGE_TIMEOUT — константа, определяющая время * «свечения» сообщения об ошибке в статус баре приложения */ ui->statusBar->showMessage(msg, STATUSBAR_MESSAGE_TIMEOUT); qInfo() void MainWindow::onStateChanged(int state) < if(state == QModbusDevice::UnconnectedState) < ui->connectButton->setText(«Подключить COM-порт»); qInfo() else if (state == QModbusDevice::ConnectedState) < ui->connectButton->setText(settings->getPortName() + QString(» «) + QString::number(settings->getBaudRate()) + » bps»); qInfo() getPortName() + QString(» «) + QString::number(settings->getBaudRate()) + » bps»; > else < return; // exit from function >>
Далее возникает закономерный вопрос: “Если у нас есть метод обработки изменения состояния подключения, то как мы создадим или уничтожим это подключение?”. Вот для этого давайте и займёмся реализацией следующего метода-обработчика нажатия кнопки connect.
void MainWindow::on_connectButton_clicked() < /* * Если экземпляр объекта по каким-либо причинам не существует, * то во избежание критических ошибок и проблем покидаем функцию */ if(!modbusDevice) return; /* Если порт не подключен, то настраиваем его и пытаемся установить соединение. * Если соединение установлено, то по нажатию кнопки мы разрываем соединение * Функция setConnectionParameter принимает 2 параметра: * 1) название устанавливаемого параметра * 2) значение параметра */ if(modbusDevice->state() != QModbusDevice::ConnectedState) < modbusDevice->setConnectionParameter(QModbusDevice::SerialPortNameParameter, settings->getPortName()); // Объект settings в моей программе хранит и возвращает заданные пользователем параметры COM-порта modbusDevice->setConnectionParameter(QModbusDevice::SerialDataBitsParameter, QSerialPort::Data8); modbusDevice->setConnectionParameter(QModbusDevice::SerialParityParameter, QSerialPort::NoParity); modbusDevice->setConnectionParameter(QModbusDevice::SerialBaudRateParameter, settings->getBaudRate()); modbusDevice->setConnectionParameter(QModbusDevice::SerialStopBitsParameter, QSerialPort::TwoStop); // COM_TIMEOUT — константа. Задает время таймаута в милисекундах. modbusDevice->setTimeout(COM_TIMEOUT); // Пробуем установить соединение и в случае ошибки выводим соответствующее сообщеие if(!modbusDevice->connectDevice()) < errorMessage(«Подключение неудалось: » + modbusDevice->errorString()); > > else < modbusDevice->disconnectDevice(); > >
Далее реализуем функционал чтения данных с устройства. Нам понадобиться 3 функции. Функция readRequest(…) формирует “единицу данных”, которая определяет тип запрашиваемого элемента согласно протоколу Modbus (holding register в моём случае), стартовый адрес и количество считываемых элементов. В моём случае это достаточно тривиальная функция, так что её можно было и упразднить, подставив возвращаемое выражение напрямую в функцию prepareRead().
Аналогичную ситуацию вы увидите позже при рассмотрении реализации функционала записи данных.
// Входные параметры: startAddress — адрес первого элемента, count — количество элементов (1 или более). QModbusDataUnit MainWindow::readRequest(int startAddress, int count) const < return QModbusDataUnit(QModbusDataUnit::HoldingRegisters, startAddress, count); >// Входные параметры: startAddress — адрес первого элемента, count — количество элементов (1 или более). void MainWindow::prepareRead(int startAddress, int count) < if(!modbusDevice) return; /* «Единица данных» передается в качестве параметра в функцию sendReadRequest(..) совместно с адресом устройства. * Если запрос произведен — удаляем ссылку на указатель ответа. * Если запрос ещё не закончен (в процессе), то связываем сигнал об окончании запроса * с функцией обработчика прочитанных данных */ if(auto *lastRequest = modbusDevice->sendReadRequest(readRequest(startAddress, count), settings->getAddress())) < if(!lastRequest->isFinished()) < connect(lastRequest, MainWindow::readReady); >else < delete lastRequest; >> else < errorMessage(tr(«Ошибка чтения: «) + modbusDevice->errorString()); > >
Третьей функцией, завершающей реализацию функционала чтения будет обработчик поступивших данных readReady(..)
void MainWindow::readReady() < auto reply = qobject_cast(sender()); if (!reply) return; if (reply->error() == QModbusDevice::NoError) < const QModbusDataUnit unit = reply->result(); QStringList resultsList; // Формируем список результатов в виде строк entry И складываем их в контейнер resultsList for (uint i = 0; i < unit.valueCount(); i++) < QString entry = tr(«Address: %1, Value: %2»).arg(unit.startAddress()) .arg(QString::number(unit.value(i)); results += entry; >> else if (reply->error() == QModbusDevice::ProtocolError) < // Выводим сообщение об ошибке протокола используя объект reply->errorString(); > else < // Выводим сообщение об остальных типах ошибки используя объект reply->errorString(); // В принципе, можно убрать столь подробное деление обработчика ошибок, оставив только лишь этот блок. > reply->deleteLater(); >
Реализация функционала записи требует меньшего количества функций, а именно две, хотя, повторюсь, в данной задаче, можно упразднить одну из функций, подставив возвращаемое выражение функции writeRequest(…) вместо её вызова.
// Входные параметры: startAddress — адрес первого элемента, count — количество элементов (1 или более). QModbusDataUnit MainWindow::writeRequest(int startAddress, int count) const < return QModbusDataUnit(QModbusDataUnit::HoldingRegisters, startAddress, count); >// Входные параметры: startAddress — адрес первого элемента, count — количество элементов (1 или более). void MainWindow::prepareWrite(int startAddress, int count) < if(!modbusDevice) return; QModbusDataUnit writeUnit = writeRequest(startAddress, count); for(uint i = 0; i < unit.valueCount(); ++i) < /* setValue(int arg1, quint16 arg2) требует двух параметров * 1) arg1 — номер ячейки для записи. Нумерация идёт с нуля, т.к. при записи этот «ноль» * будет отсчитываться от startAddress. * arg2 — значение элемента для записи. * value — массив данных, которые будут записаны в регистры. */ writeUnit.setValue(i, value[i]); >if(auto *lastRequest = modbusDevice->sendWriteRequest(writeUnit, settings->getAddress())) < if(!lastRequest->isFinished()) < connect(lastRequest, if(lastRequest->error() == QModbusDevice::ProtocolError) < // Обрабатываем сообщение ошибки проткола используя reply-errorString(); >else if(reply->error() == QModbusDevice::TimeoutError) < // В случае таймаута отправляем сообщение об ошибки в метод-обработчик errorString(); errorString(modbusDevice->errorString()); > else if (reply->error() != QModbusDevice::NoError) < // Обрабатываем сообщение других ошибок используя reply-errorString(); >else if(reply->error() == QModbusDevice::NoError) < // Здесь мы можем обработать факт успешной записи данных. >reply->deleteLater(); >); > else < reply->deleteLater(); > > else < errorMessage(«Ошибка записи: » + modbusDevice->errorString()); > >
Прошу обратить внимание, что формировать список параметров для записи можно не по штучно функцией setValue(..), а импортируя вектор значений функцией void QModbusDataUnit::setValues(const QVector https://zhitenev.ru/vcaimodeistvie-s-ustroistvami-cherez-modbus-rtu-in-qt/» target=»_blank»]zhitenev.ru[/mask_link]
Работа с Modbus-устройствами Wiren Board без контроллера
Большинство устройств Wiren Board могут работать без управления контроллером, но для их настройки потребуется подключиться к ним по протоколу Modbus. Для этого вы можете использовать компьютер с ОС Windows или Linux и преобразователь интерфейса USB-RS-485, например, WB-USB485 .
Для начала работы надо клеммы A, B и GND устройства подключить к входам адаптера, подать на устройство питание и настроить программное обеспечение на компьютере.
Подготовка к работе
Перед настройкой устройства вам нужно знать:
- modbus-адрес устройства,
- коды функций чтения и записи регистров,
- адреса регистров устройства.
Перечень общих для всех устройств Wiren Board регистров можно найти в таблице общих регистров. Полный список регистров для каждого устройства смотрите в документации к нему.
Программы для работы по протоколу Modbus
Интерфейс программы Rilheva Modbus Poll