Работа с Modbus RTU

MaksVV
Онлайн
Зарегистрирован: 06.08.2015

может в тех регистрах реально ноли?

VaDoSiQ
VaDoSiQ аватар
Offline
Зарегистрирован: 19.01.2017

Да нет, вот если выводить по одному значению, всё показывает. 

Да и вот, прошло несколько минут и тут второе значение проснулось. Мистика какая то.

MaksVV
Онлайн
Зарегистрирован: 06.08.2015

значит дело в слейве

VaDoSiQ
VaDoSiQ аватар
Offline
Зарегистрирован: 19.01.2017

Delete

VaDoSiQ
VaDoSiQ аватар
Offline
Зарегистрирован: 19.01.2017

Скетч, который Вы мне дали. Писали Вы? Или где то его нашли? Мне бы узнать о нём больше. Что и как работает. А то комментариев в коде, как кот наплакал.

VaDoSiQ
VaDoSiQ аватар
Offline
Зарегистрирован: 19.01.2017

nik182 пишет:

В ардуине мало памяти, поэтому в скобках прописываются не номера регитров, а количество, которое будет в обработке. У вас два регистра. Т.е.достаточно [2]. При этом 29 регистр будет иметь индекс массива [0], 1500 будет лежать в [1]. Во всех приведённых вами скечах я ни разу не видел вывода из ардуины в компьютер данных. Как вы собираетесь понять - получила ардуина что то или нет? И как отправлять дальше в комп?

 

Можете привести пример? Как получить значение регистра?

Вот код:

#include <ModbusRtu.h>

uint16_t au16data[2];
uint8_t u8state;

Modbus master(0,0,10);

modbus_t query;

unsigned long u32wait;

void setup() {
  Serial.begin(9600);
  master.begin(9600);
  pinMode(10, OUTPUT);
  digitalWrite(10, LOW);
  master.setTimeOut(2000);
  u32wait = millis() + 1000;
  u8state = 0; 
}

void loop() {
  int Frequency = au16data[2];
  switch(u8state) {
  case 0: 
    if (millis() > u32wait) u8state++;
    break;
  case 1: 
    query.u8id = 1;
    query.u8fct = 3;
    query.u16RegAdd = 29;
    query.u16CoilsNo = 2;
    query.au16reg = au16data;

    master.query(query);
    Serial.print("Local Reference: ");  Serial.println(Frequency);
    u8state++;
    break;
  case 2:
    master.poll();
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 1000;
    }
    break;
  }
}

В коде, который давал человек, выше. Значения выводились в терминал порта. А в этом коде не получается. Мне просто нужно считать 29 регистр и вывести в мониторе порта ардуины, всё.

 

MaksVV
Онлайн
Зарегистрирован: 06.08.2015

Мой код получается из программы FLprog при создании модбаса. Данная программа предназначена для графического программитрования ардуино. Я там создавал модбас. После вывода проекта FLprog создает обычный скетч на arduino IDE. Вот его то я и выкладывал. Вопросы по работе программы и скетчу лучше задавать у них н форуме. 

MaksVV
Онлайн
Зарегистрирован: 06.08.2015

насколько я понимаю библиотека #include <ModbusRtu.h> не поддерживает подключение модбас к софт сериал порту.

Надо что бы библиотека поддерживала подключение RS 485 на СОФТсериал порт. Чтобы железный порт использовать для монитора порта. Нельзя один и тот же порт использовать и для монитора порта и для модбаса. 

VaDoSiQ
VaDoSiQ аватар
Offline
Зарегистрирован: 19.01.2017

MaksVV пишет:

насколько я понимаю библиотека #include <ModbusRtu.h> не поддерживает подключение модбас к софт сериал порту.

Надо что бы библиотека поддерживала подключение RS 485 на СОФТсериал порт. Чтобы железный порт использовать для монитора порта. Нельзя один и тот же порт использовать и для монитора порта и для модбаса. 

Из библиотеки ModbusRtu.h

#include <inttypes.h>
#include "Arduino.h"
#include "Print.h"
#include <SoftwareSerial.h>

 

nik182
Offline
Зарегистрирован: 04.05.2015

Проблема в строке 13. В программе инициализируется сериал порт, потом этот порт отдается модбасу. Для обмена с компьютером надо использовать SoftwareSerial, так как SoftwareSerial на модбасе не подразумевает RS485. Т.е.  RS485 нужно повесить на ноги 1 и 2, а с компом соединяться с помощью переходника TTL-USB через любые ноги и SoftwareSerial. После 43 строки и до 44 можно вписать любые команды вывода и обработки, как в примере software_serial_simple_master.ino

