One of my most commonly used Raspberry Pi shields I discovered a few years ago is the 433 MHz RadioFruit RFM69HCW Transceiver Radio Bonnet from Adafruit. The RFM69HCW is a transceiver module with a wide operating frequency range, and all of its major RF communication parameters are programmable. Most of these RF parameters can be dynamically set via the transceiver's SPI interface from an external source. This transceiver has the more unique feature of programmable narrow-band and wide-band communication modes. This is in thanks to the parameters that can be dynamically set via the transceiver's SX1231 based SPI interface such as those controlling the power amplifier, filter settings, automatic frequency correction, and so on. Having a radio transceiver option like the RFM69HCW is a nice option to have along side more popular wireless options of Wi-Fi and Bluetooth because they are capable of much longer range communication. It's also much easier to get two transceivers such as the RFM69HCW communication with each other since there is no scan, pair, or associate process.
While it is possible to grab the datasheet for the RFM69HCW and start sending raw bits to the SX1231 SPI module to set the configuration and control registers of the radio, there are a couple of really good libraries that have written and proven in on a large array of platforms. The RadioHead library provides a complete object-oriented library for sending and receiving packetized messages. A CircuitPython module for the RFM69 wraps this library in a way that allows for a user to easily write Python code to send and receive packets of data with the radio.
In my last project post, I laid the ground work for porting CircuitPython over to the Zynq-7000 platform for the Zynqberry given its shared formfactor with the Raspberry Pi, and creating a 'Pi OS' for the Zynqberry. The RadioFruit RFM69HCW Transceiver Radio Bonnet also has an SSD1306 OLED, which is the same OLED on the PiOLED with the same pinout on the 40-pin GPIO header as well. Starting with the Vivado and PetaLinux projects for the Zynq hardware design and embedded Linux image I created in my last post, I'll be adding the necessary hooks to use the RFM69HCW radio bonnet and install the RFM69 CircuitPython module.
There are several options for SPI interfaces in FPGA hardware, to save programmable logic space/resources I opted to use the SPI1 interface routed via EMIO in the Zynq processing system.
CircuitPython uses the SPIdev kernel which is called out as 'User mode SPI device driver support' in PetaLinux's kernel configuration editor. This must be enabled as a minimum and there are other debugging kernel options for SPI that can be enabled if so desired.
So that the kernel knows to use the SPIdev driver with SPI1 of the Zynq PS, a node in the device tree specifying Zynq's SPI1 and the appropriate SPIdev parameters is needed:
&spi1 {
#address-cells=<1>;
#size-cells=<0>;
status = "okay";
num-cs = <1>;
is-decoded-cs = <0>;
spidev0: spidev@0 {
compatible = "spidev";
/* Max SPI clock frequency via Zynq EMIO is 25MHz */
spi-max-frequency = <25000000>;
/* slave number - CS */
reg = <0>;
/* Set SPI mode = 0 */
spi-cpol = <0>;
spi-cpha = <0>;
};
};
I went over the Zynqberry.GPIO package I created to make the 40-pin header of the Zynqberry appear to CircuitPython modules the same as a Raspberry Pi's 40-pin header which applies the same in this project.
I also went over how the main steps of porting CircuitPython over to the Zynqberry were in editing the Adafruit Platform Detect and Adafruit Blinka packages. While I simply had copied the same spi.py script into the Zynqberry hooks in Adafruit Blinka from the Raspberry Pi hooks for the SPIdev interface in CircuitPython, I found that the SPIdev version in the kernel generated from PetaLinux is slightly different from the one generated for the embedded Linux images for the Raspberry Pi such as Raspbian. Specifically how the readinto() function pulled in data from a device, I had to edit it to pull in more of the buffer at once and adjust how it was indexing that buffer. At first I though this was a specific issue with the RFM69 SPI interface, but then I retested the unedited version of this script on a Raspberry Pi and it worked just fine.
After prepping the SD card with this updated version of Pi OS and refreshing the Zynqberry's QSPI with the updated boot binary image, the Zynqberry is ready to be rebooted and connected to internet. Before installing the RFM69 radio bonnet, I installed a jumper between the MOSI and MISO pins and retested SPIdev interface using the SPIdev test application from my last project post.
Run the SPIdev test application:
ls /dev/spi*
/usr/bin/spidev-test -D /dev/spidev1.0 -v
The RFM69 radio bonnet is ready to be installed on the Zynqberry, but be sure you do so while the Zynqberry is powered off.
Since a new Linux image was generated, the custom Python packages need to be installed again. It's important that the custom packages be installed in the order listed below as they have dependencies on each other that the Python3 package manager (pip3) will attempt to resolve by installing the same packages from the PyPi repository that won't have the Zynqberry customizations.
pip3 install ./Adafruit-PlatformDetect-2.4.0
pip3 install ./Adafruit-Blinka-4.1.0
pip3 install ./Zynqberry.GPIO-0.0.1
Install the OLED CircuitPython module:
pip3 install adafruit-circuitpython-ssd1306
Install the RFM69 CircuitPython module:
pip3 install adafruit-circuitpython-rfm69
To test the setup on the Zynqberry, I used Raspberry Pi RFM69 setup from my RF Hello World blog. The Raspberry Pi RFM69 ran the tx_test.py script transmitting the packet "Hello World" at a calibrated time interval, while the Zynqberry ran the rx_test.py script to receive any incoming packets (I have built both of these scripts into the embedded Linux image so they are available in the home folder at boot up). I like using cables for this kind of prove-in process to eliminate a level of troubleshooting that antennas introduce in RF projects like this. I've found it's much faster to make sure your radio works before picking an antenna and testing if the antenna works with the radio.
It's import to note that this CircuitPython module for the RFM69 has limitations to it in terms of efficiency since it currently does not have interrupt support. This means that as soon as a packet is received, it must be processed immediately or it will be lost. For example, if a packet comes in while the script is busy writing something to the OLED or out to the console in a print statement, it will be missed. If the transmitting radio is sending packets too quickly, they will overwrite each other in the receiver and cause for the receiver to read garage messages.
It took some tweaking in the transmit/receive timing of both the tx_test.py script on the Raspberry Pi and the rx_test.py on the Zynqberry before the Zynqberry was successfully receiving the "Hello World" packet from the Raspberry Pi and printing it to the OLED. Even still, you can see in the console output from the Zynqberry that some garage packets still happen every so often.
Be aware that these scripts take into account the timing of writing to the OLED and the print statements, so if these functions are removed or commented out in the script then the timing of sending/receiving will also change.
In the future I will definitely be looking into using the RadioHead source code directly to create a custom application in PetaLinux to create a more efficient setup for the RFM69 on the Zynqberry. The hooks for having CircuitPython on a Zynq based platform like the Zynqberry is invaluable though to quickly port these projects over at get them running quickly.
Comments