Il progetto: BAsic Remote Sensor (BARS)
Il progetto e' di realizzare un sensore di temperatura in ambiente domestico che trasmette via radio i dati ad una stazione centrale; il sensore trasmettitore deve costare meno di 10 euro, compresa batteria. La stazione ricevente deve ricevere dati da più sensori e renderli fruibili facilmente. Il consumo deve essere ridotto al minimo per consentire alle batterie di durare il più a lungo possibile.
Ho deciso di utilizzare:
- una coppia di moduli radio nRF24L01+ per trasmettere e per ricevere
- il chip ATtiny84 come microcontroller del sensore remoto
- il chip TMP36 come sensore di temperatura (va bene anche il TMP37)
- RaspberryPi come stazione ricevente
NB il TMP36 ha un range di temperatura tra -40 e +125 °C mentre il TMP37 va da +5 a +85; e' più preciso, ma non provate a metterlo in congelatore perche' non andrà sotto il suo minimo.
ATtiny84
Il microcontroller ATtiny84 della ATmel è un chip semplice da programmare grazie anche alla compatibilità con l'IDE di Arduino (vedi la guida di programmazione).
Il modulo radio nRF24L01+
E' un modulo radio molto economico (con un po' di fortuna si trova sotto i 3 dollari a coppia), opera a 2,4 GHz ed esistono le librerie per utilizzarlo con Arduino, ATtiny84 e RaspberryPi.
Attenzione: il modulo va alimentato al massimo a 3.6V; in caso di alimentazione superiore c'e' il rischio di bruciarlo; conviene usare un regolatore di tensione.
Il cuore del trasmettitore e' ovviamente costituito dal microcontroller ATtiny84.
Un pin in output alimenta il sensore di temperatura TMP36, mentre un pin in input analogico legge il valore di temperatura in uscita dal sensore.
Ho inserito un regolatore di tensione per evitare di bruciare il modulo radio che non sopporta tensioni di alimentazione oltre i 3,6V; infatti anche una batteria ricaricabile da 3,6V completamente carica può facilmente arrivare a 4,2V
Grazie alla presenza del regolatore di tensione, e' possibile alimentare tutto il circuito anche con un alimentatore esterno a 5 V.
Per diminuire il consumo di corrente, ho utilizzato un regolatore di tensione MIC5205-3.3YM5 smd dotato di un pin EN ("chip ENable"); con il pin a livello basso, il regolatore si comporta come un interruttore ed il circuito non consuma praticamente niente; quando attivato tramite un pin del microcontroller, il regolatore eroga i 3,3 v necessari ad alimentare il dispositivo nRF24L01+. Il condensatore C3 (470pF) e' necessario per l'abbattimento del rumore nel regolatore.
I condensatori C5 e C6 sono elettrolitici da 10 uF.
Ho aggiunto un led con resistenza (R3 da 220 ohm) per verificare il fluire del processo di lettura dei valori e trasmissione dei dati.
Il ponticello J1 e' usato come interruttore di alimentazione.
Riporto il collegamento tra microcontroller ed il modulo nRF24l01:
nRF24L
|
ATtiny84
|
||
1
|
GND
|
14
|
GND
|
2
|
VCC
|
1
|
VCC
|
3
|
CE
|
6
|
PA7
|
4
|
CSN
|
10
|
PA3
|
5
|
SCK
|
9
|
USCK
|
6
|
MOSI
|
8
|
MISO
|
7
|
MISO
|
7
|
MOSI
|
ATTENZIONE! Il piedino 8 (IRQ) del nRF24l01 non e' usato; tuttavia, nello schema (e nel pcb), ho collegato anche quel piedino del connettore al PB3 (reset) del ATtiny84 in modo da poter programmare il microcontroller senza toglierlo dallo zoccolo, collegando semplicemente un cavetto tra arduino e lo zoccolo del modulo nRF24l01. L'unica accortezza, e' quella di piegare il pin del modulo radio prima di inserirlo in modo che non venga collegato.
Come dicevo, la programmazione del microcontroller può essere effettuata senza toglierlo dallo zoccolo; è sufficiente togliere il nRF dallo zoccolo ed inserire al suo posto il connettore del cavetto verso arduino:
Le specifiche del cavetto sono:
BARS (zoccolo nRF)
|
Arduino UNO
|
|
5
|
SCK
|
13
|
6
|
MOSI
|
12
|
7
|
MISO
|
11
|
8
|
IRQ
|
10
|
BARS Alimentazione
|
Arduino UNO
|
|
1
|
+
|
+5V
|
2
|
-
|
GND
|
Un connettore a tre pin consente di collegare eventuali altri sensori direttamente ai pin PA0 e PA1 del microcontroller.
Il sistema è alimentato da una batteria da 3,6 v
Qui il progetto in formato Fritzing; di seguito l'immagine del circuito:
Per ottenere le schede già stampate e forate, ho usato il servizio di SeeedStudio, uplodando il file gerber zippato realizzato con Fritzing; 10 schede al prezzo di 9,99 $ più 3,30 di spedizione (3-4 settimane).
Elenco componenti:
Qtà
|
Componente
|
Prezzo
|
Vendor
|
Cod. Vendor
|
1
|
Microcontroller ATTINY84A-PU |
1,80
|
RS Components | 738-0684 |
1
|
Zoccolo 14 pin |
0,08
|
RS Components | 674-2438 |
1
|
Modulo radio nRF24L01+ |
1,25
|
Bobowaytoway | ND |
1
|
Zoccolo 8 pin (*) |
0,07
|
RS Components | 674-2435 |
1
|
Led |
0,13
|
RS Components | |
1
|
resistenza 220 ohm |
0,02
|
RS Components | |
1
|
regolatore LDO MIC5205-3.3YM5 |
0,55
|
RS Components | 453-467 |
1
|
Condensatore 470 pF |
0,16
|
RS Components | 699-4936 |
2
|
Condensatore elettrolitico 10 uF |
0,24
|
RS Components | 715-2511 |
1
|
PCB |
1,20
|
SeeedStudio | |
1
|
Batteria ricaricabile 3,6V |
1,57
|
SunSky | |
Totale
|
7,07
|
(*) lo zoccolo 8 pin e' usato per il modulo radio; va tagliato a metà e saldato con le due metà affiancate al contrario
Ecco il sensore assemblato:
ll software per ATtiny84
Ho utilizzato l'ambiente MacOS per la realizzazione del progetto.
Per compilare il software su Tiny e’ necessario innanzitutto correggere un bug di ArduinoIDE; seguire il link:
https://github.com/TCWORLD/ATTinyCore/tree/master/PCREL%20Patch%20for%20GCC cliccare su MAC ld.zip, poi view raw; questo fa il download del file.
unzippare il file e, avendo cura di salvare il vecchio, mettere il file ld al posto del precedente nella directory:
/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/avr/bin
/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/avr/bin
Installare le librerie RF24 da:
Come sopra, installare le librerie Narcoleptic; servirà per mandare il microcontroller in standby consumando circa 3 uA tra una trasmissione e la successiva.
scompattare il file RF24-master.zip, rinominare la directory da RF24-master in RF24; copiare la directory nella Documents/Arduino/libraries
E' possibile anche utilizzare la funzione propria di import dal menù Sketch -> Importa libreria -> Add library.
E' possibile anche utilizzare la funzione propria di import dal menù Sketch -> Importa libreria -> Add library.
Come sopra, installare le librerie Narcoleptic; servirà per mandare il microcontroller in standby consumando circa 3 uA tra una trasmissione e la successiva.
Installare questo codice sul ATtiny84:
// nRF24L01+ su ATTiny84
// code for data TX vers. 6a
// hardware connections:
// nrf24L01: 1 2 3 4 5 6 7
// attiny84: 14 1 6 10 9 8 7
#include <RF24.h>
#include <Narcoleptic.h>
// #include "printf.h" //DEBUG
RF24 radio(PA3,PA7); // attiny84: pins for nrf24l01 CSN,CE
#define tempPin A2 // TMP36 Vout connected to A2 (ATtiny pin 11)
#define radioPower 0 // EN Voltage Regulator pin is connected on pin PB0 - D0 (ATtiny pin 2)
#define ledPin 1 // Led pin is connected on pin D1 (ATtiny pin 3)
#define tempPower 2 // TMP36 Power pin is connected on pin PB2 - D2 (ATtiny pin 5)
// define user variables
int data_type = 1; // 4 digit (0-9) data type = 1 for temp sensor
char * serial = "0002"; // 4 hex digit (0-F) sensor serial number
int period = 300; // # seconds period between trasmissions < 32767 (9,13 hours)
// define software variables
int count = 0;
int tempReading = 0;
void setup(void)
{
// Serial.begin(9600); //DEBUG
// printf_begin(); //DEBUG
analogReference(INTERNAL); // set the aref to the internal 1.1V reference
pinMode(tempPower, OUTPUT); // set power pin for TMP36 to output
pinMode(ledPin, OUTPUT); // set power pin for LED to output
pinMode(radioPower, OUTPUT); // set power pin for EN Voltage regulator
period = period * 0.9; // 10% Narcoleptic library correction
}
void loop(void)
{
bitClear(PRR, PRADC); // power up the ADC
ADCSRA |= bit(ADEN); // enable the ADC
digitalWrite(tempPower, HIGH); // turn TMP36 sensor on
digitalWrite(ledPin, HIGH); // turn LED on
delay(20); // Allow 20ms for the sensor to be ready
analogRead(tempPin); // first junk read
for(int i = 0; i < 10 ; i++) // take 10 more readings
{
tempReading += analogRead(tempPin); // accumulate readings
}
tempReading = tempReading / 10 ; // calculate the average
digitalWrite(ledPin, LOW); // turn LED off
digitalWrite(tempPower, LOW); // turn TMP36 sensor off
long vcc=readVcc();
ADCSRA &= ~ bit(ADEN); // disable the ADC
bitSet(PRR, PRADC); // power down the ADC
// Calibration
double voltage = tempReading * 0.942382812;
// Temp calculation for TMP36
// double temperatureC = (voltage - 500) / 10;
// Temp calculation for TMP37
double temperatureC = voltage / 20;
// Final temp calculation
int temptx = temperatureC * 100;
// Preparing Payload (32 bytes is maximum)
char outBuffer[32]= "";
sprintf(outBuffer,"%04d:%s:%04d:%04d:%04d:",data_type,serial,count,temptx,vcc);
// Serial.println(outBuffer); //DEBUG
// turn Voltage Regulator ON
digitalWrite(radioPower, HIGH);
delay(5);
// init radio for writing on channel 76
radio.begin();
radio.setPALevel(RF24_PA_MAX);
radio.setChannel(0x4c);
radio.setDataRate(RF24_250KBPS);
radio.openWritingPipe(0xF0F0F0F0E1LL);
radio.enableDynamicPayloads();
radio.powerUp();
// radio.printDetails(); //DEBUG
// Transmit and go down.
delay(5);
radio.write(outBuffer, strlen(outBuffer));
radio.powerDown();
digitalWrite(radioPower, LOW); // turn Voltage Regulator OFF
// pause between trasmissions
int timerSeconds = period % 30;
int timerHalfminutes = ((period - timerSeconds)/30);
if (timerHalfminutes > 0)
{
for(int i = 0; i < timerHalfminutes ; i++)
{
Narcoleptic.delay(30000); // 30 sec. sleeping is max for Narcoleptic
}
}
if (timerSeconds > 0)
{
Narcoleptic.delay(timerSeconds*1000); // delay the rest
}
// increase counter
count ++;
if (count == 10000) {
count = 0;
}
}
long readVcc() {
long result;
// Read 1.1V reference against Vcc
ADMUX = _BV(MUX5) | _BV(MUX0);
delay(2); // Wait for Vref to settle
ADCSRA |= _BV(ADSC); // Convert
while (bit_is_set(ADCSRA,ADSC));
result = ADCL;
result |= ADCH<<8;
result = 1126400L / result; // Back-calculate Vcc in mV
return result;
}
Ho considerato i 32 byte come massima lunghezza del messaggio e ci ho messo:
- un data_type per identificare eventuali diversi tipi di sensore
- un serial number da 4 caratteri che può essere diverso da sensore a sensore, in modo che il ricevitore possa distinguere le varie sorgenti
- un contatore che incrementa di 1 ogni invio e si azzera a 10.000
- la temperatura con il segno (+ o -) davanti, senza virgola (le librerie del ATtiny non consentono di gestire i float, quindi il numero e’ in centesimi di grado (2040 = 20,40 gradi cent.)
- il valore delle batterie di alimentazione del sensore, anche qui in millivolt (3362 = 3,362 V)
I campi sono separati da ":"
Il ricevitore
Il ricevitore è un semplice RaspberryPi a cui e' collegato il modulo nRF24l01 sul connettore del GPIO. La libreria per la gestione del modulo e poche righe di codice C++ sono sufficienti a realizzare il ricevitore.
Il modulo radio sul ricevitore e' quello in versione con antenna esterna per massimizzare la portata della ricezione (non potendo più di tanto agire sul trasmettitore).
Ho saldato il modulo direttamente sul Raspberry (la carcassa del connettore ethernet direttamente sul bnc dorato del nRF) per poi inserire tutto facilmente in un box:
Collegamenti elettrici su Raspberry:
nRF24L
|
RaspberryPi
|
||
1
|
GND
|
6
|
GND
|
2
|
VCC
|
1
|
VCC +3V
|
3
|
CE
|
22
|
GPIO25
|
4
|
CSN
|
24
|
GPIO08/CE0
|
5
|
SCK
|
23
|
GPIO11/SCLK
|
6
|
MOSI
|
19
|
GPIO10/MOSI
|
7
|
MISO
|
21
|
GPIO09/MISO
|
Il software per Raspberry:
questo programma "nRF24l01_receive_no_DB.cpp" legge i dati che arrivano dai sensori e li mostra su standard output; la versione per inserire i dati su DB e mostrarli via webapp e' qui.
Abilitare SPI sul kernel:
Abilitare SPI sul kernel:
# raspi-config
nel menu’ “Advanced” abilitare SPI kernel module
Creare una directory per contenere la libreria RF24:
mkdir ~/rf24libs
cd ~/rf24libs
Clonare il repository RF24
git clone https://github.com/tmrh20/RF24.git RF24
Entrare nella nuova directory RF24
cd RF24
Compilare la libreria
sudo make install
cd examples_RPi
Preparare il file ”nRF24l01_receive_no_DB.cpp” e modificare il Makefile inserendo nei PROGRAMS solo il file “nRF24l01_receive_no_DB”
vi nRF24l01_receive_no_DB.cpp [copia e incolla il sorgente]
vi Makefile [inserisci nei PROGRAMS solo "nRF24l01_receive_no_DB"]
Compilare ed eseguire il binario
make
vi Makefile [inserisci nei PROGRAMS solo "nRF24l01_receive_no_DB"]
Compilare ed eseguire il binario
make
sudo ./nRF24l01_receive_no_DB
Accendere il sensore remoto, si dovrebbe vedere lo scorrere dei dati, alla cadenza programmata sui trasmettitori:
0001:00A2:0000:1809:3128:
0001:00A2:0001:1993:3137:
0001:00A2:0002:2011:3137:
0001:00A2:0003:2011:3137:
0001:00A2:0004:2011:3137:
0001:00A2:0005:2035:3137:
0001:00A2:0006:2068:3137:
I campi sono rispettivamente: data_type (= 0001), serial number del dispositivo (00A2), contatore, temperatura in centesimi di grado (2068 = 20,68 °C), tensione della batteria in millivolt (3137 = 3,137 V)
// nRF24l01_receive_no_DB.cpp
// nRF24L01+ su RaspberryPi
// example code for data RX on standard output
// connect:
// nrf24L01: 1 2 3 4 5 6 7
// RaspberryPi: 6 1 22 24 23 19 21
// compile with:
// g++ -Ofast -mfpu=vfp -mfloat-abi=hard -march=armv6zk -mtune=arm1176jzf-s -Wall -I../ -lrf24-bcm nRF24l01_receive.cpp -o nRF24l01_receive
#include <cstdlib>
#include <iostream>
#include <RF24/RF24.h>
using namespace std;
RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ);
void setup(void)
{
// init radio for reading
radio.begin();
radio.enableDynamicPayloads();
radio.setAutoAck(1);
radio.setRetries(15,15);
radio.setDataRate(RF24_250KBPS);
radio.setPALevel(RF24_PA_MAX);
radio.setChannel(76);
radio.setCRCLength(RF24_CRC_16);
radio.openReadingPipe(1,0xF0F0F0F0E1LL);
radio.startListening();
}
void loop(void)
{
// 32 byte character array is max payload
char receivePayload[32]="";
while (radio.available())
{
// read from radio until payload size is reached
uint8_t len = radio.getDynamicPayloadSize();
radio.read(receivePayload, len);
// display payload
cout << receivePayload << endl;
}
}
int main(int argc, char** argv)
{
setup();
while(1)
loop();
return 0;
}
TODO:
Gestire il pin 8 IRQ del modulo radio per evitare di doverlo piegare (dovrebbe bastare predisporre il pin 4 (PB3) del ATtiny come entrata non gestita)
Caricabatterie direttamente dalla scheda, con connettore microUSB
Creare un modulo ripetitore (HW e SW) con maggiore potenza in uscita alimentato dalla rete elettrica.
TODO:
Gestire il pin 8 IRQ del modulo radio per evitare di doverlo piegare (dovrebbe bastare predisporre il pin 4 (PB3) del ATtiny come entrata non gestita)
Caricabatterie direttamente dalla scheda, con connettore microUSB
Creare un modulo ripetitore (HW e SW) con maggiore potenza in uscita alimentato dalla rete elettrica.





Articolo e progetto molto valido e bello .Ache distanza riesci ad arrivare ? Anche io ho fatto un progetto simile ma non riesco ad arrivare ad una distanza superiore a 10mt.
RispondiEliminaGrazie e ancora bravo
Matteo
Grazie a te.
EliminaSi, è vero non andiamo molto lontano dai 10 metri; piuttosto è interessante che riesco a trasmettere dall'interno del frigorifero o del congelatore.
Ciao,
RispondiEliminabell'articolo.
Non mi sono chiare due cose:
1) Nel datasheet del MIC5205-3.3YM5 vedo un condensatore da 2.2 tra massa e 5, perchè non c'è nel tuo shema?
2) Vedo invede 2 condensatori C5 e C6...a cosa servono?
Ciao grazie
C6 e' il condensatore sul piedino 5 del regolatore di tensione. Il piedino 5 è l'uscita a 3.3 v che alimenta il modulo radio e che deve essere comunque stabilizzata con almeno 10uF; i 2.2uF indicati dal datasheet sono soddisfatti dai 10uF necessari al nRF24l01.
EliminaC5 ha la stessa funzione sulla linea di tensione in ingresso, necessario per alimentare in modo corretto il microcontroller specie a tensioni vicine ai 3V (ne ha meno bisogno se alimenti a 5V).