Обмен информацией между ардуино

Тема в разделе "Проводная и беспроводная связь", создана пользователем yden, 18 янв 2017.

  1. yden

    yden Гик

    Здравствуйте.

    Прошу совета, плиз. "Безумный дом". Часть уже реализовал, идея следующая: есть несколько arduino nano, каждый отвечает за свой сегмент. Например, один - свет, другой вентиляция, третий розетки и т.д., на каждом крутится свой код, подключены свои датчики, релюшки. Есть arduino uno - будет исполнять роль сервера, мастер. забирает информацию с nano (slave), дает указания. Например, к nano, отвечающей за свет подключен датчик освещенности. Эта информация (освещенность) нужна другому nano. Slave1 посылает эту информацию мастеру, а тот в свою очередь пересылает другому слейву. Канал связи между контроллерами - rs485 и nrf24l01 (некоторые контроллеры удобнее подключить по радио). Информация должна бегать и от мастера к слейву и от слейва к мастеру.

    Зачем еще нужен мастер? В перспективе хочу выводить информацию в веб (т.е openhub или majordomo).

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

    спасибо
     
  2. mcureenab

    mcureenab Гуру

    Для радио наверное придется "умный" мост между rs485 и nrf24l01 сделать, чтобы nrf24l01 не шумел в эфир запросами, которые ему не адресованы и главное чтобы из шумного эфира случайные "данные" в шину не сливал. Но это проверить надо. Может ошибки будут случаться достаточно редко.

    А на счёт орешков не понятно, что вам не понятно.

    Физически так подключается.

    [​IMG]

    В скетч подключаем библиотеку с поддержкой протокола modbus и забываем про провода.
     
  3. yden

    yden Гик

    Не могу разобраться с кодом запроса от мастера к слейвам, и ответ слейва. Можно сказать каша.
    Например, мастер шлет сообщение, адресованное id 2, передает значение времени (14 часов). Слейв ему отвечает, шлет значение с датчика DHT, статус реле1.
     
    Последнее редактирование: 18 янв 2017
  4. Radius

    Radius Гик

    Если что то не понятно по modbus посмотрите вот это видео, очень толково изложено:


    Слейвы сами к мастеру обращаться не могут. Мастер должен постоянно опрашивать слейвы и если у них что-то есть (запрос на обслуживание или изменение состояния датчика), то они передают это мастеру, а мастер принимает решение что делать с данным запросом.
     
    yden нравится это.
  5. mcureenab

    mcureenab Гуру

    Какими библиотеками пользуетесь? Выкладывайте свои скетчи для Master и Slave с вопросами.
     
  6. yden

    yden Гик

    Спасибо огромное за ролик.
    Да, я в курсе что инициатор связи в modbus всегда мастер. Поэтому у мастера адрес всегда 0 - широковещательный.
     
  7. yden

    yden Гик

    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. Где этот запрос-ответ и в каком виде вставить?
     
  8. mcureenab

    mcureenab Гуру

    В 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 и т.д.)
     
  9. mcureenab

    mcureenab Гуру

  10. rkit

    rkit Гуру

    В принципе, идея там такая, что слейв держит информацию в регистрах, мастер ее забирает, когда ему надо. Или записывает свою.
     
  11. yden

    yden Гик

    благодарю.
     
  12. yden

    yden Гик

    Доброго всем времени.
    Прошу помощи, плиз.
    При компиляции кода мастера (пример из библиотеки 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()/*
       
    Как ее исправить?

    спасибо
     
  13. yden

    yden Гик

    Разобрался.
    Проблема с библиотеками была - солянка.