Does one Need to Slow Down Processing in Order for Arduino to Catch Up?

I have Processing and Arduino communicating over COM port just fine. COM port is through USB and attached to Arduino Mega 2560, all on Win XP. There is a LCD with serial controller BV4618, attached to Arduino. The LCD's serial controller is using pins 4 & 5 for serial RX/TX. Communication between Arduino and LCD is set to 9600 baud, otherwise I get some silly Korean characters.

I wrote this simple Processing test program which just sends few strings down and Arduino happily receives them and displays them on LCD. Now I tried speeds up to 57600 baud and it all works fine.

But problem is that I need to put a delay(900); statement into Processing code, otherwise Arduino stops receiving data. In other words, if Processing delay is 900 or higher, than Arduino shows received data on the LCD. If I drop Processing delay to even 800, Arduino immediately stops showing any data received data, although a little RX led on Arduino blinks as sign that something went in.

Now, all this is just testing. Then end product needs to be a Processing/Arduino code that works as fast as possible, without a delay that is almost one second long. I do not know much about how Serial works. How can I avoid having this delay(900) on the Processing side?

Here is Processing code:

/**
 * v1.00.a
 *
 * This sketch illustrates how to send commands with parameters to an Arduino sketch. Commands
 * need to be terminated with LF or CR. This Processing sketch is made to work with Arduino 
 * sketch:
 *
 *   Arduino_LCD_Serial_ByVac_BV4618_and_Serial_Com
 *
 * Arduino needs to have LCD display attached and configured. The above Arduino sketch is using
 * the HD44780 LCD (4x20) controller with ByVac BV4618 serial controler.
 * 
 *
 * Updates:
 *
 *   15:37 9.5.12 - v1.00.a = Initial code.
 *
 */
 
import processing.serial.*;

// The serial port:
Serial myPort;

int i = -1;  // Counter for switch() statement must start from -1, not 0, as one 
             // would imagine.

void setup() {

  // List all the available serial ports:
  println(Serial.list());

  // Just pick up the serial port to which Arduino is hooked, or find where Arduno
  // sits in Control Panel > System > Device Manager > USB Ports.
  myPort = new Serial(this, "COM6", 57600);
}

void draw() {

 /* 
  * It was found through experimentation that Processing wants to execute only one write()
  * statement per loop. If you try to execute two write()'s it freezes, it doesn't even reset
  * Arduino, what is otherwise normal at a begining of Processing sketch run!
  *
  * Processing's switch() statement, on other hand, wants to start itterator from -1, not 0,
  * as it would be logical.
  *
  * For multiple write() statements, sending multiple commands to Arduino, just stuck them up,
  * like: "servox 472\nsrx 2437\n", by using '\n' as command delimeter.
  */
  switch (i) {
    case 0:
      // Line must be terminated either '\n' or '\r' characters.
      myPort.write("servox 1379\n"); break;
    case 1:
      myPort.write("servoy 20\r"); break;
    case 2:
      // Comands can be stuck up, one after another, like this:
      myPort.write("srx 640\nsry 480\n"); i = -1; break;
  }
  i++;
   
  // HUGE DISCOVERY!!! Only send one write() statement per loop. Trying this second one, blocks
  // the Processing code, at a runtime, and it doesn't even reset Arduino!
  ///myPort.write("servoy 20\n");

  delay(900);
}

Here is Arduino code:

/**
 * v1.00.a
 *
 * Sending characters and words, via terminal like Serial Monitor, to Arduino and displaying
 * them on the attached ByVac BV4618 serial interface. ByVac BV4618 is attached to Negative
 * Black LCD Display HD44780 4x20 (YMFC-C2004).
 *
 * Modified the original example file by removing all that looked like keypad instruction.
 * 
 * Circuit:
 *
 *   - ByVac BV4618, the HD44780 LCD controller with Serial, RS232 and I2C interface,
 *       http://doc.byvac.com/index.php5?title=Product_BV4618
 *
 *   - LCD Display HD44780 4x20 Black NEG Backlight (YMFC-C2004),
 *       http://www.ebay.co.uk/itm/LCD-Display-HD44780-4x20-chr-20x4-Black-NEG-Backlight-/170440872374?pt=Bauteile&hash=item27af1151b6
 *
 *   - power from Arduino, Vcc= +5.0V (red wire) and Gnd (black wire),
 *   - 2.7kOhm resistor between LCD's Gnd (1st pin) and Vo (3rd pin),
 *   - Ard.PWM.pin #4 - Ard's TX pin, to BV4618's RX pin, white wire,
 *   - Ard.PWM.pin #5 - Ard's RX pin, to BV4618's TX pin, green wire,
 * 
 * Usage:
 *
 *   Settings:
 *
 *     - Terminal program of choice, like Arduino IDE's Serial Monitor, must be set to:
 *
 *         - Baud rate ........... 57,600
 *         - Line termination .... Newline
 *
 *     - If instructions are sent via serial port from another program, like Processing, than
 *       lines must be terminated with '\n' or '\r'. For example, these are good ones:
 *
 *         "servox 1375\n"  or  "SRY 12\r"
 *
 *       Space or ' ', is not used as delimeter. Space ' ' is treated just as any other character.
 *
 *   Input:
 *
 *     - Type in: 'test1' (without quotes), than press return. Line termination must bed done
 *       with LF and CR. If you want to use ' ' or char(32) for line termination, you must change
 *       code.
 *     - Output to above will be 'test1,' (without quotes), etc. All outputs will follow
 *       each other like: test1,test 2,test3,test4 etc.
 *
 *       This Arduino sketch is made to work with Processing sketch:
 *
 *         Processing_Serial_Write_Command_Line_1
 *
 * Updates:
 *
 *   15:37 9.5.12 - v1.00.a = Initial code.
 *
 */
#include <BSerial.h>
#include <bv4618_S.h>

#define rxPin 5
#define txPin 4

BV4618_S lcdByVacSerial(rxPin, txPin);

boolean blnLCDRefresh = true;

char incomingChar = 0;      // Arduino receiving incoming serial data into int variable.

const int SERIAL_INPUT_BUFFER_SIZE = 32;     // Input line lenght up to 31 characcters.
char arrChrBuffer[SERIAL_INPUT_BUFFER_SIZE];

int i;

void setup()  {
 
  // 9600 Baud, 0 delay, '*' as ACK
  lcdByVacSerial.begin(9600,50,'*');
  // N.B. puts() is for arrays of char-s, or char*, but not Sting-s, putch() for individual Char-s.
  // 4 x 20 display
  lcdByVacSerial.puts("\e[4L\e[20c");
  // clear screen, needs a delay
  lcdByVacSerial.puts("\e[2J"); delay(50);  
  // cursor on, as a static underline.
  lcdByVacSerial.puts("\e[?25h");
    
  // Start up our serial port, we configured our XBEE devices for 38400 bps. 
  Serial.begin(57600); 
}

void loop() {

  // Only print to LCD if there is a new stuff to show. Otherwise LCD characters blink anoyingly.
  if (blnLCDRefresh) {

    ///lcdByVacSerial.puts("Awaiting input:");
    // Set LCD's cursor to line 2, column 1.
    ///lcdByVacSerial.puts("\e[2;1H");
    blnLCDRefresh = false;
  }

  // If character was received, show it on LCD display.
  while (Serial.available() > 0) {

    // Read the received bytes:
    incomingChar = Serial.read();    // Incoming char will become int ASCII ordinal.
    // Write into the character array all characters, except line termination ones, like: LF 
    // and CR.
    if (incomingChar != '\n' && incomingChar != '\r') {
      
      arrChrBuffer[i] = arrChrBuffer[i] + incomingChar;
      i++; 
    } else { 
      
      // This break enables stucking up of multiple commands together, like: "srx 640\nsry 480\n"
      // Individual commands just need to be separated by '\n' characters. When first '\n' is met,
      // character array that was extracted up to that point is processed. But the rest of multi-
      // command is still in the serial buffer and it is extracted in the next loop. Voila!
      break; 
    }
  }
  
  // LF and CR are used for line termination.
  if (incomingChar == '\n' || incomingChar == '\r') {
    
    // Output to LCD.
    // N.B. puts() is for arrays of char-s, or char*, but not Sting-s, putch() for individual Char-s.
    lcdByVacSerial.puts(arrChrBuffer); lcdByVacSerial.putch(',');
    // Move LCD's cursor up one place.
    ///lcdByVacSerial.puts("\e[1A");
    // Set LCD's cursor to line 2, column 1.
    ///lcdByVacSerial.puts("\e[2;1H");
    
    // After printing to LCD, reset all the auxiliary variables.
    for (int j=0;j < SERIAL_INPUT_BUFFER_SIZE;j++) { arrChrBuffer[j] = '\0'; } 
    i = 0; incomingChar = '\0';
  }
  
  // Catch a breath.
  delay(100);                      // Wait a bit.
}

Please have a look.

  if (blnLCDRefresh) {
   blnLCDRefresh = false;
  }

What is the purpose of this? If the variable contains false, set to to true. Otherwise, leave it at true.

      arrChrBuffer[i] = arrChrBuffer[i] + incomingChar;

Why are you adding the character to the character already in the array? It seems to me that you should be REPLACING the character in the array.

    for (int j=0;j < SERIAL_INPUT_BUFFER_SIZE;j++) { arrChrBuffer[j] = '\0'; }

Once again, it appears that you do not understand that a string is NULL terminated. It is not necessary to fill the whole array with NULLs.

  // Catch a breath.
  delay(100);                      // Wait a bit.

The Arduino is not breathing hard. Get rid of this!

Thanks for having a look.

  1. I switched that part off.

  2. My bad. I was mentally working with Strings, but here we are into arrays. That was corrected.

  3. I know that character arrays are null terminated, just wanted to have this 100% removed from potential bug makers.

  4. I excluded the delay() statement out of the code. But it didn't help in the slightest. I still need delay(1000) in Processing.

Interesting thing is, if I, for example do delay(600) than starting the Processing code doesn't cause Arduino reset and Arduino code does not work. Even manually resetting Arduino won't do any good. If I do delay(1000) than starting Processing code automatically causes reset on Arduino and everything runs as desired.

Any ideas? How can we remove this delay(1000) from the Processing code?

How can we remove this delay(1000) from the Processing code?

You need to look at the SerialCallResponse example, where the Arduino sends "Ready" every time it is ready to receive data. The Processing application sends data ONCE when the Arduino says it is ready.

The Arduino then needs to send "Ready" every time it is done processing existing serial data (and at the end of setup()).

Yeah, I spent whole day trying to remove that delay() from Arduino and Processing code. On Arduino side I got down to 3 millisec, but on Processing side I am still at 900 millisec.

The code you advised me to have a look at, is using delay as well, albeit only 10 millisec. It seems that some delay is unavoidable. Some people reported finding 20 millisec built into Serial.h library.

I decided to try to cut down the length of commands. Currently I am using something like this: "servox 1500\n". So I think to try to convert this into: "xBB" or "yBB", where BB would be integer defined with two bytes. That would be enough to cover 700 to 2200 microseconds range that is needed for PPM signals to servos.

Will be back :wink:

You could have Processing send the int as 2 bytes, and have the Arduino read the two bytes and reassemble the int. No need to convert to ASCII and back.

Problem is that I need to manage a complex device, with minimum 2 servo motors and few sensors. So I can't just send 2 bytes. I need to send one byte as identifier for 2 other bytes. In other words, they must go in command-parameter formation. Now, as far as I currently see, bytes in formation: "sXX" must have terminating byte, so it is maybe more like "sXX\n" which is already 4 bytes. It seems that lengthy commands like "servox 2179\n" are drag.

I really didn't expect that bottleneck is going to be on Processing side, since it runs on PC with all that hardware power attached. I tried some other Processing code, that is sending only a single byte and that runs perfectly smooth, even at 9,600 baud.

Problem is that I need to manage a complex device, with minimum 2 servo motors and few sensors. So I can't just send 2 bytes. I need to send one byte as identifier for 2 other bytes.

So do that. What I meant was that if you need to send a value, like 589, to the Arduino, you can send that as two bytes or as a string ('5', '8', '9'). Performing two reads, a left shift, an add and a store is going to be a lot faster than reading 3 bytes, and converting the 3 characters to a number.

Of course you still need to send an ID, and probably start and end markers.

Sending data when the Processing application and the Arduino are both ready should not require a delay() on either side. I don't know where the Processing or Arduino code in the SerialCallResponse example uses delay(), but they should not be necessary.

The code I have for Processing and Arduino exchanging data has no delay()s.