Vamos a programar #74 - Message Sender 1.0.1.

Hola de nuevo a todos, el día de hoy vamos a continuar con Message Sender Windows que es una actualización de Message Sender que se público ya hace algún tiempo.


En el post anterior vimos cómo es que podemos obtener el titulo de una ventana si estamos usando Windows. Si fuiste de los que intento unir todo para que funcionara, lo primero que notaste es que la versión anterior usaba las matrices de LED de 8x8 y en esta ocasión usaremos una pantalla LCD (de acuerdo a lo que pidieron).

El código en C#.


Primero veamos el código en C# para la aplicación de Windows.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace MessageSenderWindows
{
	public partial class FrmMain : Form
	{
		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern IntPtr GetForegroundWindow();
		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
		[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
		static extern int GetWindowTextLength(IntPtr hWnd); 

		private SerialPort Port = new SerialPort();

		private void ArduinoMessage(string Message)
		{
			try
			{
				Port.Write(Message);
			}
			catch (Exception e)
			{
				MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}

		}

		private string GetCaptionOfActiveWindow()
		{
			string strTitle = string.Empty;
			IntPtr handle = GetForegroundWindow();
			int intLength = GetWindowTextLength(handle) + 1;
			StringBuilder stringBuilder = new StringBuilder(intLength);
			if (GetWindowText(handle, stringBuilder, intLength) > 0)
			{
				strTitle = stringBuilder.ToString();
			}
			return strTitle;
			}

		public FrmMain()
		{
			InitializeComponent();
		}
		private void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
		{
			SerialPort sp = (SerialPort)sender;
			string indata = sp.ReadExisting();
			if (indata.Contains("\r\n"))
			{
				ArduinoMessage(GetCaptionOfActiveWindow());

			}
		}

		private void FrmMain_Load(object sender, EventArgs e)
		{
			string[] Ports = SerialPort.GetPortNames();
			cboportname.Items.AddRange(Ports);
			cboportname.Text = "COM7";
			Port.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
		}

		private void BtnConnect_Click(object sender, EventArgs e)
		{
			try
			{
				if (Port.IsOpen == false)
				{
					Port.BaudRate = (int)(NUDSpeed.Value);
					Port.PortName = cboportname.Text;
					Port.Open();
				}
			}
			catch (Exception ex)
			{
				MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
			}
		}

		private void BtnDis_Click(object sender, EventArgs e)
		{
			if (Port.IsOpen == true)
			{
				Port.Write("Se ha desconectado el cliente");
				Port.Close();
			}	
		}

		private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
		{
			if (Port.IsOpen == true)
			{
				Port.Close();
			}
		}
	}
}

La aplicacion usa varias funciones y procedimientos; en orden de aparacion hacen:


  • "GetForegroundWindow()". Obtiene el "handle" de una ventana.
  • "GetWindowText()". Obtiene el texto de una ventana.
  • "GetWindowTextLength()". Obtiene la longitud del texto de una ventana.
  • "ArduinoMessage()". Intenta escribir datos en un puerto serie. Recibe un parámetro del tipo "string" que es el mensaje que se quiere escribir.
  • "GetCaptionOfActiveWindow()". Esta función sera la encargada de obtener el texto de la ventana que se encuentre activa. Regresa un valor del tipo "string" que representa el título de la ventana con el foco.
  • "DataReceivedHandler()". Este procedimiento es el encargado de procesar el evento "DataReceived" que se genera desde el objeto "Port". Recibe dos parámetros, el primero del tipo "object" que sera el objeto del tipo "Port" que desencadeno el evento, el segundo es un valor del tipo "EventArgs" que contienen algunos datos sobre el evento (que por ahora no usamos).
Con lo anterior tendremos una aplicación que luce mas o menos asi:
Una version reducida de la primer version.

El código para arduino.

El código para arduino es un tanto diferente al de la primer versión por el simple hecho de que usamos una pantalla LCD en lugar del las matrices LED. Lo primero que tenemos que tomar en cuenta es cómo se va a conectar la pantalla. En el post sobre el velocímetro hicimos uso de la pantalla de 16x2 y la conectamos directamente al arduino, pero ahora usaremos una pantalla de 20x4, pero además usaremos un adaptador al que se conectará la pantalla y nos permitirá utilizar I2C para hacer el intercambio de datos.

Lo primero que debemos de hacer es instalar la librería, una vez hecho esto, es importante tener en cuenta que el código está diseñado para una PANTALLA DE 20X4, por lo que si usas una diferente deberás de hacer los ajustes pertinentes (igual incluyo los comentarios que creo son pertinentes). Con todo lo anterior dicho, veamos el código fuente de la aplicación para arduino.

#include <Wire.h> 
#include <LiquidCrystal_I2C.h>
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27,20,4);  // set the LCD address to 0x27 for a 16 chars and 2 line display
char Texto[80];

void setup() {
  lcd.init();     
  lcd.backlight();
  Serial.begin(9600);
}
// la mayor parte las pantallas lcd vienen en tamaños de 16x2 o 20x4
// para que la funcion "funcione" en la version mas pequeña, debemos de hacer los calculos
// en  multiplos de 16
void MoveText(String Message){
	lcd.clear();
	if (Message.length() < 21)
		lcd.print(Message);
	if (Message.length() > 20 && Message.length() < 41)
	{
		lcd.setCursor(0 , 0);
		lcd.print(Message.substring(0 , 20));
		lcd.setCursor(0 , 1);
		lcd.print(Message.substring(20 , Message.length()));
	}
	if (Message.length() > 40 && Message.length() < 61)
	{
		lcd.setCursor(0 , 0);
		lcd.print(Message.substring(0 , 20));
		lcd.setCursor(0 , 1);
		lcd.print(Message.substring(20 , 40));
		lcd.setCursor(0 , 2);
		lcd.print(Message.substring(40 , Message.length()));
	}
	if (Message.length() > 60)
	{
		lcd.setcursor(0 , 0);
		lcd.print(Message.substring(0 , 20));
		lcd.setCursor(0 , 1);
		lcd.print(Message.substring(20 , 40));
		lcd.setCursor(0 , 2);
		lcd.print(Message.substring(40 , 60));
		lcd.setCursor(0 , 3);
		lcd.print(Message.substring(60 , Message.length()));
	}
	delay(1500); 
	Serial.println("\0\0");	
}

void loop() {
	int i = 0;
	if (Serial.available()) {
		while (Serial.available() > 0) {
			Texto[i] = Serial.read();
			i++;
		}
		Texto[i] = '\0';

	}
	MoveText(Texto);
}


La aplicación consta de solo una función "MoveText()", en está función es donde se procesa todo el texto y está es la parte a la que debemos de prestar atención. Al iniciar nos encontramos con un "if" que sirve  para comprobar si la longitud del texto es mayor a 20 pero si la pantalla fuera de 16x2 por ejemplo; deberíamos de hacer la comprobación con el número 16, esto para que el texto se muestre en orden (aunque creo que en la pantalla de 16x2 no hay mayor problema, después veremos un poco de cómo es que funciona la pantalla), pero en el caso de la pantalla de 20x4, lo que haremos es separar el texto de tal forma que solo haya 20 caracteres por linea. Si viste el post anterior, notarás que la pantalla hacia un efecto de scroll, pero decidí omitirlo porque tardaba mucho y para no complicar tanto el proyecto, simplemente opté por la solución sencilla.

Y bien, por ahora es todo, en futuros posts veremos una explicación un poco más detallada sobre las pantallas de este tipo. Cómo de costumbre el código fuente lo puedes descargar de mi dropbox para probarlo y no dudes en modificarlo si tienes alguna idea.

Los leo luego.

No hay comentarios.