Problema di compilazione su switch/case [Risolto]

Salve a tutti,
stavo scrivendo uno sketch e sto usando uno switch/case per testare una variabile char, usando la label 0xFF però segnala 2 errori di compilazione, cioè che il valore della label supera il valore massimo del tipo e che le istruzioni non verranno mai eseguite.
Ho scritto uno sketch minimale solo con quel pezzo di codice ed effettivamente continua a segnalare l'errore:

char test;
char prova;
void setup() {
  // put your setup code here, to run once:
  test=0xff;
  Serial.println(test,HEX);
  switch (test){
    case 0xff:
    prova=0xff;
    Serial.println(prova,HEX);
    break;
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}

questo quello che restituisce il compilatore:

C:\Arduino\sketch_may15b\sketch_may15b.ino: In function 'void setup()':

C:\Arduino\sketch_may15b\sketch_may15b.ino:8:5: warning: case label value exceeds maximum value for type

     case 0xff:

     ^~~~

C:\Arduino\sketch_may15b\sketch_may15b.ino:9:10: warning: statement will never be executed [-Wswitch-unreachable]

     prova=0xff;

     ~~~~~^~~~~

In assegnazzione del valore 0xFF non da errore (se assegno ad esempio 0xFFF ovviamente segnala l'overflow) non capisco quindi il perchè dell'errore nello switch visto che il char dovrebbe essere di 8 bit e quindi il valore FF potrebbe essere contenuto nella variabile, al di là della sua rappresentazione.
Inoltre se testo la varibile con un if invece dello switch/case, non segnala alcun errore e compila liscio:

char test;
char prova;
void setup() {
  // put your setup code here, to run once:
  test=0xff;
  Serial.println(test,HEX);
  if (test==0xff){
    prova=0xff;
    Serial.println(prova,HEX);
  }
}

void loop() {
  // put your main code here, to run repeatedly:

}

La versione dell'IDE installata è la 1.8.12 .

Vorrei capire dove sto sbagliando e grazie dell'aiuto,
ciao.

Intanto non capisco perché vuoi proprio usare una variabile char e non capisco perché vuoi proprio usare una notazione esadecimale, un numero se lo scrivi in decimale, esadecimale o binario è la stessa cosa, rappresenta lo stesso valore.

Vediamo di chiarire una variabile char è una variabile con segno, memorizza sia numeri positivi sia numeri negativi, una variabile char memorizza valori da -128 a 127, una variabile unsigned char da 0 a 255.
Quindi il valore 255 non può essere assegnato a una variabile char ma a una unsigned char.
Ma puoi tranquillamente usare una variabile byte che equivale a unsigned char.

grazie della celere risposta torn24,
cerco di spiegare meglio:
uso una variabile char perché nel programma intero sto ricevendo un flusso di testo dalla rete, prodotto da una controparte umana che utilizza un client. Il client invia il testo direttamente codificato in ASCII, in pacchetti con un payload di 1 byte. Il protocollo prevede però che vengano inviati anche comandi di controllo e nello specifico utilizza caratteri non stampabili, tra cui il valore 0xFF per avvertirmi che sta per inviare un comando di controllo della comunicazione e non il normale testo imputato dall'utente.

quindi di fatto metto quello che leggo dentro la variabile char, la testo e poi decido se il suo contenuto lo devo mettere nel buffer dei dati (un array char che contiene il testo prodotto dalla controparte umana) oppure nel buffer dei comandi di controllo (un secondo e distinto array char), a cui dovrò dare una risposta al client.

I comandi di controllo inviati dal client potrebbero arrivare in qualsiasi momento e in mezzo ai dati utente.

Benché l'if non dia errore, utilizzo lo switch perché devo testare anche altri caratteri non stampabili che mi vanno a modificare come tratto il buffer dati, questa volta inseriti dalla controparte umana, ad esempio il backspace, la tabulazione ecc...

Infine devo prendere il buffer dati, farci un parsing ed elaborarlo, che è quello che a me interessa veramente.

questo spero spieghi il perché uso una variabile char, la notazione HEX e che non sto né inserendo né testando il valore decimale 255.

certamente come mi hai ricordato il char include valori da -128 a 127 nella notazione decimale, ma come scrivevo al di là della notazione che si utilizza dovrebbe poter contenere 0xFF, anzi dirò di più, ho provato per curiosità ad utilizzare la notazione binaria, e se faccio lo stesso giochino testando il valore B11111111 anzichè 0xFF (che dovrebbe essere comunque la medesima cosa) l'errore permane.

Da qui il mio dubbio e la mia incomprensione all'errore riportato dal compilatore: perché mi da l'errore se voglio sapere se dentro una variabile char X di 8 bit cerco il valore "11111111" o l'hex FF?

In alternativa potrei si usare come dici tu una variabile byte, testarla e successivamente fare un casting della variabile dentro l'array char (spero di non dire una cavolata), ma mi resterebbe il tarlo in testa sul perché non funzioni l'altro modo.

grazie ancora.

E' un problema di come è implementato il char in Arduino ... esso è un signed char, mentre il valore 0xFF è un unsigned char e quindi c'è un problema.

La cosa si risolve facilmente utilizzando il cast. Se davati a quello 0xFF metti (char) vedrai che non ti di alcun problema e funziona :

switch (x) {
    case (char) 0xFF:

Guglielmo

P.S.: Su Arduino un char di valore 0xFF vale -1 mentre 0XFF da solo, visto come un byte, vale 255 che è un valore troppo grande per un signed char.

Grazie gpb01 per l'aiuto,
mettendo il cast nella label fa sparire l'errore di compilazione, la cosa che mi ha mandato fuori strada è stato il fatto che anche mettendo direttamente il binario B11111111 al posto dell'hex FF (mi ero posto il problema che l'hex si traducesse in 255 sempre e comunque) mi dava l'errore, mi aspettavo invece che in quel modo io andassi a controllare i bit reali effettivamente presenti nella variabile, sia che questi poi rappresentassero -1 oppure 255, ma l'errore compariva lo stesso, in più usando if al posto dello switch (e anche nell'assegnazione del valore nella variabile) il problema non si presentava, facendomi ancora più confusione.
grazie ancora ciao!

andrearizla:
Da qui il mio dubbio e la mia incomprensione all'errore riportato dal compilatore: perché mi da l'errore se voglio sapere se dentro una variabile char X di 8 bit cerco il valore "11111111" o l'hex FF?

Ma scusa tanto, se, come hai detto, in ingresso tu puoi avere anche dei valori diversi da caratteri stampabili, significa che almeno QUEL byte non puoi considerarlo char per questa e per le ragioni che ti hanno già spiegato, quindi quale problema hai nel considerarlo byte?

La questione è semplicissima: se è un codice di controllo allora ce l'hai già come byte; se non lo è, allora dato che un carattere ha semplicemente un valore che va da 32 a 127, usi il byte così com'è perché puoi confrontare benissimo un byte con un char ossia una cosa del tipo:

byte test;
#define CONTROL1 0xFF
...
  switch (test) {
    // Codici di controllo
    case CONTROL1:
      Serial.println("Ricevuto CONTROL1");
      break;
    // Caratteri stampabili
    case 'a':
    case 'A':
      Serial.println("Ricevuto carattere A");
      break;

    case 'b':
...
  }

Ciao docdoc,
ora che ho capito un po' meglio non c'è nessun problema, usavo il char perchè poi infilavo la variabile un un array char null terminated (C string ?). Primo credevo dovessi poi farci il casting, secondo per linearità sull'uso delle variabili, ho visto invece che se assegno il byte direttamente ad un char non serve farci nemmeno il cast o perlomeno il compilatore non lo segnala (sto nuovamente sbagliando?), ma questo forse è più una questione di stile (cosa di cui credo esserne privo :slight_smile: ).
Visto però che nel case dovrei fare comunque il cast per farlo funzionare credo che opterò per utilizzare il byte.

Mi resta sempre un pochino l'amaro in bocca per il fatto che il l'if funziona lo stesso (senza errori) e se stampo l'hex del char sulla seriale dopo che ci ho assegnato FF sul monitor esce FF, sicuramente il problema di fondo è nella mia conoscenza (bassa) del C++ ma ciò ha solo contribuito a farmi più confusione e a farmi impuntare su una logica errata. Ad ogni modo grazie a voi ho imparato qualcosa in più!

andrearizla:
se assegno il byte direttamente ad un char non serve farci nemmeno il cast o perlomeno il compilatore non lo segnala (sto nuovamente sbagliando?)

Dipende. Se il byte che inserisci in un char ha valore positivo minore o uguale 128 va bene, se ha un valore maggiore di 127 o negativo, allora potrebbe non andare bene. Se fai il cast rischi comunque di avere risultati non attesi, e poi questo char se fa parte di una stringa (char array) è anche l'intera stringa che va modificata in un byte array in quanto se per caso ricevi un byte a zero (0x0) quello finisce per danneggiarti la stringa!

Quindi se vuoi entrare più nello specifico fammi vedere un esempio di codice dove usi questo valore (inclusa la dichiarazione delle variabili dove leggi e scrivi quel valore) e provo a consigliarti.

ma ciò ha solo contribuito a farmi più confusione e a farmi impuntare su una logica errata

Beh non devi impuntarti né demoralizzarti perché è semplice: se cerchi di ricordare che byte e char sono in sostanza la stessa cosa (un byte) ma gestiti in modo diverso, diventa tutto più chiaro.

Semmai ti basta sempre considerare COSA stai ricevendo come dato e COME vuoi o dei trattarlo o inviarlo. Ed in questo caso se nel tuo protocollo sai che puoi avere un byte che assume genericamente qualsiasi valore tra 0 e 255 8quindi "byte") e che può rappresentare un carattere stampabile, allora devi SEMPRE usare il tipo di dato più generale possibile, in questo caso quindi "byte", tutto qui. :wink:

ecco qui questo è il pezzo di codice reale (isolato) che mi dava problemi, ma che ora compila senza errori, tieni presente che sono un pessimo programmatore, che non scrivevo codice da forse vent'anni e non ultimo il C++ lo sto studiando da meno di un mese :o (scrivevo in pascal/delphi, php e un pochino di java :cry: ) se vedi cose ignobili sei autorizzato ad insultarmi :smiley:

per il valore 0 hai ragione, ma per fortuna è un carattere speciale di comando, quindi posso e devo gestirlo quando arriva, anche se non l'ho ancora fatto, tieni presente che sono ancora in fase di bozza e sto inserendo man mano le situazioni critiche da gestire, ciò nonostante se non faccio troppo il cattivo (cioè evito di inviare comandi o caratteri non stampabili non gestiti) il codice nella sua interezza funziona.

#include <SPI.h>
#include <SD.h>
#include <Ethernet.h>

void setup() {
  // put your setup code here, to run once:

}

void loop() {
  // put your main code here, to run repeatedly:

}

/*
GetNetworkData
return 0 if no error
return an error code value based by error type
use exe_client to manage data with other side
use action as output to do something
use parameter as action parameter
use charsReceived as input/output to mange user data index
use textBuffer as input/output to user data / partial user data
use textBufferSize as max user data lentgh
*/
byte getNetworkData(EthernetClient &exe_client, byte &action, char parameter[], int &charsReceived, char textBuffer[], const byte textBufferSize)//verificare variabili e uso
{
  byte ErrorLevel=0; //store error condition
  char c; //store input stream one byte at time
  byte cmd=0; //store start of protocol command
  byte subcmd=0; //store command requested
  byte cmdparam=0; //store command request type
  char cmdresponse[3]; //output rsponse to command
  byte charsWaiting; //store how many data is waiting on nic buffer
    charsWaiting = exe_client.available(); //update data waiting in nic buffer
    do { //start reading data
    c = exe_client.read();
    switch (c) {
      case 0x08://backspace received come back on user data buffer
      if (charsReceived==0){ //too many backspace
        exe_client.print(0x07); //ring my bell!
        exe_client.print('>'); //rewrite prompt symbol, user deleted it
      } else {
        charsReceived--; //decrease data waiting index
        textBuffer[charsReceived] = '\0'; //delete char received last time
      }
      break;
      case (char) 0xff:  //hey command incoming!
      cmd=c; //store command start for next loop
      break;
      default:
      if (cmd==0){ //user data incoming
        textBuffer[charsReceived] = c; //put data in user buffer
        charsReceived++; //increase chars received index
        textBuffer[charsReceived] = '\0'; //terminate the string
      } else { //i'm wating to complete command
        if (cmdparam==0){ //request type is missing, reading it now
          cmdparam=c;
        } else { //last time was a request type, now the command is coming
          subcmd=c;
          ErrorLevel=CmdResponse(subcmd,cmdparam,cmdresponse); //parse the command and respond to it
          for (int i=0; i<3; i++){
            exe_client.write(cmdresponse[i]);
            //reset the status
            cmd=0;
            subcmd=0;
            cmdparam=0;
          }
        }
      }
      break;
    }
    charsWaiting--;  //decrease incoming data counter
  }// check do loop condition, stop if buffer full, or line feed received or no more data in nic buffer
  while((charsReceived <= (textBufferSize-1)) && (!(textBuffer[charsReceived-1] == 0x0a)) && (charsWaiting > 0));
  if((textBuffer[(charsReceived)-2] == 0x0d) && (textBuffer[(charsReceived)-1] == 0x0a) && (ErrorLevel==0)) {
      ErrorLevel=parseReceivedText(action, textBuffer, parameter); //CR & LF received from user, start parsing user data
  } else {
    action=111; //partial data received, do nothing and resume when next time function is invoked
  }
  // if textBuffer full without reaching a CR/LF, raise error condition
  if(charsReceived >= (textBufferSize-1)) {
    ErrorLevel=3; //too long data string, exceding buffer size
    action=255; //report error to user
  }
  // if textBuffer not full and no CR, do nothing else;
  // go back to loop until more characters are received
  return ErrorLevel;
}

/*
CmdResponse
return 0 if no error
return an error code value based by error type
use inputCmd as command selector
use inputCtrl as type request
use OutputCmd as output response
*/
byte CmdResponse (char inputCmd, char inputCtrl, char outputCmd[3]){
  byte ErrorLevel=0; //store error condition
  outputCmd[0]=0xff; //output command alway start with FF
  outputCmd[2]=inputCmd; //output command is in response to request received
  switch (inputCmd) {
    case (char) 0x1f: //terminal window size
    switch (inputCtrl){
      case (char) 0xfe: //receive don't
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfd: //receive do
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfc: //receive won't
      outputCmd[1]=0xfe; //send don't
      break;
      case (char) 0xfb: //receive will
      outputCmd[1]=0xfe; //send don't
      break;
      default:
      ErrorLevel=2; //unknow request type received
      break;
     }
    break;
    case (char) 0x20: //terminal speed
    switch (inputCtrl){
      case (char) 0xfe: //receive don't
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfd: //receive do
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfc: //receive won't
      outputCmd[1]=0xfe; //send don't
      break;
      case (char) 0xfb: //receive will
      outputCmd[1]=0xfe; //send don't
      break;
      default:
      ErrorLevel=2; //unknow request type received
      break;
     }
    break;
    case (char) 0x18: //terminal type
    switch (inputCtrl){
      case (char) 0xfe: //receive don't
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfd: //receive do
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfc: //receive won't
      outputCmd[1]=0xfe; //send don't
      break;
      case (char) 0xfb: //remote will
      outputCmd[1]=0xfe; //send don't
      break;
      default:
      ErrorLevel=2; //unknow request type received
      break;
     }
    break;
    case (char) 0x27: //enviromental option
    switch (inputCtrl){
      case (char) 0xfe: //receive don't
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfd: //remote do
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfc: //remote won't
      outputCmd[1]=0xfe; //send don't
      break;
      case (char) 0xfb: //remote will
      outputCmd[1]=0xfe; //send don't
      break;
      default:
      ErrorLevel=2; //unknow request type received
      break;
     }
    break;
    case (char) 0x01: //echo
    switch (inputCtrl){
      case (char) 0xfe: //receive  don't
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfd: //receive  do
      outputCmd[1]=0xfc; //send won't
      break;
      case (char) 0xfc: //receive  won't
      outputCmd[1]=0xfe; //send don't
      break;
      case (char) 0xfb: //receive  will
      outputCmd[1]=0xfe; //send don't
      break;
      default:
      ErrorLevel=2; //unknow request type received
      break;
     }
    break;
    case (char) 0x03: //suppress go ahead
     switch (inputCtrl){
      case (char) 0xfe: //receive don't
      outputCmd[1]=0xfb; //send will
      break;
      case (char) 0xfd: //receive  do
      outputCmd[1]=0xfb; //send will
      break;
      case (char) 0xfc: //receive  won't
      outputCmd[1]=0xfd; //send do
      break;
      case (char) 0xfb: //receive  will
      outputCmd[1]=0xfd; //send do
      break;
      default:
      ErrorLevel=2; //unknow request type received
      break;
     }
    break;
    default:
    ErrorLevel=1; //unknow command type received
    break;
  }
  return ErrorLevel;
}

/*parseReceivedText
returno 0 if no error
return an error code value based by error type
use line_buffer as input
use action and parameter as output
*/
byte parseReceivedText(byte &action, char line_buffer[], char parameter[]){ 
  byte ErrorLevel=0; //store error condition
  //parsing data
  return ErrorLevel;
}

Capisco che tu sia un pessimo programmatore, ma se scrivi il codice in questo modo diventa difficile per chiunque...:wink: Devi indentare decentemente il codice, nell'IDE di Arduino premi Ctrl-T e te lo indenta lui, poi da quel momento cerca di mantenere quel tipo di indentazione.

Per dire, in questo pezzo di codice:

...
  byte charsWaiting; //store how many data is waiting on nic buffer
    charsWaiting = exe_client.available(); //update data waiting in nic buffer
    do { //start reading data
    c = exe_client.read();
    switch (c) {
      case 0x08://backspace received come back on user data buffer
      if (charsReceived==0){ //too many backspace
        exe_client.print(0x07); //ring my bell!
...

hai una serie di comandi con 2 spazi, poi improvvisamente passi a 4 spazi (charsWaiting), quindi inizi un ciclo con do() ed apri la graffa, ma poi quello che segue è indentato allo stesso modo, quindi apri uno switch() ed il "case" lo indenti ma poi il suo contenuto è allo stesso livello del "case"... Insomma, un "case"ino. :wink:
Poi ti consiglio di mettere sempre uno spazio tra istruzione condizionale o ciclo e sua graffa, oppure metterla a capo, così come se metti i commenti sulla stessa riga si leggono meno facilmente (anche se dipende comunque dalla larghezza della tua finestra di codice, io al massimo li lascio sulle dichiarazioni di variabili e nei "case", il resto sempre a capo).

L'indentazione corretta anche per i commenti per me sarebbe:

...
  byte charsWaiting; //store how many data is waiting on nic buffer
  //update data waiting in nic buffer
  charsWaiting = exe_client.available(); 
  do { 
    //start reading data
    c = exe_client.read();
    switch (c) {
      case 0x08://backspace received come back on user data buffer
        if (charsReceived==0) 
        { //too many backspace, ring my bell!
          exe_client.print(0x07); 
...

Stasera con calma magari vedo più in dettaglio il codice, ma inizia a dare un Ctrl-T ;).

Non capisco perché parli di "label" (etichetta)... Di solito si intende l'etichetta a cui saltare con un (orrore! :slight_smile: ) goto...

Datman:
Non capisco perché parli di "label" (etichetta)... Di solito si intende l'etichetta a cui saltare con un (orrore! :slight_smile: ) goto...

Perchè qui switch...case - Arduino Reference le chiama così...

Docdoc hai ragione, sinceramente non avevo visto che con "san" ctrl-t identasse in automatico, di solito (commenti a parte che li infilavo un po a caso) me lo sistemavo a mano, li dove sono passato da 2 a 4 non l'ho proprio visto, nei case poi effettivamente identavo solo dopo lo switch, non i singoli case... chiedo venia :sob: