[Risolto] Stranissima corruzione dell'array di caratteri usando una "è" o una "é"!

Ciao a tutti.

Nelle scritte scorrevoli che sto realizzando ogni tanto mi capita una cosa stranissima: se in un messaggio inserisco una "è" o una "é", a volte in fase di compilazione il messaggio stesso viene corrotto da qualche lettera prima di quella fino alla fine del messaggio. Mi sembra che capiti prevalentemente se salvo il file prima di caricarlo...

La corruzione si manifesta solo in quel messaggio, quindi in quell'elemento dell'array, e non dipende dalla lunghezza del testo.

Il difetto persiste anche scollegando e ricollegando l'alimentazione (è stato programmato con l'array corrotto), mentre gli altri messaggi non hanno difetti. Il difetto si ripete se riprogrammo il microcontrollore senza fare altro; per risolvere il problema devo riscrivere la lettera e riprogrammare.

Il problema non si manifesta usando una "e" non accentata o una "t" (non ho provato tutte le lettere!...).

unsigned char testo[4][100]= // nMsg va da 1 a N; gli indici dei testi vanno da 0 a N-1.
{
// n.1, indice 0
"Trovate i nostri video su www.youtube.com/mariachescioglieinodi",
// n.2, indice 1
"Questo è il testo 2",        
// n.3, indice 2
"Questo qui è il testo 3",    
// n.4, indice 3
"Testo numero quattro"        
};

Ho già pubblicato il programma completo qui:
[FUNZIONA!] Scritte scorrevoli con MaxMatrix e 7219 [CON VIDEO]
Questo è un video di ciò che accade:

Qualcuno ha idea della ragione per cui accade?...

Grazie
Gianluca

È un problema di codifica dei caratteri, Le lettere accentate non sono ASCII standard (1 byte per carattere con codici da 0 a 127) ma occupano più di un byte nell'array. Quali e quanti dipende dal tipo di codifica implicita adottata dal sistema operativo, dal compilatore, e dall'editor usato.

Grazie, Claudio.
I codici ASCII con cui vengono riconosciute le due e accentate, però, sono 232 e 233, che rientrano negli 8 bit, quindi entrano in un unsigned char... Infatti prima non riuscivo a riconoscerle, perché usavo un array semplicemente char.

I codici superiori al 127 non sono ASCII. Sono altre codifiche. Gli stessi byte possono rappresentare caratteri differenti a seconda della codifica usata dal sistema. Quando il sorgente viene salvato su disco potrebbe benissimo essere salvato in UTF-8, o qualche ISOxxxx. Non so cosa avviene alla rilettura da parte dell'IDE :frowning:

Fai una semplice prova, metti una lettera accentata in un unsigned char

unsigned char b[2] = "è";

Federico

E poi?...
Sto già usando un unsigned char.

Datman:
E poi?...
Sto già usando un unsigned char.

Hai provato? Ci sta?
Non riesco a provare, ma visto che hai problemi, ho dubbio che non ci stia :slight_smile:

Immagino che poi tu legga byte per byte la stringa, ma se da quella lettera in poi ottieni strani caratteri, probabilmente il byte che leggi non è quello che ti aspetteresti, ne deduco che forse, in compilazione, non ti basta un byte per le accentate, come già ipotizzato da Claudio.

Update
Se così fosse, ti basta sostituire, nella stringa, le accentate con caratteri standard che sicuramente non usi, per poi ri-mapparli con la corretta sequenza binaria, e naturalmente non usare unsigned char, ma char per riconoscere immediatamente caratteri non standard.

Altra soluzione è non usare stringhe, ma elenco numerico che fa riferimento direttamente agli indici della matrice con le sequenze binarie dei caratteri.

Federico

Ma perché a volte compila bene e altre male?...
Quando ribatto la "è" su quella già scritta, probabilmente vede che ho cambiato qualcosa e compila nuovamente, ma che è cambiato?...

