В эпоху интернета вещей (Internet of Things, IoT) беспроводная связь с каждым годом становится все более популярной. В наши дни электронные устройства могут "разговаривать" друг с другом с помощью какого либо облачного сервиса или локальной сети. Для задач локальной (на небольшие расстояния) беспроводной связи наибольшее распространение получили различные радиочастотные модули (RF Module), например, Bluetooth Low Energy, Zigbee, ESP Wi-Fi модули, радиочастотные модули 433MHz, Lora, nRF и т.д.
В данном проекте мы будем использовать модуль nRF24L01, который представляет собой микросхему радиочастотного приемопередатчика от компании Nordic Semiconductor. Данный модуль работает в диапазоне 2.4GHz ISM, который на западе разрешен для безлицензионного использования в науке, промышленности и медицине, и поддерживает скорости передачи от 250 кБит/с до 2 Мбит/с. Данные скорости передачи являются разрешенными во многих странах.
На чипе nRF24L01 выпускается достаточно много различных типов модулей, как со встроенными антеннами с дальностью связи до 100 м, так и с внешними антеннами с дальностью связи до 1000 м. В данном проекте мы с помощью данных радиомодулей будем осуществлять связь между платой Arduino и микроконтроллером PIC18F622K.
Ранее на нашем сайте мы рассматривали и другие проекты с использованием радиочастотных модулей nRF24L01:
- передача данных на смартфон с помощью Arduino, модуля NRF24L01 и Bluetooth;
- радиостанции большого радиуса действия на Arduino и модулях nRF24L01;
- пульт дистанционного управления дроном на Arduino;
- радиосвязь между Raspberry Pi и Arduino Uno с помощью модулей nRF24L01.
Принципы работы радиочастотного модуля nRF24L01
nRF24L01 представляет собой расположенный на одном чипе радиочастотный приемопередающий модуль с низким энергопотреблением. Поскольку модуль приемопередающий, то его можно сконфигурировать для работы как в режиме передачи, так и в режиме приема. Но возможна работа только в режиме полудуплекса, по этой причине в один момент времени мы можем либо передавать, либо принимать данные. Распиновка модуля nRF24L01 представлена на следующем рисунке.
Модуль отличается очень низким энергопотреблением, поэтому он хорошо подходит для проектов интернета вещей. С другими устройствами модуль взаимодействует по интерфейсу SPI, поэтому его легко подключить к любому современному микроконтроллеру. Модуль поддерживает достаточно высокие передачи данных (для модулей этого типа) от 250 кБит/с до 2 Мбит/с и дальность связи от 100 до 1000 метров (в зависимости от используемой антенны).
nRF24L01 – это очень дешевый модуль, способный работать в диапазоне 2.4GHz ISM, использующий GFSK модуляцию (гауссова модуляция с минимальным сдвигом). Модуль поставляется в двух вариантах: дешевый – без внешней антенны и усилителя и более дорогой – с внешней антенной и усилителем (PA+LNA), который усиливает сигнал модуля до уровня 3dBm.
Модули nRF24L01 могут запитываться от напряжения от 1.9V и 3.6V и потребляют в режиме функционирования ток всего 13.5mA, а в режиме глубоко сна потребляемый ими ток составляет 26uA. Интересен и тот факт, что контакты SPI данного модуля толерантны (терпимо относятся) к напряжению 5V, поэтому их можно непосредственно подключать к контактам платы Arduino без всякого преобразования уровней.
Необходимые компоненты
- Микроконтроллер PIC18F46K22 (купить на AliExpress).
- Плата Arduino Uno (купить на AliExpress) или Arduino Nano (купить на AliExpress).
- Радиочастотный модуль nRF24L01 – 2 шт. (купить на AliExpress).
- Программатор PICkit 3 (купить на AliExpress).
- Кварцевый генератор 20 МГц (купить на AliExpress).
- Конденсаторы 33 пФ (2 шт.) (купить на AliExpress).
- Резисторы 4,7 кОм, 100 Ом (купить на AliExpress).
- Развязывающие конденсаторы 10 и 0,1 мкФ для модулей nRF24L01 – 2 шт.
- Светодиод (купить на AliExpress).
- Кнопка.
- Макетная плата.
- Соединительные провода.
- Адаптер 12V.
- MPLAB-X IDE и компилятор xc8.
- Arduino IDE.
Реклама: ООО "АЛИБАБА.КОМ (РУ)" ИНН: 7703380158
Схема проекта
Схема подключения радиочастотного модуля nRF24L01 к микроконтроллеру PIC18F46K22 представлена на следующем рисунке.
Примечание: модули nRF24L01 достаточно сильно чувствительны к шумам по питанию, поэтому для снижения уровня этих шумов вместе с данными модулями целесообразно использовать развязывающие конденсаторы. Можно использовать конденсаторы емкостью 10 и 0,1 мкФ – в нашем проекте мы их припаяли непосредственно к контактам модуля. Контакт 3.3V платы Arduino не всегда может обеспечить достаточным питанием модуль nRF24L01, в связи с чем для питания модуля целесообразно использовать внешний источник питания.
Внешний вид собранной конструкции проекта показан на следующих рисунках.
Объяснение программ проекта
В данном проекте мы будем использовать библиотеку nRF24L01 library для платы Arduino для передачи и приема данных с помощью модуля nRF24L01. Для конфигурирования программы для микроконтроллера PIC18F46K22 мы будем использовать Microchip Code Configurator (MCC).
Примечание: для удобства мы импортировали библиотеку NRF из Arduino, ее можно скачать по следующей ссылке:
• Ported Arduino Library for PIC18F622K Microcontroller
Объяснение кода программы для микроконтроллера PIC18F46K22
Первым делом в программе подключим используемые библиотеки.
1 2 3 |
#include "mcc_generated_files/mcc.h" #include "nrf24_lib.h" #include <string.h> |
Затем запишем два макроса для осуществления операций передачи (nRF24L01 Tx) и приема (Rx) в модуле. Нам необходимо будет изменять значение NRF24L01_TX_EX на 1 для операций передачи (Tx) и на 0 для операций приема (Rx).
1 2 |
#define NRF24L01_TX_EX 0 #define NRF24L01_RX_EX !NRF24L01_TX_EX |
Затем запрограммируем функцию для мигания светодиода. Периодичность мигания светодиода будет задаваться с помощью Timer0. Число, загружаемое в Timer0, мы подберем таким образом чтобы период мигания светодиода составлял 1 секунду. Данный светодиод будет у нас индикатором того, что наша система функционирует правильно.
1 2 3 |
void blink_led() { LED_Toggle(); } |
В основной функции программы main() мы будем вызывать функцию SYSTEM_Initialize() для инициализации периферийных устройств (прерывания, контакты, Timer0, Uart, SPI1). Также в ней мы установим глобальное разрешение прерываний и разрешим прерывания от периферийных устройств.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void main(void) { // Initialize the device SYSTEM_Initialize(); // If using interrupts in PIC18 High/Low Priority Mode you need to enable the Global High and Low Interrupts // If using interrupts in PIC Mid-Range Compatibility Mode you need to enable the Global and Peripheral Interrupts // Use the following macros to: // Enable the Global Interrupts INTERRUPT_GlobalInterruptEnable(); // Disable the Global Interrupts //INTERRUPT_GlobalInterruptDisable(); // Enable the Peripheral Interrupts INTERRUPT_PeripheralInterruptEnable(); // Disable the Peripheral Interrupts //INTERRUPT_PeripheralInterruptDisable(); |
После этого мы будем вызывать функцию TMR0_SetInterruptHandler() чтобы задать функцию обработки прерывания от таймера, и затем мы запустим в работу Timer0.
1 2 |
TMR0_SetInterruptHandler(blink_led); TMR0_StartTimer(); |
После всех этих инициализаций мы начнем работу с модулем nRF24L01. Сначала нам необходимо инициализировать nRF24, для этого мы вызовем функцию nrf24_rf_init(), установим режим работы nRF24L01 на передачу или прием (Tx или Rx) и радиочастотный канал (0-127). После успешной инициализации модуля выведем на экран его текущие настройки.
1 2 3 4 5 6 7 8 9 10 |
#if NRF24L01_TX_EX ret = nrf24_rf_init(TX_MODE, 115); // Tx mode with 2400+115 Ghz RF frq #elif NRF24L01_RX_EX ret = nrf24_rf_init(RX_MODE, 115); // Rx mode with 2400+115 Ghz RF frq #endif if (ret == NRF24_INIT_OK) { printf("###############################################################\r\n"); printf("NRF24L01 Initialize successful\r\n"); nrf24_printf_rf_config(); printf("###############################################################\r\n"); |
После инициализации nRF24 мы будем выполнять операции передачи и приема в цикле while.
В режиме передачи (In Tx mode): мы создадим буфер с помощью переменной целого типа, затем этот буфер мы будем передавать в качестве параметра в функцию nrf24_send_rf_data(), после чего будем инкрементировать значение переменной целого типа.
1 2 3 4 5 6 |
#if NRF24L01_TX_EX static char val = 0; sprintf(bufferTX, "Sending: %d", val); nrf24_send_rf_data(bufferTX); val++; __delay_ms(100); |
В режиме приема (In Rx mode): с помощью функции nrf24_is_rf_data_available() мы будем проверять принимает ли nRF24 какие либо данные или нет. После того, как данные на приеме будут все же обнаружены, мы будем вызывать функцию nrf24_read_rf_data() чтобы считать эти данные в буфер, после чего будем выводить на экран содержимое буфера.
1 2 3 4 5 6 7 |
#elif NRF24L01_RX_EX while (nrf24_is_rf_data_available()) { } nrf24_read_rf_data(bufferRX); printf("RX data %s\r\n", bufferRX); __delay_ms(10); #endif |
Объяснение кода программы для платы Arduino
На стороне Arduino мы начнем код программы с подключения необходимых библиотек. Среди них будет библиотека для работы с радиочастотным модулем, библиотека для работы с интерфейсом SPI и библиотека printf. Также запишем два макроса для работы с модулем nRF24L01.
1 2 3 4 5 6 7 |
//Include Libraries #include <SPI.h> #include <nRF24L01.h> #include <RF24.h> #include "printf.h" #define NRF24L01_TX_EX 1 #define NRF24L01_RX_EX !NRF24L01_TX_EX |
Далее создадим объект для работы с RF24, который будет работать с модулем через контакты CE и CSN. Также укажем адрес, с помощью которого мы будем взаимодействовать с модулем.
1 2 3 4 |
//create an RF24 object RF24 radio(9, 8); // CE, CSN //address through which two modules communicate. const byte address[6] = {0xe1, 0xe1, 0xe1, 0xe1, 0xe1}; |
Затем в функции setup инициализируем последовательную связь для целей отладки и инициализируем радиочастотный модуль.
1 2 3 4 5 6 |
Serial.begin(115200); printf_begin(); Serial.println(F("\n\rRF24/examples/scanner/")); // Setup and configure rf radio radio.begin(); radio.setAutoAck(false); |
Теперь вспомним о тех двух макросах, которые определили ранее – с их помощью мы легко сможем переключать модуль между передающим и приемными режимами.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#if NRF24L01_TX_EX //set the address radio.openWritingPipe(address); radio.setPALevel(RF24_PA_MIN); //set as: RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX radio.setDataRate(RF24_2MBPS); //set as: F24_250KBPS, F24_1MBPS, F24_2MBPS ==>250KBPS = longest range radio.setChannel(115); //sets channel from 2.4 to 2.524 GHz in 1 MHz increments 2.483.5 GHz is normal legal limit radio.setCRCLength(RF24_CRC_8); radio.printDetails(); //Set module as transmitter radio.stopListening(); #elif NRF24L01_RX_EX radio.openReadingPipe(0, address); radio.setPALevel(RF24_PA_MIN); //set as: RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH, RF24_PA_MAX radio.setDataRate(RF24_2MBPS); //set as: F24_250KBPS, F24_1MBPS, F24_2MBPS ==>250KBPS = longest range radio.setChannel(115); //sets channel from 2.4 to 2.524 GHz in 1 MHz increments 2.483.5 GHz is normal legal limit radio.setCRCLength(RF24_CRC_8); // Get into standby mode radio.startListening(); radio.stopListening(); radio.printDetails(); // Get into standby mode radio.startListening(); #endif |
Далее, в функции loop, мы объявим массив длительностью 32 символа, в котором мы будем хранить передаваемое сообщение. Данный массив мы будем передавать другому радиочастотному модулю. После этого, с помощью функции sprint(), мы будем передавать по одному символу, также эти символы мы будем печатать в окно монитора последовательной связи для целей отладки. А в конце передачи мы будем добавлять задержку 250 мс чтобы сделать работу модуля более стабильной.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void loop() { #if NRF24L01_TX_EX //Send message to receiver char text[32] = {0}; sprintf(text, "Hello PIC18 %d", val++); radio.write(&text, sizeof(text)); Serial.println(text); #elif NRF24L01_RX_EX //Read the data if available in buffer if (radio.available()) { char text[32] = {0}; radio.read(&text, sizeof(text)); Serial.println(text); } #endif delay(250); } |
Тестирование работы проекта
После того как аппаратная и программная части проекта будут готовы, можно приступать к тестированию его работы. Для этой цели в Arduino IDE мы использовали окно монитора последовательной связи, а для микроконтроллера PIC мы использовали программу PuTTY.
Лог отладки № 1 (Uno as Tx & PIC as Rx):
В этом случае мы в окне монитора последовательной связи Arduino IDE должны увидеть сообщение “Hello PIC18 10”. Если вы в PuTTY увидели это же самое сообщение, то значит передача данных прошла без ошибок. Эта ситуация показана на следующем рисунке.
Лог отладки № 2 (Uno as Rx & PIC as Tx):
В этом случае в PuTTY мы должны увидеть сообщение “Hello Arduino 10”, если после этого в окне монитора последовательной связи Arduino IDE увидели то же самое, значит передача данных прошла без ошибок. Эта ситуация показана на следующем рисунке.
Более подробно работу проекта вы можете посмотреть на видео, приведенном в конце статьи.