Muchas veces nos encontramos con la necesidad de tener datos de nuestra electrónica en una hoja de cálculo y la verdad es que hay varias formas de lograrlo. Podemos hacer uso de alguna macro con Excel pero como seguramente se ha visto, el tema de las macros es bastante resistido por cuestiones de seguridad. El ejemplo que proponemos hace uso de xlApp, este servicio puede crear objetos que interactúen con programas del paquete Office, un pequeño programa escrito en C y sirve como interfaz entre Arduino y Excel, básicamente recibe comandos desde Arduino y los traduce en acciones sobre el sistema Windows. En el ejemplo creamos un objeto para Excel pero podríamos haber hecho lo mismo con cualquier programa Office. La sintaxis de xlApp es bastante simple. xlApp = CreateOleObject("Excel.Application") Esta línea crea un objeto Excel, la aplicación se inicia. xlApp.Exec(PropertySet("Visible") << true)Esta línea hace visible el Excel ya que podría correr en modo no visible, esto es interesante si por ejemplo se quisiera enviar un mail informando un estado pero en modo no visible. (Funciona perfecto!!). Para poner a corre Excel se ha dispuesto un botón en el pin dos de Arduino que al ser presionado envía un comando por el puerto serial que dispara la ejecución de estas dos lineas de código.
Una vez que Excel se encuentra corriendo vamos a abrir una hoja de trabajo dentro del Excel, otro botón conectado en el pin tres de Arduino envía el comando para ejecutar la siguientes lineas de programa. xlBooks = xlApp.Exec(PropertyGet("Controlador"))y xlBooks.Exec(Procedure("Open") << "\\Controlador.xls") La primer línea crea un archivo llamado Controlador.xls o si el archivo existe lo abre para enviarle datos. El siguiente vídeo muestra el funcionamiento de la aplicación con Arduino Nano.
Del lado del microcontrolador las cosas son bastantes simples, cada vez que se oprime uno de los botones lo único que hace Arduino es enviar un número que se usa como comando para ejecutar las funciones xlApp. La ventaja de este servicio Windows es que podemos iniciar programas completos y trabajar con ellos tal como si lo hiciéramos desde la propia computadora. El código para Arduino es el siguiente.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/* ***************************************************************************** File Name : EXCEL.ino** Descripción : Envía datos del conversor en 8 bit (A0) al EXCEL** Pin 2 Inicia Excel - Pin3 Inicia Hoja** Se comunica por la USART a 9600 baudios.** Autor : www.firtec.com.ar** Target : Arduino Nano** ***************************************************************************/#define NOP __asm__ __volatile__ ("nop\n\t") // 62.5ns en 16MHz #define excel 2 // Pin del boton para activar Excel#define hoja 3 // Pin para activar la hoja de trabajo#define F_CPU 16000000#define USART_BAUDRATE 9600#define UBRR_VALUE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)//---- Variables del programaunsignedintconversion=0;bytemuestras=0;unsignedintM0=0;unsignedintcontador=0;bytebandera_1=0;volatilebytedato=0;voidLeer_Conversor(void){do{M0+=analogRead(A0)>>2;// Lee el A/D y acumula el dato en M0NOP;NOP;NOP;muestras++;// Incrementa el contador de muestras}while(muestras<=4);// Se tomaron muestras ??conversion=M0/5;// Se busca el promedio de las 5 muestrasM0=0;muestras=0;}//------- Funcion de configuracion del Hardaware ---------- voidsetup(){DDRB=0b00100000;// Pin 13 (PB5) como salida.UCSR0A=0x00;// Limpia las banderas de la UARTUBRR0H=(uint8_t)(UBRR_VALUE>>8);// Configura baudiosUBRR0L=(uint8_t)UBRR_VALUE;UCSR0C|=(1<<UCSZ00)|(1<<USBS0);// Datos en 8-bits con 1 bit de stopUCSR0B|=(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0);// Activa TX y la RX por interrupción interrupts();pinMode(excel,INPUT_PULLUP);pinMode(hoja,INPUT_PULLUP);pinMode(A0,INPUT);}//----------------------------------------voidloop(){if(digitalRead(excel)==LOW&&bandera_1==0){UDR0=0x01;// Comando para iniciar Excelbandera_1=1;}if(digitalRead(hoja)==LOW&&bandera_1==1){UDR0=0x02;// Comando para iniciar hoja de trabajobandera_1=2;}contador++;if(contador==10000){Leer_Conversor();contador=0;}}ISR(USART_RX_vect){bytedato=UDR0;if(dato==0x01)// Excel pide datos?UDR0=conversion;PORTB^=0b00100000;// Cambia estado del pin 13}//*************** Fin del archivo Firtec Argentina ********************
En este código se puede ver que se usa la interrupción de la UART para recibir la solicitud de datos desde la aplicación interfaz con Excel, la clásica función Serial.available() para recibir datos en Arduino, no funciona correctamente en este ejemplo siendo necesario el uso de una rutina mas eficiente en lo que respecta a los tiempos, por eso se ha usado la configuración de la UART por registros y el servicio de interrupción correspondiente. Como el funcionamiento de xlApp es independiente de la electrónica usada, el mismo ejemplo funciona correctamente para cualquier sistema de microcontroladores. En el siguiente vídeo se pude ver un PIC conectado a un enlace por radio que hace el mismo trabajo con Excel.
El siguiente es el código para el Microcontrolador PIC.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
*****************************************************************************FileName:EXCEL.c**Descripción:Envíadatosdelconversoren8bit(RA0)alEXCEL**RA1IniciaExcel-RA2IniciaHoja-RA3Resetdelsistema**SecomunicaporlaUSARTa9600baudios.**Autor:www.firtec.com.ar**Target:40PINPIC18F4620**Compilador:MicrochipC18**IDE:MicrochipMPLABIDE**XTAL:20MHZ****************************************************************************/#include <p18f4620.h>#pragma config OSC=HS,PWRT=ON,MCLRE=ON,LVP=OFF,WDT=OFF#include <stdio.h>#include "lcd_c18.h"#include <adc.h> #include <usart.h> //*********** Macros************************************#define bit_set(bit,ubicacion) (ubicacion |= 1<<bit)#define bit_clr(bit,ubicacion) (ubicacion &= ~(1<<bit))//******** Funciones Propias ***************************voidisr_conversor(void);voidverifica_isr(void);//******** Variables del Sistema ***********************volatilefloatvoltaje=0.00;volatileunsignedintconversion=0;volatileunsignedcharconversiones=0;volatileunsignedintM0=0;volatilecharData;volatileunsignedcharHi=0;unsignedcharbandera=0;#define led PORTBbits.RB0 // No se usa//************ Configura el vector de interrupción LOW**********************#pragma code prioridad_nomal = 0X0008voidISR(void){_asmGOTOverifica_isr_endasm}#pragma code//****************** Se discrimina el origen de la interrupción*************#pragma interrupt verifica_isrvoidverifica_isr(void){if(PIR1bits.ADIF){// Verifica la bandera de inte. del conversor A/Disr_conversor();// Salta a la rutina correspondiente}if(PIR1bits.RCIF==1){// Discrimina la inte del receptorData=getcUSART();// Se lee dato recibidoif(Data==0x01){bit_clr(6,PIE1);// Desactiva la INTE del conversorputcUSART(Hi);// TX la parte baja de los bitŽsbit_set(6,PIE1);}PIR1bits.RCIF=0;// Borra bandera de interrupción}}//**************************************************************************/voidmain(void){ADCON1=0X0E;// Configura los pines del PORT A como I/O DigitalesTRISA=0X0F;// Configura el pin 2,3 y 7 como entradas digitalesTRISBbits.TRISB0=0;// No se usaPORTA=0;// Puerto A inicia en cerolcd_init();//Inicializa el LCDOpenADC(ADC_FOSC_RC&ADC_RIGHT_JUST&ADC_16_TAD,ADC_CH0&ADC_REF_VDD_VSS&ADC_INT_ON,ADC_1ANA);OpenUSART(USART_TX_INT_OFF&// TX sin interrupciónUSART_RX_INT_ON&// RX con interrupciónesUSART_ASYNCH_MODE&// Modo asincrónicoUSART_EIGHT_BIT&// Modo alta velocidadUSART_CONT_RX&// Recepción continuaUSART_BRGH_HIGH,129);// 9600 baudios a 20Mhz RCONbits.IPEN=0;// Deshabilitamos prioridadesINTCONbits.PEIE=1;// Habilitamos la interrupcion de perifericoslcd_gotoxy(2,1);lcd_putrs("Excel -> Boton_RA1");lcd_gotoxy(2,2);lcd_putrs("Hoja -> Boton_RA2");INTCONbits.GIEH=1;//Habilita las interrupciones, por defecto//el vector es 0x008 (sin prioridad)//******************************************************while(1){// Bucle donde se leen los botones de funciones para Excelif(PORTAbits.RA1!=1&&bandera==0){lcd_gotoxy(2,1);lcd_putrs("Realizado.........");putcUSART(0x01);// Inicia el Excelbandera=1;}if(PORTAbits.RA2!=1&&bandera==1){lcd_gotoxy(2,2);lcd_putrs("Realizado.........");Delay10KTCYx(250);Delay10KTCYx(250);lcd_gotoxy(2,1);lcd_putrs(" EXCEL EN LINEA ");lcd_gotoxy(1,2);lcd_putrs(" Dato Enviado: ");putcUSART(0x02);// Inicia la hoja de calculo Controlador.xlsbandera=3;}//*********************************************************if(bandera==3){while(1){// Bucle infinito para medir y efectuar el resetif(PORTAbits.RA3!=1&&bandera==3){// Botón RA3 reset del sistemaNop();Nop();Reset();}ConvertADC();}}}}//**************************************************************************voidisr_conversor(void){conversion=ReadADC();// Guarda el estado del conversor en conversionM0+=ReadADC()>>2;// Pasa a 8 bitsif(49==conversiones++){// 50 Conversionesconversion=M0/50;Hi=conversion;// Salvo el dato del conversorconversiones=0;// Limpia registros M0=0;// para la próxima mediciónlcd_gotoxy(16,2);stdout=_H_USER;// Necesario para usar printf re-dirigido al LCDprintf("%03u",conversion);// Muestro el valor en LCD }PIR1bits.ADIF=0;// Borra la bandera de Interrupción del conversor A/D}
La aplicación que hace uso de xlApp también fue escrita en C++ y se puede descargar desde este link. Sin embargo el resultado es el mismo si escribimos la aplicación con programas como Visual Basic, C#, etc. El código de la aplicación es el siguiente.
//---------------------------------------------------------------------------#include <vcl.h>#pragma hdrstop#include <Comobj.hpp>#include "Excel.h"//---------------------------------------------------------------------------#pragma resource "*.dfm"floatFinal;unsignedcharexcel_bandera=0;boolbandera=false;boolError=false;boolpagina=false;boolok;AnsiStringDato;shortinta=0;unsignedcharDato_Recibido;TForm1*Form1;//---------------------------------------------------------------------------__fastcallTForm1::TForm1(TComponent*Owner):TForm(Owner){}//---------------------------------------------------------------------------void__fastcallTForm1::Boton1Click(TObject*Sender){if(bandera==false){try{PuertoCom->Open();}catch(...){MessageBox(0,"ERROR!! EL PUERTO YA ESTA ABIERTO O NO EXISTE!!!","Firtec",MB_OK|MB_SYSTEMMODAL);Error=true;}if(Error==false){Boton1->Caption="Cortar";bandera=true;Timer1->Enabled=true;}Error=false;}else{ok=false;Timer1->Enabled=false;Boton1->Caption="Conectar";PuertoCom->PurgeRead();PuertoCom->PurgeReadWrite();PuertoCom->Close();bandera=false;}}//---------------------------------------------------------------------------void__fastcallTForm1::PuertoChange(TObject*Sender){Boton1->Enabled=true;PuertoCom->PortNum=Puerto->ItemIndex+1;}//---------------------------------------------------------------------------void__fastcallTForm1::Timer1Timer(TObject*Sender){switch(excel_bandera){case1:{excel_bandera=2;//-------- hace EXCEL visiblexlApp=CreateOleObject("Excel.Application");xlApp.Exec(PropertySet("Visible")<<true);break;}case3:{excel_bandera=4;//-------- crea acceso al objeto 'libro'/workbookxlBooks=xlApp.Exec(PropertyGet("Controlador"));//-------- Crea un Archivo nuevo// ... o abre uno existentexlBooks.Exec(Procedure("Open")<<"\\Controlador.xls");break;}case4:{//-------- Tomo el primer libro de la aplicaciontry{xlBook=xlBooks.Exec(PropertyGet("Item")<<1);}catch(...){Boton1->Caption="Conectar";PuertoCom->Close();bandera=false;MessageBox(0,"No puedo Conectarme con Controlador.XLS","ERROR",MB_OK|MB_SYSTEMMODAL);Close();Timer1->Enabled=false;Boton1->Caption="Conectar";PuertoCom->PurgeRead();PuertoCom->PurgeReadWrite();PuertoCom->Close();bandera=false;}//-------- crea acceso al objeto 'hoja'/worksheetsxlSheets=xlBook.Exec(PropertyGet("Worksheets"));//-------- Tomo la primera hoja del libroxlSheet=xlSheets.Exec(PropertyGet("Item")<<1); //-------- Trabajando con celdas...if(ok==false){VRange=xlSheet.Exec(PropertyGet("Range")<<"A17");VRange.Exec(PropertySet("Value")<<" www.firtec.com.ar");ok=true;}PuertoCom->WriteChar(1);//Pido los datos al microcontrolador enviando un "1"//-------- Escribe texto en las primeras 3x3 celdasVRange=xlSheet.Exec(PropertyGet("Range")<<"D6");VRange.Exec(PropertySet("Value")<<a);}}}//---------------------------------------------------------------------------void__fastcallTForm1::PuertoComRxChar(TObject*Sender,intCount){Byteconvertir;Dato=PuertoCom->ReadText();convertir=Dato[1];a=convertir;if(excel_bandera==0){if(a==0x01){excel_bandera=1;}}if(excel_bandera==2){if(a==0x02){excel_bandera=3;}}}
Conclusión. Para Excel en particular hay varias opciones para mostrar datos desde una electrónica por ejemplo PLX DAQ sin embargo al usar xlApp no solo tenemos acceso a Excel sino a cualquier programa del paquete Office pudiendo incluso ejecutarlo en formar trasparente al usuario cambiando la propiedad de visibilidad a false.