Leer datos desde el puerto serie de una SAI / UPS

Si querés seguir trabajando los datos como string en vez de un array de bytes como te sugerí, probá que pasa si le imponés el encoding que vas a utilzar, te sugiero el cp437:

C#:
...
            Encoding cp437 = Encoding.GetEncoding(437);

            puertoSerie = new SerialPort()
            {
                // Configuración del puerto serie.
                PortName = "COM3",           // Nombre del puerto serie.
                BaudRate = 2400,             // Velocidad en baudios.
                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.
                DtrEnable = true,            // Línea de terminal de datos.
                RtsEnable = true,            // Línea de solicitud.

                // Establecer los tiempos de espera de lectura / escritura.
                ReadTimeout = 500,           // Tiempo de espera de escritura en ms.
                WriteTimeout = 500,          // Tiempo de espera de escritura en ms.

                // Más configuraciones.
                DiscardNull = false,         // Descartar bytes nulos recibidos.
                ParityReplace = 63,
                ReadBufferSize = 4096,
                WriteBufferSize = 2018,
                ReceivedBytesThreshold = 1,
                Encoding = cp437
            };
...

Y después cuando ya tengas el string de la "S", convertilo a bytes usando el objeto cp437 como hice en el mensaje anterior.
 
Estaba haciendo pruebas haber que pasa. Lo que si haré lo que dices, todo a byte a byte que es mejor, lo que sugeriste.
Haga lo que haga con el:
C#:
Encoding cp437 = Encoding.GetEncoding(437);

Me sale este error.
aaaaaaaa.JPG
 
Última edición:
Probá con Windows-1252:

C#:
...
Encoding w1252 = Encoding.GetEncoding(1252);
...
aaaaaaaa.JPG

En estos momentos estoy averiguando capturar la famosa S y pasarlo de binario a decimal que en realidad sería partido así.

165
132
128
136
132
192

Hay 6 bytes.

Como puse en este post con código incluido, introduzco cualquier código en decimal y me aparece los bits activos y no.

sai-leer-bit-de-un-byte-jpg.307031

Esto ya es haciendo pruebas para entender las cosas y luego trabajar como debe ser.

Saludos.
 
Google, puede ser un buen amigo:


Si estás en .Net Core, fijate registrando los encoding:

Código:
using System.Text;

// Register the CodePages encoding provider at application startup to enable using single and double byte encodings.
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);


Vas a necesitar instalar el paquete de esa página.

Tengo un Visual Studio 22 instalado en otra PC, lo pruebo y te comento.
 
Última edición:
Pude recrear el error usando .Net 7.0, para resolverlo tuve que agregar una línea:

C#:
...
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); //Agregá esta línea

Encoding w1252 = Encoding.GetEncoding(1252);
...
 
Cualquier encoder o ninguno que ponga, siempre en salida[8] me da como resultado esto: ??????

SAI 6 incógnitas.JPG

Sigo investigando.
 
Última edición:
Cuando lo convertís en array de bytes ¿qué sucede? (esto es lo importante, no el string en si mismo)

Creo que con esto agoté todas las ideas que podía tener en el manejo de esa trama como string, si nada de esto funciona, yo optaría por usar el método Read en array de bytes que te brinda la clase del puerto serie.
 
Antes de optar por el Read que lo terminaré haciendo, he hecho esto en un programa a parte en modo consola haber que tal.

C#:
using System;
using System.Text;

namespace Leer_la_S_tramas_de_bytes_SAI_UPS_Consola_01
{
    internal class Program
    {
        static void Main(string[] args)
        {
            #region Configuración ventana.
            // Título de la ventana.
            Console.Title = "Leer tramas de bytes de la S";

            // Tamaño de la ventana, x, y, o ancho y alto.
            const byte ANCHO_X = 70, ALTO_Y = 25;
            Console.SetWindowSize(ANCHO_X, ALTO_Y);

            // Color de fondo.
            Console.BackgroundColor = ConsoleColor.White;

            // Color de las letras.
            Console.ForegroundColor = ConsoleColor.Black;

            // Limpiar pantalla y dejarlo todo en color de fondo.
            Console.Clear();

            // Visible el cursor.
            Console.CursorVisible = true;
            #endregion

            string entrada = "#I225.7O226.2L006B100V25.7F50.2H50.2R0080S€„€ˆ„\r";
            char[] separadores = { '#', 'I', 'O', 'L', 'B', 'V', 'F', 'H', 'R', 'S', '\r' };
            string[] salida = entrada.Split(separadores);

            for (int i = 2; i < 10; i++)
            {
                Console.WriteLine("> {0}", salida[i]);
            }

            foreach (uint b in Encoding.Default.GetBytes(salida[10]))
            {
                Console.WriteLine("> {0}", b);
            }

            // Pulse cualquie tecla para salir.
            Console.ReadKey();
        }
    }
}

aaaaaaaa.JPG


Deja que vaya teniendo otras ideas primero y haré lo que indicas.

Saludos.
 
Ya me salió en modo consola. Almacenar en un array tipo uint.

C#:
            string entrada = "#I225.7O226.2L006B100V25.7F50.2H50.2R0080S€„€ˆ„\r";
            char[] separadores = { '#', 'I', 'O', 'L', 'B', 'V', 'F', 'H', 'R', 'S', '\r' };
            string[] salida = entrada.Split(separadores);
            uint[] numeros = new uint[6];
            uint j = 0;

            for (int i = 2; i < 10; i++)
            {
                Console.WriteLine("> {0}", salida[i]);
            }

            foreach (uint b in Encoding.Default.GetBytes(salida[10]))
            {
                Console.WriteLine("> {0}", b);
                Console.WriteLine($"Array: {numeros[j] = b}");
                ++j;
            }

Lo curioso que no me sale en Windows Form, cosa más rara, deja ver si me sale y lo muestro.
 
He probado de todo, si l pongo en ASCII, UTF7, 8 y 32 siempre muestra 63 en todo el array.

Si lo hago en el Windows Form, ponga el que ponga siempre sale 63.

C#:
          uint[] numeros = new uint[6];
          uint j = 0;

          foreach (uint b in Encoding.Default.GetBytes(salida[8]))
          {
              //richTextBox1.Text += b.ToString();
              richTextBox1.Text += Environment.NewLine; // Nueva línea.
              richTextBox1.Text += $"Array: {numeros[j] = b}";
              ++j;
          }

Se ve que esto no cuadra. Visto lo visto a ponerme con el Read.

Menudo diseñador que hizo esto así. A parte de horas cosas como poner 480 minutos en vez de horas y minutos. Son muchas cosas caras que hicieron.

En impresoras fiscales, ups de APC, pesas con el protocolo RS232 no ocurre esto.
 
Ya no sé qué desastre están haciendo, a ver, prueba usar:
C#:
string buffer = SerialPort.ReadTo('S');
int[] flags = new int[6];
int index = 0;
int next = SerialPort.ReadByte();
while(next != '\r'){
  flags[index] = next;
  index++;
}
O combina esos 6 bytes en un int64, el punto a considerar es que esos son datos binarios, no caracteres, todos los válidos siempre deben iniciar con el bit7=1 por lo que el retorno de carro es completamente distinto, el índex te podría indicar cuantos bytes válidos se recibieron.
Este código puede tener problemas, revísalo primero, son casi las 3am, me iba a dormir así que solo puse la primera cosa que se me ocurrió.

¿Qué se supone que necesita procesar el dato?
No entiendo la finalidad de hacerlo con una string simulada cuando es un protocolo hibrido que incluye datos binarios aparte de caracteres, si lo piensas meter en un Arduino mejor ya trabaja en el protocolo directamente ahí porque habrá que cambiar demasiado código si tu simulación se hace en C# con las API a comparación del Arduino con su C++.
 
Última edición:
Ahora mismo no centrarse Arduino. Estoy enre el PC y la UPS.

Cuando todo funcione bien como debe ser, me meteré Arduino con un súper mega grande GLCD y la UPS.

Empezaré con el lcd normal de 16×2 y 20x4.

Cuando llegue a mi casa pruebo tu código.

Cuando se termine todo esto haré tal cual al modo read.

Hace años metí una imagen dentro de Arduino uno y lo capturada con c# por si quieren husmear. Hecho con read en c# y Serial.Write(

Me olvidé ponerte el código.
C#:
using System;
using System.Text;
using System.IO.Ports; // No olvidar.
using System.IO;
using System.Diagnostics;
using System.Threading;

namespace Recibir_archivo_desde_Arduino_consola_06
{
    class Program
    {
        static int cantidadBytes;
        static StringBuilder sb = new StringBuilder();

        static void Main(string[] args)
        {
            string COM = "";

            // Tamaño ventana consola.
            Console.WindowWidth = 55; // X. Ancho.
            Console.WindowHeight = 18; // Y. Alto.
            Console.Title = "Recoger foto desde Arduino y crearlo en el disco duro"; // Título de la ventana.

            // Crear un nuevo objeto SerialPort con la configuración predeterminada.
            SerialPort Puerto_serie = new SerialPort();

            // Configuración.
            Console.Write(@"
Introduzca un número para seleccionar puerto COM.
Por ejemplo el 4, sería COM4.

Puerto: ");
            COM = Console.ReadLine(); // Escribir el número del puerto.
            Console.Clear();

            Puerto_serie.PortName = "COM" + COM; // Número del puerto serie.


            Puerto_serie.BaudRate = 115200; // Baudios. 115200.
            Puerto_serie.Parity = Parity.None; // Paridad.
            Puerto_serie.DataBits = 8; // Bits de datos.
            Puerto_serie.StopBits = StopBits.One; // Bits de parada.
            Puerto_serie.Handshake = Handshake.None; // Control de flujo.

            // Establecer la lectura / escritura de los tiempos de espera.
            Puerto_serie.ReadTimeout = -1; // 500.
            Puerto_serie.WriteTimeout = -1; // 500.

            try
            {
                Puerto_serie.Open(); // Abrir el puerto serie.
            }

            catch (IOException)
            {
                Console.ForegroundColor = ConsoleColor.Red; // Texto en rojo.
                Console.CursorVisible = false;
                Console.SetCursorPosition(16, 6);
                Console.WriteLine(@"El puerto " + Puerto_serie.PortName + @" no existe
                o no lo encuentra.");
                Console.ReadKey(); // Pulse cualquier tecla para salir.
            }

            catch (UnauthorizedAccessException e)
            {
                Console.WriteLine(e);
            }
            Puerto_serie.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);

            Console.WriteLine("Esperando datos desde Arduino... \n");
            Console.ReadKey();
            Puerto_serie.Close(); // Cerrar puerto.
        }

        private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
        {
            try
            {
                SerialPort sp = (SerialPort)sender;
                string indata = sp.ReadExisting();
                string[] data = indata.Split('A');
                cantidadBytes = int.Parse(data[0]);


                switch (data[1].ToString())
                {
                    case "1":
                        Console.WriteLine("Tamaño: " + cantidadBytes + " Bytes.");
                        Console.WriteLine("Foto: " + data[1] + ". Tipo de archivo: JPG");
                        break;

                    case "2":
                        Console.WriteLine("Tamaño: " + cantidadBytes + " Bytes.");
                        Console.WriteLine("Foto: " + data[1] + ". Tipo de archivo: PNG.");
                        break;

                    default:
                        Console.WriteLine("Cosas raras en 'data': " + data.ToString()); // Por si hal algún error.
                        break;
                }

                int contador = 0;
                byte[] datosArray = new byte[cantidadBytes];

                //byte[] miBuffer = Encoding.ASCII.GetBytes("OK1");
                //sp.Write(miBuffer, 0, miBuffer.Length); // Envía OK1 a Arduino.

                switch (indata)
                {
                    case "17729A1":
                        Thread.Sleep(100); // Retardo.
                        byte[] miBuffer = Encoding.ASCII.GetBytes("OK1"); // Codificación ASCII.
                        sp.Write(miBuffer, 0, miBuffer.Length); // Envía OK1 al puerto serie.
                        break;

                    case "2065A2":
                        Thread.Sleep(100); // Retardo.
                        byte[] miBuffer2 = Encoding.ASCII.GetBytes("OK2"); // Codificación ASCII.
                        sp.Write(miBuffer2, 0, miBuffer2.Length); // Envía OK2 al puerto serie.
                        break;

                    default:
                        Console.WriteLine("Cosas raras en 'indata': " + indata.ToString()); // Por si hal algún error.
                        break;
                }

                while (true)
                {

                    contador += sp.Read(datosArray, contador, datosArray.Length - contador);

                    Console.SetCursorPosition(10, 6);
                    Console.Write("Datos recibidos:  {0}", contador + " Bytes.");
                    Console.WriteLine("                   ");

                    if ((contador == cantidadBytes) && (contador == 17729))
                    {
                        Mensaje1();
                        File.WriteAllBytes("fotón.jpg", datosArray); // Crear archivo en el disco duro.
                        Mensaje2();
                        Process.Start("fotón.jpg"); // Ejecutar visualizador de imágenes.
                        Mensaje3();
                        break; // Garantiza que el ciclo termine.
                    }

                    if ((contador == cantidadBytes) && (contador == 2065))
                    {
                        Mensaje1();
                        File.WriteAllBytes("fotón.png", datosArray); // Crear archivo en el disco duro.
                        Mensaje2();
                        Process.Start("fotón.png"); // Ejecutar visualizador de imágenes.
                        Mensaje3();
                        break; // Garantiza que el ciclo termine.
                    }
                }

                void Mensaje1()
                {
                    Console.WriteLine();
                    Console.WriteLine("Creando archivo al disco duro...");
                }

                void Mensaje2()
                {
                    Console.WriteLine();
                    Console.WriteLine("Archivo creado. Ejecutando imagen.");
                }

                void Mensaje3()
                {
                    Console.WriteLine();
                    Console.WriteLine("Imagen ejecutada.");
                    Console.WriteLine();
                    Console.WriteLine("Cabecera recibida: " + indata + "\n");
                    Console.ForegroundColor = ConsoleColor.Yellow; // Letras amarillas.
                    Console.WriteLine("FIN DE PROGRAMA.");
                    Console.ForegroundColor = ConsoleColor.Gray; // Letras grises otra vez.
                }
            }

            catch (FormatException)
            {
                // System.FormatException: 'La cadena de entrada no tiene el formato correcto.'
            }
        }
    }
}

Aquí si que hay un buen read.
 

Adjuntos

  • Recibir_archivo_desde_Arduino_consola_07.zip
    4 KB · Visitas: 2
Última edición:
Buenas:

Todavía sigo chamusqueándome las neuronas.

Teniendo estos resultados que viene tipo string, lo convierto en hexadecimal y en binario con Windows Form.
C#:
#I223.4O223.6L007B100V26.0F50.2H50.2R0080S??????

23493232332E344F3232332E364C303037423130305632362E304635302E324835302E325230303830533F3F3F3F3F3F0D

1000111001001110010110010110011101110110100100111111001011001011001110111011011010011001100001100001101111000010110001110000110000101011011001011011010111011000010001101101011100001011101100101001000110101110000101110110010101001011000011000011100011000010100111111111111111111111111111111111111111101

¿Cómo se puede capturar los datos exactamente sin editar tal como se recibieron tal como si se muestra abajo?

Precisamente son estos:
35 73 50 50 51 46 52 79 50 50 52 46 50 76 48 48 55 66 49 48 48 86 50 54 46 48 7
0 53 48 46 49 72 53 48 46 49 82 48 48 56 48 83 144 132 128 136 132 192 13

Ver imagen con un terminal que si lo hace.
SAI UPS captura datos tal como vienen.JPG
Lo marcado en rojo arriba es justo lo que quiero meter en un array para luego mostrarlo uno a uno en Windows form con su label correspondiente.

Espero que así se entienda mejor.

Código hasta ahora en Windows Form con C# es este:
C#:
private void Actualizar(object sender, EventArgs e)
{

    // Asignar el valor de la trama al richTextBox.
    richTextBox1.Text += recibidos;
    // Nueva línea.
    richTextBox1.Text += Environment.NewLine;

    // Pasar a hexadecimal.
    //foreach (byte b in recibidos)
    foreach (byte b in recibidos.Select(v => (byte)v))
    {
        // x = minúscula, X = mayúscula.
        richTextBox1.Text += b.ToString("X2");
    }

    // Nueva línea.
    richTextBox1.Text += Environment.NewLine;
    richTextBox1.Text += Environment.NewLine;

    // Pasar a binario.
    foreach (string leer in recibidos.Select(c => Convert.ToString(c, 2)))
    {
        richTextBox1.Text += leer.ToString();
    }

    // Nueva línea.
    richTextBox1.Text += Environment.NewLine;
    richTextBox1.Text += Environment.NewLine;

    // Selecciona la posición final para leer los mensajes entrantes.
    richTextBox1.SelectionStart = richTextBox1.Text.Length;

    // Mantiene el scroll en la entrada de cada mensaje.
    richTextBox1.ScrollToCaret();

    // Limpiar.
    recibidos = "";
}

Sigo investigando...

Saludos.

PD: Me cago en el ingeniero que tuvo la brillante idea que meter varios tipos de datos diferentes en una mista trama de caracteres por el puerto serie.
 
Última edición:
Uno literal le pone el código y prefiere complicarse.
En lugar de ReadExisting() que te lee hasta que se acabe, usa ReadTo('S') que lee hasta encontrar la S, luego se detiene, entonces ya tienes tu String y solo ejecutas lectura de ReadByte para los restantes. Se está complicando la vida queriendo leer todo el flujo completo y luego corregir el encoding resultante por ello.
 
He hecho una mini pureba con el Read. No me funciona y no encuentro el motivo.

Se trata de capturar y mostrar tal cual los bytes recibidos, almacenarlo en tipo byte[].

C#:
using System;
using System.IO.Ports;

namespace Almacenar_byte_puerto_serie_Consola_01
{
    internal class Program
    {
        static SerialPort puertoSerie;
        static byte[] datoRecibido;
        static void Main(string[] args)
        {
            puertoSerie = new SerialPort()
            {
                // Configuración del puerto serie.
                PortName = "COM4",           // Nombre del puerto serie.
                BaudRate = 2400,             // Velocidad en baudios.
                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.
                DtrEnable = true,            // Línea de terminal de datos.
                RtsEnable = true,            // Línea de solicitud.

                // Establecer los tiempos de espera de lectura / escritura.
                ReadTimeout = 500,           // Tiempo de espera de escritura en ms.
                WriteTimeout = 500,          // Tiempo de espera de escritura en ms.

                // Más configuraciones.
                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 += SerialPort_DataReceived;
            puertoSerie.Open();

            Console.WriteLine("Presiona cualquier tecla para detener la captura...");
            Console.ReadKey();

            puertoSerie.Close();

            Console.WriteLine("Datos recibidos:");
            PrintBytes(datoRecibido);
        }
        static void SerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            SerialPort sp = (SerialPort)sender;
            int bytesToRead = sp.BytesToRead;
            byte[] buffer = new byte[bytesToRead];
            sp.Read(buffer, 0, bytesToRead);

            if (datoRecibido == null)
            {
                datoRecibido = buffer;
            }
            else
            {
                Array.Resize(ref datoRecibido, datoRecibido.Length + bytesToRead);
                Array.Copy(buffer, 0, datoRecibido, datoRecibido.Length - bytesToRead, bytesToRead);
            }
        }

        static void PrintBytes(byte[] data)
        {
            for (int i = 0; i < data.Length; i++)
            {
                Console.Write($"{data[i]} ");
            }
            Console.WriteLine();
        }
    }
}

Como dicen que cuatro ojos ven mejor que dos, pueden ver que se me puede escapar algo que ahora mismo no capto.
 
Me parece que al ser nulo datoRecibido, cuando le asignás buffer, después se pierde al terminar el método.

A datoRecibido le asignaría un array desde el vamos y usaría ese mismo array para recibir los datos.
 
En cuanto al primer programa ya funciona, solo necesitaba tener el encoding adecuado y en la parte adecuada.

C#:
Encoding = Encoding.GetEncoding(28591),

Ejemplo:
C#:
                    // Configuración del puerto serie.
                    PortName = "COM3",           // Nombre del puerto serie.
                    BaudRate = 2400,             // Velocidad en baudios.
                    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),
                    DtrEnable = true,            // Línea de terminal de datos.
                    RtsEnable = true,            // Línea de solicitud.

                    // Establecer los tiempos de espera de lectura / escritura.
                    ReadTimeout = 500,           // Tiempo de espera de escritura en ms.
                    WriteTimeout = 500,          // Tiempo de espera de escritura en ms.

                    // Más configuraciones.
                    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.

Para evitar estar todo el rato con Encoding, mejor usar el read que dijiste.
Voy a averiguar porqué no me funciona.
 
Atrás
Arriba