nRF905 Python 3 Library - Raspberry Pi

nRF905 Python 3 Library ā€“ Raspberry Pi 4

I am posting here because I could not find anything related to Python for using these modules. There is an Arduino Library that works but is too complex for porting to Python. I have used the Arduino Library but modified it extensively to do what I wanted.

The code below was developed from scratch. There is much room for improvement by adding more options and using more of the features of the RF module. It was developed to the point where it worked for what I wanted but may not for other situations. Maybe it is a starting point for anyone using these modules with a Raspberry Pi. Hopefully it will be of use to someone.

Arduino Compatible RF Transceiver Module** CAT.NO: **XC4522 Jaycar

The modules are a little pricey at $20, but they work well when setup correctly. They provide an alternative option to using Wifi or Bluetooth.

'''
nRF905 Configuration
AUTO_RETRAN = 0   # 0=No 1=Yes retransmission
RX_RED_PWR = 1    # 0=Normal RX power, 1=Low RX power
PA_PWR = 0        # 0=-10dBm 1=-2dBm 2=+6dBm 3=+10dBm, TX power (9mA to 30mA)
HFREQ_PLL = 0     # 0=433 MHz band, 1=868/915MHz band
CH_NO_MSB = 0     # 0 or 1
CH_NO = 0x6C      # 0x6C=433.2MHz, channel seperation = 100KHz, ie 0x6D=433.3MHz
                  # fRF = ( 422.4 + (CH_NO/10))*(1+HFREQ_PLL) MHz
TX_AFW = 4        # TX address field width, 4 bytes or 1 byte 
RX_AFW = 4        # RX address field width, 4 bytes or 1 byte 
TX_PW = 32        # Payload number of bytes, usually 32
RX_PW = 32        # Payload number of bytes, usually 32
CRC_MODE = 0      # 0=8 bit CRC, 1=16 bit CRC
CRC_EN = 0        # 0=disable, 1=enable CRC check
XOF = 3           # 0=4MHz 1=8MHz 2=12MHz 3=16MHz 4=20 MHz Crystal Frequency
UP_CLK_EN = 0     # 0=No 1=Yes External clock signal
UP_CLK_FREQ = 0   # 0=4MHz  1=2MHz  2=1MHz  3=500kHz External clock Frequency

#example nRF905 Dictionary Definition
RX_Address = bytearray((0xE7,0xE7,0xE7,0xE7))
bufsize = 32
cfg = {'AUTO_RETRAN':0,'RX_RED_PWR':1,'PA_PWR':0,'HFREQ_PLL':0,'CH_NO_MSB':0,
       'CH_NO':0x6C,
       'TX_AFW':4,'RX_AFW':4,
       'TX_PW':bufsize,
       'RX_PW':bufsize,
       'RX_Address0':RX_Address[0],
       'RX_Address1':RX_Address[1],
       'RX_Address2':RX_Address[2],
       'RX_Address3':RX_Address[3],
       'CRC_MODE':0,'CRC_EN':0,'XOF':3,'UP_CLK_EN':0,'UP_CLK_FREQ':0}
'''
############################################################################################
import time
import RPi.GPIO as GPIO
import spidev

############################################################################################
class nRF905(object):
    def __init__(self, bus=None, port=None, freq=None, CE=None,TXEN=None,PWR=None,DR=None,CSN=None,AM=None,buff=32):
        
        GPIO.setmode(GPIO.BCM)
        GPIO.setwarnings(False)

        GPIO.setup(CE, GPIO.OUT)
        GPIO.setup(TXEN, GPIO.OUT)
        GPIO.setup(PWR, GPIO.OUT)
        GPIO.setup(DR, GPIO.IN, pull_up_down=GPIO.PUD_UP)
        GPIO.setup(CSN, GPIO.OUT)
        GPIO.setup(AM, GPIO.IN, pull_up_down=GPIO.PUD_UP)

        GPIO.output(PWR, 1)       # set power up state
        GPIO.output(CE, 0)        # disable radio, allow SPI
        GPIO.output(TXEN, 0)      # set RX mode
        GPIO.output(CSN, 1)       # disable SPI communications

        self.spi = spidev.SpiDev()
        self.spi.open(bus,port)
        self.spi.max_speed_hz = freq
        
        self._CSN = CSN
        self._CE = CE
        self._TXEN = TXEN
        self._PWR = PWR
        self._DR = DR
        self._AM = AM
        
        self.bufsize = buff

    def read_Config_Reg(self):
        GPIO.output(self._CSN, 0)
        self.spi.writebytes(b"\x10")               # command to read all registers
        data = bytearray(self.spi.readbytes(10))   # returns array that is changeable
        GPIO.output(self._CSN, 1)
        return data
    
    def write_Config_Reg(self, data):
        GPIO.output(self._CSN, 0)
        self.spi.writebytes(b"\x00")              # command to write all registers
        self.spi.writebytes(data)
        GPIO.output(self._CSN, 1)
        return

#############################################################################################
# Radio TX RX 
#############################################################################################
    def read_TX_Address(self):
        GPIO.output(self._CSN, 0)
        self.spi.write(b"\x23")
        data = bytearray(self.spi.readbytes(4))
        GPIO.output(self._CSN, 1)
        return data

    def write_TX_Address(self, data):
        GPIO.output(self._CSN, 0)
        self.spi.writebytes(b"\x22")
        self.spi.writebytes(data)
        GPIO.output(self._CSN, 1)
        return

    def read_TX_Payload(self,size):
        GPIO.output(self._CSN, 0)
        self.spi.writebytes(b"\x21")
        data = bytearray(self.spi.readbytes(size))
        GPIO.output(self._CSN, 1)
        return data

    def write_TX_Payload(self, data):
        GPIO.output(self._CSN, 0)
        self.spi.writebytes(b"\x20")
        self.spi.writebytes(bytearray(data,'utf-8'))
        GPIO.output(self._CSN, 1)
        return

    def read_RX_Payload(self, size):
        GPIO.output(self._CSN, 0)
        self.spi.writebytes(b"\x24")
        data = bytearray(self.spi.readbytes(size))
        GPIO.output(self._CSN, 1)
        return data

###############################################################################
# Load values to configuration variable and write to nRF905
#############################################################################################
    def print_Config_Reg(self,msg,Config_Reg):
        print(msg,hex(Config_Reg[0]), hex(Config_Reg[1]), hex(Config_Reg[2]), hex(Config_Reg[3]), hex(Config_Reg[4]), hex(Config_Reg[5]),
              hex(Config_Reg[6]), hex(Config_Reg[7]), hex(Config_Reg[8]), hex(Config_Reg[9]))
        return

    def set_Configuration_Register(self,cfg):
        GPIO.output(self._TXEN, 0)      # set RX mode
        GPIO.output(self._CE,   0)      # disable radio, allow SPI

        Config_Reg = bytearray((0,0,0,0,0,0,0,0,0,0))
#    print_Config_Reg("Before Assignment",Config_Reg)
        Config_Reg = self.read_Config_Reg()
#    print_Config_Reg("After Read       ",Config_Reg)
        Config_Reg[0] =  cfg['CH_NO']
        Config_Reg[1] =  cfg['AUTO_RETRAN'] << 5 | cfg['RX_RED_PWR'] << 4 | cfg['PA_PWR'] << 2 | cfg['HFREQ_PLL'] << 1 | cfg['CH_NO_MSB']
        Config_Reg[2] =  cfg['TX_AFW'] << 4 | cfg['RX_AFW']
        Config_Reg[3] =  cfg['TX_PW']
        Config_Reg[4] =  cfg['RX_PW']
        Config_Reg[5] =  cfg['RX_Address0']
        Config_Reg[6] =  cfg['RX_Address1']
        Config_Reg[7] =  cfg['RX_Address2']
        Config_Reg[8] =  cfg['RX_Address3']
        Config_Reg[9] =  cfg['CRC_MODE'] << 7 | cfg['CRC_EN'] << 6 | cfg['XOF'] << 3 | cfg['UP_CLK_EN'] << 2 | cfg['UP_CLK_FREQ']

#    print_Config_Reg("Before Write     ",Config_Reg)
        self.write_Config_Reg(Config_Reg)
#    print_Config_Reg("After Write      ",Config_Reg)
        Config_Reg = self.read_Config_Reg()
#        self.print_Config_Reg("nRF905 = ",Config_Reg)
        return Config_Reg

##############################################################################################
# TX Message
##############################################################################################
    def TX_Message(self, Address, Payload):
        GPIO.output(self._TXEN,0)                      # set RX mode while SPI
        GPIO.output(self._CE,0)                        # disable radio
# Check for valid length
        if len(Payload) < 32:
            for i in range(len(Payload),32):
                Payload = Payload + " "
        else:
            if len(Payload) > 32:
                Payload = Payload[0:32]
# set address and data
        self.write_TX_Address(Address)
        self.write_TX_Payload(Payload)
        GPIO.output(self._TXEN,1)                      # enable TX mode
        GPIO.output(self._CE,1)                        # enable radio
# TX should start here
        while not GPIO.input(self._DR):                # wait for DR to go high, indicates successful transmit
            pass
        GPIO.output(self._CE,0)                        # disable radio
        GPIO.output(self._TXEN,0)                      # set RX mode while SPI
        return

##############################################################################################
# RX Message
##############################################################################################
    def RX_Message(self):
        data = ''
        RX_OK = False
        GPIO.output(self._TXEN,0)            # set RX mode while SPI
        GPIO.output(self._CE,1)              # enable radio
        for i in range(50):                  # wait 0.5 secs for response
            if GPIO.input(self._AM) == 1:
                if GPIO.input(self._DR) == 1:
                    data = self.read_RX_Payload(self.bufsize)
                    RX_OK = True
            time.sleep(0.01)                 # wait 10ms before trying again
        GPIO.output(self._CE,0)              # disable radio
        return RX_OK, data

##############################################################################################
##############################################################################################
##############################################################################################
##############################################################################################
# Example for using nRF905 Library
# Send 'Hello World'
# Print response
##############################################################################################

from SPI_nRF905 import nRF905

##############################################################################################
# nRF905 Configuration Variables
##############################################################################################
TX_Address = bytearray((0xAA,0x55,0xAA,0x55))     # TX & RX addresses opposite for other end
RX_Address = bytearray((0x55,0xAA,0x55,0xAA))
bufsize = 32
nRF905_Config = {'AUTO_RETRAN':0,'RX_RED_PWR':0,'PA_PWR':3,'HFREQ_PLL':0,'CH_NO_MSB':0,
                 'CH_NO':0x6C,
                 'TX_AFW':4,'RX_AFW':4,
                 'TX_PW':bufsize,
                 'RX_PW':bufsize,
                 'RX_Address0':RX_Address[0],
                 'RX_Address1':RX_Address[1],
                 'RX_Address2':RX_Address[2],
                 'RX_Address3':RX_Address[3],
                 'CRC_MODE':0,'CRC_EN':0,'XOF':3,'UP_CLK_EN':0,'UP_CLK_FREQ':0}

comms = nRF905(bus=0, port=0, freq=125000, CE=22, TXEN=23, PWR=24, DR=25, CSN=26, AM=27)

# set configuration and display
n = comms.set_Configuration_Register(nRF905_Config)
config = "nRF905 = "+hex(n[0])+" "+hex(n[1])+" "+hex(n[2])+" "+hex(n[3])+" "+hex(n[4])+" "
config = config + hex(n[5])+" "+hex(n[6])+" "+hex(n[7])+" "+hex(n[8])+" "+hex(n[9])+'\n'
print(config)

TX_Payload = 'Hello World'
print(TX_Payload)
comms.TX_Message(TX_Address,TX_Payload)
RX_OK, RX_Payload = comms.RX_Message()
if RX_OK:
    print(RX_Payload)
else:
    print("Nil Receive")

###############################################################################
###############################################################################
###############################################################################
###############################################################################
###############################################################################

Program output, second TX/RX module not activated.

Python 3.9.2 (/usr/bin/python3 on Freddy)
>>> %Run test.py
nRF905 = 0x6c 0xc 0x44 0x20 0x20 0x55 0xaa 0x55 0xaa 0x18

Hello World
Nil Receive
>>>

Regards
Jim

4 Likes

Setup a second TX/RX unit, actually my watering system project.
Send ā€˜01ā€™, responds with date and time from RTC.

TX_Payload = '01'
print("Sent",TX_Payload)
comms.TX_Message(TX_Address,TX_Payload)
RX_OK, RX_Payload = comms.RX_Message()
if RX_OK:
    print("Received",RX_Payload)
else:
    print("Nil Receive")

Python 3.9.2 (/usr/bin/python3 on Freddy)
>>> %Run test.py
nRF905 = 0x6c 0xc 0x44 0x20 0x20 0x55 0xaa 0x55 0xaa 0x18

Sent 01
Received bytearray(b'01 21/04/22 11:05:05            ')
>>>
4 Likes

Hi Jim,

Absolutely incredible!!!

We just got these modules into the range: nRF905 RF Transceiver Module | Core Electronics Australia

Iā€™m keen to see all of your projects and other makers to use your code!!
Liam

1 Like