NRF24L01+ - stm32duino (blue pill) sender to micropython (ESP32) receiver not working

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
icecold
Posts: 3
Joined: Fri Jun 14, 2019 6:29 am

NRF24L01+ - stm32duino (blue pill) sender to micropython (ESP32) receiver not working

Post by icecold » Fri Jun 14, 2019 6:50 am

So I flashed micropython on two ESP32 boards and tried the nrf24l01+ driver and test in the official libraries. This worked at the first go without a hitch. I'm trying to now switch to a sender using a blue pill board (STM32F103C8T6) running stmduino and this nrf library https://github.com/MarkKharkov/RF24 - as I need to run the sender on battery and esp32 is way too power hungry.

however, this hasn't worked at all for me yet. The micropython side running the slave just keeps waiting. On the arduino blue pill side, all I see are send failures. I've tried to match up all the params of the radio based on the constructor code in micropython and ardunio.

Has anyone used arduino/micropython comms with NRF24L01+ successfully? If so, could you please post details of your setup and any obvious gotchas I might be missing. Code for both micropython and ardunio below.

ESP32 receiver:

Code: Select all

"""Test for nrf24l01 module.  Portable between MicroPython targets."""

import sys
import ustruct as struct
import utime
from machine import Pin, SPI, resetWDT
from nrf24l01 import NRF24L01
from micropython import const

# Slave pause between receiving data and checking for further packets.
_RX_POLL_DELAY = const(15)
# Slave pauses an additional _SLAVE_SEND_DELAY ms after receiving data and before
# transmitting to allow the (remote) master time to get into receive mode. The
# master may be a slow device. Value tested with Pyboard, ESP32 and ESP8266.
_SLAVE_SEND_DELAY = const(10)

if sys.platform == 'pyboard':
    cfg = {'spi': 2, 'miso': 'Y7', 'mosi': 'Y8', 'sck': 'Y6', 'csn': 'Y5', 'ce': 'Y4'}
elif sys.platform == 'esp8266':  # Hardware SPI
    cfg = {'spi': 1, 'miso': 12, 'mosi': 13, 'sck': 14, 'csn': 4, 'ce': 5}
elif 'esp32' in sys.platform:  # Software SPI
    cfg = {'spi': 1, 'miso': 12, 'mosi': 13, 'sck': 14, 'csn': 15, 'ce': 27}
else:
    raise ValueError('Unsupported platform {}'.format(sys.platform))

pipes = (b'\xf0\xf0\xf0\xf0\xe1', b'\xf0\xf0\xf0\xf0\xd2')
nrf = None
def initspi():
    global nrf
    if nrf:
        return

    didInit = True
    csn = Pin(cfg['csn'], mode=Pin.OUT, value=1)
    ce = Pin(cfg['ce'], mode=Pin.OUT, value=0)
    spi = SPI(cfg['spi'], sck=Pin(cfg['sck']),
              mosi=Pin(cfg['mosi']), miso=Pin(cfg['miso']))
    nrf = NRF24L01(spi, csn, ce, payload_size=8)

def master():
    initspi()
    nrf.open_tx_pipe(pipes[0])
    nrf.open_rx_pipe(1, pipes[1])
    nrf.start_listening()

    num_needed = 16
    num_successes = 0
    num_failures = 0
    led_state = 0

    print('NRF24L01 master mode, sending %d packets...' % num_needed)

    while num_successes < num_needed and num_failures < num_needed:
        # stop listening and send packet
        nrf.stop_listening()
        millis = utime.ticks_ms()
        led_state = max(1, (led_state << 1) & 0x0f)
        print('sending:', millis, led_state)
        try:
            nrf.send(struct.pack('i', millis))
        except OSError:
            pass

        # start listening again
        nrf.start_listening()

        # wait for response, with 250ms timeout
        start_time = utime.ticks_ms()
        timeout = False
        while not nrf.any() and not timeout:
            if utime.ticks_diff(utime.ticks_ms(), start_time) > 250:
                timeout = True

        if timeout:
            print('failed, response timed out')
            num_failures += 1

        else:
            # recv packet
            got_millis, = struct.unpack('i', nrf.recv())

            # print response and round-trip delay
            print('got response:', got_millis, '(delay', utime.ticks_diff(utime.ticks_ms(), got_millis), 'ms)')
            num_successes += 1

        # delay then loop
        utime.sleep_ms(250)

    print('master finished sending; successes=%d, failures=%d' % (num_successes, num_failures))
    nrf.stop_listening()
    nrf.powerDown()

def slave():
    initspi()
    
    nrf.open_tx_pipe(pipes[1])
    nrf.open_rx_pipe(1, pipes[0])
    nrf.start_listening()

    print('NRF24L01 slave mode, waiting for packets... (ctrl-C to stop)')

    while True:
        resetWDT()
        if nrf.any():
            while nrf.any():
                buf = nrf.recv()
                millis = struct.unpack('i', buf)
                led_state = 0
                print('received:', millis, led_state)
                for led in leds:
                    if led_state & 1:
                        led.on()
                    else:
                        led.off()
                    led_state >>= 1
                utime.sleep_ms(_RX_POLL_DELAY)

            # Give master time to get into receive mode.
            utime.sleep_ms(_SLAVE_SEND_DELAY)
            nrf.stop_listening()
            try:
                nrf.send(struct.pack('i', millis))
            except OSError:
                pass
            print('sent response')
            nrf.start_listening()

try:
    import pyb
    leds = [pyb.LED(i + 1) for i in range(4)]
except:
    leds = []

print('NRF24L01 test module loaded')
print('NRF24L01 pinout for test:')
print('    CE on', cfg['ce'])
print('    CSN on', cfg['csn'])
print('    SCK on', cfg['sck'])
print('    MISO on', cfg['miso'])
print('    MOSI on', cfg['mosi'])
print('run nrf24l01test.slave() on slave, then nrf24l01test.master() on master')


Blue pill sender (arduino)

Code: Select all


#include "WProgram.h"
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <stdio.h>

//
// Hardware configuration
//
#define PIN_START PB9
int CE_PIN = PB0;
int CSN_PIN = PA4;

// SPI Class for communication with SPI

SPIClass SPI_1(1);
RF24 radio(SPI_1, CE_PIN, CSN_PIN);

// Radio pipe addresses for the 2 nodes to communicate.
const uint64_t pipes[2] = {0xF0F0F0F0E1, 0xF0F0F0F0D2};

void setup(void)
{
    delay(5000);

    Serial.begin(9600);
    pinMode(LED_BUILTIN, OUTPUT);

    SPI_1.begin();
    SPI_1.setDataMode(SPI_MODE0);
    SPI_1.setBitOrder(MSBFIRST);

    pinMode(PIN_START, INPUT_PULLDOWN);
    delay(5);
    while (!digitalRead(PIN_START))
    {
        Serial.println("Pull pin PB9 high to start");
        delay(1000);
    }
    radio.begin();

    radio.setChannel(46);
    radio.setDataRate(RF24_250KBPS);
    radio.setPALevel(RF24_PA_MAX);
    radio.setRetries(6, 8);
    radio.setPayloadSize(8);

    radio.setPayloadSize(8);

    radio.openWritingPipe(pipes[0]);
    radio.openReadingPipe(1, pipes[1]);

    radio.startListening();

    //
    // Dump the configuration of the rf unit for debugging
    //

    radio.printDetails();
}

void toggleLED()
{
    digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
}

void loop(void)
{
    toggleLED();

    // First, stop listening so we can talk.
    radio.stopListening();

    // Take the time, and send it.  This will block until complete
    unsigned long time = millis();
    printf_P("Now sending %lu...", time);
    bool ok = radio.write(&time, sizeof(unsigned long));
    if (ok)
        Serial.println("ok...\r\n");
    else
        Serial.println("failed.\r\n");
    // Now, continue listening
    radio.startListening();

    // Wait here until we get a response, or timeout (250ms)
    unsigned long started_waiting_at = millis();
    bool timeout = false;
    while (!radio.available() && !timeout)
        if (millis() - started_waiting_at > 200)
            timeout = true;

    // Describe the results
    if (timeout)
    {
        Serial.println("Failed, response timed out.\r\n");
    }
    else
    {
        // Grab the response, compare, and send to debugging spew
        unsigned long got_time;
        radio.read(&got_time, sizeof(unsigned long));

        // Spew it
        printf_P("Got response %lu, round-trip delay: %lu\r\n", got_time, millis() - got_time);
    }

    toggleLED();

    // Try again 1s later
    delay(1000);
}

User avatar
jimmo
Posts: 664
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia

Re: NRF24L01+ - stm32duino (blue pill) sender to micropython (ESP32) receiver not working

Post by jimmo » Fri Jun 14, 2019 10:54 am

So just to make sure I understand -- you had:

Code: Select all

ESP32 A (uPy)  <--SPI-->  NRF24  <--radio-->  NRF24 <--SPI-->  ESP32 B (uPy)
(Which worked fine other than power usage -- does any of the sleep modes help you here?)

Now you have:

Code: Select all

Blue Pill (Arduino)  <--SPI-->  NRF24  <--radio-->  NRF24 <--SPI-->  ESP32 B (uPy)
Because this is the MicroPython forum, I feel compelled to suggest ditching the Arduino bit :) Unfortunately there's no STM32F1 MicroPython port, but there are lots of other great STM32 options.

Do you have a signal analyser or oscilloscope by any chance? Any way to inspect the SPI data from the Blue Pill to the NRF? (And compare it to the ESP32 to NRF?)

icecold
Posts: 3
Joined: Fri Jun 14, 2019 6:29 am

Re: NRF24L01+ - stm32duino (blue pill) sender to micropython (ESP32) receiver not working

Post by icecold » Fri Jun 14, 2019 12:05 pm

Yes - that's the setup with both ESP32 running uPy

For part 2 - yeah - Blue Pill with arduino.

I've explored the sleep modes for ESP32 but went in favour of the blue pill. Unfortunately I don't have a signal analyzer to inspect SPI data.

User avatar
jimmo
Posts: 664
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia

Re: NRF24L01+ - stm32duino (blue pill) sender to micropython (ESP32) receiver not working

Post by jimmo » Fri Jun 14, 2019 1:55 pm

Are the send failures at the radio level or the SPI level? I notice that arduino library has very little in the way to verify that any of the SPI operations succeed?

I can highly recommend Sigrok and the DSLogic Plus reviewed here https://www.youtube.com/watch?v=xZ5wKYnCNcs :)

icecold
Posts: 3
Joined: Fri Jun 14, 2019 6:29 am

Re: NRF24L01+ - stm32duino (blue pill) sender to micropython (ESP32) receiver not working

Post by icecold » Sat Jun 15, 2019 4:56 am

jimmo wrote:
Fri Jun 14, 2019 1:55 pm
Are the send failures at the radio level or the SPI level? I notice that arduino library has very little in the way to verify that any of the SPI operations succeed?

I can highly recommend Sigrok and the DSLogic Plus reviewed here https://www.youtube.com/watch?v=xZ5wKYnCNcs :)
So I've made some progress... I replaced uPy on ESP32 and replaced it with Arduino code with the RF24 library from Github. With this and the STM32, I have ping-pong working (no changes on the stm32 side). With that working, one big relief is that it rules out any hardware or SPI issues on the STM32 side.

Now I need to dig back into why it won't work with uPy.

User avatar
jimmo
Posts: 664
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia

Re: NRF24L01+ - stm32duino (blue pill) sender to micropython (ESP32) receiver not working

Post by jimmo » Sat Jun 15, 2019 11:35 am

One observation from looking at the Arduino library you're using -- not all NRF24L01 support 250kbps, so although both your Python code and Arduino sketch both explictly set it to 250kbps, in Python setting the data rate will do nothing, leaving it on the default 2Mbps. Whereas the Arduino library always forces a default of 1Mbps.

Maybe try explicitly using 1Mbps in both?

Post Reply