Bi-directional serial communication with MAX MSP

Hello there-

I’m trying to connect 24 actuators and a few proximity sensors to MAX MSP communicating at the highest possible speed via serial. (I am constrained to 57600bps as using Xbee’s and Fio).

I’ve breaking my mind trying to work out a reliable serial communication protocol to send values to the arduino, store them in an array for speed of each actuator, update the actuaotrs with the arry of speed values (using the TLC5940) and at reasonable intervals send the values from the sensors to MAX MSP, and POSSIBLY the array of values of speed.

My first instinct was to buffer up a line of data and then process it, but I’m getting dropouts of data with anything above 4 values ranging from 0-255 when sending ASCII using space as a separator in the format of:

0 255 0 200

anymore than this and I loose bytes, making the string of values useless.

I’m now thinking to label each actuator and send in groups, ie

a255 b0 c200 d30

e123 f0 g100 h230

but this seems wrong somehow…

Does anyone know an effective way of sending 24 values at once down the serial connection reliably? (and then get them back again, with other data)

I think I might be expecting too much?

Does anyone know an effective way of sending 24 values at once down the serial connection reliably? (and then get them back again, with other data) I think I might be expecting too much?

Sending a string of 24 values at once should be pretty standard as long as long as you do not exceed the capacity of the the serial output buffer. Collecting 24 inputs will probably be complex without some type of flow control (assuming you are using a single serial input).

Yes, collecting the values and putting them in an array on the arduino is the main problem here, the values become corrupt if I send anymore than 4. I'm now considering scaling the values of 0-255 into 26 letters (a=0.. z=255) and sending a 3 sets of 8 values to actuators labeled with A-X. ie:

AxBaCaDxEaFxGaHx

as a very fast way of sending the data to the arduino.. but then might spend more time deciphering it into values in the arduino and storing them, then actuating the array, that the serial buffer will get over-run :/

the values become corrupt if I send anymore than 4.

This is almost certainly because of HOW you are collecting them on the Arduino. Some code would be most helpful.

I'm now considering scaling the values of 0-255 into 26 letters (a=0.. z=255) and sending a 3 sets of 8 values to actuators labeled with A-X.

Not necessary. Let's see some code.

If you are sending 24 3 digit values, that's only 72 bytes. 96 if you have some separator. At 115,200 bytes per second (maximum possible transmission rate), that takes about 80 microseconds to send. About the blink of an eye.

Where does the bi-direction (from the subject title) come into play? We need to see some code.

I’m not an expert and have borrowed some code… the problem is keeping on top of the serial buffer. Things just go missing when I try to get them from it:

#define STRING_SIZE 128 // for incomming data
#define MAX_ARGS 28 // for incomming data

int updatetimer = 0; // 
int update_time = 1000; // cycles to run before send sensor information to serial
int left,right;
// range finders set up
int sensorPin=0; // Analog Pin In
int sum=0; // Variable to calculate SUM
int avgrange=50; // Quantity of values to average
int sensorValue; // Value for te average
int i,media,d; // Variables
float cm,inch; // Converted to cm
int left_sensor_pin = 1 ; 
int right_sensor_pin = 2 ; 
boolean codes = true; // whether to send IDs before values to Serial

// set up and array for changing multiple actuators
int actuator[24] = {
  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

void setup(){
  Serial.begin(57600);   // connect to computer via xbee (must be 57600 for xbee com)
}

void loop(){
  
  if( Serial.available() >0 ) {  

    char* cmdbuf = (char*)malloc(sizeof(char) * STRING_SIZE);
    char c;
    int i = 0;
    Serial.print("RECIEVED: ");
    while( Serial.available() && c != '\n' && c !='Z' ) {  // buffer up a line
      c = Serial.read();
      if(c>=0 && c<=255) {
      cmdbuf[i++] = c;
      Serial.print(c);
      }
      
    }

    i = 0;
     while( cmdbuf[++i] != ' '  ) ; // find first space

   cmdbuf[i] = 0;          // null terminate command
   char* cmd = cmdbuf;
   int cmdlen = i;         // length of cmd
   int args[5] = {
     0    }
   , a;         // 'a' is arg counter
   char* s;
   char* argbuf = cmdbuf+cmdlen+1;
 
     free(cmdbuf);
     
     while( (s = strtok(argbuf, " ")) != NULL && a <= MAX_ARGS ) {
     argbuf = NULL;
     args[a++] = (byte)strtol(s, NULL, 0); // parse hex or decimal arg
   }

   int argcnt = a;         // number of args read

   Serial.print(" Have been sent: ");
   Serial.println(argcnt);
   Serial.print(" args. ");
   

   
   if (argcnt>23) { // assuming we have all 24
   Serial.print(" assigning to actuators. ");
   for (int change = 0; change <(argcnt+1); change++) {
     actuator[change] = args[change];
   }
 //  update(); // will update the TLCs with the actuator array
   }
   
   Serial.print(" Changed ");
   Serial.println(argcnt);
   
  }
  
updatetimer++;
if (updatetimer > update_time) {
  updatetimer = 0;
  
  // Send all data from sensors to Serial port  
  // (will probably change this to call/response if I get incoming serial working correctly!)
 d=analogRead(sensorPin); // Read the analog value
left=analogRead(left_sensor_pin); // Read the analog left
right=analogRead(right_sensor_pin); // Read the analog right


cm = (d / 2) * 2.4; // Convert the value to centimeters
inch = d/2; // Value in inches  
  
Serial.print("D ");
if (codes) { Serial.print("L"); }
Serial.print(left);
Serial.print(" ");
if (codes) { Serial.print("R"); }
Serial.print(right);
Serial.print(" ");
if (codes) { Serial.print("U"); }
Serial.print(cm); //Print average of all measured values
Serial.print(" ");
Serial.println(" "); 

  
}

}

You may also have some electrical issues if you have 24 tx lines tied together inputing a single arduino rx input. If two or more tx lines are in use at the same time to the single rx, then the input will probably be a garbled mess.

There are a bunch of things I’d recommend fixing in this code.

    char* cmdbuf = (char*)malloc(sizeof(char) * STRING_SIZE);

Every time there is serial data available to read, you dynamically allocate space. Why?
Why not use the same space over and over?

The data that you are sending is separated by spaces. So this:

    i = 0;
     while( cmdbuf[++i] != ' '  ) ; // find first space

   cmdbuf[i] = 0;          // null terminate command

does not make sense. Read in a bunch of data separated by spaces, and then terminate processing at the first space. And you wonder why you are not getting all the data to process?

   int args[5] = {
     0    }

Local variables go out of scope at the end of loop. Is there some reason why you are not using a global variable that remains in scope for the life of the application?

Why are there only 5 values possible in the array, when MAX_ARGS is defined to be 28?

   char* cmd = cmdbuf;

What’s this for?

   char* argbuf = cmdbuf+cmdlen+1;

argbuf now points to the first character after the NULL you just inserted (possibly the only one in the array).

     free(cmdbuf);

You just released the memory you had allocated…

     while( (s = strtok(argbuf, " ")) != NULL && a <= MAX_ARGS ) {
     argbuf = NULL;
     args[a++] = (byte)strtol(s, NULL, 0); // parse hex or decimal arg

And now you try to read data from the memory.

The strtok function looks at the array that is input, and locates the next space in that array, up to the first NULL. Since it has no idea how long the array is, it will keep searching for that NULL beyond where YOU know the array ends.

Every time you put a character into the array, you should put a NULL after it. That way, the array is always NULL terminated, and the string functions will operate correctly on that array.