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

NRF52840 Communication #878

Closed TMRh20 closed 1 year ago

TMRh20 commented 1 year ago

I recently received an XIAO BLE Sense board based on the nrf52840 and so have started working on a compatibility layer, to support ESB and general communication using the Arduino platform.

It can be found here: https://github.com/TMRh20/nrf_to_nrf To get the example working with NRF24l01+ the gettingstarted example must be modified as follows:

  1. Call radio.setChannel(7);
  2. Call radio.enableDynamicPayloads();
  3. Comment out the lines for openReadingPipe and openWritingPipe (it uses the default addresses)

Questions:

  1. Would we want to incorporate support for this device into the RF24 library?
  2. How to get auto-ack and general ESB support working including regular transmissions. I'm stumped right now how to get TX to work properly on this device.
  3. Does anyone else want to help with this? It seems like it will be quite a challenge to get ESB support fully working on Arduino

Most of the functionality is not working, but there is enough for hte NRF52 to receive payloads. It looks like the first byte of the received payload is the dynamic payload length The second byte is a counter that increments every RX After that it is just the data in the buffer

2bndy5 commented 1 year ago

I have adamantly researched this a few times (initially for CircuitPython firmware), and I would love to help!

  1. The nR52840 actually has 8 pipes and it's underlying radio FIFO management is much more abstracted from the chip's hardware. I don't think adding compatibility to RF24 lib is the best approach. I had a thought a few months ago to fork Adafruit's nRF52840 Arduino core and modify it for ESB protocol usage, but if we can make a separate Arduino lib for any nRF52840 board, then that might be more sustainable (in terms of updates from the Adafruit core or nRF5 SDK).

    There's also the question of what to do about the BLE protocol support. Theoretically, we could use the nRF5 SDK's timeslots API (dependant on the softdevice used in the Arduino core), but that may introduce problems we don't want to struggle with (at first). My thinking here is that the radio peripheral should be treated like a singleton for easy usage -- meaning only use 1 protocol or the other. I'm personally not interested in the Gazell protocol which the BBC microbit allegedly uses, but I'm not sure based on the MicroPython firmware's src for that board.

  2. According to the nRF5 SDK, auto-ack is by default always on unless the nrf_esb_config_t::selective_auto_ack (equivalent to RF24::enable_dynamic_ack()) is asserted and the nrf_esb_payload_t::noack flag is asserted for a payload.
  3. I'm well equipped (with nRF82840 dongle) to help with this as it has been a dream of mine. I know avamander mentioned this as a passing curiosity also. Imagine all the power issues we wouldn't have to contend with for the nRF52840.
TMRh20 commented 1 year ago

Cool beans!

  1. a: You might be right, this would have to be fully developed and stuff before integrating with RF24 and at that point it might not be a great idea. b: I'm not as interested in BLE support right now as ESB support, but yeah it should probably just use a set protocol.

  2. It looks to me like they implemented ESB in software in the SDK, because I'm going through the registers in the datasheet, and there is nothing like auto-ack features to enable on the radio. It looks like everything must be done manually. Am I out to lunch here?

  3. It would be really nice to have this device working! Yeah, just plug it in and you have a working radio device. No pins, no power supply struggles.

I'm going to keep working at it over the next while. I've been playing around with audio on this device as well and peripheral control seems fairly straight forward, but detailed radio functionality is largely a mystery to me so far.

2bndy5 commented 1 year ago

It looks to me like they implemented ESB in software in the SDK, because I'm going through the registers in the datasheet, and there is nothing like auto-ack features to enable on the radio

Hmm. Looks like the SDK's ESB examples assert the selective_ack flag but doesn't use the noack flag when creating the payload (using the NRF_ESB_CREATE_PAYLOAD convenience macro). I don't see any logic related to implementing the auto-ack packet in the examples' user code. I'm trying to delve into the event handlers in the SDK, and so far I don't see anything that implements the auto-ack functionality.

I haven't really explored the datasheets at all. My knowledge is currently limited to the SDK. Although, lately I've been keeping up-to-date with the nRF Connect SDK (because it is integrated with VSCode) which is a mix between the nRF5 SDK and the zephyr framework.

2bndy5 commented 1 year ago

I found some extra logic in the nRF Connect SDK about FIFO management and handling the ACK packet/payload in TX/RX mode at https://github.com/nrfconnect/sdk-nrf/blob/main/subsys/esb/esb.c

2bndy5 commented 1 year ago

Looks like the nRF5 SDK is in maintenance mode, and Nordic Semi recommends using the nRF Connect SDK. The problem with the nRF Connect SDK is that it isn't RTOS agnostic because it uses the zephyr project.

I'm not sure yet what would be easier to wrap: copied sources from the nRF5 SDK or copied sources from the nRF Connect SDK. It seems that the nRF Connect SDK would be easier to use as it is (instead of Arduino IDE), but it relies on a DAP + debug probe to program the chip. The advantage of Arduino/PlatformIO is that the USB CDC is preferred (provided the board's bootloader is already Arduino framework friendly).

So, I'm leaning toward copying sources from the nRF5 SDK and porting algorithms from the nRF Connect SDK (where applicable). Time for me to start getting my hands dirty...

TMRh20 commented 1 year ago

Aha! I see stuff like

static volatile uint32_t interrupt_flags;
static volatile uint32_t retransmits_remaining;
static volatile uint32_t last_tx_attempts;
static volatile uint32_t wait_for_ack_timeout_us;

in the code, so they did do it in software!

It looks like the radio IRQ is blocked in the Arduino platform, so we would have to come up with a different method of handling ESB etc. That's been one main challenge in trying to port over ESB functionality from the nrf5 SDK so far.

2bndy5 commented 1 year ago

Found it! auto-ack is implemented in start_tx_transaction() defined in _components/proprietary_rf/esb/nrfesb.c of the nRF5 SDK. This function is called by nrf_esb_write_payload() when (after TX FIFO is managed) the nrf_esb_config_t::tx_mode is set to NRF_ESB_TXMODE_AUTO.

The auto-ack is only sent by start_tx_transaction() when dynamic payloads are enabled. This might conflict with RF24Network as that network layer seems to exploit the nRF24L01 exception that allows auto-ack when dynamic payloads are disabled. Remember, the nRF24L01 datasheet does state that dynamic payloads are a requisite for auto-ack and never mentions why auto-ack still works with static payloads.

It is worth noting that auto-ack still works (in RF24) if I set the static payload size to the same length as the nRF Connect examples' payload length (8 bytes) even though the SDK examples' use dynamic payloads.

I have a simple PTX sample running from the nRF Connect SDK, and it works with the following settings used in the pyrf24 pkg (on my RPi2):

from pyrf24 import RF24, RF24_2MBPS
r = RF24(22, 0)
r.begin()
# use ESB module defaults
r.channel = 2
r.dynamic_payloads = True
r.data_rate = RF24_2MBPS
r.open_rx_pipe(0, b"\E7" * 5)
r.open_rx_pipe(1, b"\C2" * 5)

# optional experiment using auto-ack with a static payload size
# r.payload_size = 8
# r.dynamic_payloads = False

I also noticed that it is possible to send up to 125 byte payloads (127 if CRC is disabled) between nRF5x devices using the ESB protocol.

TMRh20 commented 1 year ago

This might conflict with RF24Network as that network layer seems to exploit the nRF24L01 exception that allows auto-ack when dynamic payloads are disabled.

I guess we'll cross that bridge when we get there. It would be pretty sweet being able to run the whole RF24 stack on the nrf52840.

If you have a PTX example working, maybe see if you can examine the payload structure to see if we need to add anything to the payload to make it work with NRF24L01+ or whatever they are doing to get PTX working. I still have yet to get transmission working from the NRF52 via Arduino platform.

I also noticed that it is possible to send up to 125 byte payloads (127 if CRC is disabled) between nRF5x devices using the ESB protocol.

Cool, I guess there won't be much need for fragmentation/reassembly :p

2bndy5 commented 1 year ago

If you have a PTX example working, maybe see if you can examine the payload structure to see if we need to add anything to the payload to make it work

Dynamic payloads need to be enabled on the nRF24 node for the auto-ack to get sent from the nRF5x PRX node. Using static payloads with auto-ack enabled (in the nRF24 PTX node) resulted in sending failure. This is likely because the ESB packet that the nRF24L01 constructs has the payload length (in the Packet Control Field) enumerated when dynamic payloads are enabled. According to the nRF24L01 datasheet,

7.3.3.1 Payload length This 6 bit field specifies the length of the payload in bytes. The length of the payload can be from 0 to 32 bytes. Coding: 000000 = 0 byte (only used in empty ACK packets.) 100000 = 32 byte, 100001 = Don’t care. This field is only used if the Dynamic Payload Length function is enabled.

Minimal pyrf24 PTX example:

from pyrf24 import RF24, RF24_2MBPS
r = RF24(22, 0)
r.begin()
# use ESB module defaults
r.channel = 2
r.dynamic_payloads = True
r.data_rate = RF24_2MBPS
r.open_tx_pipe(b"\xC2" * 5)

r.listen = False
r.write(bytes(list(range(8))))

FYI, I'm using breakpoints in the nRF Connect SDK's ESB examples to see memory snapshots (via debug probe). I haven't figured out how to flash the right Adafruit bootloader to my nRF52840 dongle yet. The examples don't seem to enable USB Serial Comms... I also have a Adafruit Feather nRF52840 board, but it doesn't seem to accept bootloader updates from Windows; I can do it from Ubuntu though (on the same PC).

2bndy5 commented 1 year ago

I'm having difficulties tracking down how the ACK (from TX FIFO) is sent in RX mode. The PRX example simply loads a payload into the TX FIFO before entering RX mode. The event handler doesn't seem to implement sending the ACK packet in example/user code nor in the SDK's esb module (SWI0_IRQ_Handler is called as the event gets forwarded to user code).

On a side note, there's no difference between the steps to send an ACK payload vs sending an empty ACK packet; an ACK payload is simply discarded with the packet by the nRF24L01 if the ACK payloads feature is disabled. We might have to implement a life cycle for ACK payloads (similar to the nRF24L01 behavior) as the example simply marks the singular ACK payload for reuse.

TMRh20 commented 1 year ago

I think it would all be handled under static void start_tx_transaction() in nrf_esb.c for the SDK. I haven't looked at the NRFConnect SDK much yet.

It would seem simple, there should be a call to NRF_RADIO->TASKS_TXEN = 1; then once the radio is in TX mode and a packet ready, NRF_RADIO->TASKS_START=1; but I still can't get it working with NRF24L01.

2bndy5 commented 1 year ago

With you at the datasheet and me fumbling over the SDKs, we could scrape something together 🤣

The SDK uses something the datasheet calls "shortcuts" (more super technical terminology brought to you by Nordic nerds). See sections 6.20.6-7 of the datasheet. They essentially remove the need for idle time after ramp-up and before disable states.

I think I found the mechanism that calls start_tx_transaction() in RX mode: They use dynamic callbacks:

// These function pointers are changed dynamically, depending on protocol configuration and state.
static void (*on_radio_disabled)(void) = 0;
static void (*on_radio_end)(void) = 0;
static void (*update_rf_payload_format)(uint32_t payload_length) = 0;

// The following functions are assigned to the function pointers above.
static void on_radio_disabled_tx_noack(void);
static void on_radio_disabled_tx(void);
static void on_radio_disabled_tx_wait_for_ack(void);
static void on_radio_disabled_rx(void);
static void on_radio_disabled_rx_ack(void);

