Este artigo descreve a implementação de um utilitário de teste de porta serial usando C# e Windows Forms. O aplicativo permite configurar parâmetros de comunicação serial, enviar e receber dados em formatos ASCII e hexadecimal, e monitorar estatísticas de tráfego. A seguir, apresenta-se o código-fonte modificado com nomes alternativos de variáveis e estrutura lógica ajustada.
Implementação Principal (SerialDebuggerWindow.cs)
using System;
using System.IO.Ports;
using System.Text;
using System.Timers;
using System.Windows.Forms;
namespace SerialTools
{
public partial class SerialDebuggerWindow : Form
{
private SerialPort commPort = new SerialPort();
private Timer refreshTimer = new Timer(1500);
private StringBuilder inputBuffer = new StringBuilder();
private long rxByteCount = 0;
private long txByteCount = 0;
private object syncRoot = new object();
public SerialDebuggerWindow()
{
InitializeComponent();
SetupInterface();
RefreshAvailablePorts();
refreshTimer.Elapsed += OnTimerElapsed;
}
private void SetupInterface()
{
this.Size = new System.Drawing.Size(960, 700);
configGroup.Text = "Configuração da Porta";
controlGroup.Text = "Controle de Dados";
monitorGroup.Text = "Monitoramento";
portSelector.Items.AddRange(SerialPort.GetPortNames());
baudSelector.Items.AddRange(new object[] { 4800, 9600, 19200, 38400, 57600, 115200 });
dataBitsSelector.Items.AddRange(new object[] { 7, 8 });
paritySelector.Items.AddRange(Enum.GetNames(typeof(Parity)));
stopBitsSelector.Items.AddRange(Enum.GetNames(typeof(StopBits)));
inputBox.AcceptsReturn = true;
outputBox.Multiline = true;
outputBox.ScrollBars = ScrollBars.Vertical;
outputBox.Font = new System.Drawing.Font("Courier New", 11);
statusLabel.Text = "Desconectado";
refreshTimer.Start();
}
private void RefreshAvailablePorts()
{
portSelector.Items.Clear();
portSelector.Items.AddRange(SerialPort.GetPortNames());
if (portSelector.Items.Count > 0)
portSelector.SelectedIndex = 0;
}
private void ToggleConnection(object sender, EventArgs e)
{
try
{
if (!commPort.IsOpen)
{
ApplyPortSettings();
commPort.DataReceived += HandleDataReceived;
commPort.Open();
connectButton.Text = "Desconectar";
statusLabel.Text = $"Conectado: {commPort.PortName}";
}
else
{
commPort.Close();
connectButton.Text = "Conectar";
statusLabel.Text = "Desconectado";
}
}
catch (Exception error)
{
MessageBox.Show($"Erro: {error.Message}", "Falha", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void ApplyPortSettings()
{
try
{
commPort.PortName = portSelector.Text;
commPort.BaudRate = int.Parse(baudSelector.Text);
commPort.DataBits = int.Parse(dataBitsSelector.Text);
commPort.StopBits = (StopBits)Enum.Parse(typeof(StopBits), stopBitsSelector.Text);
commPort.Parity = (Parity)Enum.Parse(typeof(Parity), paritySelector.Text);
commPort.Handshake = Handshake.None;
}
catch (Exception configError)
{
throw new InvalidOperationException($"Configuração inválida: {configError.Message}");
}
}
private void HandleDataReceived(object sender, SerialDataReceivedEventArgs e)
{
string receivedData = commPort.ReadExisting();
lock (syncRoot)
{
inputBuffer.Append($"[{DateTime.Now:HH:mm:ss.fff}] RX: {receivedData}\r\n");
rxByteCount += receivedData.Length;
}
RefreshOutput();
}
private void SendHexData(object sender, EventArgs e)
{
try
{
byte[] dataPacket = ConvertHexToBytes(inputBox.Text);
commPort.Write(dataPacket, 0, dataPacket.Length);
txByteCount += dataPacket.Length;
LogMessage($"Envio HEX: {inputBox.Text}");
}
catch
{
MessageBox.Show("Formato hexadecimal inválido", "Erro de Entrada", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
private void SendTextData(object sender, EventArgs e)
{
string textContent = inputBox.Text;
commPort.Write(textContent);
txByteCount += textContent.Length;
LogMessage($"Envio ASCII: {textContent}");
}
private void RefreshOutput()
{
if (InvokeRequired)
{
Invoke(new Action(() =>
{
outputBox.Text = inputBuffer.ToString();
rxCounterLabel.Text = $"{rxByteCount} bytes";
txCounterLabel.Text = $"{txByteCount} bytes";
statusLabel.Text = commPort.IsOpen ? "Ativo" : "Inativo";
}));
}
}
private byte[] ConvertHexToBytes(string hexString)
{
if (hexString.Length % 2 != 0) throw new ArgumentException("Formato hexadecimal inválido");
byte[] result = new byte[hexString.Length / 2];
for (int index = 0; index < hexString.Length; index += 2)
{
result[index / 2] = Convert.ToByte(hexString.Substring(index, 2), 16);
}
return result;
}
private void LogMessage(string message)
{
lock (syncRoot)
{
inputBuffer.Append($"[{DateTime.Now:HH:mm:ss.fff}] LOG: {message}\r\n");
}
}
private void OnTimerElapsed(object sender, ElapsedEventArgs e)
{
RefreshAvailablePorts();
}
private void WindowClosing(object sender, FormClosingEventArgs e)
{
if (commPort.IsOpen) commPort.Close();
refreshTimer.Stop();
}
}
}
Definição da Interface (SerialDebuggerWindow.Deisgner.cs)
O código do designer define a estrutura da interface com grupos para configuração, operação de dados e monitoramento. Os controles incluem caixas de seleção para portas, baud rate, bits de dados, paridade e stop bits, além de botões para conexão e envio de dados, e caixas de texto para entrada e saída.
Módulos de Funcionalidade
Cálculo de CRC16:
public static class ChecksumCalculator
{
public static ushort ComputeCrc16(byte[] payload)
{
ushort accumulator = 0xFFFF;
foreach (byte element in payload)
{
accumulator ^= (ushort)(element << 8);
for (int bitCount = 0; bitCount < 8; bitCount++)
{
if ((accumulator & 0x8000) != 0)
{
accumulator = (ushort)((accumulator << 1) ^ 0xA001);
}
else
{
accumulator <<= 1;
}
}
}
return accumulator;
}
}
Gerenciamento de Logs:
public class FileLogger
{
public void RecordToDisk(string destination, string content)
{
try
{
File.AppendAllText(destination,
$"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} | {content}\r\n");
}
catch (Exception ioError)
{
MessageBox.Show($"Falha ao gravar: {ioError.Message}");
}
}
}
Monitoramento de Tráfego:
public class TrafficAnalyzer
{
private long previousRx = 0;
private long previousTx = 0;
public (double DownloadSpeed, double UploadSpeed) CalculateRates(long currentRx, long currentTx)
{
double download = (currentRx - previousRx) / 1024.0;
double upload = (currentTx - previousTx) / 1024.0;
previousRx = currentRx;
previousTx = currentTx;
return (Math.Round(download, 2), Math.Round(upload, 2));
}
}
Configuração e Execução
Pré-requisitos:
- Runtime .NET Framework 4.8 ou superior
- Ambiente de desenvolvimento Visual Studio 2019+
- Sistema operacional Windows 10 ou 11
Processo de Compilação:
- Criar um projeto Windows Forms no Visual Studio
- Inserir os componentes de interface e lógica conforme os trechos de código
- Definir
SerialDebuggerWindowcomo formulário inicial - Executar a compilação e teste
Modo de Uso:
- Selecionar a porta COM e configurar parâmetros na seção superior
- Utiliazr os botões para enviar dados em formato texto ou hexadecimal
- Monitorar as mensagens de recepção e transmissão na área de saída
- As estatísticas de bytes são atualizadas em tempo real
Possíveis Extensões
- Adicionar suporte a protocolos como Modbus RTU com decodificação automática
- Integrar geração de portas seriais virtuais para testes isolados
- Implementar criptografia AES para comunicação segura
- Desenvolver interface de rede para acesso remoto à porta serial