Inserire le credenziali per la connessione wifi dal monitor seriale

Ciao a tutti,

sto realizzando un piccolo display a batteria con un modulo esp8266 (wemos d1 mini per la precisione) per visualizzare alcuni dati provenienti dal mio sito meteorologico.
Siccome questo oggetto che devo fare dovrà collegarsi a diverse reti wifi perché verrà in giro con me avevo intenzione di inserirgli le credenziali d’accesso al wifi man mano che mi serviva e poi eventualmente salvarle nella “EEPROM”. Allora mi sono messo a fare alcune prove utilizzando il monitor seriale perché non ho ancora i display e anche perché così sono più veloce a fare uno schizzo di prova e il problema che sto avendo riguarda l’inserimento delle variabili all’interno di WiFi.begin();
Se io inserisco le credenziali nello sketch negli array ssid e pw

char ssid[20] = "SSID ROUTER"; //inserire l'ssid del router 
char pw[20] = "PASSWORD ROUTER"; //inserire password del router

mettendole all’interno di WiFi.begin(ssid, pw);WiFi.begin(ssid, pw);
e ignoro quelle che mi chiede via monitor seriale, il modulo si collega correttamente.
Se invece non do alcuna credenziale sullo sketch, ma voglio aggiungere le credenziali via monitor seriale
utilizzando un altra variabile, ma uguale

char str_ssid[20]; //variabile dove viene salvata l'ssid inserita dal monitor seriale
char str_pw[20];  //variabile dove viene salvata la password inserita dal monitor seriale

e le inserisco dentro al comando, il modulo non si connette non ostante le credenziali siano le stesse.

Ecco il codice completo, sia con le credenziali inserite sullo sketch, sia quello con le credenziali inviate dal monitor seriale ( basta cambiare le variabili su WiFi.begin(); ).

Qua vengono utilizzate le credenziali inserite nello sketch

#include <ESP8266WiFi.h>

/*VARIABILI*/
String show_ssid;
String show_pw;
char ssid[20] = "Pinco123"; //inserire l'ssid del router 
char pw[20] = "passworddipinco123"; //inserire password del router
char c;
char str_ssid[20]; //variabile dove viene salvata l'ssid inserita dal monitor seriale
char str_pw[20];  //variabile dove viene salvata la password inserita dal monitor seriale
uint8_t cont_str_ssid; //contatore per l'array str_ssid
uint8_t cont_str_pw; //contatore per l'array str_pw

void setup() {
  Serial.begin(9600);
  Serial.println("Avvio");

  //RICHIESTA CREDENZIALI PER IL ROUTER
  Serial.println("Inserire SSID");
  while(c != '\n'){
    if(Serial.available()>0){
      c = Serial.read();
      cont_str_ssid++;
      str_ssid[cont_str_ssid] = c; //inserisco i singoli caratteri nell'array dell'ssid
      show_ssid += c; //creo una stringa con il nome dell'ssid, ma non è neccessario
    }
  }
  c = ' ';
  Serial.println(show_ssid);

  Serial.println("Inserire password");
  while(c != '\n'){
    if(Serial.available()){
      c = Serial.read();
      cont_str_pw++;
      str_pw[cont_str_pw] = c; //inserisco i singoli caratteri nell'array della password
      show_pw += c; //creo una stringa con il nome dell'ssid, ma non è neccessario
    }
  }
  c = ' ';
  Serial.println(show_pw);
  delay(200);

  //verifico che gli array dell'ssid e della password siano corretti
  for(int i=0;i<sizeof(str_ssid);i++){
    Serial.print(str_ssid[i]);
  }
  for(int i=0;i<sizeof(str_pw);i++){
    Serial.print(str_pw[i]);
  }

  //Avvio la connessione
  WiFi.begin(ssid, pw);
  Serial.println("Recupero i dati");
  while(WiFi.status() != WL_CONNECTED){
    Serial.print(WiFi.status());
    Serial.print(".");
    delay(500);
  }
  Serial.println("Connesso");
}

void loop() {
}

qua vengono inserite le credenziali ricevute dal monitor seriale

#include <ESP8266WiFi.h>

/*VARIABILI*/
String show_ssid;
String show_pw;
char ssid[20] = "SSID ROUTER"; //inserire l'ssid del router 
char pw[20] = "PASSWORD ROUTER"; //inserire password del router
char c;
char str_ssid[20]; //variabile dove viene salvata l'ssid inserita dal monitor seriale
char str_pw[20];  //variabile dove viene salvata la password inserita dal monitor seriale
uint8_t cont_str_ssid; //contatore per l'array str_ssid
uint8_t cont_str_pw; //contatore per l'array str_pw

void setup() {
  Serial.begin(9600);
  Serial.println("Avvio");

  //RICHIESTA CREDENZIALI PER IL ROUTER
  Serial.println("Inserire SSID");
  while(c != '\n'){
    if(Serial.available()>0){
      c = Serial.read();
      cont_str_ssid++;
      str_ssid[cont_str_ssid] = c; //inserisco i singoli caratteri nell'array dell'ssid
      show_ssid += c; //creo una stringa con il nome dell'ssid, ma non è neccessario
    }
  }
  c = ' ';
  Serial.println(show_ssid);

  Serial.println("Inserire password");
  while(c != '\n'){
    if(Serial.available()){
      c = Serial.read();
      cont_str_pw++;
      str_pw[cont_str_pw] = c; //inserisco i singoli caratteri nell'array della password
      show_pw += c; //creo una stringa con il nome dell'ssid, ma non è neccessario
    }
  }
  c = ' ';
  Serial.println(show_pw);
  delay(200);

  //verifico che gli array dell'ssid e della password siano corretti
  for(int i=0;i<sizeof(str_ssid);i++){
    Serial.print(str_ssid[i]);
  }
  for(int i=0;i<sizeof(str_pw);i++){
    Serial.print(str_pw[i]);
  }

  //Avvio la connessione
  WiFi.begin(str_ssid, str_pw);
  Serial.println("Recupero i dati");
  while(WiFi.status() != WL_CONNECTED){
    Serial.print(WiFi.status());
    Serial.print(".");
    delay(500);
  }
  Serial.println("Connesso");
}

void loop() {
}

Ho osservato che mentre tenta di collegarsi con le credenziali inserite nello sketch, WiFi.status() corrisponde a 6 che dovrebbe essere WL_DISCONNECTED, mentre quando tenta di collegarsi usando le credenziali ricevute dal monitor seriale, WiFi.status() corrisponde a 0 che è WL_IDLE_STATUS.
Io penso che il problema sia su come vengono salvati i caratteri ricevuti dal monitor seriale, ma non riesco a capire dove sia il problema visto che quando ho provato a stampare sul monitor seriale l’array dove vengono salvate le credenziali, mi viene mostrato tutto correttamente.

Grazie mille per l’aiuto.

Ciao, credo che il tuo problema possa essere che le tue stringhe non le hai correttamente terminate. Le stringhe classiche del C prevedono che vi sia un carattere terminatore

\0

Dopo l'ultimo carattere. Trovo poi quantomeno strano creare una variabile di tipo oggetto String (che sempre ricordo essere il MALE assoluto su MCU tipo Arduino) per contenere il solito valore dell'array di char, perché non stampi direttamente quello sulla seriale senza creare qul mostro innutile? Quindi ti suggerisco di mettere questo:

str_pw[++cont_str_pw] = '\0';

al posto di

c = ' ';

e sostituire

Serial.println(show_ssid);

con

Serial.println(str_ssid);

eliminare tutto ciò che riguarda l'uso di oggetti String e verificare se era quello il problema

fabpolli: Ciao, credo che il tuo problema possa essere che le tue stringhe non le hai correttamente terminate.

Per una volta che avevo la risposta pronta (forse) :( scusa la posto comunque :)

Non sono sicuro che vada bene come hai fatto, non termini mai la stringa, prova cosi:

 while (Serial.available() > 0) {
    delay(1);
    c = Serial.read();
    if (c == '\n') {
      str_ssid[cont_str_ssid] = '\0';
    } else {
      str_ssid[cont_str_ssid] = c;
      cont_str_ssid++;
    }
  }

Federico

A dire il vero ho visto solo ora che la lettura dell'user e la password sono errate:

while(c != '\n'){
    if(Serial.available()){
      c = Serial.read();
      cont_str_pw++;
      str_pw[cont_str_pw] = c; //inserisco i singoli caratteri nell'array della password
      show_pw += c; //creo una stringa con il nome dell'ssid, ma non è neccessario
    }
  }

In questo modo metti l'invio (\n) nella stringa, quindi sarà anche per questo che non si collega, io farei:

cont_str_pw=0;
while(Serial.available())
{
    c = Serial.read();
    if(c != '\n')
    {  
      str_pw[cont_str_pw++] = c; //inserisco i singoli caratteri nell'array della password
    }
}
str_pw[cont_str_pw]='\0';

Ma anche così funziona solo se il tuo monitor seriale è imposatto per inviare SOLO il carattere \n, se invece (come normalemente è) invia sia \n che \r allora avrai comunque il problema che inserirai un caratteri di troppo, ma questo lo lascio a te ;)

Federico66: Non sono sicuro che vada bene come hai fatto, non termini mai la stringa, prova cosi:

 while (Serial.available() > 0) {
    delay(1);
    c = Serial.read();
    if (c == '\n') {
      str_ssid[cont_str_ssid] = '\0';
    } else {
      str_ssid[cont_str_ssid] = c;
      cont_str_ssid++;
    }
  }

Federico

Andrebbe anche bene se non che vale il solito discorso che ho fatto al post prima se arriva anche lo \r dopo lo \n sovrascriveresti l'ultimo carattere (che nel tuo caso sarebbe proprio il terminatore) e uscendo dal ciclo avresti comunque la stringa non terminata. Per sicurezza si consiglia di mettere il terminatore sempre fuori dai cicli che popolano la stringa così si ha la certezza che se il ciclo termina prematuramente o per qualsivoglia motivo la stringa popolata dal ciclo non è terminata allora mettendo il terminatore cempre e comunque allora la stringa sarà terminata correttamente

fabpolli: Andrebbe anche bene se non che vale il solito discorso che ho fatto al post prima se arriva anche lo \r dopo lo \n sovrascriveresti l'ultimo carattere (che nel tuo caso sarebbe proprio il terminatore) e uscendo dal ciclo avresti comunque la stringa non terminata.

di solito lo escludo con:

//
 } else if (rc == '\r') { }
//

fabpolli: Per sicurezza si consiglia di mettere il terminatore sempre fuori dai cicli che popolano la stringa così si ha la certezza che se il ciclo termina prematuramente o per qualsivoglia motivo la stringa popolata dal ciclo non è terminata allora mettendo il terminatore cempre e comunque allora la stringa sarà terminata correttamente

Grazie, a questo non avevo pensato

Federico

Concludo il pistolotto di post (oggi non ho la testa mi ricordo le cose a pezzi) Così come è stato scritto quel ciclo è pericolossissimo perché se da monitor seriale ti arriva una stringa più grande di quanto hai definito l'array di char succede un bel casino (vai a scrivere in un area di memoria che è stato potenzialmente assegnato ad altre variabili) quindi è sempre buona cosa verificare che l'indice in cui si sta scrivendo NON superi la dimensione massima dell'array di char definito meno uno (l'ultimo carattere ci servirà sempre e comunque per il terminatore quindi se anche lo uasssimo verrebbe sovrascritto)

fabpolli: Così come è stato scritto quel ciclo è pericolossissimo perché se da monitor seriale ti arriva una stringa più grande...

Anche non inserisco mai stringhe da monitor, ma solitamente controllo l'indice, e nel caso superi il max, reinizializzo ad uno e sovrascrivo la stringa... almeno non va in errore.

Federico

Federico66: Anche non inserisco mai stringhe da monitor, ma solitamente controllo l'indice, e nel caso superi il max, reinizializzo ad uno e sovrascrivo la stringa... almeno non va in errore.

Federico

E fai bene per entrambe le cose :D A parte gli scherzi, difficilmente un progetto Arduino riceverà dati dal monitor seriale se non in fase di sviluppo comunque qualunque sia la fonte della stringa è sempre imperativo evitare che un qualcosa di "esterno" posso minare la satbilità del codice. Tu hai scelto di sovrascrivere dall'indice zero, è una strategia valida come un'altra dipende dal programma quello che è giusto è non superare mai la lunghezza massima. Anche qui può andare a gusti (o in ambito lavorativo dalle specifiche e regole di sviluppo che imposta l'azienda) ma solitamente si consiglia di usare delle #define per indicare e verificare la dimensione dell'array, è vero che la verifica si può fare con la sizeof ma anche qui dipende da caso a caso. Anche se poco efficiente io a volte faccio un memset con tutto impostato a 0 (Zero che corrisponde a '\0') per ripulire il buffer e non devo terminare dopo, ma sono cose fatte "tanto per provare"

fabpolli:
… solitamente si consiglia di usare delle #define per indicare e verificare la dimensione dell’array, è vero che la verifica si può fare con la sizeof ma anche qui dipende da caso a caso…

Si solitamente uso le #define
e sto molto attento alla sizeof, ho capito a mie spese che non sempre il risultato è quello che ti aspetti :frowning:

Nel mio ultimo lavoro con le immagini, mi ha fatto impazzire!

Federico

Cavolo avete risposto in tanti e sinceramente non me lo aspettavo in così poco tempo. Adesso mi leggo per bene tutto quello che avete scritto e mi metto al lavoro. Mi sa che il problema così a prima vista sia il terminatore che probabilmente in altri tentativi non avevo implementato bene. Comunque per il controllo della lunghezza dell'array non lo avevo messo semplicemente perché questo non è il codice definitivo, è solamente una prova per capire come scrivere al meglio il codice finale. Il codice finale avrà un limite di 20 byte per ssid e pw più qualche byte di margine.

fabpolli: A dire il vero ho visto solo ora che la lettura dell'user e la password sono errate:

while(c != '\n'){
    if(Serial.available()){
      c = Serial.read();
      cont_str_pw++;
      str_pw[cont_str_pw] = c; //inserisco i singoli caratteri nell'array della password
      show_pw += c; //creo una stringa con il nome dell'ssid, ma non è neccessario
    }
  }

In questo modo metti l'invio (\n) nella stringa, quindi sarà anche per questo che non si collega, io farei:

cont_str_pw=0;
while(Serial.available())
{
    c = Serial.read();
    if(c != '\n')
    {  
      str_pw[cont_str_pw++] = c; //inserisco i singoli caratteri nell'array della password
    }
}
str_pw[cont_str_pw]='\0';

Ho risolto, c'era un errore con il terminatore. Mi sono basato sul tuo consiglio anche se ho fatto delle modifiche

Serial.println("Inserire SSID");
  while(1==1){
    if(Serial.available()){
      c = Serial.read();
      if(c != '\r'){
        str_ssid[cont_str_ssid++] = c;
        //Serial.println(c);
      }else{
        Serial.flush();
        goto out_ssid;
      }
    }
  }
  out_ssid:
  str_ssid[cont_str_ssid] = '\0';

  Serial.println("Inserire pw");
  while(1==1){
    if(Serial.available()){
      c = Serial.read();
      if(c != '\r'){
        str_pw[cont_str_pw++] = c;
        //Serial.print(c);
      }else{
        Serial.flush();
        goto out_pw;
      }
    }
  }
  out_pw:
  str_pw[cont_str_pw] = '\0';

Se non si è notato ho usato un goto anche se non è molto amato

Allora … alcune “dritte” … :wink:

  1. per ripetere un while all’infinito basta scrivere “while(true) { … }”, più elegante che scrivere “while (1==1) { … }”

  2. abbiamo detto e ripetuto più volte che per fare cicli infinti è più efficiente scrivere “for( ; ; ) { … }” che il while(true) (dipende da alcune ottimizzazioni del compilatore).

  3. cortesemente elimina quella schifezza di “goto” … per uscire da qualsiasi ciclo c’è l’apposita istruzionebreak”, usa quella!

Guglielmo

E se tu inizializzassi gli array di char prima di scriverli sarebbero anche già correttamente terminati senza bisogno che lo facessi tu apposta

Una cosa del tipo:

char ssid[20] = '\0';

... ma perché...

perché usare un loop infinito e poi verificare una condizione, quando può semplicemente usare

while (Serial.available())

perché adesso escludi solo /r, devi escludere anche /n, altrimenti devi ricordarti di configurare correttamente il monitor seriale

perché scomodare un goto, se scrivi correttamente il ciclo, non serve

infine, visto che invii da monitor, aggiungi un controllo sulla lunghezza, come ti ha consigliato @fabpollli

Federico

Federico66: perché usare un loop infinito e poi verificare una condizione, quando può semplicemente usare

while (Serial.available())

.... occhio che questo richiede una certa attenzione ... spesso la Serial.available() è più veloce della ricezione stessa e ... si esce da quel loop prima che effettivamente sia terminata la trasmissione (cosa provata e verificata). Sempre meglio utilizzare o il riconoscimento di un terminatore o un timeout, ma NON così come suggerisci.

Guglielmo

C1P8: E se tu inizializzando gli array di char prima di scriverli sarebbero anche già correttamente terminati senza bisogno che lo facessi tu apposta ....

SE non conosce la lunghezza esatta di ciò che riceverà, o SE deve ricevere cose di lunghezza variabile, NON si può inizializzare solo 'ultimo elemento, ma si può, allo stesso modo, riempire l'intero array di 0x00 così, dovunque si arrivi, comunque dopo c'è il terminatore ;)

Guglielmo

Se scrive il primo zero tutti gli altri sono automatici

Ho studiato elettronica, non informatica, è vero

Ma il reference del linguaggio lo so leggere

C1P8:
Ma il reference del linguaggio lo so leggere

:slight_smile: … Hai ragione, ma … la cosa NON è sempre quella corretta, come, ad esempio, spiegato QUI.

Guglielmo

P.S.: Comunque il mio post era derivato da una mia cattiva lettura del tuo, NON avevo visto che era in fase di dichiarazione dell’array, ma mi era sembrata l’assegnazione al 20esimo carattere :wink: