JSONP en servidor Arduino - CrossDomain

Uso: Un sitio web alojado en el dominio X quiere obtener datos de un Arduino con Ethernet Shield alojado en el dominio Y.
JSONP es la forma de implementar llamados AJAX Cross Domain:

Referencias:
      http://api.jquery.com/jQuery.ajax/
      http://json-p.org/
      http://www.funcion13.com/2012/04/12/como-realizar-peticiones-ajax-cross-domain-jsonp-jquery/

Test.php:


<html>

<head>
<script type="text/javascript" src="js/jquery-1.8.2.js"></script>
<script type="text/javascript" src="js/funciones.js"></script>
</head>

<body>

<input id="detectar" type="button" value="Detectar...">

</body>

<script>
$(document).ready(function() {

$("#detectar").click(function() {
invocarAjax();
});
});

</script>

</html>

funciones.js


function arduinoEthernetComCallback(data)
{
alert("data es!!: " + data);


var j = eval('(' + data + ')');

alert(j["A5"]);

}

function invocarAjax()
{
rnd = '&random=' + Math.floor(Math.random()*9999999999);

$.ajax({
 url : 'http://192.168.0.200:93?rnd='+rnd,
 dataType : 'jsonp',
 crossDomain : true
});
}

Sketch:

#include <SPI.h>
#include <Ethernet.h>
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
IPAddress ip(192,168,0,200);
char callback[27] = "arduinoEthernetComCallback";
EthernetServer server(93);
void setup()
{
  Ethernet.begin(mac, ip);
  server.begin();
}
void loop()
{
  EthernetClient client = server.available();

  if (client) {
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (c == '\n' && currentLineIsBlank) {
          
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: application/json");
          client.println("Connection: close");     
          client.println();

          client.print(callback);
          client.print("('{");
          for (int i=0; i<6; i++) {
            client.print("\"A");
            client.print(i);
            client.print("\": ");
            client.print(i*10);
            if (i != 5) {
              client.print(",");
            }
          }
          
          client.println("}')");
          break;
        }
        if (c == '\n') {
          currentLineIsBlank = true;
        } 
        else if (c != '\r') {
          currentLineIsBlank = false;
        }
      }
    }

    delay(1);

    client.stop();
  }


Modulos RF 433 con arduino

http://www.pjrc.com/teensy/td_libs_VirtualWire.html

Compra en ebay:
10Pcs 433Mhz RF transmitter and receiver link kit for Arduino/ARM/MCU WL

(Ver especificaciones técnicas al final de este post)

MAC ADDRESS: Ver este link para generarla aleatoriamente:
https://nicegear.co.nz/blog/autogenerated-random-persistent-mac-address-for-arduino-ethernet/

Atencion!!! para mejorar NOTORIAMENTE la señal, agregar una antena en el emisor, un cable multihilo de 20cm de largo.
Para que virtualwire funcione en la version 1.0.1 de Arduiono IDE:

en VirtualWire.h

sustituir:

#include <stdlib.h>
#include <wiring.h>

por esto:

#include <stdlib.h>
#if ARDUINO >= 100
  #include <Arduino.h>
#else
  #include <wiring.h>
#endif

-----------------------------------------------------

En virtualwire.cpp

reemplazar

#include "WProgram.h"
por
#include "Arduino.h"

----------------------------------------------------

Ejemplo de emisor y receptor que funcionan:

Modelo de RF utilizado:

10Pcs 433Mhz RF transmitter and receiver link kit for Arduino/ARM/MCU WL


Cableado:

Se precisan 2 arduinos, uno para el emisor y otro para el receptor.

Emisor (el que tiene tres pines)
      GND -> tierra de arduino
      VCC -> 3V3 de arduino (o 5v)    [Investigar porque a mayor voltaje mayor es la intensidad, hasta 12v]
      ATAD -> digital pin 12 de arduino

Receptor (el que tiene 4 pines)
     GND -> tierra de arduino
     VCC -> 5v de arduino
     Datos (pin que esta al lado de GND) -> digital pin 11 de arduino

Sketch:

(Para probarlo, abrir la consola del arduino receptor)

//---------- TRANSMISOR ------------


#include <VirtualWire.h>

const int led_pin = 11;
const int transmit_pin = 12;
const int receive_pin = 2;
const int transmit_en_pin = 3;

void setup()
{
  // Initialise the IO and ISR
  vw_set_tx_pin(transmit_pin);
  vw_set_rx_pin(receive_pin);
  vw_set_ptt_pin(transmit_en_pin);
  vw_set_ptt_inverted(true); // Required for DR3100
  vw_setup(2000); // Bits per sec
}

byte count = 1;

void loop()
{
  //char msg[7] = {'h','e','l','l','o',' ','#'};
  char msg[7] = {'h','e','l','l','o'};

  msg[6] = count;
  digitalWrite(led_pin, HIGH); // Flash a light to show transmitting
  vw_send((uint8_t *)msg, 7);
  vw_wait_tx(); // Wait until the whole message is gone
  digitalWrite(led_pin, LOW);
  delay(1000);
  count = count + 1;
}




-----------------------------------------------------------------------------------------------

//---------- RECEPTOR ------------
//---------- para tener un feedback visual, poner un led en el pin 2 con una resistencia de 10k
//---------- cada vez que se recibe la palabra "hello", se enciende y apaga el led.


//------- RECEPTOR ---------

#include <VirtualWire.h>

const int led_pin = 6;
const int transmit_pin = 12;
const int receive_pin = 11;
const int transmit_en_pin = 3;

String str = "";
 
void setup()
{  
    delay(1000);
    Serial.begin(9600); // Debugging only
    Serial.println("setup");

    // Initialise the IO and ISR
    vw_set_tx_pin(transmit_pin);
    vw_set_rx_pin(receive_pin);
    vw_set_ptt_pin(transmit_en_pin);
    vw_set_ptt_inverted(true); // Required for DR3100
    vw_setup(2000); // Bits per sec

    vw_rx_start();       // Start the receiver PLL running
}

void loop()
{
    uint8_t buf[VW_MAX_MESSAGE_LEN];
    uint8_t buflen = VW_MAX_MESSAGE_LEN;

    if (vw_get_message(buf, &buflen)) // Non-blocking
    {
int i;

        digitalWrite(led_pin, HIGH); // Flash a light to show received good message
// Message with a good checksum received, print it.
Serial.print("Got: ");

        str = "";
for (i = 0; i < buflen; i++)
{
   Serial.print(buf[i], HEX);
            str += char(buf[i]);
            Serial.print(" ");
}

        Serial.println("");
        Serial.println("Texto recibido: " + str);


        Serial.println("Texto recibido: " + str);

        if (str.indexOf("hello") >= 0)
        {
            digitalWrite(2, HIGH);
            delay(1000);
            digitalWrite(2, LOW);
            delay(1000);
        }


        digitalWrite(led_pin, LOW);
    }
}


-----------------------------------------------------------------------------------------

Especificaciones técnicas


TX Technical Specifications:
A. Working voltage: 3V~12V
B. Working current: max≤40mA (12V), min≤9mA(3V)
C. Resonance mode: sound wave resonance (SAW)
D. Modulation mode: ASK /OOK
E. Working frequency: 315MHz-433.92MHz, customized frequency is available.
F. Transmission power: 25mW (315MHz at 12V)
G. Frequency error: +150kHz (max)
H. Velocity: ≤10Kbps
I. Self-owned codes: negative

RX Technical Specifications:
A. Working voltage: 5.0VDC +0.5V
B. Working current:≤5.5mA (5.0VDC)
C. Working principle: single chip superregeneration receiving
D. Working method: OOK/ASK
E. Working frequency: 315MHz-433.92MHz, customized frequency is available.
F. Bandwidth: 2MHz (315MHz, having result from testing at lowing the sensitivity 3dBm)
G. Sensitivity: excel –100dBm (50Ω)
H. Transmitting velocity: <9.6Kbps (at 315MHz and -95dBm)

Ethernet Shield

Listado de proveedores DDNS:

http://dnslookup.me/dynamic-dns/



Pasos para hacer funcionar el ethernet shield accediendo desde internet:

Hardware:
        Arduino UNO
        Ethernet shield con chip W5100

Pasos:

1. Conectar el ethernet shield al arduino

2. Conectar el cable de red y el usb del arduino al pc

3. Para saber la ip que se le asigno al ethernet shield, abrir el sketch Archivo / Ejemplos / Ethernet / DhcpAddressPrinter

4. Cargarlo al arduino y luego abrir el monitor serial. Va a aparecer algo así:

                   My IP address: 192.168.0.101

Esta dir es la que le dio el DHCP, pero luego en el sketch se puede indicar una IP fija.


5. Cargar este sketch (prueba básica):


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

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDA, 0x02 };
IPAddress ip(192,168,0,200); //<<< Esta es la dir fija que le quiero dar al arduino!!!

// Initialize the Ethernet server library
// with the IP address and port you want to use
// (port 80 is default for HTTP):
EthernetServer server(80);

void setup()
{
  // start the Ethernet connection and the server:
  Ethernet.begin(mac, ip);
  server.begin();
}

void loop()
{
  // listen for incoming clients
  EthernetClient client = server.available();
  if (client) {
    client.println("prueba");
    // give the web browser time to receive the data
    delay(1);
    // close the connection:
    client.stop();
  }
}