.....
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 2000; 
        Serial.println(au16data[0]);//Or do something else!
    } 
.......

 

VaDoSiQ
VaDoSiQ аватар
Offline
Зарегистрирован: 19.01.2017

Ошибка компиляции:

sketch_apr10a:56: error: no matching function for call to 'Modbus::Modbus(int)'
 
C:\Users\Теплоэнергия\Desktop\sketch_apr10a\sketch_apr10a.ino:56:16: note: candidates are:
 
In file included from C:\Users\Теплоэнергия\Desktop\sketch_apr10a\sketch_apr10a.ino:42:0:
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:238:1: note: Modbus::Modbus(uint8_t, uint8_t, uint8_t)
 
 Modbus::Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin) {
 
 ^
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:238:1: note:   candidate expects 3 arguments, 1 provided
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:222:1: note: Modbus::Modbus(uint8_t, uint8_t)
 
 Modbus::Modbus(uint8_t u8id, uint8_t u8serno) {
 
 ^
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:222:1: note:   candidate expects 2 arguments, 1 provided
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:208:1: note: Modbus::Modbus()
 
 Modbus::Modbus() {
 
 ^
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:208:1: note:   candidate expects 0 arguments, 1 provided
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:145:7: note: constexpr Modbus::Modbus(const Modbus&)
 
 class Modbus {
 
       ^
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:145:7: note:   no known conversion for argument 1 from 'int' to 'const Modbus&'
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:145:7: note: constexpr Modbus::Modbus(Modbus&&)
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:145:7: note:   no known conversion for argument 1 from 'int' to 'Modbus&&'
 
C:\Users\Теплоэнергия\Desktop\sketch_apr10a\sketch_apr10a.ino: In function 'void setup()':
 
sketch_apr10a:69: error: no matching function for call to 'Modbus::begin(SoftwareSerial*, int)'
 
C:\Users\Теплоэнергия\Desktop\sketch_apr10a\sketch_apr10a.ino:69:33: note: candidates are:
 
In file included from C:\Users\Теплоэнергия\Desktop\sketch_apr10a\sketch_apr10a.ino:42:0:
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:254:6: note: void Modbus::begin(long int)
 
 void Modbus::begin(long u32speed) {
 
      ^
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:254:6: note:   candidate expects 1 argument, 2 provided
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:303:6: note: void Modbus::begin()
 
 void Modbus::begin() {
 
      ^
 
C:\Users\Теплоэнергия\Documents\Arduino\libraries\ModbusRtu/ModbusRtu.h:303:6: note:   candidate expects 0 arguments, 2 provided
 
exit status 1
no matching function for call to 'Modbus::Modbus(int)'
 
/**
 *  Modbus master example 2:
 *  The purpose of this example is to query an array of data
 *  from an external Modbus slave device.
 *  This example is similar to "simple_master", but this example
 *  allows you to use software serial instead of hardware serial
 *  in case that you want to use D1 & D2 for other purposes.
 *  The link media can be USB or RS232.
 
  The circuit:
 * software serial rx(D3) connect to tx pin of another device
 * software serial tx(D4) connect to rx pin of another device
 
 * In this example, we will use two important methods so that we can use
 * software serial.
 *
 * 1. Modbus::Modbus(uint8_t u8id)
 * This is a constructor for a Master/Slave through USB/RS232C via software serial
 * This constructor only specifies u8id (node address) and should be only
 * used if you want to use software serial instead of hardware serial.
 * This method is called if you create a ModBus object with only on parameter "u8id"
 * u8id is the node address of the arduino that will be programmed on,
 * 0 for master and 1..247 for slave
 * for example: Modbus master(0); 
 * If you use this constructor you have to begin ModBus object by
 * using "void Modbus::begin(SoftwareSerial *softPort, long u32speed)".
 * 
 * 2. void Modbus::begin(SoftwareSerial *sPort, long u32speed)
 * Initialize class object.
 * This is the method you have to use if you construct the ModBus object by using 
 * Modbus::Modbus(uint8_t u8id) in order to use software serial and to avoid problems.
 * You have to create a SoftwareSerial object on your own, as shown in the example.
 * sPort is a pointer to your SoftwareSerial object, u32speed is the baud rate, in 
 * standard increments (300..115200)
 created long time ago
 by smarmengol
 modified 29 July 2016
 by Helium6072
 This example code is in the public domain.
 */

#include <ModbusRtu.h>
#include <SoftwareSerial.h>

// data array for modbus network sharing
uint16_t au16data[16];
uint8_t u8state;

/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
Modbus master(0); // this is master and RS-232 or USB-FTDI via software serial

/**
 * This is an structe which contains a query to an slave device
 */
modbus_t telegram;

unsigned long u32wait;

SoftwareSerial mySerial(7, 8);//Create a SoftwareSerial object so that we can use software serial. Search "software serial" on Arduino.cc to find out more details.

void setup() {
  Serial.begin(9600);//use the hardware serial if you want to connect to your computer via usb cable, etc.
  master.begin( &mySerial, 9600 ); // begin the ModBus object. The first parameter is the address of your SoftwareSerial address. Do not forget the "&". 9600 means baud-rate at 9600
  master.setTimeOut( 2000 ); // if there is no answer in 2000 ms, roll over
  u32wait = millis() + 1000;
  u8state = 0; 
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    telegram.u8id = 1; // slave address
    telegram.u8fct = 3; // function code (this one is registers read)
    telegram.u16RegAdd = 29; // start address in slave
    telegram.u16CoilsNo = 2; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino

    master.query( telegram ); // send query (only once)
    u8state++;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 2000; 
        Serial.println(au16data[0]);//Or do something else!
    }
    break;
  }
}

 

 

nik182
Offline
Зарегистрирован: 04.05.2015

У меня этот скетч компилируется без ошибок:

Скетч использует 4592 байт (14%) памяти устройства. Всего доступно 30720 байт.
Глобальные переменные используют 452 байт (22%) динамической памяти, оставляя 1596 байт для локальных переменных. Максимум: 2048 байт.
 
Повторю. С этим скетчем нельзя использовать RS-485. Или вы уже без него обходитесь?
 
VaDoSiQ
VaDoSiQ аватар
Offline
Зарегистрирован: 19.01.2017

Спасибо. Всё заработало, даже без софтсериала.

Теперь еще один вопрос, как считать 2 регистра сразу?

Спасибо)

/**
 *  Modbus master example 1:
 *  The purpose of this example is to query an array of data
 *  from an external Modbus slave device. 
 *  The link media can be USB or RS232.
 *
 *  Recommended Modbus slave: 
 *  diagslave http://www.modbusdriver.com/diagslave.html
 *
 *  In a Linux box, run 
 *  "./diagslave /dev/ttyUSB0 -b 19200 -d 8 -s 1 -p none -m rtu -a 1"
 * 	This is:
 * 		serial port /dev/ttyUSB0 at 19200 baud 8N1
 *		RTU mode and address @1
 */

#include <ModbusRtu.h>

// data array for modbus network sharing
uint16_t au16data[16];
uint8_t u8state;

/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  u8serno : serial port (use 0 for Serial)
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
Modbus master(0,0,0); // this is master and RS-232 or USB-FTDI

/**
 * This is an structe which contains a query to an slave device
 */
modbus_t telegram;

unsigned long u32wait;

void setup() {
  master.begin( 19200 ); // baud-rate at 19200
  master.setTimeOut( 2000 ); // if there is no answer in 2000 ms, roll over
  u32wait = millis() + 1000;
  u8state = 0; 
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    telegram.u8id = 1; // slave address
    telegram.u8fct = 3; // function code (this one is registers read)
    telegram.u16RegAdd = 1; // start address in slave
    telegram.u16CoilsNo = 4; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino

    master.query( telegram ); // send query (only once)
    u8state++;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 100; 
    }
    break;
  }
}

 

MaksVV
Онлайн
Зарегистрирован: 06.08.2015

строка 55 у вас говорит о том что вы считываете 4 регистра начиная с адреса, указанного в 54 строке

nik182
Offline
Зарегистрирован: 04.05.2015

Строка 54 - с какого номера регистра стартовать  ответ. Строка 55 - сколько вподряд ячеек 16бит передать. 

Если за строкой 63 поставить вывод в сом порт или на дисплей то можно будет увидеть что получили. Или вставить какую либо обработку.

VaDoSiQ
VaDoSiQ аватар
Offline
Зарегистрирован: 19.01.2017

Допустим я считываю 29 регистр в нём 2 coils. И мне нужно считать 1000 регистр, с 1 coils.

Как тут это реализовать?

telegram.u16RegAdd = 29;
telegram.u16CoilsNo = 2;
telegram.au16reg = au16data; 

 

Baks
Baks аватар
Offline
Зарегистрирован: 11.01.2016

насколько я понимаю, за 1 раз можно считать не более 16 регистров.

и это у Вас код слейва, Вам мастер шлет 2 записаных регистра №4 и № 1000?

если не ошибаюсь протакол модбус такое не поддерживает......

возможно там 2 разные телеграммы.... и в каждой не более 16 регистров.  

и должно быть чтото в роде этого 

  telegram[0].u8id = Slaves; // slave address
  telegram[0].u8fct = 3; // function code (this one is registers read)
  telegram[0].u16RegAdd = 0; // start address in slave
  telegram[0].u16CoilsNo = 6; // number of elements (coils or registers) to read
  telegram[0].au16reg = au16data; // pointer to a memory array in the Arduino
  
    // telegram 1: write a single register
  telegram[1].u8id = Slaves; // slave address
  telegram[1].u8fct = 6; // function code (this one is write a single register)
  telegram[1].u16RegAdd = 1000; // start address in slave
  telegram[1].u16CoilsNo = 1; // number of elements (coils or registers) to read
  telegram[1].au16reg = au16data+6; // pointer to a memory array in the Arduino

 

Court
Offline
Зарегистрирован: 08.07.2017

nik182 пишет:
А считать можно с помощью библиотеки modbusrtu.

Где взять библиотеку ModbusRTU.h или эту modbusrtu .

Нужна для работы в режимах Master и Slave

VaDoSiQ
VaDoSiQ аватар
Offline
Зарегистрирован: 19.01.2017

Court пишет:

nik182 пишет:
А считать можно с помощью библиотеки modbusrtu.

Где взять библиотеку ModbusRTU.h или эту modbusrtu .

Нужна для работы в режимах Master и Slave

https://github.com/prashantrar/ModbusRtu/tree/master/firmware

Court
Offline
Зарегистрирован: 08.07.2017

спасибо, с какой страницы надо начинать поиск на github и как отделить клоны библиотек от авторского оригинала?  Например, если искать с главной страницы Github.com и потом включить фильтр  "arduino", то показывает две другие библиотеки, а Вашей ссылки среди них нет:  

https://github.com/search?l=Arduino&q=ModbusRtu&type=Repositories&utf8=%E2%9C%93

Court
Offline
Зарегистрирован: 08.07.2017
#include "ModbusRTU.h"
ModbusRTU::ModbusRTUSlave<16> slave; // ???????????????????

unsigned short encoder = 0;
unsigned short analogOut[3];
bool digitalOut[4];
bool digitalIn[6];
unsigned short analogIn[2];

void setup()
{
  slave.begin(115200);

  pinMode(A0, INPUT_PULLUP);
  pinMode(A1, INPUT_PULLUP);
  pinMode(A2, INPUT_PULLUP);
  pinMode(A3, INPUT_PULLUP);
  pinMode(A4, INPUT_PULLUP);
  pinMode(A5, INPUT_PULLUP);
  pinMode(A6, INPUT_PULLUP);
  pinMode(A7, INPUT_PULLUP);
  
  slave.update();
  
  pinMode(10, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(6, OUTPUT);
  
  pinMode(8, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(7, OUTPUT);
  
  slave.addCoil(&digitalOut[0],0x0000);
  slave.addCoil(&digitalOut[1],0x0001);
  slave.addCoil(&digitalOut[2],0x0002);
  slave.addCoil(&digitalOut[3],0x0003);

  slave.addDiscreteInput(&digitalIn[0], 0x1000);
  slave.addDiscreteInput(&digitalIn[1], 0x1001);
  slave.addDiscreteInput(&digitalIn[2], 0x1002);
  slave.addDiscreteInput(&digitalIn[3], 0x1003);
  slave.addDiscreteInput(&digitalIn[4], 0x1004);
  slave.addDiscreteInput(&digitalIn[5], 0x1005);

  slave.addInputRegister(&analogIn[0], 0x2000);
  slave.addInputRegister(&analogIn[1], 0x2001);

  slave.addHoldingRegister(&analogOut[0], 0x3000);
  slave.addHoldingRegister(&analogOut[1], 0x3001);
  slave.addHoldingRegister(&analogOut[2], 0x3002);
}

void loop() 
{
  digitalIn[0] = !digitalRead(A0);
  digitalIn[1] = !digitalRead(A1);
  digitalIn[2] = !digitalRead(A2);
  digitalIn[3] = !digitalRead(A3);
  digitalIn[4] = !digitalRead(A4);
  digitalIn[5] = !digitalRead(A5);
  
  analogIn[0] = analogRead(A7);
  analogIn[1] = analogRead(A6);
  
  slave.update();
  
  analogWrite(10, analogOut[0]);
  analogWrite(9, analogOut[1]);
  analogWrite(6, analogOut[2]);
  
  digitalWrite(7, digitalOut[0]);
  digitalWrite(11, digitalOut[1]);
  digitalWrite(5, digitalOut[2]);
  digitalWrite(8, digitalOut[3]);
}

Взял для пробы пример из аналогичной библиотеки  https://github.com/norgor/ModbusRTU

Как понять строку №2 - объявление класса, а угловые скобки <16> что означают?

Sadd
Offline
Зарегистрирован: 26.04.2016

Здравствуйте, дабы не плодить темы, подскажите, имеется счётчик и ПО которое его опрашивает через rs485 по протоколу modbas rtu, задача заключается в том чтобы, с помощью конвертера rs485 и ардуино брать данные с счётчика и отправлять их на компьютер через виртуальный сом порт. Спасибо.

valkir
Offline
Зарегистрирован: 18.10.2018

Не знаю будет ли полезна вам эта информация, но я тоже несколько дней мучился на uno вычитыванием регисторов с энергометра DDS238.

Использовал библиотеку ModbusRtu.h

Подключение платки RS485 было к основному COM порту ардуины (пины RX,TX), т.е. к тому же по которому идет подключение к ПК. Так вот. Все сделал правильно, modbus сниффером слушал, пакеты отлично уходили, и отлично приходили ответы. А этот приемный регистр telegram.au16reg = au16data  ну никак не обновлялся!

Короче, я везде в программе поставил конструкции Serial.Println  И, о Боже данные вдруг считались.

Методом тыка убирал их по одной. В результате осталась одна. Как только ее убираешь все опять отваливается.

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

Вот вам кусок кода с этой конструкцией. 

Функция GetMessage вызывается для передачи на slave и получения ответа

В комментах все ясно.

Мистика. Если у вас есть идеи, интересно было бы послушать.

// moduladdr - адрес модуля DDS238
// функция последовательно считывает вольты, текущую мощность, расход квт.ч и записывает их в глоб структуру.

void ModbusDDS238 (uint8_t moduladdr)
{
   
    
    telegram.u8id = 1; // адрес slave устройства (байт)
    telegram.u8fct = 3; // код функции (байт)
    telegram.u16RegAdd = 0;
    telegram.u16CoilsNo = 15; // считаем значения 15 регистров
    telegram.au16reg = au16data;
    GetMessage(); //первый раз значение по умолчанию 
    GetMessage(); //норм
    DDS238_TEnergy=(au16data[0]*65535+au16data[1])/100; //преобразуваем два 16бит слова в 32бит переменную и преобразуем в кВт/ч
    DDS238_Voltage=au16data[12]/10; //убираем десятые доли
    DDS238_ActPower=au16data[14];
    //sprintf (str, "\nP=%d Wt, V=%d v,E=%d kwth\n",DDS238_ActPower,DDS238_Voltage,DDS238_TEnergy);
    sprintf (str, "\nhttp://192.168.1.81/mqt.php?t=DDS238&s=1&p=%d&v=%d&e=%d\n",DDS238_ActPower,DDS238_Voltage,DDS238_TEnergy);
    Serial.println(str);
    return 0;
    }


void GetMessage()
{
    unsigned long u32wait2=millis()+200;
     master.query( telegram ); // send query (only once)
     master.poll();
     while (master.getState() != COM_IDLE)
        {
          
          master.poll(); // check incoming messages
          // Вот эта штука очень важная. Эти символы вносят некую задержку, чтобы функция считывания прочухалась
          // про это нигде не написано, но если их подобрать неправильно почему данные нифига не считываются
          Serial.print("...");
          if (millis()>u32wait2)
             {
               Serial.println("modbus error");
               return 1;
             }
        }
  
}

 

 

DLS74
Offline
Зарегистрирован: 01.06.2019

Добрый день!

"Спасибо. Всё заработало, даже без софтсериала."

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

 

С уважением Дмитрий. Оренбург.