venerdì 1 maggio 2015

Comunicazione Wireless con XD-FST e XD-RF-5V e Arduino

Volevo fare un piccolo tutorial sul trasmettitore wireless XD-FST e il suo ricevitore XD-RF-5V. Ho iniziato con questi due componenti in quanto sono tra i più economici, si possono comprare in coppia su amazon ad un prezzo non superiore alle 4€ con spese di spedizione incluse, UN VERO AFFARE!

Il trasmettitore ha una modulazione a Radio Frequenza di 433Mhz ed è adatto a tutte quelle applicazioni che necessitano di un comando a distanza a ristretto raggio come l'apertura di un box, il comando per le finestre, accensione e spegnimento di luci, o come nel mio caso, il semplice invio di messaggi testuali wireless.

Il moduli si presentano cosi:


Se si fa una veloce ricerca su google si intuisce subito l'utilizzo è un gioco da ragazzi (inesperti) anche se io, per grandissima sfortuna (una sfortuna che si aggira ad 1 probabilità su 13), ho avuto un po di grattacapi, che in seguito espliciterò.

Innanzi tutto la prima cosa da fare, se non si vuole gestire manualmente tutta la comunicazione, è scaricare la libereria VirtualWire reperibile gratuitamente qui:

http://www.airspayce.com/mikem/arduino/VirtualWire/

Come scritto nelle prime righe è una libreria ormai non più mantenuta e sostituita da RadioHead, non ho ancora avuto modo di utilizzarla, ma sembra sicuramente più completa rispetto alla sua antecedente, anche se più complessa soprattutto per chi come me ha poca conoscenza dei diversi standard wireless.
Quindi continuando sulla nostra strada possiamo scaricare la versione 1.27 di VirtualWired direttamente dal link segnato sotto nella pagina dello sviluppatore, oppure più comodamente da questo link VirtualWired v1.27

Una volta scaricato e spacchettato verrà creara la cartela WirtualWire che avrà al suo interno i seguenti file:



-rw-r--r-- 1      39 gen 14  2013 CHANGES
drwxr-xr-x 2    4096 apr 29  2014 doc/
drwxrwxr-x 6    4096 apr 20 21:22 examples/
-rw-r--r-- 1      22 apr 17  2008 keywords.txt
-rw-r--r-- 1     841 apr 20  2008 LICENSE
-rw-r--r-- 1     677 apr 29  2014 Makefile
-rw-r--r-- 1     426 apr 29  2014 MANIFEST
-rw-r--r-- 1     248 apr 20  2008 README
-rw-r--r-- 1    4627 feb 24  2014 VirtualWire_Config.h
-rw-r--r-- 1   24086 apr 29  2014 VirtualWire.cpp
-rw-r--r-- 1   20522 apr 29  2014 VirtualWire.h
drwxrwxr-x 2    4096 apr 20 21:22 VWutil/


copiate quindi tutta la cartella nelle librerie utente di Arduino (nei sistemi Linux nella propria home, mentre nei sistemi Windows nella cartella Documents)

nel mio caso (con Ubuntu) la directory è la seguente:



/home/GnaGna/Arduino/libraries



Dopo aver copiato la cartella, apriamo Arduino IDE e iniziamo a sviluppare

Il cirtuito

Come si può notare il circuito è molto semplice, tutto quello che entrami i circuiti hanno bisogno è di 5V, GND e un segnale DATI.

Il trasmettitore



#include <VirtualWire.h>
void setup()
{
    vw_set_ptt_inverted(true);  // Required by the RF module
    vw_setup(2000);            // bps connection speed
    vw_set_tx_pin(3);         // Arduino pin to connect the transmit data pin
}
 
void loop()
{
   //Message to send:
   const char *msg = "Hello World";
   vw_send((uint8_t *)msg, strlen(msg));

   vw_wait_tx();        // We wait to finish sending the message       
   digitalWrite(13,HIGH);
   delay(200);         
   digitalWrite(13,LOW);   
}


Il setup non è complesso, ha solo 3 istruzioni, vw_set_ptt_inverted indica se il pin Push-To-Talk (apertura del canale durante la trasmissione) deve essere basso (zero) o alto (5v), nel caso del trasmettitore wireless XD-FST apre al comunicazione quando il pin è basso (quindi invertito rispetto al default).
La seconda istruzione indica la velocità di comunicazione, ho visto che nei vari tutorial è settata a 2000, quindi da brava pecorella l'ho riportata senza nessuna modifica, non ho provato a modificare il valore per vedere il risultato, ma sicuramente è una cosa da provare. Se si varia questo valore bisogna solo avere la cortezza di modificare il valore anche nel ricevitore. Infine, vw_set_tx_pin setta il pin utilizzato per inviare il messaggio (nel mio caso il pin 3).
Il loop non è assolutamente più complesso, la prima riga setta il messaggio da inviare (il classico Hello World), la seconda riga invia il messaggio, mentre la terza attende il completamento dell'invio, questo è tutto, per avere un riscontro di invio messaggio potete accendere il led standard 13 su arduino con le istruzioni successive.

Il Ricevitore




// include the library code:
#include <VirtualWire.h>

void setup() {
  Serial.begin(9600); 
  vw_set_ptt_inverted(true);  // Required by the RF module
  vw_setup(2000);            // bps connection speed
  vw_set_rx_pin(7);         // Arduino pin to connect the receiver data pin
  vw_rx_start();           // Start the receiver
}

void loop() { 
  uint8_t buf[VW_MAX_MESSAGE_LEN];
  uint8_t buflen = VW_MAX_MESSAGE_LEN;
  if (vw_get_message(buf, &buflen)){      // We check if we have received data
  
    buf[buflen]='\0'; // Metto il terminatore di stringa
    String msg = (char *)buf;
    
    Serial.println(msg);
    Serial.println();
  }
}


la funzione di setup è autoesplicativa, la pirma riga setta la porta seriale (per avere un riscontro nel monitor dell'IDE di Arduino. Come nel caso del trasmettitore, la seconda e la terza riga settano rispettivamente la modalità del segnale di Push-To-Talk e la velocità di comunicazione (ricordarsi di utilizzare la stessa settata nel trasmettitore). Le riga successiva vw_set_rx_pin setta il pin di ricezione (nel mio caso il pin 7), mentre l'ultima riga vw_rx_start abilita il ricevitore.
La funzione di loop è un po' più complessa, la prima riga setta l'array dove verrà messo il contenuto del messaggio (VW_MAX_MESSAGE_LEN è una "costante" definita nella libreria VirtualWire, ha un valore di 30, questo significa che il singolo messaggio trasmesso avrà una lunghezza massima di 30byte), la seconda riga setta il valore della costante nella variabile buflen. Il controllo if successivo è utilizzato per controllare se è stato ricevuto un messaggio, se la funzione vw_get_message() ritorna true, mette nella variabile buf il messaggio e nella variabile buflen la lunghezza del messaggio ricevuto.
Prima nel controllo if è possibile aggiungere la funzione vw_wait_rx(); che rimane in attesa fino alla ricezione del messaggio, attenzione, questo metodo è bloccante, di conseguenza se avete esigenza di eseguire altre istruzioni durante l'attesa non è consigliato utilizzarlo, diversamente, se tutta la logica si basa sulla ricezione ("quando ricevo, allora faccio qualcosa") questo metodo potrebbe essere utile per non "sovracaricare" il controllore.
Una volta ricevuto il messaggio e la funzione vw_get_message ritorna il valore true, aggiungo inizialmente il carattere terminatore (in modo da poter utilizzare il messaggio come String), creo una variabile String con il messaggio, e stampo sul monitor di Arduino il messaggio ricevuto dal Trasmettitore

ATTENZIONE!

la libreria VirtualWire setta impropriamente dei pin di default per l'Rx, Tx, e PTT rispettivamente i pin 11, 12 e 10, di seguito le righe di codice incrimitate



// The digital IO pin number of the press to talk, enables the transmitter hardware
static uint8_t vw_ptt_pin = 10;
static uint8_t vw_ptt_inverted = 0;

// The digital IO pin number of the receiver data
static uint8_t vw_rx_pin = 11;

// The digital IO pin number of the transmitter data
static uint8_t vw_tx_pin = 12;


di conseguenza se create un circuito che utilizza VirtualWire utilizzando i pin sopra indicati per altri scopi (e non volete perdere ORE ed ORE come nel mio caso cercando di capire il perche il circuito da voi progettato non fa quello che si attende) ricordate di modificare nella funzione setup i pin Rx,Tx e PTT di VirtualWire, è sufficiente chiamare le seguenti funzioni di set.



void vw_set_rx_pin(uint8_t pin)
void vw_set_tx_pin(uint8_t pin)
void vw_set_ptt_pin(uint8_t pin)


lunedì 23 marzo 2015

Linee infinite con un LCD 16x2

LCD 16x2 è uno dei primi componenti degni di nota in quanto ci da modo di visualizzare senza troppa fatica del testo scritto, unica limitazione è il numero di caratti e linee che questo oggetto presenta: due linee con sedici caratteri ciascuna.
Sul mercato si possono trovare diverse varianti con più o meno righe, caratteri e soprattutto pià o meno euro. Possiamo dire che l'LCD 16x2 è un buon compromesso utilità/prezzo.

Come dicevo l'unica vera limitazione, forse viziati da questi megaschermi hd high resolution (mio padre probabilmente negli anni 80 non la pensava allo stesso modo), è il limitato numero di linee, SOLO 2!!!!!!!
Siccome 2 linee mi stavano strette ho pensato di creare un piccolo skatch che mi potesse dare un infinito numero di linee.

partiamo dai componenti:
- 1x LCD 16x2 (va benissimo qualsiasi lcd con standard Hitachi HD44780, io ho utilizzato l'LCD-00255 di sparkfun)
- 1x trimpot 10k
- 2x ButtonSwitch
- 2x Resistenze 10KΩ
- 1x Resistenza 220Ω
- 1x Arduino (ovviamente, io ho utilizzato Leonardo)
- un po di cavetteria

Per poter gestire in modo semplice il componente ho utilizzato la libreria di arduino LiquidCrystal, è molto semplice da usare, dopo aver fatto i primi due esempi chiunque è in grado di sviluppare qualcosa di più concreto (come nel mio caso).

Anche la progettazione del circuito è una piccola variante rispetto a quella che si trova nelle pagine di tutorial di LiquidCrystal, anzi se vogliamo essere sinceri, è quella di LiquidCrystal con due bottoni in più -_-'

Di seguito il circuito:

Come si può notare dall'immagine il circuto è uguale a quello presente sul sito di ardunino, l'unica differenza sono gli switch collegati ai pin 8 e 9 i quali verranno utilizzati per intercettare i bottoni e spostare le linee dell'LCD su e giu.

L'implementazione:
in tutto ho sviluppato 6 metodi

- begin(int lineNumber)
utilizzato per dichiarare e inizializzare il numero di linee che verranno visualizzate

- setLine(int lNumber, String txt)
prende in ingresso il numero di linea dove inserire il testo, e il testo

- showLine(int line)
prende in ingresso la linea da visualizzare sull'lcd, line è da considerasi il puntatore alla linea che verrà messa sulla riga 1

- showLine()
questo metodo non fa altro che richiamare il metodo showLine(int line) con la riga attuale, è utile per effettuare dei refresh nel caso si voglia modificare una linea e visualizzare il suo contentuo

- goUp()
questo metodo incrementa di uno il puntatore che indica le linee da visualizzare

- goDown()
questo metodo decrementa di uno il puntatore che indica le linee da visualizzare


Un po di codice:
innanzi tutto le variabili

//test vars
String txt1 = "this is 1st line";
String txt2 = "this is 2nd line";
String txt3 = "this is 3rd line";
String txt4 = "this is 4th line";

//PINS
int RS = 12;
int RW = 11;
int D4 = 5;
int D5 = 4;
int D6 = 3;
int D7 = 2;
int pinBtnUp = 9;
int pinBtnDown = 8;

//Internal variables
LiquidCrystal _lcd;
int _lineNumber;
String * _lines;
int _firstLineToShow;

txt1-4 sono le righe utilizzate per eseguire un sempice test (di 4 righe)
I pin da RS a D7 sono per comandare l'lcd
Mentre pinBtnUp e pinBtnDown sono rispettivamente per spostare la riga verso l'altro e verso il basso
_lcd è il puntatore alla libreria LiquidCrystal utilizzato per fare le operazioni sull'lcd
_lineNumber è il numero di linee che si stanno utilizzando (nel nostro caso 4)
_lines è l'array di testi che verrà visualizzato
_firstLineToShow è il "puntatore" al numero di linea che verrà visualizzato (sulla linea 1)


void begin(int lineNumber) {
    _lineNumber = lineNumber;

    _lines = new String[_lineNumber];
    for (int i = 0; i < _linenumber; i++) {
        _lines[i] = "";
    }
}
Per inizializzare la gestione delle linee ho creato un metodo chiamato begin il quale prende in ingresso il numero massimo di linee che si vogliono visualizzare, lo salva nella variabile _lineNumber, crea l'array e lo inizializza con stringhe vuote


void setLine(int lNumber, String txt) {
    if (lNumber >= 0 && lNumber < _lineNumber) {
        _lines[lNumber] = txt;
    }
}
Per settare le righe ho creato il metodo setLine. La variabile di ingresso lNumber dopo un piccolo check sull'overflow (deve essere compreso tra 0 e il numero massimo di linee settato nel metodo begin), viene utilizzato come indice dell'array _line dove viene settata la stringa txt.


void showLine(int line) {

    _firstLineToShow = line;

    String l1 = "";
    String l2 = "";
    _lcd.clear();


    if (_firstLineToShow >= 0 && _firstLineToShow < _lineNumber) {
        l1 = _lines[_firstLineToShow];
    }

    if ((_firstLineToShow + 1) >= 0 && (_firstLineToShow + 1) < _lineNumber) {
        l2 = _lines[(_firstLineToShow + 1)];
    }

    _lcd.setCursor(0, 0);
    _lcd.print(l1);

    _lcd.setCursor(0, 1);
    _lcd.print(l2);
}
showLine è il metodo principale di tutto il programma, è leggermento più complicato rispetto a tutti gli altri. In ingresso riceve il numero di linea dovrà essere posta come prima linea sull'lcd, la prima istruzione che viene eseguita è il set della variabile globale _firstLineToShow che terrà traccia dell'ultima linea da visualizzare richiesta; successivamente vengono create (e inizializzate) due variabili l1 e l2 che verranno successivamente settate con le righe richieste ed infine visualizzate.
_lcd.clear() serve per dare una pulita allo schermo. ATTENZIONE! la variabile _lcd prima di essere utilizzata deve essere istanziata, per maggior chiarezza sto presentado prima i metodi funzionali core del programma.
Per recuperare il testo della riga da visualizzare _lines[_firstLineToShow] faccio un controllo della variabile indice, che ovviamente deve essere maggiore di 0 e non deve superare il numero massimo di linee dichiarate precedentemente. Come accennato qualche riga sopra il valore di _lines[_firstLineToShow] viene messo nella variabile l1.
Lo stesso discorso vale per la seconda linea che ovviamente avrà come indice (_firstLineToShow + 1).
Se i controlli non soddisfano i requisiti l1 e l2 manterranno il valore di inizializzazione: stringa vuota.
Le ultime righe servono per stampare sull'lcd le righe calcolate, con il comando _lcd.setCursor(0, 0) sposto il cursore sulla prima colonna e prima riga (valore 0, 0) e con l'istruzione _lcd.print(l1) stampo il testo.
Le stesse operazione vengono fatte sulla secondo linea settando il cursore sulla prima colonna e seconda riga.


void goUp() {

    if (_firstLineToShow + 1 < _lineNumber) {
        _firstLineToShow++;
    }
}

void goDown() {

    if (_firstLineToShow > 0) {
        _firstLineToShow--;
    }
}
Ora del due funzioni goUp e goDown non fanno altro che incrementare o decrementare _firstLineToShow.

ed infine i metodi standard Arduino:

void setup() {
    _lcd = new LiquidCrystal(RS, RW, D4, D5, D6, D7);
    _lcd.begin(16, 2);
    _firstLineToShow = 0;

    pinMode(pinBtnUp, INPUT);
    pinMode(pinBtnDown, INPUT);

    begin(4);
    setLine(0, txt1);
    setLine(1, txt2);
    setLine(2, txt3);
    setLine(3, txt4);
    showLine();
}
Nel metodo setup viene istanziato LiquidCrysyal e inizializzato (16 il numero di catteri per linea e 2 il numero di linee), viene inizializzata _firstLineToShow a 0 in modo da incominciare a visualizzare la prima riga e vengono settati i pin (INPUT) per i bottoni. Le istruzioni successive servono per inizializzare la parte che gestirà il controller delle linee: si setta il numero di linee che si intende visualizzare, vengono settate le linee con stringhe (dalla posizione 0 alla 3) e successivamente si da un primo comando di visualizzazione linee (che visualizzerà la linea 1).


void loop() {
    if (digitalRead(pinBtnUp) == HIGH) {
        goDown();
        showLine();
        delay(300);
    }
    if (digitalRead(pinBtnDown) == HIGH) {
        goUp();
        showLine();
        delay(300);
    }
}
il metodo loop risulta molto semplice, se uno dei due bottoni viene premuto, si richiameranno le funzioni che spostano di una liena il puntatore delle linee e con showLine si effettua un refresh delle linee da visualizzare. Infine per evitare che il singolo push di un bottone venga percepito più di una volta, ho aggiunto un delay di 300 millisecondi.


Qui il codice completo dello skatch


#include "LiquidCrystal.h"

//test vars
String txt1 = "this is 1st line";
String txt2 = "this is 2nd line";
String txt3 = "this is 3rd line";
String txt4 = "this is 4th line";

//PINS
int RS = 12;
int RW = 11;
int D4 = 5;
int D5 = 4;
int D6 = 3;
int D7 = 2;
int pinBtnUp = 9;
int pinBtnDown = 8;

//Internal variables
LiquidCrystal _lcd;
int _lineNumber;
String * _lines;
int _firstLineToShow;

void setup() {
    _lcd = new LiquidCrystal(RS, RW, D4, D5, D6, D7);
    _lcd.begin(16, 2);
    _firstLineToShow = 0;

    pinMode(pinBtnUp, INPUT);
    pinMode(pinBtnDown, INPUT);

    begin(4);
    setLine(0, txt1);
    setLine(1, txt2);
    setLine(2, txt3);
    setLine(3, txt4);
    showLine();
}

void loop() {
    if (digitalRead(pinBtnUp) == HIGH) {
        goDown();
        showLine();
        delay(300);
    }
    if (digitalRead(pinBtnDown) == HIGH) {
        goUp();
        showLine();
        delay(300);
    }
}

// MULTILINE FUNCTIONS

void begin(int lineNumber) {
    _lineNumber = lineNumber;

    _lines = new String[_lineNumber];
    for (int i = 0; i < _linenumber; i++) {
        _lines[i] = "";
    }
}

void setLine(int lNumber, String txt) {
    if (lNumber < _lineNumber) {
        _lines[lNumber] = txt;
    }
}

void showLine() {
    showLine(_firstLineToShow);
}

void showLine(int line) {

    _firstLineToShow = line;

    String l1 = "";
    String l2 = "";
    _lcd.clear();


    if (_firstLineToShow >= 0 && _firstLineToShow < _lineNumber) {
        l1 = _lines[_firstLineToShow];
    }

    if ((_firstLineToShow + 1) >= 0 && (_firstLineToShow + 1) < _lineNumber) {
        l2 = _lines[(_firstLineToShow + 1)];
    }

    _lcd.setCursor(0, 0);
    _lcd.print(l1);

    _lcd.setCursor(0, 1);
    _lcd.print(l2);
}

void goUp() {

    if (_firstLineToShow + 1 < _lineNumber) {
        _firstLineToShow++;
    }
}

void goDown() {

    if (_firstLineToShow > 0) {
        _firstLineToShow--;
    }
}

       
 

Ecco il video: