31 octubre 2013

Arduino y el GSM PROPOX MMsmartGSM ( SIM900 )

¡Hola a todos!

Nuevamente mil disculpas por no haber escrito nada en las últimas casi tres semanas. Actualmente en mi trabajo oficial (ese que me da de comer) estamos realizando tareas de levantamiento de encuestas en campo y eso está consumiendo mucho de mi tiempo. Tal vez un día con más de tiempo les cuento un poquito de lo que estamos trabajando y de paso les sirve de ayuda en su trabajo diario.

Ahora, regresando al tema de las últimas entradas, hace unos días conseguimos los módulos GSM PROPOX para la Tienda TeUbi.co, estos módulos me gustaron mucho ya que están basados en el muy versátil módulo GSM SIM900, este es un módulo capaz de funcionar casi en cualquier lugar del mundo donde haya cobertura de GSM y tiene algunas funciones muy prácticas en caso de que quieran conectar su proyecto electrónico a Internet.

Esta entrada me voy  dedicar a explicarles la configuración básica del mismo para que lo puedan utilizar con su Arduino y como utilizar funciones básicas como envío de SMS y realización de peticiones a servidores HTTP. En esta entrada vamos a centrarnos en el envío de SMS y la siguiente en hacer peticiones HTTP.

Advierto que esta entrada podría resultar un "poco larga" y llena de texto, pero les recomiendo la lean por completo ya que si bien el módulo es sencillo de utilizar requiere de algunas preparaciones previas que son completamente necesarias antes de que podamos utilizarlo en conjunto con nuestro Arduino.

Habiendo dicho esto... ¡Comencemos!

Conociendo el MMsmartGSM


El MMsmartGSM es una "tableta de expansión" que nos sirve para acceder fácilmente a los pines del módulo SIM900 de SIMCOM. Si bien hay "shields" para Arduino basados en este módulo, lo que me gustó en particular de la versión de PROPOX es que expone todos los pines, permitiéndote utilizar toda su funcionalidad como más te convenga, además que el espacio que ocupa el módulo de esta manera es menor que cuando se utiliza en forma de "shield".

El SIM900 en el que está basado este módulo tiene capacidades para voz, fax y datos, literalmente es un teléfono celular en un chip. Tal vez lo mejor del módulo es que toda la funcionalidad del mismo es accesible desde sencillos comandos AT facilitando enormemente su utilización. Es posible también controlarlo desde las interfaces I2C o SPI pero en esta entrada nos concentraremos en los comandos AT ya que son más fáciles de comprender y además tenemos una exténsa documentación sobre su uso.

Tal vez el único punto en "contra" es que los pines están expuestos en dos líneas paralelas a los lados de la tableta, lo que hace que sea un poco "complicado" de utilizarse en breadboard. Sin embargo al final del día solo utilizaremos realmente las líneas de comunicación de datos en serie y alimentación así que no debería representar mayor inconveniente al momento de realizar el montaje.

El chip de celular se encuentra en la parte inferior del módulo y no debería de ser demasiado difícil accesarlo, aunque debo aceptar que para instalaciones más permanentes eso podría resultar un problema en caso de que necesiten estar cambiando de "chip". Una posible solución sería utilizar un par de headers hembra 13x2 para poder remover fácilmente el módulo.

Preparando el módulo para Arduino


Algunas consideraciones previas


Lastimosamente, a diferencia del shield, este módulo no viene listo para usarse directamente en Arduino por dos razones: La primera, que puede resultar frustrante, es que el módulo está diseñado para comunicarse vía serial a través de control de flujo de señalizado por hardware.

Lo anterior en español significa que además de las líneas TX y RX necesitamos tener acceso las lineas CTS y RTS. Estas líneas son muy comunes en el estándar de comunicación serial RS232, lástimosamente la comunicación UART que utilizamos en Arduino solo trabaja con las líneas de envío y recepción de datos.

Así que primero lo primero. Antes de conectar el módulo a nuestro Arduino necesitamos configurar el módulo con un convertidor USB<->Serial RS232, en esta entrada utilizaré de SparkFun Electronics la tableta expansora del FTDI FT232RL que nos provee de todas las líneas necesarias para comunicarnos con dispositivos RS232.

La segunda razón por la cual no podemos utilizar el módulo directamente en Arduino es que trabaja a 3.7V. Este módulo está diseñado de esta manera para utilizarse directamente con una batería de Ion-Litio.

Para alimentar el módulo más por pereza que otra cosa estoy utilizando una batería de Ión de Litio de 3.7V. Es posible, sin embargo, construir una pequeña fuente con un regulador de voltaje lineal que provea de suficiente voltaje y corriente al módulo como se muestra en la hoja técnica sobre notas de implementación.

Lo anterior implica también que los niveles lógicos del módulo trabajan a 3.3V (en la práctica a cerca de 2.8V) por lo que necesitaremos también un convertidor de nivel lógico de voltaje para conectar las líneas de señal.

Respecto al consumo de corriente hay que tomar en consideración que este módulo puede llegar a requerir hasta 2A de picos de corriente al momento de transmitir datos a toda velocidad vía GPRS, por ello es muy recomendable utilizar directamente una batería de Ion-Litio o Ion-Polímero o en su defecto una fuente de voltaje que sea capaz de proveer la potencia necesaria en tanto el módulo la necesite.

Para los ejemplos que vamos a mostrar en este blog no vamos a enviar cantidades astronómicas de datos, pero es bueno que tomen esto en consideración en el caso de que estén planeando utilizar el módulo en aplicaciones altamente intensivas de uso de datos.

Una vez habiendo tomado lo anterior en consideración, vamos a conectar el módulo GSM a nuestro convertidor USB->Serial de la siguiente manera:




Notarán que el diagrama incluye un pulsador en la línea PWRKEY del MMsmartGSM. Este pulsador lo ocuparemos para "encender" a nuestro módulo una vez se encuentre alimentado.

La otra línea que utilizamos es VDD_EXT, esta línea mantiene el voltaje lógico regulado de referencia. Utilizaremos esta línea como el "voltaje bajo" en nuestro convertidor de niveles de voltaje.

El módulo armado se ve de la siguiente manera:
Nota: En lo personal no me gusta la estética de los cables "jumper". Prefiero tomar un poco más de tiempo y armar los prototipos con cable UTP, sin embargo si como yo están un poco cortos de tiempo resultan una excelente opción.

Configurando la señalización por software


Para poder utilizar el SIM900 en Arduino primero tenemos que configurarlo para utilizar señalización por sofware. Esto debemos hacerlo así ya que el módulo esperará la señalización en las líneas CTS y RTS para enviar datos.

Primero debemos encender nuestro módulo presionando por un momento el pulsador sobre la línea PWRKEY hasta que encienda el LED sobre la tableta de expansión.

Luego conectamos nuestro FT232RL a nuestra computadora y esperamos a que sea detectado por el sistema operativo.

Si estamos en linux podemos iniciar "minicom" con el siguiente comando:

$ minicom -D /dev/ttyUSB0 -b 9600



Es muy posible que nos muestre una indicación de que minicom se encuentra OFFLINE, pero podemos fácilmente ignorarla y continuar.

Si estamos en windows debemos de identificar que número de puerto "COM" se ha asignado en el administrador de dispositivos y tenemos que crear una nueva conexión utilizando dicho puerto.

Una forma sencilla de probar que todo está conectado y funcionando es escribir el siguiente comando en la consola:

AT

Al presionar [Enter] nos deberá de responder con:

OK


Si al escribir no notamos ningún cambio o nisiquiera vemos las letras que estamos escribiendo, muy probablemente el módulo se encuentre apagado o no se haya inicializado correctamente. Debemos entonces verificar que la alimentación sea la adecuada y que las líneas de datos estén bien conectadas.

Habilitando el control de flujo por Software


Para habilitar el control de flujo por software debemos digitar el siguiente comando:

AT+IFC=1,1

Al presionar [Enter] nos deberá de responder con:

OK


Este comando se encarga de activar el control de flujo por software para envío y recepción de datos.

El siguiente comando nos permitirá definir el "formato" de transmisión de datos en la línea serial. Ya que el control de flujo se realizará por Software es necesario definir algunos parámetros que facilitarán la detección de cada bloque de datos transmitidos por el puerto.

Vamos a utilizar el comando AT+ICF de la siguiente manera:

AT+ICF=1,1

Al presionar [Enter] nos responderá con "OK".


Esta configuración hará que el módem envíe bloques de 8 bits de datos, cero bits de paridad y 2 bits de parada. La paridad del los datos será "par".

Si quieren ver otras opciones del formato de señalización pueden escribir el comando:

AT+ICF=?

Y presionar [Enter].

No es necesario utilizar una configuración en particular más si es necesario que recuerden que parámetros están utilizando para configurar adecuadamente su software de terminal.

Fijando la velocidad de transmisión de datos


El SIM900 tiene la capacidad de detectar automáticamente la velocidad de transmisión de datos, sin embargo en la práctica con Arduino es mucho más fácil trabajar a una velocidad fija y predecible.

Para no tener que quebrarnos tanto la cabeza utilizaremos una velocidad de transmisión de datos de 9,600bps. Esto lo configuramos con el siguiente comando:

