Инициализация слейва в сети modbus RTU

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

Помогите хотя бы составить алгоритм действий как можно проверять мастером слейв на предмет того, в сети он или нет. Мое видение такое.
Слейв периодически записывает в какой-нибудь регистр модбаса, то «0», то «1». Мастер периодически считывает этот регистр например сериями по 4 раза. В одной серии каждое считывание записывается в свою переменную. По окончании серии (4—ое считывание) переменные сравниваются на предмет равенства между собой, и если они равны друг другу (все ноли либо все единицы) то слейв не отзывается. Если разные - он дергает регистром, значит в сети.
Но мне кажется это громоздко, так сказать будет поговнокодски. Опытные, подскажите как проще сделать?

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

Достаточно послать любой запрос. Если случился любой error - не в сети. Если ответ пришёл - в сети. На любой запрос библиотека возвращает статус. Его надо проверять. Этого достаточно.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Каждый слейв с точки зрения МК мастера должен быть объектом, который описан как класс или как структура. Скорее у мастера даже есть указатель на обьект Cl_slave * Slave1, Slave2;  И вот в МК мастера идет команда  Slave1->Ready();  И если возвращает 0, то слев тю-тю . А вот если 1 то он в сети.  И разумеется в методе byte Cl_slave::Ready() или идет прямой запрос по сети и ждет ответ. Разумеется это тормозит процессор. Или скорее всего раз в минуту мастер посылает общий запрос по сети "Кто живой". И заносит в объекты Slave1, Slave2 их ответы.  Это ускоряет работу МК вцелом.

ПС: Что тут придумавать . Вот посмотрите работу Serial.available();

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

спасибо, ваш ответ направил меня на поиск туда, с помощью чего я создаю модбас - программа FLprog. Оказывается автор проги всё уже сделал, для этого есть специальная функция

 

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

вот что получилось. работает 


////////////// Пример Master-а считывающего Coils Register 01, 
////////////// и мониторящего slave ID-1 на предмет пропадания в сети

// ниже для модбаса
#include <SoftwareSerial.h>
SoftwareSerial Serial100(7, 8);   // модбас (RS 485) висит на софтсериале. 7-RX;   8-TX

bool _modbusMasterDataTable_0_reg_1[1];
int _modbusMasterAddressTable_0_reg_1[1] = {0}; // адреса считываемого регистра, в данном случае "0" Coils
byte _modbusMasterBufferSize = 0;
byte _modbusMasterState = 1;
long _modbusMasterSendTime;
byte _modbusMasterLastRec = 0;
long _modbusMasterStartT35;
byte _modbusMasterBuffer[64];
byte _modbusMasterCurrentReg = 0;
byte _modbusMasterCurrentVariable = 0;
struct _modbusMasterTelegramm {
  byte slaveId;        
  byte function;        
  int startAddres;   
  int numbeRegs;   
  int valueIndex;
};
_modbusMasterTelegramm _modbusTelegramm;
long _startTimeMasterRegs[1];
long _updateTimeMasterRegsArray[] = {1000};  // интервал обмена данными со слейвом - 1 секунда
const unsigned char _modbusMaster_fctsupported[] = {1, 5};
byte _modbuseLastError1_ErrCode = 0;
//////ниже наши переменные 

bool Register1_Coils = 0; // сюда пишется регистр 01 coils модбаса  

long prevMillis_Serial = 0;    // храним время последнего вывода в сериал порт

long prevMillis_Errors = 0;    // храним время последнего считываения ошибок модбаса

int errorsSlave1 =0; // количество ошибок "слейв не отвечает"
int Slave1good = 0;  // количество измерений, при которых отсутствуют ошибки "слейв не отвечает"

bool StatusSlave = 0; // статус активности слейва

void setup()
{
Serial.begin (9600);
Serial100.begin(9600); // софт сериал для модбаса( RS-485)
pinMode(4, OUTPUT);    // пин DE-RE  RS-485 к 4 пину дуни, нельзя менять
digitalWrite(4, LOW);  
pinMode(13, OUTPUT);   // индикация втроенным светодиодом, когда слейв не отвечает
digitalWrite(13, LOW);

// цикл фор нужен для модбаса
for(int i=0; i<1; i++) {_startTimeMasterRegs[i] =  millis();}
}

void loop()
{

/////// Проверка и подсчет ошибок  "слейв не отвечает" 1 раз в секунду
unsigned long curMillis_Errors = millis();

if(curMillis_Errors - prevMillis_Errors > 1000 ) { 
// в описании ошибок FLpog неправильно указана ошибка "слейв не отвечает" (там 244, на самом деле ошибка 254)
 if (_modbuseLastError1_ErrCode==254) errorsSlave1++; // прибавляем счетчик ошибок при их наличии
 if (_modbuseLastError1_ErrCode==0) Slave1good++; // прибавляем счетчик отсутствия ошибок при их отсутствии
prevMillis_Errors = curMillis_Errors;}  

///////Вывод данных в сериал раз в полсекунды

unsigned long curMillis_Serial = millis();

if(curMillis_Serial - prevMillis_Serial > 500 ) { 
Serial.print ("Error ID01: "); Serial.print (_modbuseLastError1_ErrCode); 
Serial.print ("   Coils_Reg01: "); Serial.print (Register1_Coils); 
Serial.print (" ErrorsSlave1: "); Serial.print (errorsSlave1); 
Serial.print (" Slave1good: "); Serial.print (Slave1good); 
Serial.print (" StatusSlave: "); Serial.println (StatusSlave); 
prevMillis_Serial = curMillis_Serial;}  


////////диапазон счетчиков ошибок от 0 до 10
if (Slave1good>10) {Slave1good =10; errorsSlave1 = 0;}
if (errorsSlave1>10) {errorsSlave1 =10; Slave1good = 0;}

////////если 10 ошибок набралось, значит слейв молчит,  статус в ноль
if (errorsSlave1>=10) StatusSlave = 0;

////////если 10 секунд без ошибок - слейв активен, статус 1
if (Slave1good>=10) StatusSlave = 1;

/////// зажигаем сигнальную лампу, если слейв упал. 
digitalWrite (13,!StatusSlave);


/////// пишем регистр модбаса в переменную
Register1_Coils = (_modbusMasterDataTable_0_reg_1[0]);


// всё что ниже, требуха для модбаса

switch ( _modbusMasterState ) {
    case 1:
      _nextModbusMasterQuery();
      break;
    case 2:
      pollModbusMaster();
      break;
  }
}
bool _isTimer(unsigned long startTime, unsigned long period )
  {
  unsigned long currentTime;
currentTime = millis();
if (currentTime>= startTime) {return (currentTime>=(startTime + period));} else {return (currentTime >=(4294967295-startTime+period));}
  }
int modbusCalcCRC(byte length, byte bufferArray[])
{
  unsigned int temp, temp2, flag;
  temp = 0xFFFF;
  for (unsigned char i = 0; i < length; i++) {
    temp = temp ^ bufferArray[i];
    for (unsigned char j = 1; j <= 8; j++) {
      flag = temp & 0x0001;
      temp >>= 1;
      if (flag)   temp ^= 0xA001;
    }
  }
  temp2 = temp >> 8;
  temp = (temp << 8) | temp2;
  temp &= 0xFFFF;
  return temp;
}
void _nextModbusMasterQuery()
{
_selectNewModbusMasterCurrentReg(_modbusMasterCurrentReg, _modbusMasterCurrentVariable);
if (_modbusMasterCurrentReg == 0)  return;
_createMasterTelegramm();
_modbusMasterSendQuery();
}
void _selectNewModbusMasterCurrentReg(byte oldReg, byte oldVar)
{
bool isNeeded = 1;
if (oldReg == 0) {_selectNewModbusMasterCurrentReg(1, 0); return;}
if (!(_isTimer  ((_startTimeMasterRegs[oldReg - 1]),(_updateTimeMasterRegsArray[oldReg -1])))) {isNeeded = 0;}
if( ! isNeeded ) {if(oldReg < 1) {_selectNewModbusMasterCurrentReg(oldReg+1, 0); return;} else {_modbusMasterCurrentReg = 0; _modbusMasterCurrentVariable = 0; return;}}
if (oldVar == 0) {_modbusMasterCurrentReg = oldReg; _modbusMasterCurrentVariable = 1; return;}
byte temp;
switch (oldReg) {
case 1:
temp = 1;
 break;
 }
if (oldVar < temp) {_modbusMasterCurrentReg = oldReg; _modbusMasterCurrentVariable = oldVar +1; return;}
_startTimeMasterRegs[oldReg -1] = millis();
if(oldReg < 1) { _selectNewModbusMasterCurrentReg(oldReg+1, 0); return;} 
_modbusMasterCurrentReg = 0; _modbusMasterCurrentVariable = 0; return;
}
void _createMasterTelegramm()
{
switch (_modbusMasterCurrentReg) {
case 1:
_modbusTelegramm.slaveId = 1;
switch (_modbusMasterCurrentVariable) {
case 1:
_modbusTelegramm.function = 1;
_modbusTelegramm.startAddres = 0;
_modbusTelegramm.numbeRegs = 1;
_modbusTelegramm.valueIndex = 0;
break;
}
break;
}
}
void _modbusMasterSendQuery()
{
byte currentIndex = _modbusTelegramm.valueIndex;
  _modbusMasterBuffer[0]  = _modbusTelegramm.slaveId;
  _modbusMasterBuffer[1] = _modbusTelegramm.function;
  _modbusMasterBuffer[2] = highByte(_modbusTelegramm.startAddres );
  _modbusMasterBuffer[3] = lowByte( _modbusTelegramm.startAddres );
  switch ( _modbusTelegramm.function ) {
case 1:
 _modbusMasterBuffer[4] = highByte(_modbusTelegramm.numbeRegs );
      _modbusMasterBuffer[5] = lowByte( _modbusTelegramm.numbeRegs );
      _modbusMasterBufferSize = 6;
      break;
}
  _modbusMasterSendTxBuffer();
  _modbusMasterState = 2;
}
void _modbusMasterSendTxBuffer()
{
 byte i = 0;
int crc = modbusCalcCRC( _modbusMasterBufferSize, _modbusMasterBuffer );
  _modbusMasterBuffer[ _modbusMasterBufferSize ] = crc >> 8;
_modbusMasterBufferSize++;
 _modbusMasterBuffer[ _modbusMasterBufferSize ] = crc & 0x00ff;
 _modbusMasterBufferSize++;
digitalWrite(4, 1 );
delay(5);
Serial100.write( _modbusMasterBuffer, _modbusMasterBufferSize );
digitalWrite(4, 0 );
Serial100.flush();
  _modbusMasterBufferSize = 0;
  _modbusMasterSendTime = millis();
}
void pollModbusMaster()
{
if (_modbusTelegramm.slaveId == 0) {   _modbusMasterState = 1;   return;}
  if (_isTimer(_modbusMasterSendTime, 1000)) {
    _modbusMasterState = 1;
switch ( _modbusTelegramm.slaveId ) {case  1:
_modbuseLastError1_ErrCode = 254;
break;
}
    return;
  }
  byte avalibleBytes = Serial100.available();
  if (avalibleBytes == 0) return;
  if (avalibleBytes != _modbusMasterLastRec) {
    _modbusMasterLastRec = avalibleBytes;
    _modbusMasterStartT35 = millis();
    return;
  }
  if (!(_isTimer(_modbusMasterStartT35, 5 ))) return;
  _modbusMasterLastRec = 0;
  byte readingBytes = _modbusMasterGetRxBuffer();
  if (readingBytes < 5) {
    _modbusMasterState = 1;
    return ;
  }
byte exeption = validateAnswer();
  if (exeption != 0) {
switch ( _modbusTelegramm.slaveId ) {case  1:
_modbuseLastError1_ErrCode = exeption;
break;
}
 _modbusMasterState = 1;
    return;
  }
_modbuseLastError1_ErrCode = 0;
 switch ( _modbusMasterBuffer[1] ) {
case 1:
get_FC1(0);
break;
}
  _modbusMasterState = 1;
  return;
}
byte _modbusMasterGetRxBuffer()
{
boolean bBuffOverflow = false;digitalWrite(4, LOW );
 _modbusMasterBufferSize = 0;
  while (Serial100.available() ) {
    _modbusMasterBuffer[ _modbusMasterBufferSize ] = Serial100.read();
    _modbusMasterBufferSize ++;
    if (_modbusMasterBufferSize >= 64) bBuffOverflow = true;
  }
  if (bBuffOverflow) {return -3;}
  return _modbusMasterBufferSize;
}
byte validateAnswer()
{
uint16_t u16MsgCRC =    ((_modbusMasterBuffer[_modbusMasterBufferSize - 2] << 8) | _modbusMasterBuffer[_modbusMasterBufferSize - 1]);
  if ( modbusCalcCRC( _modbusMasterBufferSize - 2,_modbusMasterBuffer ) != u16MsgCRC ) { return 255; }
  if ((_modbusMasterBuffer[1] & 0x80) != 0) {return _modbusMasterBuffer[2] ;}
  boolean isSupported = false;
  for (byte i = 0; i < sizeof( _modbusMaster_fctsupported ); i++) {
    if (_modbusMaster_fctsupported[i] == _modbusMasterBuffer[1]) {
      isSupported = 1;

      break;
    }
  }
  if (!isSupported) {return 1;}
  return 0;
}
void get_FC1(byte table)
{
 byte currentByte = 3;
  byte currentBit = 0;
bool value;
  byte i;
  int currentIndex = _modbusTelegramm.valueIndex;
  for (i = 0; i < _modbusTelegramm.numbeRegs; i++) {
 value= bitRead(_modbusMasterBuffer[currentByte], currentBit);
switch ( _modbusMasterCurrentReg ) {
case 1 :
if(table == 0) {_modbusMasterDataTable_0_reg_1[currentIndex] =value;}
break;
}
currentIndex ++;
    currentBit ++;
    if (currentBit > 7) {
      currentBit = 0;
      currentByte ++;
    }
  }
  _modbusMasterState = 1;
}

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

И что это за письменное народное творчество- называемое ФИГ-ВАМ. И куда эту простынь засунуть.

Я размечтался что будет так. А получилось как в сказке.

#include "Cl_wire.h"
Cl_wire Wire;
#include "Cl_Slave.h"
Cl_Slave Slave_1(&Wire, 0x01);//0x01 адрес 1 внеш устройства
Cl_Slave Slave_2(&Wire, 0x02);//0x02 адрес 2 внеш устройства
void setup() {
  Serial.begin(9600);
  Wire.setup();
  Slave_1.setup();
  Slave_2.setup();
}

void loop() {
  Slave_1.loop();
  Slave_2.loop();
  if (Slave_1.available())Serial.println(Slave_1.buffer);
  if (Slave_2.available())Serial.println(Slave_2.buffer);
}

 

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

Как смог так и сделал. Опыта мало. Надо было с детства начинать программировать. А простыню можно во вкладку отдельную сложить. ВигВа... Модбас называется.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

MaksVV пишет:
Как смог так и сделал. Опыта мало. Надо было с детства начинать программировать. А простыню можно во вкладку отдельную сложить. ВигВа... Модбас называется.

Так это не вкладка будет, а библиотека. Вот я поэтому вам подсказываю куда двигать. Вот посмотрите вариант Пакет здесь https://yadi.sk/d/WaW7iUgj3GPUvp

Головной файл выложу если интересно кому

/*Wire_74HC595.ino 
*/
const int Latch_pin = 3;
const int CLK_pin  = 4;
const int Data_pin = 2;
const int max_device = 4;
bool stat = 0;
#include "Out_74HC595.h"
Out_74HC595 Out(Latch_pin, CLK_pin, Data_pin, max_device);
#include "Cl_led.h"
Cl_led led[] = {
  Cl_led(&Out, 0, 1, 0),  // шина-Out,1 горит,1-я 74HC595,D0
  Cl_led(&Out, 0, 1, 1),  // шина-Out,1 горит,1-я 74HC595,D1
  Cl_led(&Out, 0, 1, 2),  // шина-Out,1 горит,1-я 74HC595,D2
  Cl_led(&Out, 0, 1, 3)   // шина-Out,1 горит,1-я 74HC595,D3
};
void setup() {
  Out.setup();
  for (byte i = 0; i < 4; i++)  led[i].setup();
  led[0].blink(100);
  led[1].blink(150);
  led[2].blink(500);
  led[3].blink(1000);
}

void loop() {
  for (byte i = 0; i < 4; i++)  led[i].loop();
}