SparkFun Forums 

Where electronics enthusiasts find answers.

All things pertaining to wireless and RF links
By dmpthekiller
#25336
Hey!

i'm trying to send a data packet using tlp/rlp. i'm using a pic12f629 to send the message and a pic18f2550 to receive it. it works perfectly with wires, but replacing the cable with the modules doesn't work as i expected.

the line encoding used is RS232-like encoding (1 start bit - 8 data bit - 1 stop bit, using mark/spaces). i've read a couple of threads with some information about these modules, suggesting to use a preamble to make the receive recognize the start of a packet, avoiding noise.

i implemented the preamble as a 6 ms mark of ones (also found somewhere that rx turn-on time is 5 ms), but some people say that these modules need frequent bit transitions to avoid losing sync, which can be obtained using manchester encoding.

i'm not sure if i should implement such encoding, because the wireless link is actually working, but no longer than 20 cm (great, uh?), and it starts to receive garbage with longer distances (crc check allways fails). I think maybe it is a bad antenna design or something (i even though my project board might be "attenuating" signal and mounted it on pcb, and couple of days ago saw pictures with these modules on protoboard).

please, i need tips for using these devices:
- what antenna should i use, how should i connect it, and how can i design it on pcb (at least tx).
- do i really need manchester or other encoding (lots of people claim to make it work without it, just connecting hardware usart).
- is it ok to use a preamble of only 1's?... should i change preamble with a different code, instead of a bunch of ones?
- can i test these modules on common project boards, or do i need a "high frequency capable" project board or something?...

Thanks A Lot!!!!
By saipan59
#25351
please, i need tips for using these devices:
- what antenna should i use, how should i connect it, and how can i design it on pcb (at least tx).
I've been working with these lately (the 4800 baud 434 Mhz version).
With an antenna of about 17cm of wire, I found that my link didn't work when too close to the xmtr - the rcvr overloads. It only worked when I moved them a few feet apart. When very close together (for testing/debug), don't use any antenna at all.
For a PCB xmit antenna, there is an app note somewhere on the Microchip site. Also download the docs for the "rfPICkit".
- do i really need manchester or other encoding (lots of people claim to make it work without it, just connecting hardware usart).
Yes, you do need some sort of encoding for DC balance if your packets are more than 1 or 2 bytes long (and a preamble would still be needed). So far my encoding is just to follow each byte with its bitwise inverse. I consider it a stop-gap method - it may not be reliable since it can go for up to 8 bits without a transition. Manchester or 8/10 encoding should be good for all situations.
- is it ok to use a preamble of only 1's?... should i change preamble with a different code, instead of a bunch of ones?
No, the preamble needs to do 3 of things: Set the initial DC balance on the rcvr, set the framing for the first byte, and provide a 'unique' sequence that your rcvr can use to distinguish between a real packet and something else. My current preamble is AA AA AA AA FF A9 56 B2. The 'FF' sets the framing in the UART. The last 3 bytes are mostly arbitrary, but I chose them to have reasonable DC balance.
- can i test these modules on common project boards, or do i need a "high frequency capable" project board or something?...
No. I started using an ordinary proto-board, and it was fine. Now I'm using a PC board. My board includes 2 useful features:
- A 555 1-shot driving the xmtr, such that the xmtr is only powered up when data is being sent.
- An SA630 SPDT RF switch to switch the common antenna from the xmtr to the rcvr. When the 555 has enabled the xmtr, it also switches the antenna from rcvr to xmtr.

Pete
By saipan59
#25353
Here is a zip containing my code, and the schematics. The file "stackable_p3.bmp" is the xmtr/rcvr board.
The MPU is an 18F2620.
The current functionality is that it receives a packet, then it measures the temperature with a TC1047A sensor (not shown on the schematics), then it sends back a packet with the temperature in degrees C.

www.geocities.com/saipan59/robots/stackable.zip

Pete
By reklipz
#25354
Hey saipan, I'm starting to use these modules also. On the receiver side, how do you determine if you have valid data or not? Do you use interrupts on RX for the USART, and if so, do you need to worry about clearing the error bits and such?

Does your interrupt function look something like the following:
Code: Select all
function yaddayadda() {
   if( getcUSART() != 0xA9 )
      return;
   if( getcUSART() != 0x56 )
      return;
   if( getcUSART() != 0xB2 )
      return;

   /* If we've gotten here, then we've got valid data, do whatever we need to with it */

   return;
}
If you don't mind, could you show me how to get started with the receiver? (I've already got the Manchester encoding done for the xmitter and the decoder for the receiver).

Also, do you use 4800baud? and the settings for the USART, they're just 8bit, no parity, and a stop bit?

Thanks much for the information, I really appreciate it!

Also, I like the one shot 555 idea, but seeing as how I'll be kind of limited on space for my transmitter, I opted to use a pin of the PIC to turn on / off a transistor which provides power to the xmitter, although yours is much more elegant. :)

-Nate
By saipan59
#25355
Yes, I use 4800 baud, standard '8N1' setup.
It reads *all* incoming data all the time (which is normally just random junk from the rcvr). When it sees the 3 consecutive bytes from the preamble, then it assumes that the next 20 bytes will be data (really 10 bytes, but each is followed by its inverse). This works because while the xmtr is transmitting, the rcvr is not outputting the random noise. Of course, it would fail if it received another transmission at the same time. In the future I'll add a checksum byte to the end of each packet, so that the rcvr knows if it got a good-looking packet or not.

Here's a stripped-down version of my interrupt handler:
Code: Select all
void InterruptHandlerLow ()
{
    // SCI (serial port) interrupt.
    if (PIR1bits.RCIF)   
    {
        sci_byte = RCREG;
 
        if (!RCSTAbits.OERR)
        {
            // Every other byte is for DC balance - ignore it.
        	odder++;
    		if ((preamble) && (odder & 1))
    		{
        		buffer[pos] = sci_byte;
    			pos++;
    			
    			// All packets are the same width.
    			// Check the address and command.
    			if (pos >= BUFFER_SIZE)
    			{
        			if (buffer[1] == TARGET_ADDRESS)
        			{
            			// Disable the rcvr until we've responded.
            			RCSTAbits.CREN = 0;
            			// Record who to send the response to.
            			send_to_address = buffer[0];
    				    if (buffer[2] == SEND_TEMPERATURE)
    				        send_temperature = TRUE;
    				    else
    				        // Command not recognized. Re-enable the rcvr.
    				        RCSTAbits.CREN = 1;
    				}
    				preamble = FALSE;
    			}
    		}
    		
    		if (!preamble)
    		{
        		c1 = c2;
        		c2 = c3;
        		c3 = sci_byte;
        		if ((c1 == 0xA9) && (c2 == 0x56) && (c3 == 0xB2))
        		{
        			preamble = TRUE;
        			pos = 0;
        			odder = 0;
        		}
    		}
        }
        else
        {
            RCSTAbits.CREN = 0;
            Nop();
            RCSTAbits.CREN = 1;
        }
        Nop();
    } // end of serial port stuff
 } // LOW_ISR()
Pete
By saipan59
#25356
BTW, notice in that code snippet that each packet contains a "from address", a "to address", and a "command". Currently, the only command is "send temperature".
Also note the "preamble" variable. It becomes TRUE after I've seen the correct sequence of 3 bytes, and it stays true until 10 X 2 bytes have been received.
The "odder" variable is used to skip over every-other incoming byte.
After a command has been received, the ISR sets a flag to tell main() how to respond. At the moment, main() makes a temperature reading, then assembles a packet that includes the temperature, then sends it out.

Pete
By reklipz
#25357
OK, thanks for the quick response.

So, basically, the interrupt will be called every time that the receive buffer is filled, and you won't be able to read more than the one byte, as it can only be read on the next interrupt, thats what was messing me up.

The preamble variable was messing me up at first, but after I kept reading, it became clear what you were doing.

Thanks again, and I'll let you know if I get the thing to work, and post the source.
By dmpthekiller
#25361
Thanks a lot!!!

I'm convinced about implementing manchester encoding (since im using long packets, 37 bytes for now) and changing protocol preamble... i'll start doing that tomorrow... by the way, reklipz, you said you have manchester encoding done for both tx and rx... would you mind sharing your code?...

Also, i'm gonna look for the ANs about pcb antennas...

Keep you informed about my progress or any other doubts... Thanks!!!
By reklipz
#25374
dmpthekiller wrote:by the way, reklipz, you said you have manchester encoding done for both tx and rx... would you mind sharing your code?...
Sure, I'll post it as soon as I get home and have some time.
By reklipz
#25404
Well, heres the code:
Code: Select all
/* ---- Manchester Encode ----*/
unsigned int
manEncode(const unsigned char data) {
   return ((unsigned int)((data[i] & 0xAA) | (~data[i] & 0xAA) >> 1)<<8) | ((data[i] & 0x55) | (~data[i] & 0x55) << 1);
}

/* ---- Manchester Decode ---- */
unsigned char
manDecode(const unsigned int data, unsigned char *error) {
   unsigned char odd, even;

   odd = data >> 8; // We want the upper 8 bits
	
   if( (odd & 0xAA) ^ ((~odd & 0x55) << 1) ) {
      *error = 1;
      return 0;
   } else
      odd &= 0xAA;

   even = data; // Will automatically cast, keeping only the lower 8 bits
   if( (even & 0x55) ^ ((~even & 0xAA) >> 1) ) {
      *error = 1;
      return 0;
   } else
      even &= 0x55;
	
   *error = 0;
	
   return odd | even;
}
Please note, I cannot take credit for this code (maybe for porting it to Microchip C18 and getting rid of the global constant that was used in the original).
The original can be found here:
http://www.ottawarobotics.org/articles/rf/rf.html
which was found in this thread:
http://www.sparkfun.com/cgi-bin/phpbb/v ... php?t=5369

The two functions I actually use just wrap these functions and include the transmission of the preamble, frame alignment, and identification codes, and look like the following:
Code: Select all
/* * *
 * Transmitter Code
 */

#define TX_PWR PORTBbits.RB0

void
main(void) {
	unsigned char dat[2] = {0b00001111, 0b11110000};

	... // Setup transmitter

	sendData(dat, 2);

	... // Do other stuff

}

/* ---- Send Data ---- */
void
sendData(const unsigned char *data, const unsigned char size) {
	unsigned char i;
	
	/* Power on the transmitter */
	TX_PWR = 1;
	Delay1KTCYx(1);
	
	/* Send preamble */
	for( i = 0; i < 5; i++ ) {
		putcUSART(0x55);
		while( BusyUSART() );
	}
	/* Send framing fix */
	putcUSART(0b01000100);
	while( BusyUSART() );
	putcUSART(0b00010001);
	while( BusyUSART() );
	putcUSART(0b01000101);
	while( BusyUSART() );
	putcUSART(0b00010101);
	while( BusyUSART() );
	
	/* Send identification code */
	putcUSART(0b01011001);
	while( BusyUSART() );
	putcUSART(0b01001010);
	while( BusyUSART() );
	putcUSART(0b10110010);
	while( BusyUSART() );
	
	/* Send data */
	for( i = 0; i < size; i++ ) {
		putcUSART((data[i] & 0xAA) | (~data[i] & 0xAA) >> 1);
		while( BusyUSART() );
		putcUSART((data[i] & 0x55) | (~data[i] & 0x55) << 1);
		while( BusyUSART() );
	}
	
	/* Power off the transmitter */
	Delay1KTCYx(1);
	TX_PWR = 0;
}
Code: Select all
/* * *
 * Receiver Code
 */

void
main(void) {
	unsigned char byte;
	unsigned char test[3] = {0b01011001, 0b01001010, 0b10110010};
	unsigned char i;

	... // Setup receiver

	while( 1 ) { // Loop, forever receiving data
		PORTBbits.RB0 = 1; // Turn on receive LED
		
		i = 0;
		while( i < 3) {
			if( PIR1bits.RCIF ) {
				byte = getcUSART();
				
				if( RCSTAbits.OERR ) {
					RCSTAbits.CREN = 0;
					RCSTAbits.CREN = 1;
					break;
				}
				
				if( byte != test[i] )
					break;
				
				i++;
			}
		}
		
		if( i == 3 ) {
			PORTBbits.RB1 = 1; // Turn on valid receive LED
			byte = getData(i); // Should check for error (check i)
			byte = getData(i); // Should check for error (check i)
			PORTBbits.RB1 = 0; // Turn off valid receive LED
		}
		
		PORTBbits.RB0 = 0; // Turn off receive LED
	}
}

/* ---- Get Data ---- */
unsigned char
getData(unsigned char *error) {
	unsigned char odd, even;

	odd = getcUSART();
	even = getcUSART();
	
	if( (odd & 0xAA) ^ ((~odd & 0x55) << 1) ) {
		*error = 1;
		return 0;
	} else
		odd &= 0xAA;
	if( (even & 0x55) ^ ((~even & 0xAA) >> 1) ) {
		*error = 1;
		return 0;
	} else
		even &= 0x55;
	
	*error = 0;
	
	return odd | even;
}
If you have any questions, just let me know, I didn't bother to comment the stuff very well.

Also note, the Manchester encoding isn't exactly what you might be taught, as the bits are no longer in order( ie, its not b7, b7', b6, b6', ... b0, b0', where bn is the nth bit, and bn' is the nth bits compliment, its jumbled up, which saves on computation, but still ensures the same amount of DC balancing).

-Nate
By dmpthekiller
#25589
Thanks a Lot!!!

Sorry for taking too long to answer, but anyway thanks!!! People like you make this forum so great!!!

Doing some changes to your routines to adapt them to my needs, but they're just fine!!!...

If anything comes up, i'll just "shout"...
By ttabbal
#37645
I'm attempting to use the code Nate posted but I'm not getting the valid data notification on my receive PIC. It's connected to a Sparkfun UBW board (18F2455 Fosc = 48Mhz), so I changed the LED call to light the user LED on the UBW board. If I put a scope on the RX pin, I can see the background noise before I plug in the transmitter. Once the transmitter is operating the noise falls off quickly and I can see the spike of data come in. So the hardware seems to be working. RX code below. Any hints would be great.
Code: Select all
void main(void)
{      
   unsigned char byte;
   unsigned char test[3] = {0b01011001, 0b01001010, 0b10110010};
   unsigned char i;

  OpenUSART(USART_TX_INT_OFF &
   USART_RX_INT_OFF &
   USART_ASYNCH_MODE &
   USART_EIGHT_BIT &
   USART_CONT_RX &
   USART_BRGH_LOW, 
   311); //2400 bps
   
   while( 1 ) 
   { // Loop, forever receiving data
      
      i = 0;
      while( i < 3) 
      {
         if( PIR1bits.RCIF ) 
         {
            byte = getcUSART();
            
            if( RCSTAbits.OERR ) 
            {
               RCSTAbits.CREN = 0;
               RCSTAbits.CREN = 1;
               break;
            }
            
            if( byte != test[i] )
               break;
            
            i++;
         }
      }
      
      if( i == 3 ) 
      {
         mLED_2_On(); // Turn on valid receive LED
         byte = getData(i); // Should check for error (check i)
         byte = getData(i); // Should check for error (check i)
         mLED_2_Off(); // Turn off valid receive LED
      }
   }
And the getData function....
Code: Select all
unsigned char getData(unsigned char *error) 
{
   unsigned char odd, even;

   odd = getcUSART();
   even = getcUSART();
   
   if( (odd & 0xAA) ^ ((~odd & 0x55) << 1) ) 
   {
      *error = 1;
      return 0;
   } 
   else odd &= 0xAA;
   
   if( (even & 0x55) ^ ((~even & 0xAA) >> 1) ) 
   {
      *error = 1;
      return 0;
   } 
   else even &= 0x55;
   
   *error = 0;
   
   return odd | even;
}

Travis
By jasonharper
#37649
Travis, I don't see how the code you posted could compile at all - you should be getting a type mismatch on both calls to getData().

The LED is only going to be on for the duration of those two calls, which may not be sufficient for it to be visible.
By ttabbal
#37663
The only changes I made were for formatting and the LED call. Someone else posted that the code worked, so I didn't worry too much about it. It does compile. I'll be checking it more tonight.
By Philba
#37797
I don't get how that is manchester coding. per http://en.wikipedia.org/wiki/Manchester_code

for example,
original data 10 10 10 10 would encode in Manchester (GE Thomas) as
1001 1001 1001 1001 whereas the above algorithm would encode it as
1111 1111 0000 0000. I know the usart/NRZ would balance the transmission so it may well meet the needs of the transmitter but it's not really Manchester.
[/code]