Sunteți pe pagina 1din 4

A layman’s guide to RF transmission encoding-decoding using PbPro

As promised a while ago, I finally got the OK from our client to release part of the code that was used in
their RF data link project. The project, which will become a commercial instrument at the end of the next
quarter, consisted of 16 remote logging stations with RF data links into a main controller located in the
main building. We ended up utilizing Radiotronix module OOK transmitter and receiver modules because
of their specs (109dbm sensitivity on the receiver, 75 kHz accuracy around the center frequency of 433.92
MHz on the transmitter and 4800-baud rate speed on both) and low cost. By the end of the project we were
able to achieve 99.9% link reliability and 100% data integrity reliability, measured over a period o 35 days,
and operating 24-7 at distances in excess of 70 meters inside buildings of various types. After each remote
is polled, it is given 5 opportunities to come back. Should transmission fail after the 5th attempt (data
integrity, no answer etc.), then that link is assigned a fail code until the next time. For our purposes, at 4800
baud, using CRC and Manchester encoding and based on the number bytes that we are dealing with, each
transmission is well under 1 second ( more like 400 msec)

As RF was new to us, it took a while to learn (the hard way I might add) that you cannot treat RF serial
communications as you would ,say, a hard-wired system. We got bit by most, if not all, of the “alligators”
which normally attack such a project, that is, PC board layout, choice and positioning of the antenna,
propagation interference, signal absorption, reflection, multipath interference and radio interference inside
the buildings, nulls, deep nulls etc. After solving the usual hardware problems, it was then that we found
out the hard way that RF serial transmission has to be treated quite a bit differently than hard-wired serial
comms.

Given the nature of the OOK type receivers all of which contain data slicers, the transmitted bit pattern has
to be able to:
1. Provide some sort of synching for the receiver
2. Provide a pattern which will keep the receiver’s data slicer at mid point (DC balanced) when using
NRZ type data -(Manchester encoding)
3. Provide some sort of control for its integrity at the receive end -(16 bit CRC check).
4. Provide some sort of unique identifier (if you have more than one remote working)

Our transmission sequence and format looked like the following:


Gosub Manchester_Encode
Gosub Calc_CRC
Transmit_Data:
Serout2 RFSerOut,RfBaudRate,WaitDelay,[Preamble,Preamble,Preamble,Preamble,_
Preamble,XmtSynch,EncodedWord.LowByte,EncodedWord.HighByte]

At the receiver end our receive proc looked like the following:
Receive_Results:
TempVar = SerialNum[z]
gosub Manchester_Encode ' Encode byte
RFSerout = 1 ' prepare xmitter
pause 15 ' time to settle
Serout2 RFSerOut , RfBaudRate , 15 , [Preamble,Preamble,Preamble,Preamble, Preamble, Synch ,
EncodedWord.Lowbyte,EncodedWord.Highbyte] ' wake up remote
gosub Xmit_OFF ' xmitter is OFF
Gosub Rcv_ON ' receiver is ON
CRC = 0
ChannErr = 1 ' If fail code 1 then time out
goto Get_Rx_Data ' get the data
Continue1:
If TempVar = SerialNum[z] then ' Is the remaote answering the correct one?
ChannErr = 2 ' If fail code 2 then SerNum <>;Timeout 2
goto Get_Rx_Data ' get the data
Continue2:

1
x = TempVar ' Remote S/N
gosub Calc_CRC ' calculate CRC for byte
ChannErr = 3 ' Type comparison error code
goto Get_Rx_Data ' get the data
Continue3:
ChannErr = 4 ' Type comparison error code
If SensorType[z] <> (TempVar) then Finish_Receive ' Type error;remote type <> base type
SensorType = TempVar
x = SensorType
gosub Calc_CRC ' calculate CRC for byte
goto Get_Rx_Data ' get the data
Continue4:
ResultSign = TempVar
x = ResultSign ' Result sign
gosub Calc_CRC ' calculate CRC for byte
ChannErr = 5
goto Get_Rx_Data ' get the data
Continue5:
Result.LowByte = TempVar
x = Result.LowByte ' result low byte
gosub Calc_CRC ' calculate CRC for byte
ChannErr = 6 ' Result High Byte error code
goto Get_Rx_Data ' get the data
Continue6:
Result.HighByte = TempVar
x = Result.HighByte ' Result high byte
gosub Calc_CRC ' calculate CRC for byte
ChannErr = 7 ' result low byte error code
goto Get_Rx_Data ' get the data
Continue7:
Result2.HighByte = TempVar
x = Result2.Highbyte ' must do CRC with Hi CRC byte first
gosub Calc_CRC ' otherwise CRC <> 0 on good xmission
ChannErr = 8
goto Get_Rx_Data ' get the data
Continue8:
Result2.LowByte = TempVar
x = result2.Lowbyte
gosub Calc_CRC
If CRC <> 0 then ' Calculated CRC must = 0 ;else crc error
ChannErr = 9 ' CRC Error
goto Finish_Receive ' end reception with error
endif
ChannErr = 0 ' Reset error var; receive OK
gosub Rcv_OFF ' Evereything OK; Receiver is OFF
Goto Finish_Receive
endif
Get_Rx_Data:
Serin2 RFSerIn,RfBaudRate,RxTimeOut,Finish_Receive,[Wait(Synch), Encoded Word.Lowbyte ,
EncodedWord.Highbyte]
gosub Manchester_Decode ' decode the variable back to a byte

