martes, 4 de diciembre de 2012

Uso del módulo Enc28J60 con arduino, 2a. parte, control de salidas digitales desde internet



Protocolos de comunicación: SPI
SPI es un protocolo serial síncrono que se utiliza para comunicar un microcontrolador con otro y con periféricos a distancias cortas. Para hacer una conexion SPI siempre habrá un dispositivo maestro (usualmente un microcontrolador) que controlará uno o varios periféricos (esclavos), se utilizan por lo generar 3 lineas de conexión  y una de selección que son:

* SO o MISO (Master In Slave Out). La linea que utiliza el esclavo para enviar datos al maestro
* SI o MOSI (Master
Out Slave In). Datos del maestro al esclavo.
* SCK (Serial
clock). Pulsos de reloj para sincronizar la comunicación
* CS o
Select. Se usa por el master para habilitar o deshabilitar un determinado periférico

Los
microcontroladores atmel incluyen las 4 lineas para usar protocolo SPI las cuales obviamente están presentes en los pines del arduino, para el arduino uno se deben usar: Pin digital 10 para CS, Pin digital 11 para SI, Pin digital 12 para SO y Pin digital 13 para SCK, en el caso del arduino mega se usaría los sig. pines: 50 (MISO), 51 (MOSI), 52 (SCK), and 53 (CS).

La
alimentación del módulo puede  es a 3.3 los cuales son suministrados por el arduino

Protocolos de comunicación: HTTP
Hypertext Transfer Protocol o HTTP (en español protocolo de transferencia de hipertexto) es el protocolo usado en cada transacción de la Web.
Es un protocolo orientado a transacciones y sigue el esquema petición-respuesta entre un cliente y un servidor.
Al cliente que efectúa la petición (un navegador web o un spider) se lo conoce como "user agent" (agente del usuario). Y a la información transmitida se la llama recurso y se la identifica mediante un localizador uniforme de recursos (URL). Los recursos pueden ser archivos, el resultado de la ejecución de un programa, una consulta a una base de datos, en este caso en particular la respuesta será la lectura de un sensor o el resultado de la ejecución de un proceso en Arduino, etc.
El módulo ENC28J60 y su librería desarrollada para el arduino lo convierten en un pequeño servidor WEB capaz de responder a interacciones del usuario desde cualquier navegador.

Librería ENC28J60
El módulo que usaremos está basado en un chip ENC28J60 de Microchip que interactúa con Arduino y le permite la comunicación de datos mediante un puerto Ethernet.
Se integra el controlador de la MAC address, un buffer de 8 KB de transmisión/recepción de de paquetes de doble puerto y una FIFO circular que se gestionan a nivel de hardware, además permite la programación de retransmisión de datos en caso de colisión.
La librería de funciones fue desarrollada por Jean-Claude Wippler del sitio http://jeelabs.org y liberada con licencia creative commons como software libre, a partir de ahí ha sido utilizada por diferentes fabricantes para soportar diferentes productos basados en el mismo controlador.


Antes de iniciar la operación de nuestro módulo debemos llevara acabo la configuración de los parámetros para comunicación en la red ethernet, datos como:
Dirección IP
Máscara de red
Puerta de enlace
Tamaño del buffer de datos.
También es factible operar con una dirección IP Dinámica, con lo que sólo será necesario llamara a la función DHCP para solicitarla al gateway de la red.

Las principales funciones incluidas en la librería son:
ether.begin : Crea un objeto para interactuar con el controlador
ether.packetReceive(); Devuelve la cantidad de paquetes pendientes de recibir que estan almacenados en el buffer.
ether.packetLoop(len); Da entrada todos los paquetes almacenados en el buffer, len es la cantidad de paquetes que se van a recibir;
ether.httpServerReply(homePage()); Responde al cliente que realizo la petición, homePage es una variable que apunta a un string o cadena de texto con formato HTML con una respuesta válida para el cliente.

El siguiente programa controla el encendido y apagado de un led mediante una pagina web que se accede desde la direccion ip del modulo enc-28j60 conectado al arduino, el diagrama de conexion del modulo con el arduino es el que ya vimos en la primera parte de este tutorial mas un led conectado en el pin digital 2 que sera el que controlemos via red:

// Arduino demo sketch for testing ethernet
// 2012-09-20 lmdiaz@tecnotinker.com

// This sketch is derived from RF12eth.pde:
// May 2010, Andras Tucsni, http://opensource.org/licenses/mit-license.php

#include <EtherCard.h>

#define DEBUG   0   // set to 1 to display free RAM on web page
#define SERIAL  0   // set to 1 to show incoming requests on serial port

// buffer for an outgoing data packet
static char outCount = -1;


// configuration, as stored in EEPROM
struct Config {
    byte band;
    byte group;
    byte collect;
    word refresh;
    byte valid; // keep this as last byte
} config;


#define NUM_MESSAGES  10    // Number of messages saved in history
#define MESSAGE_TRUNC 15    // Truncate message payload to reduce memory use

static BufferFiller bfill;  // used as cursor while filling the buffer

static byte history_rcvd[NUM_MESSAGES][MESSAGE_TRUNC+1]; //history record
static byte history_len[NUM_MESSAGES]; // # of RF12 messages+header in history
static byte next_msg;       // pointer to next rf12rcvd line
static word msgs_rcvd;      // total number of lines received modulo 10,000
byte ledstatus = 0;
byte Ethernet::buffer[1000];   // tcp/ip send and receive buffer

// ethernet interface mac address
static byte mymac[] = { 0x74,0x69,0x69,0x2D,0x30,0x31 };
// ethernet interface ip address
static byte myip[] = { 192,168,1,188 };
// gateway ip address
static byte gwip[] = { 192,168,1,1 };

#if DEBUG
static int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}
#endif

void setup(){
#if SERIAL
    Serial.begin(57600);
    Serial.println("\n[etherNode]");
#endif

    
    if (ether.begin(sizeof Ethernet::buffer, mymac) == 0) 
      Serial.println( "Failed to access Ethernet controller");
    ether.staticSetup(myip, gwip);
  
#if SERIAL
    ether.printIp("IP: ", ether.myip);
#endif
}

char okHeader[] PROGMEM = 
    "HTTP/1.0 200 OK\r\n"
    "Content-Type: text/html\r\n"
    "Pragma: no-cache\r\n"
;

static void homePage(BufferFiller& buf, byte led) {
    
    buf.emit_p(PSTR("$F\r\n"
        "<meta http-equiv='refresh' content='$D'/>"
        "<title>Controlar un led desde internet</title>" 
        "<body><p>Estatus del led: $D</br>"
        "<a href='a'>Apagar</a> </br>"
        "<a href='e'>Encender</a><br></p>"), okHeader, 1, led);

    // Cronometrar el tiempo de ejecucion del programa
    long t = millis() / 1000;
    word h = t / 3600;
    byte m = (t / 60) % 60;
    byte s = t % 60;
    buf.emit_p(PSTR(
        "Tiempo corriendo $D$D:$D$D:$D$D</body>"), h/10, h%10, m/10, m%10, s/10, s%10);
#if DEBUG
    buf.emit_p(PSTR(" ($D bytes free)"), freeRam());
#endif
}

void loop(){
    word len = ether.packetReceive();
    word pos = ether.packetLoop(len);
    // check if valid tcp data is received
    if (pos) {
        bfill = ether.tcpOffset();
        char* data = (char *) Ethernet::buffer + pos;
        
        
#if SERIAL
        Serial.println(data);
#endif


        // Pagina sin solicitud
        if (strncmp("GET / ", data, 6) == 0) {
            homePage(bfill, ledstatus);
        }
        else if (strncmp("GET /a", data, 6) == 0) {
            // Recibimos el parametro a (apagar)
            homePage(bfill, ledstatus);//configPage(data, bfill);
            if(ledstatus == HIGH) {
              digitalWrite(2, LOW);
              ledstatus = LOW;
            } 
        }
        else if (strncmp("GET /e", data, 6) == 0) {
            // Recibimos el parametro e (encender)
            homePage(bfill, ledstatus);
            if(ledstatus == LOW) {
              digitalWrite(2, HIGH);
              ledstatus = HIGH;
            } 
        }
        else 
            bfill.emit_p(PSTR(
                "HTTP/1.0 401 Invalido\r\n"
                "Content-Type: text/html\r\n"
                "\r\n"
                "<h1>401 Unauthorized</h1>"));  
                
        ether.httpServerReply(bfill.position()); // send web page data
    }
}