none
Arreglar letras raras RRS feed

  • Pregunta

  • Hola:

    Cuando recibo datos por el puerto serie, todo lo que tenga tilde en alguna letra, se muestra con caracter raro. me sal emucho el ???????, los interrogantes estos.

            // Detecta cualquier dato entrante.
            private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
            {
                SerialPort sp = (SerialPort)sender;
                string entradaDatos = sp.ReadExisting(); // Almacena los datos recibidos en la variable tipo string.
                Console.WriteLine("Datos recibido desde Arduino: " + entradaDatos); // Muestra en pantalla los datos recibidos.
            }

    ¿Hay alguna manera de corregirlo con el códido al recibir datos?

    Lo que recibe son textos.

    Saludos.


    http://electronica-pic.blogspot.com

    jueves, 30 de julio de 2020 16:47

Todas las respuestas

  • El método ReadExisting convierte los bytes que le llegan en caracteres Unicode para meterlos en el String. Para poder hacer correctamente dicha conversión, necesita saber cuál es la codificación que traen esos bytes. Eso se le indica al SerialPort a través de la propiedad Encoding:

    SerialPort sp = (SerialPort)sender;
    sp.Encoding = System.Text.Encoding.GetEncoding(1252);
    string entradaDatos = sp.ReadExisting();

    En este ejemplo te he puesto el Encoding 1252 que es el Windows ANSI en español. Pero por supuesto si el aparato que te está enviando esos bytes usa otra codificación distinta, tienes que poner la codificación correcta en lugar del 1252. No contestes diciendo que no funciona y que te siguen saliendo mal los caracteres si copias el ejemplo a ciegas y dejas escrito el 1252 sin cambiarlo para que sea el valor correcto para tu dispositivo.

    https://docs.microsoft.com/en-us/dotnet/api/system.io.ports.serialport.encoding?view=dotnet-plat-ext-3.1

    • Propuesto como respuesta Sergio Parra viernes, 31 de julio de 2020 12:05
    • Marcado como respuesta Metaconta viernes, 31 de julio de 2020 13:58
    • Desmarcado como respuesta Metaconta viernes, 31 de julio de 2020 23:49
    jueves, 30 de julio de 2020 19:26
  • Hola:

    Viene los datos desde Arduino. Los datos son tipo write, byte.

    He probado:

    sp.Encoding = Encoding.GetEncoding("us-ascii");


    sp.Encoding = Encoding.GetEncoding("utf-32");


    Para que lo sepan. El código de Arduino es este.

    // Palabras del array para imprimir en pantalla.
    const String TEXTO[] = { "ATRÁS", "GUARDAR" };

    Se envía al puerto del PC con esto.

      // Muestra todos los caracteres del array
      for (int k = 0; k < 2; k++)
      {
        Serial.println(TEXTO[k]);
      }

    Saludos.


    http://electronica-pic.blogspot.com


    • Editado Metaconta viernes, 31 de julio de 2020 0:33
    viernes, 31 de julio de 2020 0:23
  • > Los datos son tipo write, byte.

    Ahí precisamente está el problema. No existe una única forma de convertir de String a byte. Hay varias codificaciones distintas, y según la codificación te salen diferentes bytes a partir de un mismo string. Cuando esos bytes se reciben en el otro extremo, es necesario saber cuál fue la codificación empleada en el origen para poder hacer correctamente el paso contrario, es decir, reconstruir el string a partir de los bytes.

    La clave está aquí:

    const String TEXTO[] = { "ATRÁS", "GUARDAR" };

    Estás escribiendo caracteres tal como Á (codificados según el encoding empleado por el editor de texto que estés usando en ese momento) y pasándoselos al compilador, que los guarda en un String empleando la codificación que utilice para los Strings el entorno de desarrollo que estés usando en ese momento. Y después, donde llamas a Serial.println, ese método tiene que convertir internamente los caracteres en bytes para enviarlos por la línea, usando una vez más el sistema de codificación que esa función tenga implementado internamente.

    No sé qué editor ni qué compilador usas para el Arduino. Si todo esto estuviera hecho en .Net, el resultado sería UTF-8, pero no tengo forma de saber qué es lo que usa tu editor de texto, los Strings de tu entorno de desarrollo, o esa función println a la que estás llamando.

    Si tienes alguna forma de capturar el valor binario de los bytes que se están transmitiendo, y nos los pones aquí, podemos tratar de inferir cual es la codificación comparándola con los tipos más comunes y conocidos. Por ejemplo, si la Á se transmite como un único byte cuyo valor es valor C1 (expresado en hexadecimal), podemos suponer que la codificación es ISO-8859-1 (o Windows 1252, que en su mayoría coincide con el ISO-8859-1), pero si es un B5 entonces inferiríamos el juego IBM850, por poner un par de ejemplos.

    • Propuesto como respuesta Sergio Parra viernes, 31 de julio de 2020 12:05
    • Marcado como respuesta Metaconta viernes, 31 de julio de 2020 13:58
    • Desmarcado como respuesta Metaconta viernes, 31 de julio de 2020 23:48
    viernes, 31 de julio de 2020 6:00
  • Hola:

    Los formatos están por aquí.

    http://manueldelgadocrespo.blogspot.com/p/serialprint.html

    A lo mejor es bueno guardarlo en otra forma. Copiés ATRÁS y GUARDAR de forma independiente, en dos variables y en tipo hexadecimal. Así que Arduinon lo enviará así. Dejo cl ecódigo completo de Arduino abajo.

    ¿No será recibirlo así desde el PC?

    C#:

    byte[] datosArray = new byte[cantidadBytes];

    Lo pregunto para que recibe todos los bytes, luego lo interpreta a la consola de C#.

    Arduino:

    #include <LiquidCrystal.h>
    
    // Inicializa la librería con sus pines indicados.
    LiquidCrystal lcd(8, NULL, 9, 4, 5, 6, 7);
    
    // Variables.
    const unsigned char atras[6] = {0x41, 0x54, 0x52, 0xC3, 0x81, 0x53}; // ATRÁS
    const unsigned char guardar[7] = {0x47, 0x55, 0x41, 0x52, 0x44, 0x41, 0x52}; // GUARDAR
    
    
    void setup()
    {
      // La pantalla es de 16 caracteres y 2 filas.
      lcd.begin(16, 2);
      lcd.print(F("  Probando..."));
    
      // Baudios del puerto serie.
      Serial.begin(115200);
      // Espere a que se conecte el puerto serie. Necesario USB nativo.
      while (!Serial);
      // Envía un OK al puerto serie avisando que se detecto la conexión.
      Serial.println(F("OK"));
    
      // Indicar luz de fondo como salida.
      // pinMode(LuzFondo, OUTPUT);
    
      // Enviar al puerto serie.
      for (int i = 0; i < 6; i++)
      {
        Serial.write(atras[i]);
      }
    
      for (int j = 0; j < 7; j++)
      {
        Serial.write(guardar[j]);
      }
    }
    
    void loop()
    {
    
    
    }

    Como los datos son tal cual


    http://electronica-pic.blogspot.com


    • Editado Metaconta viernes, 31 de julio de 2020 12:15
    viernes, 31 de julio de 2020 11:58
  • ¿No será recibirlo así desde el PC?

    C#:

    byte[] datosArray = new byte[cantidadBytes];

    Sí, claro, eso es ideal. De esa manera sabes que has recibido con seguridad los bytes exactos que te enviaron.

    Pero si más adelante vas a tener que convertir esos bytes en caracteres para mostrarlos, te vas a encontrar de nuevo con que necesitas conocer el Encoding para hacer este segundo paso.

    viernes, 31 de julio de 2020 12:25
  • Sigo en busca de los encoding...

    El monitor Serie de Arduino IDE 1.8.13 funciona las tildes bien.

    Con la consola de C# no.

    Es lo que debo saber.

    Sigo buscando información a ver como se hace y cual es.

    Saludos.


    http://electronica-pic.blogspot.com

    viernes, 31 de julio de 2020 12:59
  • Observo que salen dos interrogaciones. Eso usualmente indica que es UTF8 (pero no es seguro).

    Prueba System.Text.Encoding.UTF8.

    • Marcado como respuesta Metaconta viernes, 31 de julio de 2020 13:58
    • Desmarcado como respuesta Metaconta viernes, 31 de julio de 2020 23:48
    viernes, 31 de julio de 2020 13:40
  • Acertaste.

    No se como sabes que una letra con tilde que muestra dos ?? es UTF-8.

            // Detecta cualquier dato entrante.
            private static void DataReceivedHandler(object sender, SerialDataReceivedEventArgs e)
            {
                SerialPort sp = (SerialPort)sender;
                sp.Encoding = Encoding.GetEncoding("utf-8");
                string entradaDatos = sp.ReadExisting(); // Almacena los datos recibidos en la variable tipo string.
                Console.WriteLine("Datos recibido desde Arduino: " + entradaDatos); // Muestra en pantalla los datos recibidos.
    
            }

    Estás hecho un campeón.


    http://electronica-pic.blogspot.com

    viernes, 31 de julio de 2020 13:58
  • No se como sabes que una letra con tilde que muestra dos ?? es UTF-8.

    Eso es porque el juego UTF8 coincide con el ASCII en los primeros 127 caracteres, pero luego las letras acentuadas llevan un carácter de "escape" seguido de otro que indica cuál es la letra. Es decir cada letra acentuada se codifica con dos bytes en UTF8. Por eso al ver dos interrogaciones me he imaginado que se correspondían con dos bytes, y de ahí la inferencia de que podría tratarse de UTF8.
    viernes, 31 de julio de 2020 18:35
  • Hola:

    Me di cuenta que la ñ y la Ñ no lo coje UTF-8.

    A pesar de todo, sigo teniendo problemas a pesar que las tildes si funcionan.

    Saludos.


    http://electronica-pic.blogspot.com

    viernes, 31 de julio de 2020 22:33
  • Es el momento de capturar los bytes en binario y ver qué valor está realmente llegando, para buscarlo en las tablas de códigos. Procura capturar simultáneamente en un mismo mensaje a la vez una letra acentuada y la ñ, para asegurarnos de tener más información acerca de qué codificación se está usando.

    Lógicamente, hay que capturarlos en binario antes de que el serialport los meta en un string, porque para entonces ya les ha aplicado el Encoding y se ha perdido el valor original. Usa alguno de los métodos del SerialPort que reciben bytes en lugar de strings.

    sábado, 1 de agosto de 2020 6:59
  • Hemos declarado el array como unsigned char, por lo tanto es un byte. A la hora de leerlo y grabarlo en memoria lo interpreta como byte. Como el valor UTF-8 de 'ñ' es 0x3CB1, solo lee y guarda la parte baja, es decir, 0xB1.

    Se me ocurre que el compilador trata de una manera especial la codificación UTF8. Así cuando ponemos un caracter fuera del ASCII de 7 bits (que es un conjunto de UTF-8), el compilador lo interpreta como un unsinged long (32 bits). A partir que aqui, dependiendo de como lo tratemos lo interpretará de manera diferente:

    - Si le preguntamos el tamaño con sizeof('ñ') este será 2.

    - Si lo imprimimos como un caracter nos dará FFFFC3B1 o -15439. dependiendo de si usamos HEX o no, usando los 32 bits.

    - Si lo usamos en una cadena, por ejemplo "gañan", el tamaño de la cadena es 6. Al imprimirlo por serial sustitye la 'ñ' por dos bytes (utf-8 de la 'ñ') y muestra dos caracteres raros.

    Ahora bien, cuando el proceso es al revés, lo enviamos a través del terminal del IDE hacia al Arduino, ocurre algo maravilloso y desconcertante: sólo recibimos un caracter, si mostramos el valor en hex nos da que para la 'ñ' recibimos 0xF1 y para la 'Ñ' nos da 0xD1, que resulta ser el valor UNICODE.

    Da que pensar el tema...

    http://electronica-pic.blogspot.com

    sábado, 1 de agosto de 2020 11:56
  • si mostramos el valor en hex nos da que para la 'ñ' recibimos 0xF1 y para la 'Ñ' nos da 0xD1, que resulta ser el valor UNICODE.

    No, eso no es el valor UNICODE. Esos dos valores que indicas (F1 y D1) son los códigos de ñ y Ñ en ISO-8859-1 o en Windows ANSI.

    Tienes que buscar cómo y dónde estás haciendo la lectura y/o conversión de estos valores, para ver por qué está usando ese Encoding y posiblemente cambiarlo.

    > Hemos declarado el array como unsigned char

    Nótese que esto solo existe en C o C++, pero no en C#. El tipo equivalente en C# es byte. Si en C# lo declaras como Char, eso es un carácter Unicode (16 bits), lo mismo que si forma parte de un string. Si estás pasando datos de uno a otro, hay que tener presente que en alguno de los pasos intermedios tiene que hacer una conversión usando el Encoding, y si pones el encoding incorrecto (o lo usa por defecto si no has puesto nada) pues entonces salen los bytes incorrectos a partir de los caracteres originales.

    sábado, 1 de agosto de 2020 12:16
  • Claro, porque ahora estas escribiendo bytes.

    Un byte no tiene codificación solo es un número binario. Si lo envias por Serial, dependerá del receptor lo que quiera interpretar. El terminal por ejemplo, escribe caracteres, solo los que pueda imprimir, los que no los dejará en blanco o hará cualquier cosa.

    Mira este programa:

    #include <EEPROM.h>
    
    void setup() {
      Serial.begin(9600);
      for (int i=0; i<256; i++) {
        Serial.write((unsigned char)i);
        if (i!=0 && i%16==0) Serial.println();
        else Serial.print(' ');
      }
    }
    
    void loop() {
     
    }
    

    Enviará por el puerto serie los bytes del 0 al 255, he añadido un salto
    de línea cada 16 caracteres y he usado el terminal del ide y realTerm, con el siguiente resultado:



    RealTerm utiliza ASCII/ANSI, mientras que el terminal usa UTF-8. Como ves el resultado es que cada byte tiene asociado un caracter en el caso de ANSI y los que no tienen un caracter ni los pinta. En el caso del IDE, ocurre tres cuartas de lo mismo, pero en este caso si pinta cuadrados en los caracteres que no tienen esa asociación.

    Una cosa buena de las codificaciones es que generalmente los bytes comprendidos entre 0x21 (33) y 0x7F (127) son comunes. No importa si la codificación es tal o cual, será reconocido. Estos caracteres son:

    ! " # $ % & ' ( ) * + , - . / 0
    1 2 3 4 5 6 7 8 9 : ; < = > ? @
    A B C D E F G H I J K L M N O P
    Q R S T U V W X Y Z [ \ ] ^ _ `
    a b c d e f g h i j k l m n o p
    q r s t u v w x y z { | }
    

    Pero como ves, no hay acentos, ni ñ, ni caracter raro.

    Así que si quieres evitar el problema de la codificación hay dos vias:

    Usando texto: obvia tildes y caracteres raros. Así en lo "comandos" que propusiste en la imagen del primer post serían: ok, atras, guardar, etc. Sin usar tildes ni nada por el estilo.

    Así no importa si es utf-8 lo que haga arduino con los caracteres, o si es ascii, será lo mismo. E igualmente en el lado del emisor, que lo raro es lo del ide, ya que si fuese utf-8 deberia de mandar en el caso de la ñ 0x3cB1 y envia, parece ser, utf-16.

    Usando binario. En este caso lo que mandas son valores binarios, asi que tanto como emisor como receptor deben enteder el idioma, lo que Peter ha dicho que son protocolos. Por ejemplo, el arduino recibe valores binarios y un valor 0x34 es el comando "atrás", entonces el emisor solo debe mandar un byte que es 0x34.




    http://electronica-pic.blogspot.com

    lunes, 3 de agosto de 2020 12:29