ارتباط بی سیم آردوینو و رزبری پای توسط ماژول رادیویی NRf24l01

الکترونیک و رباتیک -> ماژول ها و سنسور ها 1880 5 کاربر آکادمی پارتینه

طراحان سیستم‌های الکترونیکی از پروتکل‌های ارتباطی بی‌سیم مختلفی مانند بلوتوث، زیگبی (Zigbee)، ESP8266 ، ماژول Wi-Fi ، ماژول‌هایRF ، LoRa و غیره استفاده می‌کنند که انتخاب پروتکل ارتباطی بستگی به نوع کاربرد مورد نظر از جانب کاربر دارد. در میان تمامی این سیستم‌های ارتباطی، یک شبکه ارتباطی بی‌سیم محبوب ، شبکه محلی nRF24L01 است. این ماژول‌ها در پهنای باند 2.4 گیگاهرتز باند  ISM با سرعت انتقال‌‌داده از 250Kbps تا 2Mbps که مقداری استاندارد در بسیاری از کشورها بوده و قابل استفاده در کاربردهای صنعتی و پزشکی است، عمل می‌کنند. همچنین ادعا می‌شود که در صورت استفاده از آنتن مناسب، این ماژول ها قابلیت ارتباط تا فاصله‌ی حداکثر 100 متر را دارند.

در آموزش‌ قبلی نحوه‌ی کنترل سروو موتور از طریق ماژول nRF24L01 مورد بررسی قرار گرفت. در این آموزش از این ماژول برای برقراری ارتباط بی سیم بین رزبری پای و آردوینو استفاده خواهد شد.  به این صورت که رزبری پای به عنوان فرستنده و آردوینو به عنوان گیرنده عمل کرده و پیام ارسال شده توسط رزبری بر روی نمایشگر کاراکتری 1602 نشان داده‌ می‌شود. آموزش به دو بخش تقسیم می‌شود. در بخش اول ارتباط nRF24L01 با Arduino به عنوان گیرنده بررسی شده و در بخش دوم ارتباط رزبری پای با nRF24L01 عنوان فرستنده مورد بررسی قرار خواهد گرفت.

معرفی ماژول فرستنده گیرنده رادیویی NRF24L01

ماژول‌های NRF24L01 ماژول‌های فرستنده گیرنده (Transceiver) هستند که می توانند داده‌ها را بطور همزمان ارسال یا دریافت کنند. پروتکل ارتباطی این ماژول با میکروکنترلر SPI است.

ولتاژ کاری این ماژول از 1.9 ولت تا 3.6 ولت است (به طور معمول 3.3 ولت). سایر پایه‌­های ارتباطی ماژول قابلیت تحمل 5 ولت را داشته و مستقیما به میکرو کنترلرهای 5 ولتی متصل می‌شوند. جریان مصرفی این ماژول در حالت آماده کار نزدیک به صفر بوده و در حالت دریافت سیگنال با بیشترین سرعت انتقال داده (Mbps2) حدود 13 میلی‌آمپر جریان می‌کشد. این میزان جریان مصرفی بسیار کم، امکان استفاده از این ماژول در پروژه‌های قابل‌حمل که انرژی را از طریق باتری تامین می‌کنند، فراهم می‌کند. مزیت دیگر استفاده از این ماژول ها این است که هر ماژول NRF می‌تواند به 6 ماژول دیگر متصل شده و یک شبکه با توپولوژی ستاره یا مش تشکیل دهد.

مواد اولیه :
# عنوان تعداد لینک
0 بورد آردوینو 1 لینک خرید
1 بورد رزبری پای ( Raspberry Pi ) 1 لینک خرید
2 نمایشگر LCD کاراکتری ۱۶۰۲ 1 لینک خرید
3 ماژول NRF24L01 2 لینک خرید
4 سیم جامپر 1 لینک خرید

مرحله 1 : اتصال ماژول nRF24L01 به آردوینو

 اتصال nRF24L01 به آردوینو کاملا آسان بوده و نیاز به المان خاصی نیست. ارتباط  nRF24l01 توسط پروتکل SPI و نمایشگر کاراکتری 1602 توسط پروتکل I2C صورت می‌گیرد.

 

مرحله 2 : اتصال ماژول nRF24L01 به رزبری پای

اتصال nRF24L01 به رزبری پای نیز بسیار ساده بوده و از طریق پروتکل SPI انجام می‌شود.

مرحله 3 : پروگرام کردن رزبری پای برای ارسال پیام توسط ماژول nRF24l01

در این پروژه برنامه‌نویسی رزبری پای با استفاده از Python3 انجام می‌شود. در حال‌حاضر یک کتابخانه برای ماژول nRF24l01 در پایتون موجود است که می‌توانید از صفحه github دانلود کنید. توجه داشته باشید کتابخانه و اسکرپت پایتون باید در یک پوشه یکسان قرار داشته باشند تا پایتون قادر به یافتن کتابخانه باشد. پس از نصب كتابخانه می‌توانید کد نویسی را آغاز کنید. در مرحله اول برای دسترسی به پورت‌های GPIO رزبری پای و دسترسی به توابع مربوط به زمان کتابخانه‌های زیر فراخوانی می‌شوند.

import RPi.GPIO as GPIO 
import time     
import spidev
from lib_nrf24 import NRF24 

ذر این مرحله حالت‌های GPIO توسط کانال "Broadcom SOC" یعنی با شماره BCM مشخص می‌شود.

GPIO.setmode(GPIO.BCM) 

در گام بعدی آدرسی را به منظور برقراری ارتباط با گیرنده آردوینو تعریف کرده که این آدرس به صورت هگزا دسیمال تعریف می‌شود.

pipes = [[0xE0, 0xE0, 0xF1, 0xF1, 0xE0], [0xF1, 0xF1, 0xF0, 0xF0, 0xE0]]

برای ماژول رادیویی از GPIO08 به عنوان CE و GPIO25 به عنوان پین CSN استفاده می‌شود.

radio.begin(0, 25)  

دیتا به صورت 32 بیتی ، آدرس ماژول 76 ، سرعت انتقال داده 1 مگابیت در ثانیه و میزان توان ماژول حداقل تعریف می‌شود.

radio.setPayloadSize(32)  
radio.setChannel(0x76) 
radio.setDataRate(NRF24.BR_1MBPS)    
radio.setPALevel(NRF24.PA_MIN)

Pip را باز کرده تا خواندن و نوشتن دیتا بر روی ماژول انجام شود.

radio.openWritingPipe(pipes[0])     
radio.printDetails()

پیام زیر به صورت string برای نمایش به آردوینو ارسال می‌شود.

sendMessage = list("Hi..Arduino UNO")  
while len(sendMessage) < 32:    
    sendMessage.append(0)

اطلاعات دریافت شده همراه با زمان دریافت اطلاعات را در ماژول ثبت شده و پیام Sent the message نمایش داده می‌شود.

while True:
    start = time.time()      
    radio.write(sendMessage)   
    print("Sent the message: {}".format(sendMessage))  
send
    radio.startListening()     


بعد از اتمام ارسال پیام در صورتیکه ماژول در دسترس نباشد یا پیام به صورت کامل دریافت نشود ، پیغام زیر روی نمایشگر نشان داده می‌شود.

## while not radio.available(0):
        time.sleep(1/100)
        if time.time() - start > 2:
print("Timed out.")  # print error message if radio disconnected or not functioning anymore
           ## break

در چنین حالتی ارتباط با ماژول را به مدت 3 ثانیه متوقف شده و بعد از آن مجددا ارتباط برقرار می‌شود..

  radio.stopListening()     # close radio
    time.sleep(3)  # give delay of 3 seconds

در این مرحله دریافت پیام از ماژول متوقف شده و ارتباط قطع می‌شود. بعد از 3 ثانیه ارتباط مجدداً برقرار شده تا پیام بعدی ارسال شود.

مرحله 4 : اجرای برنامه بر روی رزبری پای

گام اول:  برنامه پایتون و کتابخانه‌ها را در یک پوشه قرار دهید.

گام دوم: برای مثال نام فایل برنامه قرستنده nrfsend.py  است.

گام سوم:  وارد Command Terminal رزبری پای شده و محل ذخیره فایل برنامه را با فرمان “cd” مشخص کنید.

گام چهارم: سپس فایل برنامه را باز کرده و دستور “sudo python3 your_program.py” اجرا کنید. با این کار قادر خواهید بود جزئیات و مشخصات ماژول nRf24 را ببینید.  هر 3 ثانیه یکبار ارسال پیام آغاز شده و بعد از ارسال کاراکترها پیام دریافت نشان داده می‌شود.

 

مرحله 5 : پروگرام کردن آردوینو برای ارسال پیام توسط ماژول nRF24l01

روند پروگرام کردن آردوینو  UNO نیز شبیه رزبری پای است. کتابخانه nRF24l01 برای آردوینو را از صفحه github دانلود کرده تا از توابع این کتابخانه برای کار با این ماژول استفاده شود. ارتباط با نمایشگر 1602 از طریق رابط I2C صورت می‌گیرد. بنابراین کتابخانه‌ی  Wire.h را نیز فراخوانی می‌کنیم. همچنین ماژول  nRF24l01 از طریق پروتکل  SPI نیز ارتباط برقرار می‌کند. برای دسترسی به توابع RF24 و نمایشگر از دو کتابخانه RF24 و LiquidCrystal_I2C استفاده کنید.

#include<SPI.h>                   
#include <Wire.h>
#include<RF24.h>                  
#include <LiquidCrystal_I2C.h>


آدرس نمایشگر 27 بوده و ابعاد آن 2x16 است. تابع زیر با توجه به این موارد تکمیل می‌شود.

LiquidCrystal_I2C lcd(0x27, 16, 2);

در این قسمت پین‌های اتصال پایه‌های ماژول (CE وCS) مشخص می‌شوند. مطابق با اتصالات به پایه‌های 9 و 10 متصل شده‌اند.

RF24 myRadio (9,10);

در این حرحله ارتباط آغاز شده و سطح توان ماژول مشخص می‌شود.  همچنین آدرسpipe مطابق با آدرس رزبری پای تنظیم شده و pipe برای خواندن اطلاعات باز می‌شود.

radio.begin();        
  radio.setPALevel(RF24_PA_MAX) ;   
  radio.setChannel(0x76) ;            
  const uint64_t pipe = 0xE0E0F1F1E0LL ;    
  radio.openReadingPipe(1, pipe) ;

در این مرحله ارتباط I2C را برقرار شده و نمایشگر راه‌اندازی اولیه می‌شود.

Wire.begin();                 
  lcd.begin();                    
  lcd.home();                       
  lcd.print("Ready to Receive");

حال ماژول منتظر دریافت پیام مانده ، در صورت دریافت ذخیره و نمایش داده می‌شود. سپس دریافت پیام به مدت 10 میکرو ثانیه متوقف شده و مجددا آغاز می‌شود.

کد کامل برنامه:

NRF Transmitter Side Code (Raspberry Pi):

import RPi.GPIO as GPIO  # import gpio
import time      #import time library
import spidev
from lib_nrf24 import NRF24   #import NRF24 library

GPIO.setmode(GPIO.BCM)       # set the gpio mode

  # set the pipe address. this address shoeld be entered on the receiver alo
pipes = [[0xE0, 0xE0, 0xF1, 0xF1, 0xE0], [0xF1, 0xF1, 0xF0, 0xF0, 0xE0]]
radio = NRF24(GPIO, spidev.SpiDev())   # use the gpio pins
radio.begin(0, 25)   # start the radio and set the ce,csn pin ce= GPIO08, csn= GPIO25
radio.setPayloadSize(32)  #set the payload size as 32 bytes
radio.setChannel(0x76) # set the channel as 76 hex
radio.setDataRate(NRF24.BR_1MBPS)    # set radio data rate
radio.setPALevel(NRF24.PA_MIN)  # set PA level

radio.setAutoAck(True)       # set acknowledgement as true 
radio.enableDynamicPayloads()
radio.enableAckPayload()

radio.openWritingPipe(pipes[0])     # open the defined pipe for writing
radio.printDetails()      # print basic detals of radio

sendMessage = list("Hi..Arduino UNO")  #the message to be sent
while len(sendMessage) < 32:    
    sendMessage.append(0)

while True:
    start = time.time()      #start the time for checking delivery time
    radio.write(sendMessage)   # just write the message to radio
    print("Sent the message: {}".format(sendMessage))  # print a message after succesfull send
    radio.startListening()        # Start listening the radio
    
    while not radio.available(0):
        time.sleep(1/100)
        if time.time() - start > 2:
            print("Timed out.")  # print errror message if radio disconnected or not functioning anymore
            break

    radio.stopListening()     # close radio
    time.sleep(3)  # give delay of 3 seconds

# >

 

NRF Receiver Side Code (Arduino):

#include<SPI.h>                   // spi library for connecting nrf
#include <Wire.h>                             // i2c libary fro 16x2 lcd display
#include<RF24.h>                  // nrf library
#include <LiquidCrystal_I2C.h>     // 16x2 lcd display library

LiquidCrystal_I2C lcd(0x27, 16, 2);         // i2c address is 0x27

RF24 radio(9, 10) ;  // ce, csn pins    
void setup(void) {
  while (!Serial) ;
  Serial.begin(9600) ;     // start serial monitor baud rate
  Serial.println("Starting.. Setting Up.. Radio on..") ; // debug message
  radio.begin();        // start radio at ce csn pin 9 and 10
  radio.setPALevel(RF24_PA_MAX) ;   // set power level
  radio.setChannel(0x76) ;            // set chanel at 76
  const uint64_t pipe = 0xE0E0F1F1E0LL ;    // pipe address same as sender i.e. raspberry pi
  radio.openReadingPipe(1, pipe) ;        // start reading pipe 
  radio.enableDynamicPayloads() ;
  radio.powerUp() ;          
  Wire.begin();                 //start i2c address
  lcd.begin();                    // start lcd 
  lcd.home();                       
  lcd.print("Ready to Receive");  // print starting message on lcd 
  delay(2000);
  lcd.clear();
}

void loop(void) {

  radio.startListening() ;        // start listening forever
  char receivedMessage[32] = {0} ;   // set incmng message for 32 bytes
  if (radio.available()) {       // check if message is coming
    radio.read(receivedMessage, sizeof(receivedMessage));    // read the message and save
    Serial.println(receivedMessage) ;    // print message on serial monitor 
    Serial.println("Turning off the radio.") ;   // print message on serial monitor
    radio.stopListening() ;   // stop listening radio
    String stringMessage(receivedMessage) ;     // change char to string
    lcd.clear();    // clear screen for new message
    delay(1000);    // delay of 1 second 
    lcd.print(stringMessage);   // print received mesage
  }
  delay(10);
}