Здравствуйте. Прошу совета, плиз. "Безумный дом". Часть уже реализовал, идея следующая: есть несколько arduino nano, каждый отвечает за свой сегмент. Например, один - свет, другой вентиляция, третий розетки и т.д., на каждом крутится свой код, подключены свои датчики, релюшки. Есть arduino uno - будет исполнять роль сервера, мастер. забирает информацию с nano (slave), дает указания. Например, к nano, отвечающей за свет подключен датчик освещенности. Эта информация (освещенность) нужна другому nano. Slave1 посылает эту информацию мастеру, а тот в свою очередь пересылает другому слейву. Канал связи между контроллерами - rs485 и nrf24l01 (некоторые контроллеры удобнее подключить по радио). Информация должна бегать и от мастера к слейву и от слейва к мастеру. Зачем еще нужен мастер? В перспективе хочу выводить информацию в веб (т.е openhub или majordomo). Начал копать в сторону modbus, но не могу раскусить этот орешек. Поделитесь советом, как лучше организовать перечисленное выше, имея в распоряжении именно то железо, что я описал. Многое уже сделано и работает на этом железе. спасибо
Для радио наверное придется "умный" мост между rs485 и nrf24l01 сделать, чтобы nrf24l01 не шумел в эфир запросами, которые ему не адресованы и главное чтобы из шумного эфира случайные "данные" в шину не сливал. Но это проверить надо. Может ошибки будут случаться достаточно редко. А на счёт орешков не понятно, что вам не понятно. Физически так подключается. В скетч подключаем библиотеку с поддержкой протокола modbus и забываем про провода.
Не могу разобраться с кодом запроса от мастера к слейвам, и ответ слейва. Можно сказать каша. Например, мастер шлет сообщение, адресованное id 2, передает значение времени (14 часов). Слейв ему отвечает, шлет значение с датчика DHT, статус реле1.
Если что то не понятно по modbus посмотрите вот это видео, очень толково изложено: Слейвы сами к мастеру обращаться не могут. Мастер должен постоянно опрашивать слейвы и если у них что-то есть (запрос на обслуживание или изменение состояния датчика), то они передают это мастеру, а мастер принимает решение что делать с данным запросом.
Спасибо огромное за ролик. Да, я в курсе что инициатор связи в modbus всегда мастер. Поэтому у мастера адрес всегда 0 - широковещательный.
SimpleModbusMaster.h и SimpleModbusSlave.h Примеры из этих библиотек. Код (C++): /* Пример будет использовать 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[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, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); } // конец void setup() 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[2] = 255; // запись данных master-slave (slave адрес 1, регистр 2), запись константы regs[3] = analogRead(0); // запись данных master-slave (slave адрес 1, регистр 3), значение из аналогового входа 0 } // конец void loop() Слейв Код (C++): /* SimpleModbusSlaveV10 supports function 3, 6 и 16. serial ring buffer 64 bytes or 32 registers. function 3 58 bytes or 29 registers. function 16 54 bytes or 27 registers. */ #include <SimpleModbusSlave.h> //////////////////// Макроопределения портов и настройки программы /////////////////// #define TxEnablePin 2 // Tx/Rx пин RS485 #define baud 9600 // скоростьобмена по последовательному интерфейсу. (UART) #define timeout 1000 // Длительность ожидание ответа (таймаут modbus) #define polling 200 // скорость опроса по modbus #define retry_count 10 // количесво запросов modbus до ошибки и останова обмена #define Slave_ID 1 // Адрес Slave устройсва #define LED1 13 // светодиод 1 #define LED2 9 // светодиод 2 const int buttonPin = 3; // номер входа, подключенный к кнопке // переменные int buttonState = 0; // переменная для хранения состояния кнопки //////////////// Регистры вашего Slave /////////////////// enum { //Просто добавьте или удалите регистры. Первый регистр начинается по адресу 0 slave_to_master_val_1, // с адресом массива 0 slave_to_master_val_2, // с адресом массива 1 master_to_slave_val_1, // с адресом массива 2 master_to_slave_val_2, // с адресом массива 3 HOLDING_REGS_SIZE // Это не удалять размер массива HOLDING_REGS. //общее количество регистров для функции 3 и 16 разделяет тотже самый массив регистров //т.е. то же самое адресное пространство }; unsigned int holdingRegs[HOLDING_REGS_SIZE]; // функции 3 и 16 массив регистров //////////////////////////////////////////////////////////// void setup() { // инициализируем пин, подключенный к кнопке, как вход pinMode(buttonPin, INPUT); /* 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) 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 SERIAL_8N1 option */ modbus_configure(&Serial, baud, SERIAL_8N1, Slave_ID, TxEnablePin, HOLDING_REGS_SIZE, holdingRegs); modbus_update_comms(baud, SERIAL_8N1, 1); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); }// конец void setup() void loop() { int temp; // считываем значения с входа кнопки buttonState = digitalRead(buttonPin); // проверяем нажата ли кнопка // если нажата, то buttonState будет HIGH: if (buttonState == HIGH){temp=255;}else{temp=0;} //if (temp == 255){digitalWrite(LED1, HIGH);}else{digitalWrite(LED1, LOW);} modbus_update(); // запуск обмена по Modbus holdingRegs[slave_to_master_val_1] = analogRead(A0); // запись данных slave-master // (регистр 0), значение из аналогового входа 0. holdingRegs[slave_to_master_val_2] = temp; // запись данных slave-master // (регистр 1), запись значения переменной temp. по нажатию кнопки. // чтение данных master-slave (регистр 2) if (holdingRegs[master_to_slave_val_1] == 255){digitalWrite(LED1, HIGH);}else{digitalWrite(LED1, LOW);} // если пришло 255 зажигаем светодиод analogWrite(LED2, holdingRegs[master_to_slave_val_2]>>2); // чтение данных master-slave (регистр 3) // (ограничеть количесво бит данных числом 255), прочитаное значение выводим шимом на аналоговый выход pin9 }// конец void loop() /* Использование enum инструкции не обязательно. Вы могли установить допустимый максимум размер для holdinRegs [], определяя HOLDING_REGS_SIZE, используя константу и затем доступ holdingRegs[] обращением по "Index" массива. holdingRegs[0] = analogRead(A0); analogWrite(LED, holdingRegs[1]/4); holdingRegs[ADC_VAL] = 250; // данные об обновлении, которые будут прочитаны владельцем, чтобы приспособить PWM ШИМ analogWrite(LED, holdingRegs[PWM_VAL]>>2); // ограничьте АЦП arduino значением 255 holdingRegs[ADC_VAL] = holdingRegs[PWM_VAL]; */ Не могу понять следующее, выше приводил пример. Мастер шлет сообщение, адресованное id 2, передает значение времени (14 часов). Слейв ему отвечает, шлет значение с датчика DHT, статус реле1. Где этот запрос-ответ и в каком виде вставить?
В Slave это строки: Код (C++): holdingRegs[slave_to_master_val_1] = analogRead(A0); // запись данных slave-master // (регистр 0), значение из аналогового входа 0. holdingRegs[slave_to_master_val_2] = temp; // запись данных slave-master // (регистр 1), запись значения переменной temp. по нажатию кнопки. Регистр slave_to_master_val_1 передает показания с пина A0. У вас это может быть DHT. Регистр slave_to_master_val_2 передает состояние кнопки (значение temp, оно выше вычисляется). На Master код не так очевиден. Сначала создается отображение команд и пакетов modbus в ячейки регистра regs (строчки modbus_construct). Далее через массив regs программа передает и принимает значения от бибилиотеки. К сожалению в Master для номеров ячеек массива нет таких констант как в Slave (slave_to_master_val_1, slave_to_master_val_2 и т.д.)
Что касается Master, то эта библиотека и пример не выглядят столь ужасно: https://github.com/4-20ma/ModbusMaster/blob/master/examples/RS485_HalfDuplex/RS485_HalfDuplex.ino
В принципе, идея там такая, что слейв держит информацию в регистрах, мастер ее забирает, когда ему надо. Или записывает свою.
Доброго всем времени. Прошу помощи, плиз. При компиляции кода мастера (пример из библиотеки SimpleModbusMaster.h) выскакивает ошибка: /Users/yyyy/Documents/Arduino/libraries/SimpleModbusMasterV12/SimpleModbusMaster.h:126:6: note: declared here void modbus_configure(HardwareSerial* SerialPort, ^ exit status 1 too many arguments to function 'void modbus_configure(HardwareSerial*, long int, unsigned char, unsigned int, unsigned int, unsigned char, unsigned char, Packet*, unsigned int)' Вот код: Код (C++): /* Пример будет использовать 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[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, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs); pinMode(LED1, OUTPUT); pinMode(LED2, OUTPUT); } // конец void setup() 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[2] = 255; // запись данных master-slave (slave адрес 1, регистр 2), запись константы regs[3] = analogRead(0); // запись данных master-slave (slave адрес 1, регистр 3), значение из аналогового входа 0 } // конец void loop()/* Как ее исправить? спасибо