Indice

Comunicazione a infrarosso

Esperimenti di comunicazione a infrarosso con Arduino usando un ricevitore IR integrato e un IRED:

Hardware

ricevitore IR

Un integrato con un fotodiodo e un circuito di condizionamento che si occupa della demodulazione del segnale a 38kHz inviato dal trasmettitore.

Tre piedini: Vcc, GND e segnale.

Segnale in uscita: alto quando non riceve (space), basso quando riceve un segnale IR a 38kHz (mark/burst).

Decodifica: esistono tanti protocolli che codificano 0 e 1 un maniera differente (ma sempre in base alla durata di space e mark). Il protocollo definisce anche cosa viene trasmesso e con che tempi (in genere 8 bit per indirizzo e comando che vengono inviati due volte e un codice per il repeat).

Trasmettitore

Un LED a infrarosso della lunghezza d'onda giusta (compatibile col ricevitore) da pilotare a 38kHz con un microcontrollore.

Idee per progetti

Osservazioni

Decodifica segnale IR con Arduino

Esiste una libreria molto facile da utilizzare fatta da Ken Shirrif. Il programma seguente invece non usa librerie ed è sufficientemente facile da capire. Prerequisiti:

/* 
 TODO
 - MAXPULSE dice che sono ms ma viene confrontato con 
 variabili che sono us/20
 
 
 RICEVITORE IR
 demodula il segnale di un trasmettitore IR generando
 - livello H per space (trasmettitore off)
 - livello L per mark/burst (impulsi a 38kHz)
 
 LEGGERE I SEGNALI CON I REGISTRI I/O
 l'istruzione digitalRead() è troppo lenta perciò bisogna
 usare l'accesso diretto ai pin con i registri I/O
 PIND è il registro che permette di leggere il valore 
 degli ingressi da 0 a 7 (0 livello L, 1 livello H)
 il terzo bit da destra di PIND è riferito al pin 2
 nel test del ciclo while shifta 1 a sinistra di due
 posizioni (da 00000001 a 00000100) poi fa un AND con gli
 otto bit di PIND; se il risultato è ancora 00000100 
 il segnale è alto
 vedi PortManipulation e BitMath nella Reference 
 
 PROGRAMMA ORIGINALE
 da adafruit modificato nello stile (const invece che define)
 e commentato in italiano
 
 */
 
// pin segnale ricevitore IR
const byte IRpin = 2;
 
// valore massimo di highpulse/lowpulse prima del timeout
// 65000 intervalli da 20us = 1.3 secondi
const unsigned int MAXPULSE = 65000;
 
// risoluzione in us nella lettura degli impulsi
// (gli intervalli saranno multipli di questo valore)
const byte RESOLUTION = 20;
 
// memorizza la durata di H e L per 100 impulsi
unsigned int pulses[100][2];
byte currentpulse = 0; // indice dell'array pulses
 
void setup(void) {
  Serial.begin(9600);
  Serial.println("Attesa sequenza IR!");
}
 
void loop(void) {
  // durata H e L in numero di intervalli di 20us
  unsigned int highpulse, lowpulse;  
  // azzerati a ogni ciclo
  highpulse = lowpulse = 0; 
 
  // se il segnale demodulato è alto (space)
  while (PIND & (1 << IRpin)) {
 
    // incrementa highpulse e aspetta 20 us
    highpulse++;
    delayMicroseconds(RESOLUTION);
 
    // timeout: stampa la sequenza di impulsi, azzera 
    // l'indice e esce da loop (ricominicia da capo)
    if ((highpulse >= MAXPULSE) && (currentpulse != 0)) {
      //printpulses();
      printpulses2();
      currentpulse=0;
      return;
    }
  }
 
  // il segnale diventa L (o timeout)
  // scrivo la durata dell'impulso H
  pulses[currentpulse][0] = highpulse;
 
  // se il segnale è basso (mark)
  while (! (PIND & (1 << IRpin))) {
 
    // incrementa lowpulse e aspetta 20 us
    lowpulse++;
    delayMicroseconds(RESOLUTION);
 
    // timeout: stampa la sequenza di impulsi, azzera 
    // l'indice e esce da loop (ricominicia da capo)
    if ((lowpulse >= MAXPULSE)  && (currentpulse != 0)) {
      //printpulses();
      printpulses2();
      currentpulse=0;
      return;
    }
  }
 
  // il segnale diventa H (o timeout)
  // scrivo la durata dell'impulso L
  pulses[currentpulse][1] = lowpulse;
 
  // passiamo al prossimo impulso (coppia durate H/L)
  currentpulse++;
}
 
