Как соединить две ардуино
Соедините пару плат Arduino в два процессора или сделайте удаленный датчик с радиоканалом.
Большие связи Ника Вейча все это позволяют.
Есть куча причин, по которым вашему проекту может понадобиться больше одного Arduino -возможно, у вас есть массив удаленных датчиков или вы хотите управлять роботом на базе Arduino с другого Arduino. В любом случае, без взаимодействия тут не обойтись. К счастью, организовать его довольно просто. Способов передачи и приема данных много, и выбор зависит от ваших потребностей — в частности, от расстояния.
Интерфейс SPI быстр и хорошо поддерживается в Arduino, но требует жуткого количества проводов — на него тратится по меньшей мере четыре вывода (или больше, для адресации). Тут еще есть смысл при беспроводном соединении, но если платы связаны локально, более чем достаточно двухпроводного интерфейса I 2 C. Библиотека Wire (см. наши более ранние эксперименты с ЕЕ-PR0M в LXF152/153) позволяет просто связать два Arduino, сэкономив множество дополнительных выводов. В ней надо задействовать аналоговые выводы 4 и 5, но это тоже просто.
Используется конфигурация «ведущий/ведомый», шиной управляет одно устройство, и никто не заговорит, пока к нему не обратятся первым. Теоретически на одной шине может быть множество плат Arduino или других устройств, управляет которыми одно главное. На практике, без применения специальных аппаратных повторителей длина создаваемой шины ограничена. Чтобы поговорить с устройством, выполните следующие шаги:
Wire.begin();
Wire.beginTransmission(Slave_address);
Wire.send(OxOI); Wire.endTransmission();
Первая строка инициализирует это устройство как главное. Затем ему нужно поговорить с определенным устройством, и мы загружаем его адрес. Данные обычно отправляются байтами, но в качестве параметров метода send() можно использовать указатель и длину. Наконец, всегда стоит приятно завершать разговор — endTransmission() на самом деле инициирует передачу данных, которые были помещены в буфер методом send() . Чтобы получить данные с ведомого устройства, главное инициирует разговор и говорит, сколько байтов оно хочет получить:
char bufter[8];
Wire.requestFrom(slave_address, 8);
int count=0;
while(Wire.available();
buffer[count] = Wire.receive();
cont++;
>
Функция Wire.available() возвращает количество принятых байт, находящихся в буфере в данный момент — для их получения используется метод Wire.receive() . Можно и ввернуть в эту передачу добавочный код. чтобы убедиться в получении нужного количества данных или получить фрагменты, потерявшиеся по дороге.
В порядке более полезного примера напишем короткую программу «пинга». Она установит одно устройство как главное, другое — как подчиненное. Можно использовать один и тот же код для обоих устройств и задать главное устройство на аппаратном уровне, просто подключив один из цифровых выходов к +5В и проверяя его внутри программы.
Вы не всегда предпочтете писать код таким образом, но для проверки гораздо проще иметь один блок кода — оказывается, ведомому устройству многое все равно не нужно. «Железа» тоже много не потребуется. На рис. 1а показано, как это делается физически (если у вас хорошие проводники, на макете можно обойтись без повышающих резисторов и подключить платы друг к другу напрямую).
Рис. 1а. Соединить Arduino через шину PC просто — иногда
даже не нужны повышающие резисторы.
Рис. 1b проясняет эту схему, и шину можно расширить для подключения других устройств.

Разобьем наш тестовый код на фрагменты (полностью он приведен на DVD в файле LXF152-arduino-code.zip):
#include
const int configpin =7
bool config;
unsigned char buffer[8];
int Slave=8;
В начале программы импортируется нужная библиотека и объявляются переменные — несколько для описания нашей схемы аппаратной конфигурации, затем буфер данных и адрес подчиненного устройства, с которым мы будем общаться.
Код настройки по состоянию вывода определит, является ли устройство ведущим или ведомым, и установит все как надо:
void setup(void)
pinMode(configpin, INPUT); config = digitalRead(configpin);
Serial.begin(9600);
Serial, print(lnitialised as:»);
if (config) < Serial.println("transmitter");
Wire.begin();
>
else < Serial.println("receiver");
Wire.begin(Slave);
Wire.onReceive(slaveRX);
Wire.onRequest(slaveTX):
>
>
Код в общем понятен. Самое интересное происходит во фрагменте для ведомого устройства. Существует два метода установки callback -функции (обратного вызова) — в одном она вызывается, когда библиотека Wire фиксирует отправку данных ведущим устройством, а в другом — когда принимается запрос на возврат данных. Этим мы пока не занимались.
В главном цикле должен быть код только для главного ведущего, но в качестве обратной связи мы можем отправлять какие-то сообщения о состоянии по последовательному каналу при возникновении определенных событий. Основной код здесь передает байт данных, а затем просит передать его обратно. С помощью встроенного таймера (путем вызова функции millis() можно узнать, сколько времени занимают отправка и возврат:
void loop(void)
if (config) uint32_t time = millis();
uint32 t start:
bool timeout = false;
Serial.print(«Now sending»);
Wire.beginTransmission(Slave);
Wire.send(0xFF);
WireTransmissioin();
О методах для передачи данных мы говорили выше. Теперь, когда данные отправлены, можно попросить прислать их обратно:
Serial.printlnf»
Waiting for response»);
delay(20);
Wire.requestFrom(Slave,1);
delay(20);
Serial.println(«Datarequested»);
buffer[0]=Wire.receive():
Serial, print(response received»);
Serial.println(buffer[0],HEX);
Serial.print(«round-trip:»);
Serial.println(millis()-time):
delay(2000);
>
else
Serial.print(«waiting»);
delay(1000);
>
>
Наконец, нужно написать функции для работы на ведомом устройстве. Каждая из них вызывается с аргументом — целым числом, которое задает число принятых или запрошенных байт:
void slaveRX(int bytes) buffer[0]=Wire.receive();
Serial.print(«value received:»); ;
Serial.println(buffer[0]. HEX);
>;
void slaveTX() Serial.println(«sending reply»); ;
Wire.send(buffer[0]);
Здесь есть несколько задержек, чтобы линии могли приходить в исходное состояние между передачами данных — на хороших шинах этого не потребуется: может, впрочем, оказаться, что нужно несколько повышающих резисторов, если напряжение на линиях будет с трудом достигать 5 В. Подключите к каждой линии на +5 В резистор сопротивлением 2 кОм. На нашем тестовом оборудовании на отправку и прием байта ушло около 90 мкс без задержек. Не так уж плохо.
Что нам надо? Две платы Arduino (любых). » Для схем с радиосвязью вам понадобятся две платы nRF2401+ (см. http://www.sparkfun.com/products/152. http://proto-pic.co.uk/transceiver-nrf2401a-with-chip-antenna/. или поищите на eBay ). » Библиотека RF c LXFDVD.
Если вам нужен беспроводной канал связи, вариантов несколько. Из дорогих — стандарт «ZigBee», поддерживаемый схемами и модулями ХВее, предоставит вам все, о чем вы мечтаете. В большинстве схем ХВее применяется простой последовательный интерфейс, и они часто используются в библиотеках и примерах Arduino. Большой недостаток — стоимость: около 20 фунтов за устройство — это не то, что вы охотно приплюсуете к каждому проекту. Из более доступных — платы радиосвязи, работающие по принципу регенерации (для щеголяющих старомодностью, поясню: «автодины»). Они достаточно дешевы, и их легко собрать или переделать самим. Но им не хватает оснастки — придется писать собственные протоколы для отправки и приема данных; вдобавок эти устройства способны интерферировать друг с другом, и пользоваться несколькими устройствами по соседству может быть затруднительно. «Золотая середина» -серия трансиверов RF24XX. Производимые Nordic Semiconductors, эти чудесные маленькие схемы работают на частоте 2.4 ГГц. Немного не дойдя до полноценного сетевого протокола, они предлагают столь полезные возможности, как выбор канала, передача пакетов подтверждения и различные скорости передачи (чтобы выжать из сигнала наибольшее расстояние), и обойдутся вдесятеро дешевле ХВее.
Эти микросхемы можно купить уже смонтированными на оконечной плате [breakout board] вместе с антенной. Такие платы выпускает Sparkfun. Их довольно сложно пристроить к Arduino, но скоммутировать все на макетной плате не составит труда.
Схемы nRF24XX являются полудуплексными. Они могут отправлять или принимать данные, но не одновременно. Чтобы реализовать это программно, придется немного повозиться, но эта проблема свойственна и другим (не по радиоканалу) способам соединения. В любом случае, микросхемы ATmega для многозадачности не совсем пригодны.
Существует пара реализаций библиотеки и для этого устройства. Более сложная из двух, но и с большей функциональностью — библиотека RF24 от Джеймса Колиза-младшего [James Coliz, Jr]. Она поддерживает многие аппаратные функции микросхемы без лишних осложнений. Посмотрим, как с помощью этой библиотеки настроить соединение по радиоканалу и воспользоваться им:
#include
#include»nRF24L01.h»
#include «RF24.h»
Эта библиотека использует библиотеку SPI для Arduino, поэтому ее тоже нужно подключить. Здесь также подключается класс RF24 — с его помощью легко создать радиоканал, и после подключения всего этого мы создаем экземпляр радиоканала:
Здесь инициализируется объект radio с использованием выводов 8 и 9 Arduino в качестве выводов СЕ (Chip Enable — микросхема активна) и CSN (Chip Select Not — микросхема не выбрана) соответственно. Название «Микросхема не выбрана» может показаться забавным, но по сути это инвертированный вывод «Микросхема выбрана» (Chip Select) — он активен в состоянии «нуля», и микросхема выбирается, когда этот вывод соединяется с «землей». Это обычное дело для интерфейсов SPI. поэтому даже если вывод отмечен как CS или SS, проверьте, активируется ли он «нулевым» уровнем. Другие выводы для обмена данным по SPI -те, что обычно используются библиотекой SPI в Arduino: 11,12 и 13.
В микросхемах nRF24 есть набор внутренних каналов для приема и передачи данных. С помощью одного канала можно принимать или отправлять данные, с помощью еще пяти -только слушать. У каждого канала есть адрес — нечто вроде МАС-адреса в сети. Это 64-битное число, и оно должно быть максимально случайным, чтобы избежать конфликтов. Поэтому ваша микросхема может передавать данные на один заданный адрес и принимать данные с пяти адресов. Единственное ограничение — в том, что первые четыре байта адреса для всех принимающих каналов должны быть одинаковыми. Чтобы задать 64-битное число в нашем коде, мы используем 64-битное беззнаковое целое число (добавляем LL (long long) в конец шестнадцатеричного значения числа):
const uint64_t txpipe = 0x818181818101LL;;
const uint64_t rxpipe1 = 0xFFFFFFFF01LL;;
const uint64_t rxpipe2 = 0xFFFFFFFF02LL;

He все микросхемы для создания канала удобно подключать к макету .
Урок 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.

Для других моделей плат соответствие выводов такое:
Плата
Пин SDA
Пин SCL
Arduino Uno, Nano, Pro и Pro Mini
Arduino.ru
Доброго дня всем. Имеются 2 модуля enc28j60 и две ардуины ) Нужно их подружить по сети, для решения простейшей задачи, на клиенте замкнулись контакты, на сервере сработало реле. В инете везде в основном описание, как поднять вебсервер. А мне вебка не нужна, чистое общение ардуинок по сети ! Ещё нигде в примерах не видел, чтобы на enc28j60 использовали шлюз по умолчанию, этот модуль и библиотека к нему умеют работать в сети полноценно ? Может кто-то поможет ссылками, направит в нужное русло так сказать ?
- Войдите на сайт для отправки комментариев
Пнд, 01/06/2015 — 12:36
Gippopotam

Зарегистрирован: 12.09.2014
Вы хотите соединить платы по сети, но не использовать сетевые протоколы?
Чем вам клиент-сервер не угодил?
- Войдите на сайт для отправки комментариев
Пнд, 01/06/2015 — 12:37
Зарегистрирован: 01.06.2015
Ну как же без сетевых протоколов, без них никуда )
Клиент-Сервер, отличное решение, просто я не могу найти примеров именно такого использования
- Войдите на сайт для отправки комментариев
Пнд, 01/06/2015 — 13:10
Gippopotam

