Simplificate 2.0

Si algo parece muy complicado, es que esta mal explicado.

La libreria Ethernet de Arduino esta mal, aqui la tienes corregida.

with 4 comments


Modulo Ethernet Arduino W5100

Así, tal cual, esta mal, me explicare, tengo la necesidad de transmitir los datos de un GPS NMEA, las cadenas de datos (Información de las cadenas de datos NMEA aquí)  por una red Ethernet por lo que primero estuve buscando una opción comercial, un GPS con salida Ethernet, pero no encontré nada, lo más parecido era unos Gateways que convierten tu GPS RS-485 o RS-232 a Ethernet y ademas demasiado caro, entonces recordé que tengo un GPS SirfIII, un Arduino y un modulo Ethernet, me propuse que aunque al final tenga que comprar uno de los caros (ya que es para el trabajo) me iba a fabricar un GPS con salida Ethernet, el funcionamiento iba a ser muy simple.

1° Usar la libreria SoftwareSerial para crear un puerto serie virtual donde conectar mi GPS a 4800 Baudios, esto es relativamente simple, tan solo tenemos que importar la librería he inicializar los datos, con los ejemplos que hay por la red es suficiente.

2° Crear un socket de servidor TCP en un puerto que nos convenga, en mi caso me conviene en el puerto 6164, para esto utilizamos la libreria Ethernet  del entorno de Arduino, básicamente cogí un ejemplo que había y lo modifique.

3° Una vez un cliente conecta con el socket  comenzamos a enviarle los datos GPS sin procesar nada, simplemente uno detras de otro.

Pues bien me queda un sketch  como este:

 

#include <SoftwareSerial.h>
#include <SPI.h>
#include <Ethernet.h>

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };// La dirección MAC a utilizar.
IPAddress serverIP(192,168,1,125);//Dirección IP que queremos para nuestro Arduino.
int serverPort=6164;//Puerto donde escuchar las peticiones.
/*Inicializamos el objeto SoftwareSerial con el pin de Arduino donde vamos a
recibir los datos y el pin por el que vamos a enviar datos al GPS.
Los GPS NMEA emiten datos cada segundo sin necesidada de interrogarlos por lo que
el pin de TX no es necesario cablearlo
*/
SoftwareSerial gps(8,9);// Pin de RX el 8, pín de TX el 9

//Creamos el socket de servidor.
EthernetServer server(serverPort);

void setup()
{
  //El puerto serie para DEBUG
  Serial.begin(9600);
  //Iniciamos el puerto serie por software para el GPS
  gps.begin(4800);
  // Arrancamos el shield ethernet
  Ethernet.begin(mac, serverIP);
  //Iniciamos el socket de servidor.
  server.begin();
  //Ponemos cualquier cosa para saber que hemos pasado de aqui.
  Serial.println("Servidor de GPS Momitel.");
}

void loop()
{
  /*
  Aqui en el ejemplo original decia "esperamos nuevos clientes"
  Pero es falso, el Arduino no se queda bloqueado aqui hasta
  que alguien conecta, la funcion server.available()
  devuelve un onjeto cliente con valor negativo o un numero de
  los 4 socket que el chip wiznet puede soportar*/
  EthernetClient client = server.available();
  //Si el cliente es no valido el status sera 0 y no pasara del if
  //lo que nos devolvera a la linea superior.
  if(client)
  {
    //Si el cliente es valido entraremos aqui.
    //Mientras el cliente permanece conectado seguimos en este bucle.
    while (client.connected())
    {
      //Si hay datos en el puerto serie virtual
      //los enviamos al socket.
      if(gps.available())
      {
        char c = gps.read();// leemos el dato proveniente del puerto serie.
        client.print(c);// lo enviamos al socket.
      }
    }
    // Cuando el cliente no este conectado llegamos aquí.
    delay(10);
    // Cerramos el cliente y volveria arriba.
    client.stop();
  }
}

