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-секундная задержка между каждым пакетом сигнала
}

, 👍1


3 ответа


Лучший ответ:

5

Мне удалось сделать это с помощью решения из блога Кена Шерифа. . Кен приводит простой пример и прекрасно его документирует. Из-за особенностей моего ИК-приемника (отклоняющего непрерывный луч) я использовал пример кода, предоставленный Стефаном Денио в разделе комментариев. Спасибо обоим...

#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() {

}
,

1

Я ДУМАЮ, проблема в том, что Arduino требуется некоторое время (очень минимальное) для отправки сигнала, но когда дело доходит до его приема, сигнал уже рассеялся по вашей комнате.


отправлять и получать в любое время, а затем активировать сигнал тревоги/светодиод, как только «луч» прерывается.

Используйте лазер, он дешевле, точнее и больше похож на Джеймса Бонда :)

,

Спасибо за идею @Dat Ha. Я думал, это круче!.... но я исключил это, так как этот проект будет использоваться детьми, поэтому я подумал, что лучше избегать лазеров! Спасибо что нашли время ответить, @user1843591

Эта теория здесь не совсем верна — в то время как распространение света достаточно быстрое, чтобы его можно было игнорировать, время отклика демодулирующего детектора соответствует шкале Arduino. Проблема заключается в архитектуре программного обеспечения - схемы ИК-пульта дистанционного управления здесь неприменимы, скорее, необходима посылка модуляции (включение и выключение импульсов) и проверка приемника в промежуточных точках. Но также обратите внимание, поскольку правильный ответ разъясняет, что сам модулированный сигнал должен включаться и выключаться с более медленным временем цикла, иначе детектор отклонит его., @Chris Stratton


2

основываясь на концепции 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
    );
}

,