No puedo leer "ñ" ni vocales con tilde desde el monitor serial

Estoy tratando de leer desde el monitor serial las letras ñ, á,é,í,ó,ú pero no las reconoce.
Hice un sketch donde se leen los carácteres del monitor serial y con un switch case que lee un char, se prenden algunos motores. Pero no los reconoce.

Intenté cambiar el switch case a leer int y ponerlos con su codigo ASCII, por ejemplo la ñ tiene el 164, la letra á con 160pero aún así NO lo reconoce.

¿Alguna solución?

Hola
¿Está utilizando Serial.read() para leer los caracteres especiales (ñ, á,é,í,ó,ú), a través de SerialMonitor?
Estos caracteres son tratados por arduino como caracteres
unicode-8 y tiene 2 bytes para cada carácter.
por ejemplo el

es UTF-8 (hexadecimal) 0xC3B1, (0xFFFFFFC3 y 0XFFFFFFB1)
https://www.fileformat.info/info/charset/UTF-16/list.htm
el

es el UTF-8 (hexadecimal) 0xC3A1, (0xFFFFFFC3 y 0XFFFFFFA1) Complete Character List for UTF-16

Si usa Serial.read(), tendrá que leer 2 caracteres.
Pero puede leer definiendo su variable que recibirá los datos escritos con String y leyendo con Serial.readString();
los datos se podían imprimir y aparecían a medida que los escribía.

1 Like

Mira este hilo del foro: https://forum.arduino.cc/t/n-y-letras-con-tilde-en-arduino/502952 tal vez te sea de ayuda.

Así es, estoy leyendo por medio de Serial.read() a través del monitor serial.
Muchas gracias, ¿ podría ayudarme por favor poniendome esto?

¿cómo sería?

Ola

String leitura = " ";
//----------------------------------------------------
void setup() {
  Serial.begin(115200);
}
//----------------------------------------------------
void loop() {
  if (Serial.available() > 0)
  {
    leitura = Serial.readString();
    Serial.println(leitura); 
  }
}

Siguiendo el código de @IgnoranteAbsoluto del link que ha sugerido he querido probarlo por interesante y no me ha funcionado o no termino de interpretar las instrucciones.

He puesto un simple Serial.print cuando el caracter es

    if (datoSerie >= 0) {           // Un valor mayor o igual a cero es que hemos recibido un byte
        Serial.println(datoSerie, HEX);

y los resultados han sido estos

ñ
F1
A
á
E1
A
é
E9
A
í
ED
A
ó
F3
A
ú
FA
A

descartemos el A.
Agregué tmb un par de Serial.print en las teclas especiales á é í ó ú y ñ

Hola @Surbyte, no tengo claro qué prueba has hecho. Creo que vendría bien que pusieras el código que has usado para la prueba.

Comentar de todas maneras que el valor que se ha de usar es el de caracterUTF8 siempre que sea distinto a -1. Por eso está la condición if (caracterUTF8 != -1) y que hay que tener en cuenta que es de tipo wchar_t que no sé si el Serial.print() lo soporta como un simple int. Porque lo he probado en un simulador y no lo imprime como caractéres, sino como un entero con signo (int). Y es que cuando el carácter UTF-8 es de un byte, sólo se ha de «imprimir» el byte menos significativo, mientras que si es un carácter de dos bytes, primero se ha de «imprimir» el byte más significativo y luego el menos significativo. Aquí te dejo un programa a modo de prueba, que simplemente hace «eco» de lo que le llega por el puerto serie. Manda primero el byte más significativo si este byte es distinto de cero.

void setup(){
    Serial.begin(9600);
    Serial.println("Prueba a mandar algo con tildes o con ñ.");
}

byte byteMasSignificativoUTF = 0;   // Cero para indicar que no se está recibiendo un carácter "extendido"

void loop(){
    wchar_t caracterUTF8 = -1;      // Con -1 indicamos que aún no se ha recibido un carácter válido
    int datoSerie = Serial.read();  // Leemos un byte del puerto serie (-1 nos indica que no hay datos)
    if (datoSerie >= 0) {           // Un valor mayor o igual a cero es que hemos recibido un byte
        if (byteMasSignificativoUTF == 0) {
            // Si no se ha recibido con anterioridad el bit más significativo de un carácter "extendido"
            if ((datoSerie & B11100000) == B11000000) {
                // Se trata del byte más significativo de un carácter extendido, así que lo "guardamos para después"
                byteMasSignificativoUTF = datoSerie;
            }
            else {
                // Asumimos que es un carácter de un solo byte, con lo que ya tenemos el valor del carácter
                caracterUTF8 = datoSerie;
            }
        }
        else {
            // Ya habíamos recibido con anterioridad el byte más significativo, así que "completamos" el carácter recibido
            caracterUTF8 = (byteMasSignificativoUTF << 8) | datoSerie;  // A la parte más significativo le añadimos la parte menos significativa
            byteMasSignificativoUTF = 0;    // Ya no estamos esperando un carácter "extendido" porque ya lo hemos completado
        }
    }

    if (caracterUTF8 != -1) { // Si tenemos un carácter válido (no poner < 0 ya que los caracteres "extendidos" tienen el bit más significativo a uno)
      if ((caracterUTF8 & 0xff00) != 0) { // Si el byte más significativo es disto de cero, es que un carácter UTF-8 de dos bytes
          Serial.print((char)(caracterUTF8 >> 8)); // Escribimos el byte más significativo
      }
      Serial.print((char)(caracterUTF8)); // Escribe el byte menos significativo
   }
}

Como dije puse el código que tu sugeriste porque me resultó interesante ver si funcionaba.

void setup(){

    pinMode(5, OUTPUT); // Declaramos que utilizaremos el pin 13 como salida
    pinMode(6,OUTPUT);
    pinMode(7,OUTPUT);
    pinMode(8,OUTPUT);
    pinMode(9,OUTPUT);
    pinMode(10,OUTPUT);
    Serial.begin(115200);
    Serial.println("Listo");
}

byte byteMasSignificativoUTF = 0;   // Cero para indicar que no se está recibiendo un carácter "extendido"

void loop(){
    wchar_t caracterUTF8 = -1;      // Con -1 indicamos que aún no se ha recibido un carácter válido
    int datoSerie = Serial.read();  // Leemos un byte del puerto serie (-1 nos indica que no hay datos)
    if (datoSerie >= 0) {           // Un valor mayor o igual a cero es que hemos recibido un byte
        Serial.println(datoSerie, HEX);
        if (byteMasSignificativoUTF == 0) {
            // Si no se ha recibido con anterioridad el bit más significativo de un carácter "extendido"
            if ((datoSerie & B11100000) == B11000000) {
                // Se trata del byte más significativo de un carácter extendido, así que lo "guardamos para después"
                byteMasSignificativoUTF = datoSerie;
            }
            else {
                // Asumimos que es un carácter de un solo byte, con lo que ya tenemos el valor del carácter
                caracterUTF8 = datoSerie;
            }
        }
        else {
            // Ya habíamos recibido con anterioridad el byte más significativo, así que "completamos" el carácter recibido
            caracterUTF8 = (byteMasSignificativoUTF << 8) | datoSerie;  // A la parte más significativo le añadimos la parte menos significativa
            byteMasSignificativoUTF = 0;    // Ya no estamos esperando un carácter "extendido" porque ya lo hemos completado
        }
    }

    if (caracterUTF8 != -1) {   // Si tenemos un carácter válido (no poner < 0 ya que los caracteres "extendidos" tienen el bit más significativo a uno)
        switch (caracterUTF8) {
            //A  
            case 'a':

                digitalWrite(5, HIGH);
                digitalWrite(6, LOW);
                digitalWrite(7, LOW);
                digitalWrite(8, LOW);
                digitalWrite(9, LOW);
                digitalWrite(10, LOW);
                break;
                //B
            case 'b':

                digitalWrite(5, HIGH);
                digitalWrite(6, HIGH);
                digitalWrite(7, LOW);
                digitalWrite(8, LOW);
                digitalWrite(9, LOW);
                digitalWrite(10, LOW);
                break;
                //C
            case 'c':

                digitalWrite(5, HIGH);
                digitalWrite(6, LOW);
                digitalWrite(7, LOW);
                digitalWrite(8, HIGH);
                digitalWrite(9, LOW);
                digitalWrite(10, LOW);
                break;
                //D
            case 'd':

                digitalWrite(5, HIGH);
                digitalWrite(6, LOW);
                digitalWrite(7, LOW);
                digitalWrite(8, HIGH);
                digitalWrite(9, HIGH);
                digitalWrite(10, LOW);
                break;

                //E
            case 'e':

                digitalWrite(5, HIGH);
                digitalWrite(6, LOW);
                digitalWrite(7, LOW);
                digitalWrite(8, LOW);
                digitalWrite(9, HIGH);
                digitalWrite(10, LOW);
                break;

            case 'á':
                Serial.println("a acentuada");
                digitalWrite(5,HIGH);
                digitalWrite(6,HIGH);
                digitalWrite(7,HIGH);
                digitalWrite(8,LOW);
                digitalWrite(9,HIGH);
                digitalWrite(10,HIGH);
                break;

            case 'é':
                Serial.println("e acentuada");
                digitalWrite(5,LOW);
                digitalWrite(6,HIGH);
                digitalWrite(7,HIGH);
                digitalWrite(8,HIGH);
                digitalWrite(9,LOW);
                digitalWrite(10,HIGH);
                break;

            case 'í':
                Serial.println("i acentuada");
                digitalWrite(5,LOW);
                digitalWrite(6,LOW);
                digitalWrite(7,HIGH);
                digitalWrite(8,HIGH);
                digitalWrite(9,LOW);
                digitalWrite(10,LOW);
                break;

            case 'ó':
                Serial.println("o acentuada");
                digitalWrite(5,LOW);
                digitalWrite(6,LOW);
                digitalWrite(7,HIGH);
                digitalWrite(8,HIGH);
                digitalWrite(9,LOW);
                digitalWrite(10,HIGH);
                break;

            case 'ú':
                Serial.println("u acentuada");
                digitalWrite(5,LOW);
                digitalWrite(6,HIGH);
                digitalWrite(7,HIGH);
                digitalWrite(8,HIGH);
                digitalWrite(9,HIGH);
                digitalWrite(10,HIGH);
                break;

            case 'ü':
                digitalWrite(5,HIGH);
                digitalWrite(6,HIGH);
                digitalWrite(7,LOW);
                digitalWrite(8,LOW);
                digitalWrite(9,HIGH);
                digitalWrite(10,HIGH);
                break;

            case 'ñ':
                Serial.println("enie");
                digitalWrite(5,HIGH);
                digitalWrite(6,HIGH);
                digitalWrite(7,LOW);
                digitalWrite(8,HIGH);
                digitalWrite(9,HIGH);
                digitalWrite(10,HIGH);
                break;
        }
    }
}

@Surbyte creo que ya sé lo que te está ocurriendo. Creo que el monitor serie está mandando los caracteres en latin1 (ISO-8859-1) o similar, mientras que el editor del código del programa está en UTF-8. Por lo que los caracteres no ASCII del programa son de dos bytes y, obviamente, diferentes a los que mandas por el monitor serie que son de un byte. Les recuerdo que los caracteres ASCII son estrictamente los 128 primeros, los 128 restantes dependen del juego de caracteres con que estemos trabajando. Lo que pusiste da la pista de que lo que mandas por el monitor serie es latin1, el valor en hexadecimal F1 que se ve al mandar la ñ coincide con el valor que ha de tener la ñ cuando el juego de caracteres que estamos usando es latin1. Véase el valor para la eñe en esta tabla https://es.sttmedia.com/unicode-tabla-latin1

Por eso no te detecta las tildes y la eñe. No estoy seguro de si funcionará o si al publicarlo, copiarlo o pegarlo se va a «perder» los caracteres en latin1. Pero prueba a copiar y pegar sin modificar el código siguiente:

void setup(){

    pinMode(5, OUTPUT); // Declaramos que utilizaremos el pin 13 como salida
    pinMode(6,OUTPUT);
    pinMode(7,OUTPUT);
    pinMode(8,OUTPUT);
    pinMode(9,OUTPUT);
    pinMode(10,OUTPUT);
    Serial.begin(115200);
    Serial.println("Listo");
}

byte byteMasSignificativoUTF = 0;   // Cero para indicar que no se está recibiendo un carácter "extendido"

void loop(){
    wchar_t caracterUTF8 = -1;      // Con -1 indicamos que aún no se ha recibido un carácter válido
    int datoSerie = Serial.read();  // Leemos un byte del puerto serie (-1 nos indica que no hay datos)
    if (datoSerie >= 0) {           // Un valor mayor o igual a cero es que hemos recibido un byte
        Serial.println(datoSerie, HEX);
        if (byteMasSignificativoUTF == 0) {
            // Si no se ha recibido con anterioridad el bit más significativo de un carácter "extendido"
            if ((datoSerie & B11100000) == B11000000) {
                // Se trata del byte más significativo de un carácter extendido, así que lo "guardamos para después"
                byteMasSignificativoUTF = datoSerie;
            }
            else {
                // Asumimos que es un carácter de un solo byte, con lo que ya tenemos el valor del carácter
                caracterUTF8 = datoSerie;
            }
        }
        else {
            // Ya habíamos recibido con anterioridad el byte más significativo, así que "completamos" el carácter recibido
            caracterUTF8 = (byteMasSignificativoUTF << 8) | datoSerie;  // A la parte más significativo le añadimos la parte menos significativa
            byteMasSignificativoUTF = 0;    // Ya no estamos esperando un carácter "extendido" porque ya lo hemos completado
        }
    }

    if (caracterUTF8 != -1) {   // Si tenemos un carácter válido (no poner < 0 ya que los caracteres "extendidos" tienen el bit más significativo a uno)
        switch (caracterUTF8) {
            //A  
            case 'a':

                digitalWrite(5, HIGH);
                digitalWrite(6, LOW);
                digitalWrite(7, LOW);
                digitalWrite(8, LOW);
                digitalWrite(9, LOW);
                digitalWrite(10, LOW);
                break;
                //B
            case 'b':

                digitalWrite(5, HIGH);
                digitalWrite(6, HIGH);
                digitalWrite(7, LOW);
                digitalWrite(8, LOW);
                digitalWrite(9, LOW);
                digitalWrite(10, LOW);
                break;
                //C
            case 'c':

                digitalWrite(5, HIGH);
                digitalWrite(6, LOW);
                digitalWrite(7, LOW);
                digitalWrite(8, HIGH);
                digitalWrite(9, LOW);
                digitalWrite(10, LOW);
                break;
                //D
            case 'd':

                digitalWrite(5, HIGH);
                digitalWrite(6, LOW);
                digitalWrite(7, LOW);
                digitalWrite(8, HIGH);
                digitalWrite(9, HIGH);
                digitalWrite(10, LOW);
                break;

                //E
            case 'e':

                digitalWrite(5, HIGH);
                digitalWrite(6, LOW);
                digitalWrite(7, LOW);
                digitalWrite(8, LOW);
                digitalWrite(9, HIGH);
                digitalWrite(10, LOW);
                break;

            case '�':
                Serial.println("a acentuada");
                digitalWrite(5,HIGH);
                digitalWrite(6,HIGH);
                digitalWrite(7,HIGH);
                digitalWrite(8,LOW);
                digitalWrite(9,HIGH);
                digitalWrite(10,HIGH);
                break;

            case '�':
                Serial.println("e acentuada");
                digitalWrite(5,LOW);
                digitalWrite(6,HIGH);
                digitalWrite(7,HIGH);
                digitalWrite(8,HIGH);
                digitalWrite(9,LOW);
                digitalWrite(10,HIGH);
                break;

            case '�':
                Serial.println("i acentuada");
                digitalWrite(5,LOW);
                digitalWrite(6,LOW);
                digitalWrite(7,HIGH);
                digitalWrite(8,HIGH);
                digitalWrite(9,LOW);
                digitalWrite(10,LOW);
                break;

            case '�':
                Serial.println("o acentuada");
                digitalWrite(5,LOW);
                digitalWrite(6,LOW);
                digitalWrite(7,HIGH);
                digitalWrite(8,HIGH);
                digitalWrite(9,LOW);
                digitalWrite(10,HIGH);
                break;

            case '�':
                Serial.println("u acentuada");
                digitalWrite(5,LOW);
                digitalWrite(6,HIGH);
                digitalWrite(7,HIGH);
                digitalWrite(8,HIGH);
                digitalWrite(9,HIGH);
                digitalWrite(10,HIGH);
                break;

            case '�':
                digitalWrite(5,HIGH);
                digitalWrite(6,HIGH);
                digitalWrite(7,LOW);
                digitalWrite(8,LOW);
                digitalWrite(9,HIGH);
                digitalWrite(10,HIGH);
                break;

            case '�':
                Serial.println("enie");
                digitalWrite(5,HIGH);
                digitalWrite(6,HIGH);
                digitalWrite(7,LOW);
                digitalWrite(8,HIGH);
                digitalWrite(9,HIGH);
                digitalWrite(10,HIGH);
                break;
        }
    }
}

Si se ha copiado y pegado bien, todos los case «conflictivos» han de tener un símbolo «raro» con una interrogante. Eso es porque el carácter no es un carácter UTF-8 válido. Si el editor o donde se pegue está en latin1 entonces sí se debería de ver los caracteres correctos. El programa debería de funcionar correctamente a aquellos que no les funciona la prueba de @Surbyte.

Otra cosa que me hizo sospechar es que pones "enie" en lugar de "eñe". Seguramente no pones "eñe" en el monitor serie te aparece eñe porque en UTF-8 la ñ son dos bytes que al «interpretarlo» como latin1 lo muestra como los dos caracteres que representan esos dos bytes en latin1 (ñ).

Nota: esta versión sólo ha de funcionar correctamente si se copia «tal cual» y el monitor serie trabaja en latin1 o similar, no en UTF-8.

Pensándolo mejor, por si las moscas, adjunto el fichero por si da problemas el copi+paste.

foro_latin.ino (5.5 KB)

Por cierto, si tanto el programa como el monitor serie está en latin1, no hace falta la parte de «leer UTF-8» ya que cada carácter es un byte, y funciona igual que si sólo se trabaja con los primeros 128 caracteres, los ASCII que son siempre los mismos en la mayoría de juegos de caracteres basados en el alfabeto europeo.

Eso fue adrede solo para mostrar que estoy en el case correspondiente. Recuerda que no logré que se viera nada. Lo probaré a mi regreso. Ahora entro unos dias en descanso.
Igual entraré al foro pero ahora probar cosas no es tan fácil.