Зарегистрирован: 12.09.2014
Dump пишет:
В инете везде в основном описание, как поднять вебсервер.
Одна Ардуино сервер, другая — клиент.
У Осипова смотрели?
- Войдите на сайт для отправки комментариев
Пнд, 01/06/2015 — 13:38
Зарегистрирован: 01.06.2015
Да смотрел. Собственно с клиентом проблема, как и что там. Сейчас вроде нашел информацию, читаю изучаю. Плохо что взял 2860 , надо было wiznet5100 брать, у него tcp стек реализован железно, на 2860 я так понял стек реализован программно в библиотеке
- Войдите на сайт для отправки комментариев
Пнд, 01/06/2015 — 15:34
Gippopotam

Зарегистрирован: 12.09.2014
Dump пишет:
. Плохо что взял 2860 , надо было wiznet5100 брать, у него tcp стек реализован железно, на 2860 я так понял стек реализован программно в библиотеке
Не переживайте — все через это прошли.
- Войдите на сайт для отправки комментариев
Пнд, 01/06/2015 — 15:38
Зарегистрирован: 01.06.2015
Пока попробую на этом модуле, если не будет стабильно работать, что ж придётся переделывать )
- Войдите на сайт для отправки комментариев
Втр, 07/07/2015 — 15:43
Зарегистрирован: 01.06.2015
Доброго времени суток всем. Снова вернулся к этой теме, ибо припекло )))) Вообще подразумевается сделать такую конструкцию: Одна Ардуина(1) подключенная к ethernet — контроллирует цепь на замыкание/размыкание , вторая Ардуина(2) подключенная к ethernet на другом конце города, управляет релюшкой в соответствии с условиями полученными от первой. На данный момент я пытаюсь разобраться с сетевой составляющей программы. Так как не требуется никакого подтверждения прохождения пакетов, решил использовать для этих целей протокол UDP. В голове картинка такая: В зависимости от того, замкнута или разомкнута цепь на первом контроллере, он отправляет в сеть UDP пакет «true» или «false». Второй контроллер принимает пакет, обрабатывает его и в случае изменения состояния, замыкает или размыкает реле. Возможно идея изначально неправильная, тогда подскажите как сделать лучше, но на данный момент я имею вот что:
#include static byte mymac[] = < 0x5A,0x5B,0x1A,0x2A,0x5A,0x5A >; // ip статический / постоянный Address нашей Web страницы. static byte myip[] = < 192,168,10,226 >; static byte hisip[] = < 192,168,10,227 >; // Буфер, чем больше данных на Web странице, тем больше понадобится значения буфера. byte Ethernet::buffer[900]; BufferFiller bfill; static uint32_t timer; const int dstPort PROGMEM = 1234; const int srcPort PROGMEM = 4321; void setup() < Serial.begin(9600); if (ether.begin(sizeof Ethernet::buffer, mymac,10) == 0); if (!ether.dhcpSetup()); // Выводим в Serial монитор IP адрес который нам автоматический присвоил наш Router. // Динамический IP адрес, это не удобно, периодический наш IP адрес будет меняться. // Нам придётся каждый раз узнавать кой адрес у нашей страницы. ether.printIp("My Router IP: ", ether.myip); // Выводим в Serial монитор IP адрес который нам присвоил Router. // Здесь мы подменяем наш динамический IP на статический / постоянный IP Address нашей Web страницы. // Теперь не важно какой IP адрес присвоит нам Router, автоматический будем менять его, например на "192.168.1.222". ether.staticSetup(myip); ether.printIp("My SET IP: ", ether.myip); // Выводим в Serial монитор статический IP адрес. ether.printIp("SRV: ", ether.hisip); >char textToSend[] = "PRIVET KOT"; void loop() < delay(1); // Дёргаем микроконтроллер. word len = ether.packetReceive(); // check for ethernet packet / проверить ethernet пакеты. word pos = ether.packetLoop(len); // check for tcp packet / проверить TCP пакеты. if (millis() >timer) < timer = millis() + 5000; //static void sendUdp (char *data,uint8_t len,uint16_t sport, uint8_t *dip, uint16_t dport); ether.sendUdp(textToSend, sizeof(textToSend), srcPort, hisip, dstPort ); >>
#include #include #define STATIC 1 // set to 1 to disable DHCP (adjust myip/gwip values below) #if STATIC // ethernet interface ip address static byte myip[] = < 192,168,10,227 >; // gateway ip address static byte gwip[] = < 192,168,10,1 >; #endif // ethernet mac address - must be unique on your network static byte mymac[] = < 0x70,0x69,0x69,0x2D,0x30,0x31 >; byte Ethernet::buffer[500]; // tcp/ip send and receive buffer //callback that prints received packets to the serial port void udpSerialPrint(word port, byte ip[4], const char *data, word len) < IPAddress src(ip[0], ip[1], ip[2], ip[3]); Serial.println(src); Serial.println(port); Serial.println(data); Serial.println(len); >void setup() < Serial.begin(9600); Serial.println(F("\n[backSoon]")); if (ether.begin(sizeof Ethernet::buffer, mymac, 10) == 0) Serial.println(F("Failed to access Ethernet controller")); #if STATIC ether.staticSetup(myip, gwip); #else if (!ether.dhcpSetup()) Serial.println(F("DHCP failed")); #endif ether.printIp("IP: ", ether.myip); ether.printIp("GW: ", ether.gwip); ether.printIp("DNS: ", ether.dnsip); //register udpSerialPrint() to port 1234 ether.udpServerListenOnPort(&udpSerialPrint, 1234); //register udpSerialPrint() to port 42. ether.udpServerListenOnPort(&udpSerialPrint, 42); >void loop()< delay(1); // Дёргаем микроконтроллер. ether.packetLoop(ether.packetReceive()); >
В итоге я ожидал увидеть в консоли второго контроллера, UDP пакеты, но не увидел ))))
Коды созданы из общедоступных примеров, поэтому комментарии там немного не в тему )
Arduino.ru
Я приобрел две ардуино и два блютуз модуля HC-06.
Я хочу сделать так называемую Метео станцию на двух ардуино по блютузу.
Суть в том чтобы с одной ардуино освещение и температуру с улицы передавало на другую ардуино по блютузу.
Освещение для лампы ( если темно включается, а если светло то выключается, также будет кнопка для вкл. и выкл. ) . Если на улице поменялась температура, то отправляется на основную ардуино, и потом при подключении телефона оно показывало температуру. Т.е. в основной ардуино температура оставалось в памяти.
Заранее спасибо!
- Войдите на сайт для отправки комментариев
