GREP non vuol dire greppia, quindi venite a mangiare a questo setaccio

che sarebbe un gioco di parole su GREP che somiglia a greppia, ma che si comporta come un setaccio
in questo periodo si assiste al passaggio di persone che cercano termostati e altre che cercano di leggere la seriale ed interpretarla come comandi
volevo dare una mano ai primi, in considerazione del fatto che le termosonde DS18B20 hanno un tempo di conversione di circa 750mS, volevo vedere di realizzzare una versione “non bloccante” della lilbreria, che permettess il “lancio” della conversione, e poi, quando comodi, tornare a vedere se la conversione era finita e leggere il dato, senza bloccare la loop
andando a vedere la libreria ho scoperto che è già previsto, lo chiamano “modo asincrono”, OK non vi è bisogno di me in questo
e quindi ho pensato di fare il filtro grep universale, che permettesse di identificare in una seriale (o simili) una qualsiasi sequenza di stringhe (MAI Stringhe, sempre char *, intendiamoci)
e anche di separare eventuali argomenti dai comandi
ecco a voi il risultato di un pomeriggio di pioggia
cominciamo dal principio di funzionamento:
carattere per carattere, si confronta il carattere in ingresso con i primi caratteri di un array di stringhe
se corrisponde si incrementa l’indice associato a quella specifica stringa
se non corrisponde si resetta l’indice
se corrisponde E la stringa è finita, si guarda un secondo array di stringhe, SE la seconda stringa corrispondente è esistente (non di lunghezza 1, il solo terminatore) la si riempie, carattere per carattere col flusso dei caratteri entranti
indi si restituisce l’indice che identifica, nello array delle stringhe da cercare, la stringa specifica trovata
è lo stesso meccanismo usato nel mio teelegrambot di settmbre, ma anche che ho consigliato a giugno a quello del LINbus
ho riscritto ex-novo tutto, ma alla fine è venuto talmente simile al precedetne che tantovaleva copiarlo
la parte di riconoscimento delle stringhe entranti:

int setaccia(char cx)
{
    // riconosce in un flusso di testo le scritte presenti in un array pre-caricato
    // restituisce -1 se non lo ha ancora trovato
    // restituisce il numero ordinale della scritta riconosciuta nell'array delle scrit da cercare
    // se è presente la parte opzionale "carico" scrive nella stringa carico[N] l'eventuale sottostringa che segue la parte trovata
    // basato sul lavoro svolto per la telegrambot
    // a sua volta per basato sull'aiuto dato a quello che cercava di decodificare il protocollo delle ventole della Prius
    // un array di indici per le posizioni correnti nei semi
    static byte indici[FILTRI];
    //indice per la posizione corrente nel carico pagante
    static int pagante;
    // un flag per sapere se abbiamo trovato un seme
    static int trovato = -1;

    // solo se non abbiamo ancora trovato nulla
    if (trovato == -1)
    {
        // per tutti i semi
        for (byte i = 0; i < FILTRI; i++)
        {
            // se il carattere ricevuto è uguale al corrispondente carattere del seme
            if (cx == seme[i][indici[i]])
            {
                // avanti un passo
                indici[i]++;

                // ok, se corrisponde però devo vedere se sono arrivato alla fine del seme
                if (seme[i][indici[i]] == 0)
                {
                    // ok fine del seme
                    // se NON c'è un carico pagante
                    if (!argomento[i][0])
                    {
                        // non c'è carico pagante
                        // restituisco l'indice del seme trovato
                        return i;
                    }
                    else
                    {
                        // e qui è più difficile
                        // perchè devo proseguire
                        // scrivendo tanti caratteri nella stringa di carico pagante fino a riempirla
                        // senza però continuare a cercare nei semi
                        // alzo un flag di trovato
                        trovato = i;
                        // al prossimo carattere, se trovato ha un valore utile scrivo il carattere nel carico pagante
                    }
                }
            }
            else
            {
                // non corrisponde
                indici[i] = 0;
            }
        }

        // se sono arrivato fin qui non ho trovato una cippa
        return -1;
    }
    else
    {
        // abbiamo gia' trovato un carico pagante
        argomento[trovato][pagante] = cx;
        pagante++;

        if (!argomento[trovato][pagante])
        {
            //finito, siamo usciti, abbiamo scritto abbastanza caratteri da arrivare in fondo alla stringa di carico pagante per il seme trovato
            int ritorno = trovato;
            // resetto trovato
            trovato = -1;
            // resetto l'indice del carico pagante
            pagante = 0;
            return ritorno;
        }
    }
}

restituisce -1 fino a che non ha trovato qualcosa, e scrive nella stringa argomento, i caratteri successivi richiesti
al prossimo post, sempre poco per non sbordare

ok, serve di dichiarare le variabili globali

// quante corrispondenze sono da trovare
#define FILTRI 5
// array di stringhe da ri-cercare
char * seme[FILTRI] = { "accendi", "spegni", "inverti" , "intermittenza", "lampeggia "};
// array di valori di carico pagante
volatile char * argomento[FILTRI] = { "", "", "", "", "+"};
// DEVONO essere terminati, quindi devono essere inizializzati almeno a stringa vuota

come vedete un array di stringhe da cercare e un array di argomenti da riempire, se lunghi 1 (il solo terminatore) non vi è nulla da riempire
la stringhe da trovare, che ho chiamato seme, possono comodamente avere i nomi dei comandi da dare
naturalmente nella loop serve di leggere la seriale e, se presente, mandare in trattamento il carattere appena ricevuto

void setup(void)
{
    pinMode(13, OUTPUT);
    Serial.begin(9600);
}
void loop(void)
{
    if (Serial.available())
    {
        char rx = Serial.read();
        int a = setaccia(rx);

        if (a >= 0)
        {
            Serial.print("trovato: ");
            Serial.println(seme[a]);
            // mando in esecuzione direttamente la stringa trovata
            lista[a]();
        }
    }

    if (intermitta)
    {
        if (!(millis() % 1000))
        {
            digitalWrite(13, !digitalRead(13));
        }
    }
}

come vedete è brevissima, è quasi più lunga la parte dell’intermittenza del led 13 che la parte di lavoro vero e proprio
per essere sicuro che la cosa vada ho pensato a comandare il led 13 di una UNO, accendi spegni, inverti lo stato, falla andare a intermittnza e lampeggia un numero di volte passato come argomento

per la selezione, invece di una noiosa catena di if else if oppure un gigantesco switch ho pensato ad un array di puntatori a fuizioni, corrispondeti uno a uno ai comandi, usando l’indice del comando trovato come indice del vettore di puntatori a fuonzioni
naturalmente serve di creare il vettore

// un array di puntatori a funzioni void che restituiscono void
typedef void(*funpointer)(void);
funpointer lista[FILTRI] = {accendi,  spegni, inverti, intermittenza, lampeggia};
// il fatto di usare gli stessi nomi dei comandi è una sola comodità

e creare le funzioni corrispondenti, che dvono corrispondere al tipo del vettore void f(void)

// creo le funzioni da usare
void accendi(void)
{
    digitalWrite(13, 1);
}
void spegni(void)
{
    digitalWrite(13, 0);
}
void inverti(void)
{
    digitalWrite(13, !digitalRead(13));
}
void intermittenza(void)
{
    // attiverà' l'intermittenza semplicemente alzando o abbassando un flag
    // il lavoro vero lo farà la loop
    intermitta=!intermitta;
}

void lampeggia(void)
{
    // questa esegue tanti cicli di lampeggio
    // quanti è scritto in ASCII nel primo carattere dopo la sua stringa di ricerca
    // per prima cosa controllo che sia un carattere numerico
    char cx = argomento[4][0];
    Serial.print("Lampeggio con ");
    Serial.print(cx);
    Serial.println(" cicli");

    if (cx > '9' || cx < '0')
    {
        Serial.println("Comando errato, serve un numero (di una sola cifra)");
        return;
    }

    for (byte i = 0; i < cx - '0'; i++)
    {
        digitalWrite(13, 1);
        delay(100);
        digitalWrite(13, 0);
        delay(100);
    }
}

come vedete anche qui il grosso del lavoro è solo per far lampeggiare un led

