none
Tramas de byte recibida por el puerto serie en realidad es un archivo y quiero copiarlo al disco duro RRS feed

  • Pregunta

  • Hola:

    Tengo un código base de leer el puerto serie de otro PC. Como dice el título, debo recogerlo en memoria o como sea para luego crear un archivo.jpg en este caso.

    Código base no acabado y mal hecho:

    using System;
    using System.IO;
    using System.IO.Ports;
    using System.Text;
    
    namespace Recibir_archivo_desde_Arduino_consola
    {
        class Program
        {
            public static string Recibidos = "";
            public static byte[] datosArray = Encoding.ASCII.GetBytes(Recibidos);
    
            static void Main(string[] args)
            {
                string COM = "";
    
                // Tamaño ventana consola.
                Console.WindowWidth = 55; // X. Ancho.
                Console.WindowHeight = 15; // Y. Alto.
                Console.Title = "Serial Port C# - v.02"; // 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.
                Puerto_serie.Parity = Parity.None; // Paridad.
                Puerto_serie.DataBits = 8; // Bits de datos.
                Puerto_serie.StopBits = StopBits.Two; // Bits de parada.
                Puerto_serie.Handshake = Handshake.None; // Control de flujo.
    
                // Establecer la lectura / escritura de los tiempos de espera.
                Puerto_serie.ReadTimeout = 500;
                Puerto_serie.WriteTimeout = 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.");
                }
    
                Puerto_serie.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
    
                Console.Read();
                Puerto_serie.Close(); // Cerrar puerto.
            }
    
            private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    
            {
                SerialPort sp = (SerialPort)sender;
                Recibidos = sp.ReadExisting();
                //Console.Clear();
    
                //Recibidos = Recibidos.Replace("\n\r", "");
    
                // int Variar_este_valor = Convert.ToInt32(Recibidos);
    
                // datosArray = Recibidos;
            }
        }
    }


    No se como almacenar los datos recibidos de tal magnitud.

    ¿Cómo se hace?

    Una vez cuando tenga los datos ya almacenado en memoria, pues lo paso al disco duro y se hace así:

    File.WriteAllBytes("fotón.jpg", Recibidos); // Puede ser otra variable y me da que tiene que ser tipo byte[].

    Una vez en el disco duro el archivo en este caso fotón.jpg, se abre así:

    Process.Start("fotón.jpg");

    El problema que no se como almacenarlo los datos recibidos.

    ¿Alguna idea?

    Faliz año nuevo 2017.


    http://electronica-pic.blogspot.com





    • Editado Metaconta lunes, 2 de enero de 2017 6:45
    lunes, 2 de enero de 2017 5:40

Respuestas

  • La verdad es que nunca he usado los timeouts del SerialPort. Puedes experimentar un poco con ellos a ver como funcionan, o abrir una nueva pregunta en el foro a ver si alguien que conozca el tema puede decirte cómo usarlos. Me imagino que la propiedad ReadTimeout afectará a las llamadas bloqueantes, mientras que las no-bloqueantes retornarán inmediatamente devolviendo cero bytes, cosa que podrás comprobar con un "if" y en ese caso controlar el timeout manualmente usando el reloj del sistema dentro del bucle en el que estás leyendo los bytes. Pero todo esto son ideas generales que no he probado; yo recomendaría que experimentes con las distintas opciones hasta ver cuál te funciona mejor. O si te lías y no te salen los experimentos, abre una nueva pregunta en el foro preguntando expresamente sobre los timeouts del puerto serie. Tal vez alguien que los haya probado pueda indicarte la mejor manera de usarlos, y te evitas tener que hacer las pruebas.
    • Marcado como respuesta Metaconta martes, 3 de enero de 2017 11:15
    martes, 3 de enero de 2017 10:44

Todas las respuestas

  • Una duda que no queda clara a la vista del código que has puesto es cómo detectas que se ha terminado de recibir tu archivo. Es decir, qué criterio sigues para saber cuándo se acaba. Dependiendo de cómo lo estés enviando, puede ser (por ejemplo) que lo primero se envíe la longitud del archivo, y luego los bytes, y se sabe cuándo ha terminado de recibirse contando los bytes. Otra forma de hacerlo es enviar al final un carácter de "fin de fichero", pero esto no valdría en el caso del fichero .jpg porque puede contener todos los posibles valores de bytes, así que no existe ninguno que se pueda usar para indicar que hemos terminado. Otra opción es dividir el archivo en tramas que se envían con cierto prefijo de inicio y de fin, y cierta codificación en los bytes, y que exista una trama específica para indicar el final. Y otra opción es "cerrar" el puerto serie remitente cuando se termina el envío, lo cual hará cambiar el estado de alguno de los hilos del puerto (tal como el DTR) y en el lado receptor detectar el cambio de ese hilo para interpretar que la transmisión finalizó. Nótese que esto último impone requisitos en cuanto al cableado del puerto, necesitándose que la línea correspondiente esté debidamente conectada entre ambos equipos (no bastaría con los pines de datos).

    Así que, ¿cómo funciona tu protocolo de envío? ¿Qué haces en el lado remitente para indicarle al receptor que el fichero ya ha terminado de enviarse?

    Una vez que tengas respuesta a eso, una de las cosas que puedes hacer es repetir la instrucción que ya tienes: Recibidos = sp.ReadExisting(); salvando "Recibidos" (lo puedes enviar directamente al fichero de destino con la instrucción Write de un FileStream) y ejecutándola en un bucle hasta que se termine la recepción del fichero (donde lo de "se termine" depende de la respuesta anterior).

    lunes, 2 de enero de 2017 8:59
  • Hola:

    Como puedes ver, no está acabado y es muy buena idea lo que indicas. El archivo está almacenado en un microcontrolador Arduino, lo metí dentro, se puede enviar el archivo pero en C# se ve carácteres ASCII, pero al menos se muestra en "Recibidos".

    No he puesto nada de saber cuando termina.

    Solo uso los cables de datos, Rx y Tx.

    Esbueno tener un contador de Bytes aunque sea solo para ver.

    Hay que buscar la mejor solución cuando acaba los datos, simplemente deja de recibir datos alguno desde el puerto serie.

    Pues si, es buena idea cerrar el puerto cuado acaba los datos.

    Si hay que poner algo de inicio como START cuando empieza recibiendo las tramas de bytes, y piner al final encontes STOP cuando se finaliza. Este último que me dijiste me gustó.

    Más datos que te puedo decir es, que la codificación se envía en hex desde arduino, tal cual lo que puedes ver en la foto con un editor hexadecimal.

    Si falta algo más que comentar, adelante. ;)

    No esperaba que esto fuera muy majadero para hacerlo.

    Feliz año nuevo 2017.


    http://electronica-pic.blogspot.com

    lunes, 2 de enero de 2017 9:22
  • Hay que buscar la mejor solución cuando acaba los datos, simplemente deja de recibir datos alguno desde el puerto serie.

    Lo de "simplemente deja de recibir" podrías controlarlo con un "timeout", es decir, controlar cuánto tiempo ha pasado desde la última vez que se recibió algo y si pasan más de x milisegundos sin que se reciba nada (donde x tendrás que calcularlo en función del baud rate de la transmisión) entonces entender que ha terminado la transmisión.

    la codificación se envía en hex desde arduino, tal cual lo que puedes ver en la foto con un editor hexadecimal.

    Ojo, esto hay que clarificarlo (ya que no se ve ninguna foto en tu mensaje). ¿Se transmite realmente el hex (es decir dos bytes ASCII por cada byte binario del fichero) o se transmite el binario equivalente a ese hex, y la única razón de que se ve hex es porque el editor binario convierte el binario a hex a la hora de visualizarlo? Esto es importante, porque en el primer caso no puedes salvar el hex directamente al fichero, primero hay que convertirlo de vuelta a binario. En cambio, en el segundo caso, no puedes usar el método "ReadExisting" del serialport porque solo funciona con texto, no con binarios. En su lugar podrías usar Read:

    https://msdn.microsoft.com/es-es/library/ms143549(v=vs.110).aspx

    lunes, 2 de enero de 2017 10:33
  • Hola y gracias por la aclaración.

    Los datos vienen de binario y se muestra en HEX. No está codificado en nada. Me interesa poner un contador de byte recibidos como lectura y como curiosidad para saber cuanto recibo.

    La imagen que digo es esta.

    Los datos introducidos de una foro se muestra estos hex y precisamente estos los recibe C#.

    Por lo que cuentas, tengo que modificar el código por todas partes. ;)

    Buena idea lo de esperar con el timeout.

    Donde puse este código que no sirve.

                SerialPort sp = (SerialPort)sender;
                Recibidos = sp.ReadExisting();

    Puse el que me dijiste.

                SerialPort sp = (SerialPort)sender;
                Recibidos = sp.Read()

    Gravedad    Código    Descripción    Proyecto    Archivo    Línea
    Error    CS1501    Ninguna sobrecarga para el método 'Read' toma 0 argumentos    Recibir_archivo_desde_Arduino_consola    C:\Users\Meta\Documents\Visual Studio 2015\Projects\Recibir_archivo_desde_Arduino_consola\Recibir_archivo_desde_Arduino_consola\Program.cs    77

    Recibidos = sp.Read(datosArray, 0, datosArray.Length);

    Todavía no capto el read como hacerlo. ;)


    Saludos.


    http://electronica-pic.blogspot.com



    • Editado Metaconta lunes, 2 de enero de 2017 11:21
    lunes, 2 de enero de 2017 10:45
  • El Read es más o menos así:

    int bytesLeidos = sp.Read(nombreDelArray, posicion, numeroQueSeDeseaLeer);

    Es decir, le pides un determinado número de bytes, y le dices en qué posición del array quieres guardarlos. Te contesta con el número que ha conseguido leer hasta ese momento, que normalmente será mucho menor que los que le has pedido (por ejemplo, si esperas que tu imagen mida 10000 bytes, le pides los 10000, pero seguramente te contestará que solo ha leído 16 (o menos), que son los que caben en el buffer de la UART. Luego tienes que repetirlo en un bucle, y pedirle los bytes que te falten (10000-16), y grabarlos en la posición que toque en el array (la 16), y así sucesivamente hasta que se te acaben los bytes. Ojo, lo de "16" es un ejemplo, tendrás que usar el número que realmente te devuelva en bytesLeidos.

    lunes, 2 de enero de 2017 14:53
  • Arduino UNO,usa 64 byte de buffer.

    Voy hacer pruebas y te digo.


    http://electronica-pic.blogspot.com

    lunes, 2 de enero de 2017 19:41
  • Hola de nuevo:

    Me da error.

                SerialPort sp = (SerialPort)sender;
                int ByteLeidos = sp.Read(datosArray, 0, 28256);
    
                Console.Write(ByteLeidos);

    Exactamente puse los mismos bytes que dice aquí.

    using System;
    using System.IO;
    using System.IO.Ports;
    using System.Text;
    
    namespace Recibir_archivo_desde_Arduino_consola
    {
        class Program
        {
            public static string Recibidos = "";
            public static byte[] datosArray = Encoding.ASCII.GetBytes(Recibidos); // Esta variable la dejo por si hace falta.
    
            static void Main(string[] args)
            {
                string COM = "";
    
                // Tamaño ventana consola.
                Console.WindowWidth = 55; // X. Ancho.
                Console.WindowHeight = 15; // 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.
                Puerto_serie.Parity = Parity.None; // Paridad.
                Puerto_serie.DataBits = 8; // Bits de datos.
                Puerto_serie.StopBits = StopBits.Two; // Bits de parada.
                Puerto_serie.Handshake = Handshake.None; // Control de flujo.
    
                // Establecer la lectura / escritura de los tiempos de espera.
                Puerto_serie.ReadTimeout = 500;
                Puerto_serie.WriteTimeout = 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.");
                }
    
                Puerto_serie.DataReceived += new SerialDataReceivedEventHandler(DataReceivedHandler);
                
                Console.Read();
                Puerto_serie.Close(); // Cerrar puerto.
            }
    
            private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
    
            {
                SerialPort sp = (SerialPort)sender;
                int ByteLeidos = sp.Read(datosArray, 0, 28256);
    
                Console.Write(ByteLeidos); // Mostrar bayte recibidos.
    
         
            }
        }
    }


    http://electronica-pic.blogspot.com



    • Editado Metaconta martes, 3 de enero de 2017 2:35
    martes, 3 de enero de 2017 1:35
  • El mensaje de error que te sale indica que no está bien dimensionada la variable datosArray. Le has pedido que meta 28256 bytes dentro del Array, pero el array lo has dimensionado para menos de 28256 bytes.
    martes, 3 de enero de 2017 8:08
  • Buenas:

    Estoy perdido.

    ¿Alguna idea?

    Lo que se supone que estamos haciendo para contar los bytesy mostarlo en pantalla. Recuerda que puedo recibir cualquier archivo de diferentes dimensiones. Para eso indicar con el outime cuando dejen de transmitir.

    Me imagino que aquí es a lo que te referías antes.

    Serie.ReadTimeout = 10       'Fuera de tiempo en receción

    Más información.

    http://blogs.msmvps.com/peplluis/2006/11/21/principales-mandatos-para-utilizar-el-puerto-serie-del-espacio-system-io-ports/

    Saludos.


    http://electronica-pic.blogspot.com





    • Editado Metaconta martes, 3 de enero de 2017 10:01
    martes, 3 de enero de 2017 9:17
  • La verdad es que nunca he usado los timeouts del SerialPort. Puedes experimentar un poco con ellos a ver como funcionan, o abrir una nueva pregunta en el foro a ver si alguien que conozca el tema puede decirte cómo usarlos. Me imagino que la propiedad ReadTimeout afectará a las llamadas bloqueantes, mientras que las no-bloqueantes retornarán inmediatamente devolviendo cero bytes, cosa que podrás comprobar con un "if" y en ese caso controlar el timeout manualmente usando el reloj del sistema dentro del bucle en el que estás leyendo los bytes. Pero todo esto son ideas generales que no he probado; yo recomendaría que experimentes con las distintas opciones hasta ver cuál te funciona mejor. O si te lías y no te salen los experimentos, abre una nueva pregunta en el foro preguntando expresamente sobre los timeouts del puerto serie. Tal vez alguien que los haya probado pueda indicarte la mejor manera de usarlos, y te evitas tener que hacer las pruebas.
    • Marcado como respuesta Metaconta martes, 3 de enero de 2017 11:15
    martes, 3 de enero de 2017 10:44