|
1
|
Programación / .NET (C#, VB.NET, ASP) / Leer bien los datos recibidos.
|
en: 15 Marzo 2025, 20:37 pm
|
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:#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#: 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.
|
|
|
3
|
Programación / .NET (C#, VB.NET, ASP) / El depurador ahora muestra valores devueltos insertados con ayuda de IA para mejorar la eficacia.
|
en: 14 Noviembre 2024, 10:50 am
|
Hola gente: ¿Cómo están todos?  Actualizado el Visual Studio Community 2022 (Gratuito), por ahora no se ha nombrado uno nuevo completo. Una de las novedades de ahora es esto indicado abajo. El depurador de Visual Studio muestra ahora valores insertados para las instrucciones de devolución, respondiendo así a una de las características más solicitadas por la comunidad de desarrolladores.
Esta mejora le permite ver los valores exactos que devuelven las funciones directamente en el código, eliminando la necesidad de código adicional o variables temporales para inspeccionar los valores de devolución.
 Valores devueltos insertados
Con GitHub Copilot, puede ir más allá utilizando la opción Preguntar a Copilot al mantener el mouse para analizar los valores devueltos directamente en Visual Studio, lo que le permite abordar los problemas de inmediato.
Se admite tanto en código nativo como administrado.Por curiosidad hice este ejemplo en C# y no me funciona. using System; namespace Metodo_Consola_01 { internal class Program { static void Main(string[] args) { int resultado = Sumar(5, 10); Console.WriteLine($"El resultado de la suma es: {resultado}."); // Pulse cualquier tecla para salir. Console.ReadKey(); } static int Sumar(int a, int b) { return a + b; } } }
Al compilar funciona y el resultado da 15. Se trata de pasar el ratón por encima del IDE sin compilar nada y muestra los resultados. A mi no me funciona ni a la de tres. ¿A alguien le ha funcionado? Gracias.
|
|
|
5
|
Programación / Programación C/C++ / Re: Controlar variable
|
en: 2 Noviembre 2024, 10:24 am
|
Viendo este enlace. https://learn.microsoft.com/es-es/cpp/error-messages/compiler-warnings/compiler-warning-level-3-c4996?view=msvc-170&f1url=%3FappId%3DDev17IDEF1%26l%3DES-ES%26k%3Dk(C4996)%26rd%3Dtrue cambiar de strncpy a este strncpy_s. Solo tenías que haberlo dicho así tal cual.  Aún así, muchas gracias, ahora compila. 1. Uso de strncpy_s : Esta función toma como argumentos el destino, el tamaño del destino, la cadena de origen y un valor que indica cómo manejar el truncamiento. Usar _TRUNCATE asegura que la cadena se trunque si es demasiado larga. 2. Terminación de la cadena: La línea lectura[n] = '\0'; puede no ser necesaria si strncpy_s ya se encarga de ello, pero si n es menor que la longitud de la cadena copiada, asegúrate de que la cadena esté correctamente terminada. 3. Incluir : Asegúrate de incluir la cabecera para poder usar cout . Si prefieres desactivar la advertencia en lugar de cambiar el código, puedes agregar #define _CRT_SECURE_NO_WARNINGS al inicio de tu archivo, pero no es la mejor práctica, ya que podrías perder las ventajas 
|
|
|
7
|
Programación / Programación C/C++ / Re: Controlar variable
|
en: 1 Noviembre 2024, 11:39 am
|
Hice un ejemplo pero como que no. int main() { char lectura[50] = { 0 }; // Supongamos que has recibido datos y n es el número de bytes leídos int n = 20; // Por ejemplo, supongamos que leíste 20 bytes strncpy(lectura, "ON - Led encendido.", sizeof(lectura) - 1); // Simulando la lectura lectura[n] = '\0'; // Asegúrate de terminar la cadena // Ahora puedes comparar la cadena if (strcmp(lectura, "ON - Led encendido.") == 0) { cout << "Recibido: " << lectura << endl; } else { cout << "No se recibió el mensaje esperado." << endl; } return 0; }
Gravedad Código Descripción Proyecto Archivo Línea Estado suprimido Detalles Error C4996 'strncpy': This function or variable may be unsafe. Consider using strncpy_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. Datos de entrada puerto serie CPP nativo Consola 01 D:\Visual Studio 2022\Datos de entrada puerto serie CPP nativo Consola 01\Datos de entrada puerto serie CPP nativo Consola 01\Datos de entrada puerto serie CPP nativo Consola 01.cpp 51
|
|
|
8
|
Programación / Programación C/C++ / Controlar variable
|
en: 31 Octubre 2024, 18:15 pm
|
Tengo esta variable declarada en C++ nativo con Visual Community 2022. char lectura[50] = { 0 };
Al recibir datos, parece que como máximo recibo 50 caracteres o bytes. if (n > 0) { lectura[n] = '\0'; // Finaliza la cadena. cout << "Recibido: " << lectura << endl; cout << "-------------------" << endl; }
El dato que recibo por ejemplo debe ser exactamente esto: ON - Led encendido.Si miro la variable en el IDE, me muestra esto: Ver zoom. Al recibir esos datos quiero hacer algo como esto: if (lectura == "ON - Led encendido.") { cout << "Recibido: " << lectura << endl; }
¿Hay alguna manera de quitar los 0'\0' datos restantes que molestan? Saludos.
|
|
|
9
|
Programación / Programación C/C++ / Usar switch o otra manera para poner en gotoXY textos
|
en: 29 Octubre 2024, 07:01 am
|
Buenas camaradas: Usando este código. Recibo datos por el puerto serie. // Lee datos del puerto serie. int n = Puerto->ReadData(lectura, sizeof(lectura) - 1); // Recibe datos del puerto serie. if (n > 0) { lectura[n] = '\0'; // Finaliza la cadena. switch (lectura) { default: break; } cout << "Recibido: " << lectura << endl; cout << "-------------------" << endl; }
Justo en switch (lectura) me da error de este tipo. Gravedad Código Descripción Proyecto Archivo Línea Estado suprimido Detalles Error C2450 una expresión switch de tipo "char [50]" no es válida Arduino y puerto serie CPP Consola 01 D:\Visual Studio 2022\Arduino y puerto serie CPP Consola 01\Arduino y puerto serie CPP Consola 01\Arduino y puerto serie CPP Consola 01.cpp 114 Dentro de los case quiero poner sus coordenadas para poner los textos en cualquier zona de la pantalla con gotoXY que es este. // Función posición del cursor. void gotoxy(int ancho_x, int alto_y) { HANDLE hcon = GetStdHandle(STD_OUTPUT_HANDLE); COORD dwPos{}; dwPos.X = ancho_x; dwPos.Y = alto_y; SetConsoleCursorPosition(hcon, dwPos); }
¿Alguna solución? Código C++ nativo completo hecho con Visual Studio 2022. #include <iostream> #include <windows.h> // Para mostrar texto en el título de la ventana. #include "SerialClass.h" using namespace std; //using std::cout; //using std::cin; void ConfigurarConsola() { #pragma region "Configuración ventana." // Mostrar caracteres correctamente en pantalla y título de la ventana. SetConsoleOutputCP(CP_UTF8); wchar_t titulo[128]; MultiByteToWideChar(CP_UTF8, 0, "Título: Arduino puerto serie - C++ nativo", -1, titulo, 128); SetConsoleTitleW(titulo); // Tamaño de la pantalla. Se cambia en los dos últimos dígitos. SMALL_RECT r = { 0, 0, 120, 40 }; // X = 80, Y = 20. SetConsoleWindowInfo(GetStdHandle(STD_OUTPUT_HANDLE), TRUE, &r); // Cambio color de fondo 6 (amarillo / naranja), color letras 0 (negro). system("color 60"); // Color de fonde y texto. // Ocultar cursor. CONSOLE_CURSOR_INFO cci; GetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci); cci.bVisible = FALSE; // FALSE oculta. TRUE muestra cursor. SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cci); #pragma endregion } // Función posición del cursor. void gotoxy(int ancho_x, int alto_y) { HANDLE hcon = GetStdHandle(STD_OUTPUT_HANDLE); COORD dwPos{}; dwPos.X = ancho_x; dwPos.Y = alto_y; SetConsoleCursorPosition(hcon, dwPos); } int main() { // Configura consola. ConfigurarConsola(); // Inicializa el puerto serie. Serial* Puerto = new Serial("COM2"); if (!Puerto->IsConnected()) { cerr << "Error al conectar con el puerto serie." << endl; delete Puerto; return 1; } // Comandos para Arduino. char Luz_ON[] = "Luz_ON"; // Envía "Luz_ON" al puerto serie. char Luz_OFF[] = "Luz_OFF"; char Salir[] = "Salir"; char lectura[50] = { 0 }; // Guardan datos de entrada del puerto leído. //int tecla; // Guarda un 1 ó 2 tipo entero que introduces desde la consola. cout << "Pulse 1 para encender el Led, pulse 2 para apagar. (Ctrl+C para salir)" << endl; while (true) { // Detecta si se ha pulsado una tecla. Desde que pulse una tecla, automáticamente envía el comando. // ¿Has pulsado la tecla 1 ó 1 extendido? if (GetAsyncKeyState('1') & 0x8000 || GetAsyncKeyState(VK_NUMPAD1) & 0x8000) // Si se presiona '1' o 'Numpad 1'. { // Sí. Envía este comando. cout << "Enviando: " << Luz_ON << endl; Puerto->WriteData(Luz_ON, strlen(Luz_ON)); Sleep(500); // Espera un momento para evitar múltiples envíos. } if (GetAsyncKeyState('2') & 0x8000 || GetAsyncKeyState(VK_NUMPAD2) & 0x8000) // Si se presiona '2' o 'Numpad 2'. { cout << "Enviando: " << Luz_OFF << endl; Puerto->WriteData(Luz_OFF, strlen(Luz_OFF)); Sleep(500); // Espera un momento para evitar múltiples envíos. } if (GetAsyncKeyState('3') & 0x8000 || GetAsyncKeyState(VK_NUMPAD3) & 0x8000) { cout << "Enviando: " << Salir << endl; Puerto->WriteData(Salir, strlen(Salir)); Sleep(500); // Espera un momento para evitar múltiples envíos. } // Lee datos del puerto serie. int n = Puerto->ReadData(lectura, sizeof(lectura) - 1); // Recibe datos del puerto serie. if (n > 0) { lectura[n] = '\0'; // Finaliza la cadena. cout << "Recibido: " << lectura << endl; cout << "-------------------" << endl; } Sleep(100); // Reduce la carga de CPU. } delete Puerto; // Libera memoria. return 0; }
Saludos.
|
|
|
|
|
|
|