I just started trying to get the nRF52840 dongle to start working in Arduino IDE. It seems like the Adafruit core isn't overly supportive of the board. I'll be re-trying the Feather nRF52840 next (the nRF Connect SDK's "Programmer" app is much better than the nrfutil python tool).

I'm pondering a way to use the files from the nRF5 or Connect SDKs, and it would require way more than just the ESB headers since it is heavily dependent on other SDK files (some of which weren't copied into the Adafruit core)... We may end up copying only the code that we need (seems like this was done by Nordic as well when migrating from the nRF5 to the Connect SDK). I'm more interested in wrapping the algorithms provided in the SDK instead of hand-rolling them on our own (FIFO management wouldn't be easy).

TMRh20 commented 1 year ago

The shortcuts or "shorts" are defined pretty well in the datasheet although I have yet to figure out how to use them. Been focusing on learning the main functionality of the radio first.

I'm pondering a way to use the files from the nRF5 or Connect SDKs

Hehe, whatever we can get away with, but as I noted above, the main Radio IRQ is blocked in the Arduino environment, so we might have to think about doing things slightly differently.

2bndy5 commented 1 year ago

the main Radio IRQ is blocked in the Arduino environment

How so? It would be hard to expose a whatHappened() alternative in that case. I haven't really been looking at the Adafruit core's code yet. It might be that they reserve the radio IRQ for BLE ops.

TMRh20 commented 1 year ago

I loaded up the ESB RX example from the NRF5 SDK, copied a handful of files from the SDK and attempted to run it in the Arduino environment. It almost worked, but I was getting an error about Radio IRQ previously defined. I had to comment out the void RADIO_IRQHandler() function to get it running, but of course, all I could really do was grab the radio configuration.

I just tested the example again, and I have success with sending to the NRF52 at least. The example doesn't work, but it is receiving packets and sending ACKs to Arduino.

Also tested adding void RADIO_IRQHandler() to my Arduino sketch, and it compiled, so not sure what is going on exactly.

2bndy5 commented 1 year ago

shit

2bndy5 commented 1 year ago

I found a similar problem in CirPy where they just hard coded the radio for only BLE ops (barely even used the SDK's SoftDevice interface). Implementing a concurrency of protocols is no simple task, and it is the main reason that the Arduino official core doesn't expose ESB usage (citing that the Timeslots API isn't foolproof).

2bndy5 commented 1 year ago

I've had my eye on that xiao nRF52840 board because I freaking love that form factor. Which Arduino core are you using, the mbed enabled or the non-mbed enabled?

2bndy5 commented 1 year ago

Never mind, I see that they forked it from the adafruit core (& the mbed-enabled core is kind of a patch on top of that forked core).

TMRh20 commented 1 year ago

Well I managed to at least get the radio switching between transmitting and receiving successfully now, and updated the nrf_to_nrf repo with better startListening() and stopListening() functionality, but am totally confused as to why I can't receive anything on the Arduino side. The nrf52 is saying it is receiving and sending data successfully but I just can't seem to receive anything. Frustrating. There has gotta be something in the packet I need to add or remove or something.

2bndy5 commented 1 year ago

Just looking at the code, I think you need to increment the PID on every new payload.

TMRh20 commented 1 year ago

Tried that. it looks like the PID is odd for ACK and even for noAck, and they bitshift the value instead of incrementing it.

In the static void start_tx_transaction() function, the following looks pretty simple:

case NRF_ESB_PROTOCOL_ESB_DPL:
            ack = !mp_current_payload->noack || !m_config_local.selective_auto_ack;
            m_tx_payload_buffer[0] = mp_current_payload->length;
            m_tx_payload_buffer[1] = mp_current_payload->pid << 1;
            m_tx_payload_buffer[1] |= mp_current_payload->noack ? 0x00 : 0x01;
            memcpy(&m_tx_payload_buffer[2], mp_current_payload->data, mp_current_payload->length);

"Looks pretty simple" lol but it ain't workin.

2bndy5 commented 1 year ago

They increment the PID nrf_esb_write_payload().

        memcpy(m_tx_fifo.p_payload[m_tx_fifo.entry_point], p_payload, sizeof(nrf_esb_payload_t));

        m_pids[p_payload->pipe] = (m_pids[p_payload->pipe] + 1) % (NRF_ESB_PID_MAX + 1);
        m_tx_fifo.p_payload[m_tx_fifo.entry_point]->pid = m_pids[p_payload->pipe];

The bitshift is because the PID occupies the 2 MSb in S2 where the noack flag is bit 0.


The fact that they keep track of the PIDs for each pipe seems strange, but maybe the nRF5x series can transmit from any pipe.

2bndy5 commented 1 year ago

I started my own experiment in which I copied the esb module from nRF5 SDK and

  1. removed all #includes to other SDK files that aren't in the adafruit core.
    • I also removed the conenience macro function that the SDK exposese for users to create their own nrf_payload_t objects. We don't need it, and it depends on more SDK files.
    • Had to change a function-scoped variable's type from ret_code_t to uint32_t
  2. then wrote a simple sdk_to_arduino_core_bridge.h that wraps functionality in the adafruit core into VERIFY_* macro functions that the original ESB code uses.

It is compiling but I get a strange Serial is undefined error after this other error

gettingStarted.ino:12: undefined reference to `Adafruit_USBD_CDC::begin(unsigned long)'
2bndy5 commented 1 year ago

Turns out that if a nRF52xxx sketch doesn't use SPI or I2C libs, then the user needs to add the Adafruit_TinyUSB.h manually to use Serial objects. I solved this by adding the following to my experimebtal lib's header:

#ifdef USE_TINYUSB
    #include <Adafruit_TinyUSB.h>
#endif

see https://github.com/adafruit/Adafruit_nRF52_Arduino/issues/653#issuecomment-853259978


Time to start tinkering with hardware now... 🤓

TMRh20 commented 1 year ago

Looks like we are both making progress! I just got acks working this morning and committed the code to my nrf_to_nrf repo. Turns out I had it working a bit yesterday, just had to enhance the functionality a bit. Just sending of data is not working yet, but I do have bidirectional communication working now!

2bndy5 commented 1 year ago

I got slowed down by going through and wrapping as much of the RF24 API as I could. I'm about to create a repo and push it, but I haven't done any real hardware tests yet... There are a few functions I'm not sure about:

  1. I've already voiced my "babysitting" complaint about RF24's txStandby() and writeBlocking() functions (could be written by the user if we just expose the STATUS byte). My use of an IRQ handler complicates emulating this RF24 API behavior a bit. On the plus side, we could expose callbacks in the lib's IRQ handler for each individual event -- maskIRQ() and whatHappened() are no longer needed because the events aren't all tied to a single GPIO pin.
  2. power*() may require direct register manipulations (& corresponding changes to the ESB state). The nrf_esb module only has functions to disable/suspend/init the ESB protocol (all of which flush the FIFOs).
  3. reuseTx() will require a more involved approach because the nRF5 SDK's nrf_esb_reuse_pid() doesn't fully emulate the RF24::reuesTx() behavior.
  4. st**CarrierWave() -- I simply haven't had the time to research how to do this yet. I'm stealing your sample_ed() function instead of porting testRPD().

Everything else should theoretically be working, but I foresee some fine tuning (auto-ack/ack payloads especially).

Since all public API in nrf_esb module returns a error/success code, I've resorted to using uint32_t failureDetected instead of ignoring the return values.

2bndy5 commented 1 year ago

Geez, I'm not well enough versed in C. I'm now battling compiler errors because I'm calling on things that apparently are supposed to be specific to nrf_esb.c (mainly the FIFO objects). Other errors are actually related to the SDK and should be fixed upstream like 1a linked list struct (typedef) that doesn't declare its own type before declaring a pointer to the next linked list item (of the same type) or 2a macro that declares members of a struct out of order. I'm guessing the zephyr project has less pedantic default compiler checks because I didn't see these problems in the nRF Connect SDK build logs when trying the ESB examples.

I'm gunna try and get some sleep now...

TMRh20 commented 1 year ago

Hope you got some sleep. In that time I've gotten reception more reliable and ack-payloads working as well as transmitting successfully to my RPi! Just have to write some more code to tighten it all up. Together we should have this covered.

2bndy5 commented 1 year ago

Sounds great! For me, I fixed the compiler errors, but hardware tests produce no desired behavior. I think I need to delve into the IRQ implementation. I may also start writing a printDetails() that reads from the registers. For now, I think I'll just push what I have to a new repo.

WIP repo lives at https://github.com/2bndy5/Arduino_nRF5_ESB

2bndy5 commented 1 year ago

I setup my Arduino CI to compile the WIP for several nRF52xxx boards, and the Feather nRF52832 has problems with commands in the sample_ed():

  /home/runner/Arduino/libraries/Arduino_nRF5_ESB/src/nRF5_ESB.cpp: In member function 'uint8_t nRF5_ESB::sample_ed()':
  /home/runner/Arduino/libraries/Arduino_nRF5_ESB/src/nRF5_ESB.cpp:319:16: error: 'struct NRF_RADIO_Type' has no member named 'TASKS_EDSTART'; did you mean 'TASKS_START'?
    319 |     NRF_RADIO->TASKS_EDSTART = 1; // Start
        |                ^~~~~~~~~~~~~
        |                TASKS_START
  /home/runner/Arduino/libraries/Arduino_nRF5_ESB/src/nRF5_ESB.cpp:320:23: error: 'struct NRF_RADIO_Type' has no member named 'EVENTS_EDEND'; did you mean 'EVENTS_END'?
    320 |     while (NRF_RADIO->EVENTS_EDEND != 1) {
        |                       ^~~~~~~~~~~~
        |                       EVENTS_END
  /home/runner/Arduino/libraries/Arduino_nRF5_ESB/src/nRF5_ESB.cpp:324:22: error: 'struct NRF_RADIO_Type' has no member named 'EDSAMPLE'; did you mean 'RSSISAMPLE'?
    324 |     val = NRF_RADIO->EDSAMPLE;                             // Read level
        |                      ^~~~~~~~
        |                      RSSISAMPLE
2bndy5 commented 1 year ago

A quick look at the datasheet, and I see the nRF52832 does not support IEEE 802.15.4 protocol. Thus sampling the Energy Detection (ED) per IEEE 802.15.4 standards is not supported in the nRF52832 chip's registers.

We should probably explore the reverse engineering tactics that RF24 scanners use to get nRF52's RSSI data from ambient noise "packets".

TMRh20 commented 1 year ago

We should probably explore the reverse engineering tactics that RF24 scanners use to get nRF52's RSSI data from ambient noise "packets".

There is some options to take an RSSI sample in the registers as well, though I haven't played with it yet. Should be pretty interesting to see what the RF52s can do when it comes to scanning etc.

2bndy5 commented 1 year ago

FYI, changing the channel may require dropping out of active TX/RX mode to put the radio in idle state. The ESB API asserts that the the radio is not busy when calling nrf_esb_set_rf_channel()

2bndy5 commented 1 year ago

I'm going to have to brush up on the datasheet more. It seems the Arduino core enables SWI1_IRQn for BLE use, then creates/schedules a task to to be executed by the FreeRTOS.

On a side note, I left my Feather nRF52840 board plugged in all night, and Windows logged a power surge on the USB port while I slept; the builtin LED was pulsing light red when I woke up. I never saw this happen before, so I'm skeptic about calling NRF_POWER->DCDCEN = 1.

TMRh20 commented 1 year ago

Yeah I think most settings and config should be done from the disabled state. The radio needs to go into a disabled state when switching from RX <-> TX as well per the image below: image

I don't think calling DCDCEN is necessary.

TMRh20 commented 1 year ago

nrf_to_nrf is now working with the entire RF24 comm stack!

RF24Network and up need some minor modifications, unless there is a way to alias nrf_to_nrf as the RF24 class or something but I can't figure out how to do that so far, or know if its possible?

TMRh20 commented 1 year ago

Also tried out your repo and getting an include error about missing "verify.h". Are there some files you forgot to include? I'd like to try it out.

2bndy5 commented 1 year ago

RF24Network and up need some minor modifications, unless there is a way to alias nrf_to_nrf as the RF24 class or something but I can't figure out how to do that so far, or know if its possible?

If not including srcs from RF24 lib, then you could typedef nrf_to_nrf RF24. Otherwise, I don't think it's possible if RF24 class is already defined, and the network (& mesh?) layer currently relies on RF24_config.h (mostly for logging reasons).

tried out your repo and getting an include error about missing "verify.h"

verify.h is specific to the adafruit core. This is strange because I test compiled the src on the seed studio fork without problems.

2bndy5 commented 1 year ago

Well, its official I toasted my Feather nRF52840. My USB C -> mini USB adapter actually melted over night (when the "power surge" happened); I just didn't see it until I touched the board to reset it into the bootloader. My guess is that the asserting NRF_POWER->DCDCEN had a negative affect on the onboard battery charging circuit (which is tied into the USB power).

I'm sooooo glad my PC is earth grounded! If this was on my laptop, I'd probably have to tape off the USB port as well 😥. Off to buy another Feather 840 because the DAP plug (very useful) is already soldiered on, and I want to make sure this doesn't happen again when the board has a battery-charging circuit tied to USB power.

2bndy5 commented 1 year ago

Oh but wait, I just realized I also have an old Particle Xenon sitting around... 🤣 They're pretty much identical to the Feather 840 Express but the NFC antenna is wired slightly different (has a dedicated connector onboard).

Who else would just have nRF boards sitting around that they forgot about? 🤦🏼‍♂️

2bndy5 commented 1 year ago

Even more good news! I was able to rescue my Feather 840 via DAP and a different USB cable. My guess it that the wires inside the previous USB cable melted together because every time I plug it in (with the particle xenon) the cable immediately gets hot. The USB port on my PC is also still usable. All in all, I'm down a USB cable.

My first attempt to schedule a IRQ handler task with FreeRTOS basically bricked the board 🤷🏼‍♂️ ... I'm going to have to spend even more time figuring out how to use the SWI with FreeRTOS.

TMRh20 commented 1 year ago

At least you're just down a USB cable! Hehe thats been my experience too with IRQ handling. Going to have to delve into it soon...

I had my XIAO board running all night with RF24Ethernet and its still running well! Just had to sort out CRC error handling and now it looks rock-solid.

2bndy5 commented 1 year ago

That's impressive! Implementing FIFOs wouldn't be hard, but the ISR is proving difficult. I've been comparing the bluefruit lib (shipped with the core) with the FreeRTOS docs, but there are so many different events. And, BLE on the nRF52 family relies on the SDK's SoftDevice API...

TMRh20 commented 1 year ago

The RSSI functionality of the radio is pretty simple I find, it gives a direct value from 0 to -127 in dBm so I had put a simple scanner example into my library based on the RF24 example.

I'm also pondering whether or not to update the higher layer libraries to accept nrf_to_nrf lib. Should I wait to see how your library develops or do you see value in supporting both?

2bndy5 commented 1 year ago

I can see pros for each approach. The dream I had envisioned so long ago was feature rich, and I suspect users might presume that all features available to the nRF24 would be made usable on the nRF52. Although, your approach is so minimal, it could be considered "ESB lite" because I imagine it is less than

Sketch uses 52592 bytes (6%) of program storage space. Maximum is 815104 bytes. Global variables use 8284 bytes (3%) of dynamic memory, leaving 229284 bytes for local variables. Maximum is 237568 bytes.

If the API is identical to RF24, then it should be relatively easy with a pointer to RF24Network::radio. I have also considered a polymorphic approach, but that would require some base class to act as an abstracted API declaration in RF24 lib.

Doesn't RF24Network use txStandby() in the event of an initial TX failure?

TMRh20 commented 1 year ago

Cool, I'll look at submitting the required changes to the libs soon. I'll have to figure how what defines to use or if there is a generic NRF52 define instead of specific defines for each device like 52840 etc.

RFNetwork does use txStandby, but I've imitated its use by just calling a regular write, so it functions ok, but not exactly as the 24l01.

2bndy5 commented 1 year ago

if there is a generic NRF52 define instead of specific defines for each device like 52840 etc

Check out the core's nordic/nrfx/mdk files. Not sure what exactly you're looking for though.

TMRh20 commented 1 year ago

I think I can just use #if defined ARDUINO_ARCH_NRF52840 || defined ARDUINO_ARCH_NRF52833 thats not too bad, how many of these devices have radios is kind of my question? I think these two should pretty much cover the main RF52 boards?

2bndy5 commented 1 year ago

Oh, the core uses SDK defines for various models: NRF52840_XXAA, NRF52832_XXAA.

the 52832 requires a lot of workarounds for addresses set to the registers An excerpt from `nrf_esb_set_address_length()`: ```c #ifdef NRF52832_XXAA uint32_t base_address_mask = length == 5 ? 0xFFFF0000 : 0xFF000000; if ((NRF_FICR->INFO.VARIANT & 0x0000FF00) == 0x00004200) //Check if the device is an nRF52832 Rev. 1. { /* Workaround for nRF52832 Rev 1 Errata 107 Check if pipe 0 or pipe 1-7 has a 'zero address'. Avoid using access addresses in the following pattern (where X is don't care): ADDRLEN=5 BASE0 = 0x0000XXXX, PREFIX0 = 0xXXXXXX00 BASE1 = 0x0000XXXX, PREFIX0 = 0xXXXX00XX BASE1 = 0x0000XXXX, PREFIX0 = 0xXX00XXXX BASE1 = 0x0000XXXX, PREFIX0 = 0x00XXXXXX BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXXXX00 BASE1 = 0x0000XXXX, PREFIX1 = 0xXXXX00XX BASE1 = 0x0000XXXX, PREFIX1 = 0xXX00XXXX BASE1 = 0x0000XXXX, PREFIX1 = 0x00XXXXXX ADDRLEN=4 BASE0 = 0x00XXXXXX, PREFIX0 = 0xXXXXXX00 BASE1 = 0x00XXXXXX, PREFIX0 = 0xXXXX00XX BASE1 = 0x00XXXXXX, PREFIX0 = 0xXX00XXXX BASE1 = 0x00XXXXXX, PREFIX0 = 0x00XXXXXX BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXXXX00 BASE1 = 0x00XXXXXX, PREFIX1 = 0xXXXX00XX BASE1 = 0x00XXXXXX, PREFIX1 = 0xXX00XXXX BASE1 = 0x00XXXXXX, PREFIX1 = 0x00XXXXXX */ if ((NRF_RADIO->BASE0 & base_address_mask) == 0 && (NRF_RADIO->PREFIX0 & 0x000000FF) == 0) { return NRF_ERROR_INVALID_PARAM; } if ((NRF_RADIO->BASE1 & base_address_mask) == 0 && ((NRF_RADIO->PREFIX0 & 0x0000FF00) == 0 ||(NRF_RADIO->PREFIX0 & 0x00FF0000) == 0 || (NRF_RADIO->PREFIX0 & 0xFF000000) == 0 || (NRF_RADIO->PREFIX1 & 0xFF000000) == 0 || (NRF_RADIO->PREFIX1 & 0x00FF0000) == 0 ||(NRF_RADIO->PREFIX1 & 0x0000FF00) == 0 || (NRF_RADIO->PREFIX1 & 0x000000FF) == 0)) { return NRF_ERROR_INVALID_PARAM; } } #endif ```

Technically, the adafruit core only supports 52840 and 52832 because those are the only chips used by the supported boards. IDK if there is a core for nRF51 devices...