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