Урок 57. Обмен данными между платой Ардуино и компьютером через UART по протоколу ModBus. Библиотека Tiny_ModBusRTU_Slave.

Подключение платы Ардуино к компьютеру

В уроке представлю библиотеку для разработки программного обеспечения ведомого контроллера Ардуино в сетях с протоколом ModBus RTU. В качестве примера реализую связь контроллера с компьютером через интерфейс UART.

Предыдущий урок     Список уроков     Следующий урок

В уроке 48 мы разработали простой локальный контроллер и подключили его к компьютеру через стандартный интерфейс UART. Обмен данными происходил по специализированному протоколу, созданному специально для нашего контроллера.

 

В предыдущем уроке я подробно рассказал о протоколе ModBus RTU, ставшем стандартным протоколом обмена данными в малых распределенных вычислительных системах. Считается, что любой контроллер должен поддерживать работу в сети ModBus.

Давайте и наш контроллер из урока 48 подключим к компьютеру по этому протоколу. Для этого я разработал простую библиотеку. Прежде всего, подробно опишу ее функции и возможности.

 

Библиотека Tiny_ModBusRTU_Slave.

Библиотеку Tiny_ModBusRTU_Slave можно загрузить по этой ссылке:

Зарегистрируйтесь и оплатитеВсего 60 руб. в месяц за доступ ко всем ресурсам сайта!

Библиотека позволяет простыми средствами реализовать программное обеспечение ведомого контроллера с протоколом ModBus RTU.  Может быть использована совместно с интерфейсами UART, RS-485, RS-422, RS-232 и т.п. Поддерживает управление передатчиком в шинных интерфейсах с третьим состоянием, подобных RS-485.

Слово ”Tiny” (крошечный) в названии библиотеки подчеркивает, что реализован минимальный набор функций. Всего 3 кода функции работы с регистрами хранения. Тем не менее, большинство контроллеров поддерживает именно этот набор операций.

Библиотека выполняет свои функции параллельным процессом. Все операции обмена по сети происходят в прерывании по таймеру. В основном цикле программа просто использует таблицу регистров ModBus.

Библиотека Tiny_ModBusRTU_Slave имеет конструктор и всего одну функцию.

class Tiny_ModBusRTU_Slave {
  public:
    Tiny_ModBusRTU_Slave(byte adress, byte timeOut, unsigned int * holdingRegTable, unsigned int lengthTable); // конструктор
    Tiny_ModBusRTU_Slave(byte adress, byte timeOut, unsigned int * holdingRegTable, unsigned int lengthTable, byte directPin); // конструктор
    void update();  // загрузка данных
    boolean flagRead; // признак данные были считаны
    boolean flagWrite; // признак данные были записаны
};

Конструктор.

Tiny_ModBusRTU_Slave(byte adress, byte timeOut, unsigned int * holdingRegTable, unsigned int lengthTable, byte directPin)

Создает объект со следующими параметрами:

  • address – адрес ведомого устройства в сети.  Может иметь значение от 1 до 247.
  • timeOut – время паузы (тишины) перед передачей ответа. Значение должно быть не менее времени, необходимого на передачу 3,5 байта. Вычисляется, как timeOut * период вызова функции update().
  • holdingRegTable – указатель на массив (имя массива) – таблицы регистров хранения.
  • lengthTable – размер таблицы регистров хранения.
  • directPin – номер вывода разрешения работы передатчика ведомого устройства. Используется для шинных интерфейсов, у которых передатчик имеет три состояния. Например, RS-485. Параметр не обязательный. При его отсутствии вывод не используется.

Tiny_ModBusRTU_Slave slave(1, 12, regTable, 12); // создаем объект, адрес 1, таймаут 6 мс, массив regTable, размер 12

Метод.

update() – загрузка данных. Единственная функция класса. В ней происходит все управление обменом. Функция должна вызываться регулярно в параллельном процессе, например, в прерывании по таймеру.

// обработчик прерывания 500 мкс
void  timerInterrupt() {
  slave.update();
}

Метод update() полностью контролирует обмен данными по сети. Программа основного цикла работает только с массивом regTable (таблица регистров хранения). Данные массива доступны ведущему устройству сети для чтения и записи.

На всякий случай я добавил 2 признака, сообщающие об обращении ведущего устройства к регистрам хранения.

  • flagRead – признак чтения данных. Активное состояние (true) говорит о том, что ведущее устройство прочитало хотя бы один регистр хранения. Признак должен сбрасываться в основной программе при обработке.
  • flagWrite – признак записи данных. Активное состояние сообщает о том, что произошла запись хотя бы одного регистра. Т.е. данные таблицы были изменены. Признак должен сбрасываться в основной программе.

