Modbus RTU

alexval2007
Offline
Зарегистрирован: 10.11.2012

Доброго времени суток товарищи возникла проблема с библиотекой SimpleModbusMaster возникла у меня нужда ее вытащить в скетч для дальнейшей работы с ней нужно ее несколько переделать чтобы modbus запросы шли не в цикле а однократно пришла команда ну пусть будет с кнопки дало 1 запрос получило ответ все тишина ждем следующего. Вот сей код но чтото проблемы при компилировании я так понял ему ненравится обявление структуры хотя странно то что работало в библиотеке нехочет работать в скетче. Выкладываю скетч и библиотеку отдельно. Может кто поможет разобратся я уже низнаю с какой стороны подойти перепробовал всё.

http://alexval2007.ucoz.ru/Smart_home/SimpleModbusMasterV2rev2.rar

http://alexval2007.ucoz.ru/Smart_home/SimpleModbusMaster_v07.ino

//#include <SimpleModbusMaster.h>
#include "Arduino.h"
#include "HardwareSerial.h"
// state machine states
#define IDLE                   1
#define WAITING_FOR_REPLY      2
#define WAITING_FOR_TURNAROUND 3
#define BUFFER_SIZE           64   // размер буфера
//////////////////// Макроопределения портов и настройки программы  ///////////////////
#define baud_1                  9600 // скоростьобмена по последовательному интерфейсу. (UART)
#define timeout_1             1000 // Длительность ожидание ответа (таймаут modbus)
#define polling_1             200  // скорость опроса по modbus
#define retry_count_1         10   // количесво запросов modbus до ошибки и останова обмена 
#define TxEnablePin_1         2    // Tx/Rx пин RS485
#define TOTAL_NO_OF_REGISTERS 4

#define COIL_OFF 0x0000              // Function 5 OFF request is 0x0000
#define COIL_ON 0xFF00               // Function 5 ON request is 0xFF00
#define READ_COIL_STATUS 1           // Reads the ON/OFF status of discrete outputs (0X references, coils) in the slave.
#define READ_INPUT_STATUS 2          // Reads the ON/OFF status of discrete inputs (1X references) in the slave.
#define READ_HOLDING_REGISTERS 3     // Reads the binary contents of holding registers (4X references) in the slave.
#define READ_INPUT_REGISTERS 4       // Reads the binary contents of input registers (3X references) in the slave. Not writable.
#define FORCE_SINGLE_COIL 5          // Forces a single coil (0X reference) to either ON (0xFF00) or OFF (0x0000).
#define PRESET_SINGLE_REGISTER 6     // Presets a value into a single holding register (4X reference).
#define FORCE_MULTIPLE_COILS 15      // Forces each coil (0X reference) in a sequence of coils to either ON or OFF.
#define PRESET_MULTIPLE_REGISTERS 16 // Presets values into a sequence of holding registers (4X references).

unsigned char state;
unsigned char retry_count;
unsigned char TxEnablePin;
unsigned char buffer;
long timeout;                 // интервал таймаута(timeout interval)
long polling;                 // циклический интервал задержки (turnaround delay interval)
unsigned int T1_5;            // inter character time out in microseconds
unsigned int frameDelay;      // frame time out in microseconds
long delayStart;              // init variable for turnaround and timeout delay
unsigned int total_no_of_packets;
//******************************************************************************
typedef struct{
  // Специальная пакетная информация
  unsigned char id;
  unsigned char function;
  unsigned int  address;
  unsigned int  data;
  unsigned int  local_start_address;
  // счетчики информации о modbus
  unsigned int  requests;
  unsigned int  successful_requests;
  unsigned int  failed_requests;
  unsigned int  exception_errors;
  unsigned int  retries;
  //******************************
  unsigned char connection; // состояние соединения пакета
}Packet;

// Объявим функции
void modbus_update();
void modbus_construct(Packet* _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address);
void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, unsigned char _retry_count, unsigned char _TxEnablePin, Packet* _packets, unsigned int _total_no_of_packets, unsigned int* _register_array);


Packet* packetArray;          // начальный адрес пакета
Packet* packet;               // текущий пакет
unsigned int* register_array; // указатель на массив регистров в master
HardwareSerial* ModbusPort;

// Масив пакетов modbus
enum
{
  PACKET1,
  PACKET2,
  PACKET3,
  PACKET4,
  TOTAL_NO_OF_PACKETS // эту строку неменять
};

unsigned char frame[BUFFER_SIZE];
Packet packets[TOTAL_NO_OF_PACKETS];     // Масив пакетов модбус
unsigned int regs[TOTAL_NO_OF_REGISTERS];// Массив хранения содержимого принятых и передающихся регистров
//********************************************************************************************************
// Объявим функции
void idle();
void constructPacket();
unsigned char construct_F15();
unsigned char construct_F16();
void waiting_for_reply();
void processReply();
void waiting_for_turnaround();
void process_F1_F2();
void process_F3_F4();
void process_F5_F6_F15_F16();
void processError();
void processSuccess();
unsigned int calculateCRC(unsigned char bufferSize);
void sendPacket(unsigned char bufferSize);
//********************************************************************************************************
// Создадим пакет модбус

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!тут!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

void modbus_construct(Packet* _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address){}
{
  _packet->id = _id;
  _packet->function = _function;
  _packet->address = _address;
  _packet->data = _data;
  _packet->local_start_address = _local_start_address;
  _packet->connection = 1; 
}
//********************************************************************************************************
// Modbus Master State Machine (конечный автомат)
void modbus_update() // Функция обновления пакетов модбус
{
	switch (state)
	{
		case IDLE:
		idle();
		break;
		case WAITING_FOR_REPLY:
		waiting_for_reply();
		break;
		case WAITING_FOR_TURNAROUND:
		waiting_for_turnaround();
		break;
	}
}
//**********************************************************************************************************
void idle()
{
static unsigned int packet_index;
unsigned int failed_connections = 0;
unsigned char current_connection;
	do
	{
		if (packet_index == total_no_of_packets) // wrap around to the beginning (повторитесь к началу)
		packet_index = 0;
		// proceed to the next packet (продолжите к следующему пакету)
		packet = &packetArray[packet_index];

		// get the current connection status (получите текущее состояние соединения)
		current_connection = packet->connection;
		if (!current_connection)
		{
			// Если все атрибуты соединения - False то возврат
			// сразу к основной программе
			if (++failed_connections == total_no_of_packets)
				return;
		}
		packet_index++;
        // если у пакета нет соединения, получают следующий
	// if a packet has no connection get the next one
	}while (!current_connection);
	constructPacket();
}
//******************************************************************************************************************
// создадим пакет
void constructPacket()
{
  packet->requests++;
  frame[0] = packet->id;
  frame[1] = packet->function;
  frame[2] = packet->address >> 8;   // address Hi
  frame[3] = packet->address & 0xFF; // address Lo
  // Включайте их данные в регистр данных а не в master массив
  if (packet->function == FORCE_SINGLE_COIL || packet->function == PRESET_SINGLE_REGISTER)
  packet->data = register_array[packet->local_start_address]; // получим данные
  frame[4] = packet->data >> 8;   // MSB
  frame[5] = packet->data & 0xFF; // LSB
  unsigned char frameSize;

  // создайте фрейм согласно функциям modbus
  if (packet->function == PRESET_MULTIPLE_REGISTERS)
		frameSize = construct_F16();
	else if (packet->function == FORCE_MULTIPLE_COILS)
		frameSize = construct_F15();
	else           // иначе функции 1,2,3,4,5 и 6 приняты.
	               // Они все совместно используют тот же самый формат запроса.
  frameSize = 8; // размер запроса всегда - 8 байт для вышеупомянутых функций.
  unsigned int crc16 = calculateCRC(frameSize - 2);
  frame[frameSize - 2] = crc16 >> 8; // разделение crc на 2 байта
  frame[frameSize - 1] = crc16 & 0xFF;
  sendPacket(frameSize);
  state = WAITING_FOR_REPLY; // изменение состояния на ожидание для ответа
  // Если передана широковещательная посылка требуется установить (id == 0) для функций 5,6,15 и 16 тогда переопределение
  // the previous state and force a success since the slave wont respond
  // предыдущее состояние и вызов успешно начиная с slave хотеть отвечать
	if (packet->id == 0)processSuccess();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char construct_F15()
{
  // функция 15 coil информации - упаковать LSB сначала, пока первые 16 битов не обработаны
  // Это получено темже же путём..
  unsigned char no_of_registers=0;
  unsigned char no_of_bytes=0;
  no_of_registers = packet->data / 16;
  no_of_bytes = no_of_registers * 2;
  // if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
  // если число бит не помещается даже в 2х-байтовые суммы (один регистр) тогда используют другой регистр и клавиатуру
  if (packet->data % 16 > 0)
  {
    no_of_registers++;
    no_of_bytes++;
  }
  frame[6] = no_of_bytes;
  unsigned char bytes_processed = 0;
  unsigned char index = 7; // user data starts at index 7 (пользовательские данные запускаются по индексу 7)
  unsigned int temp;
  for (unsigned char i = 0; i < no_of_registers; i++)
  {
    temp = register_array[packet->local_start_address + i]; // получим данные
    frame[index] = temp & 0xFF;
    bytes_processed++;

    if (bytes_processed < no_of_bytes)
    {
      frame[index + 1] = temp >> 8;
      bytes_processed++;
      index += 2;
    }
  }
unsigned char frameSize = (9 + no_of_bytes); // первые 7 байт массива + 2 байта CRC + noOfBytes
return frameSize;
}
//*********************************************************************
unsigned char construct_F16()
{
  unsigned char no_of_bytes = packet->data * 2;
  //следующий 6 байт из массива + no_of_bytes + 2 байта контрольной суммы
  frame[6] = no_of_bytes;  // количесво байт
  unsigned char index = 7; // пользовательские данные запускаются по индексу 7
  unsigned char no_of_registers = packet->data;
  unsigned int temp;
  for (unsigned char i = 0; i < no_of_registers; i++)
  {
    temp = register_array[packet->local_start_address + i]; // get the data
    frame[index] = temp >> 8;
    index++;
    frame[index] = temp & 0xFF;
    index++;
  }
  unsigned char frameSize = (9 + no_of_bytes); // следующий 7 байт из массива + 2 байта контрольной суммы + no_of_bytes
  return frameSize;
}
//**************************************************************************
void waiting_for_turnaround()
{
  if ((millis() - delayStart) > polling) state = IDLE;
}
//***************************************************************************
// получите последовательные данные из буфера
void waiting_for_reply()
{
	if ((*ModbusPort).available()) // Чтото есть в буфере?, проверим
	{
		unsigned char overflowFlag = 0;
		buffer = 0;
		while ((*ModbusPort).available())
		{
			if (overflowFlag)(*ModbusPort).read();
			else
			{
				if (buffer == BUFFER_SIZE) overflowFlag = 1;

				frame[buffer] = (*ModbusPort).read();
				buffer++;
			}
			delayMicroseconds(T1_5); // таймаут для передачи одного символа
		}
		if ((buffer < 5) || overflowFlag)// Проверяем буфер на переполнение
			processError();
		else if (frame[0] != packet->id) // проверим возврат id.
			processError();
		else
			processReply();
	}
	else if ((millis() - delayStart) > timeout) // проверьте тайм-аут
	{
		processError();
		state = IDLE; // изменение состояния, переопределение processError() состояние
		              // state change, override processError() state
	}
}
//************************************************************************
void processReply()
{
  // объединить crc Low и High байты.
  unsigned int received_crc = ((frame[buffer - 2] << 8) | frame[buffer - 1]);
  unsigned int calculated_crc = calculateCRC(buffer - 2);
  if (calculated_crc == received_crc) // проверим контрольную сумму.
  {
	// Чтобы указать ответ исключения, slave устройсво будет 'ИЛИ'
        // запрошенная функция с 0x80
	// To indicate an exception response a slave will 'OR'
	// the requested function with 0x80
     if ((frame[1] & 0x80) == 0x80) // извлечение 0x80
     {
      packet->exception_errors++;
      processError();
     }
     else
     {
       switch (frame[1]) // проверьте, что функция возвратилась
       {
        case READ_COIL_STATUS:         // возвратилась функция 1.
        case READ_INPUT_STATUS:        // возвратилась функция 2.
        process_F1_F2();               // вызов функции чтения  1, 2.
        break;
        case READ_INPUT_REGISTERS:     // возвратилась функция 4.
        case READ_HOLDING_REGISTERS:   // возвратилась функция 3.
        process_F3_F4();               // вызов функции чтения  3, 4.
        break;
	case FORCE_SINGLE_COIL:        // возвратилась функция 5.
	case PRESET_SINGLE_REGISTER:   // возвратилась функция 6.
        case FORCE_MULTIPLE_COILS:     // возвратилась функция 15.
        case PRESET_MULTIPLE_REGISTERS:// возвратилась функция 16.
        process_F5_F6_F15_F16();       // вызов функции записи 5, 6, 15 и 16.
        break;
        default: // возвратилась недопустимая функция (у нас такой нет или ошибка)
        processError();
        break;
      }
    }
  }
  else // контрольная сумма неправильная
  {
    processError();
  }
}
//********************************************************************************************
// Функции чтения 1 и 2
void process_F1_F2()
{
  // packet->data для функции 1 и 2 фактически число булевых точек(количево бит)
  unsigned char no_of_registers = packet->data / 16;
  unsigned char number_of_bytes = no_of_registers * 2;
  // if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
  if (packet->data % 16 > 0)
  {
    no_of_registers++;
    number_of_bytes++;
  }

  if (frame[2] == number_of_bytes) // контрольное число байтов возвратилось
  {
    unsigned char bytes_processed = 0;
    unsigned char index = 3;       // запустим с 4го элемента массива во фрейме и объедините Lo байт
    unsigned int temp;
    for (unsigned char i = 0; i < no_of_registers; i++)
    {
      temp = frame[index];
      bytes_processed++;
      if (bytes_processed < number_of_bytes)
      {
	temp = (frame[index + 1] << 8) | temp;
        bytes_processed++;
        index += 2;
      }
      register_array[packet->local_start_address + i] = temp;
    }
    processSuccess();
  }
  else // возвратилось неправильное количесво байтов
    processError();
}
//***********************************************************************
// Функции чтения 3 и 4
void process_F3_F4()
{
  // контрольное число байт возвратилось - unsigned int == 2 байта
  // данные для функции 3 и 4 это яколичесво регистров
  if (frame[2] == (packet->data * 2))
  {
    unsigned char index = 3;
    for (unsigned char i = 0; i < packet->data; i++)
    {
      // запустим с 4го элемента массива фрейме и объединим с Lo байтом
      register_array[packet->local_start_address + i] = (frame[index] << 8) | frame[index + 1];
      index += 2;
    }
    processSuccess();
  }
  else // возвратилось неправильное количесво байтов
    processError();
}
//*******************************************************************************
// Функции записи 5, 6, 15 и 16
void process_F5_F6_F15_F16()
{
  // Ответ функций 5,6,15 и 16 являются просто эхом запроса
  unsigned int recieved_address = ((frame[2] << 8) | frame[3]);
  unsigned int recieved_data = ((frame[4] << 8) | frame[5]);
  if ((recieved_address == packet->address) && (recieved_data == packet->data))
    processSuccess();
  else
    processError();
}
//*******************************************************************************
// Обработка ошибок
void processError()
{
	packet->retries++;         // Увеличим на 1 в структуре packet перемнную retries (повторной попытки)
	packet->failed_requests++; // Увеличим на 1 в структуре packet перемнную failed_requests (неудавшиеся запросы)
	// если количесво повторных запросов достигло макс. допустимого значения то
	// прекращаем запрашивать определенный пакет.
  if (packet->retries == retry_count)
	{
           packet->connection = 0;// Обнулим в структуре packet перемнную connection (подключения)
	   packet->retries = 0;   // Обнулим в структуре packet перемнную retries (повторной попытки)
	}
	state = WAITING_FOR_TURNAROUND;// Установим состояние "Ожидания благоприятного состояния".
	delayStart = millis();         // Запустим задержку цикла (обновим)
}
//*************************************************************************************************
// Успех процесса
void processSuccess()
{
	packet->successful_requests++; // транзакция выполнена успешно
	packet->retries = 0;           // если запрос был успешным сбросим счетчик повторной попытки (retry counter)
	state = WAITING_FOR_TURNAROUND;
	delayStart = millis();         // Запустим задержку цикла
}
//************************************************************************************************
  // Настроим модбус

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!тут!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, 
unsigned char _retry_count, unsigned char _TxEnablePin, Packet* _packets, unsigned int _total_no_of_packets, unsigned int* _register_array)
{
	if (_baud > 19200)
		T1_5 = 750;
	else
		T1_5 = 16500000/_baud; // 1T * 1.5 = T1.5

	frameDelay = T1_5 * 2; // задержка фрейма
	// Инициализация переменных
	state = IDLE;
        timeout = _timeout;
        polling = _polling;
	retry_count = _retry_count;
	TxEnablePin = _TxEnablePin;
	total_no_of_packets = _total_no_of_packets;
	//packetArray = _packets;
	register_array = _register_array;
	ModbusPort = SerialPort;
	(*ModbusPort).begin(_baud, _byteFormat); // Настроим скорость обмена по UART и формат
	pinMode(_TxEnablePin, OUTPUT);          // Настроим pin управления микросхемой MAX485 как выход
        digitalWrite(_TxEnablePin, LOW);
}
//**************************************************************
// Создадим пакет модбус

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!тут!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

void modbus_construct(Packet* _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address)
{
  
  _packet->id = _id;
  _packet->function = _function;
  _packet->address = _address;
  _packet->data = _data;
  _packet->local_start_address = _local_start_address;
  _packet->connection = 1;
}
///***************************************************************
// Считам контрольную сумму CRC16
unsigned int calculateCRC(unsigned char bufferSize)
{
  unsigned int temp, temp2, flag;
  temp = 0xFFFF;
  for (unsigned char i = 0; i < bufferSize; i++)
  {
    temp = temp ^ frame[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;
 // возвращенное значение уже поменяна местами,
 // crcLo байт первый, & crcHi байт является последним.
  return temp;
}
//****************************************************************************
// Отправим пакет модбус
void sendPacket(unsigned char bufferSize)
{
	  digitalWrite(TxEnablePin, HIGH);  // Установим микросхему MAX485 в режим передачи
	  // Выполняем в цикле пока не опустошим весь буфер
	  for (unsigned char i = 0; i < bufferSize; i++)(*ModbusPort).write(frame[i]);
	 (*ModbusPort).flush();           // очистим буфер
	 delayMicroseconds(frameDelay);   // задержка фрейма
	 digitalWrite(TxEnablePin, LOW);  // Установим микросхему MAX485 в режим приёма
	 delayStart = millis();           // начнём отсчёт задержки таймаута.
}
//***************************************************************************
void setup()
{

// Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
// modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS,    0, 1, 0); // чтение данных slave-master (slave адрес 1, регистр 0) 
// modbus_construct(&packets[PACKET2], 1, READ_HOLDING_REGISTERS,    1, 1, 1); // чтение данных slave-master (slave адрес 1, регистр 1)
// Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра.
// modbus_construct(&packets[PACKET3], 1, PRESET_MULTIPLE_REGISTERS, 2, 1, 2); // запись данных master-slave (slave адрес 1, регистр 2)
// modbus_construct(&packets[PACKET4], 1, PRESET_MULTIPLE_REGISTERS, 3, 1, 3); // запись данных master-slave (slave адрес 1, регистр 3)
 
 // инициализируем протокол модбус
//modbus_configure(&Serial, baud, SERIAL_8N1, timeout_1, polling_1, retry_count_1, TxEnablePin_1, packets, TOTAL_NO_OF_PACKETS, regs);
} // конец void setup()

void loop()
{
  modbus_update(); // запуск обмена по Modbus
} // конец void loop()

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

alexval2007 пишет:

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

А можно не Ваше понимание, а сообщение компилятора? Пожалуйста, код, который Вы компилируете (тот самый, который компилируете, а не какой-то другой) и сообщение компилятора как оно есть.

alexval2007
Offline
Зарегистрирован: 10.11.2012

это тот самый код

вот сообщение компилятора

D:\Program Files\arduino-1.5.2\hardware\tools\avr\bin\avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -MMD -mmcu=atmega328p -DF_CPU=16000000L -DARDUINO=152 -ID:\Program Files\arduino-1.5.2\hardware\arduino\avr\cores\arduino -ID:\Program Files\arduino-1.5.2\hardware\arduino\avr\variants\standard C:\DOCUME~1\Admin\LOCALS~1\Temp\build5886494271995209925.tmp\SimpleModbusMaster_v07.cpp -o C:\DOCUME~1\Admin\LOCALS~1\Temp\build5886494271995209925.tmp\SimpleModbusMaster_v07.cpp.o 
SimpleModbusMaster_v07:30: error: 'Packet' has not been declared
SimpleModbusMaster_v07:102: error: expected unqualified-id before '{' token
SimpleModbusMaster_v07.ino: In function 'void modbus_construct(Packet*, unsigned char, unsigned char, unsigned int, unsigned int, unsigned int)':
SimpleModbusMaster_v07:456: error: redefinition of 'void modbus_construct(Packet*, unsigned char, unsigned char, unsigned int, unsigned int, unsigned int)'
SimpleModbusMaster_v07:101: error: 'void modbus_construct(Packet*, unsigned char, unsigned char, unsigned int, unsigned int, unsigned int)' previously defined here

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015
В строке 101 определяется функция modbus_construct как пустая (с пустыми фигурными скобками - так и надо???), затем, встроке 456 она же определяется ещё раз - так нельзя. Определите функцию modbus_construct  один раз.
 
В строках 102 и 109 стоят фигурные скобки. В глобальном контексте их не должно быть. Разберитесь зачем они у Вас. Думаю, что это следствие предыдущей ошибки.
 
Теперь про объявлени типа Packet.
 
Это старое определение типа (пришедшее из С) обычно для его нормальной компиляции нужно указывать специальные опции компилятора, я сейчас не буду вдаваться в подробности, просто сделайте следующее:
 
Вместо 
 
typedef struct{
.... внутренности ........
}Packet;
напишите
 
class Packet {
public:
.......... те же самые внутренности ...............
};

и всё будет нормально.

 

alexval2007
Offline
Зарегистрирован: 10.11.2012

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

Я так понимаю это вариант для С++ в скетче он скомпилируется я надеюсь там по идее С. Еще вопрос обращение к элементам структуры изменитсяили такжебудет?

 
class Packet {
public:
.......... те же самые внутренности ...............
};

Мда номер непрошол все исправил как указано выше

//SimpleModbusMaster
#include "Arduino.h"
#include "HardwareSerial.h"
// state machine states
#define IDLE                   1
#define WAITING_FOR_REPLY      2
#define WAITING_FOR_TURNAROUND 3
#define BUFFER_SIZE           64   // размер буфера
//////////////////// Макроопределения портов и настройки программы  ///////////////////
#define baud_1                9600 // скоростьобмена по последовательному интерфейсу. (UART)
#define timeout_1             1000 // Длительность ожидание ответа (таймаут modbus)
#define polling_1             200  // скорость опроса по modbus
#define retry_count_1         10   // количесво запросов modbus до ошибки и останова обмена 
#define TxEnablePin_1         2    // Tx/Rx пин RS485
#define TOTAL_NO_OF_REGISTERS 4

#define COIL_OFF 0x0000              // Function 5 OFF request is 0x0000
#define COIL_ON 0xFF00               // Function 5 ON request is 0xFF00
#define READ_COIL_STATUS 1           // Reads the ON/OFF status of discrete outputs (0X references, coils) in the slave.
#define READ_INPUT_STATUS 2          // Reads the ON/OFF status of discrete inputs (1X references) in the slave.
#define READ_HOLDING_REGISTERS 3     // Reads the binary contents of holding registers (4X references) in the slave.
#define READ_INPUT_REGISTERS 4       // Reads the binary contents of input registers (3X references) in the slave. Not writable.
#define FORCE_SINGLE_COIL 5          // Forces a single coil (0X reference) to either ON (0xFF00) or OFF (0x0000).
#define PRESET_SINGLE_REGISTER 6     // Presets a value into a single holding register (4X reference).
#define FORCE_MULTIPLE_COILS 15      // Forces each coil (0X reference) in a sequence of coils to either ON or OFF.
#define PRESET_MULTIPLE_REGISTERS 16 // Presets values into a sequence of holding registers (4X references).

unsigned char state;
unsigned char retry_count;
unsigned char TxEnablePin;
unsigned char buffer;
long timeout;                 // интервал таймаута(timeout interval)
long polling;                 // циклический интервал задержки (turnaround delay interval)
unsigned int T1_5;            // inter character time out in microseconds
unsigned int frameDelay;      // frame time out in microseconds
long delayStart;              // init variable for turnaround and timeout delay
unsigned int total_no_of_packets;
//******************************************************************************
/*
typedef struct{
  // Специальная пакетная информация
  unsigned char id;
  unsigned char function;
  unsigned int  address;
  unsigned int  data;
  unsigned int  local_start_address;
  // счетчики информации о modbus
  unsigned int  requests;
  unsigned int  successful_requests;
  unsigned int  failed_requests;
  unsigned int  exception_errors;
  unsigned int  retries;
  //******************************
  unsigned char connection; // состояние соединения пакета
}Packet;
*/

class Packet {
  public:
  // Специальная пакетная информация
  unsigned char id;
  unsigned char function;
  unsigned int  address;
  unsigned int  data;
  unsigned int  local_start_address;
  // счетчики информации о modbus
  unsigned int  requests;
  unsigned int  successful_requests;
  unsigned int  failed_requests;
  unsigned int  exception_errors;
  unsigned int  retries;
  //******************************
  unsigned char connection; // состояние соединения пакета
};

// Объявим функции
void modbus_update();
void modbus_construct(Packet* _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address);
void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, unsigned char _retry_count, unsigned char _TxEnablePin, Packet* _packets, unsigned int _total_no_of_packets, unsigned int* _register_array);


Packet* packetArray;          // начальный адрес пакета
Packet* packet;               // текущий пакет
unsigned int* register_array; // указатель на массив регистров в master
HardwareSerial* ModbusPort;

// Масив пакетов modbus
enum
{
  PACKET1,
  PACKET2,
  PACKET3,
  PACKET4,
  TOTAL_NO_OF_PACKETS // эту строку неменять
};

unsigned char frame[BUFFER_SIZE];
Packet packets[TOTAL_NO_OF_PACKETS];     // Масив пакетов модбус
unsigned int regs[TOTAL_NO_OF_REGISTERS];// Массив хранения содержимого принятых и передающихся регистров
//********************************************************************************************************
// Объявим функции
void idle();
void constructPacket();
unsigned char construct_F15();
unsigned char construct_F16();
void waiting_for_reply();
void processReply();
void waiting_for_turnaround();
void process_F1_F2();
void process_F3_F4();
void process_F5_F6_F15_F16();
void processError();
void processSuccess();
unsigned int calculateCRC(unsigned char bufferSize);
void sendPacket(unsigned char bufferSize);
//********************************************************************************************************
// Создадим пакет модбус

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!тут!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

void modbus_construct(Packet* _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address)
{
  _packet->id = _id;
  _packet->function = _function;
  _packet->address = _address;
  _packet->data = _data;
  _packet->local_start_address = _local_start_address;
  _packet->connection = 1; 
}
//********************************************************************************************************
// Modbus Master State Machine (конечный автомат)
void modbus_update() // Функция обновления пакетов модбус
{
	switch (state)
	{
		case IDLE:
		idle();
		break;
		case WAITING_FOR_REPLY:
		waiting_for_reply();
		break;
		case WAITING_FOR_TURNAROUND:
		waiting_for_turnaround();
		break;
	}
}
//**********************************************************************************************************
void idle()
{
static unsigned int packet_index;
unsigned int failed_connections = 0;
unsigned char current_connection;
	do
	{
		if (packet_index == total_no_of_packets) // wrap around to the beginning (повторитесь к началу)
		packet_index = 0;
		// proceed to the next packet (продолжите к следующему пакету)
		packet = &packetArray[packet_index];

		// get the current connection status (получите текущее состояние соединения)
		current_connection = packet->connection;
		if (!current_connection)
		{
			// Если все атрибуты соединения - False то возврат
			// сразу к основной программе
			if (++failed_connections == total_no_of_packets)
				return;
		}
		packet_index++;
        // если у пакета нет соединения, получают следующий
	// if a packet has no connection get the next one
	}while (!current_connection);
	constructPacket();
}
//******************************************************************************************************************
// создадим пакет
void constructPacket()
{
  packet->requests++;
  frame[0] = packet->id;
  frame[1] = packet->function;
  frame[2] = packet->address >> 8;   // address Hi
  frame[3] = packet->address & 0xFF; // address Lo
  // Включайте их данные в регистр данных а не в master массив
  if (packet->function == FORCE_SINGLE_COIL || packet->function == PRESET_SINGLE_REGISTER)
  packet->data = register_array[packet->local_start_address]; // получим данные
  frame[4] = packet->data >> 8;   // MSB
  frame[5] = packet->data & 0xFF; // LSB
  unsigned char frameSize;

  // создайте фрейм согласно функциям modbus
  if (packet->function == PRESET_MULTIPLE_REGISTERS)
		frameSize = construct_F16();
	else if (packet->function == FORCE_MULTIPLE_COILS)
		frameSize = construct_F15();
	else           // иначе функции 1,2,3,4,5 и 6 приняты.
	               // Они все совместно используют тот же самый формат запроса.
  frameSize = 8; // размер запроса всегда - 8 байт для вышеупомянутых функций.
  unsigned int crc16 = calculateCRC(frameSize - 2);
  frame[frameSize - 2] = crc16 >> 8; // разделение crc на 2 байта
  frame[frameSize - 1] = crc16 & 0xFF;
  sendPacket(frameSize);
  state = WAITING_FOR_REPLY; // изменение состояния на ожидание для ответа
  // Если передана широковещательная посылка требуется установить (id == 0) для функций 5,6,15 и 16 тогда переопределение
  // the previous state and force a success since the slave wont respond
  // предыдущее состояние и вызов успешно начиная с slave хотеть отвечать
	if (packet->id == 0)processSuccess();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char construct_F15()
{
  // функция 15 coil информации - упаковать LSB сначала, пока первые 16 битов не обработаны
  // Это получено темже же путём..
  unsigned char no_of_registers=0;
  unsigned char no_of_bytes=0;
  no_of_registers = packet->data / 16;
  no_of_bytes = no_of_registers * 2;
  // if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
  // если число бит не помещается даже в 2х-байтовые суммы (один регистр) тогда используют другой регистр и клавиатуру
  if (packet->data % 16 > 0)
  {
    no_of_registers++;
    no_of_bytes++;
  }
  frame[6] = no_of_bytes;
  unsigned char bytes_processed = 0;
  unsigned char index = 7; // user data starts at index 7 (пользовательские данные запускаются по индексу 7)
  unsigned int temp;
  for (unsigned char i = 0; i < no_of_registers; i++)
  {
    temp = register_array[packet->local_start_address + i]; // получим данные
    frame[index] = temp & 0xFF;
    bytes_processed++;

    if (bytes_processed < no_of_bytes)
    {
      frame[index + 1] = temp >> 8;
      bytes_processed++;
      index += 2;
    }
  }
unsigned char frameSize = (9 + no_of_bytes); // первые 7 байт массива + 2 байта CRC + noOfBytes
return frameSize;
}
//*********************************************************************
unsigned char construct_F16()
{
  unsigned char no_of_bytes = packet->data * 2;
  //следующий 6 байт из массива + no_of_bytes + 2 байта контрольной суммы
  frame[6] = no_of_bytes;  // количесво байт
  unsigned char index = 7; // пользовательские данные запускаются по индексу 7
  unsigned char no_of_registers = packet->data;
  unsigned int temp;
  for (unsigned char i = 0; i < no_of_registers; i++)
  {
    temp = register_array[packet->local_start_address + i]; // get the data
    frame[index] = temp >> 8;
    index++;
    frame[index] = temp & 0xFF;
    index++;
  }
  unsigned char frameSize = (9 + no_of_bytes); // следующий 7 байт из массива + 2 байта контрольной суммы + no_of_bytes
  return frameSize;
}
//**************************************************************************
void waiting_for_turnaround()
{
  if ((millis() - delayStart) > polling) state = IDLE;
}
//***************************************************************************
// получите последовательные данные из буфера
void waiting_for_reply()
{
	if ((*ModbusPort).available()) // Чтото есть в буфере?, проверим
	{
		unsigned char overflowFlag = 0;
		buffer = 0;
		while ((*ModbusPort).available())
		{
			if (overflowFlag)(*ModbusPort).read();
			else
			{
				if (buffer == BUFFER_SIZE) overflowFlag = 1;

				frame[buffer] = (*ModbusPort).read();
				buffer++;
			}
			delayMicroseconds(T1_5); // таймаут для передачи одного символа
		}
		if ((buffer < 5) || overflowFlag)// Проверяем буфер на переполнение
			processError();
		else if (frame[0] != packet->id) // проверим возврат id.
			processError();
		else
			processReply();
	}
	else if ((millis() - delayStart) > timeout) // проверьте тайм-аут
	{
		processError();
		state = IDLE; // изменение состояния, переопределение processError() состояние
		              // state change, override processError() state
	}
}
//************************************************************************
void processReply()
{
  // объединить crc Low и High байты.
  unsigned int received_crc = ((frame[buffer - 2] << 8) | frame[buffer - 1]);
  unsigned int calculated_crc = calculateCRC(buffer - 2);
  if (calculated_crc == received_crc) // проверим контрольную сумму.
  {
	// Чтобы указать ответ исключения, slave устройсво будет 'ИЛИ'
        // запрошенная функция с 0x80
	// To indicate an exception response a slave will 'OR'
	// the requested function with 0x80
     if ((frame[1] & 0x80) == 0x80) // извлечение 0x80
     {
      packet->exception_errors++;
      processError();
     }
     else
     {
       switch (frame[1]) // проверьте, что функция возвратилась
       {
        case READ_COIL_STATUS:         // возвратилась функция 1.
        case READ_INPUT_STATUS:        // возвратилась функция 2.
        process_F1_F2();               // вызов функции чтения  1, 2.
        break;
        case READ_INPUT_REGISTERS:     // возвратилась функция 4.
        case READ_HOLDING_REGISTERS:   // возвратилась функция 3.
        process_F3_F4();               // вызов функции чтения  3, 4.
        break;
	case FORCE_SINGLE_COIL:        // возвратилась функция 5.
	case PRESET_SINGLE_REGISTER:   // возвратилась функция 6.
        case FORCE_MULTIPLE_COILS:     // возвратилась функция 15.
        case PRESET_MULTIPLE_REGISTERS:// возвратилась функция 16.
        process_F5_F6_F15_F16();       // вызов функции записи 5, 6, 15 и 16.
        break;
        default: // возвратилась недопустимая функция (у нас такой нет или ошибка)
        processError();
        break;
      }
    }
  }
  else // контрольная сумма неправильная
  {
    processError();
  }
}
//********************************************************************************************
// Функции чтения 1 и 2
void process_F1_F2()
{
  // packet->data для функции 1 и 2 фактически число булевых точек(количево бит)
  unsigned char no_of_registers = packet->data / 16;
  unsigned char number_of_bytes = no_of_registers * 2;
  // if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
  if (packet->data % 16 > 0)
  {
    no_of_registers++;
    number_of_bytes++;
  }

  if (frame[2] == number_of_bytes) // контрольное число байтов возвратилось
  {
    unsigned char bytes_processed = 0;
    unsigned char index = 3;       // запустим с 4го элемента массива во фрейме и объедините Lo байт
    unsigned int temp;
    for (unsigned char i = 0; i < no_of_registers; i++)
    {
      temp = frame[index];
      bytes_processed++;
      if (bytes_processed < number_of_bytes)
      {
	temp = (frame[index + 1] << 8) | temp;
        bytes_processed++;
        index += 2;
      }
      register_array[packet->local_start_address + i] = temp;
    }
    processSuccess();
  }
  else // возвратилось неправильное количесво байтов
    processError();
}
//***********************************************************************
// Функции чтения 3 и 4
void process_F3_F4()
{
  // контрольное число байт возвратилось - unsigned int == 2 байта
  // данные для функции 3 и 4 это яколичесво регистров
  if (frame[2] == (packet->data * 2))
  {
    unsigned char index = 3;
    for (unsigned char i = 0; i < packet->data; i++)
    {
      // запустим с 4го элемента массива фрейме и объединим с Lo байтом
      register_array[packet->local_start_address + i] = (frame[index] << 8) | frame[index + 1];
      index += 2;
    }
    processSuccess();
  }
  else // возвратилось неправильное количесво байтов
    processError();
}
//*******************************************************************************
// Функции записи 5, 6, 15 и 16
void process_F5_F6_F15_F16()
{
  // Ответ функций 5,6,15 и 16 являются просто эхом запроса
  unsigned int recieved_address = ((frame[2] << 8) | frame[3]);
  unsigned int recieved_data = ((frame[4] << 8) | frame[5]);
  if ((recieved_address == packet->address) && (recieved_data == packet->data))
    processSuccess();
  else
    processError();
}
//*******************************************************************************
// Обработка ошибок
void processError()
{
	packet->retries++;         // Увеличим на 1 в структуре packet перемнную retries (повторной попытки)
	packet->failed_requests++; // Увеличим на 1 в структуре packet перемнную failed_requests (неудавшиеся запросы)
	// если количесво повторных запросов достигло макс. допустимого значения то
	// прекращаем запрашивать определенный пакет.
  if (packet->retries == retry_count)
	{
           packet->connection = 0;// Обнулим в структуре packet перемнную connection (подключения)
	   packet->retries = 0;   // Обнулим в структуре packet перемнную retries (повторной попытки)
	}
	state = WAITING_FOR_TURNAROUND;// Установим состояние "Ожидания благоприятного состояния".
	delayStart = millis();         // Запустим задержку цикла (обновим)
}
//*************************************************************************************************
// Успех процесса
void processSuccess()
{
	packet->successful_requests++; // транзакция выполнена успешно
	packet->retries = 0;           // если запрос был успешным сбросим счетчик повторной попытки (retry counter)
	state = WAITING_FOR_TURNAROUND;
	delayStart = millis();         // Запустим задержку цикла
}
//************************************************************************************************
  // Настроим модбус

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!тут!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, 
unsigned char _retry_count, unsigned char _TxEnablePin, Packet* _packets, unsigned int _total_no_of_packets, unsigned int* _register_array)
{
	if (_baud > 19200)
		T1_5 = 750;
	else
		T1_5 = 16500000/_baud; // 1T * 1.5 = T1.5

	frameDelay = T1_5 * 2; // задержка фрейма
	// Инициализация переменных
	state = IDLE;
        timeout = _timeout;
        polling = _polling;
	retry_count = _retry_count;
	TxEnablePin = _TxEnablePin;
	total_no_of_packets = _total_no_of_packets;
	//packetArray = _packets;
	register_array = _register_array;
	ModbusPort = SerialPort;
	(*ModbusPort).begin(_baud, _byteFormat); // Настроим скорость обмена по UART и формат
	pinMode(_TxEnablePin, OUTPUT);          // Настроим pin управления микросхемой MAX485 как выход
        digitalWrite(_TxEnablePin, LOW);
}
//**************************************************************
// Создадим пакет модбус

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!тут!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

void modbus_construct(Packet* _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address)
{
  
  _packet->id = _id;
  _packet->function = _function;
  _packet->address = _address;
  _packet->data = _data;
  _packet->local_start_address = _local_start_address;
  _packet->connection = 1;
}
///***************************************************************
// Считам контрольную сумму CRC16
unsigned int calculateCRC(unsigned char bufferSize)
{
  unsigned int temp, temp2, flag;
  temp = 0xFFFF;
  for (unsigned char i = 0; i < bufferSize; i++)
  {
    temp = temp ^ frame[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;
 // возвращенное значение уже поменяна местами,
 // crcLo байт первый, & crcHi байт является последним.
  return temp;
}
//****************************************************************************
// Отправим пакет модбус
void sendPacket(unsigned char bufferSize)
{
	  digitalWrite(TxEnablePin, HIGH);  // Установим микросхему MAX485 в режим передачи
	  // Выполняем в цикле пока не опустошим весь буфер
	  for (unsigned char i = 0; i < bufferSize; i++)(*ModbusPort).write(frame[i]);
	 (*ModbusPort).flush();           // очистим буфер
	 delayMicroseconds(frameDelay);   // задержка фрейма
	 digitalWrite(TxEnablePin, LOW);  // Установим микросхему MAX485 в режим приёма
	 delayStart = millis();           // начнём отсчёт задержки таймаута.
}
//***************************************************************************
void setup()
{

// Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
 modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS,    0, 1, 0); // чтение данных slave-master (slave адрес 1, регистр 0) 
// modbus_construct(&packets[PACKET2], 1, READ_HOLDING_REGISTERS,    1, 1, 1); // чтение данных slave-master (slave адрес 1, регистр 1)
// Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра.
// modbus_construct(&packets[PACKET3], 1, PRESET_MULTIPLE_REGISTERS, 2, 1, 2); // запись данных master-slave (slave адрес 1, регистр 2)
// modbus_construct(&packets[PACKET4], 1, PRESET_MULTIPLE_REGISTERS, 3, 1, 3); // запись данных master-slave (slave адрес 1, регистр 3)
 
 // инициализируем протокол модбус
modbus_configure(&Serial, baud_1, SERIAL_8N1, timeout_1, polling_1, retry_count_1, TxEnablePin_1, packets, TOTAL_NO_OF_PACKETS, regs);
} // конец void setup()

void loop()
{
  modbus_update(); // запуск обмена по Modbus
} // конец void loop()


/////////////////////////////////////////////////////////////////////////////////////////////
Сообщение компилятора
D:\Program Files\arduino-1.5.2\hardware\tools\avr\bin\avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=152 -ID:\Program Files\arduino-1.5.2\hardware\arduino\avr\cores\arduino -ID:\Program Files\arduino-1.5.2\hardware\arduino\avr\variants\mega C:\Temp\build3496320038438604186.tmp\SimpleModbusMaster_v07.cpp -o C:\Temp\build3496320038438604186.tmp\SimpleModbusMaster_v07.cpp.o
SimpleModbusMaster_v07:30: error: 'Packet' has not been declared
SimpleModbusMaster_v07.ino: In function 'void modbus_construct(Packet*, unsigned char, unsigned char, unsigned int, unsigned int, unsigned int)':
SimpleModbusMaster_v07:476: error: redefinition of 'void modbus_construct(Packet*, unsigned char, unsigned char, unsigned int, unsigned int, unsigned int)'
SimpleModbusMaster_v07:121: error: 'void modbus_construct(Packet*, unsigned char, unsigned char, unsigned int, unsigned int, unsigned int)' previously defined here

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Простите, Вы издеваетесь? Я же Вам писал, что у Вас дважды определена функция modbus_constructТак она у Вас по-прежнему дважды определена. Первый раз в строке 121, а второй раз в строке 476. Уберите двойное определение! 

С Packet, похоже не помогло (про Ваш вопрос - нет, ничего не поменяется - эти определения - полные синонимы). Странно, компилятор ругается на строку 30, но там никакой Packet не используется.

Давайте так:

1. Исправьте всё остальное (двойное определение функции)

2. Кстати, уберите #include "Arduino.h" - в скетче это не нужно

3. После этого, давайте снова код и сообщение компилятора с единственной ошибкой насчёт Packet и я внимательно посмотрю.

alexval2007
Offline
Зарегистрирован: 10.11.2012

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

Вот осталась одна ошибка

//SimpleModbusMaster
#include "HardwareSerial.h"
// state machine states
#define IDLE                   1
#define WAITING_FOR_REPLY      2
#define WAITING_FOR_TURNAROUND 3
#define BUFFER_SIZE           64   // размер буфера
//////////////////// Макроопределения портов и настройки программы  ///////////////////
#define baud_1                9600 // скоростьобмена по последовательному интерфейсу. (UART)
#define timeout_1             1000 // Длительность ожидание ответа (таймаут modbus)
#define polling_1             200  // скорость опроса по modbus
#define retry_count_1         10   // количесво запросов modbus до ошибки и останова обмена 
#define TxEnablePin_1         2    // Tx/Rx пин RS485
#define TOTAL_NO_OF_REGISTERS 4

#define COIL_OFF 0x0000              // Function 5 OFF request is 0x0000
#define COIL_ON 0xFF00               // Function 5 ON request is 0xFF00
#define READ_COIL_STATUS 1           // Reads the ON/OFF status of discrete outputs (0X references, coils) in the slave.
#define READ_INPUT_STATUS 2          // Reads the ON/OFF status of discrete inputs (1X references) in the slave.
#define READ_HOLDING_REGISTERS 3     // Reads the binary contents of holding registers (4X references) in the slave.
#define READ_INPUT_REGISTERS 4       // Reads the binary contents of input registers (3X references) in the slave. Not writable.
#define FORCE_SINGLE_COIL 5          // Forces a single coil (0X reference) to either ON (0xFF00) or OFF (0x0000).
#define PRESET_SINGLE_REGISTER 6     // Presets a value into a single holding register (4X reference).
#define FORCE_MULTIPLE_COILS 15      // Forces each coil (0X reference) in a sequence of coils to either ON or OFF.
#define PRESET_MULTIPLE_REGISTERS 16 // Presets values into a sequence of holding registers (4X references).

unsigned char state;
unsigned char retry_count;
unsigned char TxEnablePin;
unsigned char buffer;
long timeout;                 // интервал таймаута(timeout interval)
long polling;                 // циклический интервал задержки (turnaround delay interval)
unsigned int T1_5;            // inter character time out in microseconds
unsigned int frameDelay;      // frame time out in microseconds
long delayStart;              // init variable for turnaround and timeout delay
unsigned int total_no_of_packets;
//******************************************************************************
/*
typedef struct{
  // Специальная пакетная информация
  unsigned char id;
  unsigned char function;
  unsigned int  address;
  unsigned int  data;
  unsigned int  local_start_address;
  // счетчики информации о modbus
  unsigned int  requests;
  unsigned int  successful_requests;
  unsigned int  failed_requests;
  unsigned int  exception_errors;
  unsigned int  retries;
  //******************************
  unsigned char connection; // состояние соединения пакета
}Packet;
*/

class Packet {
  public:
  // Специальная пакетная информация
  unsigned char id;
  unsigned char function;
  unsigned int  address;
  unsigned int  data;
  unsigned int  local_start_address;
  // счетчики информации о modbus
  unsigned int  requests;
  unsigned int  successful_requests;
  unsigned int  failed_requests;
  unsigned int  exception_errors;
  unsigned int  retries;
  //******************************
  unsigned char connection; // состояние соединения пакета
};

// Объявим функции
void modbus_update();
void modbus_construct(Packet* _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address);
void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, unsigned char _retry_count, unsigned char _TxEnablePin, Packet* _packets, unsigned int _total_no_of_packets, unsigned int* _register_array);


Packet* packetArray;          // начальный адрес пакета
Packet* packet;               // текущий пакет
unsigned int* register_array; // указатель на массив регистров в master
HardwareSerial* ModbusPort;

// Масив пакетов modbus
enum
{
  PACKET1,
  PACKET2,
  PACKET3,
  PACKET4,
  TOTAL_NO_OF_PACKETS // эту строку неменять
};

unsigned char frame[BUFFER_SIZE];
Packet packets[TOTAL_NO_OF_PACKETS];     // Масив пакетов модбус
unsigned int regs[TOTAL_NO_OF_REGISTERS];// Массив хранения содержимого принятых и передающихся регистров
//********************************************************************************************************
// Объявим функции
void idle();
void constructPacket();
unsigned char construct_F15();
unsigned char construct_F16();
void waiting_for_reply();
void processReply();
void waiting_for_turnaround();
void process_F1_F2();
void process_F3_F4();
void process_F5_F6_F15_F16();
void processError();
void processSuccess();
unsigned int calculateCRC(unsigned char bufferSize);
void sendPacket(unsigned char bufferSize);
//********************************************************************************************************
// Modbus Master State Machine (конечный автомат)
void modbus_update() // Функция обновления пакетов модбус
{
	switch (state)
	{
		case IDLE:
		idle();
		break;
		case WAITING_FOR_REPLY:
		waiting_for_reply();
		break;
		case WAITING_FOR_TURNAROUND:
		waiting_for_turnaround();
		break;
	}
}
//**********************************************************************************************************
void idle()
{
static unsigned int packet_index;
unsigned int failed_connections = 0;
unsigned char current_connection;
	do
	{
		if (packet_index == total_no_of_packets) // wrap around to the beginning (повторитесь к началу)
		packet_index = 0;
		// proceed to the next packet (продолжите к следующему пакету)
		packet = &packetArray[packet_index];

		// get the current connection status (получите текущее состояние соединения)
		current_connection = packet->connection;
		if (!current_connection)
		{
			// Если все атрибуты соединения - False то возврат
			// сразу к основной программе
			if (++failed_connections == total_no_of_packets)
				return;
		}
		packet_index++;
        // если у пакета нет соединения, получают следующий
	// if a packet has no connection get the next one
	}while (!current_connection);
	constructPacket();
}
//******************************************************************************************************************
// создадим пакет
void constructPacket()
{
  packet->requests++;
  frame[0] = packet->id;
  frame[1] = packet->function;
  frame[2] = packet->address >> 8;   // address Hi
  frame[3] = packet->address & 0xFF; // address Lo
  // Включайте их данные в регистр данных а не в master массив
  if (packet->function == FORCE_SINGLE_COIL || packet->function == PRESET_SINGLE_REGISTER)
  packet->data = register_array[packet->local_start_address]; // получим данные
  frame[4] = packet->data >> 8;   // MSB
  frame[5] = packet->data & 0xFF; // LSB
  unsigned char frameSize;

  // создайте фрейм согласно функциям modbus
  if (packet->function == PRESET_MULTIPLE_REGISTERS)
		frameSize = construct_F16();
	else if (packet->function == FORCE_MULTIPLE_COILS)
		frameSize = construct_F15();
	else           // иначе функции 1,2,3,4,5 и 6 приняты.
	               // Они все совместно используют тот же самый формат запроса.
  frameSize = 8; // размер запроса всегда - 8 байт для вышеупомянутых функций.
  unsigned int crc16 = calculateCRC(frameSize - 2);
  frame[frameSize - 2] = crc16 >> 8; // разделение crc на 2 байта
  frame[frameSize - 1] = crc16 & 0xFF;
  sendPacket(frameSize);
  state = WAITING_FOR_REPLY; // изменение состояния на ожидание для ответа
  // Если передана широковещательная посылка требуется установить (id == 0) для функций 5,6,15 и 16 тогда переопределение
  // the previous state and force a success since the slave wont respond
  // предыдущее состояние и вызов успешно начиная с slave хотеть отвечать
	if (packet->id == 0)processSuccess();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char construct_F15()
{
  // функция 15 coil информации - упаковать LSB сначала, пока первые 16 битов не обработаны
  // Это получено темже же путём..
  unsigned char no_of_registers=0;
  unsigned char no_of_bytes=0;
  no_of_registers = packet->data / 16;
  no_of_bytes = no_of_registers * 2;
  // if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
  // если число бит не помещается даже в 2х-байтовые суммы (один регистр) тогда используют другой регистр и клавиатуру
  if (packet->data % 16 > 0)
  {
    no_of_registers++;
    no_of_bytes++;
  }
  frame[6] = no_of_bytes;
  unsigned char bytes_processed = 0;
  unsigned char index = 7; // user data starts at index 7 (пользовательские данные запускаются по индексу 7)
  unsigned int temp;
  for (unsigned char i = 0; i < no_of_registers; i++)
  {
    temp = register_array[packet->local_start_address + i]; // получим данные
    frame[index] = temp & 0xFF;
    bytes_processed++;

    if (bytes_processed < no_of_bytes)
    {
      frame[index + 1] = temp >> 8;
      bytes_processed++;
      index += 2;
    }
  }
unsigned char frameSize = (9 + no_of_bytes); // первые 7 байт массива + 2 байта CRC + noOfBytes
return frameSize;
}
//*********************************************************************
unsigned char construct_F16()
{
  unsigned char no_of_bytes = packet->data * 2;
  //следующий 6 байт из массива + no_of_bytes + 2 байта контрольной суммы
  frame[6] = no_of_bytes;  // количесво байт
  unsigned char index = 7; // пользовательские данные запускаются по индексу 7
  unsigned char no_of_registers = packet->data;
  unsigned int temp;
  for (unsigned char i = 0; i < no_of_registers; i++)
  {
    temp = register_array[packet->local_start_address + i]; // get the data
    frame[index] = temp >> 8;
    index++;
    frame[index] = temp & 0xFF;
    index++;
  }
  unsigned char frameSize = (9 + no_of_bytes); // следующий 7 байт из массива + 2 байта контрольной суммы + no_of_bytes
  return frameSize;
}
//**************************************************************************
void waiting_for_turnaround()
{
  if ((millis() - delayStart) > polling) state = IDLE;
}
//***************************************************************************
// получите последовательные данные из буфера
void waiting_for_reply()
{
	if ((*ModbusPort).available()) // Чтото есть в буфере?, проверим
	{
		unsigned char overflowFlag = 0;
		buffer = 0;
		while ((*ModbusPort).available())
		{
			if (overflowFlag)(*ModbusPort).read();
			else
			{
				if (buffer == BUFFER_SIZE) overflowFlag = 1;

				frame[buffer] = (*ModbusPort).read();
				buffer++;
			}
			delayMicroseconds(T1_5); // таймаут для передачи одного символа
		}
		if ((buffer < 5) || overflowFlag)// Проверяем буфер на переполнение
			processError();
		else if (frame[0] != packet->id) // проверим возврат id.
			processError();
		else
			processReply();
	}
	else if ((millis() - delayStart) > timeout) // проверьте тайм-аут
	{
		processError();
		state = IDLE; // изменение состояния, переопределение processError() состояние
		              // state change, override processError() state
	}
}
//************************************************************************
void processReply()
{
  // объединить crc Low и High байты.
  unsigned int received_crc = ((frame[buffer - 2] << 8) | frame[buffer - 1]);
  unsigned int calculated_crc = calculateCRC(buffer - 2);
  if (calculated_crc == received_crc) // проверим контрольную сумму.
  {
	// Чтобы указать ответ исключения, slave устройсво будет 'ИЛИ'
        // запрошенная функция с 0x80
	// To indicate an exception response a slave will 'OR'
	// the requested function with 0x80
     if ((frame[1] & 0x80) == 0x80) // извлечение 0x80
     {
      packet->exception_errors++;
      processError();
     }
     else
     {
       switch (frame[1]) // проверьте, что функция возвратилась
       {
        case READ_COIL_STATUS:         // возвратилась функция 1.
        case READ_INPUT_STATUS:        // возвратилась функция 2.
        process_F1_F2();               // вызов функции чтения  1, 2.
        break;
        case READ_INPUT_REGISTERS:     // возвратилась функция 4.
        case READ_HOLDING_REGISTERS:   // возвратилась функция 3.
        process_F3_F4();               // вызов функции чтения  3, 4.
        break;
	case FORCE_SINGLE_COIL:        // возвратилась функция 5.
	case PRESET_SINGLE_REGISTER:   // возвратилась функция 6.
        case FORCE_MULTIPLE_COILS:     // возвратилась функция 15.
        case PRESET_MULTIPLE_REGISTERS:// возвратилась функция 16.
        process_F5_F6_F15_F16();       // вызов функции записи 5, 6, 15 и 16.
        break;
        default: // возвратилась недопустимая функция (у нас такой нет или ошибка)
        processError();
        break;
      }
    }
  }
  else // контрольная сумма неправильная
  {
    processError();
  }
}
//********************************************************************************************
// Функции чтения 1 и 2
void process_F1_F2()
{
  // packet->data для функции 1 и 2 фактически число булевых точек(количево бит)
  unsigned char no_of_registers = packet->data / 16;
  unsigned char number_of_bytes = no_of_registers * 2;
  // if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
  if (packet->data % 16 > 0)
  {
    no_of_registers++;
    number_of_bytes++;
  }

  if (frame[2] == number_of_bytes) // контрольное число байтов возвратилось
  {
    unsigned char bytes_processed = 0;
    unsigned char index = 3;       // запустим с 4го элемента массива во фрейме и объедините Lo байт
    unsigned int temp;
    for (unsigned char i = 0; i < no_of_registers; i++)
    {
      temp = frame[index];
      bytes_processed++;
      if (bytes_processed < number_of_bytes)
      {
	temp = (frame[index + 1] << 8) | temp;
        bytes_processed++;
        index += 2;
      }
      register_array[packet->local_start_address + i] = temp;
    }
    processSuccess();
  }
  else // возвратилось неправильное количесво байтов
    processError();
}
//***********************************************************************
// Функции чтения 3 и 4
void process_F3_F4()
{
  // контрольное число байт возвратилось - unsigned int == 2 байта
  // данные для функции 3 и 4 это яколичесво регистров
  if (frame[2] == (packet->data * 2))
  {
    unsigned char index = 3;
    for (unsigned char i = 0; i < packet->data; i++)
    {
      // запустим с 4го элемента массива фрейме и объединим с Lo байтом
      register_array[packet->local_start_address + i] = (frame[index] << 8) | frame[index + 1];
      index += 2;
    }
    processSuccess();
  }
  else // возвратилось неправильное количесво байтов
    processError();
}
//*******************************************************************************
// Функции записи 5, 6, 15 и 16
void process_F5_F6_F15_F16()
{
  // Ответ функций 5,6,15 и 16 являются просто эхом запроса
  unsigned int recieved_address = ((frame[2] << 8) | frame[3]);
  unsigned int recieved_data = ((frame[4] << 8) | frame[5]);
  if ((recieved_address == packet->address) && (recieved_data == packet->data))
    processSuccess();
  else
    processError();
}
//*******************************************************************************
// Обработка ошибок
void processError()
{
	packet->retries++;         // Увеличим на 1 в структуре packet перемнную retries (повторной попытки)
	packet->failed_requests++; // Увеличим на 1 в структуре packet перемнную failed_requests (неудавшиеся запросы)
	// если количесво повторных запросов достигло макс. допустимого значения то
	// прекращаем запрашивать определенный пакет.
  if (packet->retries == retry_count)
	{
           packet->connection = 0;// Обнулим в структуре packet перемнную connection (подключения)
	   packet->retries = 0;   // Обнулим в структуре packet перемнную retries (повторной попытки)
	}
	state = WAITING_FOR_TURNAROUND;// Установим состояние "Ожидания благоприятного состояния".
	delayStart = millis();         // Запустим задержку цикла (обновим)
}
//*************************************************************************************************
// Успех процесса
void processSuccess()
{
	packet->successful_requests++; // транзакция выполнена успешно
	packet->retries = 0;           // если запрос был успешным сбросим счетчик повторной попытки (retry counter)
	state = WAITING_FOR_TURNAROUND;
	delayStart = millis();         // Запустим задержку цикла
}
//************************************************************************************************
  // Настроим модбус

//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!тут!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, 
unsigned char _retry_count, unsigned char _TxEnablePin, Packet* _packets, unsigned int _total_no_of_packets, unsigned int* _register_array)
{
	if (_baud > 19200)
		T1_5 = 750;
	else
		T1_5 = 16500000/_baud; // 1T * 1.5 = T1.5

	frameDelay = T1_5 * 2; // задержка фрейма
	// Инициализация переменных
	state = IDLE;
        timeout = _timeout;
        polling = _polling;
	retry_count = _retry_count;
	TxEnablePin = _TxEnablePin;
	total_no_of_packets = _total_no_of_packets;
	//packetArray = _packets;
	register_array = _register_array;
	ModbusPort = SerialPort;
	(*ModbusPort).begin(_baud, _byteFormat); // Настроим скорость обмена по UART и формат
	pinMode(_TxEnablePin, OUTPUT);          // Настроим pin управления микросхемой MAX485 как выход
        digitalWrite(_TxEnablePin, LOW);
}
//**************************************************************
// Создадим пакет модбус
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!тут!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
void modbus_construct(Packet* _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address)
{
  _packet->id = _id;
  _packet->function = _function;
  _packet->address = _address;
  _packet->data = _data;
  _packet->local_start_address = _local_start_address;
  _packet->connection = 1; 
}
//********************************************************************************************************
// Считам контрольную сумму CRC16
unsigned int calculateCRC(unsigned char bufferSize)
{
  unsigned int temp, temp2, flag;
  temp = 0xFFFF;
  for (unsigned char i = 0; i < bufferSize; i++)
  {
    temp = temp ^ frame[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;
 // возвращенное значение уже поменяна местами,
 // crcLo байт первый, & crcHi байт является последним.
  return temp;
}
//****************************************************************************
// Отправим пакет модбус
void sendPacket(unsigned char bufferSize)
{
	  digitalWrite(TxEnablePin, HIGH);  // Установим микросхему MAX485 в режим передачи
	  // Выполняем в цикле пока не опустошим весь буфер
	  for (unsigned char i = 0; i < bufferSize; i++)(*ModbusPort).write(frame[i]);
	 (*ModbusPort).flush();           // очистим буфер
	 delayMicroseconds(frameDelay);   // задержка фрейма
	 digitalWrite(TxEnablePin, LOW);  // Установим микросхему MAX485 в режим приёма
	 delayStart = millis();           // начнём отсчёт задержки таймаута.
}
//***************************************************************************
void setup()
{
// Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
 modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS,    0, 1, 0); // чтение данных slave-master (slave адрес 1, регистр 0) 
// modbus_construct(&packets[PACKET2], 1, READ_HOLDING_REGISTERS,    1, 1, 1); // чтение данных slave-master (slave адрес 1, регистр 1)
// Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра.
// modbus_construct(&packets[PACKET3], 1, PRESET_MULTIPLE_REGISTERS, 2, 1, 2); // запись данных master-slave (slave адрес 1, регистр 2)
// modbus_construct(&packets[PACKET4], 1, PRESET_MULTIPLE_REGISTERS, 3, 1, 3); // запись данных master-slave (slave адрес 1, регистр 3)
 
 // инициализируем протокол модбус
modbus_configure(&Serial, baud_1, SERIAL_8N1, timeout_1, polling_1, retry_count_1, TxEnablePin_1, packets, TOTAL_NO_OF_PACKETS, regs);
} // конец void setup()

void loop()
{
  modbus_update(); // запуск обмена по Modbus
} // конец void loop()


/////////////////////////////////////////////////////////////////////////////////////////////
Сообщения компилятора
D:\Program Files\arduino-1.5.2\hardware\tools\avr\bin\avr-g++ -c -g -Os -w -fno-exceptions -ffunction-sections -fdata-sections -MMD -mmcu=atmega2560 -DF_CPU=16000000L -DARDUINO=152 -ID:\Program Files\arduino-1.5.2\hardware\arduino\avr\cores\arduino -ID:\Program Files\arduino-1.5.2\hardware\arduino\avr\variants\mega C:\Temp\build3496320038438604186.tmp\SimpleModbusMaster_v07.cpp -o C:\Temp\build3496320038438604186.tmp\SimpleModbusMaster_v07.cpp.o 
SimpleModbusMaster_v07:29: error: 'Packet' has not been declared

Вот еще прикол закоментировал функцию

void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, 
unsigned char _retry_count, unsigned char _TxEnablePin, Packet* _packets, unsigned int _total_no_of_packets, unsigned int* _register_array)

В 433 строке и в 516 и код скомпилировался может какаято ошибка в этой функции

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Блин, разобрался! Вот ведь знал, что IDE - говно, но что такое! … серия непечатных слов …

Это не Ваша вина - это ошибка IDE. Давайте, я сначала объясню суть ошибки, а потом расскажу как её побороть.

Суть ошибки

В нормальном С/С++ функция обязана быть определена (объявлена) до того, как она впервые вызывается. Это понятно, т.к. при вызове компилятор должен иметь возможность проверить параметры и т.п. Так вот это коз нехорошие люди - разработчики IDE решили «облегчить жизнь новичкам» … серия непечатных слов … и позволили нам с Вами определять функции в любом порядке, а их препроцессор перед компиляцией программы тупо собирает все объявления функций и помещает их в самое начало кода, чтобы типа при любом вызове, определения оказались выше вызова!

Ну, и что получается? У Вас есть две функции modbus_construct и modbus_configure в параметрах которых встречается тип Packet. Они у Вас объявлены в строках 77 и 78, т.е.  ПОСЛЕ того, как тип Packet уже определён. И это правильно! Но когда эти муда нехорошие люди собирают все объявления и насильно перетаскивают их в самое начало программы – они оказываются ДО объявления типа и компилятор, разумеется, ругается … серия непечатных слов ….

Как с этим бороться?

Возможно, ему (препроцессору) можно какой-то хитрой командой сказать, чтобы он не мчудил, но я его команд не знаю, поэтому я покажу Вам как избавиться от типов Packet в параметрах этих двух функций так, чтобы изменения в программе были минимальными и абсолютно ничего в ней не поменялось.

Делаем аккуратно - не ошибаемся.

1. В строках 77 и 78 заменяем свово Packet (оно там встречается два раза) на слово void. Строки получатся:

void modbus_construct(void * _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address);
void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, unsigned char _retry_count, unsigned char _TxEnablePin, void * _packets, unsigned int _total_no_of_packets, unsigned int* _register_array);

2. В строке 434 заменяем выражение "Packet* _packets" на "void *_packets1". Должно получиться

unsigned char _retry_count, unsigned char _TxEnablePin, void * _packets1, unsigned int _total_no_of_packets, unsigned int* _register_array)

3. После строки 435 вставляем строку:

Packet * _packets = (Packet *) _packets1;

4. В строке 459 заменяем выражение "Packet* _packet" на "void * _packet1". Должно получиться:

void modbus_construct(void * _packet1, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address)

5. После строки 460 вставляем строку:

Packet * _packet = (Packet *) _packet1;

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

Если что-то не так, скажите, я могу просто выложить Вам нормально компилируемый код.

 

alexval2007
Offline
Зарегистрирован: 10.11.2012

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

Вот исправленный код завра проверю в железе.

Может подскажете как мне реализовать чтобы modbus запросы шли не в цикле а однократно пришла команда пусть будет с кнопки дало 1 запрос получило ответ все тишина ждем следующего. Хочу использовать скетч с кодом модбус TCP и реализовать модбус шлюз но для нормальной его работы нужно слать по одному запросу инициализируемому кодом модбус TCP.


//SimpleModbusMaster
#include "HardwareSerial.h"
//////////////////// Макроопределения портов и настройки программы  ///////////////////
// state machine states
#define IDLE                   1
#define WAITING_FOR_REPLY      2
#define WAITING_FOR_TURNAROUND 3
#define BUFFER_SIZE           64   // размер буфера
#define baud_1                9600 // скорость обмена по последовательному интерфейсу. (UART)
#define timeout_1             1000 // Длительность ожидание ответа (таймаут modbus)
#define polling_1             200  // скорость опроса по modbus
#define retry_count_1         10   // количество запросов modbus до ошибки и останова обмена 
#define TxEnablePin_1         2    // Tx/Rx пин RS485
#define TOTAL_NO_OF_REGISTERS 4
//******************************************************************************
#define COIL_OFF 0x0000              // Function 5 OFF request is 0x0000
#define COIL_ON 0xFF00               // Function 5 ON request is 0xFF00
#define READ_COIL_STATUS 1           // Reads the ON/OFF status of discrete outputs (0X references, coils) in the slave.
#define READ_INPUT_STATUS 2          // Reads the ON/OFF status of discrete inputs (1X references) in the slave.
#define READ_HOLDING_REGISTERS 3     // Reads the binary contents of holding registers (4X references) in the slave.
#define READ_INPUT_REGISTERS 4       // Reads the binary contents of input registers (3X references) in the slave. Not writable.
#define FORCE_SINGLE_COIL 5          // Forces a single coil (0X reference) to either ON (0xFF00) or OFF (0x0000).
#define PRESET_SINGLE_REGISTER 6     // Presets a value into a single holding register (4X reference).
#define FORCE_MULTIPLE_COILS 15      // Forces each coil (0X reference) in a sequence of coils to either ON or OFF.
#define PRESET_MULTIPLE_REGISTERS 16 // Presets values into a sequence of holding registers (4X references).
//******************************************************************************
unsigned char state;
unsigned char retry_count;
unsigned char TxEnablePin;
unsigned char buffer;
long timeout;                 // интервал таймаута(timeout interval)
long polling;                 // циклический интервал задержки (turnaround delay interval)
unsigned int T1_5;            // inter character time out in microseconds
unsigned int frameDelay;      // frame time out in microseconds
long delayStart;              // init variable for turnaround and timeout delay
unsigned int total_no_of_packets;
//******************************************************************************
class Packet {
  public:
  // Специальная пакетная информация
  unsigned char id;
  unsigned char function;
  unsigned int  address;
  unsigned int  data;
  unsigned int  local_start_address;
  // счетчики информации о modbus
  unsigned int  requests;
  unsigned int  successful_requests;
  unsigned int  failed_requests;
  unsigned int  exception_errors;
  unsigned int  retries;
  //******************************
  unsigned char connection; // состояние соединения пакета
};

