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 programa
unsigned int conversion =0;
byte muestras =0;
unsigned int M0=0;
unsigned int contador =0;
 
byte bandera_1 = 0;
volatile byte dato = 0;
 
void Leer_Conversor(void){   
do{ 
  M0 += analogRead(A0)>>2;    // Lee el A/D y acumula el dato en M0
  NOP;
  NOP;
  NOP;
  muestras++;            // Incrementa el contador de muestras
}while(muestras <=4);    // Se tomaron  muestras ??
  conversion = M0/5;     // Se busca el promedio de las 5 muestras
  M0 =0;
  muestras =0;
 }
 
//------- Funcion de configuracion del Hardaware ---------- 
 void setup(){ 
  DDRB = 0b00100000;   // Pin 13 (PB5) como salida.
   UCSR0A = 0x00; // Limpia las banderas de la UART
   UBRR0H = (uint8_t)(UBRR_VALUE >> 8); // Configura baudios
   UBRR0L = (uint8_t)UBRR_VALUE;
   UCSR0C |= (1 << UCSZ00) | (1 << USBS0); // Datos en 8-bits con 1 bit de stop
   UCSR0B |= (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);
 
} 
//----------------------------------------
void loop() {
  if (digitalRead(excel) == LOW && bandera_1 == 0) {  
      UDR0 = 0x01;	// Comando para iniciar Excel
      bandera_1 = 1;
  }
  if (digitalRead(hoja) == LOW && bandera_1 == 1) {  
      UDR0 = 0x02;	// Comando para iniciar hoja de trabajo
      bandera_1 = 2;
  }
   contador++;
    if(contador == 10000){
        Leer_Conversor();
        contador = 0;
    }
  } 
 
ISR(USART_RX_vect){  
  
  byte dato = 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
***************************************************************************
**  File Name    : EXCEL.c
**  Descripción  : Envía datos del conversor en 8 bit (RA0) al EXCEL
**                 RA1 Inicia Excel - RA2 Inicia Hoja - RA3 Reset del sistema
**                 Se comunica por la USART a 9600 baudios.
**  Autor        : www.firtec.com.ar
**  Target       : 40PIN PIC18F4620
**  Compilador   : Microchip C18
**  IDE          : Microchip MPLAB IDE
**  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 ***************************
void isr_conversor(void);
void verifica_isr(void);
//******** Variables del Sistema ***********************
volatile float voltaje = 0.00;
volatile unsigned int conversion=0;
volatile unsigned char conversiones=0;
volatile unsigned int M0=0;
volatile char Data;
volatile unsigned char Hi=0;
unsigned char bandera = 0;
#define led   PORTBbits.RB0 // No se usa
 
//************ Configura el vector de interrupción LOW**********************
#pragma code prioridad_nomal = 0X0008
 void ISR(void){
 _asm GOTO verifica_isr _endasm
 }
#pragma code
//****************** Se discrimina el origen de la interrupción*************
#pragma interrupt verifica_isr
void verifica_isr(void){
  if(PIR1bits.ADIF){   // Verifica la bandera de inte. del conversor A/D
     isr_conversor(); // Salta a la rutina correspondiente
  }
if (PIR1bits.RCIF==1){ // Discrimina la inte del receptor
Data=getcUSART(); // Se lee dato recibido
if(Data == 0x01){
bit_clr(6,PIE1);  // Desactiva la INTE del conversor
putcUSART (Hi);  // TX la parte baja de los  bitŽs
bit_set(6,PIE1);
 }
PIR1bits.RCIF=0; 		// Borra bandera de interrupción
 } 
}
 
//**************************************************************************/
 
void main(void){
ADCON1 = 0X0E;  // Configura los pines del PORT A como I/O Digitales
TRISA=0X0F;		// Configura el pin 2,3 y 7 como entradas digitales
TRISBbits.TRISB0=0; // No se usa
PORTA=0;	// Puerto A inicia en cero
lcd_init();	 //Inicializa el LCD
OpenADC(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ón
           USART_RX_INT_ON &	// RX con interrupciónes
           USART_ASYNCH_MODE &	// Modo asincrónico
           USART_EIGHT_BIT &	// Modo alta velocidad
           USART_CONT_RX &		// Recepción continua
           USART_BRGH_HIGH,129);	// 9600 baudios a 20Mhz  
RCONbits.IPEN = 0; // Deshabilitamos prioridades
INTCONbits.PEIE=1; // Habilitamos la interrupcion de perifericos
 
lcd_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 Excel
if(PORTAbits.RA1!= 1 && bandera == 0){ 
lcd_gotoxy(2,1); 
lcd_putrs("Realizado.........");
putcUSART (0x01); // Inicia el Excel
bandera =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.xls
bandera =3;
}
//*********************************************************
if(bandera ==3){
while(1){		// Bucle infinito para medir y efectuar el reset
if(PORTAbits.RA3!= 1 && bandera == 3){ // Botón RA3 reset del sistema
 Nop();
   Nop();
     Reset();
     }
    ConvertADC(); 
   } 
  }  
 }
}
//**************************************************************************
void isr_conversor(void){   
conversion= ReadADC(); 	// Guarda el estado del conversor en conversion
M0 += ReadADC()>>2;	// Pasa a 8 bits
if(49==conversiones++){	// 50 Conversiones
conversion = M0/50;    
Hi=conversion;       	// Salvo el dato del conversor
conversiones=0;         // Limpia registros            
M0=0;					// para la próxima medición
lcd_gotoxy(16,2); 
stdout= _H_USER;		// Necesario para usar printf re-dirigido al LCD
printf("%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"
float Final;
unsigned char excel_bandera =0;
bool bandera = false;
bool Error = false;
bool pagina = false;
bool ok;
AnsiString Dato;
short int a=0;
unsigned char Dato_Recibido;
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
}
//---------------------------------------------------------------------------
 
 
void __fastcall TForm1::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 __fastcall TForm1::PuertoChange(TObject *Sender)
{
Boton1 -> Enabled = true;
PuertoCom->PortNum = Puerto ->ItemIndex + 1;
}
//---------------------------------------------------------------------------
 
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
  switch(excel_bandera){
  case 1: {
    excel_bandera = 2;
  //-------- hace EXCEL visible
        xlApp = CreateOleObject("Excel.Application");
        xlApp.Exec(PropertySet("Visible") << true);
     break;
  }
 case 3:{
 excel_bandera = 4;
//-------- crea acceso al objeto 'libro'/workbook
    xlBooks = xlApp.Exec(PropertyGet("Controlador"));
//-------- Crea un Archivo nuevo
// ... o abre uno existente
     xlBooks.Exec(Procedure("Open") << "\\Controlador.xls");
    break;
 }
 case 4:{
 //-------- Tomo el primer libro de la aplicacion
try{
    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'/worksheets
    xlSheets = xlBook.Exec(PropertyGet("Worksheets"));
 
    //-------- Tomo la primera hoja del libro
    xlSheet = 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 celdas     VRange = xlSheet.Exec(PropertyGet("Range") << "D6");     VRange.Exec(PropertySet("Value") << a);    }  }         } //---------------------------------------------------------------------------   void __fastcall TForm1::PuertoComRxChar(TObject *Sender, int Count) { Byte convertir;         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.