Dividere stringa letta da Serial Monitor

Salve a tutti,
Come da titolo vorrei "dividere" una unica stringa inserita nel monitor seriale... in particolare dividere le parole interrotte da uno spazio in altrettante variabili.
Mi spiego meglio:
Immaginiamo di inserire nel monitor: "Albero Collina Mare".... vorrei fare in modo che il programma crei tre stringhe chiamate "Albero", "Collina", "Mare".
Con questa parte di codice riesco a leggere una stringa, ma non due o tre:

char* serialString()

{
  static char str[21]; // For strings of max length=20
  if (!Serial.available()) return NULL;
  delay(64); // wait for all characters to arrive
  memset(str,0,sizeof(str)); // clear str
  byte count=0;
  while (Serial.available())
  {
    char c=Serial.read();
    if (c>=32 && count<sizeof(str)-1)
    {
      str[count]=c;
      count++;
    }
  }
  str[count]='\0'; // make it a zero terminated string
  return str;
}

Potete indicarmi dove STUDIARE per risolvere l' aquisizione in variabili distinte?
Grazie

strtok

Grazie vbextreme, ho studiato e sono riuscito ad applicare questo codice:

//   strtok test                                                               */

#include <string.h>

#define MAX_STRING_LEN  20

char *record1 = "Albero Collina Mare";

char *p, *i;

void setup() {

  Serial.begin(9600);
 
  Serial.println("Split record1: ");
  Serial.println(subStr(record1, " ", 1));
  Serial.println(subStr(record1, " ", 2));
  Serial.println(subStr(record1, " ", 3));

}

void loop () {
}

// Function to return a substring defined by a delimiter at an index
char* subStr (char* str, char *delim, int index) {
  char *act, *sub, *ptr;
  static char copy[MAX_STRING_LEN];
  int i;

  // Since strtok consumes the first arg, make a copy
  strcpy(copy, str);

  for (i = 1, act = copy; i <= index; i++, act = NULL) {
     //Serial.print(".");
     sub = strtok_r(act, delim, &ptr);
     if (sub == NULL) break;
  }
  return sub;

}

La domanda ora è come posso inserire (subStr(record1, " ", 1)) all'interno di una variabile ?
Ossia, me lo scrive correttamente con la Serial.print, ma io ho bisogno di confrontarlo con un if, ad esempio così:

alstringa = (subStr(record1, " ", 1))
if ((alstringa) == "Albero") { al=1;}

Grazie

#include <string.h>

#define MAX_STRING_LEN  20

char record1[] = "Albero Collina Mare";

void setup() {
   char *mem[10];
    byte countmem=0;

  Serial.begin(9600);
 
  Serial.println("Split record1: ");

  mem[countmem++]= strtok(record1, " ");
  while( NULL != p  && countmem < 10)
  {
     mem[countmem++] = strtok(NULL, " ");
   }

    byte i;
    for(i = 0; i < countmem; ++i)
        Serial.println(mem[i]);

}

record1 non è più una costante.
scansiono record1 con strtok e salvo i puntatori in un vettore, la strtok mi mette gli 0 dove necessario e proseguo fino a NULL.
Scorro il vettore mem e lo visualizza su seriale.

[edit]scusate per l'indentazione ma oggi il cellulare fa le bizze[/edit]

vbextreme grazie tantissimo dell'aiuto.

Il tuo codice mi riporta :'p' was not declared in this scope

Comunque dovrei aver capito il tui ragionamento.

Sarò via il week-end.... appena possibile lo studio e ti racconto se ho risolto

Un caro saluto

scusa ho sbagliato il ciclo, rispondo quasi sempre dal cellulare, scusa ancora.

mem[countmem]= strtok(record1, " ");
  while( NULL != mem[counter] && countmem < 9)
  {
    ++countmem;
     mem[countmem] = strtok(NULL, " ");
   }

o comunque qualcosa di simile

GRANDE vbextreme!
Grazie al tuo aiuto sono riuscito a dividere la stringa e perfino a fare in modo che ogni "pezzetto diviso dallo spazio" diventi un valore (perchè oltre a parole come Albero mi serviranno dei valori) così:

    String aNum = (mem[i]);
    int value = aNum.toInt();
    Serial.println(value);

Ora il problema mi rimane solo acquisire la stringa da tastiera (da monitor seriale) !!!

Ossia, questo è già "scritto" nel void setup:

char record1[] = "Albero Collina Mare";

Io, però, vorrei acquisire "Albero Collina Mare" da tastiera, ma non riesco.

Praticamente mi servirebbe char record1 [] = Le tre parole che immetto da tastiera

Vi viene in mente qualcosa ?

Grazie

@Zamundo: ... fai una ricerchina sul forum ... :smiling_imp: ... ne avremo parlato solo ... UN MILIONE DI VOLTE :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes: :stuck_out_tongue_closed_eyes:

Guglielmo

Guglielmo lo sai che scrivo solo se ho cercato.
La strada più semplice mi sembrerebbe questa:

void setup ()
{
Serial.begin(9600); 
String inputString = "";
inputString.reserve(100);

}

 void loop (){
  while (Serial.available())  {
    char c = (char)Serial.read();
    inputString += c;
   
    if (c == '\n') {
        //----------stringa su cui lavorare-----------
        Serial.println(inputString);
        //--------------------------------------------
        inputString = "";
    }
  }
}

Ma non funziona!

Da errore qui: inputString += c;

Hai presente quando ti senti ad un passo dal risolvere ????

:o :o :o

NON usare le String (classe) ... ed impara ad usare le stringhe del C ...

Sei su una piccola MCU e ... con la classe String, aggiornando in continuazione una stringa, tra alloc e free ... rischi di esaurire la SRAM ... quindi, dai retta, imparati bene quelle classiche del C (array di char) e usa la libreria, sempre inclusa e disponibile, AVR libc ... c'è TUTTO quello che ti occorre :wink:

Guglielmo

P.S.: Hai un MP :wink:

Guglielmo ho provato a "guardare" la AVR libc.... ma purtroppo non ho assolutamente le competenze per comprenderla a fondo.
Ho capito di provare ad usare l'array di char, però sinceramente non è intuititvo.
Adesso studio ancora.
In fondo si tratta solo di acquisire tre parole separate da spazi sul monitor seriale, dividere queste tre parole in variabili, trasformare le variabili in numeri e da li iniziare a programmare per davvero quello che i valori dovranno fare. ??!!??!!??!!??!!??!! :o

Zamundo ... se vuoi in futuro evitare errori molto difficili da diagnosticare, continuo a sconsigliarti caldamente l'uso della classe String.

Perdi un po' di tempo a studiare le stringhe del C (... prenditi un buon libro di C e dedicaci qualche giorno) ... non sono complesse, e, almeno ... hai tutto sotto controllo !!!

La AVR libc contiene veramente di tutto, ma tu intanto studia la parte <string.h> di cui, per altro, hai già visto la strtok() :wink:

Guglielmo

Allora datemi l'aiutino finale e ci siamo!

Con questo programma riesco a visualizzare la stringa immessa.

char* serialString()
{
  static char str[21]; // For strings of max length=20
  if (!Serial.available()) return NULL;
  delay(64); // wait for all characters to arrive
  memset(str,0,sizeof(str)); // clear str
  byte count=0;
  while (Serial.available())
  {
    char c=Serial.read();
    if (c>=32 && count<sizeof(str)-1)
    {
      str[count]=c;
      count++;
    }
  }
  str[count]='\0'; // make it a zero terminated string
  return str;
}

void setup() {
  Serial.begin(9600);
}

void loop()
{
  static boolean needPrompt=true;
  char* userInput;
  if (needPrompt)
  {
    Serial.print("Please enter inputs and press enter at the end:\n");
    needPrompt=false;
  }
  userInput= serialString();
  if (userInput!=NULL)
  {
    Serial.print("You entered: ");
    Serial.println(userInput);
   
    
    Serial.println();
    needPrompt=true;
  }
}

La mia ingenuità mi faceva pensare che userInput contenesse già la mia stringa da poter far "digerire" a quest'altro pezzo di codice:

#include <string.h>

#define MAX_STRING_LEN  20

char record1[] = "Albero Collina Mare";

void setup() {
   char *mem[10];
    byte countmem=0;

  Serial.begin(9600);
 
  Serial.println("Split record1: ");

  mem[countmem]= strtok(record1, " ");
  while( NULL != mem[countmem]  && countmem < 10)
  {
     ++countmem;
     mem[countmem] = strtok(NULL, " ");
   }

   
    Serial.println(mem[0]);
    Serial.println(mem[1]);
    Serial.println(mem[2]);

    String aNum1 = (mem[0]);
    int value1 = aNum1.toInt();
    Serial.println(value1);

    String aNum2 = (mem[1]);
    int value2 = aNum2.toInt();
    Serial.println(value2);

    String aNum3 = (mem[2]);
    int value3 = aNum3.toInt();
    Serial.println(value3);
    
    
    
        

}
void loop ()
{}

Mi manca solo come INSERIRE userInput al posto di "Albero Collina Mare"

:confused:

per leggere puoi usare questa:

uint8_t sex_read8()
{
    while ( !Serial.available() );
    return Serial.read();
}

char* sex_readstrln(char* d, byte sz)
{
    byte rd = 0;
    while( sz-- )
    {
        d[rd] = (char) sex_read8(); 
        if ( d[rd] == '\r'){ ++sz;continue;}
        if ( d[rd] == '\n') break;
        ++rd;
    }
    d[rd] = '\0';
    return d;
}

la usi cosi:

void loop()
{
    char* buffer[32];
    sex_readstrln( buffer, 32);
}

poi il resto te l'ho gia spiegato.

se il succo è comunque che vuoi leggere le stringhe separate da spazi puoi forkare la mia funzione e modificarla cosi:

char* sex_readtoken(char* d, byte sz)
{
    byte rd = 0;
    while( sz-- )
    {
        d[rd] = (char) sex_read8(); 
        if ( d[rd] == '\r'){ ++sz;continue;}
        if ( d[rd] == '\n' || d[rd] == ' ' ) break;
        ++rd;
    }
    d[rd] = '\0';
    return d;
}

ora richiamandola buffer conterrà solo il token separato da spazi e dall'enter.

esempio:

void loop()
{
    char buffer[32];
    sex_readtoken( buffer,32);
    Serial.println(buffer);
}

vbextreme mi stai aiutando davvero tanto e ti ringrazio.

Con questo codice riesco abbastanza bene, però "pretende" che anche l'ultima parola della frase abbia lo " spazio" come ultimo carattere e quindi non va benissimo.

In pratica se scrivo "Albero Collina Mare " è ok.
Se scrivo "Albero Collina Mare" perde il "mare" .... e se ci vogliamo fare un bagno come facciamo ? :slight_smile:

Scherzi a parte c'è modo di evitare di digitare lo spazio a fine frase ?

uint8_t sex_read8()
{
    while ( !Serial.available() );
    return Serial.read();
}

char* sex_readtoken(char* d, byte sz)
{
    byte rd = 0;
    while( sz-- )
    {
        d[rd] = (char) sex_read8();
        if ( d[rd] == '\r'){ ++sz;continue;}
        if ( d[rd] == '\n' || d[rd] == ' ' ) break;
        ++rd;
    }
    d[rd] = '\0';
    return d;
   
}

void setup (){Serial.begin(9600);}

void loop()
{
    char buffer[32];
    sex_readtoken( buffer,32);
    Serial.println(buffer);
    
    String parola = (buffer);
    if (parola == "albero"){Serial.println("OK");}

}

nel serial monitor in basso a destra hai l'opzione di terminatore di stringa, scegli crlf.
Adesso penso tu abbia settato a niente.
Per quello non preleva l'ultima parola, perché aspetta o il '\n' o lo spazio che fungono da terminatori del token.

Perfetto!
Adesso abbiamo vinto.

GRAZIE

:slight_smile:

il confronto io lo farei così:

if ( 0 == strcmp( buffer, "albero") )
    Serial.println("melo");

dimenticati della classe STRING

vbextreme:
dimenticati della classe STRING

Sono pagine che glie lo sto dicendo ...
... "errare humanum est, perseverare autem diabolicum" :smiling_imp:

Guglielmo

Ragazzi,
vorrei fare un post OT per ringraziarvi tutti !!!

Sono immensamente grato al progetto Arduino ed a Voi del Forum perchè considero una cosa meravigliosa la possibilità di insegnare ed apprendere gli uni dagli altri.
Giudico incredibile la libertà di condividere e l'entusiasmo di lavorare insieme a questa realtà che tanto ci appassiona.

In questo anno ho potuto realizzare molti progetti, soprattutto grazie al vostro aiuto.

Adesso, perfino, nel mio piccolo, ho iniziato ad insegnare qualcosa su argomenti su cui sono più ferrato (come quelli di ambito "agricolo" che talvolta vengono richiesti dai nuovi utenti).

Sarebbe bello conoscersi tutti di persona. (Con qualcuno di Roma abbiamo avuto il piacere di scambiare quattro chiacchiere dal vivo!)

Grazie
Vi abbraccio

Zamundo

PS Spero di non essere "diabolicum".... però considerate che non essendo programmatori, talvolta è difficile comprendere i consigli dati.... del resto l'approfondimento di alcune librerie con centinaia di istruzioni richiederebbe mesi di studio! Tradotto: siate un po' più clementi !!!! Vero Guglielmo ? :wink:

PPS Che l'elettrone sia con voi! :slight_smile: