Char e stringhe da comprendere

Ciao
Sto' realizzando un programma per interrogare un modem con comandi predefiniti che richiamo da seriale, con uno switch/case e per fare pratica con i char invece delle String
Se richiamo i case con "comando" funziona
Ma volevo riuscire a farlo con i char e se digito 25 il case eseguito e' il 2 e lo capisco per come e' lo switch ma se lascio [] vuote o mancanti ho un errore di compilazione
Mi passate qualche link

 //switch (casoSingolo[0]) {
  switch (casoStringa[0]) {
      //switch (comando) {

    case 25:
      Serial.println("caso 1 Manufacturer identification +CGMI");
      Serial.println("AT+CGMI");
      delay(100);
      Modem.print("AT+CGMI;\r\n");
      delay(100);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      comando = 0;
      break;

    case '2':
      Serial.println("caso 2 Request model identification +CGMM");
      Serial.println("AT+CGMM");
      Modem.print("AT+CGMM;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

sotto lo sketch completo

/*
per testare i comandi AT 
                                                            v30

09:23:01.762 -> caso 7 Signal Quality +CSQ
09:23:01.762 -> AT+CSQ
09:23:03.244 -> AT+CSQ;
09:23:03.245 -> 
09:23:03.245 -> +CSQ: 23,0
09:23:03.245 -> 
09:23:03.245 -> OK
09:23:03.245 -> 
09:23:03.245 -> 

09:33:01.690 -> caso 2 Request model identification +CGMM
09:33:01.690 -> AT+CGMM
09:33:03.869 -> AT+CGMM;
09:33:03.869 -> 
09:33:03.869 ->  MULTIBAND  900E  1800 
09:33:03.902 -> 
09:33:03.902 -> OK
09:33:03.902 -> 
09:33:03.902 -> 
 */
#include <AltSoftSerial.h>  //tx 9 rx 8

char incoming_char;
char stringOne[100];
char substringa[100];
char response[100];

char PhoneNumber1[17];

char casoSingolo[4];
char casoStringa[4];

int comando;

//constexpr char Character_[3] = { "DH" };

byte i, j, k;  //contatore

//comunicazione con modem GSM

AltSoftSerial Modem;

void setup() {

  delay(100);

  Serial.begin(9600);  //Initialize serial ports for communication.

  Modem.begin(9600);  //Initialize serial ports for communication.

  //lista comandi AT

  Serial.println("lista comandi AT digita numero");

  //General commands

  Serial.println("caso a 1 Manufacturer identification +CGMI");
  Serial.println("AT+CGMI;\r\n");

  Serial.println("caso b 2 Request model identification +CGMM");
  Serial.println("AT+CGMM;\r\n");

  Serial.println("caso c 3 Product Serial Number +CGSN");
  Serial.println("AT+CGSN;\r\n");

  Serial.println("caso d 4 Card Identification +CCID");
  Serial.println("AT+CCID;\r\n");

  Serial.println("caso e 5 Phone activity status +CPAS");
  Serial.println("AT+CPAS;\r\n");

  //Call Control commands

  Serial.println("caso d Dial command D");
  Serial.println("ATD+393385078535;\r\n");

  Serial.println("caso h Hang-Up command H");
  Serial.println("ATH;\r\n");

  //Network service commands

  Serial.println("caso g 7  Signal Quality +CSQ");
  Serial.println("0-10 not ok");
  Serial.println("11-31 ok");
  Serial.println("32-98 not defined");
  Serial.println("99 no measure available");
  Serial.println("AT+CSQ;\r\n");

  Serial.println("caso h 8  Network registration +CREG");
  Serial.println("AT+CREG?;\r\n");

  //Security commands

  Serial.println("caso p Enter PIN +CPIN");
  Serial.println("AT+CPIN=6113;\r\n");

  Serial.println("caso b Incoming Call Bearer +CICB");
  Serial.println("AT+CICB?\r\n");

  Serial.println("caso c Incoming Call Bearer +CICB");
  Serial.println("AT+CICB=2\r\n");

  //imizializzo vettori

  memset(stringOne, 0, sizeof(stringOne));
  memset(substringa, 0, sizeof(substringa));

  memset(casoSingolo, 0, sizeof(casoSingolo));
  //memset(casoStringa, 0, sizeof(casoStringa));

  memset(incoming_char, 0, sizeof(incoming_char));

  PhoneNumber1[17] = { "ATD+39xxxxx;\r\n" };

  i = 0;
  j = 0;
  k = 0;

  comando = 0;

  Serial.println("inizio programma");  //stampa di controllo

  delay(1000);
}

void loop() {

  ReadSerial();

  memset(incoming_char, 0, sizeof(incoming_char));  //svuoto vettore
  i = 0;                                            //azzero contatore

  delay(1000);

  //switch (casoSingolo[0]) {
  switch (casoStringa[0]) {
      //switch (comando) {

    case 25:
      Serial.println("caso 1 Manufacturer identification +CGMI");
      Serial.println("AT+CGMI");
      delay(100);
      Modem.print("AT+CGMI;\r\n");
      delay(100);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      comando = 0;
      break;

    case '2':
      Serial.println("caso 2 Request model identification +CGMM");
      Serial.println("AT+CGMM");
      Modem.print("AT+CGMM;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case '3':
      Serial.println("caso 3 Product Serial Number +CGSN");
      Serial.println("AT+CGSN");
      Modem.print("AT+CGSN;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case '4':
      Serial.println("caso 4 Card Identification +CCID");
      Serial.println("AT+CCID");
      Modem.print("AT+CCID;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case '5':
      Serial.println("caso 5 Phone activity status +CPAS");
      Serial.println("AT+CPAS");
      Modem.print("AT+CPAS;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case '6':
      Serial.println("caso 6 Hang-Up command H");
      Serial.println("ATH");
      Modem.print("AT+H;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case '7':
      Serial.println("caso 7 Signal Quality +CSQ");
      Serial.println("AT+CSQ");
      Modem.print("AT+CSQ;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case '8':
      Serial.println("caso 8 Network registration +CREG");
      Serial.println("AT+CREG?");
      Modem.print("AT+CREG?;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case '9':
      Serial.println("caso 9 Network registration +CPIN");
      Serial.println("AT+CPIN");
      Modem.print("AT+CPIN?;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case 'a':
      Serial.println("caso d Dial command D");
      Serial.println("ATD");
      Modem.print("ATD+39123456789;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case 'b':
      Serial.println("caso b Incoming Call Bearer +CICB");
      Serial.println("AT+CICB?");
      Modem.print("AT+CICB?;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;

    case 'c':
      Serial.println("caso c Incoming Call Bearer +CICB");
      Serial.println("AT+CICB=2");
      Modem.print("AT+CICB=2;\r\n");
      delay(50);
      ReadModem();
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
      break;
  }

  delay(1000);
}

void ReadModem() {

  //while a character is coming from the Modem

  while (Modem.available() > 0)  //verifica la presenza di segnali
  {
    incoming_char = Modem.read();   //Get the character from the Modem port.
    stringOne[i] += incoming_char;  //compone la stringa
    i++;
    //Serial.println(stringOne);  //stampa di controllo
    delay(50);
  }

  Serial.println(stringOne);  //stampa di controllo

  // for (j = 17; j <= 18; j++) {

  //   Serial.print(stringOne[j]);  //stampa di controllo
  //   response[k] += stringOne[j];
  //   k++;
  // }

  Serial.println();
  /*
  Serial.println("response");          //stampa di controllo
  Serial.println(atoi(response) * 2);  //stampa di controllo
*/
  //Serial.println("stringOne");  //stampa di controllo
  //Serial.println(stringOne);    //stampa di controllo

  memset(stringOne, 0, sizeof(stringOne));          //svuoto vettore
  memset(incoming_char, 0, sizeof(incoming_char));  //svuoto vettore
  memset(response, 0, sizeof(response));            //svuoto vettore

  i = 0;  //azzero contatore
  k = 0;
}

void ReadSerial() {

  //while a character is coming from the Serial

  while (Serial.available() > 0) {

    incoming_char = Serial.read();  //Get the character coming from the terminal

    if (incoming_char == 'z') {
      Serial.println("resetto memoria");
      memset(casoSingolo, 0, sizeof(casoSingolo));
      memset(casoStringa, 0, sizeof(casoStringa));
    }

    //Serial.println(incoming_char);  //stampa di controllo

    casoSingolo[i] = incoming_char;
    casoStringa[i] += incoming_char;

    Serial.print('i');               //stampa di controllo
    Serial.print('\t');              //stampa di controllo
    Serial.print(i);                 //stampa di controllo
    Serial.print('\t');              //stampa di controllo
    Serial.print("casoSingolo[i]");  //stampa di controllo
    Serial.print('\t');              //stampa di controllo
    Serial.println(casoSingolo[i]);  //stampa di controllo

    comando = atoi(casoSingolo);

    Serial.println("comando");
    Serial.println(comando);

    Serial.println("casoSingolo");
    Serial.println(casoSingolo);

    Serial.println("casoStringa");
    Serial.println(casoStringa);

    i++;

    delay(50);
  }
}

un singolo char non può contenere la stringa "25"; per fare qesto devi usare, appunto, le stringhe (s piccola) cioè array di char o le Stringhe (S grande); nel tuo caso stai tentando di usare le stringhe; una cosa che devi tenere presente è che in arduino non pui usare le stringhe per verificare i vari case dello switch...devi convertire la stringa in ingresso nel numero che ti interessa...per far questo ci sono diversi modi...uno potrebbe essere Serial.parseInt()

grazie per ilriscontro

quello che capsico e' che il "25" e' un vettore 2 e 5 e capsico che venga eseguito il case 2
a questo link

progmem

// save some chars
const char signMessage[] PROGMEM = {"I AM PREDATOR,  UNSEEN COMBATANT. CREATED BY THE UNITED STATES DEPART"};

{'I', ,'A','M',...}

E' la stressa cosa corretto?

quello che volevo tentare era creare/capire come creare dei case con lettere, lettere e numeri tipo

AA1
BB1
pag25
pag46

Lo switch-case ammette solo variabili integer...
Vedi reference
Se vuoi usare i char array puoi usare una catena di if.

Come ti hanno già detto non è possibile "di base".
Uno dei work-around più efficaci è eseguire lo switch/case non sulla C String stessa, ma su un eventuale hash di tipo intero che corrisponde alla stringa in questione.

1 Like

grazie
piu' complicato di quello che pensavo, comunque me lo studio
usa lo String che leggo sempre sconsigliato

avevo messo un cuoricino
poi sono andato a vedere

lo ho appena tolto

per l'uso di String,
perché è una semplice elencazione con un calcolo creato ad HOC
per l'uso della parola chiave const per dichiarare una funzione

si tratta di esempi pessimi che certamente non aiutano a capire, e se un principiante capisce qualcosa quasi certamente capisce sbagliato

mi spiace, ma non posso dirti bravo, anzi

Si può usare senza alcun problema un char array ed infatti ho dovuto usare il metodo c_str() della classe String proprio per passare la C string alla funzione hash().

Ad ogni modo in questo caso la variabile di tipo String è locale: lo spazio necessario in memoria viene allocato quando serve e poi liberato non appena si esce dallo scope in cui è dichiarata (in altre parole al di fuori dell'if, la variabile non esiste più).

grazie per la spiegazione, ho capito la differenza

Si tratta solo del riadattamento del codice in ambiente Arduino di cui ho messo il link stackoverflow per correttezza all'inizio, non è tutta farina del mio sacco.

Ad ogni modo

  • non sono uno di quelli che fa le crociate contro la classe String a prescindere; uso questa classe in moltissimi dei miei progetti (con le dovute accortezze con le MCU dotate di poca memoria) e non ho mai riscontrato nemmeno l'ombra dei tanto temuti problemi di frammentazione.

Che si debba conoscere e bene (!) come manipolare le C string è un dato di fatto, ma i consigli che troppo spesso vengono dati sull'evitare nel modo più assoluto la classe String, secondo me non fanno altro che distogliere l'attenzione sui reali problemi degli sketch sui quali i principianti tipicamente chiedono aiuto ovvero nel 99% dei casi la palese carenza e inadeguatezza dell'algoritmo che è stato implementato per risolvere il problema. Ci si concentra in dettagli di secondaria importanza o in questioni di lana caprina perdendo di vista la visione di insieme.

  • come l'autore del codice stackoverflow anche io uso const quando è opportuno perché cosi sfrutto eventuali messaggi del compilatore se accidentalmente tento di fare qualcosa che non dovrei fare (in questo caso è evidente che la funzione hash non deve alterare l'argomento, se ci provo per sbaglio il compilatore me lo segnala).
    Concordo che probabilmente il const prima della funzione può essere ritenuto una precauzione eccessiva.

Un principiante non può pretendere di imparare se si limita a guardare distrattamente un esempio e a fare il copia e incolla. Si deve chiedere il perché è stata fatta una cosa in un modo piuttosto che in un altro e approfondire di conseguenza.
Mezzi e possibilità di questi tempi ce ne sono anche troppi, se non lo fa è solo pigrizia.

1 Like

ah guarda
in questo ti do ragione

Evitare lo String oltre al fatto che viene suggerito nasceva dal fatto che a me piace avere il controllo di cosa fa il programma anche per evitare di avere risultati che non comprendo e oberare il forum con domande sciocche, quindi i vettori non mi dispiacciono
Grazie per i riscontri

A conferma di quanto ho scritto più su, l'uso di una variabile di tipo String nell'esempio non è in alcun modo vincolante e sta distogliendo l'attenzione. Ho usato una String solo perché più comoda per leggere l'input della seriale, ma se ti piace di più puoi usare un char array, il risultato non cambia.

Qui la questione è se è possibile oppure no, usare uno switch/case con variabili diverse dal tipo intero (int, long, char etc etc) e la risposta è no.

se ho capito quando scrivo case '2': equivale a case 50: corretto?
diversamente se voglio una combinazione di carateri e numeri questa va' convertita in un numero con l esempio che hai fornito su wokwi

stavo studiando questi
https://www.arduino.cc/reference/en/language/structure/pointer-access-operators/reference/
https://www.arduino.cc/reference/en/language/structure/pointer-access-operators/dereference/

grazie per i riscontri

Si perché il carattere '2' (con gli apici e non con le virgolette) corrisponde al decimale 50 come puoi verificare tu stesso nella tabella ASCII.

Per quanto riguarda i puntatori, il reference di Arduino è decisamente troppo scarno per poter comprendere bene il concetto che è di difficile comprensione per chi è all'inizio, ma di fondamentale importanza in C/C++.

Tieni inoltre in considerazione che il passaggio di argomenti per riferimento come ho fatto nella funzione void parseInput(const String& input) è valido solo in ambiente C++ .
Arduino usa un compilatore C++, quindi non ci sono problemi, ma se usi manuali del C non ne troverai traccia e ci saranno indicazioni solo per il passaggio di argomenti per puntatore void parseInput(const String* input) .

Può sembrare la stessa cosa a prima vista, ma ci sono delle piccole differenze.
Tendenzialmente il passaggio per riferimento è considerata una tecnica più sicura perché non sono accettati valori NULL o void che potrebbero causare bug e malfunzionamenti quando non gestiti come dovuto (molto più insidiosi dell'uso della classe String).

discussione molto utile per me
grazie per la conferma e soprattutto per l impegno di spiegare

Per di più non è un problema anche con la classe String.

if (Serial.available()) {
    String msg = Serial.readStringUntil('\n');
    char *p = msg.c_str();
    Serial.print("addr of msg: ");
    Serial.println((uint16_t)&msg, HEX);
    Serial.print("addr of p: ");
    Serial.println((uint16_t)p, HEX);
    Serial.println("");
    for (byte i=0; i<strlen(p); i++) {
        Serial.print(*(p+i));
    }
    Serial.println();
    parseInput(msg);
  }

Come si vede sotto l'ouput è sempre lo stesso ciò che varia è l'input 123, 1234 ecc. Gli indirizzi hanno sempre lo stesso valore,
msg vive nello stack e si capisce per l'indirizzo alto, mentre il buffer di msg vive in heap.

Type a command: 
addr of msg: 8F4
addr of p: 244

123
193432059
Not a valid command.
addr of msg: 8F4
addr of p: 244

1234
2088290703
Not a valid command.
addr of msg: 8F4
addr of p: 244

123456
2110877786
Not a valid command.
addr of msg: 8F4
addr of p: 244

12345678
938407465
Not a valid command.

In sostanza non è un problema. Lo può diventare un problema continuando ad allocare altre (tante String) sempre di differente dimensione. Se proprio vogliamo fare una critica questa riguarda la configurazione del terminale di comando che se configurato male non fa riconoscere i comandi e questo è risolvibile usando il metodo tradizionale per collezionare i caratteri da seriale dentro un array, con questo metodo non importa come è configurato il terminale funziona sempre sia con '\r', '\n' e '\r\n'.

Quindi @stefa24 vai tranquillo con il codice di @cotestatnt ma fai attenzione a quello che combini con le altre String. :smiley:

Ciao.

grazie a tuuti per spiegare
mi salvo questa lezione

ciao
solo per conoscenza un video di Aliverti al min 09 23
https://youtu.be/i3qXD9WTHA4?t=563

Ma a cosa ti riferisci?
Al fatto che stai usando json?
o al fatto che dichiara const char * name = " ecc ";
Io l'ho visto tutto il video ma mi sono dimenticato al minuto indicato cosa c'è. ahhh si il fatto che la c string null terminated sono più risparmiose di ram. :smile:

Nel caso specifico il contenuto tra apici è una c string literal, di questa si occupa il compilatore durante la compilazione nel riservargli una posizione in ram ad una specifica locazione di memoria che viene salvata nel puntatore che è esso stesso una variabile che si trova in ram ad un altro indirizzo.

Se conosci l'indirizzo puoi stampare a partire da questo il contenuto di ogni cella di memoria ram.

PS: il codice precedente è valido solo per architettura AVR, su ESP, ARM ecc al posto di (uint16_t) ci va (uint32_t).

Ciao.