Estoy usando un programa de Python para enviar un mensaje a través del puerto serie a Arduino. Este mensaje contiene el número de pin del pin y si debe ser HIGH
o LOW
. Por ejemplo: el envío 2H\n
debe establecer el pin 2 en HIGH
.
pins
array asigna los números de pin en los mensajes al número de pin real de arduino. 1H
corresponde al pin 22 de Arduino Mega.
Al enviar manualmente un mensaje a la vez, las cosas funcionan bien. Sin embargo, cuando Python envía una serie de 30 mensajes de este tipo uno tras otro en un bucle sin retrasos, el pin 1 siempre se atasca en el valor que se haya establecido en la primera serie de mensajes.
Ejemplo:
1L\n
2H\n
3H\n
4H\n
5H\n
...
seguido por
1H\n
2H\n
3H\n
4H\n
5H\n
...
hará que el pin 1 se atasque LOW
cuando debería estar alto.
En arduino, este es el código que analiza el mensaje y establece los valores de los pines.
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 se comunica con el sistema Windows 8 x64 a través de USB usando una tasa de baudios de 57600. Cuando se usa una tasa de baudios más baja de 9600, este problema no se ve.
Además, a una velocidad de transmisión de 57600, si tuviera que reemplazar setPinValues
con el siguiente código, el pin 1 se enciende y apaga correctamente.
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;
}
}
}
¿Ambas versiones de no setPinValues
hacen lo mismo? ¿Por qué la reducción de la velocidad en baudios evita el problema? No puedo usar una tasa de baudios más baja porque el búfer USB se llena y las cosas se vuelven lentas.
Para comparar el último carácter H
o L
:
String pinValueString = message.substring(message.length() - 1);
char pinValueBuffer[2];
pinValueString.toCharArray(pinValueBuffer,2);
if(pinValueBuffer[0] == 'H') {
digitalWrite(pins[pinNumber], HIGH);
}
No estoy seguro si este es tu problema, pero setPinValues
me parece raro:
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;
}
}
}
Cada vez que llame setPinValues
, i
se extenderá de 1
a length(pins)
, por lo que si se pins
declaró como pins[5]
, i
será 1
,,, y . Suponiendo que esto es un problema en sí mismo, ¡porque ni siquiera existiría!2
3
4
5
length(pins) == length(pinValues)
pinValues[5]
pinValues[5]
solo crea pinValues[0]
para (sí, sé que es extraño, está declarando 5 elementos pero, dado que C está indexado en 0 , pinValues[4]
son 0
, , , y ), por lo que NUNCA debe llegar a 5 o estará fuera de los límites, lo cual es comportamiento indefinido (y bien podría ser la fuente de sus problemas) porque está escribiendo en la ubicación de memoria incorrecta.1
2
3
4
i
La forma correcta de hacer esto es recorrer desde 0
hasta i < sizeof(pins) / sizeof(pins[0])
(observe el estricto operador menor que <
) que va desde 0
hasta length(pins) - 1
y luego solo String pinNumber = String(i + 1);
. De esa manera, 1H
cambiará el valor de pinValues[0]
, asignando sus mensajes en serie indexados en 1 a matrices C indexadas en 0.
No estoy seguro de por qué estás dando vueltas pins
. Está haciendo un trabajo innecesario y probablemente acaparando el uC (especialmente porque, como señala Dave Tweed en la otra respuesta, está usando funciones costosas como String
y comparación de cadenas con ==
).
message
tiene una sola instancia, por ejemplo, 1H\n
por lo que no se necesita un bucle. char pinNumber = message[0] - '0';
almacenará el valor entero del primer carácter en pinNumber
, con el que podrá indexar la matriz.
¿Cómo funciona?
message[0]
sería un char
tipo (por ejemplo '1'
)'0'
es también un char
, que corresponde al valor ASCII de 0 (48 en decimal).message[0]
contiene el char '0'
, entonces el resultado es'0' - '0' == 48 - 48 == 0
'1' - '0' == 49 - 48 == 1
y así sucesivamente.También es posible que desee rechazar entradas falsas:
if (pinNumber >= sizeof(pins) / sizeof(pins[0]))
return;
En cada loop()
iteración haces esto:
// Write to pins
for (int i = 1; i <= sizeof(pins) / sizeof(pins[0]); i++) {
digitalWrite(pins[i], pinValues[i]);
}
Que tiene el mismo problema con los fuera de límites que antes.
También hace un trabajo innecesario, ya que está escribiendo TODOS los valores de los pines, ya sea que hayan cambiado o no. ¿Por qué no digitalWrite(pins[pinNumber], HIGH or LOW);
entras directamente setPinValues
y evitas este bucle? Incluso podría deshacerse de la pinValues
matriz si realmente no necesita almacenarla (o puede conservarla si la necesita por algún motivo).
void setPinValue(char pin, char value) {
char pinNumber = pin - '0'; // Get the integer value of the ASCII char
if (pinNumber >= sizeof(pins) / sizeof(pins[0])) // Reject out-of-bounds input
return;
switch (value) {
case 'H':
pinValues[pinNumber] = HIGH; // Not necessary if you don't want to store the pin values
digitalWrite(pins[pinNumber], HIGH);
break;
case 'L':
pinValues[pinNumber] = LOW; // Not necessary if you don't want to store the pin values
digitalWrite(pins[pinNumber], LOW);
}
}
Y luego simplemente llámalo desde loop()
comosetPinValue(message[0], message[1]);
Déjame saber si esto resuelve tu problema.
13H\n
, char pinNumber = message[0] - '0';
¿seguirá funcionando? Estoy pensando en dividir un mensaje como 13H\n
en 13
y H
usando strstr()
, pero no estoy seguro de cómo se puede hacerString.toInt()
es tu mejor amigo. IIRC, ni siquiera necesita dividir la cadena, ya que .toInt()
analizará hasta el primer carácter no numérico, por lo que, siempre que su cadena comience con el número pin, estará bien. ¡Sería genial si confirma si .toInt()
funciona así, para futuras referencias!String.toInt()
funciona como se esperaba! Para comparar el último carácter H o L, uso el código con el que acabo de actualizar la pregunta original. ¿Será eso lo óptimo? Usé if
condicionales porque al usar switch
, recibí el errorswitch quantity not an integer
switch quantity not an integer
significa que no pusiste char
como el tipo de tu valor de cambio, ¿verdad? char
es un tipo de datos entero, por lo que debe funcionar (si no, ¡simplemente (int)
cámbielo!) Además, []
funciona con String
AFAIK, por lo que no necesita la sección intrincada .substring
y , solo para obtener el o .
.toCharArray
message[message.length() - 1]
'H'
'L'
message[message.length() - 2]
para tenerlo \n
en cuenta, aunque sería más simple y solo message += received;
si received != '\n'
.message.replace("\n", "");
el que supongo que es más lento.'\n'
es un solo carácter (si no lo fuera, no podría ponerlo entre comillas simples). Es posible que lo esté confundiendo "\r\n"
(observe las comillas dobles, es una cadena de caracteres) que es una convención solo de Windows, solo para archivos de texto y completamente ajena a las comunicaciones en serie que son independientes de la codificación. No uses .replace
ni nada por el estilo, es más lento e innecesario. ¡En los dispositivos integrados, debe mantener las cosas al mínimo!Es un problema de rendimiento. La primera versión de setPinValues()
está haciendo muchas llamadas a String()
cada vez que se llama, y esta es una función no trivial. Esto hace setPinValues()
que tarde mucho más de lo que piensas en terminar, por lo que no es demasiado sorprendente que el Arduino finalmente no pueda seguir el ritmo de los mensajes entrantes a 57600 bps.
En la segunda versión de setPinValues()
, los argumentos de las String()
llamadas son todos constantes, por lo que se evalúan en tiempo de compilación. Esta versión se ejecuta mucho más rápido y puede mantenerse al día en 57600.
Blup1980
patoescrito
Serial.available()
ySerial.read()
son solo envoltorios alrededor del búfer en serie adjunto a la interrupción por Arduino cuando llamaSerial.begin()
, por lo que en realidad ya está haciendo lo que sugiere :) El problema probablemente radica en el llenado del búfer de 128 bytes y la eliminación de bytes debido al procesamiento muy lento.Nick Alexeev