Vamos a mostrar cómo implementar un sistema de comunicación básico utilizando el protocolo de mensajería MQTT, entre un Arduino y un bróker sobre PC.

Un Arduino es una plataforma de hardware libre, basada en una placa con un microcontrolador, podeis encontrar detalles en algunos post (https://unpocodejava.wordpress.com/2013/03/05/guia-de-compra-de-arduino-versiones-de-placas-arduino-parte-1-de-2/)

Las operaciones propuestas son el envío de mensajes desde el Arduino hasta el PC, posibilitar el tratamiento lógico de las señales mediante JAVA y envío de respuesta asíncrona hacia el Arduino.

Para el desarrollo realizaremos de una prueba de concepto con un cliente Java que usa las librerías fusesource mqtt-client, un bróker MQTT Moquette y un Arduino Mega:

Arduino enviando señales de un sensor de luz al bróker (pantalla al fondo) Cuando la señal del sensor decae, la lógica del bróker devuelve al arduino una señal para encender el led


LA CONFIGURACIÓN

1 Configuración de librerías Arduino

Lo primero será descargar la librería PubSubClient para arduino. Esta librería es un cliente que permite realizar operaciones simples de publicación/subscripción de cliente sobre un servidor MQTT. Podemos encontrarla en:

http://knolleary.net/arduino-client-for-mqtt/

Descomprimir el zip y arrastrar la carpeta PubSubClient sobre el directorio el IDE de desarrollo arduino en la carpeta sketcheslibraries

Si la librería se importa correctamente aparecerá en la lista de librerías al abrir el IDE

Nota: Las librerías guardan ficheros fuente .h y .cpp que son compilados cada vez que se carga el script sobre el arduino. De este modo sería sencillo retocar/ampliar las librerías editando el código sobre la carpeta sketch.

2 Instalación Shield Ethernet del Arduino

Montar el shield (extensión) ethernet pinchando sobre los pines. Los shields son interfaces de comunicación con el exterior, éste en concreto ofrece una toma de red RJ45 y permite la conexión con internet.

Shield Ethernet

Conectar la potencia y un cable conectado a una red visible desde bróker PC. Se recomienda para las pruebas conectar la red del arduino y del bróker a un router sirviendo ips conocidas mediante DHCP.

3 Montaje de la Prueba Arduino

Para poder leer y recibir señales desde el dispositivo físico, se va a montar un led y un sensor fotovoltaico sobre la placa de prototipado. El objetivo es leer señales del sensor fotovoltaico, procesarlas desde el bróker, y devolver una señal de encendido o apagado del led en función a la intensidad de la luz (baja intensidad, encendido).

Se necesita

  • Un dispositivo Arduino (Mega en el ejemplo)
  • Shield Ethernet
  • Celula Fotovoltaica
  • 2 Resistencias (10k, 330 ohm)
  • LED
  • Cables Celula Fotovoltaica y resistencia 330ohm

Realizar el montaje como en el diagrama siguiente.



LA IMPLEMENTACIÓN

1 Software de la Prueba Arduino

Hay cuatro tareas principales que debe realizar el software del Arduino:

  • Lectura periódica del sensor de luz
  • Publicación del dato del sensor via MQTT
  • Escuchar directivas via MQTT
  • Control del LED mediante las directivas-

Los scripts que se cargan en arduino implementan dos funciones:

  • Setup, que se dispara cada vez que se resetea o inicia el dispositivo. En esta prueba el setup configura la conexión del arduino como cliente a un bróker MQTT, además del callback que procesa los mensajes entrantes del bróker.
  • Loop, función iterada indefinidamente. A cada iteración sucede lo siguiente:
    1 El cliente MQTT se conecta (si no lo ha hecho ya).
    2 Lectura de la intensidad lumínica recibida por el sensor.
    3 Publicación de la lectura mediante el cliente MQTT.

Cuando se realiza la conexión con el bróker MQTT se especifica de una función callback para los mensajes entrantes en un tópico de suscripción. En esta función procesaremos la respuesta del bróker que actuará encendiendo o apagando el LED.

Las señales entrantes del bróker serán procesadas “asíncronamente” por el cliente PubSubCliente. Como el arduino no es multihilo no hay problemas de sincronización.

#include 
#include 
#include 
/*
 * A simple MQTT demo for Arduino.
 */
PubSubClient client;

// LED
int ledPin = 0;
// Sensor Luz A
int sensorPinIn = 0;

byte mac[] = { 0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x01 }; // Mac Ip, no necesaria si DHCP
IPAddress ip( 192, 168, 2, 3 ); // Mi Ip
byte serverIp[] = { 192, 168, 2, 2 }; // Server Ip

unsigned long time;
char message_buff[ 100 ];

void setup() {
  // inicializar pin led como OUTPUT.
  pinMode( ledPin, OUTPUT );
  // inicializar consola de debug en Puerto 9600
  Serial.begin( 9600 );
  Serial.println();
  Serial.println( "Start mqtClient" );

  // Configurar conexion a red
  if ( Ethernet.begin( mac ) == 0 ) {
      Serial.println( "Failed to configure Ethernet using DHCP" );
    	// if DHCP fails, start with a hard-coded address:
      Ethernet.begin( mac, ip );
  }
  Serial.println( "connect to server..." );  
  client = PubSubClient( serverIp, 1883, callback, ethClient );

  Serial.println( "Setup ended" );
}

void loop() {
  // Bloque de reconexion del cliente
  if ( !client.connected() ) {
    Serial.println( "try connect client ..." );
    client.connect( "arduino-mqtt" );
    Serial.println( "auth ok" );
    // Suscribir cliente al canal ArduinoTopico
    client.subscribe( "ArduinoReceiveTopic" );
  }

  // leer sensor de luz (photocell)
  int lightRead = analogRead( sensorPinIn );

  // publicar lectura cada 1 segundo
  if ( millis() > ( time + 1000 ) ) {
    time = millis();
    // Componer mensaje json
    //String pubString = "{\"report\":{\"light\": \"" + String( lightRead ) + "\"}}";
    String pubString = String( lightRead );

    pubString.toCharArray( message_buff, pubString.length() + 1 );
    // publicar al canal Send Topico
    client.publish( "ArduinoSendTopic ", message_buff );
  }

  // procesado de loop ordinario del cliente MQTT 
  client.loop();
}

// recepcion del mensaje del topico suscrito
void callback( char* topic, byte* payload, unsigned int length ) {
  int i = 0;
  // creacion del buffer de recepcion
  // Quito los 2 primeros bytes, Bienen mal, ¿caracteres de longitud?
  for( i = 2; i < length; i++ ) {
    message_buff[ i - 2 ] = payload[ i ];
  }
  message_buff[ i - 2 ] = '';

  String msgString = String( message_buff );
  Serial.println( msgString );

  // Procesar el mensaje. Encendido o apagado del LED
  if ( msgString.equals( "OFF" )) {
    analogWrite( ledPin, LOW );
  }
  else if ( msgString.equals( "ON" )) {{
    analogWrite( ledPin, HIGH );
  }
}

2 Instalación del Servidor MQTT

Sobre el PC montaremos un bróker MQTT que haga las funciones de canalización de eventos entre los clientes suscritos. Elegimos por su sencillez el Moquette, un servidor opensource que corre sobre la plataforma Java. Se descarga desde:

https://code.google.com/p/moquette-mqtt/

y se ejecuta desde consola con el siguiente comando:

java -jar /libs/moquette-broker-0.1-jar-with-dependencies.jar

por defecto quedará a la escucha desde el Puerto 1883.

En el ejemplo anterior, los mensajes salientes del arduino se enrutan sobre el bróker sobre el topico ArduinoSendTopic, los mensajes entrantes se enrutan en el bróker bajo el tópico ArduinoReceiveTopic.

3 Procesado de Mensajes MQTT

En esta prueba se utilizará java como lenguaje para codificar un cliente que haga la parte lógica de procesado de mensajes. Usaremos la librería cliente MQTT de fusesource que podemos encontrar en https://github.com/fusesource/mqtt-client

Tendremos que crear un proyecto eclipse MQTT-Client para crear el software del cliente controlador. Los pasos podemos encontrarlos en este post:

https://unpocodejava.wordpress.com/2013/02/27/un-poco-de-mqtt/

Creación de un cliente java con emisión/recepción de mensajes de subscripción.

Este ejemplo hace el envío de un mensaje al topico ArduinoReceiveTopic del bróker MQTT. Dado que el arduino está suscrito a este tópico, recibirá el array de caracteres “ON” u “OFF” a través del método callback implementado en el script de arduino descrito anteriormente:

public void setup() {
// definir un cliente mqtt
MQTT mqtt = new MQTT();
mqtt.setHost( "tcp://127.0.0.1:1883" );
}

public void testJustPublish( String lecturaSensor ) throws Exception {
	// crear conexion con el broker
	BlockingConnection connection = mqtt.blockingConnection();
	connection.connect();
	// publicar al topico el mensaje ON u OFF en fncion a la intensidad de la lectura
	if ( Integer.parseInt( lecturaSensor ) < 100 ) ) {
connection.publish( "ArduinoReceiveTopic", 
"ON".getBytes(), QoS.AT_LEAST_ONCE, false );
	}
	else {
connection.publish( "ArduinoReceiveTopic", 
"OFF".getBytes(), QoS.AT_LEAST_ONCE, false );
	}
	connection.disconnect();
}

Este ejemplo realiza la recepción de mensajes suscribiéndose al tópico ArduinoSendTopic. El canal despachará al cliente en Java (a través del bróker Moquette) los mensajes del sensor de luz, que generarán una respuesta en función a su intensidad. Esta respuesta invoca una llamada al método testJustPublish anterior cerrando el circuito señal-control-respuesta del Arduino.

public void testInterface() throws Exception {
	setup(); // definer cliente mqtt
	final Promise result = new Promise();
	final CallbackConnection connection = mqtt.callbackConnection();
	// añadir un listener para mensajes entrantes en la conexion
	connection.listener( new Listener(){
		...
		@Override
		public void onFailure( Throwable value ) {
			System.out.println( "Error" );
			result.onFailure( value );
			connection.disconnect( null );
		}
		// metodo que recibe los mensajes 
		@Override
		public void onPublish( UTF8Buffer topic, Buffer payload, Runnable onComplete){
			result.onSuccess( payload );
			System.out.println( 
				"recibido en " + topic + " mensaje: " 
				+ Buffer.ascii( payload ).toString() );
		// respondemos en función a la lectura de entrada 
			testJustPublish( Buffer.ascii( payload ) .toString() );
			onComplete.run();
		}
	} );

	// crear conexión. Requiere implementar un callback para tracear el resultado
	connection.connect( new Callback() {
// Si nos conectamos con exito al servidor 
		@Override
		public void onSuccess(Void arg0) {
			// crear un topic de recepción de mensajes
			Topic[] topics = { 
new Topic( utf8( "ArduinoSendTopic" ), QoS.AT_LEAST_ONCE ) 
};
// suscribirnos a dicho topico
			connection.subscribe( topics, new Callback() {
				@Override
				public void onSuccess( byte[] value ) {
				}
				@Override
				public void onFailure( Throwable value ) {
					result.onFailure( value );
				}
			} );
		}

		@Override
		public void onFailure( Throwable value ) {
			result.onFailure( value );
		}
} );
	// Bloquear la salida del codigo. Se queda “a la escucha”
	System.in.read();
}


LAS REFERENCIAS