Era el momento del InterSystems hackathon y nuestro equipo, Artem Viznyuk y Eduard Lebedyuk tenian una placa Arduino (one) y varios componentes (de sobra). Así que su curso de acción estaba marcado - como todos los aprendices de Arduino, decidieron construir una estación meteorológica. Voy a aprovechar este artículo para seguir sus pasos y hacer lo mismo que ellos hicieron en 2016, pero con la persistencia de datos en IRIS y la visualización en Business Intelligence (antiguo DeepSee)! (Digamos que además de hacer la traducción del artículo original, aprovecho y nos actualizamos todos ;-) )

Trabajar con dispositivos


  InterSystems IRIS puede trabajar directamente con muchos tipos de dispositivos físicos y lógicos:

Puesto que Arduino utilizar el puerto COM para las comunicaciones, tenemos todo lo que necesitamos.
Generalmente, trabajar con dispositivos puede dividirse en 5 pasos:

  1. Comando OPEN para registrar el dispositivo con el proceso actual y acceder
  2. Comando USE para hacer que sea el primario
  3. Hacer algún trabajo. READ para recibir datos de un dispositivo, y WRITE para enviar datos
  4. USE de nuevo para conmutar el dispositivo primario
  5. Comando CLOSE para liberar el dispositivo

Bien, esa es la teoría, ¿cómo es en la práctica?

Un parpadeo desde InterSystems IRIS


Primeramente, construimos un dispositivo con Arduino que lee un número del puerto COM y enciende un led durante unos milisegundos.

Circuito:
C code (for Arduino)
<span class="hljs-comment">/* Led.ino
 * Receive data on a COM port
 * Connect your led to ledPin
 */</span>
<span class="hljs-comment">// Pin, to connect your led</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> ledpin 8</span>
<span class="hljs-comment">// Received data buffer</span>
String inString = <span class="hljs-string">""</span>;

// Execute once at the beginning
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">setup</span><span class="hljs-params">()</span> </span>{
	Serial.begin(<span class="hljs-number">9600</span>);
	pinMode(ledpin, OUTPUT);
	digitalWrite(ledpin, LOW);
}

// Execute indefinetly
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">loop</span><span class="hljs-params">()</span> </span>{
	<span class="hljs-comment">// Get data from com </span>
<span class="hljs-keyword"><code class="cpp hljs">	while</span> (Serial.available() > ) {
	int inChar = Serial.read();
	if (isDigit(inChar)) {
		// one character at a time
		// and append it to data buffer
		inString += (char)inChar;
	}

	// Encounter new line
	if (inChar == '\n') {
		// Power on the led
		digitalWrite(ledpin, HIGH);
        // be aware: toInt() converts a string that starts with a number to number... 
		int time = inString.toInt();
		delay(time);
		digitalWrite(ledpin, LOW);
		// Flush the buffer
		inString = "";
	}
  }

} </code>


  Y por último un método en InterSystems IRIS que envía el string 1000\n (acabado en nueva línea) al puerto COM:

<span class="hljs-comment">/// Send 1000\n to a com port</span>
<span class="hljs-keyword">ClassMethod</span> SendSerial()
{
	// Pon el puerto que te haya asignado el sistema a tu Arduino
    <span class="hljs-keyword">set</span> port = <span class="hljs-string">"COM3"</span>
	<span class="hljs-keyword">open</span> port:(:::<span class="hljs-string">" 0801n0"</span>:/BAUD=<span class="hljs-number">9600</span>)     <span class="hljs-comment">// Open device</span>
	<span class="hljs-keyword">set</span> old = <span class="hljs-built_in">$IO</span> <span class="hljs-comment">// Record current primary device</span>
	<span class="hljs-keyword">use</span> port  <span class="hljs-comment">// Switch to com port</span>
	<span class="hljs-keyword">write</span> <span class="hljs-built_in">$Char</span>(<span class="hljs-number">10</span>) <span class="hljs-comment">// Send some test data</span>
	<span class="hljs-keyword">hang</span> <span class="hljs-number">1</span>
	<span class="hljs-keyword">write</span> <span class="hljs-number">1000</span> _ <span class="hljs-built_in">$Char</span>(<span class="hljs-number">10</span>) <span class="hljs-comment">// Send 1000\n</span>
	<span class="hljs-keyword">use</span> old <span class="hljs-comment">// Back to old terminal</span>
	<span class="hljs-keyword">close</span> port <span class="hljs-comment">// Free the device</span>
}


«0801n0» es una cadena con parámetros para acceder al puerto COM, tal y como se describe en la documentación (busca portstate cuando estés en la página). Y /BAUD=9600 es, por supuesto, la velocidad de conexión.

Si ejecutamos este método en un terminal:

<span class="hljs-selector-tag">do</span> #<span class="hljs-selector-id">#class</span>(Arduino.Habr)<span class="hljs-selector-class">.SendSerial</span>()


No devolverá nada, pero un led parpadeará por un segundo.

Recepción de datos

Ahora vamos a conectar un keypad a InterSystems IRIS y recibir datos de entrada. Esto podría utilizarse para autenticar a un usuario con autenticación delegada, implementando también la rutina ZAUTHENTICATE.mac.

Circuito:
C code
<span class="hljs-comment">/* Keypadtest.ino * 
 * Uses Keypad library,
 * Connect Keypad to Arduino pins 
 * as specified in rowPins[] and colPins[]. 
 * 
 */</span>
<span class="hljs-comment">// Repository:</span>
<span class="hljs-comment">// https://github.com/Chris--A/Keypad</span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string"><Keypad.h></span></span>
<span class="hljs-keyword">const</span> byte ROWS = <span class="hljs-number">4</span>; <span class="hljs-comment">// Four rows</span>
<span class="hljs-keyword">const</span> byte COLS = <span class="hljs-number">4</span>; <span class="hljs-comment">// Three columns</span>
<span class="hljs-comment">// Map symbols to keys</span>
<span class="hljs-keyword">char</span> keys[ROWS][COLS] = {
	{<span class="hljs-string">'1'</span>,<span class="hljs-string">'2'</span>,<span class="hljs-string">'3'</span>,<span class="hljs-string">'A'</span>},
	{<span class="hljs-string">'4'</span>,<span class="hljs-string">'5'</span>,<span class="hljs-string">'6'</span>,<span class="hljs-string">'B'</span>},
	{<span class="hljs-string">'7'</span>,<span class="hljs-string">'8'</span>,<span class="hljs-string">'9'</span>,<span class="hljs-string">'C'</span>},
	{<span class="hljs-string">'*'</span>,<span class="hljs-string">'0'</span>,<span class="hljs-string">'#'</span>,<span class="hljs-string">'D'</span>}
};
<span class="hljs-comment">// Connect keypad pins 1-8 (up-down) to Arduino pins 11-4: 1->11, 2->10, ... , 8->4 </span>
<span class="hljs-comment">// Connect keypad ROW0, ROW1, ROW2 и ROW3 to this Arduino pins</span>
byte rowPins[ROWS] = { <span class="hljs-number">7</span>, <span class="hljs-number">6</span>, <span class="hljs-number">5</span>, <span class="hljs-number">4</span> };

<span class="hljs-comment">// Connect keypad COL0, COL1 and COL2 <code class="cpp hljs"><span class="hljs-comment">to this Arduino pins</span></span>
byte colPins[COLS] = { 8, 9, 10, 11 }; 

// Keypad initialization
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void setup() {
	Serial.begin(9600); 
}

void loop() {
	char key = kpd.getKey(); // Receive key pressed
	if(key)
	{
		switch (key)
	{
		case '#':
			Serial.println();
		default:
			Serial.print(key);
	}
	}
}

</code>


Y aquí tenemos un método en InterSystems IRIS utilizado para recoger los datos desde un puerto COM, una línea cada vez:

<span class="hljs-comment">/// Receive one line till we encounter line terminator from COM1</span>
<span class="hljs-keyword">ClassMethod</span> ReceiveOneLine() <span class="hljs-keyword">As</span> <span class="hljs-built_in">%String</span>
{
	set port = <span class="hljs-string">"COM3"</span>
	<span class="hljs-keyword">set</span> str=<span class="hljs-string">""</span>
	<span class="hljs-keyword">try</span> {
		<span class="hljs-keyword">open</span> port:(:::<span class="hljs-string">" 0801n0"</span>:/BAUD=<span class="hljs-number">9600</span>)
		<span class="hljs-keyword">set</span> old = <span class="hljs-built_in">$io</span>
		<span class="hljs-keyword">use</span> port
		<span class="hljs-keyword">read</span> str <span class="hljs-comment">// Read <code class="hljs cos"><span class="hljs-comment">till we encounter line terminator</span></span>
		use old
		close port
	} catch ex {
		close port
	}
	return str
}</code>


Lo ejecutamos en un terminal:

<span class="hljs-selector-tag">write</span> #<span class="hljs-selector-id">#class</span>(Arduino.Habr)<span class="hljs-selector-class">.ReceiveOneLine</span>()

Y se quedará esperando recibiendo datos de entrada hasta que se presione # (que sería enviado como un terminador de línea), tras lo cual los datos introducidos se mostrarán en el terminal.

Esto era la I/O básica Arduino-IRIS, y ahora ya estamos preparados para construir nuestra propia estación meteorológica.

Estación Meteorológica

¡Finalmente estamos llegando a la estación meteorológica! Utilizamos un fotoresistor y un sensor de Humedad y Temperatura DHT11 para obtener los datos.

Circuito:

C code
<span class="hljs-comment">/* Meteo.ino * 
 * Register <code class="cpp hljs"><span class="hljs-comment">humidity</span>, temperature and light level 
 * And send them to a COM port 
 * Output sample: H=1.0;T=1.0;LL=1;
 */</span>
// Photoresistor pin (analog)
int lightPin = ;

// DHT-11 pin (digital)
int DHpin = 8; 

// Array to store DHT-11 temporary data
byte dat[5]; 

void setup() {
	Serial.begin(9600); 
	pinMode(DHpin,OUTPUT); 
}
 
void loop() {
	delay(1000); // measure everything once per second
	int lightLevel = analogRead(lightPin); //Get brightness level 
	temp_hum(); // Get temperature and humidity into dat variable
	// And output the result
	Serial.print("H="); 
	Serial.print(dat[], DEC);   
	Serial.print('.'); 
	Serial.print(dat[1],DEC);	
	Serial.print(";T="); 
	Serial.print(dat[2], DEC);	
	Serial.print('.'); 
	Serial.print(dat[3],DEC);	 
	Serial.print(";LL="); 
	Serial.print(lightLevel);
	Serial.println(";");
}

// Get DHT-11 data into dat
void temp_hum() { 
	digitalWrite(DHpin,LOW);
	delay(30);  
	digitalWrite(DHpin,HIGH); 
	delayMicroseconds(40);
	pinMode(DHpin,INPUT);
	while(digitalRead(DHpin) == HIGH);
	delayMicroseconds(80);
	if(digitalRead(DHpin) == LOW); 
	delayMicroseconds(80);
	for(int i=;i<4;i++)
	{
	  dat[i] = read_data();
	}
	pinMode(DHpin,OUTPUT);
	digitalWrite(DHpin,HIGH);
} 

// Get a chunk of data from DHT-11
byte read_data() {
	byte data; 
	for(int i=; i<8; i++) 
	{ 
		if(digitalRead(DHpin) == LOW) 
		{ 
			while(digitalRead(DHpin) == LOW); 
			delayMicroseconds(30);
			if(digitalRead(DHpin) == HIGH) 
			{
				data |= (1<<(7-i));
			}
			while(digitalRead(DHpin) == HIGH); 
		}
	} 
	return data; 
} </code>


Una vez que hemos cargado este código en Arduino, va a comenzar a enviar datos desde el puerto COM con el siguiente formato:

H=34.0;T=24.0;LL=605;


Donde:


Vamos a almacenar estos datos en InterSystems IRIS. Para eso, escribimos una nueva clase Arduino.Info:

 
<span class="hljs-keyword">Class</span> Arduino.Info <span class="hljs-keyword">Extends</span> <span class="hljs-built_in">%Persistent</span>
{

/// Puerto COM asociado - cambialo según tu sistema
<span class="hljs-keyword">Parameter</span> SerialPort <span class="hljs-keyword">As</span> <span class="hljs-built_in">%String</span> = <span class="hljs-string">"com3"</span><span class="hljs-comment">;</span>
<span class="hljs-keyword">Property</span> DateTime <span class="hljs-keyword">As</span> <span class="hljs-built_in">%DateTime</span><span class="hljs-comment">;</span>
<span class="hljs-keyword">Property</span> Temperature <span class="hljs-keyword">As</span> <span class="hljs-built_in">%Double</span><span class="hljs-comment">;</span>
<span class="hljs-keyword">Property</span> Humidity <span class="hljs-keyword">As</span> <span class="hljs-built_in">%Double</span>(MAXVAL = <span class="hljs-number">100</span>, MINVAL = <span class="hljs-number">0</span>)<span class="hljs-comment">;</span>
<span class="hljs-keyword">Property</span> Brightness <span class="hljs-keyword">As</span> <span class="hljs-built_in">%Double</span>(MAXVAL = <span class="hljs-number">100</span>, MINVAL = <span class="hljs-number">0</span>)<span class="hljs-comment">;</span>
<span class="hljs-keyword">Property</span> Volume <span class="hljs-keyword">As</span> <span class="hljs-built_in">%Double</span>(MAXVAL = <span class="hljs-number">100</span>, MINVAL = <span class="hljs-number">0</span>)<span class="hljs-comment">;</span>
<span class="hljs-keyword">ClassMethod</span> AddNew(Temperature = <span class="hljs-number">0</span>, Humidity = <span class="hljs-number">0</span>, Brightness = <span class="hljs-number">0</span>, Volume = <span class="hljs-number">0</span>)
{
	<span class="hljs-keyword">set</span> obj = ..<span class="hljs-built_in">%New</span>()
	<span class="hljs-keyword">set</span> obj.DateTime=<span class="hljs-built_in">$ZDT</span>(<span class="hljs-built_in">$H</span>,<span class="hljs-number">3</span>,<span class="hljs-number">1</span>)
	<span class="hljs-keyword">set</span> obj.Temperature=Temperature
	<span class="hljs-keyword">set</span> obj.Humidity=Humidity
	<span class="hljs-keyword">set</span> obj.Brightness=Brightness/<span class="hljs-number">1023</span>*<span class="hljs-number">100</span>
	<span class="hljs-keyword">set</span> obj.Volume=Volume
	<span class="hljs-keyword">write</span> <span class="hljs-built_in">$SYSTEM</span>.Status.DisplayError(obj.<span class="hljs-built_in">%Save</span>())
}

Despues, escribimos un método que recibirá los datos desde Arduino y los transformará en objetos de tipo Arduino.Info:

/// Receive a RAW data in this format: H=34.0;T=24.0;LL=605;\n
/// Convert into Arduino.Info objects
ClassMethod ReceiveSerial(port = {..#SerialPort})
{
    try {
        open port:(:::" 0801n0":/BAUD=9600)
        set old = $IO
        use port
        for {
            read x //read one line
            if (x '= "") {
                set Humidity = $Piece($Piece(x,";",1),"=",2)
                set Temperature =  $Piece($Piece(x,";",2),"=",2)
                set Brightness =  $Piece($Piece(x,";",3),"=",2)
                do ..AddNew(Temperature,Humidity,Brightness) // Add data
            }
        }
    } catch anyError {
        close port
    }
}


Y finalmente conectamos Arduino y ejecutamos el método ReceiveSerial:

<span class="hljs-selector-tag">write</span> #<span class="hljs-selector-id">#class</span>(<span class="hljs-selector-tag">Arduino</span><span class="hljs-selector-class">.Info</span>)<span class="hljs-selector-class">.ReceiveSerial</span>()

Este método recibirá y almacenrá datos desde Arduino ininterrumpidamente.

Visualización de Datos


Tras construir nuestro dispositivo lo podemos poner fuera por ejemplo, para recoger datos durante una noche:

Al llegar la mañana tendremos miles de registros (en este caso más de 36.000), que podemos visualizar con las herramientas de Business Analytics utilizando por ejemplo herramientas disponibles en Open Exchange como el API REST de servidor MDX2JSON y el generador de cuadros de mando DeepSeeWeb , y aquí tenéis una muestra de los resultados:


Niveles de Luminosidad. En esta serie de datos la salida del Sol es claramente visible alrededor de las 5:50:


Gráficos de temperatura y humedad:



La correlación negativa entre la humedad y la temperatura es claramente visible.

Conclusión


Con InterSystems IRIS puedes comunicar directamente con una gran cantidad de dispositivos diferentes. Puedes desarrollar muy rápidamente tanto tus soluciones para procesamiento de datos como de visualización  - a nuestro equipo le llevó sobre 4 horas construir nuestra propia estación meteorológica, conectarla a IRIS y visualizar los resultados y la mayor parte del tiempo se empleó en el diseño del circuito y en escribir y ajustar el código C.


» Documentation
» GitHub - Repositorio del proyecto Original de Eduard Lebedyuk