Обмен данных между ардуино

Урок 1. Двусторонняя связь между двумя Arduino с использованием I2C.

При разработке проектов на Arduino часто возникает потребность в увеличении возможности, а пинов не достаточно. Также бывает необходимость объединить несколько работающих узлов для обмена данными, т.е. реализовать двухстороннюю связь между двумя Arduino. Для реализации двусторонней связи между двумя Arduino отлично подойдёт шина I2C.

Интерфейс I 2 C (или по-другому IIC) — это достаточно широко распространённый сетевой последовательный интерфейс, придуманный фирмой Philips и завоевавший популярность относительно высокой скоростью передачи данных, дешевизной и простотой реализации.

Шина I2C синхронная, состоит из двух линий: данных (SDA) и тактов (SCL). При проектировании есть 2 типа устройств: ведущий (master) и ведомый (slave). Инициатором обмена всегда выступает ведущий, обмен между двумя ведомыми невозможен. Всего на одной двухпроводной шине может быть до 127 устройств.

Такты на линии SCL генерирует ведущий (master). Линией SDA могут управлять как мастер, так и ведомый (slave), в зависимости от направления передачи. Единицей обмена информации является пакет, обрамленный уникальными условиями на шине, именуемыми стартовым и стоповым условиями. Мастер в начале каждого пакета передает один байт, где указывает адрес ведомого и направление передачи последующих данных. Данные передаются 8-битными словами. После каждого слова передается один бит подтверждения приема приемной стороной.

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

I 2 C используется во многих приложениях, таких как чтение RTC (часы реального времени), доступ к внешней памяти EEPROM. Он также используется в сенсорных модулях, таких как гироскоп, магнитометр и т. д.

Контакты Arduino I2C.

Выводы I2C расположены следующим образом на плате Arduino Uno.

Для других моделей плат соответствие выводов такое:

Источник

UART — Универсальный Асинхронный Приёмопередатчик

Общие сведения:

Универсальный асинхронный приёмопередатчик (Univsersal Asynchronos Reciever-Transmitter) — это физическое устройство приёма и передачи данных по двум проводам. Оно позволяет двум устройствам обмениваться данными на различных скоростях. В спецификацию UART не входят аналоговые уровни на которых ведётся общение между устройствами, UART это протокол передачи единиц и нулей, электрическую спецификацию на себя берут другие стандарты, такие как TTL (transistor-transistor logic — транзисторно-транзисторная логика), RS-232, RS-422, RS-485 и другие (RS [англ.recommended standard] — рекомендованный стандарт). На данный момент в микроконтроллерах используется в основном TTL (или точнее CMOS) UART для соединения не более двух устройств. В наших примерах мы часто называем его последовательным портом.

Подключение:

У каждого устройства, поддерживающего UART обычно обозначены два вывода: RX и TX. TX — означает transmit (передаю), RX — receive (принимаю). Отсюда становится понятно что RX одного устройства нужно подключать к TX другого. Если Вы подключите RX одного устройства к RX другого, то оба устройства будут слушать друг друга, вы соединили их входы. Если соединить TX и TX — это уже более опасно, это выходы низкого сопротивления устройств и если на одном будет логическая единица, а на втором ноль — по проводу пойдёт ток короткого замыкания (это зависит от конкретной программной или аппаратной реализации). Хотя в современных чипах от этого есть защита, на всякий случай, не стоит на неё ориентироваться. Так же необходимо объединить референсные уровни двух устройств (GNDGND), если не подразумевается гальваническая развязка.

Пример соединения двух UNO:

UART на Arduino:

На Arduino и Arduino-совместимых платах аппаратный UART обозначается символами RX и TX рядом с соответствующими выводами. На Arduino UNO/Piranha UNO это 0 и 1 цифровые выводы:

Arduino UNO/Piranha UNO

В скетче инициализируется функцией begin() в коде функции setup():

Пример:

Piranha ULTRA

На Piranha ULTRA присутствуют два аппаратных UART. Один на тех же выводах, что и UNO, второй на 8 (RX) и 9 (TX) выводах:

В Arduino IDE второй аппаратный UART называется Serial1 (Сериал один), и инициализируется так же как и первый:

Простой пример для копирования буфера первого UART’а во второй и наоборот:

Arduino MEGA

У Arduino MEGA, помимо UART’a на цифровых выводах 0 и 1 как и у UNO, присутствуют ещё три аппаратных UART. На плате это выводы 19 (RX1), 18 (TX1), 17 (RX2), 16 (TX2) и 15 (RX3), 14 (TX3) соответственно. UART совместимый по расположению с UNO обозначен RX0, TX0:

На заметку: На многих Arduino и Arduino-совместимых платах UART0 используется для загрузки скетчей, так что если Ваш скетч не загружается, проверьте эти выводы. Во время загрузки скетча к ним ничего не должно быть подключено.

Отладка проектов при помощи UART

В совокупности с монитором последовательного порта UART может быть очень полезен для отладки кода. Например, если не понятно доходит ли Ваш код до определённого места или какие значения принимает переменная, можно вызвать функцию println() и вывести значение переменной или какое-нибудь сообщение в монитор порта. В Arduino IDE монитор порта находится в меню Инструменты -> Монитор порта, так же его можно вызвать нажав пиктограмму лупы в правом верхнем углу главного окна Arduino IDE или вызвать с клавиатуры комбинацией клавиш Ctrl+Shift+M. Перед вызовом монитора порта необходимо выбрать порт с которым Вы хотите работать. Сделать это можно в меню Инструменты -> Порт.

Для удобства отладки можно указать директивы препроцессора языка Си #define , #ifdef , #ifndef

Пример:

Программный UART на Arduino

Помимо аппаратного UART в Arduino можно использовать программный. Программный порт хорошо подходит для простых проектов, не критичных к времени работы кода или для отладки проектов, позволяя не отключать модули использующие UART во время загрузки сетчей. При его использовании нужно лишь помнить что никакой другой код не может выполняться пока программа занимается считыванием данных из него и передача может осуществляться только в полудуплексном или симплексном режимах. Так же на программный RX можно назначать только те выводы, которые поддерживают прерывание по смене уровней. На UNO, например, это все цифровые выводы, кроме 13-го. Прежде чем собирать свой проект, проконсультируйтесь с инструкцией к конкретной плате.

Пример использования программного порта:

Далее к программному порту нужно обращаться через объект mySerial . Например: mySerial.write(data); .

UART на Raspberry Pi:

На Raspberry Pi UART находится на выводах колодки 8 — TX (GPIO14) и 10 — RX (GPIO15)

Перед работой с последовательным портом необходимо его включить. Сделать это можно из эмулятора терминала командой sudo raspi-config -> Interfacing options -> Serial -> No -> Yes -> OK -> Finish или из графической среды в главном меню -> Параметры -> Raspberry Pi Configuration -> Interfaces -> Serial Port

Пример работы с последовательным портом на Python:

Данный пример выводит строку «iArduino.ru» в последовательный порт Raspberry и ждёт данных из последовательного порта.

Подробнее о UART:

Параметры

При обозначении параметров UART принято использовать короткую запись ЦИФРАБУКВАЦИФРА

  • ЦИФРА — количество бит в кадре
    • от 5 до 9 бит. Обычно 8.
  • БУКВА — наличие и тип бита чётности
    • N — None (Отсутствует) без бита чётности
    • E — Even (Чётный). Проверка данных на чётность. Перед стоп-битом в кадр добавляется бит: 0 если в кадре было нечётное количество единиц, 1 — если чётное.
    • O — Odd (Нечётный). Проверка данных на нечётность. Перед стоп-битом в кадр добавляется бит: 1 если в кадре было нечётное количество единиц, 0 — если чётное.
  • ЦИФРА — длительность стоп-бита
    • 1, 1.5, 2. Продолжительность стоп-бита (1, 1.5 или 2 битовых интервала)

Таким образом, стандартные настройки в Arduino: 8-N-1

Кадрирование данных

При приёме-передаче данных каждое устройство ориентируется на своё внутреннее тактирование. Обычно это тактирование от 8 до 16 раз быстрее скорости передачи данных и обычно отсчитывается от стартового бита. Именно поэтому необходимо чтобы оба устройства были настроены на одну и ту же скорость передачи.

