13

I'm currently on project #14 of the Arduino project book.

I'm trying to control a processing sketch on my laptop using my Arduino. This is accomplished by using a potentiometer to control the background of an image.

Arduino code:

void setup(){
  Serial.begin(9600);
}

void loop(){
  Serial.write(analogRead(A0)/4);
}

Processing:

//imports serial library
import processing.serial.*;
//setups the serial object
Serial myPort;
//creates an object for the image
PImage logo;
//variable to store background color
int bgcolor = 0;

void setup(){
  colorMode(HSB,255);
  logo = loadImage("http://arduino.cc/logo.png");
  size(logo.width,logo.height);
  println("Available serial ports");
  println(Serial.list());
  myPort = new Serial(this,Serial.list()[0],9600);
}
//equivalent of arduino's loop function
void draw(){
  if(myPort.available() > 0)
  {
    bgcolor = myPort.read();
    println(bgcolor);
  }

  background(bgcolor,255,255);
  image(logo,0,0);
}

Now, while the code works, and the background color changes as i turn the potentiometer, there is a huge lag between turning the potentiometer and seeing the background change color, and the values from the Arduino/potentiometer change on the processing's serial monitor.

What I've tried:

  • Changing the speed of Serial communication

I've noticed that when I decrease the speed of Serial communication, e.g. around 100, the delay between turning the potentiometer and seeing it change on my laptop decreases to about 1 second. However, when I decrease the speed of Serial communication even further, e.g. a value of 1, the delay increases again.

On the flip side, at the standard speed of 9600, the delay is huge, roughly 5sec ++ before the changes in the potentiometer show up on the laptop/processing.

Why does decreasing the communication speed (up to a certain point) decrease the time lag, and increasing it increase the time lag? Also, is there anyway I can make it near instantaneous?

2
  • 3
    You're outputting a reading every single time round the Arduino loop(). It's quite possible that your Processing program isn't running fast enough to keep up with it. Try putting a delay into the loop() in your Arduino code to slow it down; e.g. delay(50). Commented Jun 4, 2014 at 12:32
  • Hi peter, thanks for the prompt answer, adding a small delay really did solve my problem. Just another small question though, is there any way i can determine the speed of my processing program in the future so as to prevent this from happening again, or does getting a better laptop/processing speed solve the issue? Also, why does entering a communication speed of 250 or 300 mess up the readings from the arduino?( the readings i get are alternate between the reading and zero E.g 147,0,147,0)
    – Kenneth .J
    Commented Jun 4, 2014 at 12:38

5 Answers 5

11

You're outputting a reading every time round the Arduino loop(), so it seems likely that your Processing program isn't running fast enough to keep up with it. Try putting a delay into the loop() in your Arduino code to slow it down, e.g.:

void loop(){
    Serial.write(analogRead(A0)/4);
    delay(50);
}

As far as I know, Processing aims to run at a consistent framerate, which you can modify using the frameRate() function. By default, it's 60 frames per second, although it may run slower on older systems (or where you're running an intensive program). You can check how fast it's running by reading the frameRate variable.

Introducing a 50 millisecond delay into the Arduino loop means it will be updating a little under 20 times per second. That means it should be fast enough for user interface purposes, but should also be well within the capabilities of your Processing program.

As far as the baud rate (communication speed) is concerned, adjusting it by arbitrary amounts is likely to have unpredictable results. That's because the hardware will only support specific speeds, and trying to use anything else can result in the data appearing garbled on the other end. The Serial.begin() documentation has some more information about supported baud rates.

14

As already pointed out, your Arduino is saying too much too fast. Adding delay() will slow it down, but still it keeps yelling at Processing. Ideally, you want Processing to ask for the value when it's convenient, and then receive one answer from your Arduino.

Enter SerialEvent().

As opposed to loop() on your Arduino and draw() in Processing, everything inside serialEvent() only excutes when there is something new in the serial buffer. So instead of Processing asking questions as fast as possible and your Arduino yelling back even faster, they can have a nice, polite (asynchronous) conversation.

Both Processing and Arduino have a serialEvent. This is serialEvent() on the Arduino and this is serialEvent() in Processing. Using serialEvent on both sides, this is what would happen:

  1. Processing sends a character to the serial connection. This could be any character, but if we predetermine one we can filter out any unwanted requests caused by e.g. a noisy signal. For this example, let's send a V every time we want a new reading of your potmeter. After the character is sent, we continue our business as usual. Not waiting around for an answer here!

  2. On the Arduino side nothing is happening, until it receives data in the serial buffer. It checks whether the incoming character is a V, and lucky us, it is. The Arduino reads the potmeter's value once, outputs that value to serial once, and goes back to chilling out, maxing relaxing all cool. Protip: terminate the value with a character (* in our case). This will help you in the next step.

  3. Processing is doing its regular interfacey pixel business when all of a sudden there's a disturbance in the force new data in the serial buffer. It switches to serialEvent(), and starts reading the serial data, until our terminating * is encountered. Knowing for sure this was the last character worth reading, we can now store the incoming value in a variable that stores the Arduino's reading.

  4. That's it. Processing now knows the new sensor value and carries on with whatever we tell it to do. Meanwhile, your Arduino is enjoying the weather or contemplating its existence until there is incoming serial data.

3
  • 1
    And while you're at it, put a capacitor in parallel with your potmeter. This smooths out little changes in your DAC's input, possibly preventing jittery motion in Processing.
    – Tom
    Commented Jun 20, 2014 at 7:36
  • Thank you for this nice (and kinda anthropomorphic) answer! Commented Nov 23, 2016 at 10:34
  • Actually, asking questions over USB can be a idea. This is because USB has a lot more latency than a serial port, so asking a question and waiting for a response is a more time consuming operation than it would otherwise be, especially compared to what could be done at high baud rates. Letting the Arduino run a little faster is fine (though it should not saturate the serial portion of the link); the catch is that the processing sketch should drain the Arduino data as it becomes available, and hold onto the last complete value to use when it needs one. Commented Jun 2, 2017 at 23:52
7

Your polling loop runs at full speed of your processor, and writes to the serial port in each round.

This way, you're writing way more often to the serial port than it can handle.

The port writes out data as fast as you configured it, and buffer data that is comming in from your program too fast, to write it out as soon as possible. It the buffer is full, it just drops new data.

What's important here is that it will keep the order of the values: It is a FIFO buffer, working in First In/First Out order.

What happens is:
The loop fills the port buffer, and keeps it 100% full.
If you turn the potentiometer, the changed value get's written to the end of the buffer, the port works as fast as it can to write out all elements in the buffer, that have still the old value.

And finally the value you are interested in. The most current value we wanted to see immediately was at the end of the FIFO, and first in/ first out also means last in/last out. The opposite of what we want.

The maximum frequency it makes sense to read your data, is the frequency you can write it out, so you should use at least a delay that is long enough to write out the bytes at the current port speed.


As another, independent measure to prevent this kind of delay in general,
you could additionally set the write buffer of the port to a minimum.

That would cause data to be dropped much earlier, instead of buffering a lot first.

Of course, in many applications that is not what you need; With bad luck, it could work anyway in the beginning, and get unstable in some situations when the timing changes based on things like processor load, and there are only some random data samples that get dropped. A large buffer generally behaves much more deterministic, so do use a large buffer by default.

1
  • Right idea, but not quite right on the statement "It the buffer is full, it just drops new data.". Once the buffer is filled, data is not dropped, rather the writes block until there is space in the outgoing buffer. This means that the input and output soon end up flowing at the same average rate, but that there's a buffer's worth of latency in between them. Commented Jun 2, 2017 at 23:55
6

Instead of constantly sending serial data, only send data when the value of the potentiometer has changed over a certain threshold.

int oldValue = 0;
const int threshold = 5;

void setup()
{
  Serial.begin(9600);
  pinMode(A0, INPUT)
}

void loop()
{
  if(oldValue >= analogRead(A0)+threshold || oldValue <= analogRead(A0)-threshold)
  {
    Serial.println(analogRead(A0));
    oldValue = analogRead(A0);
  }
}
1
  • 1
    That loop() is not filling the output buffer with equal samples, that's good. But it still runs at full speed of the processor, which may be 100 times as fast as it's needed. That means it can still fill the buffer to the limit quickly if the input changes frequently, eg from noise above threshold, or a continuous change in high resolution (which is not the case in the example application here) Commented Jun 18, 2014 at 10:01
0

Two simple solutions that are guaranteed to work for anyone who is still looking:-

  1. Increase the delay to 50 to 100 milliseconds.

  2. Add this after the Serial.begin(9600) in setup();

    Serial.setTimeout(50);
    

Step two is the most important. It worked for me only after I added the above code. This is not mentioned very often in many other forums that I have looked when I had the exact same problem.

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.