LinkedIn YouTube Facebook
Szukaj

Wstecz
SoM / SBC

[ARDUINO] Bezprzewodowy czujnik temperatury z modułami nRF24L01

Moduły nRF24L01 to idealne rozwiązanie do utworzenia między mikrokontrolerami dwukierunkowej komunikacji radiowej na niewielkie odległości.

Moduły takie pracują na częstotliwości nośnej 2,4 GHz, wykorzystując modulację GFSK (modulacja FSK z filtracją gaussowską, znana chociażby z urządzeń DECT czy Bluetooth). Dzięki zintegrowanej antenie mikropaskowej, nie potrzeba żadnych zewnętrznych elementów do ich poprawnej pracy z wysoką sprawnością. Dodatkowo, moduł nRF24L01 pozwala na wybór jednej z czterech mocy nadawania, przy czym najwyższa możliwa moc jest na poziomie 0 dBm, co odpowiada wartości 1 mW.

Komunikacja modułu z mikrokontrolerem odbywa się za pośrednictwem magistrali SPI, sam moduł natomiast pozwala na wygenerowanie przerwania w momencie odebrania danych – dzięki czemu możliwe jest np. wybudzenie procesora ze stanu wstrzymania.

 

 

W projekcie użyto dwóch płytek Arduino UNO R3, pary modułów nRF24L01, a także czujnika temperatury DS18B20 i płytki rozszerzającej do Arduino – LCD Keypad Shield.

W dalszej części artykułu zestaw Arduino + sensor temperatury + moduł nRF nazywany będzie nadajnikiem, zaś odbiornikiem nazywane będzie połączenie Arduino z nakładką LCD Keypad Shield i drugim modułem radiowym.

 

 

Zadaniem nadajnika jest przesyłanie do odbiornika informacji o temperaturze, odczytywanej z układu DS18B20. Odbiornik zaś wyświetla odebrane dane na wyświetlaczu ciekłokrystalicznym oraz przekazuje je do komputera wraz z wartością instrukcji MILLIS(), której działanie zostanie opisane później.

Moduły radiowe dołączone zostały za pomocą przewodów połączeniowych do modułu Arduino UNO. Programy do obu mikrokontrolerów zostały napisane w Arduino, z użyciem dodatkowych bibliotek (umieszczone w załączniku do artykułu): MIRF (biblioteka układu nRF24L01), oneWire oraz DallasTemperature (obie ostatnie służą do obsługi układu DS18B20).

Ze względu na ograniczenia konstrukcyjne, zasilanie układu nRF24L01 nie może przekraczać 3,6 V, dlatego zostało podłączone do punktu na płytce Arduino, w którym wyprowadzono napięcie 3,3 V. Linie sygnałowe modułu radiowego mogą natomiast przyjąć napięcia do 5 V, więc nie jest wymagane użycie konwertera poziomów napięć. Ten sposób podłączenia zasilania dotyczy obu modułów nRF24L01.

Nadajnik składa się z Arduino, czujnika DS18B20, modułu nRF24L01 oraz diody świecącej.

Miganie LED sygnalizuje nadawanie pakietu, zawierającego wynik pomiaru temperatury – taki pomiar wykonywany jest co około 2 sekundy.

Do obsługi czujnika DS18B20 wykorzystano biblioteki DallasTemperature oraz OneWire. Nadawanie wykonywane jest przy wsparciu modułu nRF24L01 biblioteką MIRF, dzięki czemu program jest prosty i przejrzysty. Deklarację bibliotek oraz funkcję setup(), inicjującą i konfigurującą podstawowe funkcje układu, przedstawiono na listingu 1.

 

Listing 1. Deklaracje i funkcja setup() w kodzie nadajnika 

#include 
#include 
#include 
#include 
#include 
#include 

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);


void setup()
{ 
  pinMode(10, OUTPUT);               // linia 10 jako wyjście dla LED
  digitalWrite(10, LOW);             // dioda nie świeci
   
  sensors.begin();                   // inicjacja czujnika
  
  Mirf.spi = &MirfHardwareSpi;       // konfiguracja SPI do użycia z biblioteką MIRF
  Mirf.init();                       // inicjacja MIRF
  Mirf.setRADDR((byte *)"67890");    // Ustawienie adresu do odbioru
  Mirf.setTADDR((byte *)"12345");    // Ustawienie adresu do nadawania
  Mirf.payload = sizeof(float);      // Maksymalny przesył przez moduły radiowe ustawiony na wielkość zmiennej FLOAT (4 bajty)
  Mirf.config();                     // Konfiguracja biblioteki MIRF
 }


Warto zwrócić uwagę na instrukcje „Mirf.setTADDR()” oraz „Mirf.setRADDR()” – służą one do ustalania adresu, pod który będzie nadawana informacja lub z którego będzie ona odbierana. Adres taki może mieć od 3 do 5 bajtów (co można ustawić w rejestrze AW modułu nRF24L01).

Za pomocą instrukcji Mirf.payload ustawiono wielkość przesyłanych danych – odpowiada ona wielkości zmiennej float (4 bajty). Maksymalnie można przekazać 32 bajty naraz.

Warto zaznaczyć, że w tym projekcie mamy jeden nadajnik i jeden odbiornik, jednakże moduły radiowe przystosowane są do pracy dwukierunkowej (każdy moduł może nadawać lub odbierać).

W pętli „void loop()” wykonywany jest pomiar temperatury, zapis wyniku do zmiennej oraz wysłanie jej przez SPI do modułu nRF24L01. W dużej części wykorzystano tu biblioteki – dzięki czemu np. odczyt temperatury czy nadanie zmiennej sprowadza się do wykonania jednej instrukcji.

Każda z instrukcji ma określony czas, przez który jest wykonywana – w przypadku odwołania do biblioteki, żądając pomiaru temperatury, wprowadzane jest opóźnienie rzędu 700 mS, które pozwala czujnikowi na odpowiednie przygotowanie zebranych danych do przekazania. Stąd, przybliżony czas wykonania pętli „void loop()” wynosi ponad 2 s. Treść pętli głównej przedstawiono na listingu 2.

 

Listing 2. Pętla główna void loop (kod nadajnika)

void loop()
{
  float tempr = 0;                       // Zmienna typu float
  
  sensors.requestTemperatures();       // Żądanie pomiaru temperatury za pomocą czujnika DS18B20 (czas wykonania < 1s)
    
  tempr = sensors.getTempCByIndex(0);   // Przypisanie wyniku pomiaru do zmiennej FLOAT 
  
  Mirf.send((byte *)&tempr);            // Nadanie zmiennej przez moduł nRF24L01
  
  while(Mirf.isSending()){             // Zapętlenie programu do czasu zakończenia wysyłania danych
  }
    
  digitalWrite(10,HIGH);                // Mignięcie diody, sygnalizujące zakończenie nadawania
  delay(300);
  digitalWrite(10,LOW);
  
  
  delay(1000);                         // Opóźnienie powtórnego wykonania pętli - 
                                       // Suma opóźnień w całym programie wynosi około 2s - jest to także okres pomiaru.
  
}


Odbiornik składa się z Arduino UNO R3, płytki z wyświetlaczem oraz modułu radiowego. Sposób podłączenia modułu nRF24L01 rożni się od zalecanego połączenia – spowodowane to jest użyciem płytki LCD Keypad Shield, której złącza sygnałowe zajęły sygnały CSN oraz CE. Dlatego też w funkcji setup() znajdują się przypisania: Mirf.csnPin = 2; Mirf.cePin = 3; Przewody SPI zostały podłączone do złącza programowania ISP, gdyż jest to jedyny punkt na płytce LCD Keypad Shield, gdzie znajdują się wyprowadzenia linii takich, jak MOSI, MISO, SCK. W funkcji setup() konfigurowana jest także transmisja szeregowa (prędkość 9600 b/s, 8 bitów danych, brak bitów parzystości, 1 bit stopu), inicjowany jest wyświetlacz HD44780 oraz konfigurowana jest linia sterująca jego podświetleniem. Po włączeniu zasilania podświetlenie wyświetlacza powinno mignąć.

 

Listing 3. Kod programu odbiornika: deklaracje i funkcja setup()

#include 
#include 
#include 
#include 
#include 

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);   // Konfiguracja linii wyświetlacza (zgodnie ze schematem Arduino LCD Shield)
   
void setup(){
  
  Serial.begin(9600);   // Inicjacja transmisji szeregowej o prędkości 9600 b/s
  lcd.begin(16, 2);     // Inicjacja wyświetlacza HD44780 o organizacji 16x2
  lcd.clear();          // Czyszczenie całego wyświetlacza
      
   pinMode(10,OUTPUT);           //wyjście sterujące podświetleniem LCD
   digitalWrite(10,LOW);         //wygaś podświetlenie LCD
   delay(100);                   //opóźnienie 0,1 s 
   digitalWrite(10,HIGH);        //włącz  podświetlenie LCD
    
  Mirf.csnPin = 2;                 // Konfiguracja pinu CSN biblioteki MIRF inaczej, niż domyślnie
  Mirf.cePin = 3;                  // Konfiguracja pinu CE biblioteki MIRF inaczej, niż domyślnie
  Mirf.spi = &MirfHardwareSpi;     // Wykorzystanie SPI do komunikacji pryz użyciu modułów nRF24L01
  Mirf.init();                     // inicjacja modułu MIRF
  Mirf.setTADDR((byte *)"67890");  // ustawienie adresu do nadawania
  Mirf.setRADDR((byte *)"12345");  // Ustawienie adresu do odbioru
  Mirf.payload = sizeof(float);    // Maksymalny przesył przez moduły radiowe ustawiony na wielkośc zmiennej FLOAT (4 bajty)
  Mirf.config();                   // Konfiguracja biblioteki MIRF
  
} 

W pętli głównej programu odbierana jest zmienna zawierająca informację przesłaną z nadajnika. Zostaje ona wyświetlona na LCD, a także przesłana przez UART do komputera, w towarzystwie wartości instrukcji MILLIS(), która liczy czas (w mS) od momentu dołączenia zasilania do płytki ARDUINO. Dzięki tej informacji, można łatwo analizować zmiany temperatur w czasie pracy układu (uwaga – po dłuższym czasie wartość instrukcji MILLIS() przepełni się, zaczynając liczenie od początku). Na ekranie LCD, poza temperaturą, wyświetlania jest także gwiazdka „*” w momencie odebrania z toru radiowego informacji wysłanej przez wcześniej opisany moduł nadajnika. Jest to sygnał dla użytkownika, że transmisja radiowa jest aktywna, a przedstawiana temperatura – aktualna. Miganie gwiazdki na wyświetlaczu odbiornika powinno być zsynchronizowane z miganiem LED w nadajniku. Listing 4 przedstawia pętlę „void loop()”.

 

Listing 4. Pętla główna void loop() w programie odbiornika

void loop(){
 float data;    //zmienna data typu FLOAT
  
  if(Mirf.dataReady())  // Warunek wykonywany tylko, gdy coś zostanie odebrane 
  
  {
    lcd.setCursor(15,0);  //Kursor na ostatniej pozycji pierwszej linii
    lcd.write("*");        // Wyświetlenie gwiazdki symbolizującej odebranie danych
    delay(300);
    lcd.setCursor(15,0);
    lcd.write(" ");        // Nadpisanie gwiazdki pustym polem
    Mirf.getData((byte *) &data);  // Odbiór zmiennej data przy pomocy modułu nRF24L01
   
    lcd.setCursor(0,0);      // Kursor na początku
    lcd.write("Temp: "); 
    
    lcd.setCursor(6,0);
    lcd.print(data);     // Wyświetlenie zmiennej FLOAT (temperatura podawana w postaci XX.XX lub XXX.XX,a także ze znakiem "-")
    lcd.write(0xDF);      // Znak stopnia
    lcd.write("C  ");    
     
  Serial.print(data);      // Wystawienie zmiennej na UART
  Serial.print(" ; ");      // Rozdzielenie
  Serial.println(millis());   // Wystawienie wartości instrukcji MILLIS(), zwięszanej automatycznie od czasu uruchomienia płytki Arduino
     
  }
}


Termometr może mierzyć temperatury z zakresu -55 do +125 stopni Celsiusza.

Sposób podłączenia modułów do Arduino opisano w komentarzach na początku programów.

Należy zwrócić uwagę na kolejność uruchamiania urządzeń – zaleca się uruchomienie jako pierwszego odbiornika.

Producent szacuje zasięg radiowy modułów na około 100m w otwartej przestrzeni.

Pełny kod (nadajnik):

/*
 * Konfiguracja linii dołączonych do modułu nRF24L01:
 *
 * MISO: 12
 * MOSI: 11
 * SCK: 13
 * CE: 8
 * CSN: 7
 *
 * Konfiguracja linii LED:
 * Katoda: GND
 * Anoda: 10 przez R = 1kOhm
 *
 *Linia czujnika DS18B20: 2 UWAGA - linia 1-Wire musi być podciągnięta do zasilania (+5V) rezystorem o wartości 4,7k?

 */
 
#include 
#include 
#include 
#include 
#include 
#include 

#define ONE_WIRE_BUS 2

OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);


void setup()
{
  pinMode(10, OUTPUT);               // linia 10 jako wyjście dla LED
  digitalWrite(10, LOW);             // dioda nie świeci
  
  sensors.begin();                   // Inicjalizacja czujnika
 
  Mirf.spi = &MirfHardwareSpi;       // konfiguracja SPI do użycia z biblioteką MIRF
  Mirf.init();                       // Inicjalizacja MIRF
  Mirf.setRADDR((byte *)"67890");    // Ustawienie adresu do odbioru
  Mirf.setTADDR((byte *)"12345");    // ustawienie adresu do nadawania
  Mirf.payload = sizeof(float);      // Maksymalny przesył przez moduły radiowe ustawiony na wielkośc zmiennej FLOAT (4 bajty)
  Mirf.config();                     // Konfiguracja biblioteki MIRF
 }




void loop()
{
  float tempr = 0;                       // Zmienna typu float
 
  sensors.requestTemperatures();       // Żądanie pomiaru temperatury za pomocą czujnika DS18B20 (czas wykonania < 1s)
   
  tempr = sensors.getTempCByIndex(0);   // Przypisanie wyniku pomiaru do zmiennej FLOAT
 
  Mirf.send((byte *)&tempr);            // Nadanie zmiennej przez moduł nRF24L01
 
  while(Mirf.isSending()){             // Zapętlenie programu do czasu zakończenia wysyłania danych
  }
   
  digitalWrite(10,HIGH);                // Mignięcie diody, sygnalizujące zakończenie nadawania
  delay(300);
  digitalWrite(10,LOW);
 
 
  delay(1000);                         // Opóźnienie powtórnego wykonania pętli -
                                       // Suma opóźnień w całym programie wynosi około 2s - jest to także okres pomiaru.
 
}
         

Pełny kod (odbiornik):

/*
 * Konfiguracja linii dołączonych do modułu nRF24L01:
 *
 * MISO: 12
 * MOSI: 11
 * SCK: 13
 * CE: 3
 * CSN: 2
 * Konfiguracja linii dołączonych do wyświetlacza HD44780:
 *
 * EN: 9
 * RS: 8
 * D4: 4
 * D5: 5
 * D6: 6
 * D7: 7
 * PODŚW.: 10
 
 */

#include 
#include 
#include 
#include 
#include 

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);   // Konfiguracja linii wyświetlacza (zgodnie ze schematem Arduino LCD Shield)
  
void setup(){
 
  Serial.begin(9600);   // Inicjalizacja transmisji szeregowej o prędkości 9600 b/s
  lcd.begin(16, 2);     // Inicjalizacja wyświetlacza HD44780 o organizacji 16x2
  lcd.clear();          // Czyszczenie całego wyświetlacza
     
   pinMode(10,OUTPUT);           //wyjście sterujące podświetleniem LCD
   digitalWrite(10,LOW);         //wygaś podświetlenie LCD
   delay(100);                   //opóźnienie 0,1 s
   digitalWrite(10,HIGH);        //włącz  podświetlenie LCD
   
  Mirf.csnPin = 2;                 // Konfiguracja pinu CSN biblioteki MIRF inaczej, niż domyślnie
  Mirf.cePin = 3;                  // Konfiguracja pinu CE biblioteki MIRF inaczej, niż domyślnie
  Mirf.spi = &MirfHardwareSpi;     // Wykorzystanie SPI do komunikacji pryz użyciu modułów nRF24L01
  Mirf.init();                     // Inicjalizacja modułu MIRF
  Mirf.setTADDR((byte *)"67890");  // ustawienie adresu do nadawania
  Mirf.setRADDR((byte *)"12345");  // Ustawienie adresu do odbioru
  Mirf.payload = sizeof(float);    // Maksymalny przesył przez moduły radiowe ustawiony na wielkośc zmiennej FLOAT (4 bajty)
  Mirf.config();                   // Konfiguracja biblioteki MIRF
 
}


void loop(){
 float data;    //zmienna data typu FLOAT
 
  if(Mirf.dataReady())  // Warunek wykonywany tylko, gdy coś zostanie odebrane
 
  {
    lcd.setCursor(15,0);  //Kursor na ostatniej pozycji pierwszej linii
    lcd.write("*");        // Wyświetlenie gwiazdki symbolizującej odebranie danych
    delay(300);
    lcd.setCursor(15,0);
    lcd.write(" ");        // Nadpisanie gwiazdki pustym polem
    Mirf.getData((byte *) &data);  // Odbiór zmiennej data przy pomocy modułu nRF24L01
  
    lcd.setCursor(0,0);      // Kursor na początku
    lcd.write("Temp: ");
   
    lcd.setCursor(6,0);
    lcd.print(data);     // Wyświetlenie zmiennej FLOAT (temperatura podawana w postaci XX.XX lub XXX.XX,a także ze znakiem "-")
    lcd.write(0xDF);      // Znak stopnia
    lcd.write("C  ");   
   
   

   
    Serial.print(data);      // Wystawienie zmiennej na UART
    Serial.print(" ; ");      // Rozdzielenie
    Serial.println(millis());   // Wystawienie wartości instrukcji MILLIS(), zwięszanej automatycznie od czasu uruchomienia płytki Arduino
    
  }
}
Inżynier elektronik, absolwent Wydziału Elektroniki i Technik Informacyjnych Politechniki Warszawskiej. Swoje życie zawodowe i hobby związał z elektroniką i mechatroniką. Specjalizuje się w projektowaniu i utrzymywaniu systemów testowania produkcyjnego w firmie SoMLabs, jest autorem artykułów poświęconych elektronice, programowaniu i systemom wbudowanym (embedded).