Modbus на практике

sadman41
Offline
Зарегистрирован: 19.10.2016

Знакомые циферки-то и активити туда-сюда все равно увидеть должен.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

kost82 пишет:

brokly пишет:

kost82 пишет:

Устройство отдает данные при подключении и отключении, причем при подключении стабильно одно и то же приходит. Я думаю, что с ловлей ответов проблем нет. Проблема с отправкой запроса ИМХО.

Что значит "отдает" ?

В мониторе порта при подаче питания на устройство я вижу цифры 02550 каждый раз

Это ничего не значит. Вполне вероятно, что процессор устройства как то дергает выходные ноги при настройке.

kost82
Offline
Зарегистрирован: 30.11.2015

brokly пишет:

Скажите пожалуйста какой именно модбас вы пытаетесь реализовать ? ModBus RTU ?

Да, ModBus RTU

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Зачем ? Судя по описанию у вас вообще не ModBus.

kost82
Offline
Зарегистрирован: 30.11.2015

В мануале написано так:

Default settings for the M-7000 DIO modules are as follows:
。 Protocol: Modbus RTU
。 Module Address: 01
。 DIO Type: Type 40
。 Baud Rate: 9600 bps
 
Почему Вы думаете, что это не Modbus RTU?
 
P.S. На 9600, кстати, тоже пробовал - без толку
kost82
Offline
Зарегистрирован: 30.11.2015

При отключении питания получаю что-то типа такой строки:
002522401280160003200000000000014000040300002240000001287000128166413617724222249002041872281459631220813000000

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

kost82 пишет:

 

 

Потому что вот это все что угодно, но не модбас.

kost82
Offline
Зарегистрирован: 30.11.2015

Хорошо, в мануале описана стандартная Modbus-команда Read Coils.

Я опять подключился к компу, и отпарвил команду на изменение скорости подключения:

01 46 06 00 06 00 00 00 01 00 00 FC B3

В ответ получил, что все ОК (01 46 06 01 00 00 00 00 00 0A BF). Теперь 100% скорость установлена 9600 и протокол Modbus. После перезагрузки оно отвечало только на скорости 9600.

Дальше отправил команду чтения:

01 01 00 00 00 01 FD CA

И получил на нее ответ:

01 01 01 00 51 88

После этого изменил скетч вот так:

#include <SoftwareSerial.h>

byte SendData[8];
unsigned long lastMillis;

#define DE_RE_PIN 7       // The pin controlling Recieve Enable and Driver Enable on the RS485 adapter
#define RX_PIN 5 // to R0 on RS485 adapter
#define TX_PIN 4 // to DI on RS485 adapter

SoftwareSerial modbusSerial(RX_PIN, TX_PIN);

void setup() {
  pinMode(DE_RE_PIN, OUTPUT);
  Serial.begin(9600);
  modbusSerial.begin(9600);
  lastMillis = millis();

   // 3.1 01 (0x01) Read Coils
  SendData[0] = 0x01; //Address
  SendData[1] = 0x01; //Function code
  SendData[2] = 0x00; //Starting channel numbers
  SendData[3] = 0x00; //Starting channel numbers
  SendData[4] = 0x00; //Output channel number
  SendData[5] = 0x01; //Output channel number
  SendData[6] = 0xFD; //checksum
  SendData[7] = 0xCA; //checksum

}

void loop() {
  if (millis() - lastMillis >= 5000) {
    digitalWrite(DE_RE_PIN, HIGH);

    for (byte i = 0; i < 8; i++) {
      modbusSerial.write(SendData[i]);
      delay(10);
    }

    digitalWrite(DE_RE_PIN, LOW);

    Serial.println("-------");

    lastMillis = millis();
  }

  modbusSerial.listen();
  while (modbusSerial.available()) {
    Serial.print(modbusSerial.read());
  }
}

И в ответ опять тишина. Нолики в мониторе. Хрень какая-то.

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

Писал уже. Если это модбас, то в цикле отправки делей ставить нельзя. У тебя делей 10 мс. При такой задержке между символами модбас не работает. Неужели трудно прочитать стандарт? Там написано, что сообщение воспринимается как новое, если тишина в линии больше чем 3.5 длительности посылки 14 бит на скорости передачи. На скорости 9600 14 бит будут передаваться около 1.5 мс. Т.е. любая пауза в передаче больше чем 5.5 мс будет восприниматься как начало новой передачи. Т.е. задержка 10 мс должна стоять после передачи последнего символа. 36 строку надо перенести на 38 .

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Не хочется повторять в десятый раз. Вот по ссылке http://arduino.ru/forum/programmirovanie/ne-mogu-razobratsya-s-modbus-rtu-i-485-interfeisom?page=1#comment-571366 , возьмите процедуру чтения-записи и колдуйте.

kost82
Offline
Зарегистрирован: 30.11.2015

СПАСИБО ВСЕМ!
Наконец-то получилось отправлять правильные запросы и получать на них ответы от устройства. Действительно delay() там не нужны, вообще нигде. Без них заработали и стандартные Modbus запросы, типа Read Coils и нестандартные, например запрос версии прошивки.

Единственное - иногда в начале ответа добавляется лишний ноль, или наоборот убавляется. Но, как мне кажется, это из-за Serial.println(). Его я оставил для удобства просмотра результата в мониторе порта, иначе получается длинная строка, читать которую не очень удобно.

Вот итоговый скетч:

#include <SoftwareSerial.h>

byte SendData[8];
unsigned long lastMillis;

#define DE_RE_PIN 7       // The pin controlling Recieve Enable and Driver Enable on the RS485 adapter
#define RX_PIN 5 // to R0 on RS485 adapter
#define TX_PIN 4 // to DI on RS485 adapter

SoftwareSerial modbusSerial(RX_PIN, TX_PIN);

void setup() {
  pinMode(DE_RE_PIN, OUTPUT);
  Serial.begin(9600);
  modbusSerial.begin(9600);
  lastMillis = millis();

  //3.7.5 Sub-function 32 (0x20) Read firmware version
  SendData[0] = 0x01; //Address
  SendData[1] = 0x46; //Function code
  SendData[2] = 0x20; //Sub-function code
  SendData[3] = 0x13; //checksum
  SendData[4] = 0xB8; //checksum

   // 3.1 01 (0x01) Read Coils
  /*SendData[0] = 0x01; //Address
  SendData[1] = 0x01; //Function code
  SendData[2] = 0x00; //Starting channel numbers
  SendData[3] = 0x00; //Starting channel numbers
  SendData[4] = 0x00; //Output channel number
  SendData[5] = 0x01; //Output channel number
  SendData[6] = 0xFD; //checksum
  SendData[7] = 0xCA; //checksum*/

}

void loop() {
  if (millis() - lastMillis >= 5000) {
    digitalWrite(DE_RE_PIN, HIGH);

    for (byte i = 0; i < 8; i++) {
      modbusSerial.write(SendData[i]);
    }

    digitalWrite(DE_RE_PIN, LOW);

    Serial.println();

    lastMillis = millis();
  }

  modbusSerial.listen();
  while (modbusSerial.available()) {
    Serial.print(modbusSerial.read());
  }
}

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

на всякий скажу, раз уж не нужен delay между байтами. можно и так в шину говорить,без цикла for: 

modbusSerial.write(SendData);

 

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

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

kost82
Offline
Зарегистрирован: 30.11.2015

Вообще delay() у меня появился из этой темы, в частности из этого поста. Там у ТС задержка в цикле по 2 мс (в предыдущих постах была 1 мс). И задержка после окончания передачи аж 500 мс. При этом все работало, но у него действительно мега2560 и хардовый сериал.

Со всеми этими задержками я тоже вдоволь наэксперементировался, и 1 мс в цикле ставил и 5 и 10. Видимо 10 осталось после какого-то из эксперимента (забыл обратно вернуть).

В моем случае (с софтовым Serial), пауза в 5 мс после передачи (перед переключением направления) уже приводит к выводу непонятного мусора в порт. Видимо с софтовым сериал все проще, там задержки появляются сами по себе.

kost82
Offline
Зарегистрирован: 30.11.2015

MaksVV пишет:

на всякий скажу, раз уж не нужен delay между байтами. можно и так в шину говорить,без цикла for: 

modbusSerial.write(SendData);

Так у меня компилятор ругается no matching function for call to 'write(byte [8])', видимо метод не переопределен для работы с типом byte.

Цикл меня вполне устраивает. Имхо, он более наглядный.

sadman41
Offline
Зарегистрирован: 19.10.2016

Макс забыл второй аргумент для write() - sizeof(array).

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

kost82 пишет:

СПАСИБО ВСЕМ!

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

1. проверкой опустошения буфера

2. расчетом времени передачи данного количества байт на данной скорости

Первый вариант точнее. Вы этого не делаете. Я вам дал ссылку на готовую процедуру отправки. Странно, что вы даже не поинтересовались.

Причем важно именно вовремя переключить направление передачи и принять ответ в буфер. Кода вы будете забирать данные из буфера уже не важно. Так вот это 500 миллисекунд, это время в течении которого данные считываются в буфер порта. Тем более, что буфер, как правило меньше пакета. 

В софтовом сериале, при отправке нет буферизации, возврат из .write происходит по окончании передачи данных. Но это в вашей реализации, есть софт сериалы, где это не так.

MaksVV
Offline
Зарегистрирован: 06.08.2015

sadman41 пишет:
Макс забыл второй аргумент для write() - sizeof(array).

точно, чёто впарился. Так можно только char [] отправлять