USANDO EL PUERTO SERIAL CON VISUAL BASIC 6 Y VISUAL C SHARP 2005

 

Antes de empezar con este tutorial recomiendo leer: http://www.lammertbies.nl/comm/info/RS-232_null_modem.html porque puede pasar que no se sepa de qué estoy hablando.



Manejo del Puerto Serie: Visual Basic 6


Manejar el puerto serie en VB6 es bastante sencillo con el control MSCOMM. 
Es posible controlar por separado las líneas RTS y DTR por si se las desea manipular independientemente al envío y recepción de datos. Para ello la propiedad de Handshaking debe estar en "none".
En este link: http://www.frro.utn.edu.ar/catedras/index.php?mostrar_contenido=127&materia=12 en la sección de Visual Basic está el pdf "Manejo de Puertos" que da toda la teoría necesaria para tener un control total del puerto serie con VB6, especialmente el manejo de eventos. Los eventos le dicen al MSCOMM si ha habido algún cambio en el puerto; el tipo de evento es recibido por la propiedad CommEvent y según su valor se puede saber qué ha ocurrido (qué pin ha cambiado su estado). Para esto, la evaluación de CommEvent se hace dentro de OnComm del control MSCOMM (en realidad es el único evento que tiene este control).
Para leer caracteres correctamente en una aplicación tipo chat, es mejor dejar la propiedad RThreshold en 1. Así el evento de lectura se disparará cada vez que se reciba un caracter.

En este link: http://www.geocities.com/ejemplo_mscomm/ hay otro ejemplo de cómo usar el MSCOMM.
Y aquí http://uttinfor.tripod.com/index/id4.html también explican las propiedades del MSCOMM.

Aquí se puede descargar mi programa y el código fuente de ejemplo que envía y recibe datos de texto, activa y desactiva las señales RTS y DTR (manipulando las propiedades RTSEnable y DTREnable respectivamente), y evalúa el estado de las líneas CD, DSR y CTS, así como si el puerto puede o no abrirse (en el caso que ya esté abierto por otro programa).

 



Manejo del Puerto Serie: C Sharp 2005


Acá las cosas son un poco más complicadas. Puede optarse por usar el ocx de Visual Basic MSCOMM eligiendo el respectivo componente .com al hacer click sobre el cuadro de herramientas->Elegir Elementos. Pero entonces no tendría sentido hacerlo desde C Sharp en lugar del mismo Visual Basic 6. En este ejemplo he querido usar la plataforma .Net para controlar el puerto serial.

Primero se debe añadir la línea:
using System.IO.Ports; 



Luego, desde en el cuadro de herramientas buscar "serialPort" y jalar este componente a nuestra aplicación (que será del tipo WindowsApplication).

El problema con C Sharp es que la recepción de datos no puede hacerse de manera tan directa como con Visual Basic 6. El evento que detecta que se ha recibido data no puede tratarse igual. 
Debe hacerse tal y como lo muestran en la página 176 de este documento: http://www.slideshare.net/Metaconta/pic-rs232-puerto-serie-con-pic16f84a-presentation 

Yo lo hice exactamente como se indica ahí y me salió :) 



Otro problema que tuve fue el no poder cambiar las propiedades de los controles (como Labels o PictureBox) tan libremente como en VB6. El C Sharp dispara una excepción cada vez que se intenta cambiar las propiedades de los controles en tiempo de ejecución como protección en un entorno multitarea. Para desactivar esta excepción puse en Form1_Load (de la documentación de Microsoft):
CheckForIllegalCrossThreadCalls = false;

Así ya se puede cambiar ciertas cositas en los controles que son necesarias, como se verá más adelante.

Para enviar data del tipo texto, basta la instrucción:
serialPort1.Write(txtEnviar.Text);

Donde txtEnviar es un RichTextBox que contiene el texto a enviar por el puerto.

Para recibir la data se llama a la propiedad: serialPort1.ReadExisting(); que lee todo el buffer de lectura del puerto.

Tuve algunos problemas con el salto de línea a la hora de recibir los datos. Para corregirlos, y hacer que después de enviar el texto salte a la siguiente línea de forma automática en el RichText que recibe los datos, tuve que añadir el caracter 13 (el salto) al texto en txtEnviar y recién enviar la data. Luego restablecía el texto original en txtEnviar sin el caracter 13 al final. Para esto usé la variable data_temp.


La diferencia con Visual Basic, es que C Sharp, a la hora de abrir el puerto, manda las señales CD, DSR y CTS a "true" ó 1. Si se desea usar estas líneas para recibir señales y disparar el evento que detecta cambios en ellas se debe trabajar con la lógica invertida. Es decir que el dispositivo que nos envía esas señales debe usar lógica invertida también.

En cambio si se desea enviar las señales de control RTS y DTR, la lógica de trabajo dependerá de cómo las interprete el dispositivo que las recibe (esto es válido también para VB6). Para este caso, también consideré lógica invertida para las señales RTS y DTR. 1 es desactivado, 0 es activado.
Así, en el control serialPort se colocan sus propiedades RtsEnable y DtrEnable como "true" para que tengan estos valores al iniciar la aplicación. Si se desea activar, su estado pasará a "false".

Para controlar los eventos, o los cambios en las líneas de entrada del puerto serial, me basé en esté código:

Imports System.IO.Ports
Public Class Form1
Private WithEvents mPort As SerialPort

Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
mPort = New SerialPort("COM1", 9600)
mPort.Open()
End Sub

Private Sub mPort_PinChanged(ByVal sender As Object, ByVal e As System.IO.Ports.SerialPinChangedEventArgs) Handles mPort.PinChanged
If e.EventType = SerialPinChange.CDChanged Then
If mPort.CDHolding Then
Debug.Print("Carrier detect on")
Else
Debug.Print("Carrier detect off")
End If
End If
End Sub
End Class

Fuente: http://social.msdn.microsoft.com/Forums/en-US/netfxbcl/thread/ffbb63a1-5a04-46c9-a71a-f52b6f7ada81/ 


Está hecho para Visual Basic 2005 así que tuve que "traducirlo" a C Sharp y adaptarlo para que reconociera los eventos que me interesaban. La lógica es la misma que con VB6: con un "switch" evaluar qué evento ha ocurrido según el valor de la propiedad que los recibe. En C Sharp es e.EventType y se evalúa versus las propiedades de serialPort CDChanged, CtsChanged y DsrChanged. Estas propiedades sólo saben si hubo un cambio en las señales CD, CTS y DSR, no conocen sus estados (para saber los estados se usan otras propiedades que se explican más abajo).

Ambos programas, el de VB6 y C Sharp funcionan igual y hacen lo mismo. La única diferencia es que el de C Sharp emplea lógica invertida. 

El código fuente y el programa en C Sharp se puede bajar aquí.

Si los "leds" que indican el estado de las señales CD, DSR y CTS en VB6 son controles tipo "Shape", en C Sharp usé controles Picturebox. Dibujar un círculo y un cuadrado en C Sharp es muy engorroso, llegué a crear una clase para ello, pero cuando quería dibujar dos círculos, al final el programa me los dibujaba uno encima de otro sin importar lo que hiciera para evitarlo. Creé dos propiedades para pasarle el valor de las coordenadas, pero igual no funcionó. Haciendo puntos de quiebre descubrí que el C Sharp primero asigna los valores a las variables con los valores de las coordenadas y luego dibuja todo lo que se le pide, usando sólo los valores finales de estas variables. Después de mucho tratar decidí no dibujar nada. Para quienes quieran intentar hallarle una solución, les dejo la clase:

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Windows.Forms;

namespace WindowsApplication1
{
/* en el programa principal crear un objeto.
Shapes cuad = new Shapes();
Estado.Paint += new PaintEventHandler(cuad.Shapes_Fill);
Estado.Paint += new PaintEventHandler(cuad.Shapes_Paint); 
*/

class Shapes
{
private Graphics g;

private int _coorX;
private int _coorY;

public Shapes()
{
_coorX = 30; 
_coorY = 30; //sólo para inicializarlas
}

public int coorX
{
get { return _coorX; }
set { _coorX = value; }
}

public int coorY
{
get { return _coorY; }
set { _coorY = value; }
}


public void Shapes_Paint(object sender, System.Windows.Forms.PaintEventArgs e)

g = e.Graphics;
Pen p = new Pen(Color.Black,2) ;
g.DrawEllipse(p, 30, 30 , 30, 30); 
}


public void Shapes_Fill(object sender, System.Windows.Forms.PaintEventArgs e) 

g = e.Graphics;
Pen p = new Pen(Color.Black, 2);
//g.DrawEllipse(p, _coorX , _coorY, 30, 30); 

g.FillEllipse(Brushes.Firebrick, 30, 80, 30, 30); 
}



}


Al final preferí usar tres picturebox para los "leds" de estado de CD, DRS y CTS, así simplemente cambiaba su color según el estado de estas líneas (para poder hacer esto tuve que poner a "false" la propiedad CheckForIllegalCrossThreadCalls mencionada más arriba). Estos estados se obtienen evaluando las propiedades de serialPort CDHolding, DsrHolding y CtsHolding respectivamente (estas propiedades son las mismas que en VB6).

Otra diferencia que hallé es que si se deja la línea CD sin conexión, VB6 no le hace nada, pero C Sharp la manda a cero al enviar data. Como está sin conectar no hay forma de ponerla a uno nuevamente.

Ambos programas los probé con el test de Loop Back puenteando los puntos 7-8-1, 4-6 y 2-3. Cualquiera de los dos ejecutables pueden usarse como programas independientes para probar el puerto serie. En el caso del C Sharp, el ejecutable está en \WindowsApplication1\