You are on page 1of 25

Serial Arduino parte II, tratar las diversas funciones para el tratamiento de Cadenas,

y que ya hemos introducido el la primera parte Arduino Serial Parte I Veremos la


manipulacin de cadenas , y arreglos o arrays[], y si vamos un poco mas profundo,
el uso de punteros. En esta entrada vamos a cubrir la funcin Serial.read() de lectura
de datos y su relacin con las cadenas.

Lo primero que vamos a ver es la funcin Serial.read() , luego lo que son los arrays[],
como se definen e inicializan. Teniendo un conocimiento de los arrays[], despus
podemos introducirnos en el mundo de las Cadenas que son un caso especial de los
arrays[], y mas tarde con los punteros dada su similitud con las cadenas que
conceptualmente estn muy ligados dado que el elemento de la cadena es un puntero
indexado.

Serial.read()
Esta funcin lee de a un Byte nicamente,

y al hacerlo lo extrae del buffer. Recordemos que un dato que enviado desde el
Monitor Serial es primero interpretado como ASCII y el monitor lo que hace es
enviar los Bytes convertidos. Por ejemplo si enviamos un 1, el monitor interpretar
que es un ASCII, as, enviar el entero 49 que es el ASCII (1). Despus resta, como
nosotros desde Arduino, lo interpretemos o que tipo de variables utilicemos para
recuperarlo. El monito de Arduino cuando enva datos, lo hace de acuerdo a la
configuracin que tengamos seteada en dicho monitor. Si observamos tenemos
varias opciones de configuracin:

Serial.read()
Para nuestro anlisis carguemos el siguiente cdigo donde Arduino lo que hace es
renviar como looptodo lo mismo que recibe
Loop

Ambos NL & CR : Esta opcin agrega un carcter de CR o carriage


Return (13)( 0D)(\r) y un carcter LF o LineFeed (10)(0A)(\n) al final
de todo lo que enviemos.

Al enviar solo un 1 en la caja de Enviar, recibimos


49 ASCII de 1
13 CR
10 LF

Sin Ajuste de Lnea: Esta opcin no enva ni CR ni LF al final de lo que


enviemos.

Enviamos un 1 Recibimos simplemente

49

Nueva Lnea: Enva solo LF al final de lo que enviemos.

Enviamos un 1 y Recibimos
49
10 LF/NL

Retorno de Carro: Esta opcin enva CR al final de lo que enviemos


Recibimos
49
13 CR

Por Default viene Ambos NL y CR.


Enviemos la cadena HOLA por default y coloquemos print , no println:
Recibimos: 72 79 76 65 13 10

La pregunta del millon: Porque si enviamos el 1 no volvemos a ver el 1?. La


respuesta est en Arduino Serial Parte I. Recordar que al recibir el 49 , lo recibe como
un entero y luego cuando lo reenva lo hace con ASCII(4)-ASCII(9).

Serial.available() Esta funcin retorna 1 si existen bytes disponibles para ser ledos ,
caso contrario devuelve -1 y hay que tener mucho cuidado cuando se la usa con
While() debido a que la velocidad de ejecucin del micro es muy superior a la
velocidad con que se reciben o transmiten los datos por el Serial Port. Supongamos el
siguiente programa:

Serial.available()
En primera instancia dejemos delay(100) comentada, para que no se ejecute.

Al ejecutar , abrimos el monitor y enviemos nuevamente el 1.


49
Ciclo:0
13
Ciclo:1
10
Ciclo:2

No estara realizando lo que pedamos, ya que saldramos de While si no hay ms


datos, como vienen 49-13-10, esperaramos que el ciclo 1 se ejecute despus de
recibir estos caracteres. Esto se produce debido a las diferencias de velocidad con que
ejecuta Arduino y la velocidad con que se reciben los datos. El clock del
Arduino=16Mhz, supongamos que 1 ciclo de instruccin como en la mayora de los
micros sea de Fclok = 4Mhz, esto implica que un ciclo de instruccin dura 0.25
microsegundos. De acuerdo al compilador una instruccin puede demorar varios
ciclos de instruccin. Supongamos que el While() demora 10 ciclos de instruccin
= 2.5 us. Un dato Byte 8N1 a 9600 demora en llegar (8+1)*1/9600=937 us. Ni bien
se reciba el 49 , se imprime y el while() vuelve a ejecutarse ,pero no habr datos
disponibles hasta 937us , en este caso el CR y, por lo tanto el flujo del programa sale
del While dado que el available=-1, y as ejecuta el ciclo 1 del for, pero como los
datos todava no estn disponibles quedan en el primer While esperando. Si sumamos
un pequeo retardo dentro del while , descomentando la lnea delay(100) ( con un
par de milisegundos bastara); obligamos a no seguir la ejecucin para dar tiempo
a que llegue el segundo carcter .Ejecutamos ahora veremos lo que queremos ver

49
13
10
Ciclo:0

As que ha tener cuidado con estas pequeas cosas que son grandes dolores de cabeza.

Arrays[]
Los arrays[] son arreglos de datos , tambin llamados vectores, en este caso de una
dimensin. El compilador reserva una cierta cantidad de bytes consecutivos en la
memoria para este tipo de datos, por lo tanto hay que especificarle una dimensin, es
decir la longitud mxima del mismo. Los arrays pueden contener cualquier tipo de
dato, inclusive Objetos. Los datos que contiene el array[] se llaman elementos del
arreglo y se numeran como 0,1,2,3,4,.(N-1), donde N es la dimensin. Puede
verse al array[] como un edificio de N pisos, cada piso tiene un departamento , 1
elementodonde vive una persona, entonces podemos hacer la analoga de la siguiente
manera para un edificio de 10 pisos:

Edificio[10], quiere decir que N=10, que es su longitud, y sus elementos ser 10
obviamente, uno en cada piso numerados del 0 al 9 (N-1)

Edificio[0] vive Juan


Edificio[1] vive Ana
Edificio[2] vive Antonio
. . . .
Edificio[9] vive Mirta
Array[]

Declaracin e inicializacin de Arreglos


Existen varias maneras de declarare inicializar datos a los arreglos, lo primero que
debemos especificar es el Tipo de dato que va a contener el array[], es decir,
siguiendo con la analoga del edificio, que es lo que vive o se aloja en cada
departamento. Lo segundo es la dimensin.

<Tipo de dato> nombreArray [Dimensin];

int arreglo[10]; // De esta manera especificamos que cada elemento ser un entero.

float arreglo[20]; // Cada elemento ser un float. De igual manera podemos


especificar tipos como String (Cadenas), char, punteros, etc, etc.

Inicializacin de Arrays
Se pueden declarar arrays[] y asignar ms tarde o directamente declarar y asignarle
los elementos, vemos los casos posibles:

Caso 1: Declaracin solamente y posterior inicializacin

Int precios [4];


precios[0]=10; precios[1]=24; precios[2]=10045; precios[3]=0;
Caso 2: Declaracin e inicializacin
Int percios[4]={10,24,10045,0};

Caso 3: Declaracin e inicializacin ( sin dimensin)


Int precios[]={10,24,10045,0};

Recorriendo Arrays[]
El array[] se accede mediante el ndice de los elementos, dicho ndice x es un puntero
dentro del array[x] que apunta al contenido en la posicin x. Si int precios
[4]={10,24,10045,0}; precios[1] ser 24, el ndice x=1. Vemos como x es un
puntero ndice dentro del array. Entonces podemos recorrer un array[] desde x=0
hasta x=Dimensin-1.

Recorrido Arrays[]Cada 500 ms se imprimir el vector completo.

Resultado Recorrido Array[]Tambin podemos armar arrays[] de


texto o cadenas de caracteres o simplemente cadenas. Si bien no hablamos del objeto
String, es fcilmente
entendible.

Array[] de Cadenas Resultado Array[] Cadenas

Cadenas
Las cadenas, como fue introducido, son conjunto de caracteres con la caracterstica
que poseen un carcter especial de terminacin de cadena \0 o llamado
carcter Null de fin de cadena o ASCII(0). Existen varias maneras de definir cadenas.

Lo que distingue simplemente un array de caracteres y una cadena , es que la cadena


es un array de caracteres + el ltimo elemento Null o \0.

String datosCadena=soy cadena tambin;

char arreglo[]={n,o,-, s,o,y,-, c,a,d,e,n,a};

char cadena[]={s,o,y,-, c,a,d,e,n,a,\0};

char otracadena[]=Soy otra Cadena;

arreglo es simplemente un vector de caracteres NO una cadena. Cadena si es una


cadena. datosCadena y otracadena tambin son cadenas.

La diferencia es que en caso de definirlas con arrays[] hay que inclur el \0 final de
cadena.

Esto es hablando extrictamente del Ansi C o standard C. Este carcter no se


visible y tampoco se puede imprimir ya que se usa como fin de cadena. Las
funciones print y println estn compliladas en sus libreras de manera buscan ese
carcter para saber cuando finaliza la impresin, o la unin de cadenas, las funciones
que manipulan cadenas en general. Cuando deben mandar algo al puerto Serie ,
previamente le agregan en Null antes de llamar a la librera, en el sistema Arduino
( Ojo esto es solo en las funciones print/println) ya que tratan al argumento como
cadenas, no ocurre as con otras funciones que manipulan cadenas pues suponen
que lo son.

Veamos este ejemplo y nos queda mas claro:


Tenemos un arreglo de caracters sin fin de cadena y datos que es una cadena

char arreglo[]={n,o,-, s,o,y,-, c,a,d,e,n,a};

char otroarreglo[]={n,o,\0, s,o,y,-, c,a,d,e,n,a};

Ahora hagamos lo siguiente:

Cadenas

Observamos como la funcin print RECORT la impresin de otroarreglo[] dado


que encontr el carcter Null antes de donde debera ir.
no-soy-cadena
no

Probemos ahora recorrer e imprimir otroarreglo , elemento por elemento ya que es


un vector tambin.

Recorrido de Cadenas como Arrays[]

Se observa:

no soy-cadena

De esta manera hemos podido imprimirla dado que lo recorrimos como un vector,
como hemos visto.
Veamos lo siguente que pasa cuando la cadena, no es cadena y se usan las funciones
de librera de Cadenas
String datos=soy Juan; es una cadena
char cadena[]={s,o,y,-, c,a,d,e,n,a,\0}; esta tambin es cadena
La funcin concat permite unir cadenas, similar al operador + de cadenas tambin
con el Objeto String

Funcin concat

Se observa: soy Juansoy-cadena , Unin de Cadenas

Ahora eliminemos el Null \0 en el fin de la variable cadena y ejecutemos


nuevamente:

Se observa: soy Juansoy-cadenasoy Juan , que pas aqu?


Sucede que ahora cadena no es mas una cadena ya que perdi su Null y por lo tanto
la librera de la funcin concat concatena pero solo encuentra Null al final de Soy
Juan que si es cadena y por lo tanto la vuelve a unir. Es muy importante este
carcter Null , ya que el grado de problemas que pueden darse por este simple
elemento puede ser un tremendo dolor de cabezas, as que a no olvidarse. En forma
similar se puede usar el operador +, de concatenacin.

Operador +
Conclusin Importante: Si trabajamos los arreglos char[] como cadenas no olvidar
el carcter Null ya que si se pretende usar las funciones cadena va a haber problemas
extraos. Si queremos usar cadenas sin arreglos pueden usar el Objeto String que
permite definir variables cadena y automaticamente genera el Null. Las funciones
print/println son funciones que tratan a las variables como cadenas de texto

Funcines de cadena
unacadena.length(): devuelve la longitud de unacadena (es decir, el nmero de
caracteres). No se cuenta el carcter nulo que marca los finales de cadena. No tiene
parmetros.

unacadena.compareTo(otracadena): compara unacadena con otracadena que se


pase como parmetro (bien de forma literal, bien mediante un objeto String).
Comparar significa detectar qu cadena se ordena antes que la otra. Las cadenas se
comparan desde su principio carcter a carcter utilizando el orden de sus cdigos
ASCII. Esto quiere decir, por ejemplo, que a va antes que b pero despus que A,
y las cifras van antes de las letras. Su valor de retorno ser un nmero negativo si
unacadena va antes que la cadena pasada como parmetro, 0 si las dos cadenas son
iguales o un nmero positivo si unacadena va despus que la cadena pasada como
parmetro.

