Postagem em destaque

Controle PID de Potência em Corrente Alternada - Arduino e TRIAC - Parte I

Este post é o primeiro de uma série de seis que escrevi tratando de controle de potência e PID (controle proporcional, integral e derivativo...

domingo, 3 de novembro de 2013

Controle Remoto para PC II - Comandando uma apresentação PowerPoint

Em novembro de 2011 eu fiz um post no meu outro blog, o de "variedades", que tratava do uso de um controle remoto com o Arduino. Como eu ainda estava começando com o Arduino, só mostrei "o que" eu fiz, mas não "o como". Por isso, volto ao assunto agora, para um post "comme il faut" (ui!).

Os aparelhos de controle remoto que a gente usa em casa se comunicam com os equipamentos que controlam através da luz infravermelha. Quando pressionamos uma tecla o controle emite uma série de pulsos (piscadas) correspondentes à tecla pressionada. Um sensor localizado no aparelho enxerga a informação e a converte para pulsos elétricos, que podem ser processados pelo Arduino. É usado o infravermelho e não a luz visível para que o sensor sofra menos influência da luz ambiente.

Para fazermos comunicação infravermelha temos duas alternativas:

1) Usarmos um LED infravermelho e um sensor tipo transistor, que conduz quando recebe um feixe de luz infravermelha. Criamos o nosso próprio protocolo e fazemos tanto a emissão quanto a recepção.

2) Usamos um sensor infravermelho desses que existem nos aparelhos eletrônicos e usamos um controle do mesmo aparelho ou de outro. Eles são razoavelmente compatíveis, então dá para trabalhar fácil, ainda que algumas teclas de um ou outro controle não possam ser identificadas no Arduino. Existem também controles e sensores proprietários, aí não rola.

O nosso projetim é do tipo 2. Como sensor usei um tal de F8483 do qual não achei nenhuma informação na net. Liguei o bicho conforme abaixo, baseado no datasheet de um semelhante:


O objetivo do projeto é controlar uma apresentação PowerPoint no PC, avançando e retornando slides com um controle remoto desses da Net. Para isso, basta:

- Ligar o sensor como visto acima;
- Baixar a lib de controle remoto, daqui;
- Copiar e colar o seguinte código no programa do Arduino;
- Baixar o código .Net C# aqui e compilar, ou baixar somente o executável zipado daqui e usar.

O controle que haqueei para criar esse programa foi esse aqui, muito comum nas nossas casas, usado pela Net.


Vc pode usar outro, claro. O programa permite que vc indique outros códigos, basta vc carregar a aplicação abaixo no Arduino, abrir o Serial Monitor, pressionar os códigos que quiser usar e digitá-los nos campos correspondentes do programa do PC.

Abaixo, o programa do Arduino.

// look for IR codes and print them as they are received

#include "NECIRrcv.h"
#define IRPIN 2    // pino onde o sensor está conectado

NECIRrcv ir(IRPIN) ;

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

void loop()
{
  unsigned long ircode ;
  
  while (ir.available()) 
  {
    ircode = ir.read() ;
    Serial.println(ircode,HEX) ;
  }
}

Esse programa, muito simples, lê o código fornecido pelo sensor e envia para a serial, em formato hexadecimal.

No PC, para que a gente possa comandar uma aplicação PowerPoint, as coisas são um pouquinho mais complicadas. Eu fiz uma aplicação em C# que:

1) Permite ao usuário indicar o arquivo ppt ou pptx que quer apresentar.

2) Permite também indicar o código da tecla que desempenhará cada função de comando da apresentação. Está pré-configurado para esse controle da Net aí de cima. Prometo que na versão 2.0 ele vai salvar a última configuração :);

3) Selecionado o arquivo o programa passa a "escutar" determinada porta serial, também indicada pelo usuário;

4) Uma vez acionado o botão Ok do controle remoto, a aplicação abre o arquivo no modo de apresentação.

5)  Ao receber dois comandos indicados passa a navegar pela apresentação. Vejamos como é feito isso, nesse caso com um bocado mais de código.

Taí a cara do bicho:




Abaixo, o código C#, devidamente comentado.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Office.Interop.PowerPoint;
using System.Timers;

namespace pptRemote
{
    public partial class fMain : Form
    {
        // Serial port to receive information frorm Arduino
        System.IO.Ports.SerialPort sp = new System.IO.Ports.SerialPort();

        public fMain()
        {
            InitializeComponent();
            // Most of the times, Arduino port is the last one available, so we try to gess it.
            string[] ports = System.IO.Ports.SerialPort.GetPortNames();
            edPorta.Text = ports[ports.Length - 1];
        }

        // PPT access objects
        Microsoft.Office.Interop.PowerPoint.Application ppApp;
        Presentations ppPresens;
        Presentation objPres;

        // Open previously selected presentation
        private void openPresentation()
        {
            // As there are COM objects involved...
            GC.Collect();
        
            // Create PPT access objects
            ppApp = new Microsoft.Office.Interop.PowerPoint.Application();
            ppApp.Visible = Microsoft.Office.Core.MsoTriState.msoTrue;
            ppPresens = ppApp.Presentations;
            
            // Load presentation from disk
            objPres = ppPresens.Open(edFName.Text, Microsoft.Office.Core.MsoTriState.msoFalse,
                Microsoft.Office.Core.MsoTriState.msoTrue, Microsoft.Office.Core.MsoTriState.msoTrue);
            Slides objSlides = objPres.Slides;
            Microsoft.Office.Interop.PowerPoint.SlideShowWindows objSSWs;
            Microsoft.Office.Interop.PowerPoint.SlideShowSettings objSSS;

            objSSS = objPres.SlideShowSettings;

            // Show slideshow
            objSSS.Run();
            objSSWs = ppApp.SlideShowWindows;
        }

        private void process()
        {
            // Open communitation session
            try
            {
                if (sp.IsOpen)
                    sp.Close();
                sp.PortName = edPorta.Text;
                sp.BaudRate = 9600;
                sp.Open();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
                return;
            }

            bool navigation = true;
            DateTime now = DateTime.Now;
            string lastCmd = string.Empty;
            while (navigation)
            {
                string cmd = string.Empty;
                if (sp.BytesToRead > 0)
                {
                    // Get command sent by Arduino
                    cmd = sp.ReadLine().TrimEnd('\r');
                    // Avoid repetition
                    TimeSpan diff = DateTime.Now.Subtract(now);
                    if (diff.Milliseconds > 200)
                        now = DateTime.Now;
                    else
                        cmd = string.Empty;

                    // Process Arduino commands
                    if (cmd == cmdOpenPresentation.Text)
                        openPresentation();
                    else
                        if (cmd == cmdForward.Text)
                            ppPresens[1].SlideShowWindow.View.Next();
                        else
                            if (cmd == cmdBackward.Text)
                                ppPresens[1].SlideShowWindow.View.Previous();
                            else
                                if (cmd == cmdClosePresentation.Text)
                                    navigation = false;
                } 
                System.Windows.Forms.Application.DoEvents();
            }
            // Close stuff
            objPres.Close();
            ppApp.Quit();
        }
        
        private void button2_Click(object sender, EventArgs e)
        {
            // Ask for PPT presentation file name
            if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK)
            {
                // if selected, process it
                edFName.Text = ofd.FileName;
                process();
            }
        }

        private void fMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            // Close stuff
             try
             {
               if (sp.IsOpen)
                   sp.Close()
             }
             catch{}
        }
    }
}


O código é um pouco longo, mas fácil de entender. Na sessão using temos a referência à assembly que faz a interface com o PPT. Ela encapsula chamadas chamadas COM, que é (ainda) o protocolo de comunicação com o Office. Quando o Office é instalado ele geralmente instala essas assemblies. O nome que a Microsoft dá a esse troço atualmente PIA, Primary Interop Assemblies, então se quiser saber mais, clique aqui.

Uma observação necessária é que a Microsoft costuma mexer nessas interfaces de uma versão do Office para a outra. Isso tem diminuído, mas se esse programa não funcionar com Office diferente do 2010, é culpa do Steve, não minha.

Voltado ao nosso código acima: dentro do construtor tem as instruções:

string[] ports = System.IO.Ports.SerialPort.GetPortNames();
edPorta.Text = ports[ports.Length - 1];

Esse trecho de código, de minha própria lavra, tenta "adivinhar" qual a porta que o Arduino está usando. 99% das vezes ele escolhe a última disponível, então é essa que a gente indica, e geralmente acerta.

Em seguida temos, logo abaixo do construtor do form, as instruções:

Microsoft.Office.Interop.PowerPoint.Application ppApp;
Presentations ppPresens;
Presentation objPres;

Elas definem os objetos necessários para se comandar uma apresentação PPT de dentro de uma aplicação .Net.

Em seguida vem a rotina openPresentation() que, uma vez indicado o arquivo a ser exibido, faz o carregamento deste para a memória "dentro" dos objetos correspondentes.A instrução:

   objPres = ppPresens.Open(edFName.Text, Microsoft.Office.Core.MsoTriState.msoFalse,
                Microsoft.Office.Core.MsoTriState.msoTrue, Microsoft.Office.Core.MsoTriState.msoTrue);

carrega o arquivo para a memória. Em seguida são criados outros objetos mas o que importa é que, após a última instrução da rotina a apresentação será exibida no modo apresentação, ou seja, pronta para que a gente possa comandá-la pelo Arduino.

Em seguida vem a rotina Process(), que é quem vai receber e processar os comandos vindos do Arduino.

A primeira parte estabelece a comunicação serial com o Arduino. Depois criam-se alguns objetos necessários e aí vem o comando while, que forma o loop de processamento dos comandos.

A rotina fica esperando que haja bytes a ser lidos, então os lê e coloca em cmd. Observe que tem uma rotininha de threshold, que busca evitar que, caso o usuário fique com a mão por muito tempo no botão esse seja indefinidamente repetido. Ocorre que a rotina que lê os códigos do Arduino já elimina as repetições, então seria dispensável esse cuidado, mas... vai que, né?

Por fim o ninho de ifs que processa as instruções enviadas pelo controle. Os comandos que eu escolhi usar para comandar a apresentação foram: o botão Ok, que mostra a apresentação, os botões ao lado do Ok para navegar pela apresentação e o botão Sair para encerrar.

Bom, é isso. Divirtam-se! Aqueles que usarem, por favor, me mandem um email dizendo o que acharam. Se tiverem sugestões, prometo que as implemento ASAP.