Issue communicating with MRF24J40MA 802.15.4 wireless modules

Hello all, first time poster here!

I am currently using Microchip's MRF24J40MA 802.15.4 Transceiver module (datasheet) with two Satshakits, which are "100% Arduino IDE and libraries compatible, fabbable and open source boards" that act as an Arduino Uno, both software-wise (e.g. when programming) and hardware-wise (e.g. pinout). I am doing so to send weather data from Adafruit's BME280 SPI Temperature Humidity Pressure Sensor connected to one Satshakit to the other wirelessly and display that information; this is all for my Fab Academy final project. I have been using karlp's MRF24J40 Arduino library that is used in many Arduino projects with the MRF24J40 wireless modules that I've seen on the web. I have been running various iterations of sample code found within the library's examples and elsewhere. To do so, I am basically keeping the PAN ID the same for both boards (which I understand is necessary to connect the two modules) and swapping the sending/receiving addresses between boards. As far as I can tell, both modules are sending data, but neither are receiving each others' transmissions.


The Setup

The wireless modules on each board is connected to the ATMega328P in the following manner (note that all signals, except for GND and VCC, are routed through a Logic Level Converter to shift the levels from 5V to 3.3V and back again since the wireless module operates on 3.3V; see attached photo & schematics for look at the modules and the LLCs, which are on the top left and bottom left of each board, respectively):

MRF24J40MA Pin (in numerical order from datasheet) Satshakit (Arduino Uno) Pin
GND GND
RESET 6
WAKE 2
INT 3
SDI 11 (MOSI)
SCK 13 (SCK)
SDO 12 (MISO)
CS 9
NC -
VIN VCC (via 3.3V LDO regulator)
GND GND
GND GND

The Code

I began with the Basic_TwoWay example from the aforementioned library (original .ino file attached for reference), and I changed the pins at the top and the attachInterrupt line to reflect my setup, and then only swapped the addresses for sending/receiving on the two boards. The resulting, modified code:

/**
 * Example code for using a microchip mrf24j40 module to send and receive
 * packets using plain 802.15.4
 * Requirements: 3 pins for spi, 3 pins for reset, chip select and interrupt
 * notifications
 * This example file is considered to be in the public domain
 * Originally written by Karl Palsson, karlp@tweak.net.au, March 2011
 */
#include <SPI.h>
#include <mrf24j.h>

const int pin_reset = 6; // UPDATED for my setup!!
const int pin_cs = 9; // UPDATED for my setup!!
const int pin_interrupt = 3; // UPDATED for my setup!!

Mrf24j mrf(pin_reset, pin_cs, pin_interrupt);

long last_time;
long tx_interval = 1000;

void setup() {
  Serial.begin(9600);
  
  mrf.reset();
  mrf.init();
  
  mrf.set_pan(0xcafe);
  // 0xcafe is our PAN (common ID)
  mrf.address16_write(0x4202); // CHANGE DEPENDING ON BOARD --> 0x4201 is address on display, 0x4202 on sensor

  // uncomment if you want to receive any packet on this channel
  //mrf.set_promiscuous(true);
  
  // uncomment if you want to enable PA/LNA external control
  //mrf.set_palna(true);
  
  // uncomment if you want to buffer all PHY Payload
  //mrf.set_bufferPHY(true);

  attachInterrupt(1, interrupt_routine, CHANGE); // UPDATED for my setup!! --> interrupt 0 equivalent to pin 3 (INT1) on ATmega328 
  last_time = millis();
  interrupts();
}

void interrupt_routine() {
    mrf.interrupt_handler(); // mrf24 object interrupt routine
}

void loop() {
    mrf.check_flags(&handle_rx, &handle_tx);
    unsigned long current_time = millis();
    if (current_time - last_time > tx_interval) {
        last_time = current_time;
        Serial.println("txxxing...");
        mrf.send16(0x4201, "abcd"); // CHANGE DEPENDING ON BOARD --> should be opposite of address stated in mrf.address16_write above ^^^
    }
}

void handle_rx() {
    Serial.print("received a packet ");Serial.print(mrf.get_rxinfo()->frame_length, DEC);Serial.println(" bytes long");
    
    if(mrf.get_bufferPHY()){
      Serial.println("Packet data (PHY Payload):");
      for (int i = 0; i < mrf.get_rxinfo()->frame_length; i++) {
          Serial.print(mrf.get_rxbuf()[i]);
      }
    }
    
    Serial.println("\r\nASCII data (relevant data):");
    for (int i = 0; i < mrf.rx_datalength(); i++) {
        Serial.write(mrf.get_rxinfo()->rx_data[i]);
    }
    
    Serial.print("\r\nLQI/RSSI=");
    Serial.print(mrf.get_rxinfo()->lqi, DEC);
    Serial.print("/");
    Serial.println(mrf.get_rxinfo()->rssi, DEC);
}

void handle_tx() {
    if (mrf.get_txinfo()->tx_ok) {
        Serial.println("TX went ok, got ack");
    } else {
        Serial.print("TX failed after ");Serial.print(mrf.get_txinfo()->retries);Serial.println(" retries\n");
    }
}

When I run this code on one Satshakit and run the same code with swapped addresses on the other Satshakit and open serial monitor for one of the boards, I only get the "txxxing..." message. I am uncertain as to whether the modules are even sending anything at all (or how I could check that they are properly functioning) or why they aren't receiving each others' messages. I also un-commented the following line:

mrf.set_promiscuous(true);

which the comment preceding it indicates should show any message on the same channel, but to no avail.

I found another similar library here that has a neat ChannelScanner example that would at least help me figure out if the modules are functioning. However, none of the examples from that library include syntax that show me how to modify the defined SPI pins, which I need to change to match my setup. (I will look through the SPI library that is included in the code and see if I can change it that way, though I would prefer a manner that wouldn't change the entire library).

(continued...)

Basic_TwoWay.ino (2.5 KB)

WirelessModules.jpg

(continued from above)

I also found the following code here that I modified for my setup and tested (webpage is in Portuguese, but I used Google Translate to view it, which is how I found the link for the code) (also note that you have to change the definition of BASE from true to false and vice versa when running the code on the two boards):

#include <SPI.h>
#include <mrf24j.h>

// Defines if the module is the base or the terminal
#define BASE true

// Defines if PA/LNA control is activated
#define PA_LNA true

// Pin definitions
const int pin_reset = 6;
const int pin_cs = 9;
const int pin_interrupt = 3;

// Network addresses
word panId = 0xcafe;
word localAddr;
word destAddr;

// Transmission delay, higher causes less collisions
int txDelay = 20;

// Transmission period tracking
unsigned int interval = 2000;
unsigned long periodStart;

// Packet data
struct packet {
  int sequenceNumber;
  int packetsSent;
  int packetsReceived;
  int rssi;
  int lqi;
} rxPacket, txPacket;

// Packet counters
int packetsSent = 0;
int packetsReceived = 0;

// RSSI and LQI sums for the current period
unsigned long rssiSum = 0;
unsigned long lqiSum = 0;

Mrf24j mrf(pin_reset, pin_cs, pin_interrupt);

void setup() {
  word addr;
  
  Serial.begin(115200);
  
  delay(1000);

  // Initialize module
  mrf.init();
  
  // Delay to let RF stabilize
  delay(1);

  // Configure module as base or node
  if (BASE) {
    // Set to coordinator
    mrf.write_short(0x00, 0x0C);
    localAddr = 0x6001;
    destAddr = 0x6002;
  } else {
    localAddr = 0x6002;
    destAddr = 0x6001;
  }

  // Configure PA_LNA control
  mrf.set_palna(PA_LNA);
  
  // Try to write and then read back the PAN ID
  mrf.set_pan(panId);
  panId = mrf.get_pan();
  Serial.print("\nPAN ID: 0x");
  Serial.print(panId, HEX);
  Serial.print("\n");

  // Try to write and then read back the module address
  mrf.address16_write(localAddr);
  addr = mrf.address16_read();
  Serial.print("Address: 0x");
  Serial.print(addr, HEX);
  Serial.print("\n");

  // Print some register values for debugging
  byte reg;
  
  reg = mrf.read_short(0x00);
  Serial.print("RXMCR: 0x");
  Serial.print(reg, HEX);
  Serial.print("\n");

  reg = mrf.read_short(0x11);
  Serial.print("TXMCR: 0x");
  Serial.print(reg, HEX);
  Serial.print("\n");

  reg = mrf.read_short(0x10);
  Serial.print("ORDER: 0x");
  Serial.print(reg, HEX);
  Serial.print("\n");

  reg = mrf.read_short(0x0D);
  Serial.print("RXFLUSH: 0x");
  Serial.print(reg, HEX);
  Serial.print("\n");

  reg = mrf.read_long(0x200);
  Serial.print("RXCON0: 0x");
  Serial.print(reg, HEX);
  Serial.print("\n");

  reg = mrf.read_short(0x32);
  Serial.print("INTCON: 0x");
  Serial.print(reg, HEX);
  Serial.print("\n");
  
  // Initialize transmission period start time
  periodStart = millis();
}

void loop() {
  int lqi = 0;
  int rssi = 0;

  // Check if messages have been received
  if (receivePacket(&rxPacket, &lqi, &rssi)) {
    packetsReceived++;
    rssiSum += rssi;
    lqiSum += lqi;
  }

  // Send packet to destination
  sendPacket(destAddr, &txPacket);
  packetsSent++;

  // Check for end of transmission period
  if (millis() - periodStart >= interval) {
    // Update data to be transmitted
    txPacket.packetsSent = packetsSent;
    txPacket.packetsReceived = packetsReceived;
    if (packetsReceived > 0) {
      txPacket.rssi = rssiSum / packetsReceived;
      txPacket.lqi = lqiSum / packetsReceived;
    } else {
      txPacket.rssi = 0;
      txPacket.lqi = 0;
    }
    
    // Reset received packet if no packets received
    if (packetsReceived == 0) {
      rxPacket.packetsSent = 0;
      rxPacket.packetsReceived = 0;
      rxPacket.rssi = 0;
      rxPacket.lqi = 0;
    }
    
    // Print data
    Serial.println();
    Serial.println("Local:");
    printPacket(txPacket);
    Serial.println("Remote:");
    printPacket(rxPacket);

    // Restart reception period
    packetsSent = 0;
    packetsReceived = 0;
    rssiSum = 0;
    lqiSum = 0;
    periodStart = millis();
  }
  
  // Delay until next transmission
  if (txDelay > 0) {
    delay(txDelay);
  }
}

void printPacket(struct packet p) {
  Serial.print("  Packets sent: ");
  Serial.println(p.packetsSent);
  Serial.print("  Packets received: ");
  Serial.println(p.packetsReceived);
  Serial.print("  Packet loss: ");
  if (p.packetsSent > 0 && p.packetsReceived > 0) {
    Serial.print(100 * (p.packetsSent - p.packetsReceived) / p.packetsSent);
    Serial.println("%");
  } else {
    Serial.println("-");
  }
  Serial.print("  RSSI: ");
  Serial.println(p.rssi);
  Serial.print("  LQI: ");
  Serial.println(p.lqi);
}

void sendPacket(word addr, struct packet* p) {
  char buf[128];
  p->sequenceNumber++;
  sprintf(buf, "%d,%d,%d,%d,%d", p->sequenceNumber, p->packetsSent, p->packetsReceived, p->rssi, p->lqi);
  mrf.send16(addr, buf);
}

boolean receivePacket(struct packet* p, int* lqi, int* rssi) {
  char buf[128];
  int frameSize;
  int i;

  // Check RXIF in INTSTAT
  if (mrf.read_short(0x31) & 0x08) {
    // Disable interrupts and receiver
    noInterrupts();
    mrf.write_short(0x39, 0x04);
    
    // Packet received, get the number of bytes
    frameSize = mrf.read_long(0x300);
    
    // Copy the message bytes into the user buffer
    for (i = 0; i < (frameSize - 11); i++) {
      buf[i] = mrf.read_long(0x301 + 9 + i);
    }
    buf[i] = '\0';

    // Get link quality indicator    
    if (lqi) {
      *lqi = mrf.read_long(0x301 + 9 + i + 2);
    }

    // Get signal strength
    if (rssi) {
      *rssi = mrf.read_long(0x301 + 9 + i + 3);
    }
  
    // Flush the reception buffer, re-enable interrupts and receiver
    mrf.write_short(0x0D, 0x01);
    mrf.write_short(0x39, 0x00);
    interrupts();
    
    // Wait until RXIF is cleared (takes a while)
    while(mrf.read_short(0x31) & 0x08);
    
    p->sequenceNumber = atoi(strtok(buf, ","));
    p->packetsSent = atoi(strtok(NULL, ","));
    p->packetsReceived = atoi(strtok(NULL, ","));
    p->rssi = atoi(strtok(NULL, ","));
    p->lqi = atoi(strtok(NULL, ","));
  
    return true;
  }
  
  return false;
}

When I opened serial monitor for each board, this is what I got:

Local:
  Packets sent: 96
  Packets received: 0
  Packet loss: -
  RSSI: 0
  LQI: 0
Remote:
  Packets sent: 0
  Packets received: 0
  Packet loss: -
  RSSI: 0
  LQI: 0

The Questions

Is my setup incorrect or unfeasible? Is there a way to modify the SPI pins used by the SPI library in the ChannelScanner code to match the pins I use without changing base definitions of the entire library (I cannot change the design of my board at this point without significant work)? How can I see whether the modules are in fact sending data?

I am quite inexperienced with wireless communication, so any advice is invaluable!

Thanks in advance!!!

Hello Kvincent,

I am sorry I have just saw your post from the last year... Did you succeed already establish the communication between your devices?

I keep developing a small sensor network project based on MRF24J40MA and Arduino. I have already created a number of experiments including MQTT-SN protocol implementation and also low power modes of the end devices.

If you are interested you can check my project on the following links. Also I am ready to help in case of any cased debug required on your project.

Hello sivanovbg,

I am having the same issue as Kvincent using the library and twoway example. Both boards will be broadcasting without being able to read each others message. If I physically disturb the board (i.e touch a cable or the board) a packet might randomly pass and be received.

I have noticed in your project you used a level shifter and capacitor for the end device but only capacitor on your gateway. I believe my issue is coming from those aspects.

Could you explain the purpose of the level shifter and capacitor? I would have thought the arduino providing a 3v3 output would be good enough to power the RF module. Or is it due to the other I/O, MISO, MOSI port being 5V and there too high for the RF module I/O?

I am using an arduino nano every and MRF24J40MA and the example provided with the library.

Thanks in advance for any information you may provide.

O.