domingo, 4 de octubre de 2015

[Lenguaje Arduino 08] parte 1: enviar datos desde la placa al exterior

Continuando con el tema anterior seguiremos ahora con los datos que enviamos desde la placa arduino hacia el exterior.

  • Serial.print(): envía a través del canal serie un dato desde el microcontrolador hacia el exterior. Ese dato puede ser de cualquier tipo: carácter, cadena, número entero, número decimal, etc. Si el dato se especifica explícitamente (en vez de a través de una variable), hay que recordar que los caracteres se han de escribir entre comillas simples y las cadenas entre comillas dobles.
  • Serial.flush(): La transmisión de los datos realizada por Serial.print() es asíncrona. Eso significa que nuestro sketch pasará a la siguiente instrucción y seguirá ejecutándose sin esperar a que empiece a realizarse el envío de los datos. Si este comportamiento no es el deseado, se puede añadir justo después de Serial.print() la instrucción Serial.flush(), esta instrucción espera hasta que la transmisión de los datos sea completa para continuar la ejecución del sketch.
  • Serial.println(): hace exactamente lo mismo que Serial.print(), pero al final de la ejecución de Serial.println() se efectúa un salto de línea. Tiene los mismos parámetros y los mismos valores de retorno que Serial.print()
En el punto actual, hemos visto el significado de todas las líneas del Ejemplo 1 de este Lenguaje Arduino, por lo tanto deberíamos de ser capaces de entender su comportamiento. Analicémos el codigo. Si recordamos el código, primero declarábamos una variable global de tipo “int” y la inicializábamos con un valor de 555. Seguidamente, arrancábamos la ejecución del programa abriendo el canal serie (a una velocidad de 9600 bits/s) para que la placa pudiera establecer comunicación con nuestro computador. Y finalmente, ya en la sección “void loop()”, primero enviábamos el valor actual de la variable a nuestro computador. Seguido de esto aumentamos en una unidad ese valor, para después volver a enviar ese nuevo valor al computador, y volver a aumentar en una unidad su valor, y volverlo a enviar... así infinitamente hasta que la placa dejara de recibir alimentación. Lo que se ve por el “monitor serial” es precisamente esos valores (uno en cada línea diferente porque Serial.println() introduce un salto de línea automático) que irían aumentando de uno en uno sin parar. En el momento que se llegara al valor máximo permitido por el tipo de datos de la variable (que en el caso de un “int” es de 32767) se seguiría por el valor mínimo (-32768) y se seguiría aumentando desde allí, en un ciclo sin infinito hasta desconectar el USB o la alimentanción.

Conviene también saber la existencia de la instrucción Serial.write(), parecida a Serial.print() pero no igual:
  • Serial.write(): envía a través del canal serie un dato (especificado como parámetro) desde el microcontrolador hacia el exterior. Pero a diferencia de Serial.print(), el dato a enviar solo puede ocupar un byte. Por lo tanto, ha de ser básicamente de tipo “char” o “byte”. En realidad, también es capaz de transmitir cadenas de caracteres porque las trata como una mera secuencia de bytes independientes uno tras otro. En cambio, otros tipos de datos que ocupen más de un byte indisolublemente (como los “int”, “word”, “float”…) no serán enviados correctamente. Su valor de retorno es, al igual que en Serial.print(), un dato de tipo “byte” que vale el número de bytes enviados.
    La gracia de Serial.write() está en que el dato es enviado siempre directamente sin interpretar. Es decir, se envía como un byte (o una serie de bytes) tal cual sin ninguna transformación de formato. Esto no pasa con Serial.print(), en la cual se puede jugar con los formatos binario, hexadecimal, etc. Por tanto, esta instrucción está pensada para la transferencia directa de datos con otro dispositivo sin una previsualización por parte nuestra.
    De todos modos, si observamos en el “Serial monitor” los datos enviados por Serial.write(), veremos que se muestran (tanto si son de tipo “char” como de tipo “byte”) en forma de sus correspondientes caracteres ASCII. Esto es así porque es el propio “Serial monitor” el que realiza en tiempo real esta “traducción” ASCII. Gracias a este comportamiento, podemos ver en todo momento el carácter asociado al byte enviado, que es lo que normalmente nos interesa.

Ejemplo 5
El siguiente código muestra de mejor manera el lo explicado anteriormente:


char cadena[]="hola";
byte bytesDevueltos;

void setup() {
          Serial.begin(9600);
          bytesDevueltos=Serial.write(cadena);
          Serial.println(bytesDevueltos);
}

void loop() {}



Si ejecutamos el código anterior y observamos el “Monitor Serial”, veremos que aparece el valor “hola4”. El 4 final muestra el valor de la variable “bytesDevueltos”, que guarda lo devuelto por Serial.write(cadena);, confirmando así que se han enviado por el canal serie 4 bytes, los correspondientes precisamente a la cadena “hola”.

Existe otra manera de utilizar Serial.write(), que es para enviar de golpe un array de datos de tipo “byte”. En ese caso, esta instrucción tiene dos parámetros: el nombre de ese array y el número de los elementos (empezando siempre por el primero) que se quieren enviar. Este último valor no tiene por qué coincidir con el número total de elementos del array.

Ejemplo 6
El código siguiente muestra mejor de mejor manera lo explicado anteriormente:

//El array ha de ser de tipo “byte” (o “char”)
byte arraybytes[ ]={65,66,67,68};

void setup() {
        Serial.begin(9600);
//Se envían solo los dos primeros elementos de ese array
        Serial.write(arraybytes,2);
}

void loop() {}


Este es un problema propio de Serial.write() al transmitir datos de tipo byte como se mencionaba anteriormente, la transmisión del byte es asíncrona. Eso significa que nuestro sketch pasará a la siguiente instrucción para seguir ejecutándose sin esperar a que empiece a realizarse el envío de ese/os byte/s. Si este comportamiento no es el deseado, se puede añadir justo después de Serial.write() la instrucción Serial.flush() –que no tiene ningún parámetro y no devuelve ningún valor de retorno–, la cual esperará hasta que la transmisión de los datos sea completa para continuar la ejecución del sketch.



No olvides visitar la pestaña Tutoriales arduino y Lenguaje arduino, donde podrás encontrar la lista de tutoriales que he hecho hasta el momento. Como siempre muchas gracias por visitar mi blog y si tienen alguna consulta o consejo puede comentarlo por medio de este mismo blog.

3 comentarios:

  1. Está muy bien explicado.
    Al fin lo he comprendido
    Gracias por compartir sus conocimientos

    ResponderBorrar
  2. Hola Sebastián, EXCELENTE explicación. Te comento una duda a ver si podes aclararme algo. Es un tema que me genera dudas que no logro despejar con la info encontrada.

    Yo quiero conectar 6(seis) Arduinos entre sí por el puerto Serial, (son 5(cinco) arduinos NANO y 1(uno) Arduino Mega) a través de conversores de RS485.

    A cada uno le asignaré un Número de esclavo (por ejemplo 100, 101, 102, 103, 104 y 105).

    Se deben intercambiar información entre todos. Es decir, no habrá un MAESTRO que solo pide info a sus esclavos, sino que todas las placas, deben enviar y recibir información a la otras.

    La pregunta es la siguiente, si yo envío una trama del tipo:

    (INICIO, DIRECCION, DATO1,DATO2,DATO3, CHECKSUM, FINAL)


    Obviamente, para que esa información sea utilizada SOLO por la placa que necesito que la reciba, primero voy a fijarme si hay SERIAL.AVAILABLE, y si así fuera leo los 2 primeros datos. Si estos se corresponden con el caracter de INICIO DE TRAMA y con la DIRECCION de la placa, leo el resto.

    Esto no me suena lógico, ya que según leí, en cuanto hago un SERIAL.READ, el dato leído se borra.

    Esto es correcto? Lo he comprendido bien?

    Como me conviene pedir y recibir el dato de una palca a otra? Agradezco tu colaboración y te felicito por tu artículo.

    ResponderBorrar