RS232 and string parsing

Hi all,

I use orangutan SVP1284 controller and I’m trying to write a simple program that will get servo target data from the user via RS232, convert this string (4 byte string) into int and send the servo to that position.
I based my code on the orangutan serial example.

The code runs well the first time I enter a target but at the second time nothing happens.

here is the function that processes the incoming char :

void process_received_byte(char byte)
{
	int target;

	if (byte == '\r')
	{
		sscanf(receive_buffer,"%d",&target);
		set_servo_target(0,target);
		receive_buffer_position = 0;
		receive_buffer[0] = '\0';
		serial_cancel_receive(UART1);
		serial_receive(UART1, receive_buffer, sizeof(receive_buffer));
		print("Done!");
	}
}

I fear it has something to do with the SERIAL_GET_RECEIVED_BYTES function, that i can’t initialize after a carriage return has been detected (that’s why I canceled receiving and started again, but it does not work…).

BTW - if someone has a more efficient idea on how to control the servo - I would love to hear it (the end result will be 2 servos and to motors controlled by RS232 string, about 40-50 times a second).

Thanks in advance for the help…

M.

First of all, the Orangutan SVP does not support RS232 without additional hardware to shift the voltages. It does support TTL serial, which it looks like you are trying to use.

You should try adding a string terminator (’\0’) so that sscanf is guaranteed to not use old data left over in the buffer from last time. But I can’t really tell if that’s necessary without seeing the rest of your code.

How fast are you sending these strings to the SVP, and what other tasks are you performing in your main loop? The reason I ask is because you have a race condition here. If your UART receives a byte of the second integer before you have processed the first integer, that byte will be discarded when you do serial_cancel_receive so the second integer will not be received correctly.

The SVP has many tools that can help you debug your program: 16x2 LCD, buzzer, and two LEDs. I suggest that you play a short beep every time you receive ‘\r’ and print the contents of your serial receive buffer to the LCD. You should use print_hex_byte so that you can tell exactly what is in your serial receive buffer, including special characters like ‘\r’ and ‘\n’. Also print the scanned value of “target” to the LCD.

I don’t understand your comment about serial_get_received_bytes(). That function should work. What do you mean by “initialize”?

If you need more help, you should simplify your code to the simplest possible thing that should work, but doesn’t, and then post the entire code here. Part of this simplification should be take out all the servo code (replacing it with printing to the LCD).

In terms of efficiency, it would be more efficient to send the commands as binary values. Then it would only take 2 bytes for a servo position, instead of 5 (4 digits plus separator), so you can send the updates faster. Also, you wouldn’t have to use sscanf. But then it gets tricky to tell when one command stops and the next command begins. You could check out the serial protocol we use on our other products like this one to see how we solved this issue:

–David

Hello David, and thanks for the quick reply.
I am using XBEE for wireless communications with a regulated board, so i can stay at TTL level and still have serial communication.

At the moment, I’m sending the commands manually via terminal, e.g - 1500 and then press enter.
The servo moves, but if you’ll look at the code I posted, it should print “Done!”.
what I get is that the LCD screen is getting filled with “Done! Done! Done! Done! Done! Done!..” , like the program is entering some kind of a loop.

after I finish reading the command and issuing it to the servo, I want to prepare the buffer and counters to start the process all over again.
at first, I zeroed only the serial_receive_position counter and placed a NULL in the first byte of the buffer.
But then I have a problem (at least I think so) with this condition:
“serial_get_received_bytes(UART1) != receive_buffer_position” because the serial_get_received_bytes counter was not zeroed.

I will go over the advices you gave me in your reply, and I’ll see what I can make of it.
using your protocol will indeed be more efficient - I’ll try to get the hang of it.

thanks for helping,
M.

Okay, so you’re not using RS232.

This LCD behavior is important information that should have been in your first post. Obviously your process_received_byte function is being called multiple times when it shouldn’t be. Without seeing the rest of your code, I can’t give any advice on why that might be happening.

The count returned by serial_get_received_bytes should be zero after calling serial_receive. If you want help with that, you should write a very simple test program to reproduce the problem and post it here.

–David Grayson

Thanks for the reply, again.
I’m changing my code strategy - I’ll give it a try and post the results here.

regards,
M.

Hello again,
Here is the new (and working) function:

void process_received_byte(char byte)
{
	int target;

	if (byte != '\r')
	{
		temp_buffer[i] = byte;
		i++;
	}
	else
	{
		temp_buffer[i] = '\0';
		sscanf(temp_buffer,"%d",&target);
		servos_start((unsigned char[]){IO_D0,IO_D1}, 2);
		set_servo_target(0,target);
		i = 0;
				
	}
}

As you can see, I am not touching the receive buffer anymore, but to a temporary one.
once I enter a target and hit enter, the servo goes there, but every 2 or 3 targets, there is a pause (sometimes of a few seconds) until the command is being carried out.
I fear it will get worse once I’ll move to 2 servos and use the sscanf to make 2 integers from the string.

what I also don’t like is that I keep calling servos_start (in addition to the one in the beginning of the main function) and I think it slows me down.
another thing - the use of sscanf, which I would gladly scrap.
I looked at the binary protocols you attached previously but I don’t see how can I implement them without using sscanf.

regards,
M.

I’m glad you’re making progress. Storing the received line in a separate buffer as you are doing is a good idea because then you don’t have to use serial_cancel_receive.

You should probably add a check to your program to guarantee that the “temp_buffer[i] = byte” line does not write to arbitrary places in memory when the user sends line that is longer than expected.

You don’t need to call servos_start every time. Just do it once at the beginning of your program. It doesn’t start sending pulses until you actually set a target.

I can’t help you debug the unwanted pause unless you simplify your code and post the entire program.

As far as I know, sscanf won’t help you do binary protocols at all because it only deals with ASCII strings. If you want to do a binary protocol, the SVP code would look vaguely like this (it depends on what your actual protocol is):

unsigned int servo_target = command_packet[1] + 256*command_packet[2];

Here command_packet is an array of bytes received on the serial port which represents a command from the user.

–David

UPDATE:
I switched to binary (similar to what you suggested) and all my problems disappeared.
apparently, SSCANF() is a very time demanding function which should be a avoided, as it completely crippled my program.
now I send less bytes and get much quicker processing time.

thanks for all the help, now it is time to deal with the motors.
regards,
M.

Hello again,
both the PC and the orangutan software are ready.
I aim at getting about 30-40 updates per second with 9 bytes of control - 2 servos, 2 motors and a CR to mark the end of the string.
right now the code responds slowly - at about an update per second.
please find attached the code, I would be very happy to hear ideas for improvements.

#include <pololu/orangutan.h>  
#include <D:\programming\winavr\avr\include\stdio.h>
#include <D:\programming\winavr\avr\include\string.h>

// receive_buffer: A ring buffer that we will use to receive bytes.
char receive_buffer[32];
//temporary buffer to store current reading
char temp_buffer[32];
int i;
// receive_buffer_position: This variable will keep track of which bytes in the receive buffer
// we have already processed.  
unsigned char receive_buffer_position = 0;
// send_buffer: A buffer for sending bytes 
char send_buffer[32];
// wait_for_sending_to_finish:  Waits for the bytes in the send buffer to
// finish transmitting.  
void wait_for_sending_to_finish()
{
	while(!serial_send_buffer_empty(UART1));
		
}

// process_received_byte: Responds to a byte that has been received on
// UART1.
void process_received_byte(char byte)
{
	int target0;
	int target1;
	int m1;
	int m2;

	if (byte != '\r')
	{
		temp_buffer[i] = byte;
		i++;
	}
	else
	{
		//This segment parses the data from the buffer and sets command
		//to the servos and motors.
		//format is: 
		//[pan:msB][pan:lsB][tilt:msB][tilt:lsB][m1:msB][m1:lsB][m2:msB][m2:lsB]
		//servos takes values 400 to 2450 and motors -255 to 255 (speed and direction).
		temp_buffer[i] = '\0';
		target0 = temp_buffer[0]*256+temp_buffer[1];
		target1 = temp_buffer[2]*256+temp_buffer[3];
		m1 = (temp_buffer[4]*255+temp_buffer[5])-255;
		m2 = (temp_buffer[6]*255+temp_buffer[7])-255;
		set_servo_target(0,target0);
		set_servo_target(1,target1);
		set_motors(m1,m2);
		clear();
		print_long(m1);
		lcd_goto_xy(0, 1);
		print_long(m2);
		i = 0;
				
	}
}

void check_for_new_bytes_received()
{
	while(serial_get_received_bytes(UART1) != receive_buffer_position)
	{
		// Process the new byte that has just been received.
		process_received_byte(receive_buffer[receive_buffer_position]);

		// Increment receive_buffer_position, but wrap around when it gets to
		// the end of the buffer. 
		if (receive_buffer_position == sizeof(receive_buffer)-1)
		{
			receive_buffer_position = 0;
		}
		else
		{
			receive_buffer_position++;
		}
	}
}

int main()
{
	
	servos_start((unsigned char[]){IO_D0,IO_D1}, 2);

	// Set the baud rate to 115200 bits per second.  Each byte takes ten bit
	// times, so you can get at most 11520 bytes per second at this speed.
	serial_set_baud_rate(UART1, 115200);

	// Start receiving bytes in the ring buffer.
	serial_receive_ring(UART1, receive_buffer, sizeof(receive_buffer));

	// Make the servo go to a neutral position.
	set_servo_target(0, 1300);
	delay_ms(1000);
	set_servo_target(1, 1300);
		
	clear();	// clear the LCD
	print("Servos ready!");
	lcd_goto_xy(0, 1);	// go to start of second LCD row
	print("Enter target");
	   

    while(1)
    {
		// Deal with any new bytes received.
		check_for_new_bytes_received();
		
    }
}

At first I thought it might be the baud rate so I increased it to 115200 baud, but it did nothing to improve the situation (maybe even worse).
theoretically, 38400 should be more than enough.

Maybe the application is an overkill for this micro controller?

Best regards,
M.

Hello. The only thing in your main loop that could possibly take a long time are your calls to the LCD related functions. The LCD functions will take a long time to finish on the SVP if your LCD is not plugged in. Is your LCD plugged in? Try removing those function calls.

–David

Hello David,

My LCD is connected and I removed the calls but it did not improve performance.
I did not look into the servo and PWM routines in the pololu library, do you think that 30-40 updates per seconds of my main loop are reasonable, or is it too much for the processor?
If it is reasonable, then I will look elsewhere - communication problems and data loss issues.

Regards,
M.

Fourty updates for per second is reasonable. Could you give more details on the experiment you did that makes you think your program is taking a second to update?

The servo and motor code should be pretty efficient. If you doubt it, you could try taking that code out, and just add some code that toggles an LED whenever a command is received.

Also, I noticed something that might cause some problems: you are using ‘\r’ as a record separator. On the PC side, are you making sure that none of the data bytes you send happen to be ‘\r’ (13)?

Also, I don’t think this line serves a purpose, so you could remove it:

–David

Well, basically my setup is a PC control station, written in C#, with a gaming wheel and pedals attached to it.
the wheel is being polled by a directx routine, the values of the pedals and the wheel position are being mixed to form a differential drive signals for the left and right tracks.
the servos are in fact a pan and tilt system for a video camera, which is controlled by a head tracking system, based on an LED and a webcam, which is not implemented yet.
a timer control fires every X ms and a reading of all the peripherals occur.
from there, it sends 9 bits to the robot through xbee 2 wireless modem.
when I configure the timer to, say 500 ms, i press the pedal, the robot starts almost immediately, but then response times are very slow.
sometimes it takes about 2 seconds from the time I leave the brake to a complete stop of the robot.
the robot is sitting on a jack at a fixed distance from the PC (so it won’t move until everything looks tuned up).

now, since you think the processor is not the bottleneck, the main suspect returns to be the xbee module…

Regards,
M.

Hello.

Do you have access to an oscilloscope? It might be much easier to find the bottleneck if you could look at the timing of your signals.

- Ben

Hello Ben,

I happen to have an oscilloscope ready.
what (or where) do you propose I should measure?

You can then use the oscilloscope to monitor various points in your system, such as the connection between your computer and the XBee and your connection between the Orangutan and the other XBee. If you want to see if the slowdown is due to the Orangutan, you can add some debugging lines to your code that drive a digital output high when an event occurs and drive it low once that event has been handled. You can use the oscilloscope to measure how long this takes and to see the frequency with which the event is occurring.

- Ben

I tried sampling a bit of the Data in pin at the robot side and i get weird results.
It has a repeating cycle of 9 1 second transitions and then quiet for about 5 seconds.

I read somewhere in the orangutan manual that i can listen to the serial port through the USB channel.
if i can do that, it might shed some light on what passes through there.
If it is true, I would love to know how it is done.

right now it seems most logical that the problem is around the communication section of the project.

regards,
M.

One of the functions of the auxiliary processor is to be a USB-to-TTL serial adapter, using the pins labeled “D/RX” and “TX” on the board. If you connect your XBee’s transmit line to D/RX, you should be able to see the bytes from the XBee on a PC over the SVP’s USB connection. See this page for more info:

–David

Problem solved!

It was indeed an Xbee problem - a tweak needed in the ZNET 2.5 protocol, requiring to address a specific node to get smooth and fast communication…go figure.

I’m now working at 20 Hz, very responsive throttle reactions.
If someone is interested in the details, feel free to contact me.

I do however have an electro mechanical problem with the RP5, but I’ll open a new post in the designated area for that one.

thanks again for all the help,

Best regards,
M.