void printpulses(void) {
  // \n\r newline + return lascia un linea vuota
  Serial.println("\n\rSequenza impulsi (tempi in microsecondi)");
  Serial.println("space (H)\tmark (L)");
  Serial.println("IR OFF\t\tIR ON");
  for (byte i = 0; i < currentpulse; i++) {
    Serial.print(pulses[i][0] * RESOLUTION, DEC);
    Serial.print("\t\t");
    Serial.println(pulses[i][1] * RESOLUTION, DEC);
  }
}
 
void printpulses2(void) {
  // \n\r newline + return lascia un linea vuota
  Serial.println("\n\rSequenza impulsi (tempi in microsecondi)");
  Serial.println("mark (L)\tspace (H)");
  Serial.println("IR ON\t\tIR OFF");
  Serial.print(pulses[0][1] * RESOLUTION, DEC);
  for (byte i = 1; i < currentpulse; i++) {
    Serial.print("\t\t");
    Serial.println(pulses[i][0] * RESOLUTION, DEC);
    Serial.print(pulses[i][1] * RESOLUTION, DEC);
  }
}

Telecomando IR con Arduino

Programma adattato da adafruit per pilotare lo scatto remoto della mia Nikon D50 (funziona). Il codice IR è stato ottenuto con un telecomando Nikon e qualcosa tipo il programma sopra. Non usa i registri quindi è sicuramente migliorabile lato prestazioni.

// Intervallometro IR per la Nikon D50 (da adafruit)
 
// IRED sul pin 13 con resistore da 1k
byte IRED = 13;
// intervallo in secondi tra gli scatti
byte intervallo = 5;
 
void setup() {                
  pinMode(IRED, OUTPUT);      
  Serial.begin(9600);
}
 
void loop() {
  Serial.println("Invio sequenza IR");
  // invia comando scatto
  SendNikonCode();
  // intervallo
  delay(intervallo * 1000);
}
 
// genera un segnale a 38kHz per tot microsecondi
void pulseIR(long usecs) {
 
  // disattiva gli interrupt
  noInterrupts();
 
  while (usecs > 0) {
    // 38 kHz corrisponde circa a 26 us (3+10+3+10)
    // digitalWrite impiega 3 us (usare i registri?) 
    digitalWrite(IRED, HIGH);
    delayMicroseconds(10);
    digitalWrite(IRED, LOW);
    delayMicroseconds(10);
    usecs -= 26;
  }
 
  // riattiva gli interrupt
  interrupts();
}
 
// sequenza IR per scatto remoto Nikon D50
void SendNikonCode() {
  // comando scatto
  pulseIR(2000);  // 2080 codice adafruit non va!
  delay(27);
  pulseIR(440);
  delayMicroseconds(1500);
  pulseIR(460);
  delayMicroseconds(3440);
  pulseIR(480);
 
  // pausa
  delay(65);
 
  // ripetizione comando scatto
  pulseIR(2000);
  delay(27);
  pulseIR(440);
  delayMicroseconds(1500);
  pulseIR(460);
  delayMicroseconds(3440);
  pulseIR(480);
}

Telecomando ON/OFF TV LG

La sequenza corretta è stata ottenuta confrontando il protocollo utilizzato dalla TV LG (il protocollo NEC) con la sequenza ottenuta col programma che decodifica i segnali IR (vedi sopra). E' possibile accendere e spegnere la TV con Arduino, un IRED (di lunghezza d'onda compatibile) e resistore. Testato su smart TV LG 43LF590V.

// TODO riscriverlo con array (o byte per la sequenza)
 
// on/off smart tv LG
// invia il comando e lo ripete dopo 2 minuti
// reset per rilanciarlo
 
// IRED sul pin 13 con resistore da 1k
byte IRED = 13;
 
void setup() {                
  pinMode(IRED, OUTPUT);      
  Serial.begin(9600);
}
 
void loop() {
  Serial.println("Invio sequenza IR tra un secondo");
  delay(1000);
  // invia comando on/off
  SendCode();
  // intervallo
  delay(120000);
}
 
// genera un segnale a 38kHz per tot microsecondi
void pulseIR(long usecs) {
  // disattiva gli interrupt
  noInterrupts();
  while (usecs > 0) {
    // 38 kHz corrisponde circa a 26 us (3+10+3+10)
    // digitalWrite impiega 3 us (usare i registri?) 
    digitalWrite(IRED, HIGH);
    delayMicroseconds(10);
    digitalWrite(IRED, LOW);
    delayMicroseconds(10);
    usecs -= 26;
  }
  // riattiva gli interrupt
  interrupts();
}
 
// sequenza IR per pulsante on/off LG
// il protocollo e il NEC
// 562 + 562 codifica il valore logico 0
// 562 + 1687 codifica il valore logico 1
// ottenuto con arduino e un ricevitore IR è questo
//mark (L)	space (H)
//IR ON		IR OFF
//8880		4420      9ms + 4ms per inizio trasmissione
//560		540       8 bit per l'indirizzo 
//560		540       
//560		1660      indirizzo 00100000
//540		540
//560		540
//540		540
//560		540
//560		540
//560		1660      ancora l'indirizzo ma complementato
//540		1660
//540		540       complementato 11011111
//560		1680
//520		1680
//520		1680
//520		1680
//520		1680
//520		540       8 bit del comando
//560		540
//560		540       comando 00010000
//560		1680
//520		540
//560		540
//560		540
//560		540
//560		1660      ancora il comando ma complementato
//540		1660
//540		1660      11101111
//540		540
//540		1680
//540		1660
//540		1660
//540		1660
//540                     fine trasmissione
 
void SendCode() {
  // inizio trasmissione
  pulseIR(9000);
  delayMicroseconds(4000);
  // indirizzo 00100000
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(1687);  
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(560);
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(560); 
  // indirizzo complementato 11011111
  pulseIR(560);
  delayMicroseconds(1687); 
  pulseIR(560);
  delayMicroseconds(1687);
  pulseIR(560);
  delayMicroseconds(560); 
  pulseIR(560);
  delayMicroseconds(1687); 
  pulseIR(560);
  delayMicroseconds(1687); 
  pulseIR(560);
  delayMicroseconds(1687);
  pulseIR(560);
  delayMicroseconds(1687);   
  pulseIR(560);
  delayMicroseconds(1687);   
  // comando 00010000
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(1687);  
  pulseIR(560);
  delayMicroseconds(560);
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(560);  
  pulseIR(560);
  delayMicroseconds(560);
  // comando complementato 11101111 
  pulseIR(560);
  delayMicroseconds(1687); 
  pulseIR(560);
  delayMicroseconds(1687);
  pulseIR(560);
  delayMicroseconds(1687); 
  pulseIR(560);
  delayMicroseconds(560); 
  pulseIR(560);
  delayMicroseconds(1687); 
  pulseIR(560);
  delayMicroseconds(1687);
  pulseIR(560);
  delayMicroseconds(1687);   
  pulseIR(560);
  delayMicroseconds(1687);    
  // fine trasmissione
  pulseIR(560);
  delayMicroseconds(4000); 
}

Riferimenti