IRRemote отправлять и получать тем же Arduino
Я новичок в Arduino (и C/C++), поэтому прошу прощения, если упустил что-то очевидное.
Я использую IRremote (ссылка на github) в качестве библиотеки IR.
Я могу получить простые демонстрации отправки и получения, работающие по отдельности.
Однако я не могу одновременно отправлять и получать данные в одном и том же Arduino.
Я не хочу делать что-то вроде примера «IRrecord», но я хочу, чтобы Arduino постоянно отправлял и получал, а затем запускал сигнал тревоги/светодиод, как только «луч» прерывается.
В качестве ИК-приемника я использую серию TSOP 22. Я пытался использовать протопотоки, но безуспешно. Любые советы приветствуются.
Мой текущий фрагмент кода:
#include <IRremote.h>
int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
IRsend irsend;
decode_results results;
void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); // Запускаем приемник
Serial.println("IR Receiver... Started");
}
void loop() {
// однопоточная природа, кажется, ломается ниже
// здесь я хочу запустить приемник
if (irrecv.decode(&results)) {
Serial.println(results.value, HEX);
irrecv.resume(); // Получаем следующее значение
}
// задержка (5000);
// пытаемся отправить сюда
for (int i = 0; i < 3; i++) {
irsend.sendSony(0xa90, 12);
Serial.println("Sent Sony......");
delay(40);
}
// задержка (5000); // 5-секундная задержка между каждым пакетом сигнала
}
@user1843591, 👍1
3 ответа
Лучший ответ:
Мне удалось сделать это с помощью решения из блога Кена Шерифа. . Кен приводит простой пример и прекрасно его документирует. Из-за особенностей моего ИК-приемника (отклоняющего непрерывный луч) я использовал пример кода, предоставленный Стефаном Денио в разделе комментариев. Спасибо обоим...
#include <IRremote.h>
#define PIN_DETECT 2
IRsend irsend;
void setup()
{
pinMode(PIN_DETECT, INPUT);
Serial.begin(115200);
Serial.println("READY");
attachInterrupt(digitalPinToInterrupt(PIN_DETECT), checkIRBeamBreak, RISING);
// Примечание: ИК-излучатель находится на контакте № 3
irsend.enableIROut(38);
IREmitterOn();
}
// Используйте эту функцию вместо delay(), так как delay() не работает в обратных вызовах прерываний
void pause(int ms) {
// Нам нужен цикл, так как наибольшее значение delayMicroseconds, которое даст точную задержку, равно 16383
for (int i = 0; i < ms; i++) {
delayMicroseconds(1000);
}
}
void IREmitterOff() {
irsend.space(0);
pause(60); // 60 мс нормально для моего TSOP, но его нужно настроить
}
void IREmitterOn() {
irsend.mark(0);
pause(10);
}
void switchOffOnIREmitter() {
IREmitterOff();
IREmitterOn();
}
void checkIRBeamBreak() {
int val = digitalRead(PIN_DETECT);
// НИЗКИЙ: без разрыва луча
// ВЫСОКИЙ: обрыв луча
if (val == LOW)
return;
detachInterrupt(digitalPinToInterrupt(PIN_DETECT));
switchOffOnIREmitter();
if (digitalRead(PIN_DETECT) == HIGH) {
Serial.println("IR BEAM BREAK !!!!");
}
attachInterrupt(digitalPinToInterrupt(PIN_DETECT), checkIRBeamBreak, RISING);
}
void loop() {
}
Я ДУМАЮ, проблема в том, что Arduino требуется некоторое время (очень минимальное) для отправки сигнала, но когда дело доходит до его приема, сигнал уже рассеялся по вашей комнате.
отправлять и получать в любое время, а затем активировать сигнал тревоги/светодиод, как только «луч» прерывается.
Используйте лазер, он дешевле, точнее и больше похож на Джеймса Бонда :)
основываясь на концепции user1843591 answer, я провел дополнительные исследования и нашел пример Ника Гэммона на форуме arduino.
После некоторых настроек я пришел к следующему: (на основе обоих) он использует Timer1 для генерации сигнала 38 кГц и Timer2 для генерации цикла 760 Гц, который включает и выключает сигнал 38 кГц. таким образом рекомендуемые импульсные циклы и интервалы времени для Ресивер (у меня TSOP4438) сохранен. он также реализует глобальные флаги, чтобы отделить ISR и ISR. основной цикл.
(оба файла должны находиться в папке IR_TSOP4438_light_barrier
)
IR_TSOP4438_light_barrier.ino
// простой тест светового барьера
extern volatile bool light_barrier_broken;
void setup(){
Serial.begin(115200);
Serial.println("IR_TSOP4438_light_barrier");
Serial.println("setup...");
setupIRLightBarrier();
Serial.println("running.");
}
void loop() {
if(light_barrier_broken) {
Serial.println("light barrier 1 break detected");
delay(20);
digitalWrite (LED_BUILTIN, LOW);
light_barrier_broken = false;
}
}
ir_light_barrier.ino
// на основе
// Пример модуляции несущей частоты 38 кГц на частоте 500 Гц с переменным рабочим циклом
// Автор: Ник Гэммон
// Дата: 24 сентября 2012 г.
// https://forum.arduino.cc/t/how-to-create-a-38-khz-pulse-with-arduino-using-timer-or-pwm/100217/44
// изменено для TSOP4438
// https://www.vishay.com/docs/82459/tsop48.pdf
// найти максимальную длину пакета и минимальное время промежутка:
//
// Минимальная длина пакета 10 циклов/пакет
// После каждого пакета длиной от 10 до 40 циклов
// требуется минимальный промежуток времени ≥ 10 циклов
// Максимальное количество непрерывных коротких импульсов в секунду: 1500
//
// переводится как:
// 38 кГц = 26,3 мкс/импульс → *10= 263 мкс
// максимальная длина пакета: <= 1052 мкс (26,3*40)
// минимальная длина промежутка: >= 263 мкс (26,3*10)
// общее время цикла (1052+263=) 1315 мкс
// что нарушает максимальное количество импульсов в секунду (666 мкс/пакет)
//
// включена опция оптимизации для наибольшего количества пакетов в секунду:
// поэтому мы используем минимальное время разрыва как заданное и используем оставшееся доступное время.
// 666 мкс - 263 мкс = длина пакета 403 мкс
// надеюсь, таким образом AGC не будет фильтровать наш поток...
// теперь мы должны подогнать это к лучшим доступным значениям прескалера/счетчика:
// 672us подходит хорошо (таким образом, мы имеем меньше, чем взрыв 1500)
// это означает 42 счета a' (0,0625us*256=) 16us
// поэтому мы используем длину промежутка 16 мкс * 17 = 272 мкс
// это дает нам длину пакета 16 мкс * (42-17) = 400 мкс
//
// Второй вариант — оптимизировать для максимальной длины пакета и иметь меньшее количество пакетов в секунду.
// здесь мы будем использовать промежуточный вариант, склоняясь к более длинным пакетам:
// 0,0625мкс*256=16мкс
// 16 мкс * 50 отсчетов = 800 мкс = 0,80 мс = 1250,00 Гц = 1250 импульсов/с
// 16 мкс * 80 отсчетов = 1280 мкс = 1,28 мс = 781,25 Гц = 781,25 импульсов/с
// длина промежутка: 16 мкс * 17 = 272 мкс
// длина пакета: 16 мкс*(80-17) = 1008 мкс
// http://www.gammon.com.au/forum/?id=11504
// Таймер 1
// ОС1А: D9
// ОС1В: Д10
const byte LED = 9;
// Таймер 2 (8 бит)
// ОС2А: D11
// ОС2В: Д3
// https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/
const byte light_barrier_PIN = 2;
// константный байт light_barrier_PIN = 3;
// глобальные флаги, которые будут обрабатываться в основном цикле...
volatile bool light_barrier_broken = false;
// Тактовая частота, деленная на желаемую частоту 38 кГц
const long timer1_OCR1A_Setting = F_CPU / 38000L;
// (16000000 / 38000) = 421,05
// это работает только на Таймере 1, так как это 16-битный таймер.
// // ---------------------------------------------------------
// // это самая медленная из возможных настроек.
// // счетчик таймера2:
// // ЦП 16МГц (0,0625us)
// // цель 63 Гц (16000 мкс = 16 мс)
// // прескалер 1024
// const long timer2_top = F_CPU/63L/1024;
// // (16000000/63/1024) = 250
// // ---------------------------------------------------------
// // оптимизировано для ~1500 пакетов в секунду
// // целевые счетчики:
// // ЦП 16МГц (0,0625us)
// // прескалер 256
// // цель 1488 кГц (672 мкс = 0,672 мс)
// const long timer2_top = (F_CPU/256L)/1488L;
// // (16000000 / 256) / 1488 = 42
// // рассчитываем коэффициент включения/выключения (точка переключения)
// const long timer2_compare = timer2_top * 400L / 672L;
// // 42 * 400 / 672 = 42 - 17 = 25
// ---------------------------------------------------------
// промежуточный - склоняемся к более длинным всплескам
// целевые счетчики:
// ЦП 16МГц (0,0625us)
// прескалер 256
// цель 781 кГц (1280 мкс = 1,28 мс)
const long timer2_top = (F_CPU / 256L) / 781L;
// (16000000 / 256) / 781 = 80
// рассчитываем коэффициент включения/выключения (точка переключения)
const long timer2_compare = timer2_top * 1008L / 1280L;
// 80 * 1008 / 1280 = 80 - 17 = 63
volatile bool sender_active = false;
void light_barrier_ISR() {
if (
sender_active
&& digitalRead(light_barrier_PIN) == HIGH
) {
digitalWrite (LED_BUILTIN, HIGH);
light_barrier_broken = true;
// если() {
// }
} else {
digitalWrite (LED_BUILTIN, LOW);
}
}
ISR (TIMER2_COMPA_vect) {
// используется для объединения двух таймеров...
// переключить D13
// PINB = бит (5);
// digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)) ;
if (sender_active == false) {
// включить выход timer1
TCCR1A |= bit(COM1A0) ; // Переключить OC1A при сравнении совпадений
// цифровая запись (LED_BUILTIN, HIGH);
delayMicroseconds(100);
sender_active = true;
} else {
sender_active = false;
delayMicroseconds(100);
// отключаем проверку входа ИК-приемника
//detachInterrupt(digitalPinToInterrupt(light_barrier_PIN));
// отключаем выход timer1
TCCR1A &= ~bit(COM1A0) ; // НЕ переключать OC1A при сравнении соответствия
digitalWrite (LED, LOW); // убедиться, что выключено
// цифровая запись (LED_BUILTIN, LOW);
}
}
void setupIRLightBarrier() {
pinMode(LED, OUTPUT);
digitalWrite(LED, LOW);
pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);
// настраиваем Таймер 1 - получаем 38,095 КГц
TCCR1A = bit (COM1A0); // включить OC1A при сравнении
TCCR1B = _BV(WGM12) | _BV (CS10); // CTC в OCR1A, без предварительного делителя
OCR1A = (16000000L / 38000L / 2) - 1; // относительный нуль
// установка Таймера 2
TCCR2A = 0;
TCCR2B = 0;
// включить OC2A при сравнении
// TCCR2A |= бит(COM2A0);
// быстрая ШИМ на OCR2A
TCCR2A |= bit(WGM21) | bit(WGM20);
TCCR2B |= bit(WGM22);
// прескалер 1024
// TCCR2B |= бит(CS22) | бит (CS21) | бит (CS20);
// прескалер 265
TCCR2B |= bit(CS22) | bit(CS21);
// вершина
OCR2A = timer2_top - 1; // относительный нуль
// точка переключения
OCR2B = timer2_compare - 1; // относительный нуль
// разрешаем прерывания
TIMSK2 = bit(OCIE2A);
// TIMSK2 = бит(OCIE2B) | бит (OCIE2A);
// включить проверку входа ИК-приемника
attachInterrupt(
digitalPinToInterrupt(light_barrier_PIN),
light_barrier_ISR,
CHANGE
);
}
- Как изменить переменную при нажатии кнопки, подключенной к контакту 2
- Ошибка переменной does not name a type!
- Использование YS-IRTM с Arduino Uno
- Объявленная переменная внутри void setup не видится в void loop
- 7-сегментный дисплей с потенциометром
- Акцептант векселей ИКТ
- Как преобразовать значения RGB в интенсивность
- Добавление нулевого заполнения к десятичным значениям
Спасибо за идею @Dat Ha. Я думал, это круче!.... но я исключил это, так как этот проект будет использоваться детьми, поэтому я подумал, что лучше избегать лазеров! Спасибо что нашли время ответить, @user1843591
Эта теория здесь не совсем верна — в то время как распространение света достаточно быстрое, чтобы его можно было игнорировать, время отклика демодулирующего детектора соответствует шкале Arduino. Проблема заключается в архитектуре программного обеспечения - схемы ИК-пульта дистанционного управления здесь неприменимы, скорее, необходима посылка модуляции (включение и выключение импульсов) и проверка приемника в промежуточных точках. Но также обратите внимание, поскольку правильный ответ разъясняет, что сам модулированный сигнал должен включаться и выключаться с более медленным временем цикла, иначе детектор отклонит его., @Chris Stratton