Библиотека поддерживает коды функций.

Код функции Название Описание
03 READ HOLDING REGISTERS Чтение значений одного или нескольких регистров хранения
06 FORCE SINGLE REGISTER Запись в один регистр хранения
16 FORCE MULTIPLE REGISTERS Последовательная запись нескольких регистров хранения

Обрабатываются следующие коды ошибок.

Код ошибки Название Описание
01 ILLEGAL FUNCTION Код функции не поддерживается в контроллере
02 ILLEGAL DATA ADRESS Недопустимый адрес данных

Библиотека поддерживает широковещательный обмен, при котором ведущее устройство формирует адрес равный 0.

Максимальное число байтов одного кадра – 64 байта.

 

 

Применение библиотеки Tiny_ModBusRTU_Slave.

Для практической реализации программы ведомого Ардуино контроллера необходимо:

  • подключить библиотеку Tiny_ModBusRTU_Slave;
  • создать массив – таблицу регистров;
  • создать объект Tiny_ModBusRTU_Slave;
  • задать параметры UART (класс Serial);
  • реализовать прерывание по таймеру и в его обработчик включить функцию update().

#include <Tiny_ModBusRTU_Slave.h>
#include <TimerOne.h>

unsigned int regTable[10];
Tiny_ModBusRTU_Slave slave(1, 8, regTable, 10); // создаем объект, адрес 1, таймаут 4 мс, массив regTable, размер 10

void setup() {
  Timer1.initialize(500); // инициализация таймера 1, период 500 мкс
  Timer1.attachInterrupt(timerInterrupt, 500); // задаем обработчик прерываний
  Serial.begin(9600);
}

void loop() {
}

// обработчик прерывания
void timerInterrupt() {
  slave.update();
}

В этом скетче цикл loop() остался пустым. Тем не менее, программа будет работать. Если подключить плату к сети ModBus, то ведущий контроллер сможет записывать и считывать данные из массива regTable. Другое дело, что больше контроллер ничего не делает. Получилось сетевое запоминающее устройство на 10 регистров.

Давайте загрузим этот простой скетч и проверим работу ведомого контроллера.

Для проверки я воспользовался эмулятором ведущего устройства ModBus. Таких программ в интернете очень много. Но то, что нужно я нашел с большим трудом.

Какие-то эмуляторы ModBus платные, другие сложные, требуют установки дополнительных программ. Некоторые допускают работу только с одним регистром. Попался мне эмулятор, который вроде меня устроил. Потом оказалось, что он при каждой посылке вырабатывает импульс на сигнале DTR конвертера USB-USRT и сбрасывает плату Ардуино. Понял я это не сразу.

В конце концов, я нашел эмулятор ведущего устройства ModBus RTU, который устроил меня полностью. Это эмулятор QModBus. Описывать его не буду. В программе все просто и интуитивно понятно.

Эмулятор QModBus опубликован по ”Универсальной общественной лицензии GNU GPL”, что допускает свободное распространение программы. Поэтому выкладываю программу на сайте: QModBus-0.2.1-win32.

Эмулятор QModBus

С помощью QModBus я проверил все коды функции библиотеки, записывал и считывал разные данные, убедился, что правильно отрабатываются коды ошибок.

 

Реализация локального контроллера ModBus RTU.

Я использовал локальный контроллер из урока 48 без каких-либо изменений аппаратной части.

Повторю его схему.

Схема локального контроллера

Еще раз покажу, как он выглядит.

Локальный контроллер

Программу, конечно, надо менять.

Прежде всего, необходимо выбрать назначение регистров хранения ModBus. Я задал адреса в подряд.

Номер регистра Формат числа Параметр
0 float Температура
1
2 float Напряжение
3
4 бит Состояние кнопки (мл. бит)
5 бит Состояние светодиода (мл. бит)

Напомню, что регистры в протоколе ModBus шестнадцатиразрядные.  Получилось 6 регистров.

Осталось взять предыдущий скетч, добавить в основной цикл обработку датчиков и загрузку измеренных значений в регистры хранения.

Я использовал программу из урока 48. Добавил к ней объект Tiny_ModBusRTU_Slave.

Скетч можно загрузить по этой ссылке:

Зарегистрируйтесь и оплатитеВсего 60 руб. в месяц за доступ ко всем ресурсам сайта!

Начальную проверку я выполнил с помощью того же эмулятора QModBus.

Эмулятор QModBus

Считывал 6 регистров хранения и убедился, что передача происходит без ошибок, данные меняются. Осмысленной получилась проверка только кнопки и управление светодиодом.

 

Программа верхнего уровня.

Мы используем числовой протокол. Поэтому показания датчиков выглядят как бессмысленный набор чисел. Для того, чтобы увидеть температуру и напряжение в ”человеческом виде” я написал программу верхнего уровня. Вернее я изменил протокол обмена программы UART_Arduino_PC из урока 48. И с помощью нее окончательно проверил работу локального контроллера.

Монитор данных локального контроллера

Все работает идеально. Ни единой ошибки.

Программу верхнего уровня можете загрузить по ссылке:

Зарегистрируйтесь и оплатитеВсего 60 руб. в месяц за доступ ко всем ресурсам сайта!

 

 

Сравнение производительности протокола ModBus и специализированного протокола из урока 48.

В предыдущем уроке я сравнивал протокол ModBus со специализированными протоколами обмена данными в общем виде, без конкретных чисел. Теперь можно посчитать производительность обоих протоколов для одного и того же устройства.

Для обмена информацией с локальным контроллером по специализированному протоколу из урока 48 нам требовалось 15 байтов.

Кадры обмена

Для передачи той же самой информации по протоколу ModBus я насчитал 39 байтов. И еще паузы между кадрами.

Кадры обмена

Производительность сети ModBus более чем в два раза ниже. Это расплата  за применение универсального протокола.

Библиотеку Tiny_ModBusRTU_Slave я буду использовать в дальнейших уроках для организации проводной сети между платами Ардуино. Проверить ее работу в сети ModBus с чужими контроллерами у меня нет возможности. Но испытание совместно с эмулятором ModBus ошибок в библиотеке не выявило.

 

В следующем уроке я представлю библиотеку для реализации программного обеспечения ведущего ModBus контроллера. Свяжу в сеть две платы Ардуино через UART по протоколу ModBus.

 

Предыдущий урок     Список уроков     Следующий урок

0

Автор публикации

не в сети 15 часов

Эдуард

280
Комментарии: 1936Публикации: 197Регистрация: 13-12-2015

73 комментария на «Урок 57. Обмен данными между платой Ардуино и компьютером через UART по протоколу ModBus. Библиотека Tiny_ModBusRTU_Slave.»

  1. Отличная статья, отличные уроки. Один вопрос. Не очень понял куда будет мастер класть передаваемые данные? В те же регистры или же надо что-то другое указать?

    0
  2. Как интересно! Почему раньше не находил ваш ресурс. Одно время тоже на ардуине, модбас устройства пилил ))) Скажите, на чем пишите приложения верхнего уровня? Ткните, если есть пост об этом.

    0
  3. Здравствуйте! С Новым годом! Спасибо за уроки, всё доступно и здорово разжёвано.

    0
  4. Добрый день! Эдуард, при изучении этого урока получилось так, что при отсутствии датчика DS18B20 на шине OneWire программа выдаёт температуру 0,03 градуса. Это происходит потому, что все якобы считанные из датчика байты равны нулю, и CRC совпадает.
    Как и ожидалось, весьма актуальна задача подключения нескольких датчиков к шине OneWire. Мне удалось осуществить это только с использованием библиотеки DallasTemperature, но есть сомнения в оптимальности кода.

    0
    • Здравствуйте!
      Я использовал минимальный вариант опроса датчика чисто в демонстрационных целях. Проблема решается, если при вызове метода reset анализировать возвращаемое значение. Возврат функцией 0 означает, что от датчика не было ответного импульса присутствия, т.е. датчик не подключен или неисправен.

      0
  5. Спасибо. Есть ещё один вопрос. Получается, что при использовании MODBUS нельзя одновременно применять UART для обычных целей (контроль выполнения программы). Или есть какой-нибудь способ развести эти процессы во времени, чтобы они друг другу не мешали?

    0
  6. Здравствуйте ! Подскажите , ищу примеры для передачи данных на ПК средствами rs485 про протоколу modbus, попробовал ваш скетч модифицировать под себя , мне нужно выводить напряжение, но ничего не получилось, скачал программку QModBus,попробовал сначала ваш скетч в оригинале , выдает ошибку ( I/O error: did not receive any data from slave) , видимо проблемы выводом, а где посмотреть на какие пины выводятся rx tx ?

    0
    • Здравствуйте!
      Библиотека работает с аппаратным UART, использует стандартные выводы RX и TX. Обмен должен происходить даже через стандартный кабель Ардуино. Вы не забыли установить правильную скорость обмена и номер виртуального порта?

      0
      • У меня не совсем ардуина , но смысл тот же , выход uart проходит через оптроны на max485 , а далее уже на pci плату rs485 , скорость я установил правильную как написано в скетче и на фото в оболочке: 9600. Если UART используется аппаратный , а как управлять RE/DE ? мне кажется проблема все таки с подключением, в вашей программе вывода данных , у меня число ошибок превышает число правильных.

        0
          • У меня не стандартный ардуино , USB нету , взята плата от устройства где реализован rs 485 , как вариант до оптронов перехватить данные

            0
  7. Здравствуйте, Эдуард! Решил добавить в Вашу библиотеку функцию 0х45 (чтение float). Вот код:


    else if( _frameBuffer[1] == 45 ) {
    //——————————— функция чтения регистров
    // проверка адреса регистра
    unsigned int adr = (((unsigned int)_frameBuffer[2]) << 8) | (unsigned int)_frameBuffer[3]; //певый параметр
    unsigned int num = (((unsigned int)_frameBuffer[4]) << 8) | (unsigned int)_frameBuffer[5]; //кол-во параметров
    if( (adr + num) <= _lengthFloatTable ) {
    // адрес допустимый — кол-во запрошенных параметров <= длинны массива

    // ответ
    _frameBuffer[2] = num * 4; //т.к. один параметр в float занимает 4 байта
    for(unsigned int i=0; i> 32);
    _frameBuffer[i*4 + 4] = (byte)((*(_inputFloatRegTable + adr)) >> 16);
    _frameBuffer[i*4 + 5] = (byte)((*(_inputFloatRegTable + adr)) >> 8);
    _frameBuffer[i*4 + 6] = (byte)(*(_inputFloatRegTable + adr));
    adr++;
    }
    flagRead= true;
    unsigned int crc = calculateCRC(3 + num*4);
    _frameBuffer[3 + num*4] = (byte)(crc >> 8);
    _frameBuffer[4 + num*4] = (byte)(crc);
    _dataNum = 5 + num*4;
    _mode= 2; // переход на ответ
    }
    else {
    // ошибка адреса регистра (02)
    _frameBuffer[1] |= 0x80;
    _frameBuffer[2] = 2;
    unsigned int crc = calculateCRC(3);
    _frameBuffer[3] = (byte)(crc >> 8);
    _frameBuffer[4] = (byte)(crc);
    _dataNum = 5;
    _mode= 2; // переход на ответ
    }
    }

    и столкнулся с проблемой. Компилятор не выдает ошибку:
    C:\Program Files (x86)\Arduino\libraries\ModBusRTU_Slave\ModBusRTU_Slave.cpp: In member function ‘void ModBusRTU_Slave::update()’:

    C:\Program Files (x86)\Arduino\libraries\ModBusRTU_Slave\ModBusRTU_Slave.cpp:237:75: error: invalid operands of types ‘float’ and ‘int’ to binary ‘operator>>’

    _frameBuffer[i*4 + 3] = (byte)((*(_inputFloatRegTable + adr)) >> 32);

    ^

    C:\Program Files (x86)\Arduino\libraries\ModBusRTU_Slave\ModBusRTU_Slave.cpp:238:72: error: invalid operands of types ‘float’ and ‘int’ to binary ‘operator>>’

    _frameBuffer[i*4 + 4] = (byte)((*(_inputFloatRegTable + adr)) >> 16);

    ^

    C:\Program Files (x86)\Arduino\libraries\ModBusRTU_Slave\ModBusRTU_Slave.cpp:239:84: error: invalid operands of types ‘float’ and ‘int’ to binary ‘operator>>’

    _frameBuffer[i*4 + 5] = (byte)((*(_inputFloatRegTable + adr)) >> 8);

    ^

    exit status 1
    Ошибка компиляции для платы Arduino/Genuino Uno.
    То есть в том месте где в буфер побайтно раскладывается значение float.
    Как его правильно разложить?

    0
  8. for(unsigned int i=0; i> 32);
    _frameBuffer[i*4 + 4] = (byte)((*(_inputFloatRegTable + adr)) >> 16);
    _frameBuffer[i*4 + 5] = (byte)((*(_inputFloatRegTable + adr)) >> 8);
    _frameBuffer[i*4 + 6] = (byte)(*(_inputFloatRegTable + adr));
    adr++;
    }

    0
  9. как-то криво код добавляется
    for(unsigned int i=0; i> 32);
    _frameBuffer[i*4 + 4] = (byte)((*(_inputFloatRegTable + adr)) >> 16);
    _frameBuffer[i*4 + 5] = (byte)((*(_inputFloatRegTable + adr)) >> 8);
    _frameBuffer[i*4 + 6] = (byte)(*(_inputFloatRegTable + adr));
    adr++;
    }
    flagRead=

    0
    • Сергей, здравствуйте!
      Может не стоит этого делать. Данные передаются пакетом и удобно в пакете передавать разные типы данных. Пакеты только из float, будут ограничивать работу системы.

      Что касается ошибки компиляции.
      C:\Program Files (x86)\Arduino\libraries\ModBusRTU_Slave\ModBusRTU_Slave.cpp:237:75: error: invalid operands of types ‘float’ and ‘int’ to binary ‘operator>>’
      _frameBuffer[i*4 + 3] = (byte)((*(_inputFloatRegTable + adr)) >> 32);

      Насколько я понимаю, _inputFloatRegTable это имя массива данных типа float, т.е. указатель типа float. Вы считываете данные по этому указателю «*(_inputFloatRegTable + adr)» и получаете данное типа float. С этим данным вы производите недопустимую для float операцию сдвига. Кстати, если сдвинуть 32 разрядное число на 32, то всегда будет 0. А затем вы пытаетесь сдвинутое float явно преобразовать в целочисленный байт и записать в массив байтов. Естественно, компилятор сходит с ума. Извините за несколько саркастический тон, но ошибки явные и серьезные.

      0
  10. Tiny_ModBusRTU_Master_Mega_8
    Tiny_ModBusRTU_Slave_Mega_8

    Кому нужны адаптированные библиотеки для ATMega 8 могу скинуть.

    1
      • Пётр, усли не затруднит, скиньте мне на raqus@rambler.ru

        Tiny_ModBusRTU_Master_Mega_8
        Tiny_ModBusRTU_Slave_Mega_8

        Спасибо!

        0
  11. Эдуард, здравствуйте! Подскажите такие моменты:
    1) У Вас реализован обмен по прерыванию таймера, т.е. вызов функции timerInterrupt() через заданный период, вопрос: если я хочу, чтобы эта функция вызывалась при определенном условии в основном коде, то вызываю как обычную функцию, но это не прокатывает, летят ошибки. Поясню что хочу: хочу чтобы слейвы посылали пакет только тогда, когда этого запросит центральное устройство.
    2) Пытаюсь смоделировать простой пример передачи центральному ус-ву в Proteus, не получается, а в железе работает.

    0
    • Здравствуйте!
      В принципе, что вам мешает вызвать отдельно функцию slave.update();.
      Но функция должна вызываться регулярно в прерывании. Она посылает ответный пакет за несколько вызовов. Все временные интервалы определяются периодом вызова функции. Функция отслеживает чужие пакеты. Она должна все время вызываться.

      0
  12. Добрый день.
    Использую Модуль USB to RS485 для Arduino. QMODBUS Выдаёт такую ошибку «Slave threw exception Result too large or function not implemented» на попытку чтения регистров 0х03.
    На осциллографе пакета не вижу.
    В диспетчере WIN модуль появляется под именем USB SERIAL CH340.
    Подскажите по возможности в чем проблема?Спасибо!

    0
    • Здравствуйте!
      А какой модуль вы используете? Посмотрите осциллографом сигнал управления передатчиком драйвера сигнал DE. Программа управляет этим сигналом?

      0
      • Здравствуйте. В вашей партнёрской программе «Всё для Ардуино»,
        Интерфейсы / 1страница / Модуль компактного USB-RS485 преобразователя.

        0
      • Здравствуйте. Модуль вставляю в ПК — нет сообщения что появилось новое устройство. В диспетчере устройств модуль появляется под именем USB SERIAL CH340. Пытаюсь обновить драйвер, система говорит что всё установлено.

        0
      • нажимаю кнопку QMODBUS Выдаёт сразу ошибку «Slave threw exception Result too large or function not implemented». В модуле не увидел на осциллографе изменения на контакте DE.

        0
  13. Здравствуйте! Настроила и проверила связь Arduino Uno с компьютером по вашему уроку. Далее установила связь через виртуальный COM-порт, со скадо-системой TraceMode. Далее хочу управлять серво-приводом через скаду, т.е. на отрисованном экране скады задавать угол поворота, который должен отрабатывать привод. Не совсем пойму как организовать прием информации от скады в ардуино (также назначать регистры и просто их считывать?)? И второе, как посмотреть то, что я получила от скады? Ведь если ардуинка занята профайлером скады, то запустить монитор порта в скетче уже не получится.
    Спасибо за уроки, все было полезно!

    0
    • Здравствуйте! Спасибо за отзыв. Рад, если помог.
      Я со Скада-системами никогда не работал. Дам общие рекомендации.
      Да, необходимо назначить регистр, через который передается параметр. В вашем случае положение вала сервопривода. В Ардуино этот регистр надо считывать и передавать в управляющую программу для сервопривода.
      Монитор последовательного порта в вашей системе работать не будет. Интерфейс UART занят на RS-485. Приходится выкручиваться, судить о принятом коде по косвенным признакам. Например можно подключить к ШИМ Ардуино светодиод и убедиться, что при изменении кода светодиод плавно меняет яркость.

      0
  14. Здравствуйте!
    Хотела бы задать конкретный вопрос. Из программы верхнего уровня (скада) я осуществляю обмен дискретными данными (кнопка, led), который успешно получается. Но возникла проблема с параметром типа float. Задача: получить данные с программы верхнего уровня (которые будут осуществлять управление сервоприводом в дальнейшем) и для контроля вернуть их обратно, чтобы убедится в их корректности. Для этого назначаю регистры и выполняю следующий код:
    noInterrupts();
    // кнопка
    if (button1.flagPress == true) regTable[4]= 1;
    else regTable[4]= 0;

    // LED
    if ( (regTable[5] & 1) == 0) digitalWrite(5, LOW);
    else digitalWrite(5, HIGH);

    //контроль параметра float — вариант 1
    //alpha = * (float *) (regTable+6);
    //* (float *)regTable = alpha;

    //контроль параметра float — вариант 2 (либо так, либо так)
    regTable[0]=regTable[6];
    regTable[1]=regTable[7];
    interrupts();

    Результат: в программе верхнего уровня значение нулевого регистра меняется, но потом сбрасывается в 257, а значение первого регистра 2049 и не меняется.
    Что нужно сделать регистрами, чтобы получить параметр типа float? Нужно ли сделать сдвиг и масштабирование? Заранее спасибо!

    0
    • Здравствуйте!
      Формат float требует для хранения 4 байта. В ModBus минимальная единица информации 16ти разрядное слово. Для передачи float требуется 2 слова. Обычно используется передача в первом слове мантиссы, во втором остаток мантиссы и порядок.
      Преобразование из регистров хранения 6 и 7 в float будет выглядеть так:
      float alpha = * (float *) (regTable+6);
      Обратное преобразование:
      * (float *) (regTable+6)= alpha;

      0
  15. Здравствуйте Эдуард. Хочу поблагодарить вас за интересные статьи и уроки. Ваш сайт стал для меня практически настольной книгой )). Ну и конечно же вопрос: Из каких соображений период прерывания выбран в 500 мкс? Могу ли я вызывать функцию slave.update(); с периодом в 1 мс ?
    Вы пишите: » timeOut – время паузы (тишины) перед передачей ответа. Значение должно быть не менее времени, необходимого на передачу 3,5 байта. Вычисляется, как timeOut * период вызова функции update() «. При этом период вызова функции нам известен, а вот необходимое время паузы как определить?

    0
    • Здравствуйте!
      Период прерывания по таймеру определяет точность, с которой отрабатываются временные интервалы обмена. Если период равен 500 мкс, то погрешность будет +- 500 мкс. При скорости передачи 9600, время передачи одного байта 1 мс. Погрешность составляет +- 50%. Это вполне допустимо. Если период прерывания увеличить до 1 мс, то погрешность тоже увеличится.
      Тоже самое будет при увеличении скорости передачи.
      При уменьшении периода (увеличении частоты) прерывания процессор будет тратить больше времени на обработчик прерывания.
      Т.е. время периода прерывания зависит от скорости передачи данных и допуске по временным погрешностям.
      Время паузы определяется из времени, необходимого на передачу одного байта. В уроке 12 есть таблица зависимости времени от скорости передачи.

      0
      • Ну мне кажется материал стоит того. На мой взгляд лучшее что есть в сети. Кроме того уроки бесплатны. Плата берётся только за пользование библиотекой.

        0
    • Товарищ, вы не правы. Вы хотя бы прикиньте — какой объем работы проделан! Да просто столько текста написать — это уже подвиг, а тут не просто текст! К тому же в большинстве случаев текст скетчей и библиотек можно скопировать из текста уроков и 25 руб. вам предлагают потратить на собственное удобство. Так жлоб — это вы получаетесь…

      0
  16. Эдуард, здравствуйте. Вы пишите, что метод update должен вызываться периодически по аппаратному таймеру. А что делать, если все аппаратные таймеры заняты другими библиотеками? Например библиотеки Servo и IRremote загребают их под себя. Делать программный таймер при помощи millis() ?

    0
  17. Добрый день. Огромное спасибо за эти уроки. Буквально с первых уроков — темы для более-менее продвинутых ардуинщиков. Очень много интересного узнал в написании и оптимизации кода. Один из лучших сайтов, что есть в сети по Ардуино.
    Маленькая проблемка: Зарегистрировался, пожертвование внес, загружаю скетч, а вместо кириллицы — кракозябры, при том во всех браузерах, компьютерах и даже на телефоне пробовал. Как поправить? (Наверное что-то с кодировкой не то?)

    0
    • Здравствуйте!
      Открываете окно с кракозябрами, нажимаете на него правой кнопкой мыши, выбираете сохранить как. Файл сохраняется в исходном виде.
      Думал, что так удобнее будет скетчи просматривать, но в некоторых браузерах надо кодировку переключать.

      0
    • Ответ нашел на форуме цитирую:
      «…Можно делать так.
      Кликаете левой кнопкой мыши на ссылку и открывается текст скетча в новом окне. В некоторых браузерах латинские символы отображаются неправильно.
      Кликаете на текст программы правой кнопкой мыши. В открывшемся окошке меню выбираете «Сохранить как». Выбираете путь и сохраняете файл, который адекватно загружается в Arduino IDE…»
      Еще раз спасибо за уроки!!!

      0
      • можно ещё проще , просто нажать правой кнопкой мыши на ссылку , в открывшемся окне нажать левой кнопкой мыши на команду ( сохранить ссылку как ) и дальше откроется окно где вам надо выбрать куда сгрузить файл . как сгрузится файл со значком ардуино можете его сразу открыть нажав на него левой кнопкой , но у вас должен быть установлен arduino ide он его и откроет без иероглифов , так как файл типа ардуино и название его это последние 3 буквы ардуино- ино . короче файл открываете в ардуино .

        0
  18. Здравствуйте!
    Пока медленно ползут бандерольки с Ардуинами, осваиваю программирование. (когда-то изучал FORTRAN-IV», но это было слишком давно 🙂 )
    Прошу подсказать, на что обратить внимание при составлении программ для удаленного датчика и «базы».
    Планируется вот что:
    Удаленный датчик — отслеживает температуру и влажность в помещении, при понижении Т ниже 18С включает обогреватель через плату реле. При этом, если ток нагрузки будет меньше 1А (например) — выдается сигнал неисправности на «базу». Так же хочу сделать экран 1602, с выводом этих данных.
    База: точно такое же отслеживание Т и влажности в основном помещении — вывод на экран 2004, состояния связи (ONLINE-NO LINE), 3 и 4 строчки — принятая с удаленного датчика информация. — Т, влажность, ток нагрузки, «норма». (Или оставить блок логики в «базе» — пусть оценивает Т и ток нагрузки, и выдает сигнал?)
    Пока пытаюсь разобраться — как лучше передавать данные.
    Пробовал «рисовать» в программе FLProg — но там нашлись свои особенности.
    Как посоветуете составить программы для датчика и базы? (С учетом возможного увеличения количества выносных датчиков в будущем)
    Или вам уже попадались где-нибудь готовые (приблизительно) программы?
    Спасибо!

    0
  19. Здравствуйте, Эдуард! Вы писали:
    «При скорости передачи 9600, время передачи одного байта 1 мс.»
    А разве не 100 микросек?

    0
    • Здравствуйте!
      Посмотрите таблицу в уроке 12. Можно посчитать.
      1000000 мкс / 9600 = 104 мкс Это время передачи 1 бита, а их 8 + стартовый + стоповый биты.

      0
  20. Понял. Отличные уроки. Объясняете просто и понятно. Вы хороший преподаватель.
    Я тестировал Вашу программу, которая с пустым loop из этого урока. Использовал MsTimer2 c периодом 2 мс. У меня, вроде, отрабатывала запись-чтение без ошибок с эмулятором QModBus.
    В моём проекте используется MsTimer2, нужно читать-записывать 6-9 16 битных чисел.
    Будет-ли косячить Модбус из-за большого периода?
    Или лучше переделать под Таймер1?
    На Мастере таймер должен быть установлен на одинаковый период прерывания с Ведомым? Или это не важно?

    0
    • Здравствуйте!
      Периоды у таймеров могут быть разными, но одинаковыми должны быть паузы между кадрами, тайм-ауты и т.п. Все зависит от скорости обмена. MsTimer2 да еще с периодом 2 мс вряд ли позволит точно отработать временной контроль обмена. Используется синхронная система. Дискретность временных параметров определяется временем цикла таймера. В вашем случае плюс минус 2 мс.

      0
    • Здравствуйте!
      Нет. Я думаю, работать не будет. В библиотеке есть место, где проверяется флаг UART, сообщающий, что закончилась передача. В ESP32, наверняка, этот флаг обозначается по-другому.

      0
  21. Добрый день интересует такой вопрос можно подключить к ардуино датчики по i2c и по modbus отправлять на компьютер, если да то как?

    0
    • Здравствуйте!
      Можно, конечно. Написать программу, использовать соответствующие библиотеки. Как можно на такой вопрос ответить односложно.

      0
  22. при подключенной библиотеки выдает ошибку компиляции
    collect2.exe: error: ld returned 1 exit status
    exit status 1
    Ошибка компиляции для платы Arduino Uno.

    0
  23. Здравствуйте. У меня сын заинтересовался программированием, нашел Ваши уроки, все очень понятно, спасибо большое за доступные материалы. В простом проекте использовали Вашу библиотеку Tiny_ModBusRTU_Slave, все отлично работает, только почему-то при чтении переменной flagRead или flagWrite всегда 0, хотя запись, чтение от мастера нормально осуществляется, без ошибок. Что можно посмотреть, помогите пожалуйста. Также хотели данную библиотеку использовать с платой на 3,3 вольта с тактовой частотой 8 МГц., подскажите пожалуйста библиотека будет работать корректно?

    0
    • Здравствуйте!
      Библиотека отлажена, давно используется. Флаги должны работать. Посмотрите, как применяется библиотека в других проектах, например в рубрике Умный дом.
      Что касается второго вопроса, то надо проверять. Библиотека рассчитана на работу с ATmega168/328. В одном месте используется прямое обращение к регистру микроконтроллера. Если микроконтроллер другой, то могут быть проблемы. А напряжение питания и тактовая частота на работу библиотеки не повлияют.

      0
  24. Здравствуйте. Залил на ардуино нано ваш скетч, запустил qmodbus с настройками как на картинке и через тот же кабель и порт попробовал соединится с ардуино. Но ардуино не отвечает. К ардуино подключена только кнопка, я думаю периферия не должна влиять на соединение.
    В чем может быть причина?

    0
    • Здравствуйте!
      Да, все должно работать.
      А светодиоды на плате мигают?
      Моя программа верхнего уровня работает?

      0
      • Да пустая программа без данных работает. Только я нее понял, как в qmodbuse передавать данные на контроллер?

        0
      • Да пустая программа без данных работает. Я немного разобрался с qmodbusом, там почему-то функция записи нескольких регистров имеет номер 10, а не 16 как у вас.

        0
  25. Вопрос с отсутствие DE снимаю. Работает нормально. Сторонний контроллер Master, Arduino — Slave.
    Другой вопрос.
    В Вашем коде:
    // перегрузка напряжения и состояния кнопки в таблицу регистров
    noInterrupts();
    * (float *)(regTable+2) = voltage;
    if (button1.flagPress == true) regTable[4]= 1;
    else regTable[4]= 0;
    interrupts();

    // перегрузка состояния светодиода из таблицы регистров
    if ( (regTable[5] & 1) == 0) digitalWrite(led, LOW);
    else digitalWrite(led, HIGH);

    , а именно фрагмент:
    * (float *)(regTable+2) = voltage;
    В какой момент указывается адрес ячейки?

    0
    • В этой строке тип float (напряжение) заносится в тип int (регистры ModBus).
      * (float *)(regTable+2) = voltage;
      regTable — указатель на массив. Имя массива является указателем на него.
      regTable+2 — указатель на 3й элемент массива (0,1,2)
      (float *)(regTable+2) — указатель int преобразовывается в указатель на float
      * (float *)(regTable+2) = voltage — переменной по указателю float присваивается значение переменной float
      float — 4 байта или 2 слова. В результате voltage загрузится в элементы regTable[2] и regTable[3] массива.
      Об этом можно почитать в уроке 15.

      0

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Нажимая кнопку "Отправить" Вы даёте свое согласие на обработку введенной персональной информации в соответствии с Федеральным Законом №152-ФЗ от 27.07.2006 "О персональных данных".