Short discussion on radio reliability

Lets face it, since code always does exactly what you tell it to do, the only thing really “non-deterministic” about the wixel is the reliability of its radio link. So I’d like to hear a little from those who have tried some experimentation with the test_radio_signal_rx/tx . I’ve tried this pair of apps ( on 2 separate wixels, obviously) and looking at the serial output report, I’ll get listings like this with the two devices just inches apart…
39, -35, 99
47, -35, 100
56, -35, 101

and numbers like this with the two units 30 feet apart
61, -69, 106
63, -69, 103
58, -69, 105

Just to refresh, the 1st number is supposed to be the num of packets received in the programmed time frame, with 100 being perfect, the 2nd is supposed be the signal strength (-10 to -100 being normal), and the 3rd item is a measure of the link quality, with larger numbers being better.

Well of course I’m glad to see it work, and see that the link quality not too dependent on the distance change. But even if the process timing is an asynchronous approximation, the fact the packets received are so unreliable is cause for concern.

I have already tried many different orientations of the board, and if it matters the receiver is being USB powered, while the transmitter is powered by 4.5VDC from a fresh battery supply (3 AAA cells). I have read the supplied documentation on this and don’t need a refresher. I just want to know if the statistics should be better and what others are actually seeing. I suppose the programs themselves may contains a bug, but I would think these examples are reasonably bulletproof.

An interesting addendum! After switching apps, making the other wixel be the transmitter, the results were considerably better at any distance. This is the output at 10 feet from transmit to recv. Now I wonder if one of my wixels is not up to snuff. Still be interested to know what results other shave seen.

94, -63, 103
93, -63, 99
85, -63, 96
96, -63, 87
90, -63, 103
96, -63, 99
90, -63, 97
94, -63, 99
95, -63, 106

I’d like to re-open this discussion. I continue to see disappointing message success rates, on several code variations based on the methods in test_radio_signal_rx and its rx counterpart. I’m not posting any code yet, because the results are the same as those examples.

In my last post I reported a better ratio of good to bad messages after swapping which of the two wixels were doing the transmitting and receiving, but that improvement was not consistent. I have tried many board orientations, several radio channels, and several distance variations between the two boards. I’ve written a variation of the example code that simply sends packets at slower a “param” controlled rate rather than “bursts” of 100. No difference. I can tell that near 100% of my messages are detected at the receiver, but even at only 1 packet per 100mS, the average number of good packets received is often only around 50%, and sometimes worse.

I’ve tried different packet lengths, but never exceeded the 16 bytes in the original example. Since no one else has answered after dozens of views, I would really appreciate some input from Pololu as to what I can reasonably expect. Maybe test_radio_signal_rx and tx are unreliable methods? Maybe there are known better channels in the somewhat unregulated 2.4Ghz band? Maybe some libraries offer better modulation methods? Or maybe the truth is 50% message success is about what I can expect?

Hello.

We’ve seen 75-99% as typical packet success rates, so 50% is on the low side, but as you can see, the are many things that affect the results. Our apps generally use acknowledgment packets to ensure that no data is lost, or they just send a constant stream of packets so that packet loss can be tolerated.

–David

Your numbers I could tolerate, but 50% or less is getting bleak. I’m going to experiment some more. If I buy a 3rd wixel and I find that one particular device has has a repeatable consistent deficiency, would Polulu be willing to make a swap?

I’m not testing wireless communication between a wixel connected by USB to a host computer (running Cycling 74’s Max), and some remote wixels connected to 3pi robots.

I’m sending commands to the motors of the 3pi’s from Max. In my application i’m sending two sets commands for each action; one command/data byte for motor1 of the 3pi and another for motor2. I’m finding that about %50 of my packets are lost.

The wixel is running the wireless_serial app, and the 3pi the serial-slave app.

I too would love some more detailed input on what may be causing me trouble.
I would also like to know what serial settings optimize the communication between the wixel and the computer; Baud rate, parity, stop bits, and such.

Here’s a video that demonstrates my problem:

http://www.youtube.com/watch?v=VPU4hz6rj5o

This video was made with a modified version of wireless_serial that allows you to switch channels. I get the same results when using the non-modified wireless_serial app. Note that in my situation, i’m certain that the packet drops are not from the iphone/remote control of the apps. The way the system is designed, a single message from the iphone should trigger stop/start on both motors, never just one; in other words, the commands to both motors ARE sent out to the usb-connected-wixel.

batchku :

So far I’ve only delved into testing the radio reliability, so my application’s only serial I/O is through the USB simulated COM port interface, and it’s output is purely diagnostic. I’ve never seen a botched or corrupted serial message, so my periodic diagnostic output has allowed me to quantify how good (or bad) the communication is. Now I only have two of these wixels, but I’m definitely seeing that the overall good to bad ratio is measurably better using one particular wixel for transmission, and the other for receiving. How much better is not 100% consistent, but one combination is always much better, more in line with the 75%->99% or better success rate David says he sees. But the other combination is a dismal 20%->50% (sometimes 60%). Since your project is more in a complete stage and less in a diagnostic stage I don’t know how well you can tell. But I’d be curious whether swapping the wixel doing the transmitting in your setup seems to make a difference on not.

One thing I can see with your robots, it seems in an application like yours it would be better if both motors didn’t respond than for the two to end up in opposite states. So maybe it would be better to make sure all commands for all motors within a target device are contained within a single packet? Since a packet needs to be received AND pass an internal CRC check to be accepted, at least this would ensure the entire robot has all its motor states in agreement, whether that command be on or off or intentionally different for each motor.

For the final application I’m planning, I can see that I am going to have either send my command multiple times to ensure it gets through, or send continuously at some rapid but controlled rate.

DAVID!..

New observation: active USB interface decreases radio reliability!

In the diagnostic radio test I discussed which is directly based on radio_test_rx & tx, I had added code to turn on the Yellow LED anytime a good packet ( w/good CRC) is received, or the Red LED anytime a packet is received with bad CRC. (a timer makes sure they are both turned off after about 100 ms). So the net result is that by simply observing the receiving wixel, you can get a reasonable idea of the ratio of good to bad packets. I also send some formatted diagnostics to a the PC 9as in radio_test_rx), which i can view via a terminal program.

So with that in mind, here is my new observation. If I disconnect the terminal program from the COM port and wait about 5 minutes, OR if I go into my device manager (Win-Xp), and disable that COM port, the radio communication improves tremendously. Likewise, if i re-enable the port, the LED pattern instantly reveals a return to the poor radio reliability.

Obviously without the diagnostic output its impossible to exactly quantify the improvement, but sending packets every 10 mS and seeing the Yellow LED nearly steadily, with RED (bad CRC) as seldom as a few per minute is VERY significant!!! I don’t know what other experimenters are experiencing, and I don’t know yet whether I’d have the same observation if I transmitted my diagnostic messages using one of the MCU’s internal UARTs directly without the USB. But one other person that posted a few messages back, whose code was based on the wireless serial app, had a similar case of unreliable radio communication.

It may be something as insidious as a collision of interrupts or something more benign. maybe the wixel has no mechanism for queuing up radio messages in a an interrupt driven circular buffer, or maybe the problem just that it hasn’t been done yet? I’d think anything less is not giving the best possible priority to processing incoming packets, so that would explain why other time consuming processes (like maybe some delays in USB maintenance) might cause lots of messages to overwrite each other.

One thing seems very clear and repeatable… if you use the wixel to develop a stand alone radio link application that does not involve USB->COM port or similar serial communication, the radio reliability is much much better. That’s fine for me right now, because my final application won’t need USB or serial communication. But for the benefit of other Wixel users, it would be advantageous if Polulo could duplicated these results and track down the underlying reason. If you don’t have time, but think I’m probably right about an ISR being needed to buffer incoming radio packets, I’ll be happy to explore it.

If Polulu (or anyone) is interested in perusing this further and seeing the problem I’m talking about, the source is here…

/* radio test experiment, Randy constan (PeterPan on wixel forum,
 * email: r_constan@elfintechnologies.com

  This project forms the basis of what will become a more complex remote control app,
  but is currently used only to test the radio reliability. You use this same
  application for either the transmitter and receiver, based on configuration variables.

  If the app is set as a transmitter, you can set the number of milliseconds
  between packets sent. You don't need to do that on the receiver side. The structure
  being sent within the packet is largely unused right now. in the transmitter app, the RED LED
  will toggle  every time a packet is sent, but will pretty much seem to stay on
  if the transmit rate is fast, such as 10mS

  On the receive side, the Yellow LED will flash every time a GOOD packet is received, or
  if a packet is received but fails the internal CRC check, the RED LED will flash.
  If the transmit side is sending quickly, such as 10ms apart, it will not be obvious
  when each packet occurs, as the LEDs are both reset to OFF only every 100mS. But
  it is still a very good visual indicator of how prelaible the radion link is.

  If the receive side is used via a USB port, you can also use a terminal program
  to view some diagnostic information. Once per second, a report will be generated
  like the below...


 id=6703, rs=  610, lq=97     tp/s=98    ep/s=22
 id=6803, rs=  605, lq=103    tp/s=99    ep/s=20
 id=6903, rs=  611, lq=104    tp/s=98    ep/s=33
 id=7003, rs=  611, lq=104    tp/s=98    ep/s=18
 id=7103, rs=  605, lq=103    tp/s=99    ep/s=24
 id=7203, rs=  612, lq=106    tp/s=98    ep/s=25
 id=7302, rs=  598, lq=105    tp/s=100   ep/s=29

 the id is simply the latest message number, which is one of the things
 sent in the packet. in this case the transmitter was set to send a message every 10
 mS, and since  the receiver is generating a diagnostic msg every second,
 you can see that the message IDs are about 100 apart. The "rs" and "lq" are the average
 values of radio quality over the number of messages received since the last report.
 You'll have to look up the library calls  radioRssi() and radioLqi() to make sense of them.
 The tp/s is the total packets received per second, and the ep/s is the number of
 error packets per second.

 Note that this rate of errors does seem disappointingly high! However, the author has noted
 that the radio communication, as evidenced by observing the RED and YELLOW LEDs
 at the receiving end is much much better when the diagnostic information is not being monitored.
 If the COM port created by the receiving wixel in the PC (via the USB port)
 is inactive, either because it has been disabled in the device manager (Windows), or
 the terminal program used to monitor the diags has been shut down for more than about 5 minutes,
 (or the receiver is simply being run from an external supply with no USB), the author has
 observed a nearly constant YELLOW indication with only a few RED flickers over the course of
 a minute, when there was no USB activity. Note that in the authors test, the transmitting wixel
 was always unconnected to the PC, and powered via a 3 alkaline cell (4.5VDC) supply.

 Another poster on the Pololu wixel forum has stated that he was also having poor radio reliability,
 and I believe he was not using a PC link, but a UART.

 It is possible that the wixel is doing too many things at once, or it may be that
 interrupts and priorities are not being optimally managed in the particular
 library calls used here. However, the calls, taken from radio_test_tx and radio_test_rx
 examples, were chosen to enable the fastest possible transfer speed of raw packets.

 The discussion thread I started on this topic is on the wixel forum, and is called
 "short discussion on radio reliability ". If anyone has  insights on what might be going on,
 and can suggest ways of improving the ability of a receiver to be less affected by
 USB activity (or perhaps UART activity), please post your results there! :-)

  */

#include <wixel.h>
#include <radio_registers.h>
#include <stdio.h>
#include <gpio.h>
#include <usb.h>
#include <usb_com.h>

int32 CODE param_radio_channel = 128;
int32 CODE param_xmit_rateMS = 100; // how often to send a packet
int32 CODE param_transmit = 1;  // set 1 for transmitter, 0 for receiver
int32 CODE param_enabDiags = 1; // allows diagnostics via USB-> COMx. ( receiver only right now)


