nRF24 / RF24

OSI Layer 2 driver for nRF24L01 on Arduino & Raspberry Pi/Linux Devices
https://nrf24.github.io/RF24
GNU General Public License v2.0
2.21k stars 1.02k forks source link

[Request] Support for RPi Pico #727

Closed mateo4778 closed 2 years ago

mateo4778 commented 3 years ago

Has anyone tried using this library with the new Raspberry Pi Pico and its new RP2040 chip? It seems like it should be easy, but since I have only personally used the RF24 library inside the Arduino environment I am not sure what I need to do to integrate this library into a separate C/C++ project or what type of dependencies are needed etc.

Any advice or comments on how I would go about trying to get this working would be much appreciated.

2bndy5 commented 3 years ago

Last I checked, we were still waiting for the Arduino RP2040 core to get finished (RP2040 has its own C++ SDK that Arduino folks had to use to to extend support for the IDE).

The RP2040 exposes multiple hardware SPI buses, so we may have to chew on that a bit.

mateo4778 commented 3 years ago

Yea I don't necessarily need to use the Arduino IDE for this. Or is this library written in such a way that it utilizes Arduino IDE build tool components or api?

As you said it seems easy enough to access the RP2040 hardware SPI bus so would it be as easy as adapting the RF24 library to the RP2040 SPI access syntax? Or maybe I am missing something big.

2bndy5 commented 3 years ago

I was thinking this library wouldn't need to change much if we rely on the Arduino core for RP2040.

If you want to add native platform support you could have a look at the docs about porting RF24 library to new platforms

2bndy5 commented 3 years ago

@mateo4778 Have made progress on this? Or have you also decided to wait for the official Arduino Core to be published?

I ask because I'm currently working on allowing non-primary SPI buses to be specified for the RF24 lib, but this means abstracting the SPI bus object's datatype (to use as a pointer) instead of directly using an instantiated singleton object...

kripton commented 3 years ago

Hi, I've started working on this. Current status: https://github.com/nRF24/RF24/compare/master...kripton:rpi-pico

I do have one nRF24L01+ attached. I'm using SPI0 and the GPIOs used are currently hardcoded in the SPI class (SCK on GPIO2, MOSI on GPIO3, MISO on GPIO4). CSN and CE are (as usual) given to the RF24 ctor. SPI baudrate is fixed to 250kbit/s.

I cannot say that I've proven this to work since today I just achieved the current status with one nRF24 module. There hasn't been yet a second module to receive/reply the packages. But I've attached a logic sniffer to the SPI bus and the initialization and sending (one packet each second) look fine in pulseview (using the integrated SPI and nRF24L01+ protocol decoders).

The code isn't yet beautiful or optimized and contains debugging printfs. I also had to copy the includes.h file to utilities/ since I didn't figure out how the include chain works otherwise. Please feel free to fix or improve.

And of course, thank you all for providing this awesome library. I can't wait to have multiple RPi Picos working with RF24Mesh :D

kripton commented 3 years ago

Screenshot of pulseview showing the communication between a RPi Pico (using the patched RF24 library) and an nRF24L01+: image

2bndy5 commented 3 years ago

I also had to copy the includes.h file to utilities/ since I didn't figure out how the include chain works otherwise.

the /configure bash script does auto detection of current platform, then copies the appropriate includes.h into the repo root folder.

I actually had a thought about trying to detect the SDK at compile time (not during ./configure) using the RP2040 SDK's macros that describe the SDK version (which are set using CMake). This would be similar to how other microcontroller platforms are detected (because Arduino's GCC commands include -DARDUINO or -DARDUINO_ARCH_xxx). Additionally, the inclusion of the RP2040's includes.h could be automatic within _RF24config.h:

#if defined (PICO_SDK_VERSION_MAJOR)
#include "utility/rp2040/includes.h"
#endif

There might be a better macro to use for this like PICO_PLATFORM, I'm not too familiar with the SDK (yet).

kripton commented 3 years ago

Ah, right, in the configure script. I didn't look there since as you correctly pointed out, the cmake based Pico projects compile all libraries and the SDK itself together. So I basically added the RF24 lib as a git submodule to the project. The idea with checking the defines and automatically including the correct file is great. I will try that tomorrow. I'd propose to use "rpi-rp2" for the platform.name, as done here: https://github.com/micropython/micropython/tree/master/ports/rp2 since "rp2040" designates the chips memory size as well and in future there might be a "rp2060" or so

2bndy5 commented 3 years ago

ok, now I'm excited!

did you have a chance to look over #743 so we could specify a secondary SPI bus (this feature will be merged with #750).

2bndy5 commented 3 years ago

@kripton I've added your fork to my local clone's remotes, and I'm still reading through the SDK docs... You might see a PR from me for your fork.

Also, I recently got a Feather RP2040 from Adafruit, so hopefully I'll also be able to do some testing ๐Ÿคž๐Ÿผ. Although, I don't have a fancy oscilloscope to look at the SPI pulses (yet).

Thank you for taking the lead on this. BTW, we're also trying to upgrade to CMake (#676) , but I'm not sure how useful that would be yet (concerning the Pico SDK).

2bndy5 commented 3 years ago

@kripton could you put a copy of the example your working with in a "examples_pico" folder (kinda like we have a "examples_linux" folder) on your fork? I just want to see the full breadth of your work so far. My hope is that we could use the Linux examples also for the RP2xxx if I trade all my cout usage for printf() (using CMake of course).

The SDK comes with board definitions for all the popular variants using the RP2040 chip. Targeting a board was made freaking easy using cmake -DPICO_BOARD=adafruit_feather_rp2040.