// Объявим функции
void modbus_update();
void modbus_construct(void * _packet, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address);
void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, unsigned char _retry_count, unsigned char _TxEnablePin, void * _packets, unsigned int _total_no_of_packets, unsigned int* _register_array);


Packet* packetArray;          // начальный адрес пакета
Packet* packet;               // текущий пакет
unsigned int* register_array; // указатель на массив регистров в master
HardwareSerial* ModbusPort;

// Масив пакетов modbus
enum
{
  PACKET1,
  PACKET2,
  PACKET3,
  PACKET4,
  TOTAL_NO_OF_PACKETS // эту строку не менять
};

unsigned char frame[BUFFER_SIZE];
Packet packets[TOTAL_NO_OF_PACKETS];     // Массив пакетов модбус
unsigned int regs[TOTAL_NO_OF_REGISTERS];// Массив хранения содержимого принятых и передающихся регистров
//********************************************************************************************************
// Объявим функции
void idle();
void constructPacket();
unsigned char construct_F15();
unsigned char construct_F16();
void waiting_for_reply();
void processReply();
void waiting_for_turnaround();
void process_F1_F2();
void process_F3_F4();
void process_F5_F6_F15_F16();
void processError();
void processSuccess();
unsigned int calculateCRC(unsigned char bufferSize);
void sendPacket(unsigned char bufferSize);
//********************************************************************************************************
// Modbus Master State Machine (конечный автомат)
void modbus_update() // Функция обновления пакетов модбус
{
	switch (state)
	{
		case IDLE:
		idle();
		break;
		case WAITING_FOR_REPLY:
		waiting_for_reply();// Задержка для ответа
		break;
		case WAITING_FOR_TURNAROUND:
		waiting_for_turnaround(); // Задержка для цикла работы
		break;
	}
}
//**********************************************************************************************************
void idle()
{
static unsigned int packet_index;
unsigned int failed_connections = 0;
unsigned char current_connection;
	do
	{
		if (packet_index == total_no_of_packets) // wrap around to the beginning (повторитесь к началу)
		packet_index = 0;
		// proceed to the next packet (продолжите к следующему пакету)
		packet = &packetArray[packet_index];

		// get the current connection status (получите текущее состояние соединения)
		current_connection = packet->connection;
		if (!current_connection)
		{
		  // Если все атрибуты соединения - False то возврат
		  // сразу к основной программе
		  if (++failed_connections == total_no_of_packets) return;
		}
		packet_index++;
        // если у пакета нет соединения, получают следующий
	// if a packet has no connection get the next one
	}while (!current_connection);
	constructPacket();
}
//******************************************************************************************************************
// создадим пакет
void constructPacket()
{
  packet->requests++;
  frame[0] = packet->id;
  frame[1] = packet->function;
  frame[2] = packet->address >> 8;   // address Hi
  frame[3] = packet->address & 0xFF; // address Lo
  // Включайте их данные в регистр данных а не в master массив
  if (packet->function == FORCE_SINGLE_COIL || packet->function == PRESET_SINGLE_REGISTER)
  packet->data = register_array[packet->local_start_address]; // получим данные
  frame[4] = packet->data >> 8;   // MSB
  frame[5] = packet->data & 0xFF; // LSB
  unsigned char frameSize;

  // создайте фрейм согласно функциям modbus
  if (packet->function == PRESET_MULTIPLE_REGISTERS)
		frameSize = construct_F16();
	else if (packet->function == FORCE_MULTIPLE_COILS)
		frameSize = construct_F15();
	else           // иначе функции 1,2,3,4,5 и 6 приняты.
	               // Они все совместно используют тот же самый формат запроса.
  frameSize = 8; // размер запроса всегда - 8 байт для вышеупомянутых функций.
  unsigned int crc16 = calculateCRC(frameSize - 2);
  frame[frameSize - 2] = crc16 >> 8; // разделение crc на 2 байта
  frame[frameSize - 1] = crc16 & 0xFF;
  sendPacket(frameSize);
  state = WAITING_FOR_REPLY; // изменение состояния на ожидание для ответа
  // Если передана широковещательная посылка требуется установить (id == 0) для функций 5,6,15 и 16 тогда переопределение
  // the previous state and force a success since the slave wont respond
  // предыдущее состояние и вызов успешно начиная с slave хотеть отвечать
	if (packet->id == 0)processSuccess();
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////
unsigned char construct_F15()
{
  // функция 15 coil информации - упаковать LSB сначала, пока первые 16 битов не обработаны
  // Это получено тем же путём..
  unsigned char no_of_registers=0;
  unsigned char no_of_bytes=0;
  no_of_registers = packet->data / 16;
  no_of_bytes = no_of_registers * 2;
  // if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
  // если число бит не помещается даже в 2х-байтовые суммы (один регистр) тогда используют другой регистр и клавиатуру
  if (packet->data % 16 > 0)
  {
    no_of_registers++;
    no_of_bytes++;
  }
  frame[6] = no_of_bytes;
  unsigned char bytes_processed = 0;
  unsigned char index = 7; // user data starts at index 7 (пользовательские данные запускаются по индексу 7)
  unsigned int temp;
  for (unsigned char i = 0; i < no_of_registers; i++)
  {
    temp = register_array[packet->local_start_address + i]; // получим данные
    frame[index] = temp & 0xFF;
    bytes_processed++;

    if (bytes_processed < no_of_bytes)
    {
      frame[index + 1] = temp >> 8;
      bytes_processed++;
      index += 2;
    }
  }
unsigned char frameSize = (9 + no_of_bytes); // первые 7 байт массива + 2 байта CRC + noOfBytes
return frameSize;
}
//*********************************************************************
unsigned char construct_F16()
{
  unsigned char no_of_bytes = packet->data * 2;
  //следующий 6 байт из массива + no_of_bytes + 2 байта контрольной суммы
  frame[6] = no_of_bytes;   // количество байт
  unsigned char index = 7; // пользовательские данные запускаются по индексу 7
  unsigned char no_of_registers = packet->data;
  unsigned int temp;
  for (unsigned char i = 0; i < no_of_registers; i++)
  {
    temp = register_array[packet->local_start_address + i]; // get the data
    frame[index] = temp >> 8;
    index++;
    frame[index] = temp & 0xFF;
    index++;
  }
  unsigned char frameSize = (9 + no_of_bytes); // следующий 7 байт из массива + 2 байта контрольной суммы + no_of_bytes
  return frameSize;
}
//**************************************************************************
void waiting_for_turnaround()
{
  if ((millis() - delayStart) > polling) state = IDLE;
}
//***************************************************************************
// получите последовательные данные из буфера
void waiting_for_reply()
{
	if ((*ModbusPort).available()) // Что то есть в буфере?, проверим
	{
		unsigned char overflowFlag = 0;
		buffer = 0;
		while ((*ModbusPort).available())
		{
  	        // Максимальное количество байт ограничено размером Serial буфера
                // в переменной BUFFER_SIZE. Если получено байт больше чем записано в переменной BUFFER_SIZE
                // то будет установлен флаг переполнения, и Serial буфер будет прочитан то
                // все данные в буфере будут удалены, в то время как slave устройство еще не ответило.
		   if (overflowFlag)(*ModbusPort).read();
		   else
		   {
		      if (buffer == BUFFER_SIZE) overflowFlag = 1;
		      frame[buffer] = (*ModbusPort).read();
		      buffer++;
		   }
               // Это не на 100% правильно, но это работает.
               // худший вариант развития событий - это когда больше чем один символьный интервал истёк
               // при чтении из буфера когда буфер наиболее вероятно пустой
               // Если есть больше байт после такой задержки, и они
               // будут приняты то это вызовет frame_error.
	       delayMicroseconds(T1_5); // таймаут для передачи одного символа
	       // Минимальный размер буфера slave устройства должен быть не менее
               // 5 байт. Если буфер был частично заполнен, устанавливается флаг frame_error.
               // Максимальное количество байт в modbus пакете составляет 256 байт.
               // Но Serial буфер ограничивает эго 64 байтами.
		}
		if ((buffer < 5) || overflowFlag)// Проверяем буфер на переполнение
			processError();

	   // Modbus по таблице данных последовательной линии утверждает что если неожиданный slave устройство
           // ответило master устройству ничего не должно делать и подождать время.
           // Это кажется глупой причиной, если бы неправильный slave устройство ответило, то вы хотели бы
           // имейте быстрый благоприятный поворот и опросите правильный снова. Если неожиданное
           // slave устройство ответило, это скорее всего будет ошибка фрейма при любых обстоятельствах

	   // Modbus over serial line datasheet states that if an unexpected slave
           // responded the master must do nothing and continue with the time out.
	   // This seems silly cause if an incorrect slave responded you would want to
           // have a quick turnaround and poll the right one again. If an unexpected
           // slave responded it will most likely be a frame error in any event
		else if (frame[0] != packet->id) // проверим возврат id.
			processError();
		else
			processReply();
	}
	else if ((millis() - delayStart) > timeout) // проверьте тайм-аут
	{
		processError();
		state = IDLE; // изменение состояния, переопределение processError() состояние
		              // state change, override processError() state
	}
}
//************************************************************************
void processReply()
{
  // объединить crc Low и High байты.
  unsigned int received_crc = ((frame[buffer - 2] << 8) | frame[buffer - 1]);
  unsigned int calculated_crc = calculateCRC(buffer - 2);
  if (calculated_crc == received_crc) // проверим контрольную сумму.
  {
	// Чтобы указать ответ исключения, slave устройство будет 'ИЛИ'
        // запрошенная функция с 0x80
	// To indicate an exception response a slave will 'OR'
	// the requested function with 0x80
     if ((frame[1] & 0x80) == 0x80) // извлечение 0x80
     {
      packet->exception_errors++;
      processError();
     }
     else
     {
       switch (frame[1]) // проверьте, что функция возвратилась
       {
        case READ_COIL_STATUS:         // возвратилась функция 1.
        case READ_INPUT_STATUS:        // возвратилась функция 2.
        process_F1_F2();               // вызов функции чтения  1, 2.
        break;
        case READ_INPUT_REGISTERS:     // возвратилась функция 4.
        case READ_HOLDING_REGISTERS:   // возвратилась функция 3.
        process_F3_F4();               // вызов функции чтения  3, 4.
        break;
	case FORCE_SINGLE_COIL:        // возвратилась функция 5.
	case PRESET_SINGLE_REGISTER:   // возвратилась функция 6.
        case FORCE_MULTIPLE_COILS:     // возвратилась функция 15.
        case PRESET_MULTIPLE_REGISTERS:// возвратилась функция 16.
        process_F5_F6_F15_F16();       // вызов функции записи 5, 6, 15 и 16.
        break;
        default: // возвратилась недопустимая функция (у нас такой нет или ошибка)
        processError();
        break;
      }
    }
  }
  else // контрольная сумма неправильная
  {
    processError();
  }
}
//********************************************************************************************
// Функции чтения 1 и 2
void process_F1_F2()
{
  // packet->data для функции 1 и 2 фактически число булевых точек(количество бит)
  unsigned char no_of_registers = packet->data / 16;
  unsigned char number_of_bytes = no_of_registers * 2;
  // if the number of points dont fit in even 2byte amounts (one register) then use another register and pad
  if (packet->data % 16 > 0)
  {
    no_of_registers++;
    number_of_bytes++;
  }

  if (frame[2] == number_of_bytes) // контрольное число байтов возвратилось
  {
    unsigned char bytes_processed = 0;
    unsigned char index = 3;       // запустим с 4го элемента массива во фрейме и объедините Lo байт
    unsigned int temp;
    for (unsigned char i = 0; i < no_of_registers; i++)
    {
      temp = frame[index];
      bytes_processed++;
      if (bytes_processed < number_of_bytes)
      {
	temp = (frame[index + 1] << 8) | temp;
        bytes_processed++;
        index += 2;
      }
      register_array[packet->local_start_address + i] = temp;
    }
    processSuccess();
  }
  else // возвратилось неправильное количество байтов
    processError();
}
//***********************************************************************
// Функции чтения 3 и 4
void process_F3_F4()
{
  // контрольное число байт возвратилось - unsigned int == 2 байта
  // данные для функции 3 и 4 это количество регистров
  if (frame[2] == (packet->data * 2))
  {
    unsigned char index = 3;
    for (unsigned char i = 0; i < packet->data; i++)
    {
      // запустим с 4го элемента массива фрейме и объединим с Lo байтом
      register_array[packet->local_start_address + i] = (frame[index] << 8) | frame[index + 1];
      index += 2;
    }
    processSuccess();
  }
  else // возвратилось неправильное количество байт
    processError();
}
//*******************************************************************************
// Функции записи 5, 6, 15 и 16
void process_F5_F6_F15_F16()
{
  // Ответ функций 5,6,15 и 16 являются просто эхом запроса
  unsigned int recieved_address = ((frame[2] << 8) | frame[3]);
  unsigned int recieved_data = ((frame[4] << 8) | frame[5]);
  if ((recieved_address == packet->address) && (recieved_data == packet->data))
    processSuccess();
  else
    processError();
}
//*******************************************************************************
// Обработка ошибок
void processError()
{
    packet->retries++;         // Увеличим на 1 в структуре packet переменную retries (повторной попытки)
    packet->failed_requests++; // Увеличим на 1 в структуре packet переменную failed_requests (неудавшиеся запросы)
    // если количество повторных запросов достигло макс. допустимого значения то
    // прекращаем запрашивать определенный пакет.
    if (packet->retries == retry_count)
    {
        packet->connection = 0;    // Обнулим в структуре packet переменную connection (подключения)
	packet->retries = 0;       // Обнулим в структуре packet переменную retries (повторной попытки)
    }
    state = WAITING_FOR_TURNAROUND;// Установим состояние "Ожидания благоприятного состояния".
    delayStart = millis();         // Запустим задержку цикла (обновим)
}
//*************************************************************************************************
// Успех процесса
void processSuccess()
{
	packet->successful_requests++; // транзакция выполнена успешно
	packet->retries = 0;           // если запрос был успешным сбросим счетчик повторной попытки (retry counter)
	state = WAITING_FOR_TURNAROUND;
	delayStart = millis();         // Запустим задержку цикла
}
//************************************************************************************************
// Настроим модбус
void modbus_configure(HardwareSerial* SerialPort, long _baud, unsigned char _byteFormat, long _timeout, long _polling, 
unsigned char _retry_count, unsigned char _TxEnablePin, void *_packets1, unsigned int _total_no_of_packets, unsigned int* _register_array)
{
 // Стандарт Модбус требует чтобы скорость в бодах выше, чем 19200 должна использовать фиксированные 750 мкс
 // для передачи символа, используют таймаут и 1.75 мс для задержки фрейма скоростей
 // ниже 19200 бод синхронизация более критически важна и должна быть вычислена.
 // Например, 9600 бод в пакете на 11 байт - 9600/11 = 872 символа в секунду
 // В миллисекундах это будет 872 символами за 1000 мс. Таким образом для 1 символа
 // 1000/872 = 1.14583 мс за символ и наконец modbus состояния
 // меж символьный интервал должен быть 1.5T или в 1.5 раза больше, чем символ. Таким образом
 // 1.5T = 1.14583 мс * 1.5 = 1.71875 мс. Задержка фрейма 3.5T.
 // Таким образом формула - T1.5 (мкс) = (1000 мс * 1000 (мкс) * 1.5 * 11 битов) / бод
 // 1000 мс * 1000 (мкс) * 1.5 * 11 бит = 16500000 может быть вычислена как константа
        Packet * _packets = (Packet *) _packets1;
        
	if (_baud > 19200)
		T1_5 = 750;
	else
		T1_5 = 16500000/_baud; // 1T * 1.5 = T1.5
// modbus определение задержки фрейма - время ожидания 3.5 символа
// между пакетами. Это - не совсем то же самое что и frameDelay, реализованный в
// этой библиотеке. frameDelay переменная которая нужна для гарантии, что последний знак
// передан неповреждённым. Значение 2х символов должен быть достаточным,
// не тормозя обмен слишком надолго.
// Описание кадра (фрейма) протокола Modbus
// В протоколе Modbus RTU сообщение начинает восприниматься как новое после паузы (тишины)
// на шине длительностью не менее 3,5 символов (14 бит), т. е. величина паузы в секундах
// зависит от скорости передачи.
// Формат кадра представляет из себя:
// Адрес 1 байт - Код функции 1 байт - Данные от 0 до 252 байта - КС 2 байта.
// Поле адреса всегда содержит только адрес ведомого устройства, даже в ответах на команду,
// посланную ведущим. Благодаря этому ведущее устройство знает, от какого модуля пришел ответ.
// Поле «Код функции» говорит модулю о том, какое действие нужно выполнить.
// Поле «Данные» может содержать произвольное количество байт. В нем может содержаться
// информация о параметрах, используемых в запросах контроллера или ответах модуля.
// Поле «Контрольная сумма» содержит контрольную сумму CRC длиной 2 байта.
	frameDelay = T1_5 * 2; // задержка фрейма
	// Инициализация переменных
	state = IDLE;
        timeout = _timeout;
        polling = _polling;
	retry_count = _retry_count;
	TxEnablePin = _TxEnablePin;
	total_no_of_packets = _total_no_of_packets;
	packetArray = _packets;
	register_array = _register_array;
	ModbusPort = SerialPort;
	(*ModbusPort).begin(_baud, _byteFormat); // Настроим скорость обмена по UART и формат
	pinMode(TxEnablePin, OUTPUT);            // Настроим pin управления микросхемой MAX485 как выход
        digitalWrite(TxEnablePin, LOW);
}
//**************************************************************
// Создадим пакет модбус
void modbus_construct(void * _packet1, unsigned char _id, unsigned char _function, unsigned int _address, unsigned int _data, unsigned int _local_start_address)
{
  Packet * _packet = (Packet *) _packet1;
  _packet->id = _id;
  _packet->function = _function;
  _packet->address = _address;
  _packet->data = _data;
  _packet->local_start_address = _local_start_address;
  _packet->connection = 1; 
}
//********************************************************************************************************
// Считаем контрольную сумму CRC16
unsigned int calculateCRC(unsigned char bufferSize)
{
  unsigned int temp, temp2, flag;
  temp = 0xFFFF;
  for (unsigned char i = 0; i < bufferSize; i++)
  {
    temp = temp ^ frame[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;
 // возвращенное значение уже поменяна местами,
 // crcLo байт первый, & crcHi байт является последним.
  return temp;
}
//****************************************************************************
// Отправим пакет модбус
void sendPacket(unsigned char bufferSize)
{
	 digitalWrite(TxEnablePin, HIGH);  // Установим микросхему MAX485 в режим передачи
	 // Выполняем в цикле пока не опустошим весь буфер
	 for (unsigned char i = 0; i < bufferSize; i++)(*ModbusPort).write(frame[i]);
	 (*ModbusPort).flush();           // очистим буфер
	 delayMicroseconds(frameDelay);   // задержка фрейма
	 digitalWrite(TxEnablePin, LOW);  // Установим микросхему MAX485 в режим приёма
	 delayStart = millis();           // начнём отсчёт задержки таймаута.
}
//***************************************************************************
void setup()
{
// Пакет,SLAVE адрес,функция модбус,адрес регистра, количество запрашиваемых регистров,локальный адрес регистра.
modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS,    0, 1, 0); // чтение данных slave-master (slave адрес 1, регистр 0) 
// modbus_construct(&packets[PACKET2], 1, READ_HOLDING_REGISTERS,    1, 1, 1); // чтение данных slave-master (slave адрес 1, регистр 1)
// Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра.
// modbus_construct(&packets[PACKET3], 1, PRESET_MULTIPLE_REGISTERS, 2, 1, 2); // запись данных master-slave (slave адрес 1, регистр 2)
// modbus_construct(&packets[PACKET4], 1, PRESET_MULTIPLE_REGISTERS, 3, 1, 3); // запись данных master-slave (slave адрес 1, регистр 3)
 
// инициализируем протокол модбус
modbus_configure(&Serial, baud_1, SERIAL_8N1, timeout_1, polling_1, retry_count_1, TxEnablePin_1, packets, TOTAL_NO_OF_PACKETS, regs);
} // конец void setup()

void loop()
{
  modbus_update(); // запуск обмена по Modbus
} // конец void loop()
OlegN
Offline
Зарегистрирован: 25.01.2016

Евгений день добрый! Помогите такому чуду как мне, проблема в том, что не могу записать данные в slave, читать вроде методом тыка добился, а вот занести не могу понять как ...

получается манипуляцией последних двух значений (int (&data), 1) либо записать 0 или 770 или 1280.

спасибо.

 

#include <SimpleModbusMaster.h>
#define TxEnablePin 2
#define TOTAL_NO_OF_REGISTERS 1
#define _local_start_address 1
int data=90;
enum
{ PACKET1,
  PACKET2,
  TOTAL_NO_OF_PACKETS
};
Packet packets[TOTAL_NO_OF_PACKETS];
unsigned int regs[TOTAL_NO_OF_REGISTERS];
int reading(int r)
{ delay(200);
  modbus_construct(&packets[PACKET1], 2, 3, r, 1, 0);
  modbus_update();
  return Serial.println(regs[0]);
}
void setup()
{
 modbus_configure(&Serial1, 4800, SERIAL_8N2, 1000, 200, 5, TxEnablePin , packets, TOTAL_NO_OF_PACKETS, regs);
  Serial.begin(9600);
}

void loop()

{
 //EEPROM.write(0,9);
 //reading(0x0100);

 reading(0x0501);
delay(100);
    modbus_construct(&packets[PACKET2], 2, 6, 0x0501, int (&data), 1);
  modbus_update();
Serial.println(int (&data)); 
}
 
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Олег, это не ко мне. Я помог человеку с синтаксисом языка, а в логике работы модбаса я не компетентен. Сожалею.

OlegN
Offline
Зарегистрирован: 25.01.2016

будем искать...

Alexey-kipia
Alexey-kipia аватар
Offline
Зарегистрирован: 14.03.2016

OlegN пишет:

будем искать...

Привет! Вот эту библиотеку не смотрели?

https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino

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

OlegN
Offline
Зарегистрирован: 25.01.2016

Спасибо, я тоже нашел эту библиотеку и с успехом пользуюсь.

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

кто может помочь по этой библиотеке? пол дня просидел так и не понял как с ней рааботать, в примерах нет пина для переключения говорю или молчу, не понятно как вывести инфу в монитор порта с ошибками как дело обстоит и как их считывать

зараннее благодарен за помощ!

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

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

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

один мастер несколько слейвов работать будут через микросхему max...  

для начала передачи с ммастера нужно подать 5в на 2 контакта модуля кы485 соединенные перемычкой - куда в примере вставить код подачи 5в ? и где подать 0в чтобы начать прослушку ответа...

/**
 *  Modbus master example 2:
 *  The purpose of this example is to query several sets 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>

uint16_t au16data[16]; //!< data array for modbus network sharing
uint8_t u8state; //!< machine state
uint8_t u8query; //!< pointer to message query

/**
 *  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[2];

unsigned long u32wait;

void setup() {
  // telegram 0: read registers
  telegram[0].u8id = 1; // slave address
  telegram[0].u8fct = 3; // function code (this one is registers read)
  telegram[0].u16RegAdd = 0; // start address in slave
  telegram[0].u16CoilsNo = 4; // 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 = 1; // slave address
  telegram[1].u8fct = 6; // function code (this one is write a single register)
  telegram[1].u16RegAdd = 4; // start address in slave
  telegram[1].u16CoilsNo = 1; // number of elements (coils or registers) to read
  telegram[1].au16reg = au16data+4; // pointer to a memory array in the Arduino
	
  master.begin( 19200 ); // baud-rate at 19200
  master.setTimeOut( 5000 ); // if there is no answer in 5000 ms, roll over
  u32wait = millis() + 1000;
  u8state = u8query = 0; 
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    master.query( telegram[u8query] ); // send query (only once)
    u8state++;
	u8query++;
	if (u8query > 2) u8query = 0;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 1000; 
    }
    break;
  }

  au16data[4] = analogRead( 0 );
  
}

 

alexval2007
Offline
Зарегистрирован: 10.11.2012

Смотрите примеры от библиотеки

// assign the Arduino pin that must be connected to RE-DE RS485 transceiver
#define TXEN 4
Modbus slave(1,0,TXEN); // this is slave @1 and RS-485

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

#define TXEN 4

........

Modbus master(0,0,TXEN);

Библиотека сама разберётся когда надо пин дергать. Только самому к правильному пину подключить надо - здесь D4.

 

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

коллеги понимаю что глупый вопрос но никак не пойму вот код слейва 

#include <ModbusRtu.h>

// assign the Arduino pin that must be connected to RE-DE RS485 transceiver
#define TXEN	4 

// data array for modbus network sharing
uint16_t au16data[16] = {
  3, 1415, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, -1 };
   //mySerial Serial1;  
/**
 *  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 slave(1,0,TXEN); // this is slave @1 and RS-485

void setup() {
  slave.begin( 9600 ); // baud-rate at 19200
}

void loop() {
  slave.poll( au16data, 16 );
}

к каким контактам RX TX модуль RS485 подключать?

alexval2007
Offline
Зарегистрирован: 10.11.2012

Если Modbus master(0,0,TXEN);
И TXEN 4 тогда для ардуино уно rx pin 0 tx pin1 ну а перекоючатель приема передачи pin4

OlegN
Offline
Зарегистрирован: 25.01.2016

Друзья я заранее прошу прощения что не прочитал всех постов внимательно. Но я провозился полгода назад 2 недели пока мне удалось все правильно сделать. одна из проблем была, что в библиотеки был зашит пин3 для переключения между приемом и передачей и ОБЯЗАТЕЛЬНО!!! в вашей программе в SETUP должна быть строка pinMode(3, OUTPUT) или другая определяющая выход; иначе недостаточно напряжения для переключения адаптера.

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

OlegN пишет:

Друзья я заранее прошу прощения что не прочитал всех постов внимательно. Но я провозился полгода назад 2 недели пока мне удалось все правильно сделать. одна из проблем была, что в библиотеки был зашит пин3 для переключения между приемом и передачей и ОБЯЗАТЕЛЬНО!!! в вашей программе в SETUP должна быть строка pinMode(3, OUTPUT) или другая определяющая выход; иначе недостаточно напряжения для переключения адаптера.

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

у Вас случаем тестового рабочего примера не осталось для ознакомления?

OlegN
Offline
Зарегистрирован: 25.01.2016

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

#include <ModbusRtu.h>
Modbus slave(4,0,2);
#include <i2cmaster.h>
uint16_t au16data[1];

void setup(){
	//Serial.begin(9600);
	//Serial.println("Setup...");
	slave.begin(9600);
	i2c_init(); //Initialise the i2c bus
	PORTC = (1 << PORTC4) | (1 << PORTC5);//enable pullups
}

void loop(){
    int dev = 0x5A<<1;
    int data_low = 0;
    int data_high = 0;
    int pec = 0;
    
    i2c_start_wait(dev+I2C_WRITE);
    i2c_write(0x07);
    
    // read
    i2c_rep_start(dev+I2C_READ);
    data_low = i2c_readAck(); //Read 1 byte and then send ack
    data_high = i2c_readAck(); //Read 1 byte and then send ack
    pec = i2c_readNak();
    i2c_stop();
    
    //This converts high and low bytes together and processes temperature, MSB is a error bit and is ignored for temps
    double tempFactor = 0.02; // 0.02 degrees per LSB (measurement resolution of the MLX90614)
    double tempData = 0x0000; // zero out the data
    int frac; // data past the decimal point
    
    // This masks off the error bit of the high byte, then moves it left 8 bits and adds the low byte.
    tempData = (double)(((data_high & 0x007F) << 8) + data_low);
    tempData = (tempData * tempFactor)-0.01;
    
    float celcius = (tempData - 273.15)*100;
    au16data[0]=celcius;
slave.poll( au16data, 2 );
   // Serial.print("Celcius: ");
    //Serial.println(celcius);

   // Serial.print("Fahrenheit: ");
   // Serial.println(fahrenheit);

    digitalWrite(13,HIGH);
    delay(50);
digitalWrite (13,LOW);
delay(50);
}

датчик температуры дистанционно измеряет и по запросу выдает мастеру.  

 

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

IRsens.clearResponseBuffer();
      delay(300);
      IRsens.readHoldingRegisters(0, 1);
      lcd.print (IRsens.getResponseBuffer(0));

показывает температуру обьекта. Заметь, что для мастера идет #include <ModbusMaster.h> эта библиотека, а для slave идет #include <ModbusRtu.h>

В мастере 3 пин TX и только, если не править библиотеку. для slave TXEN любой.

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

Давайте попробуем со слейвом с нуля. Загружаем скетч

/**
 *  Modbus slave example 1:
 *  The purpose of this example is to link a data array
 *  from the Arduino to an external device.
 *
 *  Recommended Modbus Master: QModbus
 *  http://qmodbus.sourceforge.net/
 */

#include <ModbusRtu.h>

#define TXEN 4
int pin = 13;
volatile int state = LOW;

// data array for modbus network sharing
uint16_t au16data[16] = {
  3, 1415, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, -1 };

/**
 *  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 slave(1,0,TXEN); // this is slave @1 and RS-232 or USB-FTDI

void setup() {
  slave.begin( 19200 ); // baud-rate at 19200
  pinMode(pin, OUTPUT);
  pinMode(TXEN, OUTPUT);


}

void loop() {
  au16data[0]=analogRead(A0);
  slave.poll( au16data, 16 );
  if(au16data[1]==0) state = LOW; else state= HIGH;
  digitalWrite(pin, state);
}

 

Скачиваем, устанавливаем, запускаем QMODBUS 0.2.1 https://sourceforge.net/projects/qmodbus/files/qmodbus/ 

Пока не подключаем RS485. Работаем с USB кабелем через который заливали скетч. 

В QMOBUS устанавливаем параметры связи. 

Очень важно! Порт должен быть 1 .. 9 , если не так, то поменять в свойствах системы.

Выбираем Function code 3, Num coils 16 и нажимаем кнопку Send. В поле Register должны появиться значения массива au16data из скетча. Несколько раз нажав кнопку Send можно видеть что регистр 0 показывает значение входа А0 ардуины.

Выбираем Function code 6, в поле Register - Data вводим ноль или единицу. Видим как на ввод нуля гаснет, а на ввод единицы зажигается светодиод на плате ардуины. 

Подключаем переходники RS485 к компьютеру и ардуине. Проверяем точно также. Если подключили правильно,то всё должно работать.

 

 

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

докладываю...

работы со слейвом - выполнил пункт 1 светодиодом поморгал по юсб подключению, пункт 2 проблематичнее нет переходника USB-rs485 кстати ттд конвекор можно использовать чтобы подсмотретьинформацию сети RS485?

таким https://ru.aliexpress.com/item/1PCS-USB-to-TTL-UART-Module-CH340G-CH340-3-3V-5V-Serial-Converter-Switch-Instead-of/1856263846.html

работа с мастером -  залил пример из библиотеки и подключил к 2 управляющим контактам модуля (прием передача) на 1 контакт 5в а на второй GDN. отправляю 0 получаю 0, поставил в условии отправки inc(), переменная стала увеличиваться при каждом получении.

далее подключил перемычку между управляющими контактами модуля и подключл их к пину D3 увидил ту же картинку, получаемое число увеличивается... следовательно мастер работает нормально.

вечером опыты продолжу и отпишусь...

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

Переходники в картинках гугля по запросу USB RS485.
Для ардуины можно такие http://arduino.ru/forum/apparatnye-voprosy/rs485-module-ne-schityvaet-da...
Если смотреть на трафик сети RS485 то на доподнительном USB-RS485 aдаптере с помощью любой терминальной программы, которая может показывать то что приходит на COM порт в HEX формате.

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

И так нужна помощ вот в чем, есть 2 скетса мастер и слейв

мастер

#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(3, 5);//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, 19200 ); // 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 = 4; // function code (this one is registers read)
    telegram.u16RegAdd = 3; // start address in slave
    telegram.u16CoilsNo = 1; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino  
    au16data[0] = 0;

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

 

слейв тоже из примеров библиотеки


#include <ModbusRtu.h>

#define TXEN 4
int pin = 13;
volatile int state = LOW;

// data array for modbus network sharing
uint16_t au16data[16] = {
  3, 1415, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, -1 };

/**
 *  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 slave(1,0,TXEN); // this is slave @1 and RS-232 or USB-FTDI

void setup() {
  slave.begin( 19200 ); // baud-rate at 19200
  pinMode(pin, OUTPUT);
  pinMode(TXEN, OUTPUT);


}

void loop() {
  au16data[0]=analogRead(A0);
  slave.poll( au16data, 16 );
  if(au16data[1]==0) state = LOW; else state= HIGH;
  digitalWrite(pin, state);
}

 

и теперь вот что из этого вышло 

Посылки от мастера не моргают светодеодом. Насколько я понимаю для работы слейва (включить светодеод на 13 пине) нужно чтобы мастер одинаковые команды выдавал (с терминалом).  так подскажите что нужно сделать чтобы привести это все к одинаковому виду, да еще чтобы и работало)))

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

Телеграмму надо правильно инициировать.
Посмотрите:
Функцональный код - должен быть 6.
Старовый адрес - 1
Количество байт - 1
Нулевой элемент массива = 1 для зажигания, =0 - погасить.

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

Как правильно инициировать телеграмму? вроди все из примера взято однако не стыкуется папа с мамой?)))

nik182
Offline
Зарегистрирован: 04.05.2015
Было 
	     telegram.u8id = 1; // slave address
	    telegram.u8fct = 4; // function code (this one is registers read)
	    telegram.u16RegAdd = 3; // start address in slave
	    telegram.u16CoilsNo = 1; // number of elements (coils or registers) to read
	    telegram.au16reg = au16data; // pointer to a memory array in the Arduino 
	    au16data[0] = 0;
	    master.query( telegram ); // send query (only once)
          ...
должно быть
           telegram.u8id = 1; // slave address
	    telegram.u8fct = 6; // function code (this one is registers read)
	    telegram.u16RegAdd = 1; // start address in slave
	    telegram.u16CoilsNo = 1; // number of elements (coils or registers) to read
	    telegram.au16reg = au16data; // pointer to a memory array in the Arduino 
	    au16data[0] = 1;
	    master.query( telegram ); // send query (only once)
          .... 

 

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

со слейвом вроди разобрался, 1 пример из поста #24 дергает светодиод на 13 пине через терминальную программу

так же нашел еще одну библиотеку работает для слейва 

взято тут http://arduino.ru/forum/proekty/modbusrtu-modbustcp-arduino-i-owen-plc

#include <SimpleModbusSlave.h>

/* 
   SimpleModbusSlaveV10 supports function 3, 6 & 16.
   
   This example code will receive the adc ch0 value from the arduino master. 
   It will then use this value to adjust the brightness of the led on pin 9.
   The value received from the master will be stored in address 1 in its own
   address space namely holdingRegs[].
   
   In addition to this the slaves own adc ch0 value will be stored in 
   address 0 in its own address space holdingRegs[] for the master to
   be read. The master will use this value to alter the brightness of its
   own led connected to pin 9.
   
   The modbus_update() method updates the holdingRegs register array and checks
   communication.

   Note:  
   The Arduino serial ring buffer is 64 bytes or 32 registers.
   Most of the time you will connect the arduino to a master via serial
   using a MAX485 or similar.
 
   In a function 3 request the master will attempt to read from your
   slave and since 5 bytes is already used for ID, FUNCTION, NO OF BYTES
   and two BYTES CRC the master can only request 58 bytes or 29 registers.
 
   In a function 16 request the master will attempt to write to your 
   slave and since a 9 bytes is already used for ID, FUNCTION, ADDRESS, 
   NO OF REGISTERS, NO OF BYTES and two BYTES CRC the master can only write
   54 bytes or 27 registers.
 
   Using a USB to Serial converter the maximum bytes you can send is 
   limited to its internal buffer which differs between manufactures. 
*/

#define  LED 13 

// Using the enum instruction allows for an easy method for adding and 
// removing registers. Doing it this way saves you #defining the size 
// of your slaves register array each time you want to add more registers
// and at a glimpse informs you of your slaves register layout.

//////////////// registers of your slave ///////////////////
enum 
{     
  // just add or remove registers and your good to go...
  // The first register starts at address 0
  ADC_VAL,     
  PWM_VAL,        
  HOLDING_REGS_SIZE // leave this one
  // total number of registers for function 3 and 16 share the same register array
  // i.e. the same address space
};

unsigned int holdingRegs[HOLDING_REGS_SIZE]; // function 3 and 16 register array
////////////////////////////////////////////////////////////

void setup()
{
  /* parameters(HardwareSerial* SerialPort,
                long baudrate, 
		unsigned char byteFormat,
                unsigned char ID, 
                unsigned char transmit enable pin, 
                unsigned int holding registers size,
                unsigned int* holding register array)
  */
  
  /* Valid modbus byte formats are:
     SERIAL_8N2: 1 start bit, 8 data bits, 2 stop bits
     SERIAL_8E1: 1 start bit, 8 data bits, 1 Even parity bit, 1 stop bit
     SERIAL_8O1: 1 start bit, 8 data bits, 1 Odd parity bit, 1 stop bit
     
     You can obviously use SERIAL_8N1 but this does not adhere to the
     Modbus specifications. That said, I have tested the SERIAL_8N1 option 
     on various commercial masters and slaves that were suppose to adhere
     to this specification and was always able to communicate... Go figure.
     
     These byte formats are already defined in the Arduino global name space. 
  */
	
  modbus_configure(&Serial, 9600, SERIAL_8N2, 1, 2, HOLDING_REGS_SIZE, holdingRegs);

  // modbus_update_comms(baud, byteFormat, id) is not needed but allows for easy update of the
  // port variables and slave id dynamically in any function.
  modbus_update_comms(9600, SERIAL_8N2, 1);
  
  pinMode(LED, OUTPUT);
}

void loop()
{
  // modbus_update() is the only method used in loop(). It returns the total error
  // count since the slave started. You don't have to use it but it's useful
  // for fault finding by the modbus master.
  
  modbus_update();
  
  holdingRegs[ADC_VAL] = analogRead(A0); // update data to be read by the master to adjust the PWM
  
  analogWrite(LED, holdingRegs[PWM_VAL]>>2); // constrain adc value from the arduino master to 255
  
  /* Note:
     The use of the enum instruction is not needed. You could set a maximum allowable
     size for holdinRegs[] by defining HOLDING_REGS_SIZE using a constant and then access 
     holdingRegs[] by "Index" addressing. 
     I.e.
     holdingRegs[0] = analogRead(A0);
     analogWrite(LED, holdingRegs[1]/4);
  */
  
}

а вот с мастером какаято засада,  

пробовал примеры 

/*
   Пример будет использовать packet1, чтобы считать регистр из адреса 0 (значение adc ch0)
   от arduino раба (id=1). Это будет тогда использовать это значение, чтобы скорректировать яркость
   из вовлеченного контакт 9 использований PWM.
   Это будет тогда использовать packet2, чтобы записать регистр (его собственное значение adc ch0), чтобы адресоваться 1 
   на arduino рабе (id=1) корректировка яркости вовлеченного контакт 9 использований PWM.
*/
#include <SimpleModbusMaster.h>
//////////////////// Макроопределения портов и настройки программы  ///////////////////
#define baud        9600 // скоростьобмена по последовательному интерфейсу. (UART)
#define timeout     1000 // Длительность ожидание ответа (таймаут modbus)
#define polling     200  // скорость опроса по modbus
#define retry_count 10   // количесво запросов modbus до ошибки и останова обмена 
#define TxEnablePin 2    // Tx/Rx пин RS485
#define LED1        9    // светодиод 1
#define LED2        13   // светодиод 2

// Общая сумма доступной памяти на master устройстве, для хранения данных
// не забудьте изменить макроопределение TOTAL_NO_OF_REGISTERS. Если из слейва 
// запрашиваешь 4 регистра, то тогда в массиве reg должно быть не меньше 4х ячеек 
// для хранения полученных данных.
#define TOTAL_NO_OF_REGISTERS 4

// Масив пакетов modbus
// Для добавления новых пакетов просто добавте ихсюда
// сколько вам нужно.
enum
{
  PACKET1,
 // PACKET2,
 //PACKET3,
  //PACKET4,
  TOTAL_NO_OF_PACKETS // эту строку неменять
};

// Масив пакетов модбус
Packet packets[TOTAL_NO_OF_PACKETS];

// Массив хранения содержимого принятых и передающихся регистров
unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
 // Настраиваем пакеты
 // Шестой параметр - это индекс ячейки в массиве, размещенном в памяти ведущего устройства, в которую будет 
 // помещен результат или из которой будут браться данные для передачи в подчиненное устройство. В нашем коде - это массив reg
  
 // Пакет,SLAVE адрес,функция модбус,адрес регистра,количесво запрашиваемых регистров,локальный адрес регистра.
// modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS,    0, 1, 0); // чтение данных slave-master (slave адрес 1, регистр 0) 
// modbus_construct(&packets[PACKET2], 1, READ_HOLDING_REGISTERS,    1, 1, 1); // чтение данных slave-master (slave адрес 1, регистр 1)
 
 // Пакет,SLAVE адрес,функция модбус,адрес регистра,данные,локальный адрес регистра.
 modbus_construct(&packets[PACKET1], 1, PRESET_MULTIPLE_REGISTERS, 0, 1, 0); // запись данных master-slave (slave адрес 1, регистр 2)
// modbus_construct(&packets[PACKET4], 1, PRESET_MULTIPLE_REGISTERS, 3, 1, 3); // запись данных master-slave (slave адрес 1, регистр 3)
 
 // инициализируем протокол модбус
 modbus_configure(&Serial, baud, SERIAL_8N1, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);
  
 pinMode(LED1, OUTPUT);
 pinMode(LED2, OUTPUT);
} // конец void setup()

int val = 0;

void loop()
{
  modbus_update(); // запуск обмена по Modbus

  // если пришло 255 зажигаем светодиод 
 // analogWrite(LED1, regs[0]>>2);  // чтение данных slave-master (slave адрес 1, регистр 0) 
                                  // (ограничеть количесво бит данных числом 255), прочитаное значение выводим шимом на аналоговый выход pin9
 // if (regs[1] == 255){digitalWrite(LED2, HIGH);}else{digitalWrite(LED2, LOW);} // чтение данных slave-master (slave адрес 1, регистр 1)
  
  regs[0] =val+10 ;           //  100запись данных master-slave (slave адрес 1, регистр 2), запись константы
 if (val > 252){val == 0;} 
delay(500);
  // regs[3] = analogRead(0); // запись данных master-slave (slave адрес 1, регистр 3), значение из аналогового входа 0
} // конец void loop()

примеры из библиотеки ModbusRtu тоже не удется запустить

кто что может подсказать с моей засадой?

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

Ну ещё раз с мастером. Загружаем скетч 

#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 = 40001; // 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;
  }
}

