Introduction
This post is aimed at people who want to integrate basic radio communication in their project, keep in mind that this is based on my experience working with nrf24L01 modules and might be corrected in the future.
Basically everything I know about radio communication started with @Robin2 's work, for the most part in this post
If you are a complete beginner you should read the main replies there before looking at this guide.
These are the files I talk about, you should read the code as it's mentioned later on:
transmission.h
#pragma once
#define ARRAY_SIZE 10
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
extern RF24 radio;
extern const byte thisSlaveAddress[5];
extern uint8_t dataTrasnfer[];
void transmit_data();
void receive_data();
void print_array(const uint8_t array[]);
/*
* This is just for you to test if the communication works, you should use your
* own methods to fill all the 10 bytes of the arrays, not just the first three
*/
void fill_array(uint8_t first, uint8_t second, uint8_t third);
transmission.cpp
#include "transmission.h"
void transmit_data()
{
if (radio.write(&dataTrasnfer, ARRAY_SIZE))
print_array(dataTrasnfer);
}
void receive_data()
{
if ( radio.available())
radio.read(&dataTrasnfer, ARRAY_SIZE);
}
void print_array(const uint8_t array[])
{
for (uint8_t i = 0; i < ARRAY_SIZE; i++) {
Serial.print(array[i]);
Serial.print(" ");
}
Serial.println(" ");
}
void fill_array(uint8_t first = 0, uint8_t second = 0, uint8_t third = 0)
{
dataTrasnfer[0] = first;
dataTrasnfer[1] = second;
dataTrasnfer[2] = third;
}
main.ino
#include "transmission.h"
#define TX 1
#define RX 2
// #########
#define STATE RX
// #########
#define CE_PIN 9
#define CSN_PIN 10
RF24 radio(CE_PIN, CSN_PIN);
const byte thisSlaveAddress[5] = {'R','x','A','A','A'};
uint8_t dataTrasnfer[ARRAY_SIZE] = {}; // ARRAY_SIZE is defined in transmission.h
void setup()
{
Serial.begin(9600);
radio.begin();
radio.setDataRate( RF24_250KBPS );
#if STATE == TX
radio.setRetries(3,5);
radio.openWritingPipe(thisSlaveAddress);
#elif STATE == RX
radio.openReadingPipe(1, thisSlaveAddress);
radio.startListening();
#endif
}
void loop()
{
#if STATE == TX
fill_array(1, 2, 3);
transmit_data();
#elif STATE == RX
receive_data();
print_array(dataTrasnfer); // This is just for debugging purposes
#endif
delay(1000);
}
Code explanation
Ok now we have quite a bit to unpack here:
The whole concept of this example is that you can switch between transmitter (TX) and receiver (RX) code easily, this will make testing your code a whole lot faster and less frustrating.
#if and #define are preprocessor directives: if you have never heard of the preprocessor before, here is a quick explanation which you can skip if you have some experience
When you hit the verify button on your IDE, or upload, the first thing that happens to your code is the preprocessing stage. What this means is that anything which begins with # is dealt with, this includes #include and #define and many others. With #define the preprocessor looks at your code and literally replaces,in this case, STATE, with the value you assigned it: either RX or TX.
The #if statement is an if statement for the preprocessor, as you may have guessed, the important thing to understand is that the preprocessor is basically text editing your code, if STATE is TX, everything related to RX will be cut away.
This way you can split your code in two independent parts and avoid conflict, as well as not wasting any precious memory initializing useless variables
The way you change which part of the program runs is trough STATE, defined at the top, which can be either TX or RX. You don't really need to understand the code before loop(), unless you are getting weird errors/you are not using an arduino uno, in which case look at the tips section at the bottom.
In the TX section of the loop you have fill_array(), it is a dummy function and you should put here your own methods to fill dataTransfer[], which is the hearth of the program because it is where you store the data you want to send and the one you receive.
Note that after transmit_data() is called you will jump directly to the delay(), because of the preprocessor directives.
The RX section is pretty similar: you first unload the nrf24l01's buffer into dataTransfer[] and then you can use it, in this case we just print it out using print_array().
Usecases
This code is very modular and implementing it, if you have a bit of coding experience, shouldn't be too difficult as you just need to add 2 files to your codebase. If you don't like having both transmitter and receiver code in the same project you can just copy and paste the function you need along with the correct setup.
If you have less experience or you want to keep everything in a single file there shouldn't be any problem copying and pasting everything in main.ino for as long as you delete every extern, #pragma and #include with "" after it.
Tips
I wrote this small guide because some time ago I wanted to use radio communication, but I also wanted to modularize it and make it work well with other code. In my opinion this is as close as it can get to what I intended, but since this is meant to be expanded upon, here is some advice that can save you a lot of time:
dataTransfer[] is global, and this seems like a design flaw as you should avoid global variables as much as possible, but one of my biggest problems came up when I made it not global. this was by far the biggest problem it caused. Global variables live in their own memory region and if you pass them, even by reference, they get pushed on the stack. This causes weird things to happen with the nrf24l01 as it needs a global array.
The data buffer 10 bytes, which is not much and should be used smartly, by far the biggest save you can do is using uint8_t, which is an unsigned byte of memory which can hold values between 0 and 255 (both included).
I've tried using byte or unsigned char, but they can have some slight issues with function like sizeof() and others, so uint8_t is your best bet.
Also bytes are defaulted to 0, so even if you put just a 5 in the middle of dataTransfer[], it will still be surrounded by 0.
If your connection doesn't work maybe you hooked something up incorrectly, this will be incredibly useful
(last reply on the thread). Also don't be hasty because this is a complicated topic and you need data handling to be reliable, so test your code a lot before using it in a final build.
Lastly if you use different hardware than an arduino uno you will have different pin numbers, read Robin2's post carefully.
If you find any of this unclear or you need some help just post a new thread in the right section and let me know via PM
useful links: