Научить разговаривать ардуинки по проводам

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

  1. yden

    yden Гик

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

    Помогите разобраться, плиз.

    Делаю свой проект "безумного дома". По дому раскиданы nano, каждая отвечает за свой сегмент. Так же есть одна uno - исполняет роль сервера. Роль сервера - посредник между всеми сегментами сети + сбор данных. Например, nano, отвечающий за свет, к ней подключен датчик света и часы. Значения датчика света нужно использовать в другом сегменте сети - отвечает за наружный свет. Вот сервер берет данные с одной ардуинки и передает их другой. Плюс в дальнейшем буду визуализировать эти данные в вебе. Данные планирую передавать по проводам, используя rs485.

    Сделал упор на modbus, что-то начало получаться, пока не столкнулся с проблемой, которая начисто режет все мои идеи. Это количество регистров, которые может хранить у себя ардуинка - 30. Для моей затеи это мало, так как планируется подключения множества датчиков ds18b20.

    Вопрос: подкиньте плиз рабочие идеи как можно реализовать задуманное используя провода, nano\uno, rs485.



    Спасибо
     
  2. rkit

    rkit Гуру

    А вы собрались больше 30 датчиков на одного модбус-слейва вешать? Что-то странное.
     
  3. yden

    yden Гик

    Конечно нет.
    Например, 4 слейва: гараж, баня+стайка, вентиляция дом, сигнализация.
    Гараж - 2 ds18b20
    Баня - 2 ds18b20
    Вентиляция дом - 7 ds18b20
    Сигналка - 10 ds18b20 + 3 датчика протечки.
    Это уже 24 регистра. Это все минимум. Плюс статус релюшек.
    Также хотел по 2 регистра на слейв отдать на проверку зависания слейвов.
     
  4. Onkel

    Onkel Гуру

    тут кстати про rs485 и протокол Applied Digital речь заходила, хорошая штука, поддерживается многими интеграторами вроде Motorola Premise Home и подобными, а также всеми брендами от Crestron и пр.
     
  5. sslobodyan

    sslobodyan Гик

    Скажите, а где вы столкнулись с ограничением в 30 регистров? Покажите свой код.
     
  6. yden

    yden Гик

  7. Radius

    Radius Гик

    Непонятно почему вам не хватает 30 регистров. Температуру можно передавать одним байтом, это будет диапазон -127...+127 градусов. Реле до 8 штук в одном байте. Контактных датчиков тоже до 8 шт. в одном байте. Мне кажется что для бани или гаража это "выше крыши".
     
  8. sslobodyan

    sslobodyan Гик

    На сколько я понял, это ограничения конкретной библиотеки.
    И все это растет из буфера в 64 байта. То есть если вам будет необходимо запросить все регистры последовательно, то их будет не более 30. Запрашивайте по одному, кто мешает? Задавайте свой HOLDING_REGS_SIZE и вперед, лишь бы оперативки хватило.

    [OFF]
    Я уже который раз сталкиваюсь с тем, что ардуина превратилась в набор из кубиков, причем самых разных размеров. И юзеры пытаются из них собирать домики, не придавая значения их размерам, не пытаясь их подстраивать под свои задачи. Жаль.
    [/OFF]
     
  9. yden

    yden Гик

    Не могу сообразить как по одному запрашивать.
     
  10. sslobodyan

    sslobodyan Гик

    Надо почитать спецификацию протокола и написать все самому. Я чуть внимательнее посмотрел на предложенную вами библиотеку. Не понравилась. В модбас рту установлены жесткие временные ограничения. А в этой библиотеке так и написано, что сделано не очень правильно, но вроде в большинстве случаев работает :) Разработчики не используют прерывание по приему байта, а считают, что пакеты достаточно редко идут и их можно отлавливать время от времени мониторя Serial.available(). И еще библиотека скопом запрашивает все определенные пакеты. А если вам надо один регистр раз в полчаса считать? Зачем молотить его постоянно? Плюс значения регистров хранятся локально в озу ардуинки. Опять же - зачем? Запросили только то, что в данный момент интересует, обработали и забыли. Память то не резиновая. Ладно, оставим это их совести.
    По вашему вопросу. В библиотеке конструируются пакеты. Один из параметров data - это и есть длина посылки, точнее сколько регистров (16-битных) запрашивается.
     
    yden нравится это.
  11. yden

    yden Гик

    Спасибо за подробный анализ.
    Я верно понял, что библиотека modbus rtu более надежна и логична?
    Можете поделиться рабочим кодом для мастера и слева - работа в паре, плиз? И ссылку на используемую библиотеку. Реально на одном месте топчусь.
    Благодарю.
     
    Последнее редактирование: 25 мар 2017
  12. Golowa70

    Golowa70 Нуб

    https://m.habrahabr.ru/company/flprog/blog/262521/
     
  13. yden

    yden Гик

    Еще актуально, так как не разобрался.
     
  14. yden

    yden Гик

  15. Пушной звер

    Пушной звер Оракул

  16. yden

    yden Гик

    Благодарю.
    Не здесь не modbus.
     
  17. yden

    yden Гик

    На правах топикстартера отпишусь.
    За основу взял библиотеки 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);

    }// конец

    */