#define NUM_CTRLS 4

// This is a structure I'm planning on using for a future project. 90% of it
// is unused in this test.

typedef struct 	{
			uint8 buttonBits;				// will eventually contain bitmapped button states
			uint8 ctrl_count[NUM_CTRLS];	// up/down counts for remote digital potentiometers
			uint8 future1;					// place holders, so backward compatible additions
			uint8 future2;
		// DIAGNOSTIC BYTES
			uint8 msgDiagToggle;	        // packetsSent & 1;
			uint8 msgSndsPerSecond;			// unused currently
			uint16 msgID;					// incrementing packet counter
	} MSG_BUF;

#define RADIO_PACKET_SIZE (sizeof(MSG_BUF))

	// both xmit and recv  need size byte, receive needs 2 additional
static volatile XDATA uint8 packet[1 + RADIO_PACKET_SIZE + 2 ];
MSG_BUF * pMsgBuf = (MSG_BUF *)&packet[1];	// struct ptr for convenience

//  vars common to both xmit & recv
static uint16 DATA currMsgID = 0;

// receiver only vars, many are just for statistics right now.

enum {NO_PKT=0, GOOD_PKT, BAD_CRC_PKT};
static uint8 DATA newPktRecvd = NO_PKT;
static int16 DATA buttonBits = 0;
static int16 DATA rssiAvg = 0;
static uint16 DATA lqiAvg = 0;
static int16 DATA rssiTot = 0;
static uint16 DATA lqiTot = 0;
static uint16 DATA totPktsRecvdPerSec =0;
static uint16 DATA totErrorPktsRecvdPerSec =0;
static uint16 DATA badPktTempCount = 0;
static uint16 DATA goodPktTmpCount =0;
static uint16 DATA lastPacketReceivedTime = 0;
static uint16 DATA totPacketsRecvd = 0;

// transmitter only vars
static uint16 lastXmitTime = 0;


// a little over complicated, as it is used for both xmit and recv variations
void updateLeds(int x)
{
    usbShowStatusWithGreenLed();
    if (param_transmit)
    	{
    	  LED_YELLOW(0);
    	  LED_RED_TOGGLE();  // toggel red each time I send a packet
    	}
    else
    	{
    	// Recv mode. Yellow = good packet, red = bad. Makes it easier to
    	// troubleshoot without diagnostics being sent over USB port.

    	 if (x == GOOD_PKT) LED_YELLOW(1);
    	 if( x == BAD_CRC_PKT)  LED_RED(1);
    	 if (x==0) { LED_YELLOW(0); LED_RED(0);}
    	}
}



/*
 * Most of this is verbatum from the sample radio_test_rx/tx projects, except
 * that code  common to both xmit and recieve are separated from code used
 * for each individual case. In addition, for the transmit code,
 * the whole packet buffer is simply initialized to 0 instead of alpha characters.
 */
void perTestInit()
{
    radioRegistersInit();
    CHANNR = param_radio_channel;
    PKTLEN = RADIO_PACKET_SIZE;
    MCSM0 = 0x14;    // Auto-calibrate when going from idle to RX or TX.
    MCSM1 = 0x00;    // Disable CCA.  After RX, go to IDLE.  After TX, go to IDLE.
    // We leave MCSM2 at its default value.

    // this DMA config is always the same for DMA->radio transfers
    dmaConfig.radio.DC6 = 19; // WORDSIZE = 0, TMODE = 0, TRIG = 19 (always 19 for RADIO)

    if (param_transmit) // here we'll have to add more I/O pins later
    	{
			uint8 i;
			
			IOCFG2 = 0b011011; // put out a PA_PD signal on P1_7 (active low when the radio is in TX mode)
			
			dmaConfig.radio.SRCADDRH = (unsigned int)packet >> 8;
			dmaConfig.radio.SRCADDRL = (unsigned int)packet;
			dmaConfig.radio.DESTADDRH = XDATA_SFR_ADDRESS(RFD) >> 8;
			dmaConfig.radio.DESTADDRL = XDATA_SFR_ADDRESS(RFD);
			dmaConfig.radio.LENL = 1 + RADIO_PACKET_SIZE;
			dmaConfig.radio.VLEN_LENH = 0b00100000; // Transfer length is FirstByte+1
			dmaConfig.radio.DC7 = 0x40; // SRCINC = 1, DESTINC = 0, IRQMASK = 0, M8 = 0, PRIORITY = 0

			for(i = 1; i < sizeof(packet); i++)  packet[i] = 0; // flush whole array w/0 for now
			packet[0] = RADIO_PACKET_SIZE;
			RFST = 4;  // Switch radio to Idle mode.
    	}
    else
    	{
     	    dmaConfig.radio.SRCADDRH = XDATA_SFR_ADDRESS(RFD) >> 8;
    	    dmaConfig.radio.SRCADDRL = XDATA_SFR_ADDRESS(RFD);
    	    dmaConfig.radio.DESTADDRH = (unsigned int)packet >> 8;
    	    dmaConfig.radio.DESTADDRL = (unsigned int)packet;
    	    dmaConfig.radio.LENL = 1 + RADIO_PACKET_SIZE + 2;
    	    dmaConfig.radio.VLEN_LENH = 0b10000000; // Transfer length is FirstByte+3
    	    dmaConfig.radio.DC7 = 0x10; // SRCINC = 0, DESTINC = 1, IRQMASK = 0, M8 = 0, PRIORITY = 0

    	    DMAARM |= (1<<DMA_CHANNEL_RADIO);  // Arm DMA channel
    	    RFST = 2;                          // Switch radio to RX mode.
    	}

 }


/*
 * based on sendRadioBursts() in the test_radio_tx.c project,
 * but here I am only sending a single packet. Also, for now
 * the only thing changing within the packet is toggling (1->0)
 * byte, and a rolling message id, also used
 */

int sendRadioPacket()
{
    uint16 time = (uint16)getMs();

    // nothing to do if configured time hasn't passed,
    // or last transmission not complete,

    if ((uint16)(time - lastXmitTime) < param_xmit_rateMS || MARCSTATE != 1)
    	{
    	return 0;
    	}

    lastXmitTime = time;		// reset timer.

    // update some diagnostic data in packet

   	pMsgBuf->msgDiagToggle = currMsgID & 1;
    pMsgBuf->msgID = currMsgID++;
    packet[0] = RADIO_PACKET_SIZE; // redundant

    RFIF &= ~(1<<4);                   // Clear IRQ_DONE
    DMAARM |= (1<<DMA_CHANNEL_RADIO);  // Arm DMA channel
    RFST = 3;                          // Switch radio to TX
    updateLeds(1);					   // toggle red led to show activity.
    return 1;

}

/* based on receiveRadioBursts() from radio_test_rx project. in this case,
 *
 *
 */
// RF -> report buffer
// return non zero when its time for a report

int receiveRadioPacket()
{
	static uint32 lastTime = 0;
	static uint32 lastLedTime =0;
	uint32 timeNow = getMs();
	int reportDataReady = 0;


	if (!(RFIF & (1<<4))) newPktRecvd = NO_PKT;
        // if we're here, we got something!
	else newPktRecvd = (radioCrcPassed()) ? GOOD_PKT : BAD_CRC_PKT;

	switch(newPktRecvd)
		{
		case NO_PKT:	break;

		case GOOD_PKT:
			goodPktTmpCount++;
			totPacketsRecvd++;
			currMsgID = pMsgBuf->msgID;
	        lastPacketReceivedTime = (uint16)timeNow;
	        rssiAvg += radioRssi();
	        lqiAvg += radioLqi();
	        buttonBits = pMsgBuf->buttonBits;
	        break;

		case BAD_CRC_PKT:
			badPktTempCount++;
			totPacketsRecvd++;
		 	break;
		 }

	// keep track of good and bad packets/second.

   	 if ((uint16)(timeNow - lastTime) > 1000)
     	 {
     	 lastTime = timeNow;
     	 totPktsRecvdPerSec = goodPktTmpCount + badPktTempCount;
     	 totErrorPktsRecvdPerSec = badPktTempCount;
     	 if (!goodPktTmpCount)  buttonBits = -1; // in case we never got a good one
     	 goodPktTmpCount = badPktTempCount = 0;
     	 rssiAvg  = rssiTot / totPktsRecvdPerSec;
     	 lqiAvg = lqiTot/ totPktsRecvdPerSec;
     	 rssiTot = lqiTot = 0;
     	 reportDataReady = 1;
      	 }


   	 if (newPktRecvd != NO_PKT) // prep for next msg.
   		 {
         rssiTot += radioRssi();
         lqiTot += radioLqi();
         RFIF &= ~(1<<4);                   // Clear IRQ_DONE
         DMAARM |= (1<<DMA_CHANNEL_RADIO);  // Arm DMA channel
         RFST = 2;                          // Switch radio to RX mode.
	     updateLeds(newPktRecvd);
   		 }
   	 else
   		 {
   		  if ((uint16)(timeNow - lastLedTime) > 100)
   			  {
   			  lastLedTime = timeNow;
   			  updateLeds(0);
   			  }
   		 }


    return reportDataReady;
}

/*
 * display some statistics to show how well communication is progressing.
 * This is used for the receiver only.
 */
void reportResults()
{
    static uint16 lastReportSentTime;

    if (usbComTxAvailable() >= 64 && param_enabDiags != 0)
    {
    	uint8 XDATA report[80];
    	uint8 reportLength = sprintf(report, "\r\n id=%-4d, rs=%5d, lq=%-5d  tp/s=%-4d  ep/s=%-4d" ,
        			currMsgID, rssiAvg, lqiAvg,
        			totPktsRecvdPerSec, totErrorPktsRecvdPerSec );

    	usbComTxSend(report, reportLength);
  	}
}

void main()
{
    systemInit();
    usbInit();
    perTestInit();
  
    while(1)
    {
        boardService();
        usbComService();

        if (param_transmit)
        	{
        	 sendRadioPacket();
        	}
        else
        	{
        	 if (receiveRadioPacket())
        		 {
          		 reportResults();
        		 }
        	}
    }
}

Hello, batchku.

I am sorry you are having trouble with the Wixel. This thread is a discussion of low-level packet success rates for the radio, and is unlikely to be related to your problem. The Wireless Serial app we provide uses a radio protocol that has acknowledgment packets and the ability to handle packet loss. Let’s continue the discussion of your problem in the other thread I just started:

–David

PeterPan, thanks for the interesting result! However, your LED-based feedback is reporting something different than what the standard test_radio_signal_rx app reports. You are treating packets that were received with bad CRCs differently than packets that weren’t received at all, and it sounds like you are neglecting to report the latter. Here’s a plan: the transmitter should send one packet every 100 ms. If the receiver gets a good packet, it should turn on its yellow LED, and it should turn off the yellow LED if it hasn’t received a good packet in the last, say, 90 ms. If you see the LED on about half the time, you know that only half the packets are getting through.

My guess is that the 5 minute delay is the amount of time it takes for the buffers in the USB-to-serial driver to get filled with data from your app. After the buffers are filled, the driver should stop polling the Wixel for serial data and there should be a significant drop in the amount of USB traffic.

Regarding your speculation about batchku’s problem: the radio_link library used by the Wireless Serial app has a mechanism for queuing up incoming and outgoing radio packets in an interrupt-driver circular buffer.

It’s not very efficient to be shipping cheap boards back and forth. It sounds like you are doing some interesting things with the Wixel, so if you email us directly and refer to this thread I’ll see if I can at least get you a discount on some new Wixels.

–David