issue using bridge to control two servo motors

Hi,
I just started to learn about Arduino and I got an Arduino YUN with two servo motors. I want to use python to write the next position of the two servos and pass them to ATmega32u4. Meanwhile my sketch will continuously get the position of the two servos from bridge and write it to the two servos.
I tried using this bridge method to control either of my servos and it works normal. And my servos are working well, if without bridge. But when using bridge to get and write both position variables from python continuously in the loop(). The servo will be out of control from the start of the loop().

Is there a limitation for using Bridge.get() and Bridge.put()? Or please let me know if you know what’s wrong with my code.
Here is my sketch:

#include <Servo.h>
#include <Bridge.h>
#include <stdio.h>
//Define two servo
Servo servoBase;
Servo servoArm;
//Define variables to hold value read from bridge
char posVc[3];
char posHc[3];
//PIN for servo control
const int servoBasePIN = 9;
const int servoArmPIN = 6;
//original position of two servos
int posVInt = 140;
int posHInt = 90;

void setup() {
  // put your setup code here, to run once:
  servoBase.attach(servoBasePIN);
  servoArm.attach(servoArmPIN);
  
  servoBase.write(posHInt);
  servoArm.write(posVInt);
  
  Bridge.begin();
  //posH is a variable to be read by python script
  Bridge.get("posH",posHc, 3);
  Bridge.put("posH","140");
  
  delay(10);
  //posV is a variable to be read by python script
  Bridge.get("posV",posVc, 3);
  Bridge.put("posV","90");

  delay(500);
}

void loop() {
    //Get the latest posH and write to servoBase
    Bridge.get("posH",posHc, 3);
    posHInt = atoi(posHc);
    servoBase.write(posHInt);
    delay(1000);
    
    //Get the latest posV and write to servoArm
    Bridge.get("posV",posVc, 3);
    posVInt = atoi(posVc);
    servoArm.write(posVInt);
    delay(1000);

}

In the python part, I use some simple code as:

import sys
sys.path.insert(0,'/usr/lib/python2.7/bridge')
from bridgeclient import BridgeClient as bridgeclient
client = bridgeclient()
client.put('posH','100')

Thanks alot,
Mary P.

I know how to do it without using the bridge - see Develop on PC, deploy on Yun.

...R

You're trying to read 3 characters each time you read a value, yet your receive buffers are only three bytes long. When you read three characters, those three characters get put on your buffer, then a trailing NULL to mark the end of the string is put in the buffer. This NULL overruns the end of the buffer and clobbers whatever is in memory after it. The results will be very unpredictable.

If you are getting three characters, your buffer must be at least 4 to leave room for the trailing NULL that is always automatically added. Always increase any character buffer size by one more than the expected longest string length to allow space for the NULL.

Robin2:
I know how to do it without using the bridge - see Develop on PC, deploy on Yun.

Actually, this is a very good usage of the bridge capabilities. :wink: And he could still have the same buffer overrun using your method.

The advantage to using the bridge here is that he can make a call to http://arduino.local/data/get to see the current positions, and http://arduino.local/data/put/posH/123 to force a position, both of which could be handy during testing/calibrating.

Big thanks, ShapeShifter. That saved my day!J

ShapeShifter:
You're trying to read 3 characters each time you read a value, yet your receive buffers are only three bytes long. When you read three characters, those three characters get put on your buffer, then a trailing NULL to mark the end of the string is put in the buffer. This NULL overruns the end of the buffer and clobbers whatever is in memory after it. The results will be very unpredictable.

If you are getting three characters, your buffer must be at least 4 to leave room for the trailing NULL that is always automatically added. Always increase any character buffer size by one more than the expected longest string length to allow space for the NULL.
Actually, this is a very good usage of the bridge capabilities. :wink: And he could still have the same buffer overrun using your method.

The advantage to using the bridge here is that he can make a call to http://arduino.local/data/get to see the current positions, and http://arduino.local/data/put/posH/123 to force a position, both of which could be hand during testing/calibrating.

Thanks for this info as well!

Robin2:
I know how to do it without using the bridge - see Develop on PC, deploy on Yun.

...R

ShapeShifter:
When you read three characters, those three characters get put on your buffer, then a trailing NULL to mark the end of the string is put in the buffer. This NULL overruns the end of the buffer and clobbers whatever is in memory after it. The results will be very unpredictable.

I mis-spoke about how exactly this works. The fix is the same, but the failure mechanism is a bit different.

When you have a three character buffer, and try to get() a three character string, the buffer_length parameter of the get() call is used to limit the number of characters actually received. In the case of this example, that limit is three. So up to three characters are copied, and a NULL terminator is only added if less than three characters were actually available. Since three characters were requested, and three were available, a trailing NULL character is NOT copied into the buffer. There is no buffer overrun, but there is also no end of string terminator.

So, looking at the variable definitions from the subject sketch:

//Define variables to hold value read from bridge
char posVc[3];
char posHc[3];
//PIN for servo control
const int servoBasePIN = 9;
const int servoArmPIN = 6;
//original position of two servos
int posVInt = 140;
int posHInt = 90;

Now, let’s say that the “posV” key has the value “111” and the “posH” has the value “222”. The “111” will be copied to posVc with no terminator, and “222” will be copied to posHc with no terminator. What will then be in memory is:

  • ‘1’ ← posVc
  • ‘1’
  • ‘1’
  • ‘2’ ← posHc
  • ‘2’
  • ‘2’
  • 0x8c ← posVint
  • 0x00
  • 0x5a ← posHint
  • 0x00

The first address is the start of posVc, the fourth address is the start of posHc, the seventh address is the start of posVint, and the ninth address is the start of posHint. The numeric values in posVint and posHint are stored in little-endian format so the least significant byte is first, and the most significant byte (zero for both values) is second.

So, after reading the two values, what do the string variables hold?

  • posVc: “111222*”
  • posHc: “222*”

posVc runs right into posHc, which runs right into posVint. The first byte of posVint is added to both strings, in this case the value 140 is not a common ASCII value. I’ve represented that with a ‘*’, but many computers will show it as an ‘i’ with a circumflex accent. The strings are finally terminated with the zero value that is the second byte of posVint.

So, while the mechanism is different than what I described, the net result is that the strings get mangled, and the solution is the same: always allocate at least one extra byte in the buffer, so it’s always at least one longer than the longest expected string.

Be aware that if the actual string to be returned by the get() function is larger than the expected maximum, no NULL terminator will be added. So, the most reliable way to get data is:

  • make the buffer at least two characters longer than the longest expected string
  • pass a buffer_length of one less than the actual buffer length
  • force the last character of the buffer to be a NULL

The reason for making the buffer at least two characters longer is so that you can tell if there is an invalid string. If you make it only one longer, you won’t know if the string is longer than expected, and won’t realize there is a problem. For example, let’s say the maximum value you are expecting is 255. If the key actually has the value “1234” and you only read three characters, you will get “123” and not realize there is a problem. If you try to read four characters, you will know if the string is longer than expected.

This is how I would code reading a three character value:

#define MAX_LEN 4  // Maximum string length to fetch
char buffer[MAX_LEN+1];  // Space for maximum string plus NULL terminator

Bridge.get("key", buffer, MAX_LEN); // Get the string, truncate at MAX_LEN characters
buffer[MAX_LEN] = '\0';  // Always NULL terminate the string

At this point, the string should be in buffer, and it should always be NULL terminated. Furthermore, if the key value was too large, there will be four characters in the buffer, and a strlen() call will detect the error.