Официальный сайт компании Arduino по адресу arduino.cc
Modbus RTU
- Войдите или зарегистрируйтесь, чтобы получить возможность отправлять комментарии
Пнд, 09/11/2015 - 13:20
Доброго времени суток товарищи возникла проблема с библиотекой 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()
Вот сей код но чтото проблемы при компилировании я так понял ему ненравится обявление структуры.
это тот самый код
вот сообщение компилятора
и всё будет нормально.
Насчёт скобок прошу прощения мой касяк просто эксперементировалм со структурой забыл убрать, за правильное обявление масива спасибо попробую.
Я так понимаю это вариант для С++ в скетче он скомпилируется я надеюсь там по идее С. Еще вопрос обращение к элементам структуры изменитсяили такжебудет?
Мда номер непрошол все исправил как указано выше
Простите, Вы издеваетесь? Я же Вам писал, что у Вас дважды определена функция modbus_construct. Так она у Вас по-прежнему дважды определена. Первый раз в строке 121, а второй раз в строке 476. Уберите двойное определение!
С Packet, похоже не помогло (про Ваш вопрос - нет, ничего не поменяется - эти определения - полные синонимы). Странно, компилятор ругается на строку 30, но там никакой Packet не используется.
Давайте так:
1. Исправьте всё остальное (двойное определение функции)
2. Кстати, уберите #include "Arduino.h" - в скетче это не нужно
3. После этого, давайте снова код и сообщение компилятора с единственной ошибкой насчёт Packet и я внимательно посмотрю.
Не в коем случае неиздеваюсь это я просто туплю спасибо что указали я вас просто непонял с первого раза и убрал только скобки глаза замылились ужеот кода сразу незаметил.
Вот осталась одна ошибка
Вот еще прикол закоментировал функцию
В 433 строке и в 516 и код скомпилировался может какаято ошибка в этой функции
Блин, разобрался! Вот ведь знал, что IDE - говно, но что такое! … серия непечатных слов …
Это не Ваша вина - это ошибка IDE. Давайте, я сначала объясню суть ошибки, а потом расскажу как её побороть.
Суть ошибки
В нормальном С/С++ функция обязана быть определена (объявлена) до того, как она впервые вызывается. Это понятно, т.к. при вызове компилятор должен иметь возможность проверить параметры и т.п. Так вот это
кознехорошие люди - разработчики IDE решили «облегчить жизнь новичкам» … серия непечатных слов … и позволили нам с Вами определять функции в любом порядке, а их препроцессор перед компиляцией программы тупо собирает все объявления функций и помещает их в самое начало кода, чтобы типа при любом вызове, определения оказались выше вызова!Ну, и что получается? У Вас есть две функции modbus_construct и modbus_configure в параметрах которых встречается тип Packet. Они у Вас объявлены в строках 77 и 78, т.е. ПОСЛЕ того, как тип Packet уже определён. И это правильно! Но когда эти
муданехорошие люди собирают все объявления и насильно перетаскивают их в самое начало программы – они оказываются ДО объявления типа и компилятор, разумеется, ругается … серия непечатных слов ….Как с этим бороться?
Возможно, ему (препроцессору) можно какой-то хитрой командой сказать, чтобы он не
мчудил, но я его команд не знаю, поэтому я покажу Вам как избавиться от типов Packet в параметрах этих двух функций так, чтобы изменения в программе были минимальными и абсолютно ничего в ней не поменялось.Делаем аккуратно - не ошибаемся.
1. В строках 77 и 78 заменяем свово Packet (оно там встречается два раза) на слово void. Строки получатся:
2. В строке 434 заменяем выражение "Packet* _packets" на "void *_packets1". Должно получиться
3. После строки 435 вставляем строку:
4. В строке 459 заменяем выражение "Packet* _packet" на "void * _packet1". Должно получиться:
5. После строки 460 вставляем строку:
Если Вы всё сделали аккуратно и не ошиблись, то у Вас будет нормально компилируемая программа, которая ничем не отличается от исходной - полностью идентичная.
Если что-то не так, скажите, я могу просто выложить Вам нормально компилируемый код.
Большое спасибо скомпилировалось. Я тоже грешил на среду ардуино потому как в библиотеке работает а в скетче нет.
Вот исправленный код завра проверю в железе.
Может подскажете как мне реализовать чтобы modbus запросы шли не в цикле а однократно пришла команда пусть будет с кнопки дало 1 запрос получило ответ все тишина ждем следующего. Хочу использовать скетч с кодом модбус TCP и реализовать модбус шлюз но для нормальной его работы нужно слать по одному запросу инициализируемому кодом модбус TCP.
Евгений день добрый! Помогите такому чуду как мне, проблема в том, что не могу записать данные в slave, читать вроде методом тыка добился, а вот занести не могу понять как ...
получается манипуляцией последних двух значений (int (&data), 1) либо записать 0 или 770 или 1280.
спасибо.
Олег, это не ко мне. Я помог человеку с синтаксисом языка, а в логике работы модбаса я не компетентен. Сожалею.
будем искать...
будем искать...
Привет! Вот эту библиотеку не смотрели?
https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino
В той, что приведена выше я тоже пытался разобраться, но мне показалось сложноватой. А в этой используется всего две функции, что для ардуинки в полне достаточно. Удобно то, что создаешь телеграммы разного размера опроса и с разного адреса к разным устройствам. Короче в примерах глянте там все понятно.
Спасибо, я тоже нашел эту библиотеку и с успехом пользуюсь.
кто может помочь по этой библиотеке? пол дня просидел так и не понял как с ней рааботать, в примерах нет пина для переключения говорю или молчу, не понятно как вывести инфу в монитор порта с ошибками как дело обстоит и как их считывать
зараннее благодарен за помощ!
Вы не понятно вопросы задаете. Опишите что вы хотите сделать. Модбас это не равноправный протокол. Есть мастер - один в сети имеет право послать запрос. Все остальные рабы. Они ждут запрос мастера и отвечают только на запрос к себе. Пина говорю нет. Только ответ на запрос. В библиотеке это всё скрыто. Ваша задача только проверять регулярно массив значений и если он изменился то что то делать. Если это слейв - раб. Если мастер то сами должны слать телеграммы и ждать ответы тоже следя за изменением массива регистров.
один мастер несколько слейвов работать будут через микросхему max...
для начала передачи с ммастера нужно подать 5в на 2 контакта модуля кы485 соединенные перемычкой - куда в примере вставить код подачи 5в ? и где подать 0в чтобы начать прослушку ответа...
Смотрите примеры от библиотеки
// 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
#define TXEN 4
........
Modbus master(0,0,TXEN);
Библиотека сама разберётся когда надо пин дергать. Только самому к правильному пину подключить надо - здесь D4.
коллеги понимаю что глупый вопрос но никак не пойму вот код слейва
к каким контактам RX TX модуль RS485 подключать?
Если Modbus master(0,0,TXEN);
И TXEN 4 тогда для ардуино уно rx pin 0 tx pin1 ну а перекоючатель приема передачи pin4
Друзья я заранее прошу прощения что не прочитал всех постов внимательно. Но я провозился полгода назад 2 недели пока мне удалось все правильно сделать. одна из проблем была, что в библиотеки был зашит пин3 для переключения между приемом и передачей и ОБЯЗАТЕЛЬНО!!! в вашей программе в SETUP должна быть строка pinMode(3, OUTPUT) или другая определяющая выход; иначе недостаточно напряжения для переключения адаптера.
Друзья я заранее прошу прощения что не прочитал всех постов внимательно. Но я провозился полгода назад 2 недели пока мне удалось все правильно сделать. одна из проблем была, что в библиотеки был зашит пин3 для переключения между приемом и передачей и ОБЯЗАТЕЛЬНО!!! в вашей программе в SETUP должна быть строка pinMode(3, OUTPUT) или другая определяющая выход; иначе недостаточно напряжения для переключения адаптера.
я пока 2 дня пытаюсь разобраться что к чему, как что работает и как правильно подключается, из моих продвижений пока только понял как работает мастер устройсво за исключением считывания полученой информации (из примера библиотеки) со слейвом вообще глухо в плани понимания работы, ну соответственно ничего не заработало. хотя тестовые скечи заливал из примеров
у Вас случаем тестового рабочего примера не осталось для ознакомления?
С ведомым я потратил еще больше времени, так и никто не помог, но это нужно было для работы, поэтому перепробовал все что можнои остановился на этой библиотеки. код рабочий и сейчас работает, но только в RTU.
датчик температуры дистанционно измеряет и по запросу выдает мастеру.
вот часть кода мастера для получения запроса
показывает температуру обьекта. Заметь, что для мастера идет #include <ModbusMaster.h> эта библиотека, а для slave идет #include <ModbusRtu.h>
В мастере 3 пин TX и только, если не править библиотеку. для slave TXEN любой.
Давайте попробуем со слейвом с нуля. Загружаем скетч
Скачиваем, устанавливаем, запускаем 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 к компьютеру и ардуине. Проверяем точно также. Если подключили правильно,то всё должно работать.
докладываю...
работы со слейвом - выполнил пункт 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 увидил ту же картинку, получаемое число увеличивается... следовательно мастер работает нормально.
вечером опыты продолжу и отпишусь...
Переходники в картинках гугля по запросу USB RS485.
Для ардуины можно такие http://arduino.ru/forum/apparatnye-voprosy/rs485-module-ne-schityvaet-da...
Если смотреть на трафик сети RS485 то на доподнительном USB-RS485 aдаптере с помощью любой терминальной программы, которая может показывать то что приходит на COM порт в HEX формате.
И так нужна помощ вот в чем, есть 2 скетса мастер и слейв
мастер
слейв тоже из примеров библиотеки
и теперь вот что из этого вышло
Посылки от мастера не моргают светодеодом. Насколько я понимаю для работы слейва (включить светодеод на 13 пине) нужно чтобы мастер одинаковые команды выдавал (с терминалом). так подскажите что нужно сделать чтобы привести это все к одинаковому виду, да еще чтобы и работало)))
Телеграмму надо правильно инициировать.
Посмотрите:
Функцональный код - должен быть 6.
Старовый адрес - 1
Количество байт - 1
Нулевой элемент массива = 1 для зажигания, =0 - погасить.
Как правильно инициировать телеграмму? вроди все из примера взято однако не стыкуется папа с мамой?)))
со слейвом вроди разобрался, 1 пример из поста #24 дергает светодиод на 13 пине через терминальную программу
так же нашел еще одну библиотеку работает для слейва
взято тут http://arduino.ru/forum/proekty/modbusrtu-modbustcp-arduino-i-owen-plc
а вот с мастером какаято засада,
пробовал примеры
примеры из библиотеки ModbusRtu тоже не удется запустить
кто что может подсказать с моей засадой?
Ну ещё раз с мастером. Загружаем скетч
Скачиваем, устанавливаем и запускаем симулятор слейва. https://sourceforge.net/projects/modrssim/?source=typ_redirect . Устанавливаем параметры связи 19200 и Prot.(ocol) MODBUS RS-232
Сразу видим conected 1 recieved/send n/n - всё работает штатно. Полученные значения можно посмотреть например на LCD, если подключить. COM порт сейчас занят - его нельзя использовать для контроля.
Кнопка Comms-Register покажет траффик.
Может переделаать скетч на работу через SoftwareSerial ?
и выводить все в сериал монитор?
все получилось поморгать светодеодом на 13 пине из мастера на слейве
скетч мастера
скетч слейва
распиновка модуля 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 без правки кода библиотеки?
Судя по этому https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino/blob/maste... да можно.
https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino/tree/maste...
Это пример.
Снова немного подзастрял
с мастером все понятно работает по примеру через SoftwareSerial
а вот со слейвом не могу заставить работать. вот пример может кто что увидит чего я не заметил (МАСТЕРОМ АРДУИНО МЕГА ТРУДИТСЯ)
И похоже вот в этой строке чтото не так slave.begin(&mySerial, 19200 ); очень часто моргает светодиот на 13 пине
а если пишу slave.begin( 19200 ); моргание проходит но соответственно не работает сеть по rs485
пример
Ну а кто будет при объявлении слейва передаветь ему адрес софт сериала? Сначала объявнение сериала, потом объявление слейва с адресом софтсериала вторым параметром.
nik182 очень благодарен Вам за помощь, это опечатка при копировании на форум. туда сюда да еще и без мышки.. уже поправил пример но дело не в этом, может библиотека так написана что слейв не работает, и да еще вопрос есть как в этой библиотеке в слейве поставить условие о приходе пакета?
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
тоесть можно функцию poll проверять и если возвращает 4 то пришел пакет? правильно понл? а про пример слейва с СофтВарсериалом ничего не увидили?
Если больше 4 то нормальный. Если 4 или меньше - ошибка. Если 0 - Нет пакетов.
А что не так со слейвом?
еслли заливаю в ардуинку пример из поста выше даже без подключеного модуля то светодеод 13 пина начинает быстро моргать
22
slave.begin(&mySerial, 19200 );
// ардуино МЕГА
моргает быстро
22
slave.begin( 19200 );
// ардуино МЕГА
не моргает но и не работает
думаю гдето тут, или косяк библиотеки.
#include <SoftwareSerial.h> откуда у вас взялся?
как rs 485 подключен, через мах?
вот пример через мах, A,B -tx1,rx0 переключение 10 пин
да оттуда что нужно через SoftwareSerial чтобы слейв работал, а через рх тх работает нормально, как только подключаю к SoftwareSerial все перестает.. и причем только слейв отказывается , а мастер работает нормально
Ну а кто будет при объявлении слейва передаветь ему адрес софт сериала? Сначала объявнение сериала, потом объявление слейва с адресом софтсериала вторым параметром. Здесь - Modbus slave(ID, 0, 10);
Я не работал с софтсериал, но судя по тексту библиотеки инициализировать софтсериал можно сделать двумя способами. Вы пробовали один. Попробуйте другой. Может быть сработает. Хотя очень странно что не работает. Я бы сначала запустил софтсериал и попробовал через него что нибудь передать - убедиться, что всё работает штатно.
не подскажите как со слейва мастеру передавать инфу?
пробую так от слейва
так пытаюсь считать
мастер
Тексты программ не читал, но осуждаю. Постановку вопроса. От слейва ничего нельзя предать мастеру. Никогда. Можно только ответить на запрос.
Да пускай будет так, отвечает.
Ну тогда возвращаемся к посту #24. Там слэйв отдает значение АЦП через нулевой регистр. Через остальные регистры можно отдать что угодно.
если не сложно посмотрите пример передачи ответа #46 , пробую в ответе записать еще пару резистров а на мастере прочитать и вывести все в сериал. НЕ выдает (только нули возвращает после паузы в 2000)