03 noviembre 2013

Arduino y peticiones GET HTTP con el SIM900


En esta entrada voy a tratar de explicar un tema que muchos consideran casi "magia negra" y esa es el realizar peticiones a servidores web con un módulo GSM a través de una conexión GPRS.

El objetivo de esta entrada es sencillo: Enviar datos a un servidor web a través de una petición GET utilizando únicamente los comandos AT del SIM900 y funciones de lectura de datos del puerto serial.

Para esta entrada no vamos a utilizar ningún tipo de bibliotecas extra. La dinámica que seguiremos será sencilla: Primero vamos a realizar una conexión con el módem desde la propia consola de comandos y una vez la logremos hacer funcionar de manera adecuada, vamos a desarrollar un código para Arduino que nos permita realizar esta misma acción de forma programática.

De nuevo advierto que esta entrada podría resultar un poco larga ya que explicaremos los conceptos básicos antes de entrar al detalle de la programación con Arduino. Pero de nuevo, les recomiendo que la lean completa para que tengan una mejor comprensión de como funcionan las conexiones en Internet, el protocolo HTTP y como podemos enviar datos fácilmente desde nuestro dispositivo hacia cualquier prácticamente cualquier servidor en Internet.

Un pequeño video para que se hagan una idea de lo que vamos a lograr hacer al final de esta entrada:

Habiendo dicho lo anterior... ¡Comencemos!

¿Qué es el Internet?


El Internet es la red global de comunicaciones que posee un sin fín de computadores y dispositivos interconectados entre sí a través de diferentes tecnologías de red pero que tienen algo en común: utilizan un protocolo estándar de comunicaciones conocido como TCP/IP.

El TCP/IP es un conjunto de especificaciones de protocolos que deben seguir los dispositivos (a diferentes niveles) para poder comunicarse entre sí.
Podría escribir mucho acerca del protocolo de Internet pero para no aburrirnos mejor les dejo la entrada de la Wikipedia que tiene un bonito resumen al respecto.

Resumiendo el artículo de la Wikipedia: cada dispositivo conectado a Internet utiliza una dirección numérica conocida como dirección IP, para comunicarnos con un dispositivo remoto debemos de conocer el número que ha sido asignado al mismo.

Las direcciones IP se representan como 4 cifras entre 0 y 255 separadas por puntos, por ejemplo "33.24.35.66". Esto debería cambiar gradualmente en el futuro con la implementación del Protocolo de Internet versión 6 en donde las direcciones son considerablemente más largas por lo que se representan utilizando códigos hexadecimales.

En el día a día, rara vez observamos estas direcciones porque mayormente hacemos uso de un servicio de ayuda llamado DNS o en español "Servicio de Nombres de Dominio". Este servicio se encarga de traducir una dirección como la de este blog "fuenteabierta.teubi.co" a la dirección IP correspondiente al servidor donde está alojado.

Esta resolución de nombres se realiza de manera completamente transparente al usuario de tal manera que nadie (en su sano juicio) debería de tener la necesidad de estar memorizando los números de las direcciones IP de los servidores con los que se comunica.

El Internet y las páginas web


Algo que confunde a mucha gente es el hecho de que los sitios web o son Internet. Las páginas web representan solo la parte "visible" de la Internet, existen muchos servicios que se encargan de proporcionar información haciendo uso de diferentes protocolos, las páginas web se transmiten haciendo uso del protocolo de transferencia de hipertexto (Mejor conocido como HTTP por sus siglas en inglés).

Existen muchos otros protocolos que nunca vez has utilizado directamente, por ejemplo tu e-Mail es entregado a tu buzon de correo haciendo uso del protocolo SMTP; Accedes a tu buzón de correo desde tu iPhone haciendo uso de ya sea el protocolo IMAP o POP; Puedes transferir archivos haciendo uso del protocolo FTP o Bittorrent; Y así, existen un sin fin de protocolos para transferir diferentes tipos de datos de distintas formas a través de Internet.

Para resumir digamos que los protocolos son "convenciones" de como se debe transmitir los datos a través de una conexión en Internet.

Tipos de conexiones


A pesar del sin fin de protocolos que existen, podemos decir que todos los protocolos hacen uso mayormente de los siguientes dos tipos de conexiones:

  • Conexiones TCP: Orientadas a la transmisión "continua" de datos a través de flujos o "streams". Esto significa que resultan más útiles cuando queremos mantener una conexión activa o vamos a transmitir datos de manera continua. Poseen la ventaja que el protocolo se encarga de verificar que los datos sean enviados de manera correcta y de no ser así genera un error. El protocolo HTTP utiliza conexiones del tipo TCP.
  • Conexiones UDP: Este tipo de conexiones envía "datagramas". Un datagrama es un pequeño bloque de datos. Son utilizadas cuando no nos interesa enviar información de manera continua y solo queremos transferir pequeños "bloques" de datos. El problema es que el protocolo no garantiza ni siquiera que los datos lleguen a su destino o que estos lleguen en orden, así que debemos de manejar eso directamente desde nuestro software. El protocolo DNS utiliza conexiones del tipo UDP. Hay formatos de video tipo "stream" que utilizan códecs de video especialmente diseñados para funcionar con pérdida de datos, por ello pueden ocupar fácilmente conexiones UDP.

Los números de puerto


Así como las máquinas que se conectan a Internet utilizan una dirección numérica para identificarse entre sí, cada conexión realizada en la computadora utiliza un número de puerto.

Los números de puerto van desde 0 hasta 65,535. El protocolo HTTP utiliza por convención el puerto 80. Así como sucede con el servicio DNS usualmente tu no verás estos números ya que por conveniencia se han definido algunos formatos textuales para acceder a los diferentes servicios.

Por ejemplo al escribir http://fuenteabiera.teubi.co/ realmente queremos decir que nos queremos conectar a la IP 74.125.196.121 al puerto 80.

El protocolo HTTP


Luego haber explicado lo anterior es momento de entrar en calor. El protocolo HTTP es tal vez el protocolo más difundido y más conocido por los usuarios de Internet, a través de el se transmiten las páginas web que observas a través de tu navegador.


El protocolo HTTP tiene la característica de transmitir únicamente "texto" o datos binarios codificados como texto, el formato más utilizado es el HTML o lenguaje de marcado de hipertexto.

¿Qué te parece si te digo que podemos hacer una conexión a un servidor web sin necesidad de abrir un navegador?

Si estás en windows o linux abre una consola de comandos y escribe lo siguiente:

$ telnet dev.teubi.co 80

Una vez aparezca el mensaje (en Linux, una pantalla negra en Windows):

Trying 67.23.250.27...
Connected to dev.teubi.co.
Escape character is '^]'.


Escribe lo siguiente (Presiona la tecla [Enter] cada vez que veas [entrar]):

GET /hola.php HTTP1.1[entrar]
Host: dev.teubi.co[entrar]
[entrar]

Luego de unos segundos verás algo como lo siguiente:

HTTP/1.1 200 OK
Date: Fri, 01 Nov 2013 02:38:24 GMT
Server: Apache/2.2.19 (Unix) mod_ssl/2.2.19 OpenSSL/0.9.8e-fips-rhel5 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635
X-Powered-By: PHP/5.2.17
Transfer-Encoding: chunked
Content-Type: text/plain

37
Bienvenido a tu primera conexion HTTP sin un navegador!
0
Connection closed by foreign host.

¡Listo! Acabas de realizar tu primera conexión HTTP sin necesidad de abrir un navegador.

Examinando la petición (request)


GET /hola.php HTTP1.1

En esta línea estás indicando al servidor web que estás realizando un request del tipo GET. Este es el tipo más común de peticiones y significa que todos la información que requerimos está contenida en la petición. Lo más importante que tienes que tomar en cuenta es que solo colocamos la dirección al recurso y no la dirección completa.

Por ejemplo si queremos hacer una petición a "http://www.algunlugar.com/directorio/paginaweb/contenido.html", luego de "GET" solo debemos colocar "/directorio/paginaweb/contenido.html".

Host: dev.teubi.co[entrar]

Esta línea es conocida como "encabezado de la petición". Los encabezados puedes reconocerlos porque siempre tienen la forma "<nombre encabezado>:<valor>". El encabezado "Host:" es necesario ya que el protocolo HTTP es capaz de servir contenido de diferentes sitios web bajo la misma dirección IP. Así que debemos especificar cual es el sitio web del cual requerimos información.

Examinando la respuesta


HTTP/1.1 200 OK

La primera línea corresponde al "código" de respuesta del servidor web. Cada código tiene un significado diferente puedes encontrar una lista de códigos y sus significados en la Wikipedia.

El Código 200 nos indica que el recurso que solicitamos fue encontrado en el servidor y nos será enviado.

Date: Fri, 01 Nov 2013 02:38:24 GMT
Server: Apache/2.2.19 (Unix) mod_ssl/2.2.19 OpenSSL/0.9.8e-fips-rhel5 mod_auth_passthrough/2.1 mod_bwlimited/1.4 FrontPage/5.0.2.2635
X-Powered-By: PHP/5.2.17
Transfer-Encoding: chunked
Content-Type: text/plain

Al igual que con los encabezados de la petición, los encabezados de la respuesta nos sirven para conocer las características del servidor. En este caso el servidor nos responde con la fecha, información del servidor, información extra, la codificación del contenido y el tipo de contenido. En este caso la página de prueba no responde con HTML sino con texto plano, identificado con el tipo "text/plain".

37
Bienvenido a tu primera conexion HTTP sin un navegador!
0

Las últimas líneas son los datos de la respuesta. El primer número indica el "tamaño" de los datos que el servidor enviará. Luego sigue el contenido y por último un "0" que nos indica el final de los datos.

A pesar de ser un estándar, cada servidor web implementa de maneras diferentes el protocolo HTTP, por eso es que dejamos el trabajo de desarrollar navegadores web a empresas como Mozilla o Google.

Por último abre tu navegador y accede a la siguiente dirección:


Verás que el contenido es el mismo, pero en navegador se encarga de mostrarte únicamente los datos recibidos en vez de toda la información de encabezados y comandos de conexión.
 

Realizando conexiones HTTP con el SIM900


Conectando nuestro Arduino


Primero, antes de continuar, vamos a conectar el módulo al Arduino Micro, de la misma manera que lo hicimos con la entrada de envío de SMS:


En este ejemplo vamos a utilizar la interrupción de la línea 2 para realizar el envío de datos y la interrupción de la línea 3 para enviar la señal de apagado.

Los comandos AT para realizar conexiones


El SIM900 posee ciertos comandos que facilitan enormemente el establecimiento de conexiones TCP o UDP. Obviamente no posee funcionalidad específica para realizar conexiones haciendo uso del protocolo HTTP así que tendremos que hacer algo similar a lo que hicimos con el comando telnet solo que sobre la línea serial.

Para el caso del módulo GSM la conexión GPRS se realiza siguiendo el siguiente algoritmo:

  1. Iniciamos la conexión GPRS (AT+CGATT).
  2. Definimos el APN, usuario y clave a utilizar (AT+CSTT).
  3. Activamos el perfil de datos inalámbrico (AT+CIICR).
  4. Verificamos que se obtenga una dirección IP para el módulo (AT+CIFSR).
  5. Cerramos la conexión cuando no se utilice (AT+CIPSHUT)
  6. Desactivamos la conexión GPRS (AT+CGATT)
Idealmente no debería ser necesario que realicemos 5 y 6 si necesitamos que el equipo esté conectado siempre. Sin embargo en algunas aplicaciones que requiramos bajo consumo es posible que nos interese desactivar el GPRS cuando no tengamos información que enviar. Por ejemplo si es un módulo que debe reportar datos cada seis horas posiblemente obtengamos mayor autonomía de batería activando el GPRS únicamente cuando lo vayamos a enviar información.

Una vez el módulo se ha conectado a la red de datos el envío de datos se realiza mediante el siguiente algoritmo:

  1. Inicializamos una conexión TCP o UDP (AT+CIPSTART)
  2. Enviamos los datos. (AT+CIPSEND)
  3. Leemos los datos de respuesta del servidor.
  4. Cerramos la conexión. (AT+CIPCLOSE)

Algunas funciones de ayuda


Vamos a reutilizar gran parte del código que desarrollamos para el envío de SMS, pero vamos a hacer algunas modificaciones. Primero vamos a crear una función llamada waitForString(), esta función se encargará de leer el puerto serial hasta que reciba cierto comando. Esto es muy útil para crear una especie de "chat" donde enviamos un comando y recibimos una respuesta.

Esta función deberá tener también un "timeout" o tiempo de vencimiento, esto es para que luego de pasado cierto tiempo asumamos que el comando falló y permita que las demas funciones sigan trabajando adecuadamente.

Adicionalmente retornará un valor "verdadero" o "falso". Esto nos servirá para conocer si la respuesta que esperábamos fue encontrada o de no ser así cancelar la ejecución de la secuencia de comandos.
bool waitForString(char* string, long waitForMilliseconds=5000) {
  clearBuffer();
  long startTime = millis();
  boolean found = false;
  Serial.print("Waiting for string: \"");
  Serial.print(string);
  Serial.print("\"\r\n");
  while((millis()-startTime)<waitForMilliseconds) {
    if (mySerial.available()) {
      chr = mySerial.read();
      Serial.write((char)chr);
      appendToBuffer((char)chr);
      if((char)chr=='\n') {
        if(strstr(buffer,string)!=NULL) {
          found = true;
          break;
        } else if(strstr(buffer,"ERROR")!=NULL) {
          found = false;
          break;
        }
        clearBuffer();
      }
    }
    if (Serial.available())
      mySerial.write(Serial.read());
  }
  return found;
}

Vamos a crear otra función similar a la anterior pero que utilizaremos cuando simplemente queremos escuchar datos por cierta cantidad de tiempo pero no nos interesa que datos sean recibidos, la usaremos simplemente cuando nos interese asegurarnos que el "buffer" esté limpio y queremos leer más datos.

void waitForData(int wait_delay) {
  long startTime = millis();
  while((millis()-startTime)<wait_delay) {
    if (mySerial.available())
      Serial.write(mySerial.read());
    if (Serial.available()) {
      mySerial.write(Serial.read());
    }
  }
}

Todas las funciones de ayuda las colocaremos en el archivo "utils.ino", adicionalmente crearemos un archivo "utils.h", este último solo contendrá las definiciones de las funciones para evitar que Arduino genere errores de compilación.

Definiendo variables globales


Es necesario definir algunas variables. Vamos a comenzar definiendo en un archivo aparte "enums.h" un par de enumeradores que nos serán útiles para identificar el estado actual de conexión del módulo.

Archivo "enums.h":
// Enumerador para deteccion de conexion de modulo GSM.
typedef enum {
  OFFLINE,
  ONLINE
} gsmStatus;

// Enumerador para deteccion de conexion GPRS.
typedef enum {
  DISCONNECTED,
  CONNECTED
} gprsStatus;

Al incio de nuestro proyecto de Arduino nos aseguramos de colocar todas estas variables:

#define BUFFSIZE 1024
#include 
#include 
#include "enums.h"
#include "utils.h"

// Buffer de datos serial
char buffer[BUFFSIZE];
int buffSize = 0;
volatile byte chr;

// Eliminador de rebote por software
volatile long lastInt = 0;

// Definimos un SoftSerial donde conectaremos
// el modulo GSM
SoftwareSerial mySerial(10, 11); // RX, TX

// Estas enumeraciones serviran para detectar
// Si el modulo esta conectado y si esta activa
// La red de datos
gsmStatus currentStatus = OFFLINE;
gprsStatus currentGPRS = DISCONNECTED;

// Banderas para las interrupciones
// Estas variables indicaran al loop
// principal que debera de realizar una accion
volatile boolean sendMessage = false;
volatile boolean startShutDown = false;

Código de inicialización


Al igual que el código de inicialización para envío de SMS asociaremos las líneas de interrupción 0 y 1, correspondientes a las líneas 3 y 2 de Arduino, también se inicializará el puerto Serial:

void setup()  
{
  // Abrir el puerto serie
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }
  
  // Inicializar el SoftSerial
  mySerial.begin(9600);

  // Inicializamos la ultima interrupcion para evitar que
  // se activen al encender
  lastInt = millis();
  
  // Asignamos las interrupciones 0 y 1
  pinMode(3,INPUT_PULLUP); // Maneja el apagado del modulo GSM
  attachInterrupt(0, pin3OnFalling, FALLING);
  
  pinMode(2,INPUT_PULLUP); // Maneja la conexion al GPRS y envio de datos
  attachInterrupt(1, pin2OnFalling, FALLING);

  Serial.print("Arduino: Setup finished.\r\n");
}

Manejo de Interrupciones


Como habíamos mencionado en artículos anteriores, lo importante del manejo de interrupciones es que sea rápido. Esto significa que el código únicamente se encargará de establecer una variable bandera que luego procesaremos en el loop principal:

void pin3OnFalling() {
  if((millis()-lastInt)>500) {
    startShutDown = true;
    lastInt = millis();
  }
}

void pin2OnFalling() {
  if((millis()-lastInt)>500) {
    sendMessage = true;
    lastInt = millis();
  }
}

El loop principal


Aquí es donde se realizará toda la acción. La lógica es muy sencilla ya que se sigue el siguiente algoritmo:

  1. Si la variable de estado interna está como OFFLINE, intentará detectar el módulo. Para que el módulo sea detectado simplemente lo encendemos.
  2. Si la variable de estado interna está como ONLINE entonces procesaremos el envío de datos. Si el módulo esta DISCONNECTED entonces intentará conectar el GPRS, caso contrario realizará la petición GET.
  3. La línea de apagado siempre se procesa, originalmente había pensado procesarla solo si el módulo se encontraba ONLINE, pero luego decidí procesarla siempre en caso de que el estado del módulo almacenado se "desincronizara" con el estado real.
void loop() // run over and over
{
  if(currentStatus==OFFLINE) {
    // Si el estatus es OFFLINE intentar detectar
    // el modulo por dos maneras
    Serial.write("Trying to detect SIM900 module\r\n");
    // Esperando a Call ready
    if(waitForString("Call Ready",5000)) {
      Serial.write("GSM Module detected. Current status: ONLINE\r\n");
      currentStatus=ONLINE;
      // O verificando mediante comandos AT
    } else if(checkForSIM900()) {
      Serial.write("GSM Module already connected. Changing status to: ONLINE\r\n");
      currentStatus=ONLINE;
    }
  }
  if(currentStatus==ONLINE) {
    // Si esta ONLINE procesara el comando de la interrupcion
    if(sendMessage) {
      // La primera vez que reciba la interrupcion intentara conectar
      // Este codigo ignorara el envio del mensaje
      if(currentGPRS==DISCONNECTED) {
        Serial.print("GPRS is disconnected. Starting up GPRS data.\r\n");
        Serial.print("Current request is going to be ignored.\r\n");
        // try to SETUP GPRS before connect
        setupGPRS();
        sendMessage = false;
      // Una vez este conectado
      // Intentara realizar la conexion
      } else if(currentGPRS==CONNECTED) {
        Serial.print("DATA Interrupt received\r\n");
        GET();
        sendMessage = false;
      }
    }
    bridge();
  }
  if(startShutDown) {
    Serial.print("Shutdown interrupt received\r\n");
    shutDown();
    startShutDown = false;
    currentStatus = OFFLINE;
    currentGPRS = DISCONNECTED;
  }
}

Ahora revisaremos sección por sección para comprender mejor el funcionamiento del código y su interacción con las funciones del módulo de GSM.

1 - Código de detección del módulo GSM


Aquí es donde podemos observar la gran utilidad de las funciones de ayuda, lo que realizamos es una especie de "chat", enviamos un comando y esperamos por la respuesta. Debido a que los comandos se ejecutan de manera secuencial, esperamos que las respuestas sean todas las adecuadas.

Primero esperamos por "Call Ready" este texto es enviado cuando encendemos por primera vez el módulo SIM900.

 if(waitForString("Call Ready",5000)) {
      Serial.write("GSM Module detected. Current status: ONLINE\r\n");
      currentStatus=ONLINE;
}

Si el módulo ya estuviera encendido la función checkForSIM900() nos ayudará a detectarlo. Esta función ejecuta los siguientes comandos:

AT\r\n
AT+CPIN?\r\n
AT+CFUN?\r\n
AT+CCALR?\r\n

Si toda la secuencia se ejecuta correctamente entonces se marca el módulo como detectado.

boolean checkForSIM900() {
  // AT revisa que el modulo este conectado y responda
  pushSlow("AT\r\n");
  if(!waitForString("OK",5000))
    return false;
  // Chequea que la SIMCARD no requira PIN
  pushSlow("AT+CPIN?\r\n");
  if(!waitForString("+CPIN: READY",5000))
    return false;
  // CFUN verifica que toda la funcionalidad
  // esté disponible
  pushSlow("AT+CFUN?\r\n");
  if(!waitForString("+CFUN: 1",5000))
    return false;
  // CCALR revisa que se puedan
  // realizar llamadas
  pushSlow("AT+CCALR?\r\n");
  if(!waitForString("+CCALR: 1",5000))
    return false;
  return true;
}

2.1 Código de configuración de la conexión GPRS


Este código se encarga de configurar la conexión GPRS. Antes de intentar conectarnos deshabilitamos la conexión actual. Por alguna razón (supongo causado por el proveedor) algunas veces el módulo se conectaba automáticamente y otras no. Así que para garantizar que siempre siguiera la misma secuencia lo más sencillo de realizar era forzar una desconexión y conectarla nuevamente por medio de los siguientes comandos:

AT+CGATT=1\r\n
AT+CSTT="internet.tigo.sv","",""\r\n
AT+CIICR\r\n
AT+CIFSR\r\n

Al igual que la detección del módulo solo si todos los comandos se ejecutan se considera que la conexión ha sido exitosa.

void setupGPRS() {
  // Enviamos [Enter] para limpiar cualquier
  // comando previo
  pushSlow("\r\n");
  waitForData(1000);
  // Desconectamos el GPRS
  // para evitar conflictos.
  shutdownGRPS();
  // Conectamos nuevamente
  pushSlow("AT+CGATT=1\r\n");
  if(!waitForString("OK",10000)) {
    shutdownGRPS();
    return;
  }
  pushSlow("AT+CSTT=\"internet.tigo.sv\",\"\",\"\"\r\n");
  if(!waitForString("OK",10000)) {
    shutdownGRPS();
    return;
  }
  pushSlow("AT+CIICR\r\n"); // Habilitar conexiones Wireless
  if(!waitForString("OK",10000)) {
    shutdownGRPS();
    return;
  }
  pushSlow("AT+CIFSR\r\n"); // Obtener IP local
  waitForData(1000);
  currentGPRS = CONNECTED;
}

En mi caso estoy utilizando un chip de Tigo El Salvador y el APN corresponde a "internet.tigo.sv", si deseas utilizar otro proveedor solo debes averiguar el APN correspondiente y modificarlo en la línea correspondiente.

Si la conexión falla por cualquier motivo, entonces deshabilitamos el GPRS mediante los siguientes comandos:

AT+CIPSHUT\r\n
AT+CGATT=0\r\n

Y el código que maneja la desconexión:

void shutdownGRPS() {
  // Desactiva las conexiones del modulo
  pushSlow("AT+CIPSHUT\r\n");
  waitForData(3000);
  // Desactiva el GPRS
  pushSlow("AT+CGATT=0\r\n");
  waitForData(3000);
  currentGPRS = DISCONNECTED;
}

2.2 Código de petición GET


Una vez el módulo se encuentra conectado a la red de datos y tiene su dirección IP asignada podemos comenzar a realizar conexiones a servidores.

Aquí nos detendremos un momento a revisar cada uno de los comandos que enviamos, comenzamos con establecer una conexión con:

AT+CIPSTART="TCP","dev.teubi.co",80\r\n

Nota: \r\n equivale a [Enter] en C.

El comando anterior equivale a decir "Quiero crear una nueva conexión, de tipo TCP al servidor cuyo nombre es dev.teubi.co al puerto HTTP correspondiente al número 80".

