I've bought very cheap 2.4GHz radio transceivers RFM70
( http://www.hoperf.com/rf_fsk/RFM70.htm ).
To test their operation I've connected two of them via SPI
to Linux embedded system with ARM CPU, and translated
the original manufacturer's demo code into Python.
This created an efficient platform for quick prototyping.
My code is published as public domain, however it is simple
a translation of original manufacturer's code, publicly available
without explicit license terms.
#!/bin/sh
# This is a shell archive (produced by GNU sharutils 4.9).
# To extract the files from this archive, save it to some FILE, remove
# everything before the `#!/bin/sh' line above, then type `sh FILE'.
#
lock_dir=_sh22405
# Made on 2010-11-14 14:14 CET by <wzab@wzab>.
# Source directory was `/tmp'.
#
# Existing files will *not* be overwritten, unless `-c' is specified.
#
# This shar contains:
# length mode name
# ------ ---------- ------------------------------------------
# 6743 -rw-r--r-- rfm70.py
#
MD5SUM=${MD5SUM-md5sum}
f=`${MD5SUM} --version | egrep '^md5sum .*(core|text)utils'`
test -n "${f}" && md5check=true || md5check=false
${md5check} || \
echo 'Note: not verifying md5sums. Consider installing GNU coreutils.'
save_IFS="${IFS}"
IFS="${IFS}:"
gettext_dir=FAILED
locale_dir=FAILED
first_param="$1"
for dir in $PATH
do
if test "$gettext_dir" = FAILED && test -f $dir/gettext \
&& ($dir/gettext --version >/dev/null 2>&1)
then
case `$dir/gettext --version 2>&1 | sed 1q` in
*GNU*) gettext_dir=$dir ;;
esac
fi
if test "$locale_dir" = FAILED && test -f $dir/shar \
&& ($dir/shar --print-text-domain-dir >/dev/null 2>&1)
then
locale_dir=`$dir/shar --print-text-domain-dir`
fi
done
IFS="$save_IFS"
if test "$locale_dir" = FAILED || test "$gettext_dir" = FAILED
then
echo=echo
else
TEXTDOMAINDIR=$locale_dir
export TEXTDOMAINDIR
TEXTDOMAIN=sharutils
export TEXTDOMAIN
echo="$gettext_dir/gettext -s"
fi
if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null
then if (echo -n test; echo 1,2,3) | grep n >/dev/null
then shar_n= shar_c='
'
else shar_n=-n shar_c= ; fi
else shar_n= shar_c='\c' ; fi
f=shar-touch.$$
st1=200112312359.59
st2=123123592001.59
st2tr=123123592001.5 # old SysV 14-char limit
st3=1231235901
if touch -am -t ${st1} ${f} >/dev/null 2>&1 && \
test ! -f ${st1} && test -f ${f}; then
shar_touch='touch -am -t $1$2$3$4$5$6.$7 "$8"'
elif touch -am ${st2} ${f} >/dev/null 2>&1 && \
test ! -f ${st2} && test ! -f ${st2tr} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$1$2.$7 "$8"'
elif touch -am ${st3} ${f} >/dev/null 2>&1 && \
test ! -f ${st3} && test -f ${f}; then
shar_touch='touch -am $3$4$5$6$2 "$8"'
else
shar_touch=:
echo
${echo} 'WARNING: not restoring timestamps. Consider getting and
installing GNU `touch'\'', distributed in GNU coreutils...'
echo
fi
rm -f ${st1} ${st2} ${st2tr} ${st3} ${f}
#
if test ! -d ${lock_dir} ; then :
else ${echo} "lock directory ${lock_dir} exists"
exit 1
fi
if mkdir ${lock_dir}
then ${echo} "x - created lock directory ${lock_dir}."
else ${echo} "x - failed to create lock directory ${lock_dir}."
exit 1
fi
# ============= rfm70.py ==============
if test -f 'rfm70.py' && test "$first_param" != -c; then
${echo} "x - SKIPPING rfm70.py (file already exists)"
else
${echo} "x - extracting rfm70.py (text)"
sed 's/^X//' << 'SHAR_EOF' > 'rfm70.py' &&
# This code controlls two RFM70 modules connected to the
# Linux embedded system as spidev1.0 and spidev1.1
# The IRQ and CE lines are connected as follows:
# spidev1.0: IRQ - gpio98, CE - gpio96
# spidev1.1: IRQ - gpio94, CE - gpio95
# However the code does not use IRQ lines yet
# You should start two instamces of the program
# in two different cosoles (e.g. two SSH sessions)
# once with "python rfm70.py 0"
# second time with "python rfm70.py 1"
# The code is published as public domain, however it is
# heavily based on datasheet and demo code provided
# by the manufacturer (in fact it is their code simply
# translated into Python with very small modifications):
# http://www.hoperf.com/rf_fsk/RFM70.htm
# I've also used the GPL-ed code for interfacing Python
# with spidev devices, published here:
# http://elk.informatik.fh-augsburg.de/da/da-49/trees/pyap7k/lang/py-spi
# I hope that the below Python code will help you in quick prototyping with
# RFM70 modules...
import sys
import spi
import time
a=spi.SPI()
if int(sys.argv[1]):
X nspi=1
X is_tx=True
X ce_file="/sys/class/gpio/gpio98/value"
else:
X nspi=0
X is_tx=False
X ce_file="/sys/class/gpio/gpio95/value"
X
pkt_nr=0
a.open(1,nspi)
a.mode = 0
a.msh = 500000
a.bpw = 8
# Just check if bank switching works
print [hex(i) for i in a.xfer2([0x07,0xff])]
print [hex(i) for i in a.xfer2([0x50, 0x53,])]
print [hex(i) for i in a.xfer2([0x07,0xff])]
bank1=(
(0x40, 0x4b, 0x01, 0xe2),
(0xc0, 0x4b, 0x00, 0x00),
(0xd0, 0xfc, 0x8c, 0x02),
(0x99, 0x00, 0x39, 0x41),
(0xd9, 0x9e, 0x86, 0x0b), #?? d9 (as in datasheet or f9 as in sample code?)
(0x24, 0x06, 0x7f, 0xa6),
(0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00),
(0x00, 0x00, 0x00, 0x00),
(0x00, 0x12, 0x73, 0x00),
(0x36, 0xB4, 0x80, 0x00),
(0x41,0x20,0x08,0x04,0x81,0x20,0xCF,0xF7,0xFE,0xFF,0xFF),
)
bank0=(
(0,0x0F), #reflect RX_DR\TX_DS\MAX_RT,Enable CRC ,2byte,POWER UP,PRX
(1,0x3F), #Enable auto acknowledgement data pipe5\4\3\2\1\0
(2,0x3F), #Enable RX Addresses pipe5\4\3\2\1\0
(3,0x03), #RX/TX address field width 5byte
(4,0xff), #auto retransmission dalay (4000us),auto retransmission count(15)
(5,0x80), #(5,0x17), #23 channel
(6,0x3f), #(6,0x17), #air data rate-1M,out power 0dbm,setup LNA gain
(7,0x07), #
(8,0x00), #
(9,0x00), #
(12,0xc3),# only LSB Receive address data pipe 2, MSB bytes is equal to RX_ADDR_P1[39:8]
(13,0xc4),# only LSB Receive address data pipe 3, MSB bytes is equal to RX_ADDR_P1[39:8]
(14,0xc5),# only LSB Receive address data pipe 4, MSB bytes is equal to RX_ADDR_P1[39:8]
(15,0xc6),# only LSB Receive address data pipe 5, MSB bytes is equal to RX_ADDR_P1[39:8]
(17,0x20),# Number of bytes in RX payload in data pipe0(32 byte)
(18,0x20),# Number of bytes in RX payload in data pipe1(32 byte)
(19,0x20),# Number of bytes in RX payload in data pipe2(32 byte)
(20,0x20),# Number of bytes in RX payload in data pipe3(32 byte)
(21,0x20),# Number of bytes in RX payload in data pipe4(32 byte)
(22,0x20),# Number of bytes in RX payload in data pipe5(32 byte)
(23,0x00),# fifo status
(28,0x3F),# Enable dynamic payload length data pipe5\4\3\2\1\0
(29,0x07),# Enables Dynamic Payload Length,Enables Payload with ACK,Enables the W_TX_PAYLOAD_NOACK command
)
X
adr0=[0x32,0x33,0x10,0x11,0x12]
adr1=[0x32,0x33,0x10,0x12,0x12]
adr2=[0x32,0x33,0x10,0x13,0x12]
adr3=[0x32,0x33,0x10,0x14,0x12]
adr4=[0x32,0x33,0x10,0x15,0x12]
adr5=[0x32,0x33,0x10,0x16,0x12]
X
def set_ce(vce):
X f=open(ce_file,"w")
X if vce:
X f.write("1\n")
X else:
X f.write("0\n")
X f.close()
X
def write_cmd_buf(cmd, vals):
X a.xfer2([cmd,] + list(vals))
X
def write_cmd(vals):
X a.xfer2(vals)
X
def write_reg(areg,val):
X a.xfer2([areg | 0x20,val])
X
def write_reg_buf(areg, vals):
X a.xfer2([areg | 0x20, ] + list(vals))
X
def read_reg(reg):
X r=a.xfer2([reg,0x00])
X return r[1]
X
def read_reg_buf(reg, nvals):
X r=a.xfer2([reg, ]+[0,]*nvals)
X return r
X
X
def switch_cfg(cnum):
X tmp=read_reg(0x07) & 0x80
X print "cfg="+str(tmp)
X if cnum:
X if tmp == 0:
X print "reset to 1"
X write_cmd([0x50,0x53])
X else:
X if tmp:
X print "reset to 0"
X write_cmd([0x50,0x53])
X
def switch_to_rx_mode():
X write_cmd([0xe2,0x00]) # Flush RX
X val=read_reg(0x07)
X write_reg(0x07,val)
X set_ce(0)
X val=read_reg(0x00) #Config
X val |= 0x01
X write_reg(0x00, val)
X set_ce(1)
X
def switch_to_tx_mode():
X write_cmd([0xe1,0x00]) # Flush TX
X set_ce(0)
X val=read_reg(0x00) #Config
X val &= 0xfe
X write_reg(0x00, val)
X set_ce(1)
X
def set_channel_num(cnum):
X write_reg(5,cnum)
X
def send_packet(vals):
X global pkt_nr
X pkt_nr += 1
X switch_to_tx_mode()
X fifo_sta = read_reg(0x17) # FIFO_STATUS
X if not (fifo_sta & 0x20):
X print str(pkt_nr)+" Wysylam!"+str(vals)
# write_cmd_buf(0xa0,vals)
X write_cmd_buf(0xb0,vals)
X
#The below procedure for sending packets with ACK doesn't work yet :-(
def send_packet_ack(vals):
X global pkt_nr
X pkt_nr += 1
X switch_to_tx_mode()
X fifo_sta = read_reg(0x17) # FIFO_STATUS
X if not (fifo_sta & 0x20):
X print str(pkt_nr)+" Sending!"+str(vals)
X write_cmd_buf(0xb0,vals)
X while True:
X fifo_sta = read_reg(0x17) # FIFO_STATUS
X if (fifo_sta & 0x10): #Empty!
X break
X sta = read_reg(0x07)
X print hex(sta)+":"+hex(fifo_sta)
X if sta & 0x10:
X write_reg(0x07,sta)
X print str(pkt_nr)+" Retrying!"+str(vals)
X write_cmd_buf(0xa0,vals)
X
def receive_packet():
X global pkt_nr
X sta = read_reg(0x07) # status
X if sta & 0x040:
X while True:
X pkt_nr +=1
X dlen = read_reg(0x60) #R_RX_PL_WID
X pkt = read_reg_buf(0x61,dlen)
X print str(pkt_nr)+" rcvd:"+str(pkt)
X fifo_sta = read_reg(0x17) # FIFO_STATUS
X if fifo_sta & 0x01: # RX EMPTY
X break
X write_reg(0x07,sta)
X
def rfm70_init(is_tx):
X time.sleep(0.2)
X switch_cfg(0)
X for i in range(0,20):
X write_reg(bank0[i][0],bank0[i][1])
X #first RX_addr
X write_reg_buf(10,adr0)
X write_reg_buf(11,adr1)
X write_reg_buf(16,adr1)
X i=read_reg(29)
X if i != 0:
X print "Already activated!"
X else:
X write_cmd([0x50,0x73])
X for i in range(22,20,-1):
X write_reg(bank0[i][0],bank0[i][1])
X switch_cfg(1)
X for i in range(0,15):
X write_reg_buf(i,bank1[i])
X v=list(bank1[4])
X v[0]=v[0] | 0x06
X write_reg_buf(4,v)
X v[0]=v[0] & 0xf9
X write_reg_buf(4,v)
X time.sleep(0.05)
X switch_cfg(0)
X switch_to_rx_mode()
X
while False:
X time.sleep(1.0)
X set_ce(0)
X time.sleep(1.0)
X set_ce(1)
X
rfm70_init(is_tx)
if not is_tx:
X #If this is a receiver - try to receive
X #packets and print them out
X while True:
X receive_packet()
else:
X #If this is a transmitter - send a packet every 300ms
X while True:
X send_packet([0x12,0x34,0x56,0x78]*7)
X time.sleep(0.3)
SHAR_EOF
(set 20 10 11 14 14 12 55 'rfm70.py'
eval "${shar_touch}") && \
chmod 0644 'rfm70.py'
if test $? -ne 0
then ${echo} "restore of rfm70.py failed"
fi
if ${md5check}
then (
${MD5SUM} -c >/dev/null 2>&1 || ${echo} 'rfm70.py': 'MD5 check failed'
) << \SHAR_EOF
effea1c31637a54d9b4517049fa7adad rfm70.py
SHAR_EOF
else
test `LC_ALL=C wc -c < 'rfm70.py'` -ne 6743 && \
${echo} "restoration warning: size of 'rfm70.py' is not 6743"
fi
fi
if rm -fr ${lock_dir}
then ${echo} "x - removed lock directory ${lock_dir}."
else ${echo} "x - failed to remove lock directory ${lock_dir}."
exit 1
fi
exit 0