Potrei anche mettere un #define usa_apostrofo_per_accentare_le_vocali e poi, quando viene letta una vocale seguita dell'apostrofo, visualizzare la vocale accentata.
Comunque, se al collaudo dopo il caricamento va tutto bene, i testi pre caricati non daranno problemi. Poi, però, ci sarà l'immissione tramite bluetooth...

Datman:
Ma perché a volte compila bene e altre male?...

Non saprei! Comunque stamattina ho fatto una prova e confermo che (da me) vengono codificati UTF-8:
è -> C3 A8 -> 195 168
é -> C3 A9 -> 195 169

Datman:
Potrei anche mettere un #define usa_apostrofo_per_accentare_le_vocali e poi, quando viene letta una vocale seguita dell'apostrofo, visualizzare la vocale accentata.

se per te non è un problema... può essere una soluzione :slight_smile:

Datman:
Comunque, se al collaudo dopo il caricamento va tutto bene, i testi pre caricati non daranno problemi. Poi, però, ci sarà l'immissione tramite bluetooth...

In questo caso non è un problema.
Visto che devi inviare i testi via bt, e che, immagino, l'app la farai tu, ti basta inviare la sequenza dei codici ascii (esteso), e in arduino stai attento quando decodifichi.

0 -> 48
A -> 65
a -> 97
è -> 232
é -> 233

Federico

PS
Dopo aver scritto, ho dato un'occhiata al reference, al solito bisognerebbe farlo prima ::slight_smile: ::slight_smile: ::slight_smile:

Chiedo scusa

wchar_t text[] = L"èéàìòù"; //wide characters
for (byte i=0;i<sizeof(text)/sizeof(wchar_t);i++){
  Serial.println(text[i]);
}
232
233
224
236
242
249

Al posto dell'IDE 1.8.8 che usavo, ho provato l'1.8.10: stessa cosa.

Sì, avevo detto che usavo 232 e 233.

Come posso realizzare correttamente una cosa del genere?

void printStringWithShift(unsigned char*s, int shift_speed)
{
while(*s!=0)
  {
  #ifdef usa_apostrofo_per_accentare_le_vocali
    if(.......) // Il carattere successivo è un apostrofo
      {
      if(*s==65) *s=97; // Se è una "a", trasformala in "à"
      if(*s==69) *s=95; // Se è una "e", trasformala in "è"
      ......... // Salta un carattere
      }
  #endif
    
  if(!digitalRead(6)) {selezione(); EEPROM.update(1,nMsg); while(!digitalRead(6)); Riavvia();}
  printCharWithShift(*s, shift_speed);
  s++;
  }

Ho scoperto che con l'IDE 1.8.10 il problema si manifesta sempre, anche riscrivendo il carattere poco prima del caricamento! >:(

Bhe se il puntatore al carattere è ad un carattere singolo non puoi in nessun modo sapere se il successivo sarà un apostrofo, quindi o aggiungi un parametro o trasformi il parametro in un array di char che andrai ad analizzare.
Ma visto che il programma funziona e evidentemente che è un problema dell'IDE o di come salva/ricarica i file o come li compila (Credo che la differenzia stia nel fatto che venga eseguita una compilazione parziale o totale) questa soluzione è come quella di usare il WatchDog per resettare il micro per problemi legati al programma, insomma una mezza porcata.
Credo che la cosa migliore sia quella di sottoporre il problema a chi si occupa dell'IDE, magari fornisce una spiegazione al comportamento e/o può chiedere di apportare le modifiche all'IDE per ovviare al problema (se è un problema), magari è solo un impostazione d'ottimizzazione o robe del genere che causa l'anomalia.

Datman:
Ho scoperto che con l'IDE 1.8.10 il problema si manifesta sempre, anche riscrivendo il carattere poco prima del caricamento! >:(

Questo tuo messaggio avvalora la mia ipotesi, sottoponi il problema nel topic degli sviluppi dell'IDE che magari lo correggono

Grazie anche a te, fabpolli :slight_smile:

Nel frattempo stavo pensando: se i messaggi fossero scritti in un file di testo in una SD, sarebbe meglio? Come si potrebbe realizzare? Sarebbe anche più comodo modificarli, senza dover ricompilare e ricaricare.

Si su CD non sarebbe male, puoi fare tutto nel setup.
hai già la matrice con i vari messaggi, puoi inizializzarla con del testo generico in modo che se la lettura da SD fallisce puoi mostrare sui display un testo del tipo "Errore lettura SD" o cose simili.
Nel setup apri e leggi un file specifico, e qui editandolo con un tuo edito di testo preferito potrai impostare la codifica che più ti aggrada (a meno che tu non usi l'oscido notepad di windows) e il problema dei caratteri non si pone più.
Per la lettura ti basi sugli esempi dell'IDE, leggi via via le varie righe nel file di testo e le copi con la strncpy nelle varie posizioni della tua matrice dei messaggi, occhio ad usare la strncpy e non la strcpy in modo tale che anche se innavertitamente nella SD ci piazzi un messaggio troppo lungo per il tuo vettore con la strncpy potrai specificare di copiare il numero massimo di caratteri ammissibili dal tuo vettore (meno uno perché all'ultimo carattere dovrai sempre mettere il terminatore) e qui per ottimizzare dovrai verificare se il messaggio letto da SD è più piccolo della dimensione massima del vettore paserai la sua lunghezza altrimenti passerai come già detto il numero massimo di caratteri meno uno.
Per la lunghezza della stringa strlen, infine per aggiungere il terminatore o lo fai a mano con

matrice[indice][NUMERO_DI_CARATTERI_DEL_MESSAGGIO] = '\0'; oppure
matrice[indice][NUMERO_DI_CARATTERI_DEL_MESSAGGIO] = 0 che è uguale

o con la strncat giusto per far pratica con le varie funzioni sulle stringhe classiche del C.
Quando hai finito di leggere il file o il numero di linee lette è uguale al numero massimo di messaggi ammissibili chiudi il file e parti con il loop

fabpolli:
Questo tuo messaggio avvalora la mia ipotesi, sottoponi il problema nel topic degli sviluppi dell'IDE che magari lo correggono

Se ti riferisci al problema della accentate il reference parla chiaro:

The Arduino editor and the compiler (avr-gcc) use UTF-8 for special characters. When UTF-8 is used in the program code itself, the characters can occupy one up to four bytes.

quindi tutte le accentate usano due byte, e se lui cicla sui byte, non potrà mai ri-ottenere un carattere sopra al 127 :slight_smile:

unsigned char t[2] = "è"; //-> va in errore

unsigned char t[3] = "è"; //corretto, ma...
for (byte i=0;i<3;i++)
  Serial.println(t[i]);

ottieni
195
168

che coincide con la codifica UTF-8
C3 A8 cioè -> è

Quindi IMHO se vuole avere le stringhe costanti con le accentate deve trovare un altro modo.

La mia proposta semplice (anche se non più supportata) è usare wchar_t in modo da non dover riscrivere quasi niente.

Federico

Va da se che potresti anche pensare di leggere la SD al volo durante la selezione dei messaggi rendendo di fatto la procedura virtualmente senza limite nel numero di messaggi (ma solo nella loro lunghezza massima) aprendo il file surante la selezione e mostrando di volta in volta la linea corrente, rileggendo la precedente o la successiva al variare dell'encoder e infine copiando nel buffer il messaggio selezionato. Ovviamente la cosa si complica e non avendo mai davvero lavorato con la SD non saprei dirti se esiste un modo di leggere il file non in modo sequenziale ma posizionalmente tramite indici ma sarebbe una bella cosa

Grazie, Federico66.
Come uso wchar_t? Così da errore:

const wchar_t testo[9][100]={

Grazie, fabpolli.
Vorrei selezionare il numero del messaggio da leggere e, in quel momento, caricare il testo dal file .txt.
Acquisto un lettore uSD e comincio a provare. :slight_smile: