Skip to content

14. Embedded Networking and Communications | nRF24

- Send a message between two projects:

Group assignment page: here.

My individual part:
I have documented it in the Wired communication section.

- Design, build, and connect wired or wireless node(s) with network or bus addresses:

Since my final project consists on two parts: (1) the Omni-directional vehicle itself and (2) a remote control, for this assignment I made a remote control double-sided-board, which contains: Joysticks as Input and uses a nRF24 module for wireless communication.

For testing a right communication performance, I used an Arduino UNO board with the same radio module as receiver.

Wired communication

For this group assignment we had to communicate our boards with each other. They had to be from two different students, but since in this node we are three students, we just did it all together 😄

Boards used

We have used the boards that we have designed from previous assignments:

- A ) The microcontroller board of my colleague Gerhard Mattisen.

- B ) The MyMiniBoard_X14 that I have made during the Electronics design assignment:
Features

- C ) The Fab Motor Controller of my colleague Harley Lara:
MotorController

Connections

Block-diagram:
DiagramBlock

Connection between boards:
Connection

  • Board A:

    • Has a photosensor as input.
    • It senses the brightness values.
    • Sends the sensed values through serial connection to Board B.
  • Board B:
    MyBoard

    • 1) is the Serial connection to Board A, receives sensed light intensity values; the board analyzes the values and according to them sends a message through ->
    • 2) I2C connection to Board C, to turn the motor On or Off.
    • 3) is FTDI for Serial monitor.
    • 4) is UPDI for programming.
  • Board C:

    • Receives the messages from Board B to turn the DC motor On and Off.
    • Has a DC motor connected, powered by a 9V battery.

Codes

Board A

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// ==========================
// SENDER
// ==========================

#include <SoftwareSerial.h>

SoftwareSerial softwareSerial(0, 1); // RX, TX

const int sendPin = 10; // LED pin
const int ldrPin = 9;  // LDR pin

void setup() {

  Serial.begin(9600);
  softwareSerial.begin(9600);

  pinMode(sendPin, OUTPUT);
  pinMode(ldrPin, INPUT);

  Serial.println("Sender ready");
}

void loop() {

  // Read ldr value
  int ldrStatus = analogRead(ldrPin);

  /** Serial Monitor for debugging **/
  Serial.print("[SENDING] Data: ");
  Serial.println(ldrStatus); // Displays the value of "ldrStatus" on the serial monitor

  /** Send data to Reciver **/
  softwareSerial.write(ldrStatus);

  if (ldrStatus <= 500) {
    digitalWrite(sendPin, HIGH);

    // for debugging
    //Serial.println("[STATUS] Its DARK Turn ON the LED");
  } else {
    digitalWrite(sendPin, LOW);

    // for debugging
    //Serial.println("[STATUS] Its BRIGHT Turn off the LED");
  }
  delay(200);
}

Board B

First I had to install the library that my colleague developed as his practice for this week.

In the case of my board, since I was already using the Rx and Tx of the microcontroller for the FTDI connection with the laptop for Serial monitor, I have used softwareSerial(Rx, Tx) to have serial communication with Board A.

To be able to connect another node to my board I could simply use another softwareSerial(Rx, Tx) with two of the three pins left that I have.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
// ===========================
// Receiver
// ==========================

/* Made by Jefferson Sandoval in collaboration with Harley Lara 
 * for the Embedded Networking and Communications group assignment 
 * during the FabAcademy2021
 *  
 * This code was uploaded to a board with an Attiny1614 microcontroller
 * board. Board documentation:
 * http://fabacademy.org/2021/labs/kamplintfort/students/jefferson-sandoval/assignments/week07/
 * 
 * My documentation for this assignment:
 * http://fabacademy.org/2021/labs/kamplintfort/students/jefferson-sandoval/assignments/week14/
 */

#include <SoftwareSerial.h> //Include library to create an extra serial communication
#include <FabMotorController.h> //Include library to control Motor controller board

SoftwareSerial softwareSerial(0, 1); //Assign softwareSerial(RX, TX) pins
FabMotorController MotorControl; //Create an object

const int led = 10; //Declare constant for the built-in LED
int data; //Declare ingeter variable to read data from serial connection

void setup() {

  Serial.begin(9600); //Begin serial communication (used for monitor)
  softwareSerial.begin(9600); //Begin the extra serial communication

  pinMode(LED, OUTPUT); //Set LED as output

  Serial.println("Reciever ready"); //Starting serial monitor message

  MotorControl.begin(0x04); // Initialize I2C communication at address 0x04
}

void loop() {

  while (softwareSerial.available()){
    data = softwareSerial.read(); // Read data from Sender (Board A)
  }

  //Print the received values from Board A
  Serial.print("[RECEIVER] data: ");
  Serial.println(data);

  if (data <= 125){ //If the received value is less than 125 (darkness)...
    digitalWrite(LED, HIGH); //Turn on the built-in LED
    MotorControl.run(OUTPUT1, 100); //Send message to Board C to turn on the motor
  }
  else{ //else...
    digitalWrite(LED, LOW); //Turn off the built-in LED
    MotorControl.run(OUTPUT1, 0); //Send message to Board C to turn off the motor
  }

  delay(100);
}

Board C

Firmware that my colleague used:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * FabMotorController Firmware v0.1
 * A Firmware to control through I2C the FabMotorController, 
 * a hardware based on the ATTINY1614 and the L298N driver.
 * 
 * Author: Harley Lara
 * Create: 01 May 2021
 * License: (CC BY-SA 4.0) Attribution-ShareAlike 4.0 International
 * 
 * This work may be reproduced, modified, distributed,
 * performed, and displayed for any purpose, but must
 * acknowledge this project. Copyright is retained and
 * must be preserved. The work is provided as is; no
 * warranty is provided, and users accept all liability.
 * 
 */

#include <Wire.h>

#define CONTROLLER_ADDR 0x04

/******* CONTROL PINs *******/
#define IN1 9   // OUTPUT 1 INPUT 1
#define IN2 8   // OUTPUT 1 INPUT 2
#define IN3 10  // OUTPUT 2 INPUT 1
#define IN4 2   // OUTPUT 2 INPUT 2
#define EN1 0   // 0 ENABLE 1 (PWM - SPEED CONTROL)
#define EN2 1   // 1 ENABLE 2 (PWM - SPEED CONTROL)

#define RESPONSE_SIZE 12
String response = "executing...";

void setup() {
  Wire.begin(CONTROLLER_ADDR);
  //Wire.onRequest(requestEvent); // TODO
  Wire.onReceive(receiveEvent);
  Serial.begin(115200);
  Serial.println("[INFO]: FAB MOTOR CONTROLLER - Starting ...");
  delay(20);

  // PINs Mode
  pinMode(IN1, OUTPUT);
  pinMode(IN2, OUTPUT);
  pinMode(IN3, OUTPUT);
  pinMode(IN4, OUTPUT);
  pinMode(EN1, OUTPUT);
  pinMode(EN2, OUTPUT);
}

unsigned char output;
int speed;
int direction;

void receiveEvent(){

  /******* Read data from Master *******/
  while (Wire.available()){
    output = Wire.read();
    speed =  Wire.read();
    direction = Wire.read();
  }

  /******* OUTPUT 1 *******/
  if (output == 1){
    Serial.println("[DEGUB]: OUTPUT 1");
    // Forward direction
    if (direction == 1){
      // stop output
      if (speed == 0){
        //Serial.println("[DEGUB]: OUTPUT 1 STOP FREE");
        digitalWrite(IN1, LOW);
        digitalWrite(IN2, LOW);
      }
      // moving output
      else{
        //Serial.println("[DEGUB]: OUTPUT 1 FORWARD");
        analogWrite(EN1, speed);
        digitalWrite(IN1, HIGH);
        digitalWrite(IN2, LOW);
      }
    }
    // backward direction
    else if (direction == 0){
      //Serial.println("[DEGUB]: OUTPUT 1 STOP FREE");
      if (speed == 0){
        digitalWrite(IN1, LOW);
        digitalWrite(IN2, LOW);
      }
      else{
        //Serial.println("[DEGUB]: OUTPUT 1 BACKWARD");
        analogWrite(EN1, speed);
        digitalWrite(IN1, LOW);
        digitalWrite(IN2, HIGH); 
      }
    }
  }

  /******* OUTPUT 2 *******/
  else if (output == 2){
    Serial.println("[DEGUB]: OUTPUT 2");
    // Forward direction
    if (direction == 1){
      // stop output
      //Serial.println("[DEGUB]: OUTPUT 2 STOP FREE");
      if (speed == 0){
        digitalWrite(IN3, LOW);
        digitalWrite(IN4, LOW);
      }
      // moving output
      //Serial.println("[DEGUB]: OUTPUT 2 FORWARD");
      else{
        analogWrite(EN2, speed);
        digitalWrite(IN3, HIGH);
        digitalWrite(IN4, LOW);
      }
    }
    // backward direction
    else if (direction == 0){
      if (speed == 0){
        //Serial.println("[DEGUB]: OUTPUT 2 STOP FREE");
        digitalWrite(IN3, LOW);
        digitalWrite(IN4, LOW);
      }
      else{
        //Serial.println("[DEGUB]: OUTPUT 2 BACKWARD");
        analogWrite(EN2, speed);
        digitalWrite(IN3, LOW);
        digitalWrite(IN4, HIGH); 
      }
    }
  }
}

void loop() {
  delay(50);
}

Performance

Then… when I cover the photosensor, the Motor is started:

Making my remote control board

Requirements, considerations and component selection:

  • I used a nRF24 module for communication (which is not really part of my board itself, but I needed to determine it beforehand 😅); then arrangement of the pin headers for the this module is made is such a way I can plug it in without using cables).

  • Since the nRF24 module works at 3.3V, then I needed a 3.3V voltage regulator, but ☝🏼 also the Joysticks work at 3.3-5V, aaand also the ATtiny1614 can work at that voltage. I mention it because my first thought was using also a 5V regulator for the microcontroller.

  • I used the ATtiny1614 microcontroller cause it has the necessary pins for this application.

  • UPDI connection for programming, and pin headers for Power where I plan connecting 1S Lipo battery (~3.7V).

  • 1uF capacitor for voltage stabilization.

  • Also as I have made on me previous boards, I added a LED to indicate when it’s connected.

  • I have used TH pin headers to connect both sides of the boards, as a solution to not having rivets on hand.

  • I have used the footprint of TH pin headers for the Joysticks, but the idea was just soldering short cables so I get more freedom to arrange them.

Schematic

Schematics

Board layout

I used the front layer mainly for logic connections and the back one for Power connections.

  • Front side:
    LayoutFront

  • Back side:
    LayoutBack

Milling PCB

The milling machine I used was the Roland MonoFab SRM-20.
The tool I used was a V-bit 0.2-0.5mm.

Images:

I cut the outline on the front side and made the holes on the back side.

  • Front side:

    • Traces:
      F_Traces
    • Outline:
      F_Outline
  • Back side:

    • Traces:
      B_Traces
    • Holes:
      B_Holes

Milled PCB:

Being my first double sided board ever made… I’m very proud of the result 😄

  • Front side:
    PrintedFront

  • Back side:
    PrintedBack

Soldered:

The components I used on this board were:

Qty Description
1 ATtiny1614 microcontroller
1 LM3480-3.3v voltage regulator
1 1uF capacitor
1 499Ohms resistor
1 LED
13 Single row right-angle male pin header

The joysticks go on the soldered cables.

Testing board

Connections

Transmitter

CircuitTransmitter

I just had to solder the Joysticks and connect the radio module.

For programming I used the USB and UPDI adapter boards that I made during the Electronics production assignment.

Receiver

nRF24 connection

Pins 11, 12 and 13 are the SPI connections on Arduino UNO board.

Module pin Connection
Vcc 3.3V
CE UNO - Pin 7
CSN UNO - Pin 8
SCK UNO - Pin 13
MOSI UNO - Pin 11
MISO UNO - Pin 12
IRQ NC
GND GND

CircuitReceiver

Codes

Beforehand, it’s needed to install the nRF24 (by TMRh20):
Library

I think that the codes are fairly commented, then I didn’t put extra explanations.

Transmitter

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/* Made by Jefferson Sandoval for the Embedded Networking and Communications assignment 
 * during the FabAcademy2021
 *  
 * This code was made for testing my board for a remote control for my final project which is an
 * Omni-directional three-wheeler.
 * 
 * Uploaded to my board which, using a nRF24 module and two joysticks, serves as transmitter 
 * sending data to an Arduino UNO board.
 * 
 * Documentation:
 * http://fabacademy.org/2021/labs/kamplintfort/students/jefferson-sandoval/assignments/week14/
 */

//Libraries for Communication part (nRF24)
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(4, 5); //Create radio object with and CE & CSN pins
const byte address[6] = "00111"; //Transmitter communication address

void setup() {
  radio.begin(); //Initialize radio object
  radio.setPALevel(RF24_PA_LOW); //Set radio range
  radio.openWritingPipe(address); //Set address to receiver
  radio.stopListening(); //Set module as transmitter
}

void loop() {
  int Values[3]; ////Declare variable for data to send

  //Read values from Joysticks and convert them to percentage.
  //I used 200 cause the joystick can rotate only 50% on any direction.
  Values[0] = map(analogRead(0),0,1023,-200,200); //Left and Right movement from Joystick_1
  Values[1] = map(analogRead(1),0,1023,-200,200); //Forward and Backward movement from Joystick_1
  Values[2] = map(analogRead(7),0,1023,-200,200); //Rotation movement from Joystick_2

  radio.write(&Values, sizeof(Values)); //Send array of Values to Receiver

  delay(300); //Send values every 0.3 seconds
}

Receiver

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* Made by Jefferson Sandoval for the Embedded Networking and Communications assignment 
 * during the FabAcademy2021
 *  
 * This code was made for testing my board for a remote control for my final project which is an
 * Omni-directional three-wheeler.
 * 
 * Uploaded to an Arduino UNO board which, using a nRF24 module, serves as receiver reading data 
 * from another board with 2 joysticks as input. After reading the received values, it prints on 
 * Serial monitor a message that indicates the corresponding vehicle movements.
 * 
 * Documentation:
 * http://fabacademy.org/2021/labs/kamplintfort/students/jefferson-sandoval/assignments/week14/
 */

//Libraries for Communication part (nRF24)
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

RF24 radio(7, 8); //Create radio object with and CE & CSN pins
const byte address[6] = "00111"; //Receiver Communication address

void setup() {
  Serial.begin(9600); //Begin Serial communication at 9600 bauds
  radio.begin(); //Initialize radio object
  radio.setPALevel(RF24_PA_LOW); //Set radio range
  radio.openReadingPipe(0, address); //Set address to transmitter
  radio.startListening(); //Set module as receiver
}

void loop() {
  int Values[3]; //Declare variable for data to receive
  if (radio.available()) {  
    radio.read(&Values, sizeof(Values)); //Read array of Values from Transmitter

    //Print on Serial monitor a message according to the direction of movement of the joysticks
    if (Values[0]>=15){Serial.println("Forward");}
    if (Values[0]<=-15){Serial.println("Backward");}
    if (Values[1]>=15){Serial.println("Right");}
    if (Values[1]<=-15){Serial.println("Left");}
    if (Values[2]>=15){Serial.println("C-CW rotation");}
    if (Values[2]<=-15){Serial.println("CW rotation");}
    }
    delay(300); //Read values every 0.3 seconds
}

Performance

Assignment outlook

During the testing of this board I found out that when using the Vcc and GND of the UPDI connection, the voltage regulator smokes, so I powered it up from the Power pins and used a jumper cable for the UPDI. This left me two important considerations that I will take for the official board for the final project:

  • Take as Input for the [3.3V] voltage regulator the Vcc from the UPDI.

  • Since I’ll use the Vcc and GND from the UPDI connection and use them for the battery, I will delete the current Power pins which will also save some space on the bottom of the board (taking in count the female pins I have to use to connect the battery).

  • Also for future applications, If it’s needed a separated power supply (for example 12V), the voltage regulator should contain two pin headers to close the circuit when connecting the external supply and open it when programming.

Files and references

- TransmitterTest.ino
- ReceiverTest.ino
- KiCad project: RemoteControlBoard.zip

- nRF24 datasheet


Last update: June 29, 2021