Как передать данные без проводов? На слуху у каждого современного человека два современных высокотехнологичных способа, которыми мы пользуемся каждый день: wifi и bluetooth. Оба этих способа используют для передачи радиоволны. Но есть еще один, наверное даже более распространенный вариант — передача данных с помощью инфракрасного света. И о нём мало кто задумывается при создании своих DIY проектов.
Система инфракрасной (ИК) связи повсеместно используется для управления бытовой техникой: телевизорами, кондиционерами, люстрами и пр. А было время, когда с помощью ИК сотовые телефоны и ноутбуки могли передавать друг другу данные. Кстати, до сих пор, в некоторых смартфонах есть ИК-порт для управления бытовыми приборами.
Что касается самой технологии, то она не стоит на месте. ИК-спектр нечувствителен к радиопомехам, а значит всегда будет востребован в наше насыщенное радиошумом время. Современные атмосферные ИК системы могут передавать данные со скоростями до умопомрачительных 4 Тбит/с.
На этом уроке мы создадим приёмник и передатчик инфракрасной связи, с помощью которых будем обмениваться данными между двумя платами Ардуино Уно. Попробуем реализовать передачу, используя последовательный протокол (класс Serial). А в завершении реализуем дальнобойную лазерную связь.
Этот урок посвящен основам ИК связи, а для тех, кто ищет информацию о применении ИК-приемника VS1838 и пультов с протоколом NEC рекомендуем этот урок: Ардуино: инфракрасный пульт и приемник
ИК передатчик
Для передачи сигнала нам нужен источник излучения в инфракрасном спектре, мы же делаем ИК связь, как никак. Инфракрасное излучение — это невидимый глазу свет, длина волны которого больше видимого красного света и меньше длины микроволнового излучения.
Почему именно инфракрасный? У инфракрасного спектра есть два плюса:
- во-первых, он позволяет избежать влияние (хотя и не полностью) множества источников естественного и искусственного света на качество связи;
- во-вторых, инфракрасное излучение не видно человеческому глазу, а значит такие передатчики не будут создавать паразитную засветку; плюс, канал ИК связи будет обладать некоторой скрытностью.
Самый доступный источник инфракрасного излучения — ИК светодиод. Встречаются ИК светодиоды с разной длиной волны: 740 нм, 850 нм, 880 нм, 940 нм и т.п. Для выбора подходящего светодиода, мы должны рассматривать сразу пару: излучатель и приемник. Надежный канал ИК связи подразумевает, что приёмник хорошо чувствителен к длине волны излучателя.
Подключаются ИК-светодиоды также как обычные их собратья — последовательно с токозадающим резистором. Для единичного ИК-светодиода подойдет резистор 100-200 Ом.
ИК приёмник
Теперь, когда есть излучатель, нам нужно создать приёмник этого излучения. Некий прибор, который будет улавливать инфракрасный свет и генерировать на выходе сигнал, пригодный для Ардуино: высокий уровень напряжения (HIGH) либо низкий уровень (LOW).
Такой прибор проще всего сделать с помощью фотодиода или фототранзистора. Существует несколько популярных схем детектора ИК-излучения с этими приборами, используем такую:
В схеме используется фотодиод, чувствительный к длине волны излучателя. Биполярный транзистор, например 2N2222 и три резистора. Работает схема следующим образом.
Когда на фотодиод не падает свет, он работает как обычный диод — не пропускает ток в обратном направлении. Следовательно, на базе транзистора будет низкий потенциал — транзистор окажется закрытым. Ток от источник питания +5В потечет прямиком на 3-й контакт платы Ардуино, ведь через транзистор ему путь заказан.
Если на фотодиод падает свет, то у него появляется обратный ток — от катода к аноду. Следовательно, на базе транзистора появится высокий потенциал и он откроется. Ток от источника питания потечет через транзистор на землю, и не потечет на контакт микроконтроллера.
Таким образом, схема детектора работает в инверсной логике: когда детектор видит излучение, то на контакте контроллера появляется низкий уровень (LOW), в противном случае — будет высокий уровень (HIGH).
И небольшое пояснение по резисторам. R4 — защищает схему от КЗ через транзистор, R5 — защищает базу транзистора от чрезмерного тока (хотя он особо тут не нужен), R6 — нивелирует насыщение фотодиода паразитной засветкой (солнце).
Соберем приёмник на макетной плате и подключим к Ардуино. Теперь, когда у нас есть обе части линии связи, приступим к программированию.
Программа
Проверим связь с помощью двух простых программ.
Пусть передатчик просто мигает светодиодом: 500 миллисекунд горит, другие 500 — не горит.
const byte ledPin = 2;
void setup(){
pinMode(ledPin, OUTPUT);
}
void loop(){
digitalWrite(ledPin, HIGH);
delay(500);
digitalWrite(ledPin, LOW);
delay(500);
}
Ардуино на стороне приёмника будет каждые 100 мс считывать сигнал от ИК-детектора и передавать в последовательный порт на компьютер слово «received» если сигнал есть и «-» если его нет.
const byte photoPin = 3;
void setup() {
Serial.begin(9600);
pinMode(photoPin, OUTPUT);
}
void loop() {
byte v = digitalRead(photoPin); // считываем данные с детектора
if(v == LOW){ // проверяем, что детектор выдает LOW
Serial.println("received");
} else {
Serial.println("-");
}
delay(100);
}
Загружаем программы, подаем питание и выставляем приемник и передатчик таким образом, чтобы светодиод и фотодиод смотрели друг на друга своими линзами.
Затем открываем монитор последовательного порта приёмника и проверяем, приходит ли сигнал. В случае успеха, картина будет такая:
... received - - - received received received - - ...
Если в мониторе отображаются одни минусы, сигнал детектор ничего не видит. Возможные причины:
- неверно собрана схема детектора: проверяем все соединения, особенно фотодиод и транзистор;
- нарушена соосность светодиода и фотодиода, а следовательно сигнал передатчика уходит в сторону от приёмника;
- не хватает мощности излучателя: можно попробовать пододвинуть детектор поближе к излучателю.
Другая плохая ситуация, когда приемник постоянно выдает «received», хотя должен делать это с периодом 1 сек. Если схемы собраны правильно, то вероятнее всего детектор засвечен паразитным ИК излучением.
В данной ситуации можно попробовать две вещи:
- приделать к фотодиоду бленду, которую можно сделать с помощью кусочка термоусадочной трубки;
- подобрать другой ИК спектр, для которого в данных условиях не будет паразитов-конкурентов, засвечивающих детектор.
На текущем этапе уже можно передавать с помощью ИК линии связи простые сигналы. Например, управлять включением и выключением какого-то устройства или робота. Чтобы обеспечить обмен текстом и числами, используем стандартную библиотеку и класс Serial. Об этом далее.
UART через ИК
Класс Serial, который есть в стандартной библиотеке Arduino IDE, умеет передавать данные через аппаратный интерфейс UART (он же последовательный порт). На плате семейства Ардуино контакты 0 (RX) и 1 (TX) как раз привязаны к аппаратному UART, а значит мы сможем их использовать для наших целей.
Схема излучателя у нас останется прежней, кроме контакта Ардуино, к которому она подключается. Мы передаем данные, значит используем контакт №1 (он же TX).
Программа передатчика будет каждую секунду отправлять в последовательный порт текст «ping».
void setup() {
Serial.begin(4800);
}
void loop() {
Serial.println("ping");
delay(200);
}
Готово. Теперь сделаем приёмник.
Программа приёмника должна с одной стороны слушать детектор и считывать оттуда полученные байты данных, а с другой стороны, выводить эти данные на компьютер. Задачу усложняет то, что детектор у нас имеет инверсную логику, а значит UART на принимающей стороне должен как-то уметь менять логику.
Таким образом, для создания приёмника нам необходимо, чтобы контроллер имел два UART интерфейса, причём с возможностью менять логику. Надо ли говорить, что у Ардуино Уно UART только один и логика там не меняется. Хмм.
Как вариант, можно вместо Уно использовать версию Мега, у которой есть несколько UART. Проблему с инверсной логикой можно решить переделав схему детектора и заменив транзистор на PNP. А можно всё оставить как есть и использовать программный UART интерфейс. Благо, в среде Arduino IDE есть специальная библиотека SoftwareSerial, которая как раз для таких целей и нужна.
Как и в большинстве случаев, при использовании разного рода библиотек, нам потребуется в самом начале программы создать объект класса SoftwareSerial. Пишем:
SoftwareSerial Photo(10, 11, 1);
здесь первый аргумент 10 — контакт, на котором будет RX канал; второй аргумент 11 — контакт TX канала; 1 — означает инверсию сигнала, это нам и нужно!
Программа приёмника данных будет выглядеть так.
#include <SoftwareSerial.h>
SoftwareSerial Photo (10, 11, 1);
void setup() {
Serial.begin(9600);
Photo.begin(4800);
}
void loop() {
while( Photo.available() ){
char v = Photo.read();
Serial.write(v);
}
}
Загружаем программы, выставляем излучатель и детектор на линии и подаем питание. Если при этом открыть монитор последовательного порта, то там будут появляться слова «ping».
В данном примере мы использовали скорость передачи 4800 бод. Откуда именно такая скорость? Дело в том, что скорость работы нашей ИК системы сильно проседает из-за программного UART. Если от него избавиться, то линию связи вполне можно разогнать до куда более высоких скоростей.
Подобную систему связи уже можно применить для передачи сообщений в плотной среде роевых роботов. При такой скученности радиосвязь работает плохо, а вот передача данных ИК-светом ближайшим соседям вполне подойдет.
В следующей главе посмотрим как сделать оптическую линию еще надежнее и быстрее.
Лазерная связь
Увеличить дистанцию приёма можно разными способами, один из них — использовать мощный излучатель. Например, можно заменить светодиод на лазер.
Для эксперимента подойдет обычный лазерный модуль мощностью 5 мВт со встроенным токозадающим резистором. Полупроводниковый лазер по устройству очень похож на светодиод, и подключается он точно так же: катод — к земле, анод — к контакту Ардуино №2. Собираем всё схемы, загружаем программы, подаем питание.
Самое главное при настройке лазерной связи — попасть точкой лазера в фотодиод. Даже на расстоянии в несколько метров — это не так то просто. Для упрощения задачи, на фотодиод можно надеть собирающую линзу, которую используют в датчиках движения.
Модуляция
Еще один способ увеличить дистанцию — модуляция сигнала. Это значит, что передатчик должен генерировать несущую частоту и модулировать её полезным цифровым сигналом. Полученный сигнал передается в эфир. Приёмник же должен обеспечить демодуляцию полезного сигнала.
Сделать такую систему можно из доступных каждому DIY-щику деталей. В передатчике катод ИК-светодиода подключаем не к земле, а к ещё одному контакту Ардуино. Программа приемника должна формировать на этом контакте прямоугольный сигнал частотой 38 кГц. Например так:
TCCR2A = _BV(COM2A0) | _BV(WGM21);
TCCR2B = _BV(CS20);
OCR2A = 209;
Для приёма модулированного сигнала используется стандартный ИК-приёмник на 38 кГц, например, vs1838. Этот прибор сам производит демодуляцию сигнала.
Над учитывать, что чем выше несущая частота, тем больше пропускная способность такого канала. С несущей частотой 38 кГц можно добиться скорость передачи до 2400 бод.
#include
#include
const int buttonPin = 5; // Кнопка на пине 5
IRsend irsend;
IRrecv irrecv(2, 4); // ИК-приемник подключен к пину 2, с дополнительным буфером 4
boolean isTransmitting = true; // Флаг для определения режима работы (true — передача, false — прием)
File dataFile; // Глобальная переменная для хранения файла из SD-карты
void setup() {
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);
irsend.begin(3); // Передатчик подключен к пину 3
if (!irrecv.enableIRIn()) {
Serial.println(«Unable to start receiver»);
}
if (!SD.begin(10)) {
Serial.println(«SD initialization failed!»);
while (1); // Остановить выполнение, если инициализация SD-карты не удалась
}
pinMode(10, OUTPUT); // Устанавливаем пин 10 как выход для SD-карты (CS)
// Установка пинов SCK, MOSI, MISO не требуется, они устанавливаются автоматически при инициализации SD
}
void loop() {
if (digitalRead(buttonPin) == LOW) {
delay(50); // Для устранения дребезга контактов
if (isTransmitting) {
transmitData();
} else {
receiveData();
}
}
}
void transmitData() {
dataFile = SD.open(«music.mp3», FILE_READ); // Открываем файл mp3 для чтения
if (dataFile) {
while (dataFile.available()) {
irsend.sendSony(dataFile.read(), 8); // Отправляем данные (байты файла mp3) через ИК-передатчик
delay(50);
}
dataFile.close();
isTransmitting = false; // Переключаем режим на прием
digitalWrite(6, HIGH); // Зажигаем светодиод на пине 6, чтобы показать, что передача завершена
} else {
Serial.println(«error opening file»);
}
}
void receiveData() {
decode_results results;
if (irrecv.decode(&results)) {
irrecv.resume(); // Продолжаем прием ИК-сигналов
byte receivedData = results.value & 0xFF; // Получаем принятый байт данных
dataFile = SD.open(«music_received.mp3», FILE_WRITE); // Открываем файл для записи
if (dataFile) {
dataFile.write(receivedData); // Записываем принятый байт в файл
dataFile.close();
isTransmitting = true; // Переключаем режим на передачу, чтобы готовиться к следующей передаче
digitalWrite(6, LOW); // Выключаем светодиод на пине 6
} else {
Serial.println(«error opening file»);
}
}
}
/*
pinMode(10, OUTPUT); // Устанавливаем пин 10 как выход для SD-карты (CS)
pinMode(13, OUTPUT); // Установка пина 13 (SCK) и 11 (MOSI) включается автоматически при инициализации SD
pinMode(12, INPUT); // MISO используется только для чтения, обычно устанавливается как входной
SCK (Serial Clock) — пин 13
MOSI (Master Out Slave In) — пин 11
MISO (Master In Slave Out) — пин 12
CS (Chip Select) — пин 10
*/
там не вывелся импорт библиотек IRremote.h и SD.h