Leer bien los datos recibidos.
(1/1)
Meta:
Buenas comper@s:
Uso comunicación por el puerto serie /USB con Arduino y RealTerm para probar y funciona.
Envío el comando tal cual, por ejemplo, B\r desde RealTerm o PuTTY, y recibe el comando exactamente tal cual quiero desde Arduino:
#Comando B recibido\r
Ver zoom.
El problema de C# .net 8.0 usando Windows Form, no lee bien y llevo horas y horas. En mi caso, en vez de usar RealTerm, quiero usar el mio propio hecho con C#. Al enviar comandos como en este ejemplo, B\r a Arduino, lo lee bien, el problema es al devolver los datos que si lo devuelve y bien y C# no lo interpreta bien.
O no recibe los datos, lo recibes a medias, o entre medios hace un salto de línea por la cara, hace cosas muy raras.
Código Arduino:
Código
#include <LiquidCrystal.h>
// Inicializa la librería con sus pines indicados
LiquidCrystal lcd(8, NULL, 9, 4, 5, 6, 7);
// Definir el pin para la luz de fondo y el LED
const byte LuzFondo = 10;
const byte Led = 13;
// Variable para almacenar el comando recibido
String comando = ""; // Asegúrate de declarar la variable de tipo String
char caracter;
void setup()
{
// Inicializa el LCD
lcd.begin(16, 2);
lcd.print("Esperando comando");
// Inicia la comunicación serial
Serial.begin(2400);
delay(1000); // Espera para asegurar que el LCD se inicialice
}
void loop()
{
// Mientras haya datos disponibles en el puerto serie, los leemos carácter a carácter
while (Serial.available() > 0)
{
caracter = Serial.read(); // Leer un carácter del puerto serie
comando.concat(caracter); // Concatenar el carácter al comando
delay(10); // Pequeña espera para no saturar el canal serie
}
// Verificamos si el comando es "B\r" o "X72\r" y respondemos en consecuencia
if (comando == "B\r")
{
digitalWrite(Led, HIGH); // Enciende el Led 13
Serial.write("#Comando B recibido\r"); // Envía el mensaje de vuelta a C#
lcd.setCursor(0, 0);
lcd.print("Comando B "); // Muestra en el LCD
}
else if (comando == "X72\r")
{
digitalWrite(Led, LOW); // Apaga el Led 13
Serial.write("#Comando X72 recibido\r"); // Envía el mensaje de vuelta a C#
lcd.setCursor(0, 0);
lcd.print("Comando X72 "); // Muestra en el LCD
}
else if (comando == "X5\r")
{
digitalWrite(Led, LOW); // Apaga el Led 13
Serial.write("#Comando X5 recibido\r"); // Envía el mensaje de vuelta a C#
lcd.setCursor(0, 0);
lcd.print("Comando X5 "); // Muestra en el LCD
}
// Limpiamos la cadena de comando para volver a recibir el siguiente comando
comando = "";
}
Código C#:
Código
using System.Text;
using System.IO.Ports;
namespace SAI_Arduino_LCD_01
{
public partial class Form1 : Form
{
private SerialPort puertoSerie;
private string lastReceivedData = ""; // Variable para guardar el último dato recibido
public Form1()
{
InitializeComponent();
InicializarPuertoSerie();
}
// Método para inicializar la configuración del puerto serie
private void InicializarPuertoSerie()
{
puertoSerie = new SerialPort()
{
BaudRate = 2400,
Parity = Parity.None, // Esquema para comprobar la paridad de cada byte recibido.
StopBits = StopBits.One, // Número de bits de parada por byte.
DataBits = 8, // Número de bits de datos por byte.
Handshake = Handshake.None, // Protocolo de establecimiento.
Encoding = Encoding.GetEncoding(28591), // Codificación.
DtrEnable = true, // Línea de terminal de datos.
RtsEnable = true, // Línea de solicitud.
ReadTimeout = 500, // Tiempo de espera de lectura en ms.
WriteTimeout = 500, // Tiempo de espera de escritura en ms.
DiscardNull = false, // Descartar bytes nulos recibidos.
ParityReplace = 63, // Reemplaza los bytes recibidos con errores de paridad.
ReadBufferSize = 4096, // Tamaño del búfer de lectura en bytes.
WriteBufferSize = 2018, // Tamaño del búfer de escritura en bytes.
ReceivedBytesThreshold = 1 // Número de bytes que se necesitan.
};
puertoSerie.DataReceived += PuertoSerie_DataReceived; // Suscribimos al evento
}
// Evento que se activa cuando se reciben datos del puerto serie
private void PuertoSerie_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{
// Verificamos si el puerto está abierto
if (!puertoSerie.IsOpen)
{
ActualizarRichTextBox("Puerto no abierto.");
return;
}
// Leer todos los datos disponibles hasta que no haya más
string data = puertoSerie.ReadExisting(); // Usamos ReadExisting para leer todo
// Solo actualizar si los datos recibidos son diferentes de los anteriores
if (!string.IsNullOrEmpty(data) && data != lastReceivedData)
{
lastReceivedData = data; // Actualizar el último dato recibido
ActualizarRichTextBox(data); // Mostrar los datos recibidos
}
}
catch (TimeoutException)
{
// Si hay un Timeout, podemos ignorar y no mostrar nada
}
catch (Exception ex)
{
ActualizarRichTextBox("Error: " + ex.Message + "\r\n");
}
}
// Método para actualizar el RichTextBox de manera segura desde cualquier hilo
private void ActualizarRichTextBox(string mensaje)
{
if (InvokeRequired)
{
Invoke(new Action<string>(ActualizarRichTextBox), mensaje);
}
else
{
richTextBox1.AppendText(mensaje + "\r\n");
}
}
// Método para enviar un comando por el puerto serie
private void EnviarComando(string comando)
{
try
{
// Verificar si el puerto está abierto
if (!puertoSerie.IsOpen)
{
puertoSerie.Open();
}
// Enviar cada carácter del comando como valor ASCII
foreach (char c in comando)
{
puertoSerie.Write(new byte[] { (byte)c }, 0, 1); // Escribir un byte correspondiente al carácter ASCII
}
// Enviar el retorno de carro \r como un byte
puertoSerie.Write(new byte[] { 13 }, 0, 1); // ASCII de '\r' es 13
// Esperar un breve tiempo para asegurar que Arduino procese el comando antes de continuar
Thread.Sleep(200); // Ajusta el tiempo según sea necesario
ActualizarRichTextBox("Enviado: " + comando + "\r\n");
}
catch (UnauthorizedAccessException ex)
{
MessageBox.Show("El puerto COM está en uso por otro proceso. Error: " + ex.Message);
}
catch (Exception ex)
{
MessageBox.Show("Error al intentar enviar el comando: " + ex.Message);
}
}
// Cerrar el puerto de manera segura cuando se cierre el formulario
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (puertoSerie.IsOpen)
{
try
{
puertoSerie.Close();
}
catch (Exception ex)
{
MessageBox.Show("Error al cerrar el puerto: " + ex.Message);
}
}
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
string[] puertos = SerialPort.GetPortNames();
comboBox_Puertos.Items.AddRange(puertos); // Llenar el ComboBox con los puertos disponibles
if (comboBox_Puertos.Items.Count > 0)
{
comboBox_Puertos.SelectedIndex = 0; // Seleccionar el primer puerto automáticamente
}
}
catch (Exception ex)
{
MessageBox.Show("Error al cargar los puertos: " + ex.Message);
}
}
// Método para conectar con el puerto seleccionado
private void btnConectar_Click(object sender, EventArgs e)
{
if (comboBox_Puertos.SelectedItem == null)
{
MessageBox.Show("Por favor, selecciona un puerto COM antes de intentar conectar.");
return;
}
string puertoSeleccionado = comboBox_Puertos.SelectedItem.ToString();
puertoSerie.PortName = puertoSeleccionado; // Configurar el puerto
try
{
// Verificar si el puerto ya está abierto y cerrarlo si es necesario
if (puertoSerie.IsOpen)
{
puertoSerie.Close();
ActualizarRichTextBox("Puerto cerrado previamente.\r\n");
}
// Abrir el puerto
puertoSerie.Open();
ActualizarRichTextBox("Conexión exitosa con " + puertoSeleccionado + "\r\n");
}
catch (Exception ex)
{
MessageBox.Show("Error al abrir el puerto: " + ex.Message);
}
}
// Métodos de comando
private void button_Comando_B_Click(object sender, EventArgs e) => EnviarComando("B");
private void button_Comando_X72_Click(object sender, EventArgs e) => EnviarComando("X72");
private void button_Comando_X5_Click(object sender, EventArgs e) => EnviarComando("X5");
}
}
Con RealTerm, Putty y otros, funciona bien.
¿En qué estoy fallando?
Un cordial saludo y muchas gracias por su tiempo.
EdePC:
Cambia tu línea 55:
Código
string data = puertoSerie.ReadExisting(); // Usamos ReadExisting para leer todo
por:
Código
string data = puertoSerie.ReadTo("\r");
Si usas ReadExisting como dice, lees lo que haya hasta ese momento y lo imprimes, por eso a veces sale en trozos
Si usas ReadTo("\r") lees hasta encontrar "\r" y recién imprimes
Si te das cuenta en RealTerm cuando envías el comando y empieza a mostrar el resultado, a veces también tiene un retardo y sale en trozos, pero como está todo seguido (sin saltos de línea) no se ve al finalizar, tu código si pone saltos de línea al imprimir los trozos y por eso se ve raro
Navegación