The above procedure, which was a subroutine in our program, basically:

2
1. Wakes up each of remotes based on the serial number of the remote station. Note that we send out 5
preambles. If you have the time and speed, the more the merrier since the preamble’s purpose is to help
DC balance the reeiver’s bit slicer.
2. Receives each byte of data from the remote
3. Does a CRC (integrity check) calculation on the received data (less the preambles, and the CRC byte)
4. Manchester decodes the received data
5. Note that ChannErr’s were inserted in there so that we would know exactly where the transmission
failed (if it failed).
Some of the constant and variable definitions for the above procedures to work were:
' CONSTANTS Here:
Preamble con $55 ' preamble data byte forxmiter synching;%101010
Synch con "j" ' synchronization byte
CRCPolynomial con $1021 ' CRC-CCIT

Notes:
1. We chose $55 as the preamble because of its inherently balanced nature of equal number of 0’s and
1’s. If one looks at the binary equivalent of 55h is 01010101b. Note that 1’s and zeros equal each
other and the bit run length (how many 1’s and 0’s are next to each other- also important to keep the
bit slicer DC balanced) ) is 1.
2. The “j” was chosen as the Synch byte for the same reason
3. The CRC polynomial is a standard. Much information available on the net about CCIT CRC
polynomials.

Manchester encoding and decoding the data:

Manchester_Encode:
For y = 0 to 7
If TempVar.0[y] = 0 then ' Bit = 0
EncodedWord.0[y * 2] = 0
EncodedWord.0[(y*2)+1] = 1
else
EncodedWord.0[y * 2] = 1 ' Bit = 1
EncodedWord.0[(y*2)+1] = 0
endif
next
Return

Note that:
1. TempVar is the byte we want to encode
2. We will encode it into a 16 bit word, the low byte will contain the lower nibble of the encoded
variable; ' the high byte will contain the upper nibble of the encoded variable
3. A “0” will be equated to a 0-1 transition
4. A “1” will be equated to 1-0 transition
5. I used a little touted, extremely useful, undocumented feature of PbPro- the ability to access each bit of
a byte or word using Word.0[y] feature. This saves on LOADS of “IF-AND’s” as well as code
readability and size. I suspect that the “CASE” statement in 2.34 may be usable instead of this feature.
6. The above procedure will encode each 8-bit byte to a 16-bit word with an equal number of 1’s and 0’s.
Run length is 1. The upside is that this is perfect for DC balancing the receiver’s bit slicer. The
downside is that this results in doubling bandwidth.

Since the data is encoded at the transmitter, it must be decoded at the receiver. The procedure I used for
that was:

Manchester_Decode:
For y = 0 to 7
If EncodedWord.0[y*2] = 0 then ' if bit =0

3
If EncodedWord.0[(y*2)+1] = 1 then
TempVar.0[y] = 0
Endif
Else
TempVar.0[y] = 1 'then bit =1
Endif
Next
Return
Note that:
1. We will decode EncodedWord (lower byte) as the lower nibble of tempVar
2. EncodedWord ( high byte) as the upper nibble of TempVar
3. Basically, again using the Word.0[y] feature of PbPro, we are able to access each bit of the encoded
word and decode it (strip it) back to the original “1” or “0” that was transmitted.

For data integrity at the receive end,(that is to make certain that what you receive is exactly what you send)
we used a CRC check. We found this to be much safer and reproducible than the simpler checksum. The
CRC routine was:

Calc_CRC: 'calculate byte CRC; 16 bit crc based on CCIT polynomial


CRC = (x * 256) ^ CRC
For y = 0 to 7
If CRC.15 = 0 then Multiply
CRC = (CRC << 1) ^ CRCPolynomial
Goto Next_y
Multiply:
CRC = CRC << 1
Next_y:
Next
Return

Note:
1. The 2 left shifts “<<” may be substituted for a “*” (multiply). What we are doing here is multiplying
by 2.
2. The CRC polynomial was given in the Constant definition. Note that there is a plethora of material
available on the internet about CRC calculations and the various polynomials used.
3. Caveat here: At the end of the CRC calculation on the transmitted data, the sum of the decoded CRC
must equal to “0”. If the resulting CRC calculation is NOT 0 then there was a transmission error. Pay
attention to the order in which you calculate the CRC when you transmit AND the order in which you
calculate the CRC when you receive. Also when you transmit the CRC byte, remember to transmit
High byte first. For further information, look at the above code.

Well, that is pretty much what I can release with our clients permission. The above should be enough for
anyone to incorporate in their RF projects. While I am not ( by any stretch of the imagination) an expert on
RF links, I’ll be more than happy to help and answer any questions from the list.

Enjoy!

Fritz Braun Jr.

S-ar putea să vă placă și