Este comando nos responderá con "CONNECT OK" una vez la conexión haya sido establecida. Como es una conexión "TCP" se mantendrá abierta en tanto nosotros enviemos datos.

Para enviar datos utilizaremos el comando:

AT+CIPSEND\r\n

El modem nos responderá con un "prompt"  ">" indicando que podemos comenzar a escribir nuestros datos.

> GET /hola.php HTTP/1.1\r\n
Host: dev.teubi.co\r\n
\r\n
\x1A

El comando anterior equivale a decir, ahora que estamos conectados al servidor "http://dev.teubi.co/" quiero la pagina web "/hola.php". El código "\x1A" es un carácter especial que le indica a módem que no enviaremos más datos. Una vez los datos hayan sido enviados responderá con "SEND OK".

Automáticamente el servidor cerrará la conexión y nos aparecerá la indicación "CLOSED". Pero enviaremos un AT+CIPCLOSE en caso de que por algun motivo la conexión quedara abierta.

Esto último es importante ya que el stack TCP/IP incluido en el módem solo es capaz de manejar una conexión a la vez.

El código que maneja todo lo anterior se muestra a continuación:

void GET() {
  // Enviamos [Enter] para limpiar cualquier
  // comando previo
  pushSlow("\r\n");
  waitForData(1000);
  pushSlow("AT+CIPSTART=\"TCP\",\"dev.teubi.co\",80\r\n");
  if(!waitForString("CONNECT OK",30000)) {
    return;
  }
  pushSlow("AT+CIPSEND\r\n");
  waitForData(1000);
  pushSlow("GET /hola.php HTTP/1.1\r\n");
  pushSlow("Host: dev.teubi.co\r\n");
  pushSlow("Connection: Keep-Alive\r\n");
  pushSlow("\r\n");
  pushSlow("\x1A");
  if(!waitForString("SEND OK",15000)) {
    return;
  }
  waitForData(15000);
  pushSlow("AT+CIPCLOSE\r\n");
  if(!waitForString("CLOSE OK",5000)) {
    return;
  }
}

Comandos de apagado


El comando de apagado ya lo habíamos revisado antes, solo que para esta versión lo hemos modificado de tal manera que guarda el estado de desconexión para esperar nuevamente a que el módem esté disponible:

void shutDown() {
  // Envia el comando de apagado
  pushSlow("\r\n");
  pushSlow("AT+CPOWD=1\r\n");
}

Un pequeño video


Puede que lo hayas visto al principio pero te dejamos nuevamente el video donde explicábamos en general el funcionamiento del módulo para que lo tengas como referencia.

Código fuente


Puedes descargar el código fuente desde GitHub en la siguiente dirección:


Concluyendo


Si bien la configuración puede parecer complicada, ahora tienes la oportunidad de que tu dispositivo se comunique prácticamente con cualquier servidor de Internet o incluso con otros dispositivos.

El concepto del Internet of things se basa en dispositivos inteligentes que se comunican entre sí para facilitarte la vida. ¿En que proyecto estás pensando ahora que no tienes limitaciones para que tu dispositivo se conecte desde cualquier lugar a cualquier hora?

Fuentes

25 comentarios:

CeSaR dijo...

Realmente Excelente!!!, No había visto un procedimiento tan bien explicado, claro y detallado, muchísimas gracias por compartir tus conocimientos!!!, Saludos desde Chile.

betotraso dijo...

Muy bueno el artículo. Te queria consultar de un problema que tengo, estoy utilizando un SIM900 que lo controlo ingresando a mano los comandos a través del hiperterminal de la PC, cuando utilizo el comando AT+CIPSTART y me conecto a google o a dev.teubi.co no tengo problemas, envio el GET y me responde SEND OK con la respuesta del servidor. Ahora cuando hago lo mismo con mi dominio (el cual esta alojado en un hosting gratuito llamado 000webhost)se conecta, pero al enviar el GET no me devuelve SEND OK, me devuelve CLOSE. Podrá ser un problema de tiempo de envio (timeout de servidor)?. Lo que he visto es que los otros servidores tienen una coneccion keep-alive, mientras que el que uso yo tiene una coneccion close, no se si será eso. Me he comunicado con el servidor a través de un programa en C que ejecuto desde la PC y me devuelve la respuesta sin problemas. El problema es cuando uso el SIM900. Desde ya muchas gracias por cualquier ayuda que me puedan dar. Saludos

betotraso dijo...

Problema solucionado. Tal como pensaba era un problema del servidor.

Alejandro Andres dijo...

Por favor, podrías indicarme cómo conecto los pines 2 y 3 de interrupción? Gracias!

YECID ALEXANDER TORRES FONSECA dijo...

amigo buenos dias, estoy trabajando con un arduino mega y una shield m95 de quectel, y quiero enviar datos por gprs .. soy nuevo en esto, quisiera algo de ayuda

felifain dijo...

Excelente, nos ha ayudado mucho a comprender el uso del Sim 900.
Tenemos un modulo Eledfreaks Efcom pro V1.1 conectado a un Arduino Mega.
El proyecto comenzado como hobby consiste en el desarrollo de una máquina de vending para provisión industrial conectada a un servidor. Si bien partimos de conocimiento cero de Arduino y programación, ahora con un año de trabajo tenemos desarrollado el programa que maneja los motores y genera las tablas de usuarios, consumo y disponibilidad, tablas que tenemos que subir y bajar del servidor (recién estamos epezando a estudiar PHP y Mysql).
Voy a la pregunta concreta:
1- Como profundizar el conocimiento de los comandos AT.
2- En el tutorial el comando GET está limitado por tiempo, ¿hay forma de cerrar la comunicación por algún parámetro de fin de archivo.(en realidad todos los comandos son manejados por tiempo el que nos preocupa es GET).
3- La idea es tratar de desarrollar que la comunicación sea opcional wifi y si no hay, que use el módulo Gprs.¿ podrás orientarnos donde estudiar esto?

Fernando Ortega dijo...

Hola, gracias por tu trabajo, pero no consigo que el envio GET me devuelva toda la informacion requerida, y ya he aumentado el buffer.

Sabes que podría ser ?

Al llegar SEND OK deja de recibir la respueste del servidor

Gracias.

wilfredo martel dijo...

Podrías decirme como obtener el APN y la clave?
Es esto restringido por los proveedores de telefonía?

logica_Razon@hotmail.com

Alejandro Andres dijo...

Hola Wilfredo, el APN o nombre de punto de acceso es diferente para cada prestadora de telefonia. En Argentina por ejemplo, para Movistar es "internet.gprs.com.ar" y no es necesario poner clave y usuario (puedes dejarlos en blanco). Para Claro es "igprs.claro.com.ar" y tambien funciona sin clave. No es informacion restringida, puedes encontrarla en internet. Saludos

christian a. alatorre dijo...

Hola buen Día, unos compañeros y yo estamos haciendo un proyecto para la universidad y la verdad estamos algo atorados, alguien podria ayudarme, por favor? mi correo es christianalatorre@outlook.es
agradeceria mucho la ayuda y compartiria cualquier documentación, I <3 OPEN SOURCE!!

gracias.

pedro palmero dijo...

Genial y muchísimas gracias, en otros tutoriales (hay muy poco por internet) después de "TCP","miweb.com",Puerto, el puerto lo ponen entre comillas y me fallaba, gracias al tuyo va perfecto.

David Orzanco Perello dijo...

Hola,

En primer lugar enhorabuena por el artículo, está muy bien explicado.
Aquí hemos visto como solicitar información vía TCP desde la SIM900 a un servidor, con el comando GET. ¿Sería posible hacerlo de forma inversa? Es decir, hacer una petición desde un terminal móvil para obtener los datos de un sensor (ejemplo: DameTemperatura()), o incluso pedirle desde un terminal móvil "Activar Pin3".

Un saludo.

Pedro Montes dijo...

En primer lugar felicidades por el artículo.
Hemos realizado una instalación de una farola autónoma en Auckland (Nueva Zelanda) y tenemos un problema de comunicaciones, explico a continuación:
En la instalación autónoma de la farola de Auckland, utilizamos un módem Quectel M95 con una tarjeta de vodafone de Nueva Zelanda, el módem funciona bien en estos pasos:
1- Prueba de conexión GPRS y GSM: OK
2- Tome la hora local y cambiarlo: OK
3 - Obtener una dirección IP: OK
Pero cuando se está esperando la respuesta de ping, no recibe respuesta.
Necesitamos enviar un pequeño archivo (1,2 kb aprox) a un servidor FTP cada media hora, pero el módem se queda en espera de recibir respuesta abriendo el FTP.

Por otro lado, y en el caso del sistema vandálico, el módem envía un SMS al teléfono designado por el cliente; el SMS se envía pero el teléfono receptor recibe un SMS vacío, sin el texto definido como alarma en el módem.

Hemos probado el módem y funciona bien en España, y el fabricante del módem dice que el protocolo de comunicación está bien, ya no sabemos que hacer....

Creemos que es posible fallo dependiendo de los diferentes APN o restricción alguna con el servicio de vodafone.

Nuestro socio NZ nos envió una tarjeta de NZ SIM, hemos probado muchos APN, los siguientes APN funciona bien en España con la tarjeta de NZ (con roaming)
www.vodafone.net.nz
vodafone
Internet
... pero en Nueva Zelanda no funciona ninguno de estos APN, incluyendo: live.vodafone.com
Agradecería un poco de luz al respecto.... GRACIAS Y SALUDOS

Javier Salgado dijo...

Primero que todo muy buena informacion y bien explicada, gracias por compartir.
Tengo un pequeño problema... Estoy usando el Arduino Mega y el Shield M95 de quectel (que se pone encima del arduino).. tengo entendido que igual el sim900 usa comandos AT, ya he realizado un par de pruebas distintas como envio de SMS y ha funcionado bien (el unico problema es que tengo que alimentar con una fuente externa) he revisado tu codigo y lo he cambiado un poco para probarlo en mi M95 pero al correrlo e intentar encerder el modulo este no da respuesta. No se si es por que no funcionan los comandos que estoy usando o se deba a otro problema, espero que me puedas ayudar a solucionar mi problema y muchas gracias.

<=ChItOx=> dijo...

Amigo muy buen blog, pero sabes yo uso estoy utilizando el shield http://www.elecfreaks.com/wiki/index.php?title=EFCom_GPRS/GSM_Shield pero no logro conectarlo a internet, sabes yo solo quiero mandar unas coordenadas de gps a una pagina tambien utilizo una tarjeta arduino mega.. necesito ayuda...

Cristian dijo...

Hola Mario! me podrias proporcionar tu correo? gracias.

Israel Alvarado dijo...

hola puede decirme cual es el codigo html del lado del servidor por favor gracias

Parapente Junín dijo...

Gracias, excelentísimo tutorial!

Gaby Carrizal dijo...

hola me gusto mucho tu video :) si quisiera conectar mi gsm y arduino a una aplicacion de android como seria el procedimiento?

Isaac Dew dijo...

Buenas noches, muy interesante el articulo, una duda, se puede realizar la conexion entre dos modulos sim900 mediante gprs?
si es asi, como lo hago por favor
Muchas gracias

alicia julio dijo...

Quiero cortar una historia de cambio de vida con todos los que se preocupan por leer este testimonio. Las tarjetas en blanco del atm son reales y son eficaces por todo el mundo. Mi nombre es ALICIA JULIO, vivo en ESPAÑA. Recibí esta carta de [MR OSCAR] hace un mes. Esta tarjeta realmente me ha ayudado a pagar mis débitos y ahora estoy libre de todos los problemas financieros. Yo no esto es difícil de creer, pero nunca nuevo no había este tipo de tarjeta hasta que tengo uno. Esta tarjeta de retirar más de € 6000 al día y es muy fácil de usar. Pero usted tiene que ser muy cuidadoso en otro para no ser cogido por la policía porque es ilegal. Si desea obtener más información sobre esta tarjeta y cómo obtener una sólo póngase en contacto con los hackers por esta dirección oscarwhitehackersworld@gmail.com

don peter dijo...

Tengo mi tarjeta de cajero ya programada y en blanco para
Retirar el máximo de 5.000 dólares diarios por un máximo de 30
Días vía (wesleymarkhackers@gmail.com).
Estoy muy feliz por esto porque tengo la mía la semana pasada y la he usado para obtener $ 150,000. Wesley marca Hackers está dando
La tarjeta sólo para ayudar a los pobres y necesitados, aunque es ilegal, pero
Es algo agradable y no es como otro estafa fingiendo
Para tener las tarjetas de cajero automático en blanco. Y nadie es atrapado cuando
Utilizando la tarjeta. ¡Consiga el suyo de los hackers de la marca de wesley hoy! Solo envía un correo electrónico
A (wesleymarkhackers@gmail.com)

Clifford Jackson dijo...

Are you suffering financially or do you need an urgent cash to pay your bills? And you want to take the risk of transforming your own life. Try and get your ATM blank card today and be among the lucky one's who are benefiting from this card. This ATM card is set capable of hacking into any ATM machines anywhere in the world. I have to know about this blank ATM card when I was looking for work online about a month ago. It has really changed my life forever and now I can say I'm rich because I am a living testimony. The less money I get in a day with this card is €20,000. Even now and then keep pumping money into my account. Although the card is illegal but there is no risk of being caught. It is programmed in such a way that it cannot be tracked and also has a technique that makes it impossible for the CCTV camera to detect you when using it. For details on how to get yours today contact cliffordhackerspays@gmail.com

Loan Offer dijo...

Are you looking for a business loan, personal loans, mortgages, car
loans, student loans, debt consolidation loans, unsecured loans, risk
capital, etc. ... You are in the right place
Your loan solutions! I am a private lender who lends
Individuals and businesses at a low interest rate and affordable
Interest rate of 3%. Contact us by email: powerfinance7@gmail.com

LOAN APPLICATION FORM
**********************
Your full name:
Country/State:
Loan Amount:
Duration
Phone:
Monthly income:
Occupation:

Awaiting your swift response.
May Allah bless you.
IBRAHIM MUSA
power Financial Service Pvt.
Contact Us At :powerfinance7@gmail.com
WhatsApp Number +919717357946

wilfredo martel dijo...

Hola Alejandro,
Primero quiero felicitarte por excelente explicación. Y en segundo lugar tengo una duda.
Verás que estoy interesado en comprar un "Modulo Shield Sim808 Gsm Gps Gprs Con Antenas"
y quiero saber si este módulo soporta peticiones http get y post. En otras palabras quiero saber si todo dispositivo que soporte gprs puede hacer peticiones http get y post?
Muchas gracias con anticipación.