Sunday

Securing nRF24l01 based networks: RF24Gateway & RF24Ethernet with TLS + MQTT

 Securing nRF24l01 based networks: RF24Gateway & RF24Ethernet with TLS + MQTT

Taking steps towards IoT security

 With recent developments regarding RF24Gateway and RF24Ethernet, I've been able to put together an example that demonstrates two main things:

1. How to utilize any generic wrapper that normally interacts with standard Arduino Ethernet hardware, with RF24Ethernet

2. How to use an ESP32 and the SSLClient library to encrypt MQTT connections with TLS  

This opens things up quite dramatically, when all is taken into account. It means that simple, inexpensive, low power nRF24 radios can be used to create secure IoT networks, making use of the full capabilities of the RF24 Communication Stack, including the Mesh features.   

The RF24Ethernet library, as of this writing, is still being updated with the new example, so expect a release soon. I needed to get a change put into the SSLClient library to support RF24Ethernet, and since they fixed the problem very quickly, I can adapt the example.

The RF24Gateway library and Linux devices require no changes, except maybe allowing the 8883 port used by MQTT for TLS connections in your firewall. 

The RF24Ethernet library requires the installation of the Govorox SSL client if installing via Arduino Library Manager, else digitaldragon/SSLClient on PlatformIO.

It also requires the MQTT server to be set up with certificates and a suitable configuration to support authentication and encryption. This is setup as-is standard for any MQTT server using TLS.

Overall I am very pleased with the recent changes and testing results. I've got full radio (layer 2) encryption working with nRF52 & nRF54 devices over RF24Gateway, and now higher layer security (TLS) working over RF24Gateway as well via nRF24l01 radios.

  

This is just one example, again, any library that interfaces with a standard Ethernet client should work with RF24Ethernet. For example, the ArduinoBearSSL library can be used with an STM32F411 Black Pill to achieve the same result. As of this writing, there is a bug in the BearSSL library preventing things from working fully. To work around the issue, see the following modifications to the initialization of objects.

EthernetClient ethClient;
class BearSSLClientQuickStop : public BearSSLClient {
public:
  BearSSLClientQuickStop(Client &net, const br_x509_trust_anchor *tas, int numTAs)
    : BearSSLClient(net, tas, numTAs), _net(net) {}
  void stop() override {
    _net.stop();
    BearSSLClient::stop();
  }
private:
  Client &_net;
};
// TLS layer on top of the RF24Ethernet TCP client, verified against TAs from secrets.h
BearSSLClientQuickStop sslClient(ethClient, TAs, (size_t)TAs_NUM);

 

The above changes basically forces the underlying client to close its connection before the SSL Client, preventing a hang. See https://github.com/arduino-libraries/ArduinoBearSSL/issues/68

 See the full example at https://github.com/TMRh20/Sketches/tree/master/RF24Ethernet/Sketches/MQTT_BearSSL 

Monday

Supporting Encryption on RF24Gateway with Arduino based nRF52x/nRF54x devices

 Supporting Encryption on RF24Gateway with Arduino based nRF52x/nRF54x devices

Security for your IoT networks 

 Since the nRF52x and nRF54x devices support Authenticated AES/CCM encryption, I decided to play around a bit and see if I could get it working with RF24Gateway. After a little while playing around with Claude, I was able to put together code that allows fully encrypted and authenticated radio networks using the newer nRF devices.  



 The nRF24 radios only support 32-byte payloads, but RF24Network allows fragmentation & reassembly of those packets. Encryption overhead is 12-bytes, RF24Network overhead is 8-bytes, so that leaves us with only 12-bytes per packet of actual data on the Gateway. This is OK for sending very small packets, and it works well with RF24Ethernet, due to the capabilities of RF24Network (frag & reassembly).

Changes are being made to all the supporting libraries to include these features, including the RPi installer script. Even if encryption support is enabled, users can still run unauthenticated networks by not initializing encryption. To allow encryption, see the RF24Gateway installer, along with the Encryption examples included with nrf_to_nrf.

Changes On the Gateway:  

 A: Add the include

#include <RF24Network/RF24CCM.h> 

 B: Change the constructor and add a key

RF24CCM radio(22, 0);
ESBNetwork<RF24CCM> network(radio);
ESBMesh<ESBNetwork<RF24CCM>, RF24CCM> mesh(radio, network);
ESBGateway<ESBMesh<ESBNetwork<RF24CCM>, RF24CCM>, ESBNetwork<RF24CCM>, RF24CCM> gw(radio, network, mesh);

uint8_t encryptionKey[RF24CCM_KEY_SIZE] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}; 

C: Before begin, call

 radio.setKey(encryptionKey);
 radio.enableEncryption = true;

 

Changes to the nrf_to_nrf examples:

A: Add key 

 uint8_t myKey[RF24CCM_KEY_SIZE] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};

 B: Enable encryption before calling mesh.begin

   radio.setKey(myKey);

 radio.enableEncryption = true;

  Note: Don't call radio.enableDynamicPayloads(254); since we are using the default of 32-byte payloads.

 

Data is encrypted at the radio layer, so only raw radio addresses are exposed. As of this writing, these changes are not fully in yet, but will be pushed very soon. Users will be required to update most of the RF24 Comm stack, including RF24Network, RF24Mesh, RF24Gateway, and nrf_to_nrf libraries.

Tuesday

My Custom Zephyr + Arduino Build for Arduino Uno Q: Adding Zephyr Networking to the Mix

 My Custom Zephyr + Arduino Build for Arduino Uno Q: Adding Zephyr Networking to the Mix

Using RF24Ethernet with native Zephyr networking

 For anybody not paying attention (most of you probably hehe), I've put together a Zephyr + Arduino build for the Arduino Uno Q Micro-Controller, and have managed to incorporate native Zephyr Networking into the mix. This means that RF24Ethernet no longer requires external libraries like the uIP or lwIP TCP/IP Stacks for this build, it uses the built-in networking capabilities of Zephyr.

 

This was quite a challenge, but a lot easier than incorporating the lwIP stack was, mainly because I was able to use a standardized Posix Sockets API for the actual connect/disconnect & read/write functions. Underneath that layer, is the network driver, which was much more challenging to put together. I decided upon creating a dedicated driver library for handling the internals, which is kept separate from the main RF24Ethernet files, in a new drivers/net folder. 

All in all it is working very well already! I've managed to get TCP/IP, UDP and DNS functionality all working in a relatively short period of time. There is still a bunch of work to do in cleanup and testing, but so far the testing has gone extremely well, helping me to fix some inherent bugs in RF24Ethernet when using the lwIP stack.

This work has also helped me to identify problems and suggest changes to the Arduino Core and Arduino API files on GitHub, to support compilation of RF24Ethernet on the Arduino Uno Q when using App Lab or the Arduino IDE, using lwIP instead of the current uIP stack. 

For anybody adventurous that wants to try out the custom build w/RF24Ethernet and native Zephyr Networking, the files and installation instructions are located at: https://github.com/TMRh20/Sketches/tree/master/ArduinoQ/ZephyrBuild 

 

Saturday

Creating a Customized Zephyr Project + Arduino core

 Creating a Customized Zephyr Project + Arduino core

Working outside them boxes 

 I currently have 2 Arduino Uno Q boards in my possession, and figured I should use them or at least play around and experiment a bit. Since all of my initial attempts were foiled, including attempts to build the lwIP stack, I decided to develop my own Arduino core for the MCU side of things.


  The first challenges involved figuring out the initial layout of a custom build. It was surprisingly easy to setup and configure the Zephyr Project SDK and build a working Blinky example. Then I moved onto integrating the Arduino Core and API. This was way more challenging than expected, requiring me to understand the layout of the cores and how it all works together.

Then I moved onto the core Serial functionality, and it was waaay harder than it should have been. First was trying to understand all the links between the Router, the Monitor and Serial. In short, the router handles the comms between the MCU and Linux over RPC, which uses the same uart (lpuart1) as the Monitor and Serial, the difference is that messages of Serial & Monitor are labelled differently from Bridge messages. Fundamentally, they use the same protocol over RPC. They are all aliased together too.

In between attempts at figuring the Router Bridge functions out, I managed to get the LED Matrix library working! That included a big morale boost after struggling so much with the Bridge. It was easier than expected once I found out where they hid the core functions, apart from the main include files.

Then back it was to the Bridge. I found out in the begin function that a pointer was always returning NULL until I configured  CONFIG_DYNAMIC_THREAD_STACK_SIZE=2048 and related settings in my prj.conf file. Then it started to work, but got stuck again, in the begin function. I found out I had to modify another setting in my app.overlay file, to ensure the Bridge uart (lpuart1) started up immediately at boot.

 That's pretty much it for now! I think all the functionality I need is there, and I should be able to add anything that isn't. 

 I will update the repository for this code at https://github.com/TMRh20/Sketches/tree/master/ArduinoQ/ZephyrBuild very soon, to include the new features.

Sunday

Streaming Audio over WiFi using the Arduino Uno R4 WiFi, AutoAnalogAudio Library & VLC

Streaming Audio over WiFi using the Arduino Uno R4 WiFi, AutoAnalogAudio Library & VLC

 Pushing the system to its internal limits

 With recent developments regarding the AutoAnalogAudio library and the Uno R4 WiFi, I was able to get an audio stream to play on VLC over WiFi from a microphone attached to the Uno R4. This was a little bit of a challenge due to the inherent limitations of the R4, but I was able to make it function.

 In short, the attached ESP32 that handles WiFi is connected to the main RA4M1 chip via a Serial bridge, running at 115200 baud. This pretty much limits the associated audio stream to lower quality 8Khz 8-bit samples. A modification is possible, but with out-of-the-box behaviour, my tests show that 9Khz 8-bit audio is the upper limit.

I've included an example into the AutoAnalogAudio library to demonstrate this behaviour, compressing the 14-bit output of the ADC (microphone input) into 8-bit samples using PCMU encoding and sending them directly to VLC Media Player in RTP format over UDP.

 To configure the example, one would need to enter their WiFi credentials into the associated secrets.h file. This helps to establish the WiFi connection used for audio streaming to a computer. With the PC running VLC on the same WiFi network, users then need to determine the IP Address of the VLC machine and enter it into their sketch as targetIP.

Once those two things are done, just run the example with a microphone connected to pin A1 on the Arduino Uno R4 WiFi. Then open VLC and open a Network Stream at  rtp://@:5004

The audio is lower quality, only really good for voice transmission, but as stated, users can override this behaviour by modifying the ESP32 core for the Uno R4 WiFi and configuring the bridge to run at a faster speed. See the Arduino Forum post here. With a customized ESP32 Bridge sketch, and customizing the Modem.h file in the WiFiS3 library for the Uno R4 WiFi, I've achieved internal baud rates of 2MHz, allowing streaming up to 50KB/s or about 25KHz audio sampling at 16-bits.

Version 1.55.1 of the AutoAnalogAudio library has been released including support for nRF54L15 and Uno/Nano R4 boards, and can be installed via the Arduino Library Manager.

Thursday

The RF24Mesh Users Guide

 The RF24Mesh Users Guide

         How to manage & administer RF24Mesh based networks

 If you are reading this, it is assumed you are using are or interested in using the RF24 Communication stack at some level, including the RF24Mesh library. This guide should apply whether you are using RF24Mesh directly, or using the RF24Ethernet/RF24Gateway libraries and attempts to provide a good general understanding of the basic concepts RF24Mesh was built around.

  What is RF24Mesh? 

 RF24Mesh is a wireless networking library designed to automate the mundane networking tasks that don't require user intervention, including addressing assignment & reassignment, DNS style lookups and general communication tasks. The underlying libraries, RF24 and RF24Network provide many wireless networking and communication features, and RF24Mesh ties them all together with automation.

The RF24Mesh library works using pre-assigned and unique NodeIDs, which can be thought of similar to static IP addresses. These NodeIDs are statically assigned, so any individual node can be identified for communication purposes. At a lower layer, the system uses RF24Network addresses, which are dynamic, changing regularly to accommodate nodes at any level of the network. These addresses represent the physical placement in the network, and are used for direct communication after completing a NodeID lookup, which returns the RF24Network address.

The overall goal of RF24Mesh is to allow users to create and deploy wireless networks based on RF24, RF52 and/or RF54 radios, in short order, and to have them communicate on an ongoing basis, changing physical locations and/or distance as required. Networking tasks are automated to a degree that human intervention should not be required for very long periods of time.

Everything is configured around a central "Master" node, which needs to remain online and active, since it keeps track of all nodes on the network and handles addressing look-ups, which are similar to DNS requests on a purely TCP/IP based network. 

How to set it up?

Arduino: Just download and install the RF24Mesh library via the Arduino Library Manager. As mentioned above, each device must be assigned a static NodeID, but once that is done, everything else can be automated. The network will support NodeIDs ranging from 2 to 253, with nodeID 1 and 0 typically reserved for the "Master" node, which keeps track of all NodeIDs and related addresses.

Linux: See the documentation link below, there is an installer available for Linux based devices which streamlines the installation process. 

Nodes do not need to be in range of the Master node for operation, they just need to be in range of another node that does have connectivity to the Master node. Operating nodes will automatically route traffic on behalf of other nodes. The network is built in layers, with the master at layer 0, and subsequent layers 1 to 4. This means that a given node can be as far away as 4 hops from the master, with traffic routed automatically through other nodes.  

 What can I do with RF24Mesh?

 The RF24Mesh library can handle many different communication scenarios, but was built with a focus on IoT systems, allowing users to create large-scale sensor networks or command & control scenarios. When using RF52 based radios, users can incorporate encryption into their networks directly as well. When using the RF24Ethernet or RF24Gateway libraries with RF24Mesh, encryption must be implemented at a higher level, using technologies like SSL, SSH, TLS, etc.

 Users can securely and reliably receive data from multiple sensors and control devices with a simple API and interface, customizing their system to the Nth degree.

 Tips & Tricks

 When using RF24Mesh, there is a need to continuously verify connectivity and renew addresses when nodes move around or simply lose wireless connectivity, and these features are incorporated into the examples. Nodes typically verify connectivity with a direct parent node, assuming that if the parent node remains connected, it has connectivity to the Master node. 

No matter what layer users are working at, RF24Mesh or RF24Ethernet/RF24Gateway, the mesh library generally works the same. With RF24Ethernet/RF24Gateway, users can utilize standard networking tools over the wireless network, including TCP/IP, UDP etc, making use of protocols like MQTT, SSH or HTTP. The network still functions using the underlying communication protocols, allowing users to send very small RF24Mesh or RF24Network messages, while handling large TCP/IP or UDP packets at the same time.

 This configuration allows the smallest of devices, including ATTiny devices, to be configured as routing nodes, with more powerful devices like Arduino Uno, Nano, Mega etc functioning as fully operational RF24Mesh or RF24Ethernet nodes. Larger devices capable of running Linux would make use of the RF24Gateway and/or RF24Mesh libraries directly.

RF24Mesh also allocates memory on the Master node dynamically for storing NodeID & RF24Network address combinations. The default is to configure the memory space for up to 10 nodes. Users can edit the RF24Mesh_config.h file, and set  #define MESH_MEM_ALLOC_SIZE 10 to a higher number to increase the memory allocated for storing node information. This is typically recommended when running the master node on a non-linux device so that memory is not continuously being allocated and re-allocated as nodes join the network.

In the case of devices that only receive data, and don't verify connectivity that often, users can un-comment #define RF24MESH_CONN_CHECK_TYPE RF24MESH_CONN_CHECK_MASTER in the RF24Mesh_config.h file, to ensure they are verifying connectivity with the Master node, instead of just a parent node. This also prevents some temporary DOS attacks from being possible, but does increase network traffic. 

In the case of users that require static nodes, RF24Network can be used directly at the same time as RF24Mesh. On the master node, users just need to define a static address for the static nodes, and they will function as RF24Network nodes on the RF24Mesh network, but will need to remain in the same physical location/maintain connectivity themselves. 

Linux vs Arduino

The code-base for Linux devices is a little more advanced than the Arduino code-base, but functions very similarly. The main difference is that on Linux devices, the default behaviour is to save the Address list (NodeIDs and RF24Network addresses) to file periodically. On Arduino devices the list is not saved by default, but this can be implemented quite easily if desired. This is important to understand, because on reboot, a Linux based network (Linux Master Node) will resume operations using the preexisting address assignments. An Arduino based network (Arduino Master Node) will resume operations using blank address assignments, meaning every node will need to re-join the network. Typically, nodes should verify connectivity every 30 seconds or so at least, so the Master node should remain off for at least 30-seconds in that case, to ensure all nodes refresh/renew their addresses. On a Linux based master node, a file dhcplist.txt is created, and can be deleted prior to startup to force a fresh startup.

The other important difference between Linux & Arduino devices revolves around fragmentation and re-assembly of large packets. A Linux based device will cache and store fragments for each individual node, while Arduino devices will only receive and cache fragments for a single node at a time, making Linux the system of choice for operating RF24Mesh master nodes that handle very large payloads and multiple devices transmitting at the same times.  

 How to get started?

 You have to start somewhere, and I would generally recommend installing RF24Mesh directly, and testing out some of the provided examples. It helps later to have a good understanding of the basic operation and features, including the use of NodeIDs, RF24Network addresses, and the difference between the two.

Users can then move on to trying out the more complex libraries, like RF24Ethernet and RF24Gateway, which use complex underlying code to provide a simple and seamless communication system based on TCP/IP and UDP. 

 The documentation is available at https://nrf24.github.io/RF24Mesh/ 

Sunday

Audio Playback on the Uno R4 using the 12-bit DAC and the AutoAnalogAudio Library

Audio Playback on the Uno R4 using the 12-bit DAC and the AutoAnalogAudio Library

Having some fun with audio reproduction 

 Since recently receiving some Uno R4 and Uno R4 WiFi boards, I just started playing around with audio reproduction. It seems they are not directly I2S capable, but do have ADC and DAC functionality. So far, I've gotten audio playback working via the DAC on Analog Pin 0.



I attempted to get mp3 playback working as well, but the R4 just doesn't have the available memory for decoding, so it will just be wav files and similar for now.

Initial results are pretty good, playing anywhere from 8 to 16-bit audio at 16KHz to 44.1KHz from SD card with ease. I've connected an analog amplifier directly to the output pin (A0), and results are so far good.

Next up will be the ADC, then onto figuring out other methods, including possibly hacking an I2S signal and output using the SPI peripheral.

For now, the code is available in the UnoR4Support branch of the AutoAnalogAudio library, so users can switch to that branch and download the ZIP if wanting to test it out.

Thursday

Playing around with the Arduino Uno R4 Minima, Uno R4 Wifi and Uno Q

Playing around with the Arduino Uno R4 Minima, Uno R4 Wifi and Uno Q

Stepping up to a new level of micro-controller 

 

 Thanks to the fine people at Arduino, specifically Per Tillisch, an Arduino Support Engineer, I just received some free hardware! An Uno R4 Minima, Uno R4 WiFi and Uno Q!

 

Uno R4 Wifi and Uno R4 Minima
 
Now its just a matter of figuring out what to do with this stuff. I've already begun testing with the RF24 Communication Stack, and most of it works out-of-the-box. The only change required was adjusting the CPU speed detection in RF24Ethernet to accommodate the Uno R4 Wifi and Uno R4 Minima. I've put the changes in, but haven't done a release yet. Users can download the ZIP or clone the repo to install for now.
 
The R4 Minima appears pretty straight forward, with a micro-controller attached to a familiar board type, and a lot more processing power and memory than previous AVR-based designs. Not that the AVR boards are no longer useful, but its nice playing around with the newer designs and architecture. Newer boards actually tend to have a simpler interface when getting into direct register access and control, so its easier to develop libraries and applications that work with the newer boards. The R4 WiFi is more complex, with a main MCU, the Renesas RA4M1, and an ESP32 handling WiFi with a bridge between the MCUs.
 
Now I've played around with the Arduino Q quite a bit already, so will be mostly focusing on understanding and working with these new R4 boards that I have no experience with. I've gotten them working with the RF24 Comm stack as mentioned above, so it will probably be onto the AutoAnalogAudio library, to see what can be accomplished. The Arduino Q runs Linux along with the MCU, with a Serial bridge between them, but these boards are quite different. They seem to be more comparable to regular MCUs, just with enhanced functionality.
 
The boards themselves came with no strings attached, Arduino folks just sent them for free, so I can pretty much do whatever I want, with whatever time frame I want. 
 
Of course the big difference is that the R4 WiFi has an embedded ESP32 chip for WiFi in addition to the main micro-controller, so I will need some time to understand the bridge interface and whether it is worth it to explore programming the ESP32 chip directly. At first I will focus on the Renesas RA4M1 chip and explore its main capabilities, leaving the rest for later.
 
A big thanks to Arduino for these boards!

Monday

RF24Ethernet w/lwIP - Reaching a Point of Long Term Stability

 RF24Ethernet w/lwIP - Reaching a Point of Long Term Stability

Stable & Secure Communications with the RF24 Communication Stack 

Since I started on this journey integrating the lwIP TCP/IP stack into the RF24 Comm Stack, I was unsure if I would ever get to this point. Its been about 6 months since I started, and at this point I can say I am glad I did. Using lwIP really brings the stack up to standard, as previously it used only the uIP TCP/IP stack, which works well, but is no longer maintained. Now, newer micro-controllers will use the newer, up-to-date lwIP stack, while smaller, older MCUs will use the uIP stack.

 Over the past week or so, I've put in quite a lot of work on the RF24 Comm. Stack, including RF24Ethernet. In that time, it has become more secure, more stable, and is working better than it has in a long, long time.


Constant attacks & jamming targeting my networks has been taking place for years, and with the rise of AI, I decided to do some security & code reviews involving the RF24 Comm Stack. This was hugely beneficial, since I was able to work out a number of bugs and security flaws affecting the system. I would assume that most open source projects are beginning to do this as well, since there is a huge benefit from having a computer scan and verify your code.

 In the end, I was able to fix what I see as the "Final Major Issues" affecting the communication stack, from buffer overflows, to memory leaks, to a problem with millis() timer usage, involving rollover un-safe millis() calls. The system is now more resilient and stable than ever before, still susceptible when jamming overwhelms parts of the system, but able to bring itself back online, recovering all functionality as soon as the jamming stops. The constant attacks against my network have slowed significantly, with very little to no impact on the system when they do occur, since there are now less if any avenues to exploit remotely.

 I am now just running some final tests regarding the interaction of RF24Ethernet w/RF24Network when using lwIP, since there seemed to be some timing related issues with delay() vs network.update() calls, which should be addressed in the RF24Network master branch, but require testing before a release is made. These issues cause WDT resets on my ESP8266 while using WiFi in testing, but only occur once every 3-4 days or more, so tracking them down was difficult.

 All in all, I am very happy with the progress made over the last week, satisfied that my security concerns have been addressed, functionality is back to where it should be, and everything just keeps working.

Thursday

Using RF24Ethernet & RF24Gateway with the Arduino Cloud

 Using RF24Ethernet & RF24Gateway with the Arduino Cloud

 Pushing automation even further

 Recently, I was offered an Arduino Uno Q, Arduino Uno R4 Wifi and 1-year of Arduino Cloud access for free! This came as quite the surprise, because I was contacted out of nowhere by Per Tillisch, an Arduino Support Engineer, who offered me the devices and cloud access with no strings attached! I was quick to take him up on his offer. He stated it was due to my work contributing to the Arduino ecosystem.

In looking at the cloud access, it looks like it will be very simple to create dashboards etc, and integrate everything I want from my existing systems into the cloud! No code changes, no updates, no nothing. Just some simple changes in the NodeRed system I use.

With the current wireless IoT systems I am using, based on RF24Ethernet and RF24Gateway + wireless radios + MQTT, I also use NodeRed for managing all the data and interactions locally. This means that all I had to do was install the node-red-contrib-arduino-iot-cloud nodes, setup an API key, and use the newly installed nodes to communicate with the cloud!

 My system is setup so the RF24Ethernet nodes use MQTT to communicate with the RPi running RF24Gateway & NodeRed. NodeRed then handles the communication with the cloud.

See  https://docs.arduino.cc/arduino-cloud/guides/node-red/ for more information.

Right off the bat, I was able to create a simple dashboard that displays incoming Temperature, Humidity & Barometric pressure data on some charts!


 From here, it is very simple to add/remove & create new parts of the dashboard.

All of the existing Arduino devices in my network now have potential cloud access via NodeRed! I didn't have to modify any code, upload new firmware or really do much of anything! This all works with my existing systems!

The beauty of this is users can keep all their data and integrations local, logging to a database if desired etc, and still make use of the Arduino Cloud at the same time. Some data can go to the cloud, some to a local repository, and everything just seems to work in sync!

I currently have all my integrations set up via NodeRed + NodeRed Dashboard, so it is very similar already to the Arduino Cloud Dashboard, though only available locally. Using the cloud, I can now make some parts of it accessible from anywhere.

This is all very cool, I can't wait to get the new hardware! 

 

Monday

AutoAnalogAudio Library - Updated with I2S support for nRF54L15 devices

 AutoAnalogAudio Library - Updated with I2S support for nRF54L15 devices

 Bringing audio to the nRF54L15

 I was able to dabble little over the weekend, and ended up connecting an I2S amp and microphone to a nRF54L15 device. Getting it working was a little confusing at first, but the datasheet provided enough info to figure out the compatibility differences between nRF52840 and nRF54L15 devices. It was just a matter of making a few small changes.

One of the most glaring differences is with the main data pointer (NRF_I2S->RXTXD.MAXCNT). With the nRF52840 it counts by the number of 32-bit words, where the nRF54L15 counts individual bytes, so the main data handling functions needed to be modified.  

The other glaring difference is the pin layouts, but I've used the same physical layout as the 52840. That is, the physical spots on the board are the same, but the underlying pin and port numbers have changed.

 


    Per the AutoAnalogAudio library, these are how the pins are laid out by GPIO Pin / Port numbers:

    I2S_PIN_MCK = 8;
    I2S_PORT_MCK = 2;
    I2S_PIN_SCK = 5;
    I2S_PORT_SCK = 1;
    I2S_PIN_LRCK = 7;
    I2S_PORT_LRCK = 1;
    I2S_PIN_SDOUT = 11;
    I2S_PORT_SDOUT = 1;
    I2S_PIN_SDIN = 10;
    I2S_PORT_SDIN = 1;

 The changes have been pushed to the master branch as of this writing, and I will clean up the code, possibly add support for the PWM and SAADC features, and do a release soon. For now users can clone from the master branch or install from ZIP.

Saturday

The RF24 Wireless Communication Stack: Communication, Consistency & Exploits

 The RF24 Wireless Communication Stack: Communication, Consistency & Exploits

 Using RF24Ethernet & RF24Gateway for an IoT Network

 RF24Ethernet & RF24Gateway are two complimentary software libraries for Arduino that allow users to quickly design, develop & implement IoT networks that utilize TCP/IP for general communication. They use the Arduino Ethernet client/server APIs, so anyone with minimal programming experience can construct and deploy an IoT network in short order.

 


Recently, we discovered a buffer overflow in RF24Network that affected nearly the entire communication stack on Arduino devices. This buffer overflow was "externally triggered", meaning that an external user or system would need to send a certain packet type to a device to trigger the buffer overflow. It doesn't occur under "normal" operation of the system.
 
 The interesting thing about this, is that my devices have been susceptible to this overflow for a long time, and there has been near-daily attempts to disrupt my network. It seems that somebody nearby has a LOT of time on their hands, and a penchant for causing problems. 
 
The good part of this is RF24Mesh, the part of the RF24 Comm Stack that keeps things working, almost no matter what. RF24Mesh is a wireless-mesh system for the comm stack that provides addressing, DHCP type protocols, and self-healing/management of the network. With RF24Mesh involved, the network typically remains stable, despite the constant attacks.
 
  The exploit in RF24Network has now been patched, so users are encouraged to update as soon as possible.
 
 As far as the rest of the stack goes, everything is coming together with support for nRF52 and nRF54 radios, which now work with the comm stack. I have a little bit of work remaining in this regard, but basic support for these radios has been incorporated throughout the stack in the latest releases.
 


A chart of ping response times for one node on the network using NodeRed
 
 There are a lot of different uses for this system, from monitoring, to managing and collecting data from sensors, to remotely controlling devices, etc. and it is very flexible and standardized. I've been using the NodeRed system with MQTT mainly for my devices. MQTT is a standard protocol for messaging and IoT systems, so it is well suited to this type of thing.
 
 For anyone interested in wireless IoT systems, I would highly recommend checking out RF24Ethernet/RF24Gateway. You can be up and running in minutes, with a closed system using just two Arduino devices & RF24Ethernet, or a fully fledged IoT setup, using a Raspberry Pi/Linux for a gateway and a number of Arduino nodes running RF24Ethernet.

nrf_to_nrf Arduino Library: Now supports nRF54l15 devices!

 nrf_to_nrf Arduino Library: Now supports nRF54l15 devices!

New possibilities with nrf54l15 support 

 With the recent addition of an Arduino Core supporting nRF54l15 devices, I decided it was time to attempt to get the nrf_to_nrf library working with these devices. It was confusing initially, but once I worked out the specific differences in registers, things came together pretty quickly.

The main difference between these radios and nRF24 radios is the payload sizes and speeds. The nRF24 is capable of only 32-byte payloads, while the nRF52840 and nRF54l15 devices support much larger payloads. The nRF54l15 is also capable of 4Mbps transfer speeds.

The nrf_to_nrf library is designed to work out of the box between all 3 device types, but users can call 

  radio.begin();
  radio.enableDynamicPayloads(254);

prior to calling network.begin(); or mesh.begin(); functions to configure large payload sizes if setting up communication between nRF52840 and/or nRF54l15 devices. nRF24 is not compatible with sizes > 32-bytes.

I'm still working on getting encryption working on the nRF54l15 devices, and it seems to be a little more complicated than it was on the nRF52840. This might take a while to address.

The rest of the libraries, RF24, RF24Network, RF24Mesh & RF24Ethernet are being updated to accommodate the newer radios. 

 

 

Thursday

nRF24l01 radios: Long Term Stability Issues & Error Handling Enhancements

 nRF24l01 radios: Long Term Stability Issues & Error Handling Enhancements

 Creating robust, reliable networks

 We recently identified and addressed some long-term reliability issues affecting the RF24 radio library for nRF24l01 radios. The issues affect any users that make use of the write() or txStandBy() functions of the library. About a year ago, we found some issues with the radios returning "impossible" values, with one register indicating a payload is available, another saying there is not, and another saying the payload length is 0. This and similar issues appear to have affected the radio in transmitting mode as well, a fact we missed upon initial review of the previous issue. Now, a year later, we've addressed issues affecting transmission as well.

 

As of this writing, we are just completing testing and the changes have been pushed to the master branch of RF24. A new release should be available in a few days (version higher than 1.5.0) for the Arduino and PlatformIO platforms. For now, users can install from ZIP or clone the RF24 repo directly from the master branch.

This should be the final fix for stability in the RF24 platform, as we seem to have identified and implemented handling for all failure modes now.
 

Monday

RF24Ethernet v2.0 Released - lwIP IP Stack Support + Direct TCP/IP connectivity without a RPi

RF24Ethernet v2.0 Released - lwIP IP Stack Support + Direct TCP/IP connectivity without a RPi 

 Better, faster & more reliable connectivity w/lwIP

 Starting over the holidays, I began work with RF24Ethernet and the lwIP IP stack, integrating the IP stack into my existing library. Previously, and on smaller devices, the library utilized the uIP stack, which is no longer maintained, and used only on smaller devices that cannot run the lwIP stack.

This was quite a challenge, the lwIP stack functions in a lot of ways quite differently than the uIP stack, which in itself provided some challenges with integration. The RF24Ethernet library uses the lwIP stack in Mainloop (NO_SYS) mode, and lwIP uses callbacks for most of its functionality, so learning how that worked and the correct processes to use with this IP stack took some doing.  

 I was also able to adjust the code so that a Raspberry Pi or central PC is no longer required, nodes can simply run RF24Ethernet and connect between themselves via TCP/IP & UDP. I've included two examples, in the Headless examples directory, so users can try this new feature out.

 What is it? / How does it work?

 RF24Ethernet is a wireless TCP/IP communication system using nRF24 or nRF52x radios for comms. Users can create wireless mesh networks (NOT WiFi) that use much less power than WiFi networks and have many similar features, but with lower bandwidth.  

 The system mirrors the standard Arduino Ethernet API, and can function in many ways very similarly to standard Ethernet or WiFi connected devices, although over the nRF24 or nRF52 radios. See https://docs.arduino.cc/libraries/ethernet/

Essentially, you set it up, then just use TCP/IP protocols to communicate. Very little to no knowledge of the underlying APIs or code is required. 

 When a Raspberry Pi or other routing capable device is used as the master node, the system can utilize standard protocols to connect out to the internet or other parts of your network, via MQTT, HTTP etc.

 In a standard IoT setup, for example, a Raspberry Pi can run an MQTT server, with Arduino sensors connecting in and logging sensor information to a database etc.

In another IoT system, and ESP32 can connect out to Arduino devices set up as servers. The ESP32 would send and/or retrieve sensor or other information.  

How do I use it? 

 Just install the RF24Ethernet library and its dependencies via the Arduino Library Manager. One may also need to install the Arduino lwIP stack and optionally the MQTT library by Joel Gahwiler.

 Then just run the two examples in the Headless directory on some Arduinos!

 Please log any issues or problems with us at https://github.com/nRF24/RF24Ethernet/issues

 

Saturday

More with MP3 Decoding & Playback on nRF52x devices with the AutoAnalogAudio Library


 More with MP3 Decoding & Playback on nRF52x devices with the AutoAnalogAudio Library

Direct Playback of MP3s from SD card

Since designing and having some new circuit boards made for audio prototyping, I've begun testing more and more with advanced audio options, like decoding and playing MP3 files from SD Card. This is all thanks to a sponsorship from http://PCBWay.com

I've revised some previous experimental code and worked out some bugs, thanks to the simplicity of having a circuit board instead of a big ball of wires and breadboard.

The code shown below requires installation of my AutoAnalogAudio library and the microDecoder library which can both be installed from the Arduino Library Manager.

With my existing circuit board and the Feather 52840 Express or Sense boards, this code will work out of the box. It will also work with any 52840 based board. It is still in experimental stages but is working very well so far!


/*
* Requires https://github.com/TMRh20/microDecoder library
*/

#include <SD.h>
#include <AutoAnalogAudio.h>
#include "mp3.h"  // decoder
#include "pcm.h"

mp3 MP3;
pcm audio;
AutoAnalog aaAudio;


/************ USER CONFIGURATION ***************/
const char* audioFilename = "noceil.mp3";
uint8_t SD_CS_PIN = 2;  // Set this to your CS pin for the SD card/module

float volumeControl = 0.2;
#define AUDIO_BUFFER_SIZE 1152  // Should be a multiple of 64
/**********************************************/

void setup() {

  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.print("Init SD card...");
  if (!SD.begin(SD_CS_PIN)) {
    Serial.println("init failed!");
    return;
  }
  Serial.println("SD init ok");

  aaAudio.I2S_PIN_LRCK = 28;  // Use different LRCK pin for Feather Express 52840
  aaAudio.maxBufferSize = AUDIO_BUFFER_SIZE;
  aaAudio.begin(0, 2);

  playAudio(audioFilename);
}

void loop() {

  loadBuffer();

  // Control via Serial for testing
  if (Serial.available()) {
    char c = Serial.read();
    if (c == '=') {
      volumeControl += 0.1;
    } else if (c == '-') {
      volumeControl -= 0.1;
      volumeControl = max(0.0, volumeControl);
    } else if (c == 'p') {
      playAudio("atliens.mp3");
    } else if (c == 'c') {
      playAudio("dopeBoys.mp3");
    }
    Serial.println(volumeControl);
  }
}


File myFile;

void playAudio(const char* audioFile) {

  MP3.end();
  if (myFile) {
    myFile.close();
  }
  aaAudio.disableDAC();

  //Open the designated file
  myFile = SD.open(audioFile);

  if (myFile) {

    MP3.begin(myFile);
    MP3.getMetadata();
    aaAudio.setSampleRate(MP3.Fs, 1);
    Serial.println(MP3.Fs);
    aaAudio.dacBitsPerSample = MP3.bitsPerSample;
    Serial.println(MP3.bitsPerSample);
  } else {
    Serial.println("Failed to open file");
  }
}

void loadBuffer() {

  uint32_t sampleCounter = 0;
  if (myFile.available() >= AUDIO_BUFFER_SIZE) {
    for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE / 64; i++) {
      audio = MP3.decode();
      memcpy(&aaAudio.dacBuffer16[sampleCounter], audio.interleaved, 128);  // 128 bytes
      sampleCounter += 64;                                                  // 64 samples per go
    }
    for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE; i++) {
      int16_t sample = aaAudio.dacBuffer16[i];
      sample *= volumeControl;
      aaAudio.dacBuffer16[i] = sample;
    }
    aaAudio.feedDAC(0, sampleCounter);
  } else {
    aaAudio.disableDAC();
  }
}
 

 



Wednesday

Designing & 3D Printing Some Cases for My New Circuit Boards for Audio Prototyping

Designing & 3D Printing Some Cases for My New Circuit Boards 

 The beginnings of a final design

In order to put the finishing touches on my custom designed circuit boards, I figured its about time to build some cases for them. The final addition was a set of 1.5", 4-Ohm speakers I just purchased to complete the project. As shown below, iteration v1 of a case was pretty rough, just figuring out and confirming the basic measurements and form of a design. This ended up being very bulky, which would be OK for use as a tabletop speaker or intercom etc.   

 Case V1.0
Case V1.0
 
In the case shown below, V2, I opted to make it longer instead of wider. I tend to like this layout a little more. There are still a few tweaks to make, like lowering the speaker down into the case, so the USB cable will plug into the MCU without being blocked. I may add round holes for the buttons, but I kind of like the square layout, I still need to wire up the speaker, but this is how it will look. 
 
Case V1.1
Case V1.1
Once I make the necessary changes to this form, I will put the files in the associated GitHub repository here

I'm still trying to decide on a cover, whether or not I really need one, and if so, how to make it snap in and out, so it is easy to remove. I wish I would have made the PCB a little smaller, which I could probably do pretty easily by rearranging the components. All in all though, I think its pretty good for a first attempt at building a real circuit board with functional components. Thanks again to PCBWay.com for sponsoring the PCBs!

 

Friday

Initial Testing, Notes & Progress on new PCB for Wireless Audio Prototyping

 Initial Testing, Notes & Progress on new PCB for Wireless Audio Prototyping

 Testing out the new circuit boards and getting the built-in microphone working

 If you weren't following along I just received some new custom built circuit boards from PCBWay.com and have been testing things out extensively. Everything works! I even got the custom-added I2S microphone working with the nRF52840.

The boards support functionality like voice transmission over the built-in radio, and can function as a radio, intercom or simple music player etc. 



                                            A full test of the new circuit boards

Getting the Microphone working with the AutoAnalogAudio library: 

The nRF52840 Feather Express/Sense does have an I2S interface, but it only supports up to a 24-bit window, whereas the SPH0645LM4H expects 32 SCK pulses per audio frame (18 bits of data + 14 bits of padding) and a 64 x BCK signal, where the nRF52840 typically does a 48 x BCK signal.

After a bit of searching, I found that the appropriate signal could be created by the PWM peripheral of the 52840, and the I2S interface would just need to be configured with special settings to grab the 18-bits of data properly.

The main drawback with using this mode is the I2S interface can only operate in either Input or Output mode, they can't be run at the same time unless both I2S Input & Output devices use the same settings. I had to make some modifications to the AutoAnalogAudio library, released in v1.54.0, in order to allow switching between I2S modes.

Currently, this mode is only supported for input devices. 

                                                                The Feather Express 52840 

Configuration for the Microphone: 

To use the new special settings, users need to set the aaAudio.manualI2S variable to True before calling the aaAudio.begin(2,0); function.

With the Feather Express, the LRCK or WS pin needs to be configured from the default of 29, to something like 28, which is A3 on the Feather Express 52840

Results: 

 I've put together a full Radio Transceiver example for this PCB, found at https://github.com/TMRh20/FeatherAudio/tree/main/Examples/I2S_TransmitterAndReceiver

In order to use the example, upload to two Feather 52840 Express or Sense boards, and plug them into the custom PCB, or manually attach external I2S microphone & amplifier.  

The example features 3 buttons, the PTT button is used to transmit, then there are two for Volume Up/Down. If users press and hold Volume Down, then Press Volume Up, the device will go into a continuous transmission mode, not requiring the PTT button to be pressed.

The device also toggles the onboard red LED when data is received.

 Audio is relatively high quality at 16-bits and 32kHz, so it is pretty clear and useful for many different applications.

 

Securing nRF24l01 based networks: RF24Gateway & RF24Ethernet with TLS + MQTT

 Securing nRF24l01 based networks: RF24Gateway & RF24Ethernet with TLS + MQTT Taking steps towards IoT security  With recent development...