These points are just what I've got spinning in my head, and I wanted to jot them down somewhere (so you're not blindsided by a PR from me). This SDK & chip are soooo promising!! ๐Ÿ˜ I'll probably start a how-to doc for this lib when I understand everything you did to get the example code compiled.

kripton commented 3 years ago

@kripton could you put a copy of the example your working with in a "examples_pico" folder (kinda like we have a "examples_linux" folder) on your fork? I just want to see the full breadth of your work so far. My hope is that we could use the Linux examples also for the RP2xxx if I trade all my cout usage for printf() (using CMake of course).

Oh wow, you're faster than I thought :) I was also working on this to have the conditional include in RF24_config.h So to see my current usage of the library, this would be in: https://github.com/kripton/rp2040-dongle/blob/kripton-rf24/src/wireless.cpp. So it's not a complete example, it's just the bare minimal code to get it up and running.

Yes, I did see your work on the overload-begin-branch. I didn't yet jump onto it to do the Pico-enabling work since I don't know the timeframe, when you plan to merge it into main branch. But sure, it would be great to have the flexibility to configure the SPI hardware and pins used :+1:

I agree that switching RF24 to cmake would be desirable but it probably won't make much difference when using it with a rp2040-based board. Where we could take a look is how to make RF24 compatible with the link-libraries cmake-command that the pico-examples use to access TinyUSB: https://github.com/raspberrypi/pico-examples/blob/master/usb/device/dev_hid_composite/CMakeLists.txt#L8. For the moment, in my project's cmake-file, I'm referencing the cpp files directly: https://github.com/kripton/rp2040-dongle/blob/kripton-rf24/src/CMakeLists.txt#L19

Looking at your code and the board definition for the RPi Pico, I can see you're not using the default SPI pins that are defined. We should probably use these macros in SPI::begin(), and overload it with SPI::begin(hw_id, sclk, mosi, miso) which could also apply to a SoftSPI implementation (maybe even using the PIO ASM). But softSPI is way down on the list of priorities; for an example I would look to the MicroPython SPI docs.

Further comparison to the Arduino common SPI API, the SDK's spi_init() and spi_deinit() should be used to implement SPI::beginTransaction() and SPI::endTransaction, so we can still share the SPI bus with another SPI slave. The beginTransaction would have to also internally call spi_set_format() as it resembles Arduino API's SPISettings offering.

Yes, defining the SPI pins via overload definitely makes sense. Honestly, I don't know that the default pins of SPI0 are. I assume that GPIO0 and GPIO1 are UART by default, even though they also support SPI0.

Sure, the SPI changes you are proposing all make sense to me. SPI is quite new to me, I've rather used I2C in the past.

These points are just what I've got spinning in my head, and I wanted to jot them down somewhere (so you're not blindsided by a PR from me). This SDK & chip are soooo promising!! heart_eyes I'll probably start a how-to doc for this lib when I understand everything you did to get the example code compiled.

Great, I also prefer early any open communication :) Agreed, the rp2040 is amazing (especially the PIOs) and also with a very good price point.

Awesome, having an easy and well documented solution to interface with nRF24-radios would be perfect!

kripton commented 3 years ago

I pushed my latest changes to my RF24-branch and to https://github.com/kripton/rp2040-dongle/tree/kripton-rf24. Now, the includes.h is no longer required in the utility-folder and I've renamed RPi-Pico to rp2.

kripton commented 3 years ago

Also, I recently got a Feather RP2040 from Adafruit, so hopefully I'll also be able to do some testing ๐Ÿคž๐Ÿผ. Although, I don't have a fancy oscilloscope to look at the SPI pulses (yet).

Great that you also ordered a RP2040-based board :tada: About the "scope": It's actually the sigrok/pulseview open-source software + a ~$5 "logic analyzer": https://sigrok.org/wiki/Fx2lafw https://sigrok.org/wiki/Lcsoft_Mini_Board. Just search for "fx2lp" on eBay or similar shops ;)

2bndy5 commented 3 years ago

I just had an idea that might ease testing: Use Github actions to build the examples' executable binaries against the Pico SDK (for all boards defined in the SDK) and save the build artifacts (.elf, .uf2) on success.

This would help others that don't have the development environment setup locally but want to quickly test this library's examples on their RP2040-based boards!

2bndy5 commented 3 years ago

Just found out that someone wrote an unofficial ArduinoCore that uses the Pico SDK. Although, I haven't tried it...

2bndy5 commented 3 years ago

@kripton I just created an untested branch called rp2xxx based on your work. Because you aren't using the Pico board's default SPI0 pins, the code you are running will need some modification about which pins are used by the SPI bus.

The SPI frequency should now be specified from the RF24 c'tor:

RF24 radio(28, 5, 250000); // default frequency is 10000000

Based on the pin numbers your code currently uses, the modification can be either 1 of 2 ways:

  1. in your project (probably best in wireless.cpp) you can re-define the default pins
    #define PICO_DEFAULT_SPI_SCK_PIN 2
    #define PICO_DEFAULT_SPI_TX_PIN 3
    #define PICO_DEFAULT_SPI_RX_PIN 4
  2. alter the SPI.begin() call in wireless.cpp, then pass the modified SPI object to RF24::begin()

    SPI.begin(spi0, 2, 3, 4);
    
    if (!radio.begin(&SPI)) {
       printf("Radio hardware is not responding!\n");
       // ...
    }

Questions & Notes

kripton commented 3 years ago

@kripton I just created an untested branch called rp2xxx based on your work.