poco rimane da dire, e se guardate nell’allegato avete tutti i commenti
per chi na ha bisogno: prendete ispirazione,
per chi non ne ha bisogno, grazie per la pazienza

PS, notare il nome del progetto, spero porti fortuna

Torrey_Canyon.ino (5.22 KB)

Funziona anche se un comando da individuare è "piripicchio" e sulla seriale arriva "piripiripicchio"?

Oppure arrivato a piripi si accorge del mancato match, scarta tutto tranne l'ultimo carattere (che è quello in elaborazione) e ricomincia da iripicchio?

al primo carattere che non corrisponde scarta e ricomincia, ma effettivamente NON riprova se andrebbe bene come primo.... grazie dell'idea, magari ne faccio "setaccio 2: Il Crivello"

Basta che prima di scartare la stringa al no-match la riverifichi solo sul comando corrente scartando il primo carattere fino a tornare in pari, così becchi anche i comandi super balbuzienti tipo "trotrotrotrova".

Grazie Maubarzi, ho accolto il tuo suggerimento, come promesso ecco a voi setaccio 2: Il Crivello!

int setaccia(char cx)
{
    // riconosce in un flusso di testo le scritte presenti in un array pre-caricato
    // restituisce -1 se non lo ha ancora trovato
    // restituisce il numero ordinale della scritta riconosciuta nell'array delle scrit da cercare
    // se è presente la parte opzionale "carico" scrive nella stringa carico[N] l'eventuale sottostringa che segue la parte trovata
    // basato sul lavoro svolto per la telegrambot
    // a sua volta per basato sull'aiuto dato a quello che cercava di decodificare il protocollo delle ventole della Prius
    // un array di indici per le posizioni correnti nei semi
    static byte indici[FILTRI];
    //indice per la posizione corrente nel carico pagante
    static int pagante;
    // un flag per sapere se abbiamo trovato un seme
    static int trovato = -1;

    // solo se non abbiamo ancora trovato nulla
    if (trovato == -1)
    {
        // per tutti i semi
        for (byte i = 0; i < FILTRI; i++)
        {
            // se il carattere ricevuto è uguale al corrispondente carattere del seme
            if (cx == seme[i][indici[i]])
            {
                // avanti un passo
                indici[i]++;

                // ok, se corrisponde però devo vedere se sono arrivato alla fine del seme
                if (seme[i][indici[i]] == 0)
                {
                    // ok fine del seme
                    // se NON c'è un carico pagante
                    if (!argomento[i][0])
                    {
                        // non c'è carico pagante
                        // restituisco l'indice del seme trovato
                        return i;
                    }
                    else
                    {
                        // e qui è più difficile
                        // perchè devo proseguire
                        // scrivendo tanti caratteri nella stringa di carico pagante fino a riempirla
                        // senza però continuare a cercare nei semi
                        // alzo un flag di trovato
                        trovato = i;
                        // al prossimo carattere, se trovato ha un valore utile scrivo il carattere nel carico pagante
                    }
                }
            }
            else
            {
                // non corrisponde
                indici[i] = 0;

                // però potrebbe corrispondere con il primo carattere (grazie a Maubarzi)
                if (cx == seme[i][indici[i]])
                {
                    // il problema è far ripetere lìoperazione, integralmente
                    // non basta indice[i]++, dato che dovrei anche controllare che il seme sia lungo 1, e quindi già finito
                    // balle: il seme non puo' essere lungo 1, perchè all'ora il primo carattere sarebbe già stato riconosciuto
                    indici[i]++;
                    // ri-conto il primo carattere
                }
            }
        }

        // se sono arrivato fin qui non ho trovato una cippa
        return -1;
    }
    else
    {
        // abbiamo gia' trovato un carico pagante
        argomento[trovato][pagante] = cx;
        pagante++;

        if (!argomento[trovato][pagante])
        {
            //finito, siamo usciti, abbiamo scritto abbastanza caratteri da arrivare in fondo alla stringa di carico pagante per il seme trovato
            int ritorno = trovato;
            // resetto trovato
            trovato = -1;
            // resetto l'indice del carico pagante
            pagante = 0;
            return ritorno;
        }
    }
}