Esperimenti di comunicazione a infrarosso con Arduino usando un ricevitore IR integrato e un IRED:
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).
Un LED a infrarosso della lunghezza d'onda giusta (compatibile col ricevitore) da pilotare a 38kHz con un microcontrollore.
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); } }
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); }
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); }