esempio: Trasmissione ricezione IR

Costruire il seguente circuito (serve un ricevitore, un trasmettitore IR e una resistenza da 180 ohm)

Costruiamo i due circuiti:



Ecco l'immagine del circuito ricevente

e quello del circuito trasmissivo.

Per la trasmissione IR è sufficiente un LED IR ed una resistenza per limitare la corrente di 100 Ohm (ho usato una da 180 Ohm). Se aumento la resistenza riduco la “luminosità” del LED IR riducendo la portata del segnale.

Disponiamo poi circuiti in modo che il ricevitore e il trasmettitore possano comunicare.

Il modo più semplice per gestire la trasmissione IR è quello di utilizzare una opportuna libreria IRRemote.zip .  Va scompattata nella cartella  delle librerie di arduino ovvero in: C:\Program Files\Arduino\libraries. La cartella ottenuta va rinominata in Keypad e poi, tramite il menu di arduino Sketch => Importa Libreria ... ==> AddLibrary, va aggiunta. Questa classe mette a disposizione alcuni metodi che semplificano di molto l’obiettivo.

La libreria IRremote consente sia la trasmissione che la ricezione. Vediamo ora i comandi disponibili:
 
Ricezione:

IRrecv irrecv(receivePin)
: Crea un oggetto ricevente. Il nome della variabile (istanza) può essere definito a piacere.
irrecv.enableIRIn() : Inizia il processo di ricezione. Attiva un timer che utilizza  una piccola quantità di CPU ogni  50 µs (microsecondi 10-6).
irrecv.decode(&results): Tenta di ricevere  un segnale IR. Restituisce vero se un segnale è stato ricevuto, falso altrimenti. Quando un segnale è stato ricevuto, immagazzina l'informazione all'interno della variabile strutturata  "results".  I campi di tale variabile sono:

   results.decode_type: Restituisce uno dei seguenti valori: NEC, SONY, RC5, RC6 o UNKNOWN.
   results.value: Il tipo di codifica IR (0 se il tipo è sconosciuto UNKNOWN)
   results.bits: Il numero di bits utilizzati dal segnale
   results.rawbuf:  L'array contenete i segnali ricevuti
   results.rawlen: Il numero di elementi inseriti nell'array

irrecv.resume() : Va eseguita dopo il comando di ricezione per resettare il ricevitore e prepararlo al successivo segnale
irrecv.blink13(true) : Abilita il lampeggio del LED di ricezione (per vedere l'effetto di questa istruzione basta inserire l'anodo [gambino lungo] di un led sul pin 13 e il catodo su GND). Questa funzionalità è utilizzata solo per verificare il corretto trasferimento poiché la luce infrarossa non risulta visibile.

Trasmissione:

IRsend irsend : Crea un oggetto trasmittente. La libreria utilizzata utilizza sempre il pin 3 per pilotare il LED IR per cui non è necessario passarglielo come argomento.
irsend.sendNEC(IRcode, numBits) : Spedisce un segnale in codifica NEC.
irsend.sendSony(IRcode, numBits) : Spedisce un segnale utilizzando la codifica standard Sony.
irsend.sendRC5(IRcode, numBits) : Spedisce un segnale utilizzando la codifica RC5.
irsend.sendRC6(IRcode, numBits) :  Spedisce un segnale utilizzando la codifica standard RC6
irsend.sendRaw(rawbuf, rawlen, frequency) : Invia utilizzando la codifica generica RAW. La variable rawbuf è un buffer contenete i singoli dati da inviare, rawlen la lunghezza del buffer e frequency la frequenza di invio dei dati (tipicamente 38 => 38 kHz).

Per inviare i segnali la libreria IRRemote utilizza la tecnologia PWM ed un timer che gestisce i segnali utilizzando il pin numero 3. Se volete cambiare pin dovete mettere mano alla libreria e modificarla, cosa che peraltro nelle ultime versioni è decisamente più semplice da farsi, comunque, a meno che non abbiate esigenze particolari, non dovrebbe essere un problema usare il pin 3 anziché altri.

ESEMPIO 1

Passiamo a realizzare il primo sketch di esempio:

Codice Sorgente Ricevitore

#include <IRremote.h>
int RECV_PIN = 3; // Pin di ricezione
IRrecv irrecv(RECV_PIN);
 
void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // Inizializzo il ricevitore
}
 
void loop() 
{
  decode_results results;
  if (irrecv.decode(&results)) 
  {
    // Tipi di codifica: -------------
    //    results.decode_type == NEC
    //    results.decode_type == SONY
    //    results.decode_type == HEX
    //    results.decode_type == RC5
    //    results.decode_type == RC6
    // -------------------------------
    if (results.decode_type == SONY)
       Serial.print((char)results.value);
    irrecv.resume(); // Receive the next value
  }
}

Codice Sorgente Trasmettitore

#include <IRremote.h>

void setup() 
{ 
}
 
void loop() 
{
   //http://www.pjrc.com/teensy/td_libs_IRremote.html
    IRsend irsend;
    // IRCode, Nr. Bit
    irsend.sendSony((int)'A', 12); 
    delay(40);
    irsend.sendSony((int)'v', 12); 
    delay(40);
    irsend.sendSony((int)'e', 12); 
    delay(40);
    irsend.sendSony((int)'\n', 12); 
    delay(4000); // Aspetto 4 secondi
}
Il risultato sul monitor seriale è il seguente

ESEMPIO 2

La IRremote ci permette di ottenere dati più precisi sulla decodifica effettuata utilizzando la proprietà decode_type dell'oggetto ricevitore di tipo decode_results. In altre parole possiamo avere due codici identici che però assumono significati diversi a seconda della codifica utilizzata. In particolare possiamo interpretare correttamente la codifica dopo aver stabilito la corretta codifica mediante decode_type. I codici attualmente supportati sono NEC, SONY, RC5, RC6, DISH, SHARP, PANASONIC, JVC, SANYO, MITSUBISHI ed UNKNOWN.

Passiamo a realizzare il secondo esempio che consente la spedizione tramite IR del testo scritto sul monitor seriale dell'emettitore.

Codice Sorgente Ricevitore

Lo stesso dell'esempio 1

Codice Sorgente Trasmettitore

#include <IRremote.h>
boolean MandaInvio=false;  

void setup()
{
  Serial.begin(9600);
}
void loop()
{
   IRsend irsend;
   if (Serial.available()) // se ho caratteri
   {
     irsend.sendSony((int)Serial.read(), 12); 
     delay(40);
     MandaInvio=true;
   }
   else if (MandaInvio)
   {
      irsend.sendSony((int)'\n', 12); 
      delay(40);
      MandaInvio=false;
   }
   
}

Il risultato sul monitor seriale del ricevente (se sul trasmettitore ho scritto: "Questo è un esempio\ndi trasmissione tramite IR\nAve a tutti\n" sarà il seguente

ESEMPIO 3

Passiamo a realizzare il terzo esempio. Prima di passare alla questione software vorrei prima fare alcune precisazioni. Istintivamente potremmo pensare che per inviare dei dati binari via infrarossi potrebbe bastare accendere il LED IR quando voglio rappresentare l'uno e toglierlo per gli zero. Questo però non è fattibile poiché lampadine, sole etc. utilizzano l'infrarosso al pari del LED IR. Per distinguere perciò un segnale non voluto dalla nostra comunicazione si usa inviare pacchetti di 0/1 per indicare uno stato alto, e l’assenza di segnali per quello basso. Questo è il motivo per cui i sensori IR sono sensibili ad una specifica frequenza e tagliano le frequenze indesiderate.

Analizziamo quindi il comportamento di un segnale IR. Quando premo uno dei bottoni di un telecomando a infrarossi il dispositivo emette un segnale. Questa trasmissione consiste in un segnale a 40kHz che viene acceso e spento seguendo un particolare schema (pattern). Bottoni differenti determinano sequenze on/off con pattern differenti. Quando il segnale è alto, un segnale IR a 40Khz viene emesso mentre quando è basso non viene trasmesso nulla.

Ad esempio un segnale IR Sony inizia con 2400 microsecondi (10-6) ON e 600 microsecondi OFF: si tratta del primo impulso che definisce lo start della trasmissione. Successivamente l'1 viene identificato con un segnale con 1200 microsecondi a ON e 600 a OFF mentre lo 0 con una trasmissione 600 a ON e 600 a OFF (Questa codifica si chiama SIRC). La differente ampiezza del segnale OFF / ON permette al ricevitore IR di riconoscere i bit trasmessi.

La forma d'onda sottostante rappresenta una trasmissione di 12-bit. La trasmissione è normalmente ripetuta per tutto il periodo di pressione sul bottone per un minimo di 3 volte (nel protocollo Sony). Ogni trasmissione inizia 45 microsecondi dopo la precedente trasmissione. Il protocollo Sony supporta trasmissioni a 15 e 20 bits.

Se il protocollo di decodifica è “UNKNOWN”, ossia sconosciuto, ci sono alcune preziose ulteriori informazioni che possiamo raccogliere: in particolare rawlen ci fornisce la lunghezza del buffer mentre l’array rawbuf contiene i dati ricevuti Grazie a questo array possiamo stamparne il contenuto. E' possibile stabilire la sequenza di bit di qualsiasi segnale ad infrarosso, calcolando il tempo in cui il segnale è alto e il tempo in cui il segnale è basso.

Obiettivo  del terzo esempio è scrivere programma che invia un'informazione in RAW e un secondo che decodifica il segnale ricevuto.

Codice Sorgente Trasmettitore

#include <IRremote.h>
// Definisco lo schema di rappresentazione dell'1 e dello 0
#define IR_1_A 700
#define IR_1_B 1600
#define IR_0_A 700
#define IR_0_B 400
// ----------------------------
// A => 65 => 0100 0001
// unsigned int S_Frase[18]={4600,4400, 700,400, 700,1600, 700,400, 700,400, 700,400, 700,400, 700,400,  700,1600 };
// ----------------------------
unsigned int S_Frase[20]={0};
void Codifica(unsigned int c, unsigned int S[18])
{
  int i, p=2, resti[8];
  unsigned int num=c;
  String Frase="",base2="",Bit="",s="";
  
  // Passo dalla codifica in base 10 a quella in base 2
  for(int i=7; i>=0 ; i--) 
  {
    resti[i]= num % 2;
    num=num/2;
  }
  
  // Codifica in BIT e somma di potenze di 2
  for(int i=0; i<8 ; i++) 
  {
    Bit=Bit+resti[i];
    if (resti[i]==1)
    { // Serie di potenze di 2 
      if (base2!="") 
        base2=base2+"+";
      base2=base2+"2^"+(7-i);
    }
  }

  // Start Trasmissione
  S[0]=4600;
  S[1]=4400;
  // Decodifico gli 1 e 0 in impulsi IR
  for(int i=0; i<8 ; i++) 
  {  
     if (resti[i] == 1)
     {
       S[p++]=IR_1_A;
       S[p++]=IR_1_B;
       s=s+IR_1_A+" "+IR_1_B+" ";
     }
     else  
     {
       S[p++]=IR_0_A;
       S[p++]=IR_0_B;
       s=s+IR_0_A+" "+IR_0_B+" ";
     }
  }
  // Descrivo la sequenza inviata
  Frase="Simbolo inviato: '"+String((char)c)+"' -> "+c;
  Frase=Frase+" - Decodifica in BIT: "+Bit;
  Frase=Frase+" - Somma potenze di 2: "+base2+"\n";
  Frase=Frase+"Codifica IR inviata: "+s+"\n";
  Serial.println(Frase); 
}

void setup()
{
  Serial.begin(9600);
}

void loop()
{
   IRsend irsend;
   
   Codifica('A',S_Frase); // A => 65 => 0100 0001
   irsend.sendRaw(S_Frase,20,38);
   delay(40);
   Codifica('v',S_Frase); // v => 118 => 0111 0110
   irsend.sendRaw(S_Frase,20,38);
   delay(40);
   Codifica('e',S_Frase); // e => 101 => 0110 0101
   irsend.sendRaw(S_Frase,20,38);
   delay(40);

   delay(3000); // Attendo 3 secondi
}
Il seguente codice produce il seguente output sul monitor seriale:


Codice Sorgente Ricevitore

I valori ricevuti, moltiplicati per USECPERTICK che è impostato nella libreria a 50, permette di ottenere i microsecondi degli zeri e uni che si susseguono nella trasmissione e che in gergo sono chiamati rispettivamente Space e Mark. Il parametro D serve per definire il range di errore accettabile sull'intervallo in microsecondi rilevato in ricezione. Ad esempio se la durata per per la trasmissione del segnale alto è 1600 μs allora qualsiasi durata in ricezione compresa tra [1600-D,1600+D] verrà interpretata come 1600.

#include 
// Definisco lo schema di rappresentazione dell'1 e dello 0
#define IR_1_A 700
#define IR_1_B 1600
#define IR_0_A 700
#define IR_0_B 400
#define D 130 // Margine di errore nella lettura

int RECV_PIN = 3; // Pin di ricezione
IRrecv irrecv(RECV_PIN);
 
void setup()
{
  Serial.begin(9600);
  irrecv.enableIRIn(); // Inizializzo il ricevitore
}

unsigned int Decodifica(decode_results results)
{
    unsigned int c=0, r1, r2;
    int e=7, p2=128; // 2^7
    String d="", r="", base2="", Frase="";
    r=r+(results.rawbuf[1]*USECPERTICK)+" ";
    r=r+String(results.rawbuf[2]*USECPERTICK)+" "; // posso anche usare String
    // Salto i primi 2 (Start of trasmission)
    for (int i=3; i<(results.rawlen-1) ; i=i+2)
    {  
        r1=results.rawbuf[i]*USECPERTICK;
        r2=results.rawbuf[i+1]*USECPERTICK;
        r=r+r1+" "+r2+" ";
        if ( (r1<=(IR_1_A+D)) && (r1>=(IR_1_A-D)) && (r2<=(IR_1_B+D)) && (r2>=(IR_1_B-D)) ) 
        {
           d=d+"1"; 
           if (base2!="") // Serie di potenze di 2 
              base2=base2+"+";
           c=c+p2;
           base2=base2+"2^"+e;
        } 
        else if ( (r1<=(IR_0_A+D)) && (r1>=(IR_0_A-D)) && (r2<=(IR_0_B+D)) && (r2>=(IR_0_B-D)) ) 
           d=d+"0"; 
        else
           d=d+"?";
        e--;
        p2=p2/2;
    }
    // Descrivo la sequenza ricevuta
    Frase="Codifica IR ricevuta: "+r+"\n";
    Frase=Frase+"Decodifica in BIT: "+d;
    Frase=Frase+" - Somma potenze di 2: "+base2+" = "+c;
    Frase=Frase+" - Ascii: '"+(char)c+"'\n";
    Serial.println(Frase);
}

void loop() 
{
  decode_results results;
  if (irrecv.decode(&results)) // Se ho qualcosa da decodificare ...
  {
     Decodifica(results);
     irrecv.resume(); // Ricevo il prossimo valore
     delay(40);
  }
}
Il risultato sul monitor seriale del ricevente sarà il seguente (si notino le differenti codifiche IR ricevute!)

http://www.raspibo.org/wiki/index.php?title=Ardubottino

http://www.logicaprogrammabile.it/motore-passo-passo-bipolare-driver-l298n/