Como se puede ver no es un codigo muyu extenso pero hace su labor, ahora viene cuando me encuentro con problemas.
El problema es que la funcion de la linea 40 server.available(); nunca devolvía el socket abierto por el PuTTy, para que devolviera algún dato tenia que enviarle un dato primero, básicamente usando PuTTy abría un socket de cliente contra mi servidor.
Picture
Y lo que obtenía era:
Picture2
Ningún dato, nada, el socket estaba abierto porque el PuTTy no se cerraba pero sin embargo el programa se quedaba ahi, eternamente, sin que saliera de ese estado, socket abierto, pero sin transmitir datos, lo que en nuestro programa es que no pasa de la linea 43, entonces al pulsar una tecla y darle a Enter el milagro ocurrió.
Picture3¡Teníamos datos del GPS, el código estaba “relativamente” bien!, la pregunta ahora es ¿por que cuando pulsaba la tecla los datos fluyen y si no la pulsaba los datos no fluyen?, esto me llevo a echarle un ojo a la libreria Ethernet internamente, a la función server.available() que se encuentra en el fichero EthernetServer.cpp, y me encontré con el siguiente código:

<pre>EthernetClient EthernetServer::available()
{
  accept();

  for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
    EthernetClient client(sock);
    if (EthernetClass::_server_port[sock] == _port &&
        (client.status() == SnSR::ESTABLISHED ||
         client.status() == SnSR::CLOSE_WAIT)) {
      if (client.available()) {
        // XXX: don't always pick the lowest numbered socket.
        return client;
      }
    }
  }
  return EthernetClient(MAX_SOCK_NUM);
}

Básicamente esta función como se puede ver recorre los 4 socket que el chip W5100 es capaz de soportar y genera un objeto de tipo cliente con el socket, después compara el estado de este objeto cliente en el if de la linea 7 y si el socket esta establecido pasa el if de la linea 10, y aquí es donde metemos la pata, por que la función client.available() devuelve 1 si hay datos en el buffer de lectura del chip, si no hay datos no devuelve 0 y la funcion NO nos devuelve el socket, es como si no estuviera establecida la comunicación, por eso necesitaba enviarle un carácter o algo al socket.

Una vez corregida la librería queda como esto:

EthernetClient EthernetServer::available()
{
  accept();

  for (int sock = 0; sock < MAX_SOCK_NUM; sock++) {
    EthernetClient client(sock);
    if (EthernetClass::_server_port[sock] == _port &&
        (client.status() == SnSR::ESTABLISHED ||
         client.status() == SnSR::CLOSE_WAIT))
		 {
        // XXX: don't always pick the lowest numbered socket.
        return client;
    }
  }

  return EthernetClient(MAX_SOCK_NUM);
}

Ahora, ya devuelve el socket si esta establecido, no hace falta que tenga datos que leer, y ahora si que funciona de forma correcta.
Por si alguien quiere aquí tiene la librería corregida: Libreria Ethernet para arduino 1.0.5 corregida

 

Written by cuningan

2 junio, 2014 a 18:45

Publicado en Inventos

4 comentarios

Subscribe to comments with RSS.

  1. buen dato, me percaté de este problema haciendo unas pruebas en mi ethernet esta semana, gracias por el aporte, voy a probar tu solución.

    Johnny

    30 agosto, 2016 at 20:10

  2. Excelente aporte, eres un investigador forense! Gracias, muchas gracias por tu investigacion, a mi me va funcionar por que ayuda a explicar como lo hace C++

    Nadie En Absoluto

    11 septiembre, 2015 at 19:45

  3. solo son datos en crudo

    Rodrigo Leal Sandoval

    2 junio, 2014 at 20:11

    • Claro, solo datos crudos, es lo que quería tener, si los quieres procesados puedes usar la librería tinygps

      cuningan

      2 junio, 2014 at 22:26


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: