Vamos a programar #98½ - Ver el rendimiento del PC usando arduino y C#.
Hola de nuevo a todos, el día de hoy vamos a ver cómo es posible crear una barra de progreso en una pantalla LCD.
En el post anterior vimos cómo es posible usar arduino para monitorear el uso del CPU y la memoria RAM. Usando el programa, era posible visualizar el porcentaje en dos barras de progreso. Si bien todo el programa funcionaba, no lo hacia del todo bien, ya que el procedimiento que se encargaba de hacerlo tenia un par de cosas que le hacían falta y que no consideré hasta que escribí el poste anterior.
Algunas personas me mandaron sus soluciones, y de hecho de eso se trata el blog, no solo de enseñar, si no de aprender todos juntos. Por eso en el post del día de hoy, veremos paso a paso cómo corregir esa situación.
Para empezar vamos a entender cómo es que la barra funciona, en el post anterior, vimos cómo es que se crea cada parte, ahora veamos la lógica. Si hacemos memoria, el código de la barra es el que sigue
void DrawProgressBar(uint8_t Value, uint8_t XLocation, uint8_t YLocation){
uint8_t Progress = map(Value , 1 , 100 , 0 , 20);
lcd.setCursor(XLocation , YLocation);
for (uint8_t i = 0; i < 20; i++)
{
if (i == 0)
{
if (Progress == 0)
lcd.write(0);
else
lcd.write(1);
}
else if (i == 19)
{
if (Progress == 20)
lcd.write(3);
else
lcd.write(2);
}
else
{
if (Progress <= i)
lcd.write(4);
else
lcd.write(5);
}
}
}//Para las pantallas de 16 x 2, basta con reemplazar el 20 por 16 y el 4 por 2
#define SCREEN_SIZE_X 20
#define SCREEN_SIZE_Y 4
//SCL - A5
//SDA - A4
LiquidCrystal_I2C lcd(0x27 , SCREEN_SIZE_X , SCREEN_SIZE_Y);
//{..............}
//Barra de progreso
void DrawProgressBar(uint8_t Value, uint8_t XLocation, uint8_t YLocation){
uint8_t BarSize = SCREEN_SIZE_X - XLocation;
uint8_t Progress = map(Value , 1 , 100 , 0 , BarSize);
lcd.setCursor(XLocation , YLocation);
for (uint8_t i = 0; i < BarSize; i++)
{
if (i == 0)
{
if (Progress == 0)
lcd.write(0);
else
lcd.write(1);
}
else if (i == BarSize - 1)
{
if (Progress == BarSize)
lcd.write(3);
else
lcd.write(2);
}
else
{
if (Progress <= i)
lcd.write(4);
else
lcd.write(5);
}
}
}Vamos a programar #74 - Message Sender 1.0.1.
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).
![]() |
| Una version reducida de la primer version. |
El código 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.






