Cuando queremos comunicarnos con un dispositivo, muchas veces mandamos información y recibimos información como un flujo de datos binarios que nosotros como seres humanos podemos leer traduciéndolos y aplicándoles un significado.

El problema reside es que esta traducción de las manejo de las lecturas desde el monitor serie, se realizan mediante la tabla ASCII.

Pero estos valores de la tablas ASCII funcionan correctamente cuando se manejan valores conocidos de teclado como las letras y números. Sin embargo los primeros elementos de la tabla ASCII no se identifican con teclas concretas y de alguna manera son imposibles de introducir dentro de un puerto de comunicación a menos que se haga de forma programada.

A estos elementos se les denomina caracteres de control.

asciifull

Desde el 0 hasta el 31 y la 127 existen una serie de acciones de teclado que no se asumen como una introducción de caracteres propiamente dicha. Por ello es necesario manejarlas como otro tipo de dato.

Dos de estos códigos son muy importante que son el fin de linea y el retorno de carro.

Muchas veces estos dos códigos son muy necesarios para hacer funcionar algunos de nuestros dispositivos de forma correcta dentro del terminal, ya que el fabricante los ha configurado para proporcionar respuesta solo y cuando el dispositivo reconoce estos dos códigos y que aparecen en el monitor serie en una pestaña abajo.

AT_NLCR

Tener una de estas opciones activa quiere decir que se insertarán esos códigos ASCII dentro del bitstream de envio cada vez que le presionemos el botón de enviar.

  • Sin ajuste de linea → No envia nada, solo lo que hayamos escrito en el campo de texto.
  • Nueva Linea → Envia el código bitstream correpondiente a Line Feed para saltar a una nueva linea en el monitor
  • Retorno de carro → Posiciona nuestro cursor a la primera posición de una nueva linea. Enviará por tanto el bitstream que corresponde al presionar las teclas de INTRO.
  • Ambos NL & CR →  Se pasan los dos bitstream unidos.

IMPORTANTE: Existen dispositivos que requieren de ambos bitsreams para producir una respuesta adecuada como los Bluetooth. En caso de no tener seleccionada esta pestaña es posible que no nos proporcionen respuesta a nuestras peticiones.

Ahora, quizás, uno de los peores problemas reside en como manejar este tipo de dato para su lectura desde el monitor serie. Ya que existen funciones para leer los valores desde el monitor serie read() y readString(), pero en cambio no nos identifican si los bitstream se corresponden con un carácter de control. Estas funciones, entre otras muchas almacenan sus valores en un stream que es un conjunto de datos que identifican caracteres y binarios.

Estos datos pueden ser almacenados dentro de :

  • un tipo de dato String
  • un array
  • un vector de caracteres char[]
  • o como un puntero de memoria en el que se almacenan caracteres y se especifica de la siguiente manera. char*

Vamos a realizar el siguiente ejercicio en el que mandaremos comandos AT a un dispositivo y realizaremos un control de diferenciación de caracteres.

Los dispositivos que se controlan con comandos AT, solo responden cuando identifican que el comando introducido empieza por AT seguido de más caracteres.


#include <SoftwareSerial.h>

long bps = 115200;
char* cmd = "$";
SoftwareSerial RXTX(10,11);

void setup(){
   Serial.begin(bps);
   RXTX.begin(bps);

   Serial.println("Starting...");
   Serial.println(cmd);
}

void loop(){

   if (RXTX.available()){
      String data = RXTX.readString();
      Serial.println(data);
   }

   while (Serial.available()){
      String data = Serial.readString();
      if (data == cmd){
         Serial.println("SendSMS");
      }else{
         Serial.println("SendAT");
         RXTX.println(data);
      }
   }
}

Vamos a establecer que el monitor está configurado en primera instancia con retorno de carro y fin de linea y podremos ver que no identifica el condicional y establece un envio de datos al dispositivo.

AT_SerialFAIL

Pero si ahora en lugar de tener el monitor serie configurado sin ajuste de linea, obtendremos lo siguiente.

AT_SerialDone

Esto se debe a que aunque nosotros no lo veamos, en la función readString se está guardando el valor introducido en el campo de texto y además el retorno de carro y el fin de linea. Por lo que el condicional no es correcto y el símbolo del dolar pasaría contiene más información de la que nosotros podemos ver.

Pues si profundizamos en este problema, podremos observar otros aspectos que requieren prestar atención.

En el siguiente ejercicio dispongo de un módulo GSM/GPRS SIM900A, que es una placa que permite conectar mi tarjeta de teléfono y utilizarla para conectarme a la red de datos, enviar y recibir llamadas y mensajes de texto.

Pero una manía que tengo es que me gusta experimentar en tiempo real desde el terminal de Arduino, antes que elaborar programas sin saber que voy a obtener una respuesta del dispositivo.

Pues con un comando AT, este dispositivo me permite mandar un mensaje de texto; concretamente AT+CMGS seguido del número de teléfono al que quiero enviar el mensaje. Y me encuentro con lo siguiente:

AT_failsendSMSCuando se establece este comando concreto, la placa se dispone en un modo de lectura continua para la introducción de texto hasta que no recibe un carácter de control definido.

Esto es porque los mensajes de texto, también contienen saltos de linea y retorno de carro en su interior. Es decir, que cuando editamos un mensaje para ser enviado podemos posicionar el cursos donde queramos o crear párrafos en él.

Por lo que estos dos identificadores no establecen que el mensaje se termine de editar. Pero en la documentación nos aparece lo siguiente.

AT_sendSMS

Se requiere de la introducción de un carácter de control denominado SUBSTITUTE o el que aparece como ctrl-Z. Este identificador requiere de un envio del dato en hexadecimal mediante programación una vez se haya editado el mensaje.

Para ello, hemos de identificar el factor SUBSTITUTE en la tabla ASCII ; que es el

Char  Dec  Oct  Hex
(sub) 26 0032 0x1a

y se utilizan los siguiente comandos:

   Serial.println(sms_text);   // Edición del mensaje y envio
   Serial.write(0x1A);         // Terminador del mensaje e identificador SUBS