Si se quisiera realizar una comparacin similar pero entre arrays de caracteres, se


puede utilizar la instruccin strcmp(), la cual tiene dos parmetros (que corresponden
con los dos arrays a comparar), y cuyo valor de retorno es idntico
a unacadena.compareTo().

unacadena.equals(otracadena): compara si unacadena es igual a otracadena,


pasada como parmetro. La comparacin es case-sensitive: esto quiere decir que la
cadena hola no es igual a HOLA. Esta instruccin es equivalente al operador ==
para objetos String . Su valor de retorno es true si unacadena es igual a la cadena
especificada como parmetro, o false en caso contrario.

unacadena.equalsIgnoreCase(otracadena): compara si unacadena es igual


a otracadena, pasada como parmetro. La comparacin es case-insensitive: esto
quiere decir que la cadena hola es igual a HOLA. Su valor de retorno es true si
unacadena es igual a la cadena especificada como parmetro, o false en caso
contrario.

unacadena.indexOf(otradacena o carcter,pos): devuelve la posicin (un nmero


entero) dentro de unacadena donde se encuentra el carcter o el principio de
la otracadena especificada como parmetro. Si no se encuentra nada, devuelve -1.
Observar que las posiciones se numeran empezando por 0. Por defecto, la bsqueda
comienza desde el principio de unacadena, pero mediante un segundo
parmetro pos opcional se puede indicar la posicin a partir de la cual se quiere
empezar a buscar. De esta manera, se puede utilizar esta instruccin para encontrar
paso a paso todas las ocurrencias que existan de la cadena buscada.
unacadena.lastIndexOf(otracadena/carcter, pos): devuelve la posicin (un
nmero entero) dentro de unacadena donde se encuentra el carcter o el principio
de la otracadena especificada como parmetro. Si no se encuentra nada, devuelve -1.
Observar que las posiciones se numeran empezando por 0. Por defecto, la bsqueda
comienza desde el final de unacadena hacia atrs, pero mediante un segundo
parmetro pos opcional se puede indicar la posicin a partir de la cual se quiere
empezar a buscar (hacia atrs siempre). De esta manera, se puede utilizar esta
instruccin para encontrar paso a paso todas las ocurrencias que existan de la cadena
buscada.

unacadena.charAt(indice): devuelve el carcter cuya posicin indice(dato entero)


se haya especificado como parmetro. Las posiciones se numeran empezando por 0.

unacadena.substring(posini,posfin): devuelve la subcadena dentro de unacadena


existente entre la posicin inicial (especificada como primer parmetro) y la posicin
final (especificada como segundo parmetro opcional). La posicin inicial indicada
es inclusiva (es decir, el carcter que ocupa esa posicin es incluido en la
subcadena), pero la posicin final opcional es exclusiva (es decir, el carcter
que ocupa esa posicin es el primero en no incluirse en la subcadena). Si dicha
posicin final no se especifica, la subcadena contina hasta el final de unacadena.
Observar que las posiciones se numeran empezando por 0.

unacadena.replace(subcadena / carcter , subcadenareplace): sustituye


una subcadena existente dentro de unacadena (especificada como primer
parmetro) por otra subcadenarepace (especificada como segundo), todas las veces
que aparezca. Tambin sirve para sustituir caracteres individuales. La sustitucin se
realiza sobre unacadena, sobrescribiendo su valor original.

unacadena.toLowerCase(): convierte todos los caracteres de unacadena en


minsculas. La conversin se realiza sobre unacadena, sobrescribiendo su valor
original.
unacadena.toUpperCase(): convierte todos los caracteres de unacadena en
maysculas. La conversin se realiza sobre unacadena, sobrescribiendo su valor
original.

unacadena.trim(): elimina todos los espacios en blanco y tabulaciones existentes al


principio y al final de unacadena. La conversin se realiza sobre unacadena,
sobrescribiendo su valor original.

unacadena.concat(otracadena) : aade (concatena) al final de la cadena


unacadena otracadena, pasada como parmetro. Como resultado obtendremos un
nuevo valor en unacadena: su valor original seguido de ese valor pasado por
parmetros, unidos. Es equivalente al operador + para objetos String.

unacadena.endsWith(otracadena/caracter): chequea si unacadena acaba con los


caracteres de otra cadena, pasada por parmetro. Su valor de retorno es true si
unacadena acaba con la cadena especificada como parmetro, o false en caso
contrario.

unacadena.startsWith(otracadena/ caracter) : Chequea si unacadena empieza


con los caracteres de otracadena, pasada por parmetro. Su valor de retorno es true
si unacadena empieza con la cadena especificada como parmetro, o false en caso
contrario.

unacadena.toCharArray(arreglo,cantidad ): copia una cantidad determinada de


caracteres pertenecientes a unacadena a un array de tipo char. Ese arreglo ha de
ser especificado como primer parmetro, y la cantidad de caracteres a copiar all ha
de ser especificada como segundo parmetro. Siempre se empiezan a obtener los
caracteres desde el principio de unacadena. No tiene valor de retorno.

unacadena.getBytes(arreglo, cantidad ): copia una cantidad determinada de


caracteres pertenecientes a unacadena a un array de tipo byte. Ese arreglo ha de
ser especificado como primer parmetro, y la cantidad de caracteres a copiar all ha
de ser especificada como segundo parmetro. Siempre se empiezan a obtener los
caracteres desde el principio de unacadena. No tiene valor de retorno. Hay que
tener en cuenta que arreglo [cantidad-1]=Null siempre = Ascii(0), ver ms
adelante.

unacadena.toInt(): si unacadena tiene un valor que empieza por cifras numricas,


esta instruccin es capaz de distinguirlas (descartando los posibles caracteres no
numricos posteriores) y devolver ese valor numrico transformado en un dato de tipo
entero. Es decir, transforma una cadena en un nmero entero, si es posible. Esta
instruccin no tiene parmetros.

unacadena.toFloat(): si unacadena tiene un valor que empieza por cifras numricas


y punto decimal, esta instruccin es capaz de distinguirlas (descartando los posibles
caracteres no numricos posteriores) y devolver ese valor numrico transformado en
un dato de tipo float. Es decir, transforma una cadena en un nmero float, si es posible.
Esta instruccin no tiene parmetros.

unacadena.charAt(pos): Extrae de unacadena el carcter que se encuentra en la


posicin=pos.

unacadena.setCharAt(pos, caracter): Susbtituye en la unacadena el carcter de la


posicin=pos por otro carcter especificado en carcter.

Fuente de funciones Arduino Curso prctico de formacin de Oscar Torrente


Artero , editora AlfaOmega.

A los efectos de ver como funcionan estos mtodos adjunto el siguiente programa con
algunas de ellas
funcionesCadena
Impresin Funciones Cadena
Recomiendo estas funciones ya que puede hacerse todo lo que necesitemos. Un
dispositivo cualquiera, sea monitor de Arduino o cualquier otro que tenga una
interface UART 232 TTL, puede enviar datos a Arduino y luego procesarse mediante
estas funciones cadena. Serial posee funciones propias que prcticamente hacen lo
mismo pero que en la prctica no las he necesitado realmente.

getBytes[] y toCharArray[] merecen anlisis separado


Observemos lo que hace la funcin getBytes(), tomando cantidad = 6, primeros
Bytes de la variable cadenaUno del ejercicio anterior. Recordar
que cadenaUno=PanamaHitek;
getBytes()
La salida es: 80-97-110-97-109-0

80-ASCII de P
97-ASCII de a
110-ASCII de n
97-ASCII de a
109- ASCII de m
0 ASCII NulL, como dijimos en la explicacin de getBytes(), el elemento [5] =
elemento[cantidad-1]= Null
Si quisiramos tomar en el vector Panam completo , deberamos sumar una unidad
ms a todo:

getBytes()
y la salida sera 80-97-110-97-109-97-Null. Ahora si son los Ascii de todo el
substring Panama
El arreglo[cantidad-1]=Nul, no tiene mucho sentido ni razn de porque es esto. Si la
conversin fuera no a Bytes[] sino a Char[], tendra sentido porque en realidad
obtendramos una cadena. Quizs sea un bug de la versin de Arduino.
Probemos entonces con toCharArray()

toCharArray()

Vemos que ahora no es necesario el bucle For como el caso anterior. Por qu? ,
porque arreglo2[] es un arreglo de caracteres y es uno de los argumentos favoritos de
Print y Println.

Por monitor sale: Panam , lo cual confirma que el elemento arreglo2[6]=Null por
lo que automticamente arreglo2[] se convierte en una CADENA y es por ello que es
bien aceptado por print().

Objeto String
Arduino tiene como recurso la variable String, que en realidad es un Objeto ya que si
recordamos , el Sketch hereda de C /C++ orientado a objetos. Ya hemos visto lo que
hace String, permite definir e inicializar cadenas. Lo ms frecuente es utilizar este
Objeto para trabajar con Cadenas en forma cmoda.

String cadena=Hola que tal; define e inicializa la cadena

String otracadena=c; // define e inicializa un carcter como cadena

Si deseamos convertir variable tipo Int, Bytes,long, float,etc a cadenas


utilizamos String(variable). Podemos incluso formatear de la siguiente manera.
Cadena=String(20,BIN); toma el entero 20 , lo pasa a binario y lo pasa a cadena. De
la misma manera podemos hacer Cadena=String(255,HEX), o
Cadena=String(20,OCT).

El operador + sirve para concatenar cadenas, como lo hemos visto

String cade=c;
String cad=123;
String ca=s.89;
Serial.println(cade+cad+ca);
Saldr: c123s.89

En resumen, lo ideal y por experiencia , si vamos a leer datos desde el puerto serial
desde el monitor de Arduino o desde cualquier dispositivo Serial asincrnico
tipo UART o Serial, lo ideal es leer carcter por carcter y guardarlo en un
arreglo buffer[] o cadena String y luego analizarla posteriormente para tratarla.
Podemos crear nuestro propio protocolo de comunicacin entre Arduino y cualquier
dispositivo Serial. Podemos correr una aplicacin de Java o C#, por ejemplo donde la
interface principal del programa enve una trama de datos en forma de cadena y al
principio y final de la cadena coloquemos uno caracteres especiales de final de trama
para que Arduino sepa dnde empieza y donde termina dicha trama y as detectarla
con el Serial.read(), ver Aplicacin Trasceiver de 433Mhz. Si vamos a leer desde el
Monitor de Arduino, recordar que todo lo que enve se le agrega CR y LF por default,
el cual es configurable tambin como vimos.

Por ltimo, vamos a ver una aplicacin donde desde el monitor enviamos un nmero
flotante, por ejemplo el 100.5 y deseamos interpretarlo como lo que es, ya que el
monitor va a tomar a dicho nmero como una cadena de caracteres y enviar
los ASCII uno a uno . Luego sumarle otro valor flotante , el mismo 100.5 y presentar
el resultado.
Recibiendo por SerialPort Arduino desde Monitor
El delay(20), como dijimos anteriormente es para asegurar la permanencia del flujo
del programa dentro del While(). Existe otra manera que es usar un flag que indique
el final de la recepcin, y que aunque salte el While() o salga del While() esto no
influya:
Utilizando
flag para que permita que While() salte
De esta manera permitimos evitamos el delay( ) del caso anterior que el bloqueante,
y el micro no puede hacer otra cosa que esperar. Esto permite que el Micro pueda
ejecutar otros procesos.

Si fuese por ejemplo un dispositivo, no desde el monitor , desde donde viniesen los
datos, por ejemplo BlueTooth() que tambin es serie, podramos inventar una trama
en donde la misma termine con el carcter z. Por ejemplo el dispositivo enva el
entero 145z y nosotros tenemos que filtrar y tomar el 145. Suponemos que CR y LF
no se usan as que para simular colocamos el monitor en Sin Ajuste de lnea aunque
de la otra manera igual funciona. Para probar este cdigo, desde el monitor haga Send
cualquier entero y z, ejemplo 14534z.
Cadena con fin de
trama
Con esto damos por terminada un recorrida de las diversas posibilidades que nos
permite trabajar con Cadenas y Arrays[] en general. Espero les sea de utilidad.

You might also like