AT+IPR=9600

Al presionar [Enter] nos deberá de responder con:

OK

Guardando la configuración en la EEPROM


Las configuraciones que hemos realizado hasta ahora son válidas únicamente para esta "sesión" y serán borradas al apagarse el módulo. El SIM900 es capaz de guardar dos perfiles de configuración con el comando AT&W. Esto nos permite tener dos configuraciones que podemos cambiar según necesitemos.

Para guardar la configuración actual y asegurarnos de que esta sea cargada en el siguiente reinicio escribimos el siguiente comando:

AT&W

Si se guarda correctamente al presionar [Enter] nos responderá con "OK"

Apagando el módulo


Existen dos formas de apagar nuestro módulo, una es directamente por hardware la cual no explicaremos en esta entrada y la otra es por software a través de un comando sencillo:

AT+CPOWD=1

Al presionar [Enter] nos mostrará el mensaje "NORMAL POWER DOWN" y se apagará el led de conexión. Este comando provoca que el módulo termine todas las conexiones, se desconecte de la red celular y entre a un modo de "stand by" de muy bajo consumo.

Probando la señalización por software


Ahora viene el momento de la verdad, vamos a desconectar las líneas CTS y RTS de nuestro módulo GSM y vamos a dejar únicamente las líneas TX y RX. No es necesario que desconectemos nuestro FT232RL ya que en el comando anterior nos encargamos de apagar nuestro módulo GSM.

Nuestro módulo GSM deberá quedar conectado de la siguiente manera:



Debemos de cerrar minicom o HyperTerminal e iniciarlos con los siguientes parámetros:

En minicom:

minicom -D /dev/ttyUSB0 -b 9600 -s


Esto abrirá una pequeña ventana de configuración. Debemos de elegir "Serial port setup" y elegir las siguientes opciones:
  • 8 bits de datos
  • 0 bits de paridad
  • 2 bits de parada
  • Paridad "par" (even)

Deshabilitamos el control de flujo por Hardware y dejamos únicamente habilitado el control por "Software".


En HyperTerminal es un poco más sencillo ya que la ventana de configuración se abre automáticamente al crear una nueva conexión. Solo nos aseguramos de seleccionar los mismos parámetros que mencioné anteriormente para minicom.

Luego de haber configurado el software simplemente encendemos nuevamente nuestro módulo GSM presionando el pulsador y esperamos a que aparesca el siguiente mensaje:


Call Ready


Una vez veamos este mensaje en nuestro terminal podemos decir misión cumplida. Hemos configurado nuestro módulo GSM PROPOX para poder utilizarlo en Arduino.

Por ahora antes de continuar vamos a apagar nuestro módulo.

> AT+CPOWD=1

Conectándonos en Arduino vía SoftSerial


Los ATmega cuentan por defecto con un módulo de comunicaciones UART, este módulo se encarga de manejar de manera autónoma las comunicaciones vía serial en nuestro Arduino. El problema es que solo hay un puerto disponible y este es utilizado por el programador y la computadora por lo que conectar dispositivos al mismopara controlarlos puede resultar en algunos casos un poco frustrante. Para el UNO por ejemplo cada vez que vamos a programar nuestro Arduino tenemos que desconectar físicamente el módulo que estemos utilizando, programarlo y luego volverlo a conectar.

La opción B es utilizar entonces la biblioteca SoftSerial, esta se encarga de "emular" un puerto serial sobre las líneas de datos del Arduino con algunas limitaciones.

¿Por qué utilizar un SoftSerial? Pues primero porque los datos que vamos a comunicar entre nuestro puerto y el módulo son bastante "simples" y siguen la dinámica de un "chat", es decir enviamos un comando y recibimos una respuesta.

Para aplicaciones que están enviando continuamente datos (como la salida NMEA de un GPS) posiblemente nos interese más utilizar el módulo UART integrado y dejar que se encargue de manejar la comunicación por si mismo.

Para variar un poco, vamos a utilizar en esta entrada un Arduino Micro. Este tiene funcionalidad similar al Arduino Leonardo pero en un tamaño reducido que se puede colocar fácilmente sobre una breadboard.

Creando un Puente Serial


Para monitorear que nuestro módulo está funcionando de manera adecuada vamos a utilizar un pequeño "puente serial". Este código funciona redireccionando todos los datos desde el SoftSerial hasta el módulo UART de nuestro Arduino. Nos servirá para "debbugear" y verificar que el módulo GSM está funcionando adecuadamente.

Copiamos el siguiente código en el IDE Arduino:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(10, 11); // RX, TX

void setup()  
{
  // Open serial communications and wait for port to open:
  Serial.begin(57600);
  while (!Serial) {
    ; // Esperar a que el puerto serial se conecte. Necesario para Leonardo
  }

  // Colocar aquí la velocidad pre-configurada en el PROPOX
  mySerial.begin(9600);
}

void loop() // run over and over
{
  if (mySerial.available())
    Serial.write(mySerial.read());
  if (Serial.available())
    mySerial.write(Serial.read());
}


Luego cargamos nuestro sketch y tendremos en la práctica a nuestro Arduino haciendo las veces de un convertidor USB<->Serial.

Para probar el módulo conectamos todo como se muestra en el siguiente diagráma:


Notarán que tenemos dos pulsadores en las líneas 2 y 3 de nuestro Arduino. Más adelante les explicaremos para que nos servirán estas líneas.

Probando el Arduino como puente Serial


Para probar el circuito simplemente abrimos el puerto COM generado por el Arduino de la misma manera que lo hicimos en pasos anteriores y encendemos nuestro módulo esperando la indicación de que el módulo está funcionando.

Si recibimos "Call Ready" y podemos enviar el comando "AT" entonces todo está funcionando de manera correcta.

Si no funcionara verifica nuevamente las conexiones.

Apagando el módulo GSM desde Arduino

Ahora que tenemos nuestro módulo conectado y funcionando vamos a agregar una pequeña función que nos permita apagar el módulo GSM cuando enviemos un pulso en bajo a la línea 3 de nuestro Arduino.

Esta es la razón por la cual agregamos dos pulsadores a nuestro Arduino.

Primero configuramos nuestros pines como entradas "PULLUP", esto nos ahorrará el tener que agregar una resistencia a la salida:

// Colocar dentro de SETUP luego de mySerial.begin
    pinMode(3,INPUT_PULLUP);

Luego asociamos la interrupción correspondiente para detectar cuando el estado del pin cambia de un 1 lógico (configuración por defecto del PULL-UP) a 0.

// Colocar dentro de SETUP luego de mySerial.begin
    attachInterrupt(0, pin3OnFalling, FALLING);

Vamos a definir un par de variables globales para "prevenir" el rebote del pin y para "marcar" cuando hemos recibido una interrupción de "apagado".
volatile long lastInt = 0;
volatile boolean startShutDown = false;

Agregamos nuestro código de interrupción debe de ser "muy rápido" así que simplemente guardará en una variable que se recibió la interrupción y la procesaremos luego en el loop principal.
void pin3OnFalling() {
  if((millis()-lastInt)<500) {
    startShutDown = true;
    lastInt = millis();
  }
}

En el loop principal vamos a agregar un nuevo if que se encargará de llamar a la función que enviará el comando de apagado al módulo cada vez que exista una marca de interrupción recibida.
// Copiar adentro de loop():
  if(startShutDown) {
    Serial.print("Shutdown interrupt received\r\n");
    shutDown();
    startShutDown = false;
  }

Y por último la funcion que envía el comando de apagado:
void shutDown() {
  pushSlow("AT+CPOWD=1\r\n");
}

La función pushSlow es una función utilitaria que envia caracter por caracter al serial emulado:
void pushSlow(char* command) {
  for(int i=0; i<strlen(command)-1; i++) {
    mySerial.write(command[i]);
    if(command[i]=='\n') {
      delay(1000); // Retardo de 1seg por cada fin de linea
    } else {
      delay(100); // Retardo de 100ms por cada caracter
    }
  }
}

Nota: para utilizar la función strlen (tamaño de una cadena) debemos de incluir la biblioteca <string.h> al inicio con "#include <string.h>"

¿Por qué utilizo una función tan complicada? Desafortunadamente, como mencionaba al inicio, el SoftSerial tiene ciertas limitaciones y una de ellas es que no maneja muy bien la transmisión de datos vía control de flujo por software, luego de hacer prueba y error la forma más "efectiva" aunque debo decir poco eficiente de garantizar que no se de-sincronizara la línea de envío de datos era enviar carácter por carácter y agregar un pequeño retardo entre el envío de cada carácter.

Es fea, poco eficiente... Pero funciona. Aún así no me enorgullezco demasiado de esta función así que digamos que es "provisional", en tanto encuentre una solución más elegante se las comparto.

Nota: En caso de usar el puerto serial controlado por el módulo UART integrado en su Arduino no tendrían este problema y podrían reducir el código a algo tan simple como:

void shutDown() {
  mySerial.write("AT+CPOWD=1\r\n");
}

Código para detección de encendido del módulo


Hasta ahora hemos trabajado con nuestro módulo de forma enteramente "manual". Sin embargo idealmente queremos utilizar el módulo sin necesidad de supervisión humana.

No voy a entrar en detalles sobre este código ya que lo hemos utilizado en otras entradas. La idea es simple, vamos almacenando carácter por carácter en un buffer (un string vacío) y vamos revisando el contenido cada vez que encontramos un fin de línea.

Si llegamos al fin de línea y no se ha vaciado el buffer lo "desechamos". Es importante que el tamaño del buffer deberá ser lo suficientemente grande para evitar que la palabra o la frase que estamos buscando quede cortada entre limpieza de buffers.

En este caso utilizaremos la respuesta "Call ready" para identificar que el módulo está encendido y listo para trabajar.

// Agregar en el encabezado
#define BUFFSIZE 1024 // Puedes cambiar por un buffer más pequeño
// Definir como global:
char buffer[BUFFSIZE];
int buffSize = 0;
// Agregar funciones utilitarias
void clearBuffer() {
  for(int i=0;i<BUFFSIZE;i++) {
    buffer[i] = 0;
  }
  buffSize = 0;
}

volatile byte chr;

void appendToBuffer(char c) {
  if(buffSize<BUFFSIZE) {
    buffer[buffSize++] = c;
  } else {
    clearBuffer();
    buffer[buffSize++] = c;
  }
}

// Agregar variable que nos indica que el módulo GSM está listo
boolean gsmIsReady = false;

// en loop(): Cambiar "if(mySerial.available())"  por:
  if (mySerial.available()) {
    chr = mySerial.read();
    Serial.write(chr);
    if(!((char)chr=='\r' || (char)chr=='\n')) {
      appendToBuffer((char)chr);
    } else { // PROCESS BUFFER
      if(strstr(buffer,"Call Ready")!=NULL) {
        gsmIsReady = true; // cambia a true cuando el módulo está activo
        Serial.print("ARDUINO> GSM is Ready - Waiting for interrupt\r\n");
      }
    clearBuffer();
    }
  }

Enviando un SMS cuando se reciba una interrupción


Una función interesante de este módulo y que resulta sumamente sencilla con este módulo es la de enviar SMS. Supongamos por ejemplo que tenemos un dispositivo que se activa o desactiva y queremos recibir un SMS cada vez que el dispositivo se desactive o se vuelva a activar.

Primero vamos a utilizar la línea 7 (asociaremos la interrupción 4 en Leonardo) para detectar cuando la línea pase de un 1 lógico a cero.

Para ello primero configuramos la línea 7 como INPUT_PULLUP en el setup:

  pinMode(7,INPUT_PULLUP);

Luego asociamos la interrupción correspondiente (Nótese que la interrupción 4 corresponde al pin 7 en Leonardo):
  attachInterrupt(4, pin7OnFalling, FALLING);

Al igual que como hicimos para apagar el módulo GSM vamos a guardar una variable que nos servirá como "bandera" indicadora de que debemos enviar un SMS.
volatile boolean sendMessage = false;
void pin7OnFalling() {
  if((millis()-lastInt)>500) {
    sendMessage = true;
    lastInt = millis();
  }
}

En nuestro Loop principal realizamos la llamada a la función del envío del mensaje:
  if(sendMessage && gsmIsReady) {
    Serial.print("SMS Interrupt received\r\n");
    sendSMS();
    sendMessage = false;
  }

El código de la función SMS es sumamente sencilla, vamos a utilizar el comando AT+CMGS:
void sendSMS() {
  pushSlow("AT+CMGF=1\r\n");
  pushSlow("AT+CMGS=\"+50312345678\"\r\n");
  pushSlow("DISPOSITIVO 1 APAGADO\x1A");
}

¡Y listo! ahora cada vez que enviemos la línea 7 a tierra un SMS será entregado al número de teléfono que especifiquemos con CMGS.

Código fuente


Puedes descargar el archivo .ino con todo el código ya integrado desde la siguiente dirección:

https://github.com/mxgxw/arduino/blob/master/SMSSender/SMSSender.ino

Finalizando esta primera parte


Vamos a hacer una pausa, tomar un descanso y tomarnos una soda. Con la configuración mostrada en esta entrada podrán enviar comandos AT directamente a su módulo SIM900.

La próxima entrada tratará sobre el envío de peticiones HTTP a través del módulo, nos interesa mucho la segunda ya que muchos consideran esto algo así como "magia negra", pero con el SIM900 no es algo demasiado complicado y esperamos resulte sencillo de entender.

Aun habíendo mucho más que decir... ¡Hasta la próxima!

Fuentes

Lista de materiales utilizados: