در این آموزش در مورد ماژول فرستنده و گیرنده بی سیم nRF24L01 و راه اندازی آن با آردوینو یاد خواهید گرفت. در اینجا قصد داریم با استفاده از دو عدد از این ماژولها، یک ارتباط بی سیم بین دو آردوینو برقرار کنیم.
مقدمه
برای اینکه بتوانید درک بهتر ی از این ارتباط بی سیم داشته باشید، دو مثال را در نظر خواهیم گرفت:
در مثال اول، پیغام سادهی “Hello World” را از یک آردوینو به آردوینو دیگر ارسال خواهیم نمود و در مثال دوم، یک ارتباط دو طرفه بین دو برد آردوینو خواهیم داشت. بهطوری که با استفاده از یک جوی استیک در آردوینو اول، سروو موتوری را در آردوینو دوم کنترل خواهیم کرد، و برعکس، با استفاده از یک کلید در آردوینو دوم، یک LED را در آردوینو اول کنترل خواهیم کرد.
نحوه کار ماژول nRF24L01
بیایید نگاه دقیقتری به ماژول فرستنده گیرنده nRF24L01 داشته باشیم. این ماژول از باند فرکانسی 2.4 گیگاهرتز استفاده میکند و میتواند با نرخ تبادل دادهی (Baud Rate) 250 کیلو بیت در ثانیه تا 2 مگا بیت در ثانیه کار کند. چنانچه از این ماژول در فضای باز و با نرخ تبادل دادهی کمتر استفاده شود، برد آن حداکثر به 100 متر خواهد رسید.
این ماژول میتواند از 125 کانال متفاوت استفاده کند که امکان ایجاد شبکهای از 125 مودم مستقل از هم در یک مکان را فراهم میسازد. هر کانال میتواند حداکثر 6 آدرس داشته باشد، یا هر ماژول میتواند همزمان با 6 ماژول دیگر ارتباط برقرار کند.
جریان مصرفی این ماژول هنگام انتقال داده حدود 12 میلیآمپر است که حتی از یک LED نیز کمتر میباشد. ولتاژ کاری این ماژول از 1.9 تا 3.6 ولت متغیر است، اما سایر پایهها قدرت تحمل منطق 5 ولت را دارند. بنابراین، میتوان این ماژول را بدون نیاز به مبدل ولتاژ به یک آردوینو متصل نمود.
سه تا از این پایهها برای ارتباط SPI به کار میروند و لازم است به پایههای SPI آردوینو متصل شوند، اما توجه داشته باشید هر برد آردوینو پایههای SPI متفاوتی دارد. پایههای CSN و CE میتوانند به هر یک از پایههای دیجیتال برد آردوینو متصل شوند که برای تنظیم ماژول در مد استندبای یا اکتیو و نیز برای سوئیچ کردن بین دو مد ارسال و فرمان استفاده میشوند. آخرین پایه (IRQ)، پایهی وقفه است که مورد استفاده قرار نمیگیرد.
انواع مختلفی از ماژولهای nRF24L01 وجود دارد، محبوبترین آن مدلی است که دارای یک آنتن برروی برد خود میباشد. با وجود اینکه آنتن باعث کوچک شدن ابعاد ماژول میشود، اما از سوی دیگر، برد انتقال را به فاصلهی حدود 100 متر کاهش میدهد.
دومین مدل، بهجای آنتنِ روی برد، یک کانکتور SMA دارد که برای داشتن برد انتقال بهتر، میتوانیم یک آنتن duck به آن متصل کنیم.
سومین مدل که در اینجا نشان داده شدهاست، علاوه بر آنتن duck ، یک تراشهی RFX2401C دارد که شامل PA (تقویتکنندهی توان) و LNA (تقویتکنندهی سیگنالهای ضعیف) میباشد. این تراشه، سیگنال NRF24L01 را تقویت میکند و حتی برد انتقال بهتری تا 1000 متر را در فضای باز امکانپذیر میسازد.
پایههای ماژول NRF24L01
در اینجا نگاهی دقیق به پایههای ماژول NRF24L01 و NRF24L01+ PA/LNA خواهیم داشت.
هر دو ماژول NRF24L01 و NRF24L01+ PA/LNA پایههای مشابهی دارند، بنابراین، هر دو اتصالات یکسانی در مدار ما خواهند داشت.
اتصال ماژول فرستنده گیرنده رادیویی NRF24L01 به آردوینو
در اینجا نحوهی اتصال ماژول NRF24L01 به آردوینو و راه اندازی آن نشان داده شده است.
همانطور که قبلاً اشاره شد، هر برد آردوینو پایههای SPI متفاوتی دارد، بنابراین، هنگام اتصال ماژولها به آردوینو خود، به این نکته توجه داشته باشید.
قطعات مورد نیاز برای این آموزش:
- ماژول NRF24L01
- برد آردوینو
- بردبورد و سیم جامپر
كد آردوينو و NRF24L01
پس از اینکه ماژولهای NRF24L01 را به بردهای آردوینو متصل کردیم، نوبت به نوشتن کدهای گیرنده و فرستنده میرسد. ابتدا لازم است کتابخانهی RF24 را دانلود و نصب کنید.
در اینجا دو کد برای ارتباط بیسیم قرار داده شده است.
كد فرستنده
/*
* Arduino Wireless Communication Tutorial
* Example 1 - Transmitter Code
*
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(7, 8); // CE, CSN
const byte address[6] = "00001";
void setup() {
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MIN);
radio.stopListening();
}
void loop() {
const char text[] = "Hello World";
radio.write(&text, sizeof(text));
delay(1000);
}
كد گیرنده
/*
* Arduino Wireless Communication Tutorial
* Example 1 - Receiver Code
*
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
RF24 radio(7, 8); // CE, CSN
const byte address[6] = "00001";
void setup() {
Serial.begin(9600);
radio.begin();
radio.openReadingPipe(0, address);
radio.setPALevel(RF24_PA_MIN);
radio.startListening();
}
void loop() {
if (radio.available()) {
char text[32] = "";
radio.read(&text, sizeof(text));
Serial.println(text);
}
}
توضیح کد
در ابتدا به کتابخانه SPI و کتابخانههای RF24 که بهتازگی نصبشده، نیاز داریم و پس از آن لازم است شیء RF24 را ایجاد کنیم. پارامترهای اين شیء ، پایههای CE و CSN میباشند.
RF24 radio(7, 8); // CE, CSN
در قدم بعدی باید یک آرایهی یک بایتی ایجاد کنیم که نشان دهندهی آدرس خواهد بود، یا به اصطلاح کانالی است که از طریق آن دو ماژول با هم ارتباط برقرار میکنند.
const byte address[6] = "00001";
مقدار اين آدرس را میتوانیم به هر رشته 5 حرفی تغيير دهیم، بهطوری که امکان انتخاب گیرندهی موردنظر فراهم شود. بنابراین، در این مثال، آدرس یکسانی برای فرستنده و گیرنده خواهیم داشت.
در بخش setup ابتدا شیء radio با ()radio.begin مقداردهی میشود و با استفاده از تابع ()radio.openWritingPipe آدرس گیرندهای را که قصد داریم به آن داده ارسال کنیم، تعیین مینماییم که همان رشته 5 حرفی است که قبلاً ایجاد کردیم.
radio.openWritingPipe(address);
از سوی دیگر، در گیرنده، با استفاده از تابع ()radio.setReadingPipe آدرس یکسانی را تنظیم میکنیم و به این صورت ارتباط بین دو ماژول را فراهم میسازیم.
radio.openReadingPipe(0, address);
در مرحله بعدی با استفاده از تابع ()radio.setPALevel سطح تقویتکنندهی توان را تنظیم میکنیم. در این آموزش، سطح را روی حداقل تنظیم میکنیم، زیرا ماژولها بسیار نزدیک به هم قرار دارند.
radio.setPALevel(RF24_PA_MIN);
توجه داشته باشید در صورتیکه قصد دارید از سطح بالاتری استفاده کنید، پیشنهاد میشود خازنهای بایپس (bypass) بین پایههای تغذیهی ماژولها (GND و 3.3 ولت) قرار داده شود، بنابراین، ماژولها در حین کار ولتاژ پایدارتری خواهند داشت.
سپس با استفاده از تابع ()radio.stopListening، ماژول را به عنوان فرستنده و از سوی دیگر با استفاده از تابع ()radio.startListening، ماژول را به عنوان گیرنده تعیین میکنیم.
// at the Transmitter
radio.stopListening();
// at the Receiver
radio.startListening();
در قسمت loop ، در فرستنده، آرايهای از كاراكترها برای پيغام “Hello World” ايجاد میكنيم. با استفاده از تابع ()radio.write این پيغام را برای گيرنده ارسال میکنیم. آرگومان اول اين تابع متغیری است که میخواهیم ارسال شود.
void loop() {
const char text[] = "Hello World";
radio.write(&text, sizeof(text));
delay(1000);
}
با استفاده از عملگر “&” قبل از نام متغیر، به آدرس متغیری که دیتای ارسالی را ذخیره میکند، دسترسی خواهیم داشت. با استفاده از دومین آرگومان تعداد بایتهای این متغیر را تعیین میکنیم. در اینجا تابع ()sizeof تمام بایتهای رشته “text” را دریافت میکند. در انتهای برنامه نیز 1 ثانیه تأخیر اضافه خواهیم کرد.
از طرف دیگر، در گیرنده، با استفاده از تابع ()radio.available در قسمت loop بررسی میکنیم که دادهای برای دریافت وجود دارد یا خیر. در صورتیکه دیتایی دریافت شود، آرایهی 32 رشتهای “text” را ایجاد و دادههای دریافتی را در آن ذخیره میکنیم.
void loop() {
if (radio.available()) {
char text[32] = "";
radio.read(&text, sizeof(text));
Serial.println(text);
}
}
برای خواندن دادهها از تابع ()radion.read استفاده و دادههای دریافتی در آرایه “text” ذخیره میشود. در پایان کافی است پیغام text روی سریال مانیتور چاپ شود. بنابراین، هنگامیکه هر دو برنامه را آپلود میکنیم، میتوانیم سریال مانیتور را در گیرنده اجرا کنیم و مشاهده خواهیم کرد که هر 1 ثانیه پیغام “Hello World” چاپ میشود.
عیبیابی
یکی از رایجترین مشکلاتی است که افراد در برقراری ارتباط با ماژولهای NRF24L01 با آن مواجه هستند، نویز منبع تغذیه میباشد. به طور کلی، مدارات RF یا سیگنالهای فرکانس رادیویی به نویز منبع تغذیه حساس هستند. برای حل این مشکل میتوان یک خازن جداکننده (decoupling) سر راه منبع تغذیه قرار داد و ظرفیت خازن را میتوان از 10 تا 100 میکروفاراد انتخاب نمود.
مشکل دیگری که معمولاً با آن مواجه میشوید، پایهی 3.3 ولت بردهای آردوینو است که اغلب اوقات نمیتواند توان کافی را برای ماژول فرستنده و گیرنده بی سیم NRF24L01 فراهم سازد. بنابراین، با استفاده از یک منبع تغذیهی خارجی میتوان توان لازم برای ماژول را تأمین نمود.
ارتباط دو طرفهی بی سیم آردوینو با دو ماژول NRF24L01
مثال دومی که در نظر داریم، برقراری یک ارتباط بیسیم دو طرفه بین دو آردوینو میباشد. در تصویر زیر اتصالات نشان شدهاند:
قطعات مورد نیاز برای این مثال به شرح زیر است:
- ماژول فرستنده – گیرنده NRF24L01
- برد آردوینو
- ماژول جوی استیک
- سروو موتور
- کلید
- LED
کد nRF24L01
در ادامه کدهای فرستنده و گیرنده و سپس، توضیح آنها را خواهید دید.
کد فرستنده
/*
* Arduino Wireless Communication Tutorial
* Example 2 - Transmitter Code
*
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define led 12
RF24 radio(7, 8); // CE, CSN
const byte addresses[][6] = {"00001", "00002"};
boolean buttonState = 0;
void setup() {
pinMode(12, OUTPUT);
radio.begin();
radio.openWritingPipe(addresses[1]); // 00002
radio.openReadingPipe(1, addresses[0]); // 00001
radio.setPALevel(RF24_PA_MIN);
}
void loop() {
delay(5);
radio.stopListening();
int potValue = analogRead(A0);
int angleValue = map(potValue, 0, 1023, 0, 180);
radio.write(&angleValue, sizeof(angleValue));
delay(5);
radio.startListening();
while (!radio.available());
radio.read(&buttonState, sizeof(buttonState));
if (buttonState == HIGH) {
digitalWrite(led, HIGH);
}
else {
digitalWrite(led, LOW);
}
}
کد گیرنده
/*
* Arduino Wireless Communication Tutorial
* Example 2 - Receiver Code
*
* by Dejan Nedelkovski, www.HowToMechatronics.com
*
* Library: TMRh20/RF24, https://github.com/tmrh20/RF24/
*/
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>
#define button 4
RF24 radio(7, 8); // CE, CSN
const byte addresses[][6] = {"00001", "00002"};
Servo myServo;
boolean buttonState = 0;
void setup() {
pinMode(button, INPUT);
myServo.attach(5);
radio.begin();
radio.openWritingPipe(addresses[0]); // 00001
radio.openReadingPipe(1, addresses[1]); // 00002
radio.setPALevel(RF24_PA_MIN);
}
void loop() {
delay(5);
radio.startListening();
if ( radio.available()) {
while (radio.available()) {
int angleV = 0;
radio.read(&angleV, sizeof(angleV));
myServo.write(angleV);
}
delay(5);
radio.stopListening();
buttonState = digitalRead(button);
radio.write(&buttonState, sizeof(buttonState));
}
}
تفاوت این مثال با مثال قبلی این است که در اینجا باید دو کانال یا آدرس برای ارتباط دو طرفه ایجاد کنیم.
const byte addresses[][6] = {"00001", "00002"};
در قسمت setup ، لازم است هر دو کانال را تعریف کنیم و توجه داشته باشید که آدرس نوشتن در آردوینو اول باید معادل آدرس خواندن در آردوینو دوم باشد، و برعکس، آدرس خواندن در آردوینو اول باید با آدرس نوشتن در آردوینو دوم یکسان باشد.
// at the Transmitter
radio.openWritingPipe(addresses[1]); // 00001
radio.openReadingPipe(1, addresses[0]); // 00002
// at the Receiver
radio.openWritingPipe(addresses[0]); // 00002
radio.openReadingPipe(1, addresses[1]); // 00001
در قسمت loop، با استفاده از تابع ()radio.stopListening، آردوینو اول را به عنوان فرستنده تعیین میکنیم، سپس مقدار جوی استیک را خوانده و آن را به بازهی 180-0 نگاشت میکنیم و با استفاده از تابع ()radio.write دادهها را به گیرنده میفرستیم.
radio.stopListening();
int potValue = analogRead(A0);
int angleValue = map(potValue, 0, 1023, 0, 180);
radio.write(&angleValue, sizeof(angleValue));
از سوی دیگر، با استفاده از تابع ()radio.startListening، آردوینو دوم را به عنوان گیرنده تعیین میکنیم و وجود یا عدم وجود داده را بررسی خواهیم کرد. در صورتیکه دادهای موجود باشد، آن را خوانده و در متغیر “angleV” ذخیره خواهیم کرد. سپس، از آن برای چرخش سروو موتور استفاده میکنیم.
radio.startListening();
if ( radio.available()) {
while (radio.available()) {
int angleV = 0;
radio.read(&angleV, sizeof(angleV));
myServo.write(angleV);
}
در مرحله بعد، در فرستنده، آردوینو اول را به عنوان گیرنده تعیین کرده و با یک حلقهی “while” خالی منتظر ارسال داده از سوی آردوینو دوم خواهیم ماند. این داده، وضعیت کلید یعنی فشرده شدن یا عدم فشرده شدن آن را نشان میدهد. چنانچه کلید فشرده شده باشد، LED روشن خواهد شد. این فرآیندها دائماً تکرار میشود و هر دو آردوینو دائماً مشغول ارسال و دریافت داده هستند.
امیدواریم از این آموزش لذت برده و مطالب جدیدی یاد گرفته باشید. مثل همیشه اگر سوالی داشتید، با ما در میان بگذارید.
نیاز به تغیر داشت دومین کد ولی در کل عالی تشکر از سایت خوبتون
سلام دوست عزیز،
خواهش میکنم،
میشه لطفا به ما بگید کجای کد نیاز به تغییر داشت؟