Связь нескольких Arduino по проводу [GyverBus]

ОБНОВЛЕНИЯ


  • v2.3: добавлена возможность отправки широковещательного сообщения (всем), отправлять на адрес 255
  • v2.4: исправлены ошибки, добавлена bool statusChanged() для GBUS

ТЕОРИЯ


Не нашёл нормальных способов связать несколько Ардуинок в проводную сеть, решил сделать свой. Да, существуют библиотеки на базе Wire (например EasyTransfer), но там всё очень криво, по двум проводам и на слишком высокой частоте, т.е. на большом расстояние отправить не получится. В рассмотренной библиотеке предлагается использовать программный аналог интерфейса UART с двухсторонней связью по одному проводу, встроенным протоколом передачи, адресацией, упаковщиком и распаковщиком данных, контролем целостности и прочими приколами. Скорость интерфейса невысокая, что позволяет отправлять данные по проводам на очень большие расстояния и с высокой надёжностью, а лёгкая реализация вмещается даже в ATtiny13 на приём и на отправку. Расскажу немного о том, как работает интерфейс.

Подключение производится по одному пину, земля (GND) должна быть общая. В сети могут находиться любые Ардуино – совместимые платы и МК, подключение – к любому GPIO (цифровому пину):

Сам интерфейс имеет такую же суть, как UART:

Чтение производится в ожидаемых серединах фреймов, начало отсчёта – стартовый LOW:

В начальный момент времени все устройства держат пин в подтянутом к питанию состоянии (INPUT_PULLUP). Передавать данные может только одно устройство. Все устройства в сети принимают данные одновременно, но в пакет входит адрес приёмника, поэтому приёмник поймёт, что данные предназначены ему. Остальные устройства проигнорируют принятый пакет.

Применение


Зачем нужен данный интерфейс? Можно собрать устройство с центральной базой (например на Arduino Nano) и несколькими удалёнными, например на копеечных ATtiny13. База будет общаться с удалёнными устройствами и отправлять им команды на исполнение (включить/выключить нагрузку, увеличить/уменьшить тепловую мощность, яркость и т.д.), а также может запрашивать данные с их датчиков. Связь по медленному цифровому протоколу получится очень надёжной даже на большом расстоянии (сотни метров), что избавляет от применения дополнительных конвертеров интерфейсов. Та же тинька 13 может отправлять на базу данные с датчиков температуры/влажности/освещённости и так далее.

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

А нужен ли GBUS?


Для передачи между двумя платами через Serial (обычный и software) можно использовать полностью стандартный способ: методы write() и readBytes(). Функции принимают байтовые массивы, но на ничего не мешает их обмануть и передавать что угодно, даже структуры:

Отправка

// Пример отправки и приёма структуры через Serial
// ОТПРАВИТЕЛЬ
// Ардуины соединены так:
// отправитель D11 -> приёмник D10
#include < SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX
struct Str {
  byte val_b;
  int val_i;
  long val_l;
  float val_f;
};
void setup() {
  Serial.begin(9600);
  mySerial.begin(4000);
}
void loop() {
  // буфер на отправку
  Str buf;
  // заполняем
  buf.val_b = 123;
  buf.val_i = 12345;
  buf.val_l = 123456;
  buf.val_f = 123.456;
  // отправляем родным write()
  // указываем ему буфер-структуру, но приводим тип к byte*
  // размер можно указать через sizeof()
  mySerial.write((byte*)&buf, sizeof(buf));
  delay(2000);
}

Чтение

// Пример отправки и приёма структуры через Serial
// ПРИЁМНИК
// Ардуины соединены так:
// приёмник D10 -> отправитель D11
#include < SoftwareSerial.h>
SoftwareSerial mySerial(10, 11); // RX, TX
// структура для приёма
// должна соответствовать отпраляемой
struct Str {
  byte val_b;
  int val_i;
  long val_l;
  float val_f;
};
// создаём саму структуру
Str buf;
void setup() {
  Serial.begin(9600);
  mySerial.begin(4000);
}
void loop() {
  // читаем родным методом readBytes()
  // указываем ему буфер-структуру, но приводим тип к byte*
  // размер можно указать через sizeof()
  if (mySerial.readBytes((byte*)&buf, sizeof(buf))) {
    Serial.println(buf.val_b);
    Serial.println(buf.val_i);
    Serial.println(buf.val_l);
    Serial.println(buf.val_f);
  }
}

О других способах читайте в уроке общение по Serial.

GBUS по сути делает то же самое, но предлагает дополнительные возможности:

  • GBUS использует свои способы отправки и чтения байтов (на базе read, write и available), которые не блокируют выполнение остального кода на время чтения и отправки, в отличие от стандартных. На медленных скоростях это может быть критично.
  • В GBUS встроена проверка целостности данных, в десятки раз увеличивающая надёжность передачи.
  • GBUS превращает интерфейс в шину, то есть общаться могут не два устройства, а сколько угодно, так как имеют адресацию.
  • GBUS даёт расширенную отладку ошибок по приёму данных.
  • В GBUS есть встроенные функции, позволяющие “достучаться” до приёмника, то есть отправить ему запрос и дождаться корректного ответа.

БИБЛИОТЕКА


  • Очень простой, надёжный, устойчивый к помехам и задержкам, но медленный интерфейс связи на базе UART
  • Двухсторонняя связь по одному проводу
  • Асинхронная отправка и чтение (на базе millis())
  • Двухсторонняя совместимость с аппаратным UART
  • Возможность принимать и отправлять данные внутри сети Ардуинок
  • Адресация до 254 устройств в сети (от 1 до 255)
  • Всеядная функция отправки и приёма (ест переменные, структуры, массивы)
  • Встроенная проверка CRC (контроль целостности) пакета данных
  • Возможность отправки и чтения короткого “запроса”
  • Сама библиотека предоставляет возможности по отладке (коды ошибок)
    • В примерах есть компактные варианты чтения и отправки данных, влезет даже в ATtiny

Поддерживаемые платформы: все Arduino (используются стандартные Wiring-функции)

УСТАНОВКА


  • Библиотеку можно найти и установить через менеджер библиотек по названию GyverBus в:
    • Arduino IDE (Инструменты/Управлять библиотеками)
    • Arduino IDE v2 (вкладка “Library Manager”)
    • PlatformIO (PIO Home, вкладка “Libraries”)
  • Про ручную установку читай здесь

ДОКУМЕНТАЦИЯ


Особенности новой версии


В новой версии библиотеки протокол GBUS был полностью отделён от интерфейса: сейчас GBUS - это протокол связи, то есть порядок байтов в посылке (адрес, CRC и прочее). Общаться по GBUS можно при помощи любой Serial библиотеки, а точнее - любого объекта класса Stream (родная библиотека аппаратного Serial, включая Serial1, Serial2 и прочие на Arduino Mega, SoftwareSerial на любой Ардуино-совместимой платформе, а также встроенный в GyverBus однопроводной softUART). Это позволяет удобно, надёжно и не блокируя выполнение остального кода передавать наборы данных любых типов между Ардуино-совместимыми платами (Arduino, esp8266, esp32) при помощи программного или аппаратного UART'a.

Отправка широковещательных сообщений (new!)


В версии 2.3 добавлена возможность отправки сообщений "всем" по адресу 255: любой приёмник получает сообщение, отправленное на его личный адрес и на адрес 255.

Особенности библиотеки


Библиотека содержит в себе несколько наборов инструментов:

  • GyverBus.h - базовый набор инструментов для работы с протоколом GBUS: функции для упаковки, распаковки и проверки байтовых массивов для передачи и приёма данных. Функции можно использовать отдельно от всего остального для упаковки-распаковки и проверки данных для передачи по любому интерфейсу связи, просто передавая любые данные как байтовый массив.
  • GBUS.h - класс GBUS, который позволяет удобно общаться по протоколу GBUS через любой "Serial". В отличие от родных инструментов класса Stream не блокирует выполнение кода на приём и отправку, что очень критично на невысоких скоростях.
  • softUART.h - однопроводной UART, работающий на приём и отправку, не блокирующий выполнение кода. Может использоваться для библиотеки GBUS, так как наследует класс Stream.
  • GBUSmini.h - набор отдельных функций для общения по GBUS. Все функции блокируют выполнение кода на время приёма/отправки (сделаны на базе delay()), но реализованы максимально легко и позволяют работать с шиной даже на ATtiny13. Для работы нужно подключить библиотеку и просто использовать нужные функции из доступных.

Подключение для softUART


Платы/микроконтроллеры объединяются в шину одним дата-проводом и должны иметь общую землю GND. Пин шины - любой обычный GPIO (цифровой пин входа-выхода). Пин должен быть подтянут к питанию внутренним (режим INPUT_PULLUP) или внешним резистором (5-100 кОм). При использовании GyverBus.h внутренняя подтяжка включается автоматически, а для GBUSmini.h её нужно прописать самому (см. примеры). Схема подключения - на самой первой картинке этой страницы сайта.

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

Подключение для Serial


Для одностороннего общения по классическому Serial достаточно соединить TX передатчика и RX приёмника. Для двухсторонней связи нужно будет соединить ещё и RX передатчика и TX приёмника. Например для использования функций "достукивания" до приёмника, чтобы удостовериться в том, что он получил данные.

Общение по шине


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

Совместимость softUART


softUART имеет такие же контрольные биты и тайминги, как у UART (в ардуиновской среде - Serial), и полностью с ним совместим, то есть можно отправлять данные по Serial (с ноги TX) и принимать на softUART, либо отправлять c softUART и принимать по Serial (на ногу RX). Скорость Serial нужно выставлять выше 250 бод, softUART соответственно тоже.

Скорость


  • softUART - скорость задаётся при создании объекта.
  • GBUS - скорость задаётся у самого интерфейса, например у "Serial'ов" это begin.
  • GBUSmini - скорость задаётся в GBUSmini.h, параметром GBUS_DEFAULT_SPEED, либо дефайном GBUS_SPEED в скетче перед подключением библиотеки.

Скорость задаётся в бодах (baud rate) и совпадает с бодрейтом UART'а. Время передачи одного байта равно 10 / baud секунд, соответственно скорость передачи составляет baud / 10 байт в секунду (например при скорости 300 бод - 30 байт в секунду).

Примечание: чем ниже скорость, там надёжнее передача и тем меньше влияют помехи, индуктивность линии и несовпадение частот тактирования у передатчика и приёмника. Скорость должна быть одинаковой для всей шины, т.е. всех подключенных к ней устройств!

Максимальная скорость


Максимальная скорость ограничена прежде всего соединением: длиной проводов, наличием экрана, наличием источников помех и прочим. В идеальных условиях скорость будет такая:

При использовании в качестве интерфейса аппаратного Serial скорости могут быть очень большие, вплоть до 1000000 бод.

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

При использовании в качестве интерфейса softUART очень важен частый вызов tick(): рекомендуется делать это не реже, чем каждые 1 000 000 / baud / 4 микросекунд. При наличии в остальном коде задержек или блокирующих выполнение участков на время, превышающее четверть времени бита, передача на высоких скоростях может происходить с ошибками. Можно вызывать tick() по прерыванию таймера. Ещё можно сделать так:

void loop() {
  if (rx.tick() != RECEIVING) {
    // Потенциально "тяжёлый" код.
    // Не выполняем, пока идёт приём!
  }
  // "лёгкий" код
}

При использовании библиотеки GBUSmini.h максимальная скорость ограничена частотой опроса читающих функций (вызывать не реже, чем каждые 1 000 000 / baud / 4 микросекунд) и точностью настройки коррекции: в GBUSmini.h в секции настроек есть параметры GBUS_DEFAULT_WRITE и GBUS_DEFAULT_READ. Они отвечают за коррекцию задержки в микросекундах на отправку и чтение одного бита. Это значение зависит от частоты тактирования МК (пропорционально!), модели самого МК, версии "ядра" Arduino и подбирается вручную. Также значение коррекции может меняться в зависимости от скорости шины! Привожу некоторые известные:

МКЯдроGBUS_DEFAULT_WRITEGBUS_DEFAULT_READ
ATmega328p (Arduino Nano)Стандартное версии 1.8.38 (при 16 MHz)5 (при 16 MHz)
    

Максимальная скорость с учётом "пустого" скетча и откалиброванными значениями коррекции задержки, платы соединены на бредборде проводами длиной 10см:

Отправитель - приёмникМакс. скорость
softUART - softUART25'000
GBUSmini - GBUSmini10'000
softUART - GBUSmini15'000
GBUSmini - GBUSmini10'000

Улучшение связи


Предложено Саней Чёрным. Есть предложение по улучшению помехоустойчивости работы на дальние дистанции. К примеру имеем 30 устройств на линии. У каждого подтяжка к + внешняя по 100К. В сумме считаем это как параллельное соединение резисторов и при передаче LOW(0-ка) нужно пересилить 3.45кОм это 1.45мА на вывод. Все вроде как хорошо, но если в этом же кабеле( к примеру ПВС4*1 - четырехжильный кабель с сечением 1мм^2) рядом идет 220VAC, то на линию GND Arduin-ы будет идти нехилая наводка, которая на длине в 100м(а это всего 50м от передатчика, т.к. 50м провод данных и 50м провод земли) приведет к целому ряду ложных приемов пакетов. Поэтому предлагаю схему, которая позволит каждому устройству на линии при получении LOW дублировать его. Это влечет за собой уменьшение скорости передачи данных. Т.к. каждая оптопара имеет задержку в 4мкс. А на 30 последовательно соединенных устройствах это 0.12мс.

blank

Контроль целостности


В библиотеку встроен контроль целостности данных (включен по умолчанию): последним байтом в пакете передаётся crc (очевидно 8-ми битный). Приёмник считает CRCвсего пакета, и если он совпадает - приём считается успешным. CRC повышает надёжность передачи данных: даже если один бит будет передан неправильно - приёмник отбракует посылку.

Примечание: у прошивок всех устройств в сети CRC должен быть или включен, или выключен. Устройство, настроенное на приём данных с CRC не сможет принять пакет без CRC.

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

Протокол связи


Протоколом здесь назван порядок и значение байтов в пакете:

Тип посылкиБайт 1Байт 2Байт 3Байт 4Байт 4+nБайт 4+n+1
ДанныеКоличество байтАдрес получателяАдрес отправителяБайт даты 1Байт даты nCRC
Тип посылкиБайт 1Байт 2Байт 3Байт 4
Запрос (request)0Адрес получателяАдрес отправителяCRC
Тип посылкиБайт 1Байт 2Байт 3Байт 4
Ответ (ack)1Адрес получателяАдрес отправителяCRC

Таким образом например пакет [5, 3, 8, 123, 456, 12] содержит 6 байт, предназначен для устройства с адресом 3 и отправлен с адреса 8. Байты данных имеют значение 123 и 456, а CRC - 12.

Оба способа взаимодействия с GBUS работают по этому протоколу и совместимы между собой. Если стоит задача сформировать пакет вручную, отправить и принять его через функции из библиотеки - пакет должен быть сформирован согласно протоколу.

Адресация


В GBUS используется 8-ми битная адресация, но адрес 0 зарезервирован как код ошибки чтения в GBUSmini.h. Таким образом адресовать устройства можно с 1 по 255 адрес.

Если не работает


Самая нестабильная передача - между softUART и GBUSmini, а также между устройствами на базе разных микроконтроллеров и/или с разной частотой тактирования. Первым делом стоит попробовать чуть увеличить или чуть уменьшить значения коррекции задержки.

Совместимость


  • Вариант с GBUS поверх родного Serial или софтсериал совместим абсолютно между всеми платформами.
  • softUART лично мне не удалось завести на esp8266, возможно виной тому кривая распиновка и неудачно выбранные пины
  • А так по сути Serial он везде Serial, то есть общаться можно между разными МК и платформами

Инициализация


GBUS объект(обработчик, адрес, размер буфера), где:

  • обработчик - адрес объекта-обработчика интерфейса (например &Serial, &mySerial)
  • адрес - адрес этого устройства в сети (1-255)
  • размер буфера - размер внутреннего буфера в количестве байт (больше или равен отправляемому объёму данных)

Метод tick()


Метод tick() занимается отправкой и приёмом данных по шине, то есть измеряет время и слушает/дёргает пин, что обеспечивает "асинхронную" работу интерфейса. Вызывать тик нужно как можно чаще, желательно как минимум в 4 раза чаще, чем 1'000'000 / скорость микросекунд. То есть например для скорости 300 это будет 1000000/300/4 ~ 830 микросекунд. Можно оставить его в loop() и обеспечить его "прозрачное" выполнение без задержек, либо дополнительно положить в прерывание таймера с таким периодом. Логика работы tick() автоматически переключается в зависимости от текущего режима: во всё время кроме отправки мы "слушаем" шину.

Также tick() возвращает текущее состояние шины.

Коды статусов и ошибок


Помимо тика, текущий статус можно узнать из функции getStatus(), она возвращает то же самое, что тик:

КодНазваниеОписание
0GBUS_IDLEОжидание
1TRANSMITTINGПередача
2TX_OVERFLOWБуфер переполнен
3TX_COMPLETEПередача завершена
4RECEIVINGПриём
5RX_ERRORОшибка приёма
6RX_ABORTОшибка. Приём прерван
7RX_OVERFLOWОшибка. Буфер или пакет переполнен
8RX_ADDRESS_ERRORОшибка. Не наш адрес
9RX_CRC_ERRORОшибка. Не совпадает CRC
10RX_REQUESTУспешное получение запроса
11RX_COMPLETEУспешный приём данных
12RX_ACKУспешное получение подтверждения

Статус можно опрашивать как по коду, так и по имени (константа подсветится синим).

В версии 2.5 появилась функция statusChanged(), возвращающая true при изменении статуса. Использовать можно так:

bus.tick();   // тикаем
if (bus.statusChanged()) {  // если статус изменился
  Serial.println(bus.getStatus());  // выводим код
}

Отправка данных


Для отправки данных нужно взывать sendData(адрес, дата), где адрес - адрес принимающего устройства, а дата - любой тип данных (переменная любого типа, массив переменных любого типа, включая char array, структура). Данные будут автоматически разбиты на байты и отправлены внутри tick().

Пример отправки структуры:

struct myStruct {
  byte val_b;
  int val_i;
  float val_f;
};
myStruct tx_data;

void setup() {
  // отправляем на адрес 2
  tx.sendData(2, tx_data);
}

void loop() {
  tx.tick();
}

Приём данных


На принимающей стороне опрашиваем функцию gotData(), она однократно вернёт true при получении корректного пакета данных (внутри tick() автоматически проводится проверка ошибок передачи, переполнения, совпадения адреса и контроль целостности данных, если он включен).

Данные читаются при помощи readData(дата), где дата - переменная с таким же типом данных, какой был отправлен. Функция  сама запишет данные в указанную переменную, то есть соберёт из встроенного буфера. Пример:

struct myStruct {
  byte val_b;
  int val_i;
  float val_f;
};
myStruct rx_data;

void setup() {
}

void loop() {
  rx.tick();
  if (rx.gotData()) {
    rx.readData(rx_data);
  }
}

Адрес отправителя запоминается при получении, его можно прочитать из getTXaddress().

Отправка и приём запроса


В библиотеке также реализована отправка короткого "запроса" при помощи sendRequest(адрес). При получении такого запроса на текущее устройство метод gotRequest() вернёт true.

Структура запроса для работы с шиной вручную: [0, адрес получателя, адрес отправителя]

Отправка и приём подтверждения


В библиотеке также есть отправка и приём "подтверждения" - ack. Отправить можно при помощи sendAck(адрес) и принять в gotAck(), логика такая же как у запроса.

Структура подтверждения для работы с шиной вручную: [1, адрес получателя, адрес отправителя]

Отправка запроса с подтверждением


В библиотеке реализован блокирующий метод sendRequestAck(адрес, кол-во попыток, таймаут), который отправляет запрос по указанному адресу и ждёт подтверждения приёма (ack) или отправленные в ответ данные. Если в течение указанного таймаута данные или ack не получены - запрос будет отправлен ещё раз, и так до тех пор, пока количество попыток не достигнет указанного. Это позволяет "достучаться" до приёмника и понять, получил ли он запрос, или дождаться от него данные в ответ на запрос. Смотри пример call_response_ack.

Сам метод sendRequestAck() возвращает статус:

КодНазваниеОписание
2ACK_ERRORОтвет не получен
3ACK_ONLYПолучено подтверждение
4ACK_DATAПолучены данные

Ожидание ответа (v1.1)


Метод waitAck(адрес, кол-во попыток, таймаут) работает по такой же логике, как предыдущий, но не блокирует выполнение кода. Логика такая: вручную отправляем реквест sendRequest(адрес) и при помощи waitAck() можем дождаться ответа и попытаться достучаться до приёмника, автоматически отправляя новые запросы через таймаут миллисекунд. Количество попыток ограничено заданным. Метод возвращает статусы:

КодНазваниеОписание
0ACK_IDLEНичего не делаем
1ACK_WAITЖдём ответа
2ACK_ERRORОтвет не получен
3ACK_ONLYПолучено подтверждение
4ACK_DATAПолучены данные

Примеры находятся в examples/GBUS/wait_ack. В примере wait_ack_rx можно закомментировать отправку ответа и в мониторе порта увидеть, как передатчик отправляет несколько запросов перед тем, как выдать ошибку.

Широковещательная отправка (v2.4)


Если отправлять данные или запросы на адрес 255, то их получат все устройства на линии. Вместо цифры можно использовать константу GBUS_BROADCAST.

Отправка и приём сырых данных


Сырые данные (просто байтовый массив) можно отправить по шине при помощи sendRaw(байтовый массив, размер), где размер можно передать как sizeof(массив). Метод gotRaw() вернёт true, если приёмник корректно принял какие-то данные. Размер принятого пакета можно узнать в rawSize(), а прочитать его - обратившись напрямую к члену класса buffer, как к массиву.

Инициализация


Объект создаётся через шаблон, а также принимает скорость и размер буфера (опционально): softUART<пин, режим> объект(скорость) или softUART<пин, режим> объект(скорость, буфер)
  • пин - любой GPIO пин. Автоматически будет настроен в INPUT_PULLUP. Если встроенной подтяжки нет - тянуть вручную резистором.
  • режим - режим работы объекта:
    • GBUS_FULL - двухсторонняя связь (активен по умолчанию, можно не указывать)
    • GBUS_TX - только отправка (экономит память)
    • GBUS_RX - только приём (экономит память)
  • скорость - скорость интерфейса в бодах
  • буфер - размер буфера на отправку, по умолчанию 64 байта

Использование с GBUS


Для общения по GBUS через однопроводной UART достаточно передать его в GBUS при создании:
// подключаем софт юарт
#include "softUART.h"
softUART<4> myUART(1000); // пин 4, скорость 1000

// подключаем GBUS
#include "GBUS.h"
GBUS bus(&myUART, 5, 20); // обработчик UART, адрес 5, буфер 20 байт

Буфер


softUART имеет свой программный буфер на отправку, его размер можно настроить в файле softUART.h, параметр SOFTUART_BUF_SIZE

Особенности


GBUSmini.h содержит набор функций для работы с шиной, все функции (за исключением GBUS_is_busy(пин)) - блокирующие, то есть блокируют выполнение кода на время отправки или чтения пакета. Функции чтения должны работать в таких же условиях, как и tick(): вызываться как можно чаще, чтобы не пропустить начало передачи. Все функции работают только с массивом байтов (в описании ниже - дата), также в функции передаётся его размер (например через sizeof(data)). Для работы с другими типами данных используй упаковщик и распаковщик данных из "Утилиты".

Выключение CRC


CRC включается и выключается в файле GBUSmini.h, параметр GBUS_CRC в секции настроек

Отправка и чтение данных


"Сырые" данные (без протокола) можно отправить и принять при помощи
  • GBUS_send_raw(пин, дата, размер)
  • GBUS_read_raw(пин, дата, размер) - возвращает количество принятых байт при успешном завершении приёма
Для работы по протоколу используются
  • GBUS_send(пин, адрес получателя, адрес отправителя, дата, размер)
  • GBUS_read(пин, наш адрес, дата, размер) - возвращает адрес отправителя при успешном завершении приёма. При ошибке возвращает 0

Отправка и чтение запроса


  • GBUS_send_request(пин, адрес получателя, адрес отправителя)
  • GBUS_read_request(пин, наш адрес) - возвращает адрес отправителя при успешном завершении приёма. При ошибке возвращает 0

Отправка и чтение подтверждения


  • GBUS_send_ack(пин, адрес получателя, адрес отправителя)
  • GBUS_read_ack(пин, наш адрес) - возвращает адрес отправителя при успешном завершении приёма. При ошибке возвращает 0

Запрос с ожиданием


GBUS_send_request_ack(пин, адрес получателя, адрес отправителя, кол-во попыток, таймаут между попытками) - отправить запрос и ждать подтверждения приёма, т.е. пытаться "достучаться" до приёмника. Возвращает 0 при таймауте, 1 при успехе (получили ack). Смотри примеры call response_ack
// =========== GBUS =========== 
// проверить статус принятых данных (буфер, его размер, кол-во принятых байтов, наш адрес)
// вернёт статус GBUSstatus
GBUSstatus checkGBUS(uint8_t* buffer, byte bufSize, byte amount, byte addr);


// ====== УПАКОВЩИК GBUS ====== 
// запаковать данные для отправки (буфер, его размер, дата, адрес получателя, адрес отправителя)
// вернёт количество упакованных байт
// запакует согласно протоколу [суммарное количество байт, адрес получателя, адрес отправителя, ...байты даты..., CRC]
template  byte packGBUSdata(uint8_t* buffer, byte bufSize, T &data, byte to, byte from);

// распаковать данные, минуя служебные байты (буфер, его размер, дата)
// при успехе вернёт true. Вернёт false, если буфер слишком мал для даты
template  bool unpackGBUSdata(uint8_t* buffer, byte bufSize, T &data);

// запаковать команду в буфер (буфер, команда, кому, от кого)
// команды: запрос (0), ответ (1)
// запакует согласно протоколу [команда, адрес получателя, адрес отправителя, CRC]
byte packGBUScmd(uint8_t* buffer, byte cmd, byte to, byte from);


// ====== УПАКОВЩИК БАЙТОВ ====== 
// пакуем любой тип данных в байтовый буфер (буфер, дата)
template  void packDataBytes(byte *buffer, T &data);

// распаковываем из байтового буфера обратно (буфер, дата)
template  void unpackDataBytes(byte *buffer, T &data);


// ============= CRC =============
// обновить CRC байта (crc, байт)
void GBUS_crc_update(uint8_t &crc, uint8_t data);

// расчёт crc для буфера (буфер, количество байт для проверки)
byte GBUS_crc_bytes(byte *data, byte size);

ПРИМЕРЫ


Остальные примеры смотри в папке examples библиотеки, также примеры можно открыть из Arduino IDE/Файл/Примеры

В библиотеке очень много примеров, смотри их в папке examples =) Онлайн-версия на GitHub

ПОДДЕРЖАТЬ


Вы можете поддержать меня за создание доступных проектов с открытым исходным кодом, полный список реквизитов есть вот здесь.

4.7/5 - (9 голосов)
Подписаться
Уведомить о
guest

71 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии