I'm using a Python program to send some message over serial port to Arduino. This message contains the pin number of the pin and whether it should be HIGH
or LOW
. Eg: Sending 2H\n
should set pin 2 to HIGH
.
pins
array maps the pin numbers in the messages to the actual pin number of arduino. 1H
corresponds to pin 22 on Arduinio Mega.
When manually sending one message at a time, things work fine. However when Python sends a series of 30 such messages one after the other in a loop with no delays, pin 1 is always stuck at whichever value it is set to on the first series of messages.
Example:
1L\n
2H\n
3H\n
4H\n
5H\n
...
followed by
1H\n
2H\n
3H\n
4H\n
5H\n
...
will cause pin 1 to be stuck at LOW
when it should be high.
On arduino, this is the code that parse the message and sets the pin values.
void setPinValues(String message) {
for(int i = 1; i <= sizeof(pins) / sizeof(pins[0]); i++ ) {
String pinNumber = String(i);
if( message == pinNumber + "H\n" ) {
pinValues[i] = HIGH;
}
if( message == pinNumber + "L\n" ) {
pinValues[i] = LOW;
}
}
}
void loop(){
if( Serial.available() > 0 ) {
received = Serial.read();
message += received;
if(received == '\n') {
// Set pin values
setPinValues(message);
// Write to pins
for (int i = 1; i <= sizeof(pins) / sizeof(pins[0]); i++) {
digitalWrite(pins[i], pinValues[i]);
}
// Clear buffer
message = "";
}
}
}
Arduino Mega communicates with Windows 8 x64 system over USB using a baud rate of 57600. When using a lower baud rate of 9600, this problem is not seen.
Furthermore, at baud rate of 57600, if I were to replace setPinValues
with the following code, pin 1 is properly switched on and off.
void setPinValues(String message) {
for(int i = 1; i <= sizeof(pins) / sizeof(pins[0]); i++ ) {
if( message == String(1) + "H\n" ) {
pinValues[1] = HIGH;
}
if( message == String(1) + "L\n" ) {
pinValues[1] = LOW;
}
if( message == String(2) + "H\n" ) {
pinValues[2] = HIGH;
}
if( message == String(2) + "L\n" ) {
pinValues[2] = LOW;
}
if( message == String(3) + "H\n" ) {
pinValues[3] = HIGH;
}
if( message == String(3) + "L\n" ) {
pinValues[3] = LOW;
}
}
}
Doesnt both versions of setPinValues
do the same? Why does lowering the baud rate prevent the problem? I cannot use a lower baud rate because the USB buffer gets filled up and things slow to a crawl.
To compare the last char H
or L
:
String pinValueString = message.substring(message.length() - 1);
char pinValueBuffer[2];
pinValueString.toCharArray(pinValueBuffer,2);
if(pinValueBuffer[0] == 'H') {
digitalWrite(pins[pinNumber], HIGH);
}
Best Answer
Not sure if this is your problem, but
setPinValues
looks weird to me:Going out of bounds
Each time you call
setPinValues
,i
will range from1
tolength(pins)
, so ifpins
was declared aspins[5]
,i
will be1
,2
,3
,4
and5
. Assuminglength(pins) == length(pinValues)
this is a problem in itself, becausepinValues[5]
would not even exist!pinValues[5]
only createspinValues[0]
topinValues[4]
(yes, I know it's weird, you're declaring 5 elements but, since C is 0-indexed, they're0
,1
,2
,3
, and4
), soi
should NEVER reach 5 or you will be out of bounds which is undefined behavior (and might well be the source of your problems) because you're writing to the wrong memory location.The correct way to do this is looping from
0
toi < sizeof(pins) / sizeof(pins[0])
(notice the strict less-than<
operator) which ranges from0
tolength(pins) - 1
and then justString pinNumber = String(i + 1);
. That way,1H
will change the value ofpinValues[0]
, mapping you 1-indexed serial messages to 0-indexed C arrays.Looping is unnecessary
I'm not sure why you're looping over
pins
. You're doing unnecessary work and probably hogging the uC (specially since, as Dave Tweed points out in the other answer, you're using expensive functions such asString
and string comparison with==
).message
has a single instance, e.g.,1H\n
so there's no loop needed.char pinNumber = message[0] - '0';
will store the integer value of the first character inpinNumber
, with which you're then able to index the array.How does that work?
message[0]
would be achar
type (e.g.'1'
)'0'
is also achar
, which corresponds to the ASCII value of 0 (48 in decimal).message[0]
contains the char'0'
, then the result is'0' - '0' == 48 - 48 == 0
'1' - '0' == 49 - 48 == 1
and on and on.You might also want to reject bogus input:
More spurious work
At each
loop()
iteration you do this:Which has the same problem with the out-of-bounds as before.
It also does unnecessary work, since you're writing ALL pin values, whether they changed or not! Why don't you
digitalWrite(pins[pinNumber], HIGH or LOW);
directly insetPinValues
and avoid this loop? You could even get rid of thepinValues
array if you don't really need to store them (or you can keep it if you need it for some reason).My alternative setPinValues
And then just call it from
loop()
assetPinValue(message[0], message[1]);
Let me know if this solves your problem.