miércoles, 26 de febrero de 2014

Comunicacion Arduino y Windows 8.1 Mediante Bluetooth

Este es un pequeño ejemplo de comunicación de arduino con una aplicación windows 8.1 a través del bluetooth donde manipularemos un led y servomotor enviando datos al arduino para que realice el evento requerido y recibiendo un mensaje en la aplicación del evento que se esta realizando en el arduino.

Software y materiales utilizados

Visual Studio 2013.
Visual Micro.
Arduino Leonardo.
Bluetooth shield for arduino.
Micro Servo 9g SG90.
Led.

A continuación mostrare una captura de pantalla de la aplicación, y daré una breve explicación de los elementos que se agregaron y su funcionamiento.

*Botón Buscar: para realizar la busqueda del dispositivo bleutooth.
*Botón Cancelar: para cancelar la conexión antes de que se conecte con el dispositivo.
*Botón Desconectar: para desconectar la conexión con el dispositivo.
*TextBlock Excepciones: para mostrar cualquier problema que halla durante la conexión.
*Boton Led: donde encenderá y apagara el led.
*TextBlock Estado Led; donde mostrara el estado del led si esta encendido o apagado.
*Boton Motor: para mover el servo.
*TextBlock  Estado Motor: mostrara el estado del motor hacia donde esta girando



Aqui  dejo como va conectado el circuito utilizado.




Aqui dejare el sketch de arduino :

//Librerias
#include &ltservo .h=""&gt
#include "SoftwareSerial.h"
//Variables
Servo myservo;
int Luz = 13;
const int TX_BT = 10;
const int RX_BT = 11;
SoftwareSerial BluetoothSerial(TX_BT, RX_BT);

void setup() {
  // put your setup code here, to run once:
    pinMode(Luz, OUTPUT);
    Serial.begin(9600);
 Serial.println("Serial Iniciado");
 BluetoothSerial.begin(9600);
 Serial.println("Bluetooth Iniciado");
}

void loop() {
  // put your main code here, to run repeatedly:
    LeerBluetooth();
}
//Metodo para recibir el mensaje enviado desde nuestra aplicacion 
void LeerBluetooth()
{
 if (BluetoothSerial.available())
 {
  int commandSize = (int) BluetoothSerial.read();
  char command[commandSize];
  int commandPos = 0;
  while (commandPos < commandSize)
  {
   if (BluetoothSerial.available())
   {
    command[commandPos] = (char) BluetoothSerial.read();
    commandPos++;
   }
  }
  command[commandPos] = 0;
  Eventos(command);
 }
}
//En este metodo se procesa el mensaje
//y dependiendo del mensaje enviado desde la aplicacion 
//realiza una funcion
void Eventos(char* message)
{
 Serial.println(message);
  if ((String) message == "Encender_Luz")
  {
    digitalWrite(Luz, HIGH);
    EnviarMensaje("Luz Encendida");
  }
  if ((String) message == "Apagar_Luz")
  {
    digitalWrite(Luz, LOW);
    EnviarMensaje("Luz Apagada");
  }
   
  if ((String) message == "Mover_Motor")
  {
    Mover180();
    EnviarMensaje("Servo a 180 Grados");
    delay(2000);
    Mover0();
    EnviarMensaje("Servo a 0 Grados");
    delay(2000);
  } 
  myservo.detach();
 
}
//Envia mensaje a la aplicacion del proceso que esta realizando en el arduino
void EnviarMensaje(char* message)
{
 Serial.print("> ");
 Serial.println(message);
 int messageLen = strlen(message);
 if (messageLen < 256)
 {
  BluetoothSerial.write(messageLen);
  BluetoothSerial.print(message);
 }
}
//Metodos para mover los servos
void Mover180()
{
 myservo.attach(12);
 myservo.write(180);
 delay(15);
}
void Mover0()
{
 myservo.attach(12);
 myservo.write(0);
 delay(15);
}



Ahora pasaremos al código para desarrollar la aplicación para lo que crearemos una clase llamada ConexionBluetooth. Esta clase es la encargada de buscar los dispositivos disponibles, realizar la conexión con el dispositivo, enviar y recibir mensaje con el arduino a través del bluetooth. 
El código es el siguiente :

//librerias
using System;
using System.Threading.Tasks;
using Windows.Devices.Bluetooth.Rfcomm;
using Windows.Devices.Enumeration;
using Windows.Foundation;
using Windows.Networking.Sockets;
using Windows.Storage.Streams;
using Windows.UI.Popups;

namespace BluetoothArduino
{
    public class ConexionBluetooth
    {
        #region Eventos
        //Declaramos el delegado del cambio de estado del bluetooth 
        public delegate void AddOnStateChangedDelegate(object sender, BluetoothConnectionState state);
        public event AddOnStateChangedDelegate StateChanged;
        private void OnStateChangedEvent(object sender, BluetoothConnectionState state)
        {
            if (StateChanged != null)
                StateChanged(sender, state);
        }
        //Aqui declararemos el delegado de las exepciones 
        public delegate void AddOnExceptionOccuredDelegate(object sender, Exception ex);
        public event AddOnExceptionOccuredDelegate ExceptionOccured;
        private void OnExceptionOccuredEvent(object sender, Exception ex)
        {
            if (ExceptionOccured != null)
                ExceptionOccured(sender, ex);
        }
        //En este delegado recibira el mensaje enviado desde el arduino
        public delegate void AddOnMessageReceivedDelegate(object sender, string message);
        public event AddOnMessageReceivedDelegate MessageReceived;
        private void OnMessageReceivedEvent(object sender, string message)
        {
            if (MessageReceived != null)
                MessageReceived(sender, message);
        }
        #endregion

        #region Variables
        //Declaramos nuestras Variables
        private IAsyncOperation&ltrfcommdeviceservice&gt connectService;
        private IAsyncAction connectAction;
        private RfcommDeviceService rfcommService;
        private StreamSocket socket;
        private DataReader reader;
        private DataWriter writer;

        private BluetoothConnectionState _State;
        public BluetoothConnectionState State
        {
            get { return _State; }
            set { _State = value; OnStateChangedEvent(this, value); }
        }
        #endregion

        #region Lifecycle
        //En esta metodo se muestran los dispositivos que sean del tipo SerialPort en un PopupMenu
        public async Task EnumerateDevicesAsync(Rect invokerRect)
        {
            this.State = BluetoothConnectionState.Enumerating;
            //seleccionamos todos los dispositivos disponibles
            var serviceInfoCollection = await DeviceInformation.FindAllAsync(RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort));
            //Creamos el PopupMenu en el que se mostraran los dispositivos
            PopupMenu menu = new PopupMenu();
            //Añadimos los dispositivos encontrados al PopupMenu
            foreach (var serviceInfo in serviceInfoCollection)
                menu.Commands.Add(new UICommand(serviceInfo.Name, new UICommandInvokedHandler(ConnectToServiceAsync), serviceInfo));
           //Seleccionamos el dispositvo con el que nos queremos comunicar
            var result = await menu.ShowForSelectionAsync(invokerRect);
            //Si no se pudo conectar al dispositivo se cambia el estado de la conexion a desconectado
            if (result == null)
                this.State = BluetoothConnectionState.Disconnected;
        }

        //Metodo que nos dara la conexion al dispositivo seleccionado
        private async void ConnectToServiceAsync(IUICommand command)
        {
            //Se obtiene el Id del dispositivo seleccionado
            DeviceInformation serviceInfo = (DeviceInformation)command.Id;
            //el estado de la conexion se pondra conectando
            this.State = BluetoothConnectionState.Connecting;
            try
            {
                //Inicializa el servicio del dispositivo RFCOMM de Bluetooth de destino
                connectService = RfcommDeviceService.FromIdAsync(serviceInfo.Id);
                rfcommService = await connectService;
                if (rfcommService != null)
                {
                    //Se inicializa el socket 
                    socket = new StreamSocket();
                    connectAction = socket.ConnectAsync(rfcommService.ConnectionHostName, rfcommService.ConnectionServiceName, SocketProtectionLevel.BluetoothEncryptionAllowNullAuthentication);
                    //Puedes Cancelar la conexion 
                    await connectAction;
                    //Se inicializan las variables que envian y reciben los mensajes
                    writer = new DataWriter(socket.OutputStream);
                    reader = new DataReader(socket.InputStream);
                    Task listen = ListenForMessagesAsync();
                    //se cambia el estado de conexion del bluetooth a conectado
                    this.State = BluetoothConnectionState.Connected;
                }
                else
                    OnExceptionOccuredEvent(this, new Exception("No se pudo connectar al servicio.\n Verifca que 'bluetooth.rfcomm' capabilityes es declarado con la funcion de tipo 'name:serialPort' en Package.appxmanifest."));
            }
            catch (TaskCanceledException)
            {
                this.State = BluetoothConnectionState.Disconnected;
            }
            catch (Exception ex)
            {
                this.State = BluetoothConnectionState.Disconnected;
                OnExceptionOccuredEvent(this, ex);
            }
        }

            //Cancelar la conexion
        public void AbortConnection()
        {
            if (connectService != null && connectService.Status == AsyncStatus.Started)
                connectService.Cancel();
            if (connectAction != null && connectAction.Status == AsyncStatus.Started)
                connectAction.Cancel();
        }
           
        //Terminar la conexion con el dispositivo 
        public void Disconnect()
        {
            //dejamos variables en null
            if (reader != null)
                reader = null;
            if (writer != null)
            {
                writer.DetachStream();
                writer = null;
            }
            if (socket != null)
            {
                socket.Dispose();
                socket = null;
            }
            if (rfcommService != null)
                rfcommService = null;
            this.State = BluetoothConnectionState.Disconnected;
        }
        #endregion

        #region Enviar y Recivir
        //Metodo para enviar los mensajes al arduino
        public async Task&ltuint&gt SendMessageAsync(string message)
        {
            //Declaramos variable para ver tamaño del mensaje
            uint sentMessageSize = 0;
            if (writer != null)
            {
                //obtenemos el tamaño del string y lo convertimos en bytes y lo enviamos
                uint messageSize = writer.MeasureString(message);
                writer.WriteByte((byte)messageSize);
                sentMessageSize = writer.WriteString(message);
                await writer.StoreAsync();
            }
            return sentMessageSize;
        }
        private async Task ListenForMessagesAsync()
        {
            while (reader != null)
            {
                try
                {
                    uint sizeFieldCount = await reader.LoadAsync(1);
                    if (sizeFieldCount != 1)
                    {
                        // el socket se cierra antes de que se puedan leer los datos
                        return;
                    }

                    // Leer el mensaje 
                    uint messageLength = reader.ReadByte();
                    uint actualMessageLength = await reader.LoadAsync(messageLength);
                    if (messageLength != actualMessageLength)
                    {
                        // el socket se cierra antes de que se puedan leer los datos
                        return;
                    }
                    // mensaje leido y asignado a la variable message
                    string message = reader.ReadString(actualMessageLength);
                    // Se ejecuta el evento y retorna el mensaje 
                    OnMessageReceivedEvent(this, message);
                }
                catch (Exception ex)
                {
                    if (reader != null)
                        OnExceptionOccuredEvent(this, ex);
                }
            }
        }
        #endregion
    }
    //Tipos de estados de la conexion
    public enum BluetoothConnectionState
    {
        Disconnected,
        Connected,
        Enumerating,
        Connecting
    }
}

}
Importante agregar en  Package.appxmanifest la siguientes Capabilities :
&ltCapabilities&gt
    &ltm2:DeviceCapability Name="bluetooth.rfcomm"&gt
     &ltm2:Device Id="any"&gt
       &ltm2:Function Type="name:serialPort"/&agt
      &lt/m2:Device&gt
    &lt/m2:DeviceCapability&gt
 &lt/Capabilities&gt

Se debe de agregar la  librería  TCD.Controls.dll que viene agregada en el proyecto , ahora el código del MainPage  que es el siguiente :

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using TCD.Controls;


namespace BluetoothArduino
{
    
    public sealed partial class MainPage : Page
    {
        //Hacemos instancia de la clase
        private ConexionBluetooth conexion = new ConexionBluetooth();
        // bandera que usaremos para saber el estado de la luz
        int banderaLuz = 0;
        public MainPage()
        {
            this.InitializeComponent();
            //declaramos nuestros eventos
            conexion.ExceptionOccured += delegate(object sender, Exception ex) { txtconsola.Text = ex.Message + "\n"; };
            conexion.MessageReceived += connectionManager_MessageReceived;
            conexion.StateChanged += connectionManager_StateChanged;
            conexion.State = BluetoothConnectionState.Disconnected;
        }
        protected override void OnNavigatedFrom(Windows.UI.Xaml.Navigation.NavigationEventArgs e)
        {
            conexion.Disconnect();
            base.OnNavigatedFrom(e);
            progress.Visibility = Visibility.Collapsed;
        }
        //Evento para habilitar o desahbilitar los botones segun la conexion
        private void connectionManager_StateChanged(object sender, BluetoothConnectionState state)
        {
            progress.IsIndeterminate = (state == BluetoothConnectionState.Connecting);
            btncancelar.IsEnabled = (state == BluetoothConnectionState.Connecting);
            btndesconectar.IsEnabled = (state == BluetoothConnectionState.Connected);
        }
        //Evento donde se recibe el mensaje enviado desde el arduino y los mostrara en los textblock de estados
        //dependiendo del mensaje enviado
        private async void connectionManager_MessageReceived(object sender, string message)
        {
            if (message == "Luz Encendida")
            {
                txtled.Text = message;
            }
            else if (message == "Luz Apagada")
            {
                txtled.Text = message;
            }
            else if (message == "Servo a 180 Grados")
            {
                txtmotor.Text = message;
            }
            else if (message == "Servo a 0 Grados")
            {
                txtmotor.Text = message;
            }

        }
        //Boton de buscar muestra la lista de dispositivos encontrados solo es seleccionarlo para comenzar la conexion
        private async void Button_Click_1(object sender, RoutedEventArgs e)
        {
            await conexion.EnumerateDevicesAsync((sender as Button).GetElementRect());
            progress.Visibility = Visibility.Visible;
        }
        //Boton Cancelar cancela la conexion al bluetooth
        private void btncancelar_Click(object sender, RoutedEventArgs e)
        {
            conexion.AbortConnection();
            progress.Visibility = Visibility.Collapsed;
        }
        //Boton Desconectar 
        //desconecta la conexion con el bluetooth
        private void btndesconectar_Click(object sender, RoutedEventArgs e)
        {
            conexion.Disconnect();
            progress.Visibility = Visibility.Collapsed;
        }
        //Boton Led Envia Mensaje al arduino
        private async void btnled_Click(object sender, RoutedEventArgs e)
        {
            if (banderaLuz == 0)
            {
                var res = await conexion.SendMessageAsync("Encender_Luz");
                banderaLuz = 1;
            }
            else if (banderaLuz == 1)
            {
                var res = await conexion.SendMessageAsync("Apagar_Luz");
                banderaLuz = 0;
            }
        }
        //Envia Mensaje al arduino para mover el motor
        private async void Button_Click(object sender, RoutedEventArgs e)
        {
            var res = await conexion.SendMessageAsync("Mover_Motor");
        }

    }
}

}
Esto es todo de este ejemplo, esperen nuevas entradas. Les dejo el link para su descarga: https://github.com/ricky3c/Windows-8.1---Arduino




1 comentario:

  1. Muy buen tutorial, me ha servido de mucho. Quisiera preguntarte, para que sirve la librería TCD.control o mejor dicho, para que se tiene agregar al Mainpage?

    ResponderEliminar