Wow, amaging work! I needed to do some fixes to get it compile (https://github.com/kripton/RF24/commit/1f40cb74932310a11e3c5865f09d12371f05e8a7) but well done!

  1. in your project (probably best in wireless.cpp) you can re-define the default pins
    #define PICO_DEFAULT_SPI_SCK_PIN 2
    #define PICO_DEFAULT_SPI_TX_PIN 3
    #define PICO_DEFAULT_SPI_RX_PIN 4

I tried that. gcc warned me that the values are redefined but SPI comms to the radio didn't work. Also nothing SPI was in the logic analyzer.

  1. alter the SPI.begin() call in wireless.cpp, then pass the modified SPI object to RF24::begin()

    SPI.begin(spi0, 2, 3, 4);
    
    if (!radio.begin(&SPI)) {
       printf("Radio hardware is not responding!\n");
       // ...
    }

That worked. SPI communication took place and raduo was detected (at 10MHz SPI).

Questions & Notes

Yep, that was before I attached the logic analyzer and didn't know why it failed. Probably bad cabling. By now, the command succeeds on the first call.

  • I'm guessing that you never saw any

    PicoRF24: SPI::transfernb(char tbuf, char rbuf, uint32_t len: )

    prompts in your development. It looked like the RF24.cpp SPI functions never triggered tansfers with uint8_t* buffers. You should definitely see those prompts now.

Both true. With my old code, it didn't do transactions at all. With your code, it does. Looks nice in pulseview as well :)

  • I hope its ok if I "borrow" some work from your projects' pico-build.yml to create a more generic CI build for RF24 examples (when they're written/ready).

Of course that's okay. This is why we develop Open Source: so others can also profit from what we do :)

  • The way your project includes the RF24 lib as a submodule (& given that the branch I just published is on upstream), I don't think a PR into your fork is really warranted. I could issue a PR if you want, but I'm afraid of some merge conflicts since I merged the overload-begin branch's changes with changes from your branch before making any additional changes.

Perfectly fine.

So while the code compiles, detects the module just fines and queses payloads for transmitting (looks good to my unexperienced eyes in pulseview as well), the packets are not received by an Arduino Mega running the "getting started" example. Took me a while to realize why the radio module was not detected on the Arduino: The mega has SPI on Pins 50, 51 and 52. Then, radio module wqs detected just fine but no RX reported. Next step: Have two Arduinos with the example to proof RX is working on the first place. It's a pity my RTL SDRs don't tune up to 2400MHz :(

2bndy5 commented 3 years ago

radio module was detected just fine but no RX reported.

The gettingStarted example had the payload size set to something different than 16 (actually set to 4 IIRC). need to make this setting match

Option 2 is from the overload-begin branch, so I'm glad that worked. Option 1 was from reading the SDK docs, but maybe cmake's include order would be detrimental there (evident by the gcc warning).

I'll grab your fixes into upstream before continuing. Thanks for being patient with my rookie #ifdef soup recipes; the de-referencing * and missing scope resolution (SPI::) were just things that I missed.

kripton commented 3 years ago

Just a short status update so you know where I stand:

radio module was detected just fine but no RX reported.

The gettingStarted example had the payload size set to something different than 16 (actually set to 4 IIRC). need to make this setting match

Yes, I assumed that and matched payloadsize, channel and datarate from the wireless.cpp code running on the RP2040 and the Getting started example running on the Arduino. Still no luck.

However, I tried the Getting started example with two Arduinos and 250kHz SPI clock. They both find their respective nRF24-module but don't seem to be able to exchange radio messages. So it seems that there is a more general problem than just the code for the rp2040 :(

I've also tried to get the https://github.com/BastilleResearch/mousejack / https://github.com/BastilleResearch/nrf-research-firmware running on my CrazyRadio PA dongle (nRF24LU1+). It could then be used as a scanner + sniffer. The code seems to work after some Python-3-updating (https://github.com/BastilleResearch/nrf-research-firmware/pull/28). The scanner also displays few frames and the payload looks what I would expect to be sent by the rf2040-dongle. Sadly, not nearly as much frames as expected are displayed.

Option 2 is from the overload-begin branch, so I'm glad that worked. Option 1 was from reading the SDK docs, but maybe cmake's include order would be detrimental there (evident by the gcc warning).

I'll grab your fixes into upstream before continuing. Thanks for being patient with my rookie #ifdef soup recipes; the de-referencing * and missing scope resolution (SPI::) were just things that I missed.

I've retried Option 1 (re-#define the default SPI0 pins) with the defines after the #include but it still doesn't work.

Sure, no problems about the coding woes. Most of them were easy fixes. Basically all but the missing #defines. That required some more code reading than expected.

Have you already been able to test your code on your Adafruit feather powered by the rp2040?

2bndy5 commented 3 years ago

Have you already been able to test your code on your Adafruit feather powered by the rp2040?

TBH, I took a break to return some focus to our RF24Log lib (we're trying to replace the limited printf() support that the RF24* libs use for general stdout usage). In fact, I was so excited to start this new platform support that I haven't even gotten around to soldiering the header/SWD pins to my Feather RP2040 ๐Ÿคฆ๐Ÿผโ€โ™‚๏ธ

I have been focusing more on porting the attachInterrupt() & detachInterrupt() methods to use the Pico SDK. I also briefly looked into redirecting stdout to UART instead of USB (for the same reason we're developing the RF24Log lib) which is what led me to pass on that lead (on your rpdongle project) about resetting the Pico into bootloader mode.

They both find their respective nRF24-module but don't seem to be able to exchange radio messages. So it seems that there is a more general problem than just the code for the rp2040 :(

This seems like standard troubleshooting. My usual questions to help people assess the problem would be:

  1. what capacitors are you using? Or are you using an adapter board for the nRF24 radios?
  2. What type of nRF24 module are you using? Is it the PA/LNA modules?

Both questions are concerned with power supply stability because If you're running the same code on both MCUs driving the radios, then its probably a power supply issue.

Yes, I assumed that and matched payloadsize, channel and datarate

So I see you read the COMMON_ISSUES.md file about "settings the must match", but there is other advice below that focuses on capacitors and PA/LNA modules.

2bndy5 commented 3 years ago

I've retried Option 1 (re-#define the default SPI0 pins) with the defines after the #include but it still doesn't work.

It may be better to do this on the CLI:

-DPICO_DEFAULT_SPI_SCK_PIN=2 -DPICO_DEFAULT_SPI_TX_PIN=3 -DPICO_DEFAULT_SPI_RX_PIN=4
2bndy5 commented 3 years ago

Looks like the RPi Pico board uses a RT6150B-33GQW (rated for 800mA nominal output) to regulate the USB voltage (VBus) down to 3.3V.

I soldiered the header pins to my Feather RP2040 and used the USB voltage to power my adapter board (which independently regulates 5V to 3.3V with inherent capacitors) for the nRF24L01. At first there was no power being delivered, but I think that was a wiring issue because I moved wires around and it delivered power from USB.

Initially, I tried CircuitPython firmware v6.2.0 (released yesterday) with my circuitpython_nrf24l01 library and I was able to run the gettingStarted.ino sketch on an Arduino Nano to successfully communicate with my lib's _simpletest.py example ๐Ÿ‘๐Ÿผ I intentionally wrote them to be compatible with each other (excluding library default differences like dynamic_payloads).

Next, I'll try the rp2xx branch, but I want to write a gettingStarted.cpp example to compile with CMake first... Mainly to learn about using CMake but mostly to reduce the program running on the RP2040 to minimal tasks (I'm not sure what else is going on with your rpdongle project).

ps. @kripton Does DMX stand for Digital muxer/ing (assuming it has nothing to do with the musician)? Your project's readme never explains what DMX stands for. Coming from an english tutor background its good to know your audience but bad to assume what your audience knows.

kripton commented 3 years ago

TBH, I took a break to return some focus to our RF24Log lib (we're trying to replace the limited printf() support that the RF24* libs use for general stdout usage). In fact, I was so excited to start this new platform support that I haven't even gotten around to soldiering the header/SWD pins to my Feather RP2040 ๐Ÿคฆ๐Ÿผโ€โ™‚๏ธ

Don't worry :) Working on a good common logging library is also worth a ton. Thank you :)

I have been focusing more on porting the attachInterrupt() & detachInterrupt() methods to use the Pico SDK. I also briefly looked into redirecting stdout to UART instead of USB (for the same reason we're developing the RF24Log lib) which is what led me to pass on that lead (on your rpdongle project) about resetting the Pico into bootloader mode.

Cool. Didn't yet care too much around the interrupts on the Pico. I do have some sample code running that works with interrupts to shove data via DMA to the PIO.

Yeah, that's (stdout either via hardware UART or USB) a thing that the Pico-SDK makes really easy. Just compare https://github.com/raspberrypi/pico-examples/tree/master/hello_world/serial against https://github.com/raspberrypi/pico-examples/tree/master/hello_world/usb. The https://github.com/OpenLightingProject/rp2040-dongle code needs a bit more stuff copied from the SDK since it's using custom USB identifiers and interfaces.

Yep, thanks for the lead regarding entering the bootloader :+1: Actually, in the SDK there is a specific command for that: https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-c-sdk.pdf#%5B%7B%22num%22%3A228%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C115%2C167.598%2Cnull%5D (function reset_usb_boot).

This seems like standard troubleshooting. My usual questions to help people assess the problem would be:

  1. what capacitors are you using? Or are you using an adapter board for the nRF24 radios?
  2. What type of nRF24 module are you using? Is it the PA/LNA modules? Both questions are concerned with power supply stability because If you're running the same code on both MCUs driving the radios, then its probably a power supply issue.

Indeed, I also read a lot on that lately. Answers:

  1. I didn't use any caps until today, assuming that the Pico's LDO is powerful enough for the nRF24-dongle.
  2. I have both here, trying the non-PA/LNAs first since they should consume less power

In the meantime (yesterday) I already ordered the nRF24-adapter boards so I can power the modules with 5V. They didn't yet arrive. Nevertheless I found some 85ยตF/10V-caps around here and soldered them directly onto two of the modules. Result: it's all working perfectly fine now!. In between I assumed I had counterfeit chips and the library doesn't handle them properly. But with the caps, the GettingStarted example between two Arduinos works and also sending 16 byte payload from the RP2040 to one Arduino is working like a charm! I'm so happy :D

Will you be writing a getting-started example for the rp2040 or shall I start? Just want to avoid the work being done twice in case you are already at it.

Looks like the RPi Pico board uses a RT6150B-33GQW (rated for 800mA nominal output) to regulate the USB voltage (VBus) down to 3.3V.

I soldiered the header pins to my Feather RP2040 and used the USB voltage to power my adapter board (which independently regulates 5V to 3.3V with inherent capacitors) for the nRF24L01. At first there was no power being delivered, but I think that was a wiring issue because I moved wires around and it delivered power from USB.

The Pico datasheet says that it should be safe to draw 300mA from the board's regulator: https://datasheets.raspberrypi.org/pico/pico-datasheet.pdf, chapter 2.1: "3V3 is the main 3.3V supply to RP2040 and its I/O, generated by the on-board SMPS. This pin can be used to powerexternal circuitry (maximum output current will depend on RP2040 load and VSYS voltage, it is recommended to keepthe load on this pin less than 300mA)."

Initially, I tried CircuitPython firmware v6.2.0 (released yesterday) with my circuitpython_nrf24l01 library and I was able to run the gettingStarted.ino sketch on an Arduino Nano to successfully communicate with my lib's _simpletest.py example ๐Ÿ‘๐Ÿผ I intentionally wrote them to be compatible with each other (excluding library default differences like dynamic_payloads).

Next, I'll try the rp2xx branch, but I want to write a gettingStarted.cpp example to compile with CMake first... Mainly to learn about using CMake but mostly to reduce the program running on the RP2040 to minimal tasks (I'm not sure what else is going on with your rpdongle project).

Great to hear. Yes, the rp2040-dongle does quite a bit besides RF already and it's planned to do a lot more ;)

ps. @kripton Does DMX stand for Digital muxer/ing (assuming it has nothing to do with the musician)? Your project's readme never explains what DMX stands for. Coming from an english tutor background its good to know your audience but bad to assume what your audience knows.

Right, DMX is not about the musician. It's about https://en.wikipedia.org/wiki/DMX512 (so DMX stands for digital multiplex) and used to control stage lighting mainly. The plan for the rp2040-dongle is to be connected via USB to a PC that generates the light control signals and convert them to the RS-485-based signals understood by the lighting fixtures. And in addition, wireless DMX is becoming more and more common, so why not play around with it if we can simply add a nRF24-dongle to the boards for very cheap ;) The main discussions currently happen here: https://github.com/OpenLightingProject/rp2040-dongle/discussions

You are right that the README should explain this, I will add it the coming days. Main reason it's not there is that the repo is in the context of https://github.com/OpenLightingProject / https://www.openlighting.org/. That makes it clear to people used to such things what it's about. But I fully agree that the repo itself should at least mention it.

2bndy5 commented 3 years ago

But with the caps, the GettingStarted example between two Arduinos works and also sending 16 byte payload from the RP2040 to one Arduino is working like a charm! I'm so happy :D

That's f***in awesome!!

The Pico datasheet says that it should be safe to draw 300mA from the board's regulator ... it is recommended to keepthe load on this pin less than 300mA).

Good find! I don't foresee a problem with the PA/LNA modules under a 300mA restriction.

Will you be writing a getting-started example for the rp2040 or shall I start?

Actually I put together a gettingStarted example, but got stumped on the CMake process... I'll commit what I have to rp2xxx branch.

Furthermore, in writing the example specifically for the Pico SDK, I decided to try to stick with functions native to the SDK. ie. using sleep_ms() instead of calling delay() in the examples' code. This conception led me to believe that attachInterrupt() and detachInterrupt() shouldn't be ported; its only the RF24 lib src code that needs to follow Arduino convention.

And in addition, wireless DMX is becoming more and more common, so why not play around with it if we can simply add a nRF24-dongle to the boards for very cheap ;)

What you describe sounds like an interesting project. And the nRF24Lxx is a commonly used transceiver in many manufactured solutions; I found that my Steam Controller uses a nRF24LU1 in its receiving dongle as does all Logitech unifying receiver dongles (though they switched to an even cheaper knock-off from Texas Instruments -- OTA compatible -- a few years ago).

kripton commented 3 years ago

The Pico datasheet says that it should be safe to draw 300mA from the board's regulator ... it is recommended to keepthe load on this pin less than 300mA).

Good find! I don't foresee a problem with the PA/LNA modules under a 300mA restriction.

Great to know. I will try the PA/LNA module later today or tomorrow. With and without the 85ยตF cap ;)

Actually I put together a gettingStarted example, but got stumped on the CMake process... I'll commit what I have to rp2xxx branch.

Furthermore, in writing the example specifically for the Pico SDK, I decided to try to stick with functions native to the SDK. ie. using sleep_ms() instead of calling delay() in the examples' code. This conception led me to believe that attachInterrupt() and detachInterrupt() shouldn't be ported; its only the RF24 lib src code that needs to follow Arduino convention.

Awesome, I read through the getting started and will try to compile & run it also later today or tomorrow.

Regarding integratingthe RF24 library into other projects: In order to use the target_link_libraries method in the project's CMakeLists.txt file, the RF24 library needs to declare this with the add_library call. See https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/tinyusb/CMakeLists.txt#L76 for an example. If I find the time, I will play around with that so the not all .cpp files need to be listed in the cmake file of a project using the library. Some possibly interesting documentation: https://cmake.org/cmake/help/latest/command/target_link_libraries.html

What you describe sounds like an interesting project. And the nRF24Lxx is a commonly used transceiver in many manufactured solutions; I found that my Steam Controller uses a nRF24LU1 in its receiving dongle as does all Logitech unifying receiver dongles (though they switched to an even cheaper knock-off from Texas Instruments -- OTA compatible -- a few years ago).

Indeed, the nRF24s are way more common than publicly known. I also have a stream controller at home and it seems to use the successor of the nRF24: https://www.ifixit.com/Teardown/Steam+Controller+Teardown/52578#s117511 ;)

2bndy5 commented 3 years ago

I will try the PA/LNA module later today or tomorrow. With and without the 85ยตF cap

heads-up: The RT6150B-33GQW is a switching regulator, so its highly efficient (~0.01 uA consumption) but very noisy; meaning an additional capacitor is more of an unwritten law with switching regulators. Luckily, my Feather RP2040 uses a typical BJT based voltage regulator.

Regarding integrating the RF24 library into other projects: In order to use the target_link_libraries method in the project's CMakeLists.txt file, the RF24 library needs to declare this with the add_library call. See https://github.com/raspberrypi/pico-sdk/blob/master/src/rp2_common/tinyusb/CMakeLists.txt#L76 for an example. If I find the time, I will play around with that so the not all .cpp files need to be listed in the cmake file of a project using the library. Some possibly interesting documentation: https://cmake.org/cmake/help/latest/command/target_link_libraries.html

Thank you, this was very insightful. Looking closer, the Pico SDK sets cmp0079 to NEW, and the CMake docs note that this will be the default behavior at some point in the future.

I briefly had the notion to specify platform-specific drivers (everything in the /utility folder) as separate INTERFACE libs, but it never occurred to me that RF24 could be an INTERFACE lib.

kripton commented 3 years ago

News: Build via GitHub actions is working: https://github.com/kripton/RF24/actions/runs/734164269 Now let me load the onto my RF24 to try it ;)

Here's what I did: https://github.com/nRF24/RF24/compare/rp2xxx...kripton:kripton-rp2xxx?expand=1

kripton commented 3 years ago

Mh, after flashing that .uf2-file via BOOTSEL-mode, nothing happens. In the sense of: it doesn't enumerate as a USB device at all. It could be that it already talked to the radio (I assume I need to plug it into the default SPI0oins) but I currently don't have the logic analyser attached as well. Let me see if I can fix the USB enumeration thing. I assume, the stdio_usb thingy is not being compiled in

2bndy5 commented 3 years ago

I assume, the stdio_usb thingy is not being compiled in

correct.

I assume I need to plug it into the default SPI0 pins

correct again. The default SPI0 pins are #defined in the supported boards' headers in the Pico SDK. Notice that the workflow uses v1.1.2 of the SDK because 3rd-party board support was added in v1.1.0 (your rpdongle project's workflow uses v1.0.1).

Its also worth noting that the example uses pins 7 and 8 for CE and CSN (RF24 radio(7, 8);). However, these pin numbers are temporary; we might find a set of pins for CE & CSN that are more likely to be free/unreserved for all RP2040-based boards.

kripton commented 3 years ago

Progress: USB enumeration works, I've switched the example to use stdio via USB, not via hardware UART. Currently working on USB I/O (radio number selection works, TX/RX switching seems like the last thing to get working.

Communication to nRF24-module is working. True, I had to rewire SPI and CE and CSN but not a big thing ;)

kripton commented 3 years ago

Working, pushed to https://github.com/nRF24/RF24/compare/rp2xxx...kripton:kripton-rp2xxx?expand=1 Now testing if it communicates with the default example for the Arduino ...

2bndy5 commented 3 years ago

@kripton ๐Ÿฅ‡ Have I mentioned how invaluable your work is to me?

running the artifact from your fork's latest workflow works for me!

From Arduino output:

Transmission successful! Time to transmit = 556 us. Sent: 0.14
Transmission successful! Time to transmit = 556 us. Sent: 0.15
Transmission successful! Time to transmit = 556 us. Sent: 0.16
Transmission successful! Time to transmit = 552 us. Sent: 0.17
Transmission successful! Time to transmit = 552 us. Sent: 0.18
Transmission successful! Time to transmit = 560 us. Sent: 0.19
Transmission successful! Time to transmit = 560 us. Sent: 0.20
Transmission successful! Time to transmit = 560 us. Sent: 0.21
*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK
Received 4 bytes on pipe 1: 0.21
Received 4 bytes on pipe 1: 0.22
Received 4 bytes on pipe 1: 0.23
Received 4 bytes on pipe 1: 0.24
Received 4 bytes on pipe 1: 0.25
Received 4 bytes on pipe 1: 0.26
Received 4 bytes on pipe 1: 0.27
Received 4 bytes on pipe 1: 0.28
Received 4 bytes on pipe 1: 0.29

From rp2040 (Feather) output:

Received 4 bytes on pipe 1: 0.140000
Received 4 bytes on pipe 1: 0.150000
Received 4 bytes on pipe 1: 0.160000
Received 4 bytes on pipe 1: 0.170000
Received 4 bytes on pipe 1: 0.180000
Received 4 bytes on pipe 1: 0.190000
Received 4 bytes on pipe 1: 0.200000
Received 4 bytes on pipe 1: 0.210000
*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK
Transmission successful! Time to transmit = 809 us. Sent: 0.210000
Transmission successful! Time to transmit = 774 us. Sent: 0.220000
Transmission successful! Time to transmit = 749 us. Sent: 0.230000
Transmission successful! Time to transmit = 774 us. Sent: 0.240000
Transmission successful! Time to transmit = 732 us. Sent: 0.250000
Transmission successful! Time to transmit = 726 us. Sent: 0.260000
Transmission successful! Time to transmit = 748 us. Sent: 0.270000
Transmission successful! Time to transmit = 826 us. Sent: 0.280000
Transmission successful! Time to transmit = 771 us. Sent: 0.290000
kripton commented 3 years ago

Success :partying_face:

Arduino:

--- Miniterm on /dev/ttyACM0  115200,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
RF24/examples/GettingStarted
Which radio is this? Enter '0' or '1'. Defaults to '0'
radioNumber = 0
*** PRESS 'T' to begin transmitting to the other node
Received 4 bytes on pipe 1: 0.00
Received 4 bytes on pipe 1: 0.01
Received 4 bytes on pipe 1: 0.02
Received 4 bytes on pipe 1: 0.03
Received 4 bytes on pipe 1: 0.04
Received 4 bytes on pipe 1: 0.05
*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK
Transmission successful! Time to transmit = 4056 us. Sent: 0.05
Transmission successful! Time to transmit = 12732 us. Sent: 0.06
Transmission successful! Time to transmit = 2296 us. Sent: 0.07
Transmission successful! Time to transmit = 9256 us. Sent: 0.08
Transmission successful! Time to transmit = 2304 us. Sent: 0.09
*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK

Pico board:

RF24/examples/GettingStarted
Which radio is this? Enter '0' or '1'. Defaults to '0' radioNumber = 1
*** PRESS 'T' to begin transmitting to the other node
SPI Frequency           = 10 Mhz
Channel                 = 76 (~ 2476 MHz)
RF Data Rate            = 1 MBPS
RF Power Amplifier      = PA_LOW
RF Low Noise Amplifier  = Enabled
CRC Length              = 16 bits
Address Length          = 5 bytes
Static Payload Length   = 4 bytes
Auto Retry Delay        = 1500 microseconds
Auto Retry Attempts     = 15 maximum
Packets lost on
    current channel     = 0
Retry attempts made for
    last transmission   = 0
Multicast               = Disabled
Custom ACK Payload      = Disabled
Dynamic Payloads        = Disabled
Auto Acknowledgment     = Enabled
Primary Mode            = RX
TX address              = 0x65646f4e32
pipe 0 (closed) bound   = 0x65646f4e32
pipe 1 ( open ) bound   = 0x65646f4e31
pipe 2 (closed) bound   = 0xc3
pipe 3 (closed) bound   = 0xc4
pipe 4 (closed) bound   = 0xc5
pipe 5 (closed) bound   = 0xc6
*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK
Transmission successful! Time to transmit = 16479 us. Sent: 0.000000
Transmission successful! Time to transmit = 7718 us. Sent: 0.010000
Transmission successful! Time to transmit = 2466 us. Sent: 0.020000
Transmission successful! Time to transmit = 746 us. Sent: 0.030000
Transmission successful! Time to transmit = 12901 us. Sent: 0.040000
Transmission successful! Time to transmit = 741 us. Sent: 0.050000
*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK
Received 4 bytes on pipe 1: 0.050000
Received 4 bytes on pipe 1: 0.060000
Received 4 bytes on pipe 1: 0.070000
Received 4 bytes on pipe 1: 0.080000
Received 4 bytes on pipe 1: 0.090000
kripton commented 3 years ago

@kripton 1st_place_medal Have I mentioned how invaluable your work is to me?

Thank you! Well, all the work that you, TMRh20 and all other contributors have put into the RF24*-libs is also invaluable to me! :heart: the power of Open-Source development!

And really funny that we both did the same test at the same time and both posted logs from Arduino and an rp2040-based board :rofl: . I'm really happy it works! Awesome!

I think playing around with a mesh of one RP2040 as master and two Arduinos as slaves might have to wait, it's getting quite late here. But I might check it RF24Network and RF24Mesh at least compile for the rp2040 :)

2bndy5 commented 3 years ago

On a side note: Arduino folks just released official support for the RPi Pico and their Nano RP2040 Connect via the MBED core v2.0

I doubt adafruit will fork the mbed core to supplement support for adafruit boards based on RP2040, but I could be wrong. Nonetheless, this issue (I feel) is being addressed better than the solution Arduino folks have provided.

kripton commented 3 years ago

Yes, interesting development. Didn't expect that they would use mbed to bring Arduino to the rp2040, I had rather expected a more low-level approach. But sure, as long as it works, why not :D I was (and am) rather waiting for their hardware. But honestly, I think I will still prefer the RPi Pico board due to its low cost and due to the bigger amount of free GPIOs.

2bndy5 commented 3 years ago

There was some chatter from mbed people about using both CPU cores on the RP2040, but time will tell.

2bndy5 commented 3 years ago

Quick update

I merged your changes upstream, and added more examples (still missing the IRQ example) using a foreach loop in the _/examplespico/CMakeLists.txt file. All examples compile fine, and I won't be testing any of them until I hammer out the IRQ example.

Also, I looked into why the workflow failed for Sparkfun & Pimoroni boards, and I temporarily disabled these boards in the workflow pending resolution of raspberrypi/pico-sdk#328.

kripton commented 3 years ago

Oh, great :)

One thing tough: https://github.com/nRF24/RF24/commit/85d34b11a907aa4cebb2dfd4f06fb2bf8728bd05#diff-3ba611bbbdff49fa72a050d5338dfba02d9ba9b8d403c3d346291afbc1126ad0R155 Here you removed the loop that prevented the code to return from the main-function. I do understand that this is how it's done on Arduino. However, the Arduino boards I know have a hardware USB-to-UART-chip that "always just works". The Pico board however, does the complete USB handling in hardware. So when the program returns from the main-function, the USB device will no longer enumerate, resulting in the USB-based CDC ACM terminal not working when the radio fails to initialize for some reason. I'd prefer that this fact can be told to the user via serial terminal. This is why it was solved the way it was solved ;)

Quoting https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-c-sdk.pdf, page 7:

The return code of main() is ignored by the SDK runtime, and the default behaviour is to hang the processor on exit.

Chapter 2.2 is also a nice read :+1:

And I assume the linux-port of the library also doesn't adhere to the Arduino-conventions?

2bndy5 commented 3 years ago

So when the program returns from the main-function, the USB device will no longer enumerate, resulting in the USB-based CDC ACM terminal not working when the radio fails to initialize for some reason.

oh ok. I'll adjust the examples accordingly. I do remember reading that quote from the SDK doc, but I also noticed some examples in the pico-examples repo actually used int main() instead of void main().

I assume the linux-port of the library also doesn't adhere to the Arduino-conventions?

It does; this was done for easier migration of code from Arduino to RPi (I think - I've only been on-board this nRF24 org stuff since Sep 2020). Actually, the Linux drivers (BCM27xx, WiringPi, SPIDEV, & MRAA) mimics the WiringPi library which was meant to mimic Arduino convention (using slightly different function names for some reason). Unfortunately, the WiringPi lib has gone astray in the wild (via numerous forks of an unofficial mirror of the original WiringPi lib) since the official lib was retired in Aug 2019. Did I answer/understand that question correctly?

I think the Pico SDK support is an exception to Arduino convention because those using the SDK aren't the amateurs you'd find in the Arduino realm

kripton commented 3 years ago

Also I'm trying to work on the cmake-files a bit more. So assuming the CMakeLists.txt file inside the RF24 root folder is meant to build the examples, there should be an additional cmake-file that can be included from a example's CMakeLists.txt file when the project wants to use the RF24 library but doesn't care about the examples. The problem with how it is right now is that pico_sdk_init() is being called twice. Once from the project's own CMakeLists.txt and once from the CMakeLists.txt file in RF24 that was included in the project's CMakeLists.txt.

Alternative: Make the CMakeLists.txt-file in the RF24 folder ONLY for the library and not for the examples. If someone wants to build the examples, they would have to use the CMakeLists.txt-file in the examples folder, that inlcudes the one in the root folder since the library should be used by the examples.

kripton commented 3 years ago

So when the program returns from the main-function, the USB device will no longer enumerate, resulting in the USB-based CDC ACM terminal not working when the radio fails to initialize for some reason.

oh ok. I'll adjust the examples accordingly. I do remember reading that quote from the SDK doc, but I also noticed some examples in the pico-examples repo actually used int main() instead of void main().

Great :). Yes, some examples use int main() but I guess it's just that the developers are used to doing so from the desktop-programming work. And there are (few) examples that are expected to just do their job once and halt the CPU then.

I assume the linux-port of the library also doesn't adhere to the Arduino-conventions?

It does; this was done for easier migration of code from Arduino to RPi (I think - I've only been on-board this nRF24 org stuff since Sep 2020). Actually, the Linux drivers (BCM27xx, WiringPi, SPIDEV, & MRAA) mimics the WiringPi library which was meant to mimic Arduino convention (using slightly different function names for some reason). Unfortunately, the WiringPi lib has gone astray in the wild (via numerous forks of an unofficial mirror of the original WiringPi lib) since the official lib was retired in Aug 2019. Did I answer/understand that question correctly?

Oh, interesting one! I did hear of WiringPi but never used it myself.

The alternative to looping setup() in main would be to loop in the setup-function itself. This way, the USB terminal will work and the user can be told that the radio.begin() failed while still sticking to the way Arduinos work.

I think the Pico SDK support is an exception to Arduino convention because those using the SDK aren't the amateurs you'd find in the Arduino realm

I'd assume that at least the people using the C/C++ SDK should be knowledgeable enough. The Pico board can also run MicroPython and now Arduino via mbed (which is great!). But in those cases, this library can also be used in other ways than how we are currently implementing it.

kripton commented 3 years ago

Alternative: Make the CMakeLists.txt-file in the RF24 folder ONLY for the library and not for the examples. If someone wants to build the examples, they would have to use the CMakeLists.txt-file in the examples folder, that inlcudes the one in the root folder since the library should be used by the examples.

I would prefer that, actually. Currently working on it :)

kripton commented 3 years ago

Done: https://github.com/nRF24/RF24/compare/rp2xxx...kripton:kripton-rp2xxx?expand=1 :)

This has the big advantage that (hopefully) RF24Network and RF24Mesh (which will need their own CMakeLists.txt files for the lib itself and the examples (to be written for the pico)) will find the RF24.h file

GitHub workflow might need adaption, I'm on it UPDATE: GitHub workflow updated, artifacts are being generated :+1:

2bndy5 commented 3 years ago

I still need to address #676 (& write up the IRQ example and how-to doc) before calling this support complete (at least for RF24 lib).

The RF24Network and RF24Mesh libs might be able to be imported from installed location (using so files on linux), but I don't know CMake enough propose anything substantial.

kripton commented 3 years ago

Well, at least for the rp2xxx-based boards, my proposal works fine. Of course, there is some work left to be done to make it work for other platforms but I'm happy there's progress on that as well. For rp2xxx, one would include the RF24 like that: https://github.com/kripton/rp2040-dongle/blob/kripton-rf24/src/CMakeLists.txt#L11. Basically just the same way as the CMakeLists.txt files in the examples_pico folder do in my branch.

kripton commented 3 years ago

And I just understood seconds ago what you mean with IRQ example: Making use of nRF24's IRQ out pin where it's not enough on the rp2040 to read it as GPIO but to actually make it automatically trigger a function, all while sticking to the Arduino conventions, right?

2bndy5 commented 3 years ago

I just mean to have the IRQ pin trigger progress (using an ISR callback) through the example (see the _/exampleslinux/interruptConfigure.cpp as that is my intention). There's no need to stick to Arduino convention in the _/examplespico/interruptConfigure.cpp code.

kripton commented 3 years ago

Okay, understood about the IRQ example. Do you need any information about GPIO-driven IRQs on the rp2040 or need me to test something?

I tried to rebase the rp2xxx-branch on top of the current master. However, commit 85d34b11a907aa4cebb2dfd4f06fb2bf8728bd05 gave me a major headache since according to git it's a merge commit of two of my commits but it actually contains all the additional examples that seem to get lost when I rebase. So I decided to simply merge master into rp2xxx. That lead to some merge conflicts, mainly in RP24.cpp. Result is here: https://github.com/kripton/RF24/tree/rp2xxx. git's history tree looks okay to me and the Actions also produced something: https://github.com/kripton/RF24/actions/runs/738923201 but a review might be good if we want to go ahead from that branch onwards. I've rebased by kripton-rp2xxx branch containing the CMake-structure I created yesterday: https://github.com/kripton/RF24/tree/kripton-rp2xxx

Today, I plan to add CMakeLists.txt-files to the RF24Network and RF24Mesh libraries to they can be integrated into my rp2040-dongle project as the RF24 library itself.