Arduino-Orangutan communication

Dear all,

I want to establish a serial communication between an Arduino Pro Mini 328 acting as slave device and a Baby Orangutan B-328 controller acting as Master device.

Now what exactly I need to do is when required, my Baby-O controller to read some particular data from Arduino pro. For example, I have programmed the following code to the Arduino:

int incomingByte = 0;   // for incoming serial data

void setup() {
  // initialize both serial ports:
  Serial.begin(9600);
}

void loop() {
}
    
void serialEvent(){
   if (Serial.available() > 0) {
        //read the incoming byte:
        incomingByte = Serial.read();
        if (incomingByte == 'a'){
            int value1 = 10;
            int value2 = 100;
            int value3 = 1000;
    
            Serial.print('H');
            Serial.print(",");
            Serial.print(value1, DEC);
            Serial.print(",");
            Serial.print(value2, DEC);
            Serial.print(",");
            Serial.print(value3, DEC);
            Serial.print(",");
            Serial.println();
            delay(100);
        }
        Serial.flush();
    }
    //char inByte = 7;
    //Serial.print(inByte, DEC); 
   
}

Therefore, when the Arduino receives the ASCII character ‘a’, it transmits the following frame:

H,10,100,1000,

Now, on the Baby-O side, I manage to send the requesting character “a” as follows:

#include <pololu/orangutan.h>

// A global ring buffer for data coming in.  This is used by the
// read_next_byte() and previous_byte() functions, below.

char buffer[100];

// A pointer to where we are reading from.

unsigned char read_index = 0;


char read_next_byte()
{
	char ret=0;

	while(serial_get_received_bytes()!=read_index)
	{
		ret = buffer[read_index];

		read_index ++;
		if(read_index >= 100)
			read_index = 0;
	}
	return ret;
}

int main()
{	
	// start receiving data at 115.2 kbaud
	serial_set_baud_rate(9600);
	//serial_set_mode(SERIAL_CHECK);
	//serial_receive_ring(buffer, sizeof(buffer));

	while (1)
	{
	// wait for a Data
		//char Data = read_next_byte();
		//unsigned char Data= serial_get_received_bytes();
		
		serial_send("a",1);
		delay_ms(500);
		

	}
	return 0;
}

However, I am confused with the Byte-ASCII transformations and I am not sure how to read the three integers (send by the Arduino) 10, 100, 1000 separated by commas. Please help!

Thank you in advance,

Akis

Hello, Akis.

You should be careful about using the Arduino’s hardware serial lines (TX and RX) because those lines are actually used during programming. It would be safer to use SoftwareSerial and pick some other pins.

It sounds like you have made no progress on reading the serial response from the Arduino. As a first step, I recommend writing a function in your Orangutan code that simply reads an entire line from the serial port, storing it in a buffer. This function should be called right after you send the “a” command. The pseudo code will look like this:

  1. receive a byte and store it in the buffer
  2. if the byte was not the newline character ‘\n’, go back to 1

–David

Hi David,

The truth is that I haven’t done any progress on the Arduino-Orangutan communication because I have built the motor PID control and it works perfectly!

Now, I am implementing the following program in the Orangutan:

#include <pololu/orangutan.h>

// A global ring buffer for data coming in.  This is used by the
// read_next_byte() and previous_byte() functions, below.

char buffer[100];

// A pointer to where we are reading from.

unsigned char read_index = 0;

char read_next_byte()
{
   char ret=0;

   while(serial_get_received_bytes()!=read_index)
   {
      ret = buffer[read_index];

      read_index ++;
      if(read_index >= 100)
         read_index = 0;
   }
   return ret;
}

int main()
{   
   // start receiving data at 9.6 kbaud
   serial_set_baud_rate(9600);
   serial_receive_ring(buffer, sizeof(buffer));

   while (1)
   {
   // wait for a Data
      
     // unsigned char Data= serial_get_received_bytes();
      
      serial_send("a",1); 
      char Data = read_next_byte();
      delay_ms(500);
      
   }
   return 0;
}

Whenever the Orangutan sends the “a” byte, I have confirmed that the Arduino sends the following frame:

H,10,100,1000,

Theoretically, this frame is stored in the ring char buffer[100]. My problem is that I have no means to display the contents of buffer in my screen (since the Baby-o serial port is occupied) in order to have a better idea on how to handle the data. I was wondering if you could provide some actual coding on how to manipulate the data inside the buffer in order to distinguish the number bytes and assemble then into integers once again.

Best,

Akis

To make things easier, let’s suppose your ring buffer is only 16 bytes long. Then when the Arduino sends its data the first time, your ring buffer will look something like this (where * is garbage):

0: H
1: ,
2: 1
3: 0
4: ,
5: 1
6: 0
7: 0
8: ,
9: 1
10: 0
11: 0
12: 0
13: ,
14: \n
15: *

So, for example, buffer[9] is equal to ‘1’. The next time the Arduino sends its data, your ring buffer will look like this:

0: ,
1: 1
2: 0
3: ,
4: 1
5: 0
6: 0
7: ,
8: 1
9: 0
10: 0
11: 0
12: ,
13: \n
14: *
15: H

When the data is split like this, on two sides of the ring buffer, it’s a pain to deal with it. You really want to create a buffer that holds the entire line from the Arduino, starting at offset 0. So I suggest that you either read the bytes out of the ring buffer as described above, or use something different like serial_receive, which is documented in the “Orangutan Serial Port Communication” section of the Pololu AVR Library Command Reference.

Something like this should work:

serial_send("a", 1);
serial_receive(buffer, 32);
while (serial_get_received_bytes() < 1) {};
while (buffer[serial_get_received_bytes()-1] != '\n') {};
serial_cancel_receive();
// Done receiving the line, now handle data in buffer

Do you understand what this code would do?

–David

Hi David and thank you for your reply,

Yes I do understand the code you sent me. I just have one question, does the serial_recieve function stores the arriving data to the buffer starting at position buffer[0]? Des the serial_cancel_receive function empties the receiving buffer as well?

Best,

Akis

Yes, that is correct.

No. The data that was in the buffer remains there. You don’t ever need to explicitly empty this buffer; at some point you will just decide you don’t care about the data in it any more, and you will call serial_receive again, and the old data will get overwritten with the new data.

–David

Ok I thought so. :slight_smile: Thank you David! That was all I needed for now.

Best,

Akis

David just a quick question,

One of the code lines you gave me is the following:

while (serial_get_received_bytes() < 1) {};

As far as I understand it, the program here waits until it recieves the first byte and then it exits the while loop right? Then we read the incomming data with the:

while (buffer[serial_get_received_bytes()-1] != '\n') {};

Now teh question. After the:

serial_cancel_receive();

does the serial_get_received_bytes() goes to zero resulting the reinitialization of buffer array, which means that with the next

serial_send("a", 1);

we start storing data from position buffer[0]. Right?

Best,

Akis

Yes, after serial_cancel_receive, serial_get_received_bytes() will return 0 if you call it, but calling serial_cancel_receive will not affect the data that was already received and written to the buffer. The data is still there and you can go ahead and use it after calling serial_cancel_receive.

I think the answer about serial_send is no. Serial_send simply writes the data that you give it out on the serial port, in the background using interrupts. It doesn’t store anything. It doesn’t touch the receive buffer that we set up earlier. Receiving and sending are different, mostly independent parts of the serial library. When you call “serial_send(“a”,1);” it will simply send an “a”, it won’t do anything to your buffer[] array because buffer was not one of the arguments to serial_send. The way it does this is that the compiler will create a small buffer where it puts the character ‘a’, and that’s what gets passed to the serial_send function.

If you need more information, please see the “Orangutan Serial Port Communication” section of the Pololu AVR Library Command Reference and let me if that documentation is unclear or incomplete.

–David

Hi David,

Once again thank you for your response. Ok I took my answer although I might not have been completely clear of what I meant.

Now here is my code for the Arduino slave device:

int incomingByte = 0;   // for incoming serial data

void setup() {
  // initialize both serial ports:
  Serial.begin(9600);

}

void loop() {
  
    }
    
void serialEvent(){
   if (Serial.available() > 0) {
        //read the incoming byte:
        incomingByte = Serial.read();
        if (incomingByte == 'a'){
            int value1 = 95;
            int value2 = 83;
    
            Serial.print('H');
            Serial.print(",");
            Serial.print(value1, DEC);
            Serial.print(",");
            Serial.print(value2, DEC);
            Serial.print(",");
            Serial.println();
            delay(100);
        }
        Serial.flush();
    }
    
}

And here is my code for the Babay-O:

#include <pololu/orangutan.h>
#include <stdlib.h>

// A global ring buffer for data coming in.  This is used by the
// read_next_byte() function, below.
char buffer[20];
char speed_buffer[2];

int speed1;
int speed2;
int index;

// A pointer to where we are reading from.
unsigned char read_index;
// A pointer to where we are writing to.
unsigned char write_index;

char read_next_byte()
{
	char ret=0;
	//unsigned char received_index =serial_get_received_bytes();


	while(serial_get_received_bytes()!=read_index)
	{
		ret = buffer[read_index];

		read_index ++;

		if (ret == '\n')
			read_index = 0;
		else if(read_index >= sizeof(buffer))
			read_index = 0;
	}
	return ret;
}



/////////////////////////////////////////////////////////////////////

int main()
{	

	// start receiving data at 9.6 kbaud
	serial_set_baud_rate(9600);

	while (1)
	{
	// wait for a Data
		//char Data = read_next_byte();
		//unsigned char Data= serial_get_received_bytes();
		
		serial_send("a",1);

		serial_receive(buffer, sizeof(buffer));

		while (serial_get_received_bytes()<1){

		}
		
		
		read_index = 0;
		write_index = 0;
		index=0;
		while (buffer[serial_get_received_bytes()-1] != '\n'){
			
			
			char digit=read_next_byte();
			
			
			if (digit=='H'){
			}

			else if (digit >= '0' && digit <= '9'){

				speed_buffer[write_index]=digit;
				
				write_index ++;
				
				if (write_index==2)
					write_index=0;
			
			}

			else if (digit==','){
				
				if (index==0){
					index ++;
				}

				else if(index==1){ 
					itoa(speed1, speed_buffer, 10); //will convert to decimal
					index ++;
				}

				else if(index==2){ 
					itoa(speed2, speed_buffer, 10); //will convert to decimal
					
				}

			}

		}

		serial_cancel_receive();

		delay_ms(500);
		
		set_m2_speed(speed1);
		
	}
	return 0;
}

What I am trying to do is to request from the Arduino to transmit the following frame:

H,95,83, ‘\n’

Then I am trying to read this frame in the Baby-O, store the number characters into a separate array called speed_buffer and then translate them into integers using itoa command. Finally, I am trying to activate a motor with one of these speed values (95 or 83). So far nothing happens.
Any ideas about my code?

Best,

Akis

Hello. This general strategy you are trying will not work:

      while (buffer[serial_get_received_bytes()-1] != '\n'){
         char digit=read_next_byte();
         // do stuff with digit
      }

I think the “read_next_byte” function is mostly something you copied from some example code that uses a ring buffer to receive data. That function makes things more complicated than necessary. I already showed you how to get the data into your buffer using functions in the Pololu library. Now you just need to process the data, using standard C functions and/or loops. (At this point you could ask your questions to anyone who knows C, or read a book about C to learn it.) There are many ways you could process the data, but it looks like you want to process it one byte at a time, and you could do that using a for loop:

// ... code I posted earlier for sending "a" and receiving a line into buffer, which is an array of chars
for (int i = 0; i < sizeof(buffer); i++)
{
  char c = buffer[i];
  if (c == '\n') { break; /* found the end of the line */ }
  // process the char
}

You could try copying your code into there and see if it works.

But actually I think you are doing things the hard way. This might be slower than your way, but I would probably take a different approach and just use sscanf, which is part of the AVR Libc. Something like this would probably work:

// ... code I posted earlier for sending "a" and receiving a line into buffer, which is an array of chars
int a, b;
int result = sscanf("H,%d,%d", &a, &b);
if (result == 2)
{
  // variables "a" and "b" now contain the numbers from the Arduino
}
else
{
  // the data in the buffer did not match the expected format
}

–David

Hi again,

Finally it Works!!! :smiley:

Here is the code I’ve build:

#include <pololu/orangutan.h>
#include <stdio.h>
#include <stdlib.h>

// A global ring buffer for data coming in.  This is used by the
// read_next_byte() and previous_byte() functions, below.
char buffer[20];
char speed_buffer[2];

int speed1=0;
int speed2=0;
char data;

// A pointer to where we are writing to.
unsigned char write_index;

int main()
{	
	// start receiving data at 9.6 kbaud
	serial_set_baud_rate(9600);

	delay_ms(2000);
	while (1)
	{
		serial_send("a",1);

		serial_receive(buffer, sizeof(buffer));

		while (serial_get_received_bytes()<1){

		}
		
		while (buffer[serial_get_received_bytes()-1] != '\n'){
			
		}
		
		write_index = 0;
		int index=0;
			
		for (int i=0; i<sizeof(buffer); i++){
		
			char digit=buffer[i];	
			
			if (digit=='H'){

			}

			else if (digit >= '0' && digit <= '9'){


				data=digit;//(digit-'0');

				speed_buffer[write_index]=data;

				write_index ++;
				
				if (write_index==2)
					write_index=0;
			
			}

			else if (digit==','){
				//set_m1_speed(100);
				if (index==0){
					index ++;
				}

				else if(index==1){ 

					speed1=atoi(speed_buffer); 

					//or

					//sscanf(speed_buffer,"%d", &speed1);//will convert to decimal
					
					index ++;

				}

				else if(index==2){ 

					speed2=atoi(speed_buffer); //will convert to decimal
				}

			}

			else if (digit=='\n'){
				break;
			}
		}
		serial_cancel_receive();
		
		if (speed2==83)
			set_m1_speed(150);

	}
	return 0;
}

I used the atoi command instead of sscanf just to test it. On that, I would like to ask you one final question in this thread. You mentioned that sscanf command is slower than reading each char-byte right? Why is that? Which method is the fastest one?

Once again thank you for all your replies,

Akis

I’m glad you got it working, and thanks for posting the code!

Yes, sscanf would probably be slower than the algorithm you wrote, but of course I haven’t tested it so I don’t know if it’s a few hundred microseconds slower or a few milliseconds slower. The reason is that the sscanf function is this general function that can scan all sorts of different types of strings. It needs to read your a format string to understand what it is looking for. General functions require extra if statements and conditions to work properly, so they usually take longer to run. Your algorithm is probably faster than scanf, but it could definitely be better. For one thing, you don’t need to copy the numbers into this “speed_buffer”, you could just pass atoi a pointer to some position inside of “buffer”.

The fastest method would be to write a carefully optimized algorithm in assembly, but I’m not recommending that.

–David