How to Use RFM69C With ESP32 S2

by kerryland in Circuits > Microcontrollers

2050 Views, 2 Favorites, 0 Comments

How to Use RFM69C With ESP32 S2

dipole-crop.jpg

I couldn't find a complete example of how to use an RFM69C with an ESP32, so here's everything you need to make them work together. This Instructable will tell you how to wire the component pins, and includes complete sample code that shows how to send and receive a message.

Supplies

Connect the Hardware

rfm69-circuitboard2.jpg
esp32-board-pins.jpg

First up we need to solder the RFM69C to the ESP32-S2. Note that the ESP32 S2 Mini board doesn't bother to show the "GPIO" prefix, so GPIO36 is labeled "36".

.-------------.-------------------.------------------.-----------------------------------------.
| RFM69 Label | ESP32 Interface | ESP32 Datasheet | Comment |
|-------------'-------------------'------------------'-----------------------------------------|
| 3.3v | 3v3 or VCC | | Input voltage. 3.3v DC |
| GND | GND | | Ground. One RFM69 GND used for antenna |
| SCK | GPIO36 | FSPICLK | SPI Clock Input |
| MISO | GPIO37 | FSPIQ | SPI Data Outout |
| MOSI | GPIO35 | FSPID | SPI Data Input |
| SS | GPIO34 | FSPICS0 | SPI Select |
| DIO0 | GPIO5 | | Payload Ready/Packet signal |
| ANT | | | The antenna connects here, later |
'-------------'-------------------'------------------'-----------------------------------------'


Connect an Antenna

esp32-ant.jpg
dipole-crop.jpg

Before you can power up your circuit you first need to connect an Antenna. In it's simplest form it can just be a piece of stiff wire cut to the correct length. A "quarter wave" antenna would be -- something like 80mm for a 915Mhz board, 86.2mm for an 868Mhz board and 172mm for a 433Mhz board. I say "something like" because the Internet doesn't seem to agree, and I'm no expert in the subject.

Anyway, solder your appropriately-sized length of wire into the ANT socket of your RFM69C board. While you're experimenting at your desk you won't have any connectivity problems, but if you can't make a connection when your devices are further apart you can add a second length of wire to your circuit. The second wire should be the same length as the one in the ANY socket, but should be soldered into a GND socket and pointed in the opposite direction.


Connect the Software

I used the RadioHead library with Visual Studio Code IDE and platformio using the Arduino framework. This looks like a good Instructable if you haven't used platformio before.

The key "secret" about using the RadioHead library with this circuit are the parameters passed to the RH_RF69 constructor. In our case it's this:

RH_RF69 driver(SS, 5); // SS is a predefined reference to GPIO34.
// "5" is GPIO5, the ESP32 pin we connected to DIO0 on the RFM69 board. 

You can go ahead and look at any of the RadioHead examples for the RF69 boards, just make sure you use this constructor anywhere you see code that looks like this:

// Singleton instance of the radio driver
RH_RF69 driver;
//RH_RF69 driver(15, 16); // For RF69 on PJRC breakout board with Teensy 3.1
//RH_RF69 driver(4, 2); // For MoteinoMEGA https://lowpowerlab.com/shop/moteinomega
//RH_RF69 driver(8, 7); // Adafruit Feather 32u4

I have reworked their Arduino IDE examples rf69_reliable_datagram_client.pde and rf69_reliable_datagram_server.pde for use in platformio, and moved some duplicated code into a class shared between the client and server code.

You just need to define a few properties in "glue.h", build the binaries, and you should be good to go.

glue.h

#define CLIENT_ADDRESS 1
#define SERVER_ADDRESS 2

#define FREQUENCY 868.0
#define INTERRUPT_PIN 5   // GPIO5 on the ESP32

#define SECRET_KEY "BasilBrushROCKS!" // Must be exactly 16 characters long

glue.cpp

#include <Arduino.h>
#include <RHReliableDatagram.h>
#include <RH_RF69.h>
#include "glue.h"

uint8_t password[] = SECRET_KEY;

// SS is specific to your ESP32 variant and defined via your board selection in platformio.ini
// in the case of the ESP32-S2 it equals 34
RH_RF69 driver(SS, INTERRUPT_PIN);

void setupManager(RHReliableDatagram manager)
{
    driver.setEncryptionKey(password);
    driver.setFrequency(FREQUENCY);
    // If you are using a high power RF69 eg RFM69HW, you *must* set a Tx power with the
    // ishighpowermodule flag set like this:
    // driver.setTxPower(14, true);

    if (!manager.init())
        Serial.println("init() failed");
}

client.cpp

#include <RHReliableDatagram.h>
#include <RH_RF69.h>
#include <SPI.h>
#include "glue.h"

extern void setupManager(RHReliableDatagram manager);
extern RH_RF69 driver;

// Class to manage message delivery and receipt, using the driver declared above
RHReliableDatagram manager(driver, CLIENT_ADDRESS);

void setup()
{
  Serial.begin(9600);
  // Beware that this will wait forever if there is no serial port connected!
  while (!Serial);
  Serial.println("Client init");

  setupManager(manager);
}

uint8_t data[] = "Hello World!";
// Dont put this on the stack:
uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];

void loop()
{
  Serial.println("Sending to server");

  // Send a message to manager_server
  if (manager.sendtoWait(data, sizeof(data), SERVER_ADDRESS))
  {
    // Now wait for a reply from the server
    uint8_t len = sizeof(buf);
    uint8_t from;
    if (manager.recvfromAckTimeout(buf, &len, 2000, &from))
    {
      Serial.print("got reply from : 0x");
      Serial.print(from, HEX);
      Serial.print(": ");
      Serial.println((char *)buf);
    }
    else
    {
      Serial.println("No reply, is server running?");
    }
  }
  else
  {
    Serial.println("sendtoWait failed");
  }
  delay(500);
}

server.cpp

#include <RHReliableDatagram.h>
#include <RH_RF69.h>
#include <SPI.h>
#include "glue.h"

extern RH_RF69 driver;
RHReliableDatagram manager(driver, SERVER_ADDRESS);
extern void setupManager(RHReliableDatagram manager);

void setup()
{
    Serial.begin(9600);
    delay(500);
    // Beware that this will wait forever if there is no serial port connected!
    while (!Serial);
    Serial.println("Server init...");
    setupManager(manager);
}

uint8_t data[] = "And hello back to you";
// Dont put this on the stack:
uint8_t buf[RH_RF69_MAX_MESSAGE_LEN];

long start = millis();

void loop()
{
    if (manager.available())
    {
        // Wait for a message addressed to us from the client
        uint8_t len = sizeof(buf);
        uint8_t from;
        if (manager.recvfromAck(buf, &len, &from))
        {
            Serial.print((millis()-start) / 1000);
            Serial.print(". got request from : 0x");
            Serial.print(from, HEX);
            Serial.print(": ");
            Serial.println((char *)buf);

            // Send a reply back to the originator client
            if (!manager.sendtoWait(data, sizeof(data), from))
                Serial.println("sendtoWait failed");
        }
    }
}

platformio.ini

[env]
platform = espressif32
board = esp32-s2-saola-1
framework = arduino
build_flags =
    -DARDUINO_USB_CDC_ON_BOOT=1
    -DARDUINO_USB_MSC_ON_BOOT=0
lib_deps =
    mikem/RadioHead@^1.120
monitor_speed = 9600

[env:esp32-s2-client]
upload_port = COM18
build_flags = ${env.build_flags}
build_src_filter = +<client.cpp>,+<glue.cpp>
board = ${env.board}
framework = ${env.framework}
lib_deps = ${env.lib_deps}

[env:esp32-s2-server]
upload_port = COM13
build_flags = ${env.build_flags}
build_src_filter = +<server.cpp>,+<glue.cpp>
board = ${env.board}
framework = ${env.framework}
lib_deps = ${env.lib_deps}

When deploying to the devices you might need to change the serial ports defined in platformio.ini:

[env:esp32-s2-client]
upload_port = COM18
...
...
[env:esp32-s2-server]
upload_port = COM13


More Information

While on my journey I learned a few more things you might be interested in.

  • The RFM69C and RFM69CW are comparatively low power, which is good for a battery powered device at the end of my driveway. These are also pin compatible with older RFM12B circuits.
  • The RFM69HCWs are more powerful, and can talk to 69Cs, but are not pin compatible.
  • LoRa modules are the long distance, newer, wunderkinds with much better marketing, but they don't talk to the RFM69s.
  • The LoRa SX1262 is newer and better than the SX1276, despite having a lower number!
  • The LoRa LLCC68 is specifically designed for "medium range" indoor and indoor/outdoor applications. It sounds like a alternative to the RFM69C!
  • You also might like to investigate ESP32-NOW, which may be good enough for your indoor needs.

Good luck!