Скачиваем, устанавливаем и запускаем симулятор слейва. https://sourceforge.net/projects/modrssim/?source=typ_redirect  . Устанавливаем параметры связи 19200 и Prot.(ocol) MODBUS RS-232

Сразу видим conected 1 recieved/send n/n - всё работает штатно. Полученные значения можно посмотреть например на LCD, если подключить. COM порт сейчас занят - его нельзя использовать для контроля.

Кнопка Comms-Register покажет траффик.

 

 

 

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

Может переделаать скетч на работу через SoftwareSerial ?

и выводить все в сериал монитор?

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

все получилось поморгать светодеодом на 13 пине из мастера на слейве 

скетч мастера 

#include <ModbusRtu.h>
#define TXEN 4
// 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,TXEN); // 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; 
    pinMode(TXEN, OUTPUT);
}

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

      master.query( telegram ); // send query (only once)    au16data[1] = 0;
    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() + 1000; 
    }
    break;
  }
}

скетч слейва 

/**
 *  Modbus slave example 1:
 *  The purpose of this example is to link a data array
 *  from the Arduino to an external device.
 *
 *  Recommended Modbus Master: QModbus
 *  <a href="http://qmodbus.sourceforge.net/" rel="nofollow">http://qmodbus.sourceforge.net/</a>
 */

#include <ModbusRtu.h>

#define TXEN 4
int pin = 13;
volatile int state = LOW;

// data array for modbus network sharing
uint16_t au16data[16] = {
  3, 1415, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, -1 };

/**
 *  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 slave(1,0,TXEN); // this is slave @1 and RS-232 or USB-FTDI

void setup() {
  slave.begin( 19200 ); // baud-rate at 19200
  pinMode(pin, OUTPUT);
  pinMode(TXEN, OUTPUT);


}

void loop() {
  au16data[0]=analogRead(A0);
  slave.poll( au16data, 16 );
  if(au16data[1]==0) state = LOW; else state= HIGH;
  digitalWrite(pin, state);
}

распиновка модуля RS485 китайского на базе max

DI -pin1  tx arduino

DO -pin0  rx arduino

DE и RE перемычкой и к pin4 настраевается в скетче #define TXEN 4

VCC - 5

GND - GND 

A - A между модулями

B - B между модулями

давольный как удав, дальше разберусь еще примеров выложу. Вот только пока 1 вопрос можно ли пользоваться этой библиотекой через  SoftwareSerial без правки кода библиотеки?

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

Снова немного подзастрял

с мастером все понятно работает по примеру через SoftwareSerial

а вот со слейвом не могу заставить работать. вот пример может кто что увидит чего я не заметил (МАСТЕРОМ АРДУИНО МЕГА ТРУДИТСЯ)

И похоже вот в этой строке чтото не так   slave.begin(&mySerial, 19200 ); очень часто моргает светодиот на 13 пине

а если пишу  slave.begin( 19200 ); моргание проходит но соответственно не работает сеть по rs485

пример

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

#define TXEN 4
int pin = 13;
volatile int state = LOW;

// data array for modbus network sharing
uint16_t au16data[16] = {
  3, 1415, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, -1 };

/**
 *  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 slave(1,0,TXEN); // this is slave @1 and RS-232 or USB-FTDI
SoftwareSerial mySerial(12, 11); // arduino Mega
void setup() {
  slave.begin(&mySerial, 19200 ); // ардуино МЕГА
  pinMode(pin, OUTPUT);
  pinMode(TXEN, OUTPUT);
}

void loop() {
  slave.poll( au16data, 16 );
  if(au16data[1]==0) state = LOW; else state= HIGH;
  digitalWrite(pin, state);
}

 

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

Ну а кто будет при объявлении слейва передаветь ему адрес софт сериала? Сначала объявнение сериала, потом объявление слейва с адресом софтсериала вторым параметром.

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

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

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

int8_t Modbus::poll( uint16_t *regs, uint8_t u8size )
*** Only for Modbus Slave ***
* This method checks if there is any incoming query
* Afterwards, it would shoot a validation routine plus a register query
* Avoid any delay() function !!!!
* After a successful frame between the Master and the Slave, the time-out timer is reset.
*
* @param *regs register table for communication exchange
* @param u8size size of the register table
* @return 0 if no query, 1..4 if communication error, >4 if correct query processed
* @ingroup loop

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

тоесть можно функцию poll  проверять и если возвращает 4 то пришел пакет? правильно понл? а про пример слейва с СофтВарсериалом ничего не увидили?

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

Если больше 4 то нормальный. Если 4 или меньше - ошибка. Если 0 - Нет пакетов.
А что не так со слейвом?

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

еслли заливаю в ардуинку пример из поста выше даже без подключеного модуля то светодеод 13 пина начинает быстро моргать 

22

  slave.begin(&mySerial, 19200 ); // ардуино МЕГА
моргает быстро

22   slave.begin( 19200 ); // ардуино МЕГА
не моргает но и не работает

думаю гдето тут, или косяк библиотеки.

James
Offline
Зарегистрирован: 26.02.2016

#include <SoftwareSerial.h> откуда у вас взялся?

как rs 485 подключен, через мах?

вот пример через мах, A,B -tx1,rx0 переключение 10 пин

#include "ModbusRtu.h"

#define ID   1      // адрес ведомого
#define stlPin  13  // номер выхода индикатора работы
                    // расположен на плате Arduino


//Задаём ведомому адрес, последовательный порт, выход управления TX
Modbus slave(ID, 0, 10); 
int8_t state = 0;
unsigned long tempus;

// массив данных modbus
uint16_t au16data[11];

void setup() {
  // настраиваем входы и выходы
  io_setup();
  // настраиваем последовательный порт ведомого
  slave.begin( 9600 ); 
  // зажигаем светодиод на 100 мс
  tempus = millis() + 100; 
  digitalWrite(stlPin, HIGH );
}

void io_setup() {
  
  digitalWrite(stlPin, HIGH ); 
  pinMode(stlPin, OUTPUT);   
}

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();
} 

 

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

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

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

Ну а кто будет при объявлении слейва передаветь ему адрес софт сериала? Сначала объявнение сериала, потом объявление слейва с адресом софтсериала вторым параметром. Здесь - Modbus slave(ID, 0, 10); 

Я не работал с софтсериал, но судя по тексту библиотеки инициализировать софтсериал можно сделать двумя способами. Вы пробовали один. Попробуйте другой. Может быть сработает. Хотя очень странно что не работает. Я бы сначала запустил софтсериал и попробовал через него что нибудь передать - убедиться, что всё работает штатно.

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

не подскажите как со слейва мастеру передавать инфу?

пробую так от слейва

int vP1 = 0;

void loop() {
  //au16data[0]=analogRead(A0);
  packet = slave.poll( au16data, 16 );
/**
*если пришел правeльный пакет  то ответ будет больше 4
* int8_t packet = 0;
*/
if (packet > 4) {
vP1 = au16data[1];
  if( vP1 ==0) state = LOW; else state= HIGH;
  digitalWrite(pin, state);
  au16data[0] = vP1 +3;
  au16data[1] = vP1 +3;
  au16data[2] = vP1 +3;
  }

так пытаюсь считать

мастер

/**
 * ro-3
 * do-5
 * TXEN - 4
 *  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>
 //#define TXEN 4
// 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(3, 5);//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(19200);//use the hardware serial if you want to connect to your computer via usb cable, etc.
  master.begin( &mySerial, 19200 ); // 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 = 6; // function code (this one is registers read)
      telegram.u16RegAdd = 1; // start address in slave
      telegram.u16CoilsNo = 1; // number of elements (coils or registers) to read
      telegram.au16reg = au16data; // pointer to a memory array in the Arduino 
      au16data[0] = random(2);
      
    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.print(au16data[0]);
        Serial.print(au16data[1]);//Or do something else!
        Serial.print(au16data[2]);
    }
    break;
  }
}

 

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

Тексты программ не читал, но осуждаю. Постановку вопроса. От слейва ничего нельзя предать мастеру. Никогда. Можно только ответить на запрос.

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

Да пускай будет так, отвечает.

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

Ну тогда возвращаемся к посту #24. Там слэйв отдает значение АЦП через нулевой регистр. Через остальные регистры можно отдать что угодно.

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

если не сложно посмотрите пример передачи ответа #46 , пробую в ответе записать еще пару резистров а на мастере прочитать и вывести все в сериал. НЕ выдает (только нули возвращает после паузы в 2000)