Так же при передаче данных присутствуют синхронизирующие биты, именуемые старт-бит и стоп-бит. Старт-бит сигнализирует о начале передачи данных и стоп-бит, соответственно об окончании.

Рассмотрим кадр данных:

При разговорах о серийный протоколах принято использовать такие слова как кадр и пакет. Кадр — интервал от старт-бита до стоп-бита. Пакет — количество кадров полезных данных. При этом не стоит путать кадр и байт: байт — это только сами данные, не включающие в себя синхронизирующие и проверочные биты.

Старт-бит:

При отсутствии передачи линия удерживается в состоянии логической единицы (в случае TTL Arduino это 5 вольт или Vcc). Как только передающее устройство притягивает линию к 0 (GND или 0 вольт в случае Arduino), это сигнализирует принимающему устройству о том что сейчас будет передача данных.

Данные:

При появлении старт-бита на линии принимающее устройство начинает отсчитывать время в соответствии с установленной скоростью и считывать состояния линии через определённые промежутки времени в соответствии с установленным количеством бит данных, после этого.

Стоп-бит:

По завершении передачи данных принимающее устройство ожидает стоп-бит, который должен быть на уровне логической единицы. Если по завершении кадра удерживается логический ноль, значит данные неверны. Если логический ноль удерживается время, превышающее длину кадра в 1,5 раза, такое состояние именуется break (разрыв линии, исторически пошло от устройств, использующих токовую петлю для передачи данных). Некоторые передатчики вызывают это состояния специально перед посылкой пакета данных. Некоторые приёмники считают такое состояние за неправильно выставленную скорость и сбрасывают свои настройки на установки «по умолчанию».

Скорость передачи данных

Скорость изменения логических уровней (импульсов) на линии принято измерять в бодах. Единица измерения названа так в честь французского изобретателя Жана Мориса Эмиля Бодо.

Скорость при использовании UART может быть любой, единственное требование — скорости передающего и принимающего должны быть одинаковы. Стандартная скорость UART принята за 9600 бод. Arduino без проблем и лишних настроек может принимать и передавать данные на скоростях до 115200 бод.

Так как при передаче данных присутствуют синхронизирующие биты, именуемые старт-бит и стоп-бит, не совсем корректно говорить, что скорость 9600 бод равна 9600 битам в секунду. Если речь идёт о полезных данных, то реальная скорость на 20% ниже. Например, если выставлены параметры 8-N-1 и 9600 бод, то на передачу одного байта уходит десять бит, и 9600/10 = 960 байт, что равно 7680 битам в секунду.

Методы связи

UART позволяет одновременно передавать и принимать данные, однако не всегда это возможно или нужно. Например, если Вам нужно только получать не критические данные (которые можно проверить следующим пакетом, например расстояние, посылаемое лидаром каждые несколько сотен миллисекунд) от цифрового датчика или любого другого устройства и не нужно ничего передавать, такой метод называется симплексным. Всего различают три метода связи:

  • Полнодуплексная — когда ведущий и ведомый могут одновременно принимать и передавать (одновременная передача в обе стороны)
  • Полудуплексная — когда ведущий и ведомый поочерёдно принимают и передают (Поочерёдная передача в обе стороны)
  • Симплексная — когда ведущий или ведомый только передают (Передача в одну сторону)

Источник

Отправка и парсинг Serial

Общение по Serial

Данный урок переехал из урока о мониторе порта, рекомендуется сначала изучить его. Стандартные инструменты “библиотеки” Serial позволяют отправлять и принимать данные по интерфейсу UART. Здесь мы рассмотрим некоторые алгоритмы и протоколы связи, чтобы наладить управление программой через монитор порта в ручном режиме, или использовать для этого специальные программы, а также приложение на смартфоне и Bluetooth-UART модули. Между двумя Ардуинами, или между Ардуино и другим МК (esp8266, STM32) можно общаться по Serial. Для этого нужно соединить их следующим образом:

  • Соединить GND, ибо сигнал не ходит по одному проводу.
  • Для односторонней связи соединить дата-пины у отправителя -> приёмника как TX -> RX.
    • Сериал может быть как аппаратный (пины подписаны на плате), так и программный, например встроенная библиотека SoftwareSerial.h. У неё пины указываются вручную.
  • Для двухсторонней связи нужно соединить также RX -> TX.
    • Внимание! Если прошивка загружается через аппаратный юарт, например TX RX на Arduino Nano, то пин RX нужно освободить, иначе прошивка не загрузится.

Для передачи данных между платами можно использовать разобранные выше функции и разобранные ниже алгоритмы, но я хочу показать вам один особенно удобный способ передачи структур данных при помощи стандартных средств ядра Ардуино. Напомню, структура представляет собой набор данных из любых типов, что очень удобно для передачи разных данных. Отправка данных осуществляется при помощи Serial.write(байтовый буфер, размер) , а приём – при помощи Serial.readBytes(байтовый буфер, размер) . Минус readBytes заключается в том, что она блокирующая: выполнение кода не идёт дальше, пока функция не примет указанное количество байт или не завершит работу по таймауту, про таймаут написано выше в этом уроке. Обе функции принимают байтовый буфер, но мы с вами знаем про указатели и их типы, поэтому можем обманом (byte*) заманить структуру в отправку и чтение. Ниже показываю примеры как отправить и принять структуру с одной Ардуины на другую при помощи SoftwareSerial, таким же образом можно использовать обычный аппаратный Serial. Также поделюсь примером отправки и чтения с контролем целостности данных – CRC, который сильно повышает надёжность передачи: позволит распознать ошибку в пакете, если хоть один бит был передан или принят неправильно. Примеры будут работать на любых Ардуино-совместимых платах, в том числе на базе esp8266. С ней есть некоторые особенности, о них расскажу ниже.

  • Тип данных int занимает на esp 4 байта, то есть Ардуина должна принимать этот тип как long .
  • Компилятор esp очень странно пакует байты в структуру, поэтому однобайтные типы данных нужно размещать в конце структуры. Например так:

Другие алгоритмы парсинга

В реальном устройстве часто требуется передавать несколько параметров, например у нас Bluetooth танк. Мы ему должны отправить например скорость правой гусеницы, скорость левой гусеницы, положение башни, состояние подсветки, команду на выстрел… Да что угодно. Как быть в таком случае? Тут начинается настоящий парсинг, и появляются варианты, нам придётся придумывать собственный протокол связи. Есть два базовых варианта: отправка пакета всех-всех данных и его парсинг, или отправка отдельно каждого параметра с уникальным “ключом” у каждого. Как это понимать: суть первого варианта состоит в принятии пакета данных, которые разделены разделителем. Также правильно будет выделить начало и конец посылки. Пример: $120 80 180 1; – начальный символ $, разделитель ” ” (пробел) и завершающий символ ; . Наличие начального и завершающего символа повышает скорость работы и помехозащищённость связи. Второй вариант – посылки вида MOT1_120, содержащие ключ и значение, соответствующее этому ключу. Как реализовать данные способы парсинга я очень подробно разбирал в примерах в сборнике полезных алгоритмов Arduino, раздел “Работа с Serial”. Но давайте я оставлю их также и здесь, пользуйтесь!

Источник

Обмен данных между arduino

ОБОРУДОВАНИЕ
ТЕХНОЛОГИИ
РАЗРАБОТКИ

Блог технической поддержки моих разработок

Урок 49. Обмен данными между платами Ардуино через интерфейс UART.

Создадим из 2 плат Ардуино простую распределенную систему сбора и отображения информации. Разработаем центральный контроллер и свяжем его с локальным контроллером из предыдущего урока.

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

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

Разработка схемы центрального контроллера.

Что должен делать центральный контроллер?

  • Обеспечивать обмен данными с локальным контроллером.
  • Отображать данные, полученные от локального контроллера:
    • температуру;
    • напряжение;
    • состояние кнопки.
  • Передавать данные на локальный контроллер, в нашем случае управлять светодиодом локального контроллера.
  • Контролировать состояние обмена. Мы проверяем работу сети и программы, поэтому необходимо фиксировать каждую ошибку обмена.

Для осуществления этих функций достаточно подключить к плате Ардуино LCD дисплей и кнопку. На дисплее будут отображаться все параметры системы, а с помощью кнопки можно управлять светодиодом локального контроллера. Учитывая, что этот центральный контроллер мы будем использовать в последующих уроках в системах с несколькими локальными контроллерами, я решил подключить 3 кнопки.

Схема центрального контроллера выглядит так.

В качестве ЖК дисплея я использовал WH2004A. У индикатора 4 строки по 20 символов. Подключил я его в 4 битном режиме. Схема аналогична схеме из урока 23. В этом же уроке можете посмотреть информацию о LCD индикаторах, подключении их к микроконтроллерам и о программировании дисплеев такого типа.

Собранная система из двух контроллеров у меня выглядит так.

Центральный контроллер я собрал на базе платы Arduino UNO R3. Запитал его стандартным кабелем от USB порта компьютера. Для питания локального контроллера использовал сигнал 5V платы Arduino UNO R3. В рабочем варианте можно отключить плату от компьютера, подав питание каким-либо другим способом.

Разработка резидентной программы центрального контроллера.

Опять обязательное условие – управление обменом данными с локальным контроллером должно происходить параллельным процессом.

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

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

Определим интерфейс между основной программой и модулем обмена данными по сети. Достаточно трех компонентов:

byte dataFromLC[10]; // буфер данных для приема из локального контроллера
byte dataToLC; // данные для передачи в локальный контроллер (светодиод)
byte stateCommun; // состояние обмена
// (0 — успешно закончено, 1 — пуск, 2 — продолжается, 3 — ошибка)

Последовательность работы следующая:

  • В переменную dataToLC мы кладем данное для передачи на локальный контроллер. У нас имеет значение только младший бит – состояние светодиода локального контроллера.
  • В переменную stateCommun загружаем число 1, что инициирует процесс обмена данными.
  • Ждем когда stateCommun примет значения 0 или 3. 0 означает, что обмен данными закончился успешно, 3 — сообщает об ошибке обмена.
  • Используем полученные из локального контроллера данные, которые оказываются в массиве dataFromLC[].

Я сразу приведу скетч программы управления обменом через интерфейс UART.

#define TIME_OUT 25 // время тайм-аута ответа (мс)

byte dataFromLC[10]; // буфер данных для приема из локального контроллера
byte dataToLC; // данные для передачи в локальный контроллер (светодиод)
byte stateCommun; // состояние обмена
// (0 — успешно закончено, 1 — пуск, 2 — продолжается, 3 — ошибка)

byte timeOutCount; // счетчик времени тайм-аута обмена

void setup() <
MsTimer2::set(1, timerInterrupt); // прерывание по таймеру 1 мс
MsTimer2::start(); // разрешаем прерывание
Serial.begin(9600); // 9600 бод
>

// ——————— обмен данными
timeOutCount++; // счетчик тайм-аута ответа

if ((stateCommun == 0) || (stateCommun == 3)) <
// обмен закончен без ошибкой или с ошибкой
// ожидание следующего пуска
timeOutCount= 0;
>

else if (stateCommun == 1) <
// пуск обмена, посылка команды
Serial.write(0x10); // код операции
Serial.write(dataToLC); // данные (светодиод)
Serial.write(0x10 ^ dataToLC ^ 0xe5); // контрольный код
stateCommun= 2; // переход на ожидание ответа
>

else if (stateCommun == 2) <
// ожидание ответа от локального контроллера
if (Serial.available() == 12) <
// принято 12 байтов
// чтение данных в буфер, проверка контрольной суммы
byte buf[10];
unsigned int sum= 0; // контрольная сумма
for (int i=0; i
buf[i]= Serial.read();
sum += buf[i];
>
sum ^= 0xa1e3;
if ( ((* ((byte *)(& sum))) == Serial.read()) && ((* (((byte *)(& sum)) + 1)) == Serial.read())) <
// контрольная сумма правильная
// перегрузка принятых данных в dataFromLC
for (int i=0; i
dataFromLC[i] = buf[i];
>
stateCommun= 0; // операция успешно закончена
>
else <
// контрольная сумма неправильная
// очистка буфера порта
while (true)
stateCommun= 3; // окончание операции с ошибкой
>
>
else <
// проверка тайм-аута
if (timeOutCount > TIME_OUT) <
// ошибка тайм-аута (ответ не пришел)
// очистка буфера порта
while (true)
stateCommun= 3; // окончание операции с ошибкой
>
>
>
else <
// сбой программы
stateCommun= 3;
>
>

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

Эта программа ничего не делает. В ней реализован только модуль управления обменом данными. Цикл loop() пустой, основной программы нет.

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

  • Модуль управления обменом данными вызывается в обработчике прерывания по таймеру с периодом 1 мс.
  • В нем проверяется состояние переменной stateCommun. Если его значение равно 1, то формируется команда обмена.
  • Дальше ожидается приход 12 байтов ответа.
  • Если в течение времени тайм-аута, заданном в константе TIME_OUT, ответ не приходит, то порт сбрасывается и в переменную stateCommun загружается число 3, признак ошибки.
  • Если все 12 байтов пришли, то проверяется контрольная сумма и данные перегружаются в массив dataFromLC[].

Основная программа контроллера.

Я посчитал, что основная программа центрального контроллера должна отображать:

  • текущее число циклов обмена;
  • число ошибок обмена;
  • значение температуры;
  • значение напряжения;
  • состояние кнопки локального контроллера.

Числа циклов и ошибок обмена необходимы для того, чтобы оценить состояние нашей сети. Ошибки в сети это нормальное явление. Это могут быть импульсные помехи, наводки и т.п. Ошибочные данные выявляются с помощью контрольного кода и игнорируются. Но ошибок не должно быть слишком много. Они не должны быть регулярными. Регулярные ошибки обмена говорят о неисправностях сети или некорректной работы программы.

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

Я выбрал период цикла основной программы 250 мс. Т.е. 4 раза в секунду основная программа обменивается данными с локальным контроллером и выводит считанные параметры на экран дисплея.

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

Основная программа работает в цикле loop().

// инициализация обмена с локальным контроллером
// состояние кнопки
if (button1.flagPress == true) dataToLC= 1;
else dataToLC= 0;
stateCommun= 1; // инициализация обмена
cyclCount++; // счетчик циклов

delay(50); // время на завершения обмена

if (stateCommun == 0) <
// данные успешно получены
// вывод данных на дисплей
disp.clear(); // очистка экрана
disp.print(«C=»);
disp.print(cyclCount);
disp.print(» E=»);
disp.print(errorCount);
disp.setCursor(0, 1);
disp.print(«T=»);
disp.print(* ((float *)dataFromLC),1);
disp.print(» C U=»);
disp.print(* ( ((float *)dataFromLC) +1 ),1);
if ( (dataFromLC[8] & 1) == 0) disp.print(» V B=F»);
else disp.print(» V B=P»);
>
else if (stateCommun == 3) <
// обмен завершен с ошибкой
errorCount++; // счетчик ошибок

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

Если данные приняты с ошибкой, то на дисплей выводится сообщение ”ERROR”. Это не совсем правильно. В рабочих программах необходимо при ошибочных данных подождать следующие, оставив на дисплее последние правильные данные. И только после нескольких в подряд ошибках обмена выводить сообщение об ошибке.

Проверка работы системы.

При загрузке программы в центральный контроллер из Arduino IDE необходимо отключать сигнал RX. Это связано с тем, что локальный контроллер удерживает сигнал в высоком уровне и блокирует сигнал от преобразователя интерфейсов USB-UART. Компьютер не в состоянии передать данные на микроконтроллер.

Другой способ – в момент загрузки программы в центральный контроллер удерживать кнопку ”RESET” локального контроллера в нажатом состоянии. Все выводы микроконтроллера локального контроллера перейдут в состояние входов, и сигнал RX не будет блокироваться.

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

У меня не появилось ни единой ошибки обмена.

Дальше я нажал кнопку ”RESET” локального контроллера и убедился, что на дисплее отображается ошибка.

Отпустил кнопку “RESET” – ошибка обмена исчезла, обмен возобновился.

Мы сделали очень простую распределенную систему сбора и отображения информации. По такому принципу может быть построена система с гораздо большим количеством датчиков и исполнительных механизмов.

Я совершенно не касался вопросов помехозащищенности интерфейса UART, длины линий связи и требованиям к ним. Об этом в следующем уроке. Там же я расскажу о других радиальных интерфейсах, позволяющих значительно увеличить расстояние между контроллерами сети.

Источник

Отправка и парсинг Serial

Общение по Serial

Данный урок переехал из урока о мониторе порта, рекомендуется сначала изучить его. Стандартные инструменты “библиотеки” Serial позволяют отправлять и принимать данные по интерфейсу UART. Здесь мы рассмотрим некоторые алгоритмы и протоколы связи, чтобы наладить управление программой через монитор порта в ручном режиме, или использовать для этого специальные программы, а также приложение на смартфоне и Bluetooth-UART модули. Между двумя Ардуинами, или между Ардуино и другим МК (esp8266, STM32) можно общаться по Serial. Для этого нужно соединить их следующим образом:

  • Соединить GND, ибо сигнал не ходит по одному проводу.
  • Для односторонней связи соединить дата-пины у отправителя -> приёмника как TX -> RX.
    • Сериал может быть как аппаратный (пины подписаны на плате), так и программный, например встроенная библиотека SoftwareSerial.h. У неё пины указываются вручную.
  • Для двухсторонней связи нужно соединить также RX -> TX.
    • Внимание! Если прошивка загружается через аппаратный юарт, например TX RX на Arduino Nano, то пин RX нужно освободить, иначе прошивка не загрузится.

Для передачи данных между платами можно использовать разобранные выше функции и разобранные ниже алгоритмы, но я хочу показать вам один особенно удобный способ передачи структур данных при помощи стандартных средств ядра Ардуино. Напомню, структура представляет собой набор данных из любых типов, что очень удобно для передачи разных данных. Отправка данных осуществляется при помощи Serial.write(байтовый буфер, размер) , а приём – при помощи Serial.readBytes(байтовый буфер, размер) . Минус readBytes заключается в том, что она блокирующая: выполнение кода не идёт дальше, пока функция не примет указанное количество байт или не завершит работу по таймауту, про таймаут написано выше в этом уроке. Обе функции принимают байтовый буфер, но мы с вами знаем про указатели и их типы, поэтому можем обманом (byte*) заманить структуру в отправку и чтение. Ниже показываю примеры как отправить и принять структуру с одной Ардуины на другую при помощи SoftwareSerial, таким же образом можно использовать обычный аппаратный Serial. Также поделюсь примером отправки и чтения с контролем целостности данных – CRC, который сильно повышает надёжность передачи: позволит распознать ошибку в пакете, если хоть один бит был передан или принят неправильно. Примеры будут работать на любых Ардуино-совместимых платах, в том числе на базе esp8266. С ней есть некоторые особенности, о них расскажу ниже.

  • Тип данных int занимает на esp 4 байта, то есть Ардуина должна принимать этот тип как long .
  • Компилятор esp очень странно пакует байты в структуру, поэтому однобайтные типы данных нужно размещать в конце структуры. Например так:

Другие алгоритмы парсинга

В реальном устройстве часто требуется передавать несколько параметров, например у нас Bluetooth танк. Мы ему должны отправить например скорость правой гусеницы, скорость левой гусеницы, положение башни, состояние подсветки, команду на выстрел… Да что угодно. Как быть в таком случае? Тут начинается настоящий парсинг, и появляются варианты, нам придётся придумывать собственный протокол связи. Есть два базовых варианта: отправка пакета всех-всех данных и его парсинг, или отправка отдельно каждого параметра с уникальным “ключом” у каждого. Как это понимать: суть первого варианта состоит в принятии пакета данных, которые разделены разделителем. Также правильно будет выделить начало и конец посылки. Пример: $120 80 180 1; – начальный символ $, разделитель ” ” (пробел) и завершающий символ ; . Наличие начального и завершающего символа повышает скорость работы и помехозащищённость связи. Второй вариант – посылки вида MOT1_120, содержащие ключ и значение, соответствующее этому ключу. Как реализовать данные способы парсинга я очень подробно разбирал в примерах в сборнике полезных алгоритмов Arduino, раздел “Работа с Serial”. Но давайте я оставлю их также и здесь, пользуйтесь!

Источник

Adblock
detector