Using radioLink library

Hi,
Trying to use radio link library to send a string to a Maestro from a Wixel.
My problem is that radioLinkTxCurrentPacket() is returning zero.
Is there more to the library than the include, init and get a handle to the current packet?
Here is some test code:

/* wireless_gyro_tracker app:
 * James Graham Software Specialites
 * Allows you to make a wireless pan tilt tracker using two Wixels, and a gyro,
 *
 * One Wixel should be running the wireless_maestro_link app and be
 * connected to a Maestro via serial.  The other Wixel should be running this app,
 * and be connected to the gyro 
 *
 * == Pinout ==
 *
 * P0_1 = Gyro Z analog input
 * P0_2 = Gyro X analog input
 *
 *
 * == Parameters ==
 * param_radio_channel 
 */

#include <wixel.h>
#include <usb.h>
#include <usb_com.h>
#include <radio_link.h>
#include <math.h>
#include <stdio.h>


#define TX_INTERVAL 30 // time between transmissions (ms)

//int32 CODE param_radio_channel  = 128;
void updateLeds()
{
    usbShowStatusWithGreenLed();
    LED_YELLOW(vinPowerPresent());
}

void txGyroState()
{
    static uint8 lastTx = 0;
 
    if ((uint8)(getMs() - lastTx) > TX_INTERVAL )
    {
      if (usbComTxAvailable() >= 64)
      {
        uint8 XDATA report[64];
        uint8 XDATA servoCmd[18];
        uint8 reportLength;
	uint8 XDATA * packet = radioLinkTxCurrentPacket();
        uint16 fx, fz;
        fx = adcRead(2);
        fz = adcRead(1);
 //   reportLength = sprintf(report, "%4d, %4d\r\n", fx, fz);
 //   usbComTxSend(report, reportLength);
	
		if (packet != 0)
		{
			if (fx > 850)
			{
				reportLength = sprintf(servoCmd, "1800 0 SERVO ");
			}
			else if (fx < 750)
			{
				reportLength = sprintf(servoCmd, "600 0 SERVO ");
			}
			else
			{
				reportLength = sprintf(servoCmd, "1200 0 SERVO ");
			}
                        reportLength = sprintf(report, "%4d, %4d\r\n", fx, fz);
                        usbComTxSend(report, reportLength);
			sprintf(packet, "%4d %s",reportLength,servoCmd);
			radioLinkTxSendPacket(0);
		}

      }


       lastTx = getMs();
    }
}

void main()
{
    systemInit();
    usbInit();
    radioLinkInit();

    //disable pull-ups on inputs hooked to Gyro outputs
    setDigitalInput(1, HIGH_IMPEDANCE);
    setDigitalInput(2, HIGH_IMPEDANCE);

    while(1)
    {
        updateLeds();
        boardService();
        usbComService();
        txGyroState();
    }
}

Hello, jamgrah.

The Maestro does not parse strings like "1800 0 SERVO " (that’s a script command that would have to be compiled on a PC and loaded on to the Maestro over USB). If you want to send commands from a Wixel to a Maestro, you should use the Maestro’s serial interface and use the Set Target command. Be sure to put the Maestro in to the right serial mode. The documentation is in the “Serial Interface” section of the Maestro User’s Guide:
pololu.com/docs/0J40

As the documentation for radioLinkTxCurrentPacket() says, that function will return 0 if there is no packet available. When it returns 0, that means that all of your TX packet buffers are busy holding data to be sent to the other Wixel.

Also, you need to put the length of the payload data in to packet[0] as a single byte, not an ascii string; your line that prints to the packet is wrong. Please see the example code in the documentation of radioLinkTxCurrentPacket().

How did you program/configure the receiving Wixel? There needs to be a receiving Wixel that is actually reading packets from the radioLink library, or else the buffers on the transmitting Wixel will fill up quickly and radioLinkTxCurrentPacket will return 0.

–David

Thanks David,
I am asking for clarification on the library I have read the brief “include/radio_link.h File Reference” that you linked to in your reply.
This I assume this is the code fragment you are referring to:

uint8 XDATA * packet = radioLinkTxCurrentPacket();
if (packet != 0)
{
    packet[0] = 3;   // payload length.  Must not exceed RADIO_LINK_PAYLOAD_SIZE.
    packet[1] = 'a';
    packet[2] = 'b';
    packet[3] = 'c';
    radioLinkTxSendPacket(0);
}

I understood that packet would be zero when there are no more packets available.
There is still a great more to using the library, such as setting up the receiving Wixels with something like this:

is there a sample app that uses this protocol?
I have read and understand the wireless_serial app specifically:

   while(uart1RxAvailable() && radioComTxAvailable())
    {
        radioComTxSendByte(uart1RxReceiveByte());
    }

    while(radioComRxAvailable() && uart1TxAvailable())
    {
        uart1TxSendByte(radioComRxReceiveByte());
    }

moving the data a byte at time and acting on it in this case sending along serial to a Maestro.
I was pursuing that (radioCom) approach when I read about the link library and the queue library.
When should we use which library and are there examples?
I have read the the Maestro’s serial interface and will use the set target commands, I had missed the script difference, I was trying to code up an example to help you understand.
I want to control my Maestro via wireless serial.
Should I:
Use the radioCom library on both ends(follow wireless_serial.c).
Set up the Maestro serial mode (like uart 9600) .
Send serial commands such as set target.
Thank you,
James

I am posting a second round of sample code using radioCom
First is the Wixel connected to a sensor array like IMU for now just a gyro.

/* wireless_gyro_tracker app:
 * James Graham Software Specialites
 * Allows you to make a wireless pan tilt tracker using two Wixels,
 *  and a gyro
 *
 * One Wixel should be running the wireless_maestro_link app and be
 * connected to a Maestro via serial.  The other Wixel should be running this app,
 * and be connected to the gyro 
 *
 * == Pinout ==
 *
 * P0_1 = Gyro Z analog input
 * P0_2 = Gyro X analog input
 *
 *
 * == Parameters ==
 * param_radio_channel 
 */

#include <wixel.h>
#include <usb.h>
#include <usb_com.h>
#include <radio_com.h>
#include <math.h>
#include <stdio.h>


#define TX_INTERVAL 30 // time between transmissions (ms)

//int32 CODE param_radio_channel  = 128;
void updateLeds()
{
    usbShowStatusWithGreenLed();
    LED_YELLOW(vinPowerPresent());
}

void txGyroState()
{
    static uint8 lastTx = 0;
 	uint8  servo, index;
    uint8 XDATA servoCmd[4];
    uint16 target,fx, fz;

    if ((uint8)(getMs() - lastTx) > TX_INTERVAL )
    {
      fx = adcRead(2);
      fz = adcRead(1);
	  
      if (usbComTxAvailable() >= 64)
      {
        uint8 XDATA report[64];
        uint8 reportLength;

		reportLength = sprintf(report, "%4d, %4d\r\n", fx, fz);
		usbComTxSend(report, reportLength);
	  }
	  index = 0;
	  servo = 0;
	  target = 1200;
	  if(fx > 850)
		target = 1800;
	  else if(fx < 750)
	    target = 600;
			
	  servoCmd[0] = 0x84; // command byte: Set Target.
	  servoCmd[1] = servo; // channel number.
	  servoCmd[2] = target & 0x7F; //  byte holds the lower 7 bits of target.
	  servoCmd[3] = (target >> 7) & 0x7F; // byte holds the bits 7-13 of target.
	  while(radioComTxAvailable() & (index < 4))
	  {
	    radioComTxSendByte(servoCmd[index++]);
	  }

    }
    lastTx = getMs();
}

void main()
{
    systemInit();
    usbInit();
    radioComRxEnforceOrdering = 1;
    radioComInit();

    //disable pull-ups on inputs hooked to Gyro outputs
    setDigitalInput(1, HIGH_IMPEDANCE);
    setDigitalInput(2, HIGH_IMPEDANCE);

    while(1)
    {
        updateLeds();
        boardService();
        usbComService();
        txGyroState();
    }
}

Second is the Wixel connected to the Maestro.

/* wireless_maestro_link app:
 * James Graham Software Specialites
 * Allows you to make a wireless pan tilt tracker using two Wixels, and a gyro.
 *  
 * 
 * One Wixel should be running the wireless_gyro_tracker app and be
 * connected to a Maestro via serial.  The other Wixel should be running this app,
 * and be connected to the gyro.
 *
 * == Pinout ==
 *  TX  P0_4
 *  RX  P0_5
 *
 * == Parameters ==
 *  param_baud_rate
 */
 
#include <wixel.h>
#include <usb.h>
#include <usb_com.h>
#include <radio_com.h>
#include <uart1.h>

int32 CODE param_baud_rate = 9600;

void updateLeds()
{
    usbShowStatusWithGreenLed();
    LED_YELLOW(vinPowerPresent());
}

void radioUartService()
{
    // Data
    while(uart1RxAvailable() && radioComTxAvailable())
    {
        radioComTxSendByte(uart1RxReceiveByte());
    }

    while(radioComRxAvailable() && uart1TxAvailable())
    {
        uart1TxSendByte(radioComRxReceiveByte());
    }

}

void main()
{
    systemInit();
    usbInit();

    uart1Init();
    uart1SetBaudRate(param_baud_rate);
    radioComRxEnforceOrdering = 1;
    radioComInit();

    while(1)
    {
        updateLeds();
        boardService();
		usbComService();
        radioUartService();
    }
}

We don’t have any sample apps that directly use the radioLink library, but you could look at the io_repeater, radio_sniffer, wireless_tilt_mouse, or wireless_tilt_mouse_receiver. They use the radioQueue library, which has an interface similar to radioLink (but a completely different implementation because it doesn’t do acknowledgements).

You can also look in the radioCom library, because it uses the radioLink library.

If you want lossless communication between exactly 2 Maestros, it is fine to use either radioCom or radioLink. It just depends on whether it is easier for your application to deal with the data as packets or as a bytes. The radioLink library lets you deal with the data as packets, so you get more control over how the data actually gets sent out on the radio and it is easier to implement multi-byte commands, but this extra control makes the radioLink library harder to use. The radioCom library exists as a wrapper around radioLink that allows you to just treat the data as streams of bytes, so you don’t have to use pointers or worry about packets.

For what you are trying to do right now, I think it is easier for you to use radioCom library on both Wixels. Also, I think you should use the unmodified wireless_serial app in UART-to-Radio mode on the receiver side.

Yes, your Maestro’s serial mode should be “UART, fixed baud rate”, and you should send binary Set Target commands to it.

The shiftbrite, serial_i2c, and wireless_serial apps are good examples of how to use the radioCom library.

–David

Regarding your transmitter code:

On this line, you accidentally used binary AND (&) instead of logical AND (&&):

Also, the way you are sending data to the radio makes it possible that you would send an incomplete command, which would cause a serial protocol error on the Maestro. I would restructure it to be like this:

if (radioComTxAvailable() >= 4)
{
    radioComTxSendByte(0x84);  // command byte: Set Target.
    radioComTxSendByte(servo); // channel number.
    radioComTxSendByte(target & 0x7F); //  byte holds the lower 7 bits of target.
    radioComTxSendByte((target >> 7) & 0x7F); // byte holds the bits 7-13 of target.
}

Regarding your receiver code:

Unfortunately, the Wixel SDK’s UART library actually uses the alternate location (TX=P1_6 and RX=P1_7) for uart1, not the pins that you wrote in your comment. The library does not yet let you choose which UART location to use, so you would have to write a little bit of extra code to reconfigure the UART if you really want to use P0_4 and P0_5. That would definitely be a good feature to have in the library, so feel free to fork the wixel-sdk on github.com and work on that.

Also, you should not set radioComRxEnforceOrdering to 1 because you are not reading the control signals from radioCom.

–David

David,
Great stuff very informative.
Thanks for the coding catches and suggestions, marking sure there is 4 bytes available then sending them all at once is great.
I did not see the pins where in the alternate location, knowing the right pins will definitely help!
The pins to use are easy to change the wires to different header locations.
I might try the link packets now that I understand what is going on and who wraps whom.
But for now com should work in this simple app.

In the Maestro from serial commands I can execute a subroutine passing a parameter to the stack.
And some serial commands return values as in get position.
Also a Maestro subroutine called from serial can execute SERIAL_SEND_BYTE to send a byte or stream back to the serial port on the Maestro.
My assumption (Maestro users guide 5b) is that these values will be sent on the Maestro tx line to the receiving Wixel passing them wireless to the gyro Wixel as my receiver code is listening to the Maestro and passing along what appears.
I will need to add a receive service to my gyro tracker Wixel code to process these responses.

Exciting might get a working test soon.
Thank you,
James

Ok. Just remember that the Maestro’s TX line will output 5V, so you will need extra circuitry (such as a voltage divider) to connect it to the Wixel’s RX line. None of the Wixel’s I/O pins are 5V tolerant.

Also, I would try to get one-way communication working before adding the other direction.

–David

David,
I built a translation board with a couple of transistors and resistors to push the 3.3 up and pull the 5 down.
Just testing one way but starting to code to enable receive from Maestro.
No success last night, debugging is slow.
I am using notepad++ and dos make, then Pololu Wixel Configuration tool.
I must load the recompiled wxl file each time in the configuration tool.
Going to try simply test tonight and fall back to loading both with usb to serial test app if I can not get my code to talk. I need to simplify my test harness and take smaller development test cycles.

Can you offer debug ideas?

James

Hello.

The Wixel Configuration Utility is not great for app developers. You can use wixelcmd to quickly load your app on to all the Wixels connected to the computer, or specify which Wixel to load it on by serial number. Better yet, you can type “make load_APPNAME” at your command prompt to both build the app and load it on to all the Wixels using wixelcmd.

Also, the Eclipse IDE takes some time to set up correctly, but it is much nicer to use than Notepad++. We talk about Eclipse in the Wixel User’s Guide:
pololu.com/docs/0J46/10.c

–David