6. Para poder acceder fuera de la LAN (desde internet):

      a. Entrar en el router, ir a Forwarding
      b. Agregar uno nuevo de esta forma:

Ojo!!! para que luego se pueda configurar el www.no-ip.com, elegir una ip que esté fuera del rango de DHCP
Para saber cual elegir, entrar a la opción DHCP y fijarse el ultimo numero:

En este caso, podría utilizar la 192.168.0.200, porque la 192.168.0.199 es la máxima dir. que puede asignar el DHCP.

      c. Averiguar la ip pública que tiene el router, ir a http://www.whatismyip.com/

      d. En un pc cualquiera, poner http://la-direccion-publica-del-router

[EL QUE TIENE LA IP PUBLICA ES EL ROUTER, LUEGO HAY QUE REDIRECCIONAR CON EL PORT FORWARDING EL TRAFICO HACIA LOS DISPOSITIVOS - PC - ETHERNET SHIELD, ETC, ETC]

DynDNS

Dado que las ip's son dinámicas y se renuevan, se pueden hacer dos cosas
1. Se configura el servicio de DynDNS en el router.
2. Se agrega código al arduino para que haga lo mismo que hace el router (ver si la ip cambió y si lo hizo, enviar el cambio)

1. Para hacerlo mediante el router:


2. Para hacerlo desde arduino:

Utilizar este código (para proveedor no-ip.com):

NOTA: En este caso se puso: 
                             GET /nic/update?hostname=crusso.no-ip.org HTTP/1.0
Si se pone: 
                             GET /nic/update?hostname=crusso.no-ip.org&myip=176.32.98.166 HTTP/1.0
Se puede redirigir a una página específica. Si no se pone el parámetro myip, se asignará la ip actual del dispositivo (la que se puede ver en whatismyip.com).


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

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //mac address
byte ip[] = { 192,168,0,200 }; //dirección ip del arduino
byte server[] = {8,23,224,120}; //dynupdate.no-ip.com

EthernetClient client;

void setup() {
  Ethernet.begin(mac, ip);
  Serial.begin(9600);

  delay(1000);

  Serial.println("Conectando...");

  if (client.connect(server, 80)) {
    Serial.println("Conexión establecida");

    client.println("GET /nic/update?hostname=crusso.no-ip.org HTTP/1.0");
    client.println("Host: dynupdate.no-ip.com");
    client.println("Authorization: Basic Y2FybG9zLnJ1c3NvQGdtYWlsLmNvbTphYmMxMjM=");   //es el mail:password de no-ip, codificado en base64
                                                                                       //para codificarlo: http://www.ludegljive.com/functions/Encode-string-to-base64.aspx

    client.println("User-Agent: arduino_ethernet/1.0 carlos.russo@gmail.com");

    client.println();
  }
  else
  {
      Serial.println("Error, no se puede conectar");
  }
}

void loop()
{
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }

  if (!client.connected()) {
    Serial.println();
    Serial.println("Desconectado");
    client.stop();

    for(;;)
      ;
  }
}


Utilizar este código (para proveedor dnsdynamic.org):


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

byte mac[] = {  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };  //mac address
byte ip[] = { 192,168,0,200 }; //dirección ip del arduino
byte server[] = {84,45,76,100}; //dnsdynamic.org

EthernetClient client;

void setup() {
  Ethernet.begin(mac, ip);
  Serial.begin(9600);

  delay(1000);

  Serial.println("Conectando...");

  if (client.connect(server, 80)) {
    Serial.println("Conexión establecida");
    //client.println("GET /api/?hostname=crusso.fe100.net&myip=176.32.98.166 HTTP/1.0");
    client.println("GET /api/?hostname=crusso.fe100.net HTTP/1.0");
   
    client.println("Host: dnsdynamic.org");
    client.println("Authorization: Basic Y2FybG9zLnJ1c3NvQGdtYWlsLmNvbTphYmMxMjM=");   //es el mail:password de no-ip, codificado en base64
                                                                                       //para codificarlo: http://www.ludegljive.com/functions/Encode-string-to-base64.aspx

    client.println("User-Agent: arduino_ethernet/1.0 info@gmail.com");

    client.println();
  }
  else
  {
      Serial.println("Error, no se puede conectar");
  }
}

void loop()
{
  if (client.available()) {
    char c = client.read();
    Serial.print(c);
  }

  if (!client.connected()) {
    Serial.println();
    Serial.println("Desconectado");
    client.stop();

    for(;;)
      ;
  }
}

Luego, el resultado es este:







Fuente: http://www.instructables.com/id/Arduino-Ethernet-Shield-Tutorial/