Здравствуйте. Помогите разобраться, плиз. Делаю свой проект "безумного дома". По дому раскиданы nano, каждая отвечает за свой сегмент. Так же есть одна uno - исполняет роль сервера. Роль сервера - посредник между всеми сегментами сети + сбор данных. Например, nano, отвечающий за свет, к ней подключен датчик света и часы. Значения датчика света нужно использовать в другом сегменте сети - отвечает за наружный свет. Вот сервер берет данные с одной ардуинки и передает их другой. Плюс в дальнейшем буду визуализировать эти данные в вебе. Данные планирую передавать по проводам, используя rs485. Сделал упор на modbus, что-то начало получаться, пока не столкнулся с проблемой, которая начисто режет все мои идеи. Это количество регистров, которые может хранить у себя ардуинка - 30. Для моей затеи это мало, так как планируется подключения множества датчиков ds18b20. Вопрос: подкиньте плиз рабочие идеи как можно реализовать задуманное используя провода, nano\uno, rs485. Спасибо
Конечно нет. Например, 4 слейва: гараж, баня+стайка, вентиляция дом, сигнализация. Гараж - 2 ds18b20 Баня - 2 ds18b20 Вентиляция дом - 7 ds18b20 Сигналка - 10 ds18b20 + 3 датчика протечки. Это уже 24 регистра. Это все минимум. Плюс статус релюшек. Также хотел по 2 регистра на слейв отдать на проверку зависания слейвов.
тут кстати про rs485 и протокол Applied Digital речь заходила, хорошая штука, поддерживается многими интеграторами вроде Motorola Premise Home и подобными, а также всеми брендами от Crestron и пр.
Непонятно почему вам не хватает 30 регистров. Температуру можно передавать одним байтом, это будет диапазон -127...+127 градусов. Реле до 8 штук в одном байте. Контактных датчиков тоже до 8 шт. в одном байте. Мне кажется что для бани или гаража это "выше крыши".
На сколько я понял, это ограничения конкретной библиотеки. И все это растет из буфера в 64 байта. То есть если вам будет необходимо запросить все регистры последовательно, то их будет не более 30. Запрашивайте по одному, кто мешает? Задавайте свой HOLDING_REGS_SIZE и вперед, лишь бы оперативки хватило. Я уже который раз сталкиваюсь с тем, что ардуина превратилась в набор из кубиков, причем самых разных размеров. И юзеры пытаются из них собирать домики, не придавая значения их размерам, не пытаясь их подстраивать под свои задачи. Жаль.
Надо почитать спецификацию протокола и написать все самому. Я чуть внимательнее посмотрел на предложенную вами библиотеку. Не понравилась. В модбас рту установлены жесткие временные ограничения. А в этой библиотеке так и написано, что сделано не очень правильно, но вроде в большинстве случаев работает Разработчики не используют прерывание по приему байта, а считают, что пакеты достаточно редко идут и их можно отлавливать время от времени мониторя Serial.available(). И еще библиотека скопом запрашивает все определенные пакеты. А если вам надо один регистр раз в полчаса считать? Зачем молотить его постоянно? Плюс значения регистров хранятся локально в озу ардуинки. Опять же - зачем? Запросили только то, что в данный момент интересует, обработали и забыли. Память то не резиновая. Ладно, оставим это их совести. По вашему вопросу. В библиотеке конструируются пакеты. Один из параметров data - это и есть длина посылки, точнее сколько регистров (16-битных) запрашивается.
Спасибо за подробный анализ. Я верно понял, что библиотека modbus rtu более надежна и логична? Можете поделиться рабочим кодом для мастера и слева - работа в паре, плиз? И ссылку на используемую библиотеку. Реально на одном месте топчусь. Благодарю.
На правах топикстартера отпишусь. За основу взял библиотеки SimpleModbusMaster.h и SimpleModbusSlave.h. Не "разговаривали" ардуинки по причине битых модулей на max485 (половину отбраковал). Пробный стенд: К мастеру подключен дисплей TM1867 - на него должна приходить информация (температура) со слейва. Мастер посылает 1. К слейву подключен ds18b20. Слейв должен получить от мастера число, если это 1 то зажечь светик на ардуинке (13 пин), и посылает мастеру температуру и число (генерация случайным образом от 0 до 9). Мастер выводит температуру на TM1867 и если число от слейва = 1 то зажигает свой светик. Что имеем: слейв отвечает мастеру, посылает ему случайное число и температуру, мастер это принимает, выводит на экран, мигает светиком. Но слейв информацию от мастера не получает, т.е 1 не приходит от мастера к слейву, соответственно он светиком не моргает. Help! В мастере сбор пакетов в loop - чтобы в перспективе больше 30 параметров передавать\получать. мастер: Код (C++): //#include <iarduino_RTC.h> #include <Wire.h> #include <SimpleModbusMaster.h> #include "TM1637.h" #define CLK 6 // CLK -> pin D6 #define DIO 5 // DIO -> pin D5 TM1637 tm1637(CLK, DIO); // Семи сегментный индикатор, модуль на TM1637 #define brightness 7 // яркость индикатора, от 0 до 1 //iarduino_RTC time(RTC_DS3231); int8_t TimeDisp[4]; //пакеты регистров Modbus long previousMillis_regs = 0; // храним время последнего формирования пакетов long interval_regs = 5000; //интервал byte i = 0; //////////////////// Макроопределения портов и настройки программы /////////////////// #define baud 19200 // скоростьобмена по последовательному интерфейсу. (UART) #define timeout 1000 // Длительность ожидание ответа (таймаут modbus) #define polling 200 // скорость опроса по modbus #define retry_count 1000 // количесво запросов modbus до ошибки и останова обмена #define TxEnablePin 4 // Tx/Rx пин RS485 #define LED1 13 // светодиод 1 #define TOTAL_NO_OF_REGISTERS 4 enum { PACKET1, PACKET2, PACKET3, PACKET4, TOTAL_NO_OF_PACKETS // эту строку неменять }; Packet packets[TOTAL_NO_OF_PACKETS]; unsigned int regs[TOTAL_NO_OF_REGISTERS]; void setup() { tm1637.init(); // инициализируем индикатор tm1637.set(brightness); // включаем подсветку индикатора tm1637.point(POINT_ON); // включаем точки pinMode(LED1, OUTPUT); } // конец void setup() void loop() { unsigned long currentMillis_regs = millis(); //-------------------------------------------------------------------------------------- //сборка пакетов регистров Modbus if (currentMillis_regs - previousMillis_regs > interval_regs) { previousMillis_regs = currentMillis_regs; i++; } //пакет 1 if (i == 1) { // Настраиваем пакеты // Шестой параметр - это индекс ячейки в массиве, размещенном в памяти ведущего устройства, в которую будет // помещен результат или из которой будут браться данные для передачи в подчиненное устройство. В нашем коде - это массив 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_8N2, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs); //modbus_update(); // запуск обмена по Modbus regs[2] = 1; i = 0; } modbus_update(); // запуск обмена по Modbus if (regs[1] == 1) { digitalWrite(LED1, HIGH); } else { digitalWrite(LED1, LOW); } //Serial.println(regs[0]); TimeDisp[0] = regs[0] / 10; TimeDisp[1] = regs[0] % 10; TimeDisp[2] = regs[1] / 10; TimeDisp[3] = regs[1] % 10; tm1637.display(TimeDisp); // воводим на индикатор } // конец void loop()/* слейв: Код (C++): #include <SimpleModbusSlave.h> #include <DallasTemperature.h> //////////////////// Макроопределения портов и настройки программы /////////////////// #define TxEnablePin 4 // Tx/Rx пин RS485 #define baud 19200 // скоростьобмена по последовательному интерфейсу. (UART) #define Slave_ID 1 // Адрес Slave устройсва #define LED1 13 // светодиод 1 //////////////// Регистры вашего Slave /////////////////// enum { //Просто добавьте или удалите регистры. Первый регистр начинается по адресу 0 reg_0, // исходящий 1 reg_1, // исходящий 2 reg_2, // входящий 1 reg_3, // входящий 2 HOLDING_REGS_SIZE // Это не удалять размер массива HOLDING_REGS. //общее количество регистров для функции 3 и 16 разделяет тотже самый массив регистров //т.е. то же самое адресное пространство }; unsigned int holdingRegs[HOLDING_REGS_SIZE]; // функции 3 и 16 массив регистров //////////////////////////////////////////////////////////// //ds18b20 OneWire oneWire(3); // вход датчиков 18b20 DallasTemperature ds(&oneWire); DeviceAddress ds_frosya = {0x28, 0xFF, 0x33, 0xD0, 0x72, 0x16, 0x5, 0x5A}; //подсчет времени //опрос датчиков long previousMillis_sensor = 0; // храним время последнего опроса датчиков long interval_sensor = 5000; //интервал //температура int temp; float i = 0; void setup() { modbus_configure(&Serial, baud, SERIAL_8N2, Slave_ID, TxEnablePin, HOLDING_REGS_SIZE, holdingRegs); modbus_update_comms(19200, SERIAL_8N2, 1); pinMode(LED1, OUTPUT); }// конец void setup() void loop() { unsigned long currentMillis_sensor = millis(); //-------------------------------------------------------------------------------------- //ds if (currentMillis_sensor - previousMillis_sensor > interval_sensor) { previousMillis_sensor = currentMillis_sensor; i++; } //dallas подготовка if (i == 1) { ds.requestTemperatures(); // считываем температуру с датчиков } //dallas считывание if (i == 2) { if (ds.getTempC(ds_frosya) <= 50) temp = ds.getTempC(ds_frosya); i = 0; } modbus_update(); // запуск обмена по Modbus holdingRegs[reg_0] = temp; holdingRegs[reg_1] = random(9); if (holdingRegs[reg_3] == 0) { digitalWrite(LED1, LOW); } else { digitalWrite(LED1, HIGH); } //Serial.println(temp); }// конец */