Ciao a tutti,
è qualche anno che uso Arduino per hobby ma da qualche giorno mi sono messo a giocare con la seriale: inviare comandi via IDE ad esempio, oppure far dialogare 2 arduino MEGA tramite la seriale interna numero 2 tra di loro per scambiarsi informazioni.
Vorrei costruire uno sketch in cui leggo la seriale dalla IDE come Stringa, e poi far delle cose in base a ciò che è scritto. Le funzioni per trovare testo in una stringa che conosco sono ad esempio equalsIgnoreCase, indexOf ...
La domanda è: se scrivo "accendi led 1 per 10 secondi" c è un modo relativamente semplice per associare ad una variabile byte numero_led il valore 1 e ad un'altra variabile int tempo_accensione il valore di 10mila millisecondi?
Usando indexOf posso capire che nel comando c è scritto "accendi led" piuttosto che "spegni led", il problema è che poi dovrei spostare il cursore di 1 posto, leggere come INT il valore che c è dopo (1), poi spostare il cursore di altri 5 posti , capire che 10 è formato da 2 caratteri (1 e 0) e fargli acquisire 1 e 0 in una sola int che sarebbe = 10. Non so se rendo l' idea.
Non chiedo lo sketch già fatto, ma soltanto per favore qualche dritta su comandi, funzioni...compatibili con arduino! Grazie a tutti
Primo errore, usare la classe String invece che le stringhe classiche del 'C' (cosa che, sulle classiche MCU AVR, prima o poi crea sicuramente problemi), secondo non aver studiato la AVR libc dove, in string.h ci sono tutte le funzioni per manipoolare le stringhe del 'C', separare in "token", ecc. e la stdlib.h dove trovi quelle per convertire stringhe in numeri, ecc.
... beh, visto che la citi, allora diamo anche il link alla discussione relativa all libreria, il link a GitHub e segnaliamo che si installa dal "Library Manager" dell'IDE
Grazie!
avevo già letto la pagina relativa a string.h https://www.nongnu.org/avr-libc/user-manual/group__avr__string.html
ma siccome sono un hobbista, non riesco a capire cosa fa ogni funzione dato che non ci sono esempi. Farò così, con un po di tempo cercherò in giro piccoli sketch con esempi di ogni funzione, per capire come combinarle per arrivare allo scopo.
Perché le funzioni di string.h (ma anche di stdlib.h) NON sono solo per Arduino, ma generali della libreria del 'C' e ... internet è pieno zeppo di esempi di uso di quelle funzioni ...
Si, mi sto documentando su altri siti con degli esempi per ogni funzione e cerco quella che fa al caso mio.
Purtroppo, anche se String può causare problemi con la Ram (come ho letto in un altro Topic su questo stesso forum) spesso ce le dobbiamo tenere perché vuoi o non vuoi semplificano molto.
Così a occhio non ho trovato IndexOf per array di caratteri, o altre comodità. Inoltre per me che lavoro ormai più con ESP32 piuttosto che con Arduino, tutto ciò che arriva da Telegram è visto come Stringa. Quindi per pulire la coscienza l unica cosa che posso fare è convertire la Stringa passata da telegram in array di caratteri e lavorare su quelli..anche se la Stringa rimane, e ad ogni messaggio ricevuto (o da inviare) la Libreria vuole sempre Stringa.
Quindi a questo punto mi chiedo: dato che sono obbligato dalla libreria ad usare Stringa quanto meno per ricevere il messaggio di testo, se poi la lavoro usando funzioni o comandi, rischio di caricare ancora di più la ram? oppure visto che il danno è fatto, tanto vale usarli?
Forse non visto il link al post #3 lo rimetto qui:
La libreria e la demo assieme fanno tutto quello che chiedi, puoi indagare su come è fatto e se lo trovi più semplice lo realizzi con la classe String. In effetti su ESP32 c'è molta più ram e le probabilità che si frammenti la memoria sono minori ma esistono. Se l'applicazione fallisce una volta al mese e non è un problema, vai pure con String ecc.
Grazie, mi era sfuggito. Lo sto guardando e a prima vista non ci capisco molto:
nel loop c è solo
int8_t err = mySerCmd.ReadSer();
if (!err) {
Serial.print("Comando sconosciuto.\n");
}
dove si trova la parte di script che legge da seriale ( serial.available() ) ? dato che è inclusa anche la servo.h mi viene difficile capire come adattare al mio scopo. A breve scaricherò la libreria e farò delle prove, grazie delle dritte! nel frattempo cercherò anche di elaborare uno script che mi legge dalla seriale e se trova quello che si aspetta, fa delle cose. Se gradite, se ci riesco posto lo script così vi fate quattro risate
La servo è incluso poiche giggetto voleva un esempio che la usasse o forse lui avrebbe usato dei motori passo passo, ma in wokwi al tempo il passo passo non c'èra è comunque è una demo che mostra come fare eseguire del codice in base a ciò l'utente scrive nella riga di comando.
Quindi es, scrivi: srv, 180
e il servo va nella posizione 180
scrivi: srv,90
e il servo va 90
Ovviamente qualunque valore nel range 0÷180 è ammesso.
Dimentica la seriale, è quello lo scopo della libreria, di NON fati vedere la parte complessa e di chiamare delle specifiche "funzioni" per ogni comando ricevuto dando la possibilità di recuperare i vari parametri separati da un "separatore" prefissato.
Però, se non ti studi il readme che accompagna la libreria ... NON avrrivi da nessuna parte.
Eccomi, ho provato e condivido questo stupendo sketch con la community. Sono ironico.
PREMESSA: inviare via seriale un comando necessariamente UGUALE a:
accendi LED x minuti y
Dove X e Y possono essere a 1 o 2 cifre.
RAGIONAMENTO (ahahah, di sabato e per hobby):
leggo dalla seriale IDE come Stringa
calcolo la lunghezza della Stringa
creo un array di caratteri della dimensione della lunghezza Stringa +1 (dato che a fine stringa mette Zero, o qualche carattere speciale, non so di preciso.. ottenuto +1 sperimentalmente)
inserisco il contenuto della Stringa all' interno dell' array di caratteri appena creato
tramite indexOf leggo al Stringa originale e vedo se c è la parola "accendi LED"
se c è, scansiono con un For tutto l' array di caratteri alla ricerca del separatore, che nel mio caso è il carattere vuoto " " (barra spazio, per gli amici ASCII 32)
mi annoto in un array di byte la posizione (cella numero) del Separatore trovato, fino a fine FOR. così avrò un array con dentro i numeri delle posizioni degli SPAZI individuati dall' array di caratteri
creo un Token1 di dimensione pari alla differenza tra le posizioni (celle) dei Separatori individuati nell'array del punto 7. questo Token1 è di tipo char[x]
riempio token1 con i numeri compresi tra 1 separatore e l altro
tramite atoi() converto token1 che è un array di caratteri, nel mio caso per forza numeri, in "int" così so QUALE led accendere
stessa cosa con token2 mi calcolo per quanti secondi (o minuti, insomma poi si vedrà) accendere il led.
Ciò premesso ecco lo sketch:
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
}
String leggi_da_seriale_Stringa; //che è una Stringa
int k=0; //così...
int lunghezza_della_Stringa=0;
int array_posizione_dei_separatori[10];
int contatore_array_dei_separatori=0;
char carattere_separatore=' ';
byte numero_led_da_accendere=0;
unsigned long tempo_led_acceso=0;
void loop() {
// put your main code here, to run repeatedly:
if(Serial.available())
{
leggi_da_seriale_Stringa = Serial.readString(); //leggo dalla seriale come Stringa
lunghezza_della_Stringa = leggi_da_seriale_Stringa.length(); //calcolo la dimensione della stringa
char array_di_caratteri[lunghezza_della_Stringa+1]; //li dichiaro qui e non nell header perché prima non potrei sapere quanto è lunga la Stringa ricevuta via Seriale!
Serial.println("ho ricevuto " +(String)leggi_da_seriale_Stringa);
Serial.println("lunghezza_della_Stringa = a " +(String)lunghezza_della_Stringa);
leggi_da_seriale_Stringa.toCharArray(array_di_caratteri, lunghezza_della_Stringa+1); //carico nell array di caratteri la Stringa ricevuta via seriale, quindi avrò 1 carattere in ogni cella
if(leggi_da_seriale_Stringa.indexOf("accendi LED") != -1)
{
Serial.println("hai inviato un comando di accensione LED");
contatore_array_dei_separatori=0;
for(k=0;k<lunghezza_della_Stringa+1;k++)
{
if(array_di_caratteri[k]==carattere_separatore) //se trovi 1 carattere separatore, inserisci la sua posizione in un array che prende nota delle coordinate di ogni separatore, fino a 1 massimo di 10 sep
{
Serial.println("Trovato separatore!");
array_posizione_dei_separatori[contatore_array_dei_separatori]=k;
contatore_array_dei_separatori++;
}
}
Serial.println("ecco l array_posizione_dei_separatori:"); //mostra l array coi separatori,ogni numero è la posizione all interno dell array del separatore nell array originale
for(k=0;k<10;k++)
{
Serial.print(" " +(String)array_posizione_dei_separatori[k]);
}
Serial.println("");
byte dimensione_token1=(array_posizione_dei_separatori[2]-array_posizione_dei_separatori[1]); //mi invento 1 array token nel quale verso byte-a-byte il contenuto tra un separatore e l altro
char token1[dimensione_token1];
for(k=0;k<dimensione_token1;k++)
{
token1[k]=array_di_caratteri[(array_posizione_dei_separatori[1]+1+k)]; //metto +1 xke deve iniziare dal carattere successivo al separatore stesso
}
Serial.print("token1 è ");
for(k=0;k<dimensione_token1;k++)
{
Serial.print(token1[k]);
}
Serial.println("");
numero_led_da_accendere=atoi(token1);
Serial.print("numero_led_da_accendere = ");
Serial.println(numero_led_da_accendere);
byte dimensione_token2=((lunghezza_della_Stringa)-array_posizione_dei_separatori[3]); //il secondo array non ha lo spazio come finale, ma il fine-array.
char token2[dimensione_token2];
for(k=0;k<dimensione_token2;k++)
{
token2[k]=array_di_caratteri[(array_posizione_dei_separatori[3]+1+k)]; //metto +1 xke deve iniziare dal carattere successivo al separatore stesso
}
Serial.print("token2 è ");
for(k=0;k<dimensione_token2;k++)
{
Serial.print(token2[k]);
}
Serial.println("");
tempo_led_acceso=atoi(token2);
Serial.print("tempo_led_acceso in secondi = ");
Serial.println(tempo_led_acceso);
}
}
delay(100);
}
Ora, so benissimo che si poteva dividere con strtok, il problema è che non ho capito come!
Leggendo in giro e guardando questo video : https://www.youtube.com/watch?v=ti3AVAOoszM
vedo che è molto facile ma non saprei oltre a "stampare sulla seriale" come salvare su variabili esterne il Numero_Led ed il Tempo_di_accensione dato che il While prosegue e non saprei come "agganciare" i dati che mi interessano.
ora stacco, domani mi dedicherò alla Fotografia e di conseguenza lunedì rileggendo questo mio sketch sicuramente non ci capirò più nulla, vi prego di compatirmi. E' probabile che settimana prossima proverò con strtok a "beccare" i valori che mi interessano intercettare
Scusa non avevo visto il video, in genere non guardo video per queste cose, preferisco il testo. Si ok dal while di esce quando token == NULL o nullptr. Se prendi un stringa e ne stampi i codici ascii vedi che ad esempio ABC è composta da 4 elementi seguenti: 65, 66, 67, 0.
Lo zero finale indica la fine della collezione dei caratteri ed è essenziale per essere conforme alle C string. Ma la C string potrebbe pure essere come segue: 65, 66, 67, 0, 67, 66, 65, 0. Sono otto elementi di array il puntatore di cui parla aliverti punta prima a 65 (indice 0), poi punta a 67 (indice 4), in entrambe i casi il terminatore 0 è presente.
Se vuoi approfondire qui qualcosa su i vettori e C string.
Mentre qui come stampare il contenuto delle celle di ram ecc.
La stringa di C termina alllo zero binario, il primo zero binario
È la definizione di stringa di C
Se poi anche lo array di char fosse più grande e magari anche popolato la cosa sarebbe comunque ignorata da tutte le funzioni di stringa (stringa di C intendo)
Infatti la strtok chiede "due" parametri: la stringa da analizzare e la stringa (non il carattere) che funge da separatore, ed ambedue terminate da uno zero binario
Ti potrà sembrare strano ma non credo si tratti di questo, sicuramente mi sono espresso di fretta e compreso male.
In breve, quando strtok (o altro) incontra il separatore al suo posto ci mette lo zero di terminazione e restituisce il puntatore a indice 0.
Quando la si richiama strtok parte dal indice seguente lo 0, quindi da 67 e restituisce il puntatore a questo.
Quando strtok non ha più nulla da fare la C string contiene quei codici.
Certo che non si può, tutte le funzioni standard si aspettano il terminatore '\0'.
strtok mette lo zero e si salva il puntatore al successivo elemento e si ferma quando incontra lo zero finale della C string e restiuisce NULL. Era solo per mostrare in linea di massima come lavora, avrei dovuto scrivere un esempio di codice e poi stampare l'array completo e mostrarlo.