[Risolto] Connessione seriale non va sotto gli 800ms

Salve a tutti!

Ho realizzato un progetto che risponde a degli input da seriale. Nello specifico, Arduino riceve un valore inviato con Java tramite seriale e attiva uno dei due relay presenti nel circuito e tutto funziona senza problemi.

L'unico limite e problema che non riesco a risolvere è la velocità di acquisizione del dato, non riesco a scendere sotto gli ~800 ms. Se invio più velocemente i dati, Arduino non reagisce.

Arduino è collegato tramite la porta USB presente sulla board stessa che utilizzo per alimentare, scrivere il codice e inviare i dati.

Il software Java potrebbe inviare i valori con intervalli di circa 50ms ma per renderlo funzionante con Arduino devo impostare un timeout di attesa di 800ms (max 790ms) scendendo di più non succede nulla.

È possibile che non si possa scendere sotto questa velocità? Cosa sbaglio?

Se servono altri dati o informazioni vi prego di chiedere, non so cosa potrebbe essere utile per permettervi di aiutarmi, grazie.

Mah..
Non saprei cosa chiederti...
Forse è troppo chiedere sorgente e schema?

La mia sfera di cristallo dice che nel codice ci sono troppi delay che bloccano l'esecuzione del programma.... se non ci becca nemmeno stavolta la butto (la sfera intendo) :grin:

Standardoil:
Mah..
Non saprei cosa chiederti...
Forse è troppo chiedere sorgente e schema?

Assolutamente no, basta chiedere. :wink:
Sotto la parte software che invia i dati in Java e il codice su Arduino.
Per lo schema, beh, non ho la versione digitale da mostrarvi, ma è molto semplice, ho collegato il modulo a due relay ai pin digitali 6 e 7 e nulla più.

-zef-:
La mia sfera di cristallo dice che nel codice ci sono troppi delay che bloccano l'esecuzione del programma.... se non ci becca nemmeno stavolta la butto (la sfera intendo) :grin:

E mi sa che è arrivato il momento di buttarla!! Aahahahaha ma dai sarà servita in altre occasioni. :wink:
Comunque nessun delay su Arduino come puoi vedere sotto.

Nel codice Java c'è il delay di 800ms perché come detto se scendo sotto questo intervallo di tempo Arduino non reagisce.

Grazie per le risposte

Questo il codice su Arduino

int r1 = 7;
int r2 = 6;
long intervallo,now;
String v = "";
int V;

void setup() {
  Serial.begin(9600);
  pinMode(r1, OUTPUT);
  pinMode(r2, OUTPUT);
  digitalWrite(r1, HIGH);
  digitalWrite(r2, HIGH);
}

void loop() {
  now = millis();
  if (Serial.available() > 0) {
    v = Serial.readString();
    V = v.toInt();
    if(V > 0 ){
      if(V > 12 ){
        digitalWrite(r1,HIGH);
        digitalWrite(r2,LOW);
        intervallo = now;
      } else{
        if(V > 3 ){
          digitalWrite(r1,LOW);
          digitalWrite(r2,HIGH);
          intervallo = now;
        } else {
          digitalWrite(r1,HIGH);
          digitalWrite(r2,HIGH);
        }
      }
    }  
  }
}

Questa la parte di Java che invia il dato

String velocita=Float.toString(speed);
            
            arduino.serialWrite(velocita);
            System.out.println(velocita);
            TimeUnit.MILLISECONDS.sleep(800);

Magari non è questo il problema ma visto che trasmetti un solo valore alla volta potresti provare a sostituire la lettura da seriale dentro alla Stringa (classe String bleah!) e la successiva conversione con questo codice:

if (Serial.available() > 0) {
    V = Serial.parseInt();
    ...

Al momento now ed intervallo non vengono utilizzate ma visto che ci sono penso tu abbia idea di usarle in seguito, se non è così rimuovile, altrimenti il tipo è errato devi definirle unsigned long e non long.
Altra ottimizzazione le due variabili per i due pin puoi definirle come const oppure usare una #define

fabpolli:
Magari non è questo il problema ma visto che trasmetti un solo valore alla volta potresti provare a sostituire la lettura da seriale dentro alla Stringa (classe String bleah!) e la successiva conversione con questo codice:

if (Serial.available() > 0) {

V = Serial.parseInt();
    ...



Al momento now ed intervallo non vengono utilizzate ma visto che ci sono penso tu abbia idea di usarle in seguito, se non è così rimuovile, altrimenti il tipo è errato devi definirle unsigned long e non long.
Altra ottimizzazione le due variabili per i due pin puoi definirle come const oppure usare una #define

Grazie mille per la risposta.

Ho implementato l'utilizzo della funzione parseInt e lato PC ho convertito il valore da Float a Integer prima di inviarlo tramite seriale. Now e intervallo, ho dimenticato a toglierle, mi serviranno in futuro. Grazie per i consigli di ottimizzazione.

Il problema persiste purtroppo. Se scendo sotto gli 800ms non ho nessuna reazione da Arduino.

Arduino

const int r1 = 7;
const int r2 = 6;
int V;

void setup() {
  Serial.begin(9600);
  pinMode(r1, OUTPUT);
  pinMode(r2, OUTPUT);
  digitalWrite(r1, HIGH);
  digitalWrite(r2, HIGH);
}

void loop() {
  if (Serial.available() > 0) {
    V = Serial.parseInt();
    if(V > 0 ){
      if(V > 12 ){
        digitalWrite(r1,HIGH);
        digitalWrite(r2,LOW);
      } else{
        if(V > 2 ){
          digitalWrite(r1,LOW);
          digitalWrite(r2,HIGH);
        } else {
          digitalWrite(r1,HIGH);
          digitalWrite(r2,HIGH);
        }
      }
    }  
  }
}

Lato PC

int v;
v = Math.round(speed);
String velocita=Integer.toString((int)v);
arduino.serialWrite(velocita);

Qual' è il valore massimo e minimo che velocità può assumere?

Da 0 a 40 in casi estremi. Il valore durante l'utilizzo oscilla tra 8 e 25.

Ok, se da Java invii non due caratteri (Es. 2 e 5 per 25) ma invii il valore 25 e lato Arduino leggendo il singolo byte a questo punto lo casti come byte e lo uti tal quale dovresti ottenere la massima velocità possibile, anche perché inviado e ricevendo un singolo byte scendere sotto tale unità è praticamente impossibile, a quel punto lettura ed esecuzione codice non possono proprio impiegare così tanto tempo.

Ho appena provato da monitor seriale di Arduino ad inviare velocemente degli input numerici e Arduino risponde istantaneamente.

Provo subito!

Mah, scusate ma ho qualche perplessità su tutto ciò...

A 9600 baud sono circa 900 byte al secondo, d'accordo che non c'è handshake hardware ed il buffer lato Arduino è relativamente piccolo, ma non vedo proprio come mandare 2 caratteri possa essere limitato a 800 ms quindi circa 400ms a carattere, come se la seriale fosse a 25 baud!

Allora, a parte evitare le "String" lato Arduino come fosse kriptonite per Superman, non conosco Java (ma esiste una ".serialWrite()" o è una tua funzione?) ma quello che vedo è che ti MANCA UN TERMINATORE! Se tu mandi due valori, diciamo 6 e 8, come li distingui da "68"? Oppure se stai mandando 84 e nel buffer è arrivato solamente la prima cifra "8"?

Ad esempio diciamo mettendo il classico LF (ma il discorso va bene con qualsiasi carattere, anche "|") qualcosa del tipo:

String velocita=Integer.toString((int)v);
arduino.serialWrite(velocita + "\n");

e quindi in Arduino aspettare che ci sia il terminatore prima di acquisire il valore.
Se tu hai fatto prove con il Monitor Seriale attento che quello accumula i caratteri digitati e li manda tutti insieme (più il Line Feed...) per cui non è la stessa cosa.

Oppure puoi mandare sempre 2 caratteri (quindi non "8" ma "08") per cui basta attendere che Serial.available() sia uguale a 2, e leggere i caratteri e convertirli in intero.

Ma, meglio ancora, puoi farlo usando un byte visto che l'intervallo dei valori interi è compatibile con un char, e visto che si tratta di un dialogo tra macchine e non con un umano, è sempre meglio progettare i propri protocollini in modo che sia tutto più agevole per le macchine...

Quindi concordo con il suggerimento di fabpolli, che ti evita persino il discorso del terminatore in quanto il pacchetto è di lunghezza fissa (1 char ossia 1 unsigned byte)...

PS poi se la seriale la metti ad una velocità maggiore, diciamo anche "solo" 38400 hai tanta di quella banda che potresti usarla anche per ben altro... :wink:

Prova anche in setup a modificare il parametro Serial.setTimeout() che di default è un po' altino

-zef-:
Prova anche in setup a modificare il parametro Serial.setTimeout() che di default è un po' altino

EUREKA!! :smiley: :smiley: :smiley: :smiley:

Una cosa così semplice eppure fondamentale! Errore mio a non aver controllato le funzioni relative alla seriale, unica cosa che posso dire in mia discolpa è che non avrei mai pensato ad una configurazione standard da 1s di attesa per la seriale.

Grazie a tutti per le risposte comunque costruttive e utili!! :wink:

Grazie specialmente a fabpolli e docdoc per le ottimizzazioni suggerite!

Permettimi di dissentire dal tuo eureka, e ti spiego il perché... quello che a te sembra la soluzione è un semplice workaround per mettere una pezza ad un malfunzionamento di base del software come ti ha descritto nel dettaglio @docdoc
E' chiaro che se esiste tale parametro c'è un perché ma non è quello di terminare la lettura al posto tuo, abbassando il timeout hai aggirato l'ostacolo, resta il fatto che c'è un bug di fondo, è un po' come usare il watchdog per sbloccare programmi che si bloccano per colpa di come sono scritti e non per quello per cui è nato (ovvero sopperire a anomalie non predicibili), per carità sembra che sia la soluzione ma in realtà non lo è, è solo un modo per "mettere la testa sotto la sabbia".
Per carità, non prenderla come un attacco personale rivolto a te, se funziona come vuoi e non ti interessa migliorare il programma... a posto felice tu e anche io ma la vera soluzione l'ha indicata @docdoc ovvero inseriere il terminatore (Es. CR+LF come fa il monito seriale se non differentemente configurato), leggere due caratteri ed uscire ecc..
E te ne dico anche un'altra, se il programma è destianto a girare per molto tempo l'uso della classe String ti porterà con buone probabilità a sperimentare blocchi imprevisti e casuali da cui difficilmente ne uscirai fuori (come più volte indicato sul forum) se non eliminando l'uso di tale classe e tornare a leggere un byte alla volta, che vada in un interno o in un array di char poco importa.
Questo pippone che ho scritto che potrebbe sembrare innutile, forse arrogante, non è rivolto a te (come già detto) ma pittosto per far capire a eventuali altri frequentatori che in futuro potrebbero imbattersi in questo topic segnato come risolto, che la "soluzione" adottata non è proprio la migliore, anzi.

scusate se m'intrometto per dire la mia, sempre non per critica ma per, se possibile, aggiunger un concetto in più.
Premetto che sottoscrivo in toto quanto scritto da fabpolli e docdoc.
Come detto da fabpolli il ridurre il time out è un accrocchio per aggirare il problema e non una vera e buona soluzione; generalmente questo tipo di soluzioni, gli accrocchi, vengono prese quando a "realizzazione concettuale" del progetto non si verifica/valuta bene su cosa si sta lavorando e che device si utilizzeranno...per fare il classico degli esempi:
PC <-> PC classe String OK
PC<->Arduino classe String NO
Arduino <-> Arduino classe String NO.
Se c'è necessità di scambio dati tra due device posso utilizzare protocolli standard, che sono conosciuti, collaudati solidi etc...oppure devo farmi io il mio...e se devo farmi il mio cosa devo considerare:
ci saranno più info nella stessa stringa?...se si come li distinguo?
come capisco che la stringa inizia e finisce?
come capisco se la stringa letta corrisponde a quella passata?
etc etc
La buona pianificazione di un progetto a volte ti evita la completa riscrittura del codice prodotto...ne migliora la manutentabilità...e magari lo rende tutto od in parte riutilizzabile per altri progetti.
Scusate il pippotto ma necessitavo di un po' di formaggio...dalle mie parti c'è un detto "xe rivà queo del formajo" (traduzione concettuale e contestualizzata -> ha parlato quello che sa tutto)...ciao

fabpolli:
Permettimi di dissentire dal tuo eureka, e ti spiego il perché... quello che a te sembra la soluzione è un semplice workaround per mettere una pezza ad un malfunzionamento di base del software come ti ha descritto nel dettaglio @docdoc
E' chiaro che se esiste tale parametro c'è un perché ma non è quello di terminare la lettura al posto tuo, abbassando il timeout hai aggirato l'ostacolo, resta il fatto che c'è un bug di fondo, è un po' come usare il watchdog per sbloccare programmi che si bloccano per colpa di come sono scritti e non per quello per cui è nato (ovvero sopperire a anomalie non predicibili), per carità sembra che sia la soluzione ma in realtà non lo è, è solo un modo per "mettere la testa sotto la sabbia".
Per carità, non prenderla come un attacco personale rivolto a te, se funziona come vuoi e non ti interessa migliorare il programma... a posto felice tu e anche io ma la vera soluzione l'ha indicata @docdoc ovvero inseriere il terminatore (Es. CR+LF come fa il monito seriale se non differentemente configurato), leggere due caratteri ed uscire ecc..
E te ne dico anche un'altra, se il programma è destianto a girare per molto tempo l'uso della classe String ti porterà con buone probabilità a sperimentare blocchi imprevisti e casuali da cui difficilmente ne uscirai fuori (come più volte indicato sul forum) se non eliminando l'uso di tale classe e tornare a leggere un byte alla volta, che vada in un interno o in un array di char poco importa.
Questo pippone che ho scritto che potrebbe sembrare innutile, forse arrogante, non è rivolto a te (come già detto) ma pittosto per far capire a eventuali altri frequentatori che in futuro potrebbero imbattersi in questo topic segnato come risolto, che la "soluzione" adottata non è proprio la migliore, anzi.

Ho cantato vittoria troppo presto. Ora mi sorge un dubbio, elaborando un software a regola d'arte con l'aggiunta del terminatore ed evitando la classe String non si presenterebbe ugualmente il problema avendo il timeout di default a 1000ms e quindi una risposta "lenta"?

ORSO2001:
scusate se m'intrometto per dire la mia, sempre non per critica ma per, se possibile, aggiunger un concetto in più.
Premetto che sottoscrivo in toto quanto scritto da fabpolli e docdoc.
Come detto da fabpolli il ridurre il time out è un accrocchio per aggirare il problema e non una vera e buona soluzione; generalmente questo tipo di soluzioni, gli accrocchi, vengono prese quando a "realizzazione concettuale" del progetto non si verifica/valuta bene su cosa si sta lavorando e che device si utilizzeranno...per fare il classico degli esempi:
PC <-> PC classe String OK
PC<->Arduino classe String NO
Arduino <-> Arduino classe String NO.
Se c'è necessità di scambio dati tra due device posso utilizzare protocolli standard, che sono conosciuti, collaudati solidi etc...oppure devo farmi io il mio...e se devo farmi il mio cosa devo considerare:
ci saranno più info nella stessa stringa?...se si come li distinguo?
come capisco che la stringa inizia e finisce?
come capisco se la stringa letta corrisponde a quella passata?
etc etc
La buona pianificazione di un progetto a volte ti evita la completa riscrittura del codice prodotto...ne migliora la manutentabilità...e magari lo rende tutto od in parte riutilizzabile per altri progetti.
Scusate il pippotto ma necessitavo di un po' di formaggio...dalle mie parti c'è un detto "xe rivà queo del formajo" (traduzione concettuale e contestualizzata -> ha parlato quello che sa tutto)...ciao

Ogni parere e consiglio è sempre ben accetto. Continuerò a lavorarci su cercando di trovare la soluzione migliore.

Essendo nuovo del forum vi chiedo un consiglio OT, lascio il [Risolto] o lo tolgo visto che a quanto pare la discussione non è giunta alla fine?

mikelefree:
Ora mi sorge un dubbio, elaborando un software a regola d'arte con l'aggiunta del terminatore ed evitando la classe String non si presenterebbe ugualmente il problema avendo il timeout di default a 1000ms e quindi una risposta "lenta"?

il timeout è una azione che sblocca, dopo un certo tempo predefinito, da una situazione di possibile stallo di carattere eccezionale.
Nel comportamento normale, una operazione non dovrebbe mai andare in timeout.
Per cui, con un software scritto correttamente, non dovresti mai avere il problema, salvo casi particolari di carattere eccezionale e impredicibile, tipo un errore di comunicazione hardware che non fa arrivare i dati nel modo corretto.

Per il "risolto", si, direi che lo puoi togliere, così non "inganni" potenziali contributori che vedendolo potrebbero passare oltre senza soffermarsi :wink:

mikelefree:
Ho cantato vittoria troppo presto. Ora mi sorge un dubbio, elaborando un software a regola d'arte con l'aggiunta del terminatore ed evitando la classe String non si presenterebbe ugualmente il problema avendo il timeout di default a 1000ms e quindi una risposta "lenta"?
Essendo nuovo del forum vi chiedo un consiglio OT, lascio il [Risolto] o lo tolgo visto che a quanto pare la discussione non è giunta alla fine?

Come hai potuto notare usando il monitor seriale se metti un terminatore non hai rallentamenti questo perché la funzione che usavi si mette a leggere dati dulla seriale finché non incontra il terminatore (che per lei è CR+LF), non appena incontra tali caratteri ritorna il controllo al tuo programma che riprende il flusso.
Se tu invece scegli un altro terminatore dovai per forza implementare una lettura "carattere x carattere" dalla seriale finché non incontri il tuo terminatore, ad esempio il carattere virgola, pipe o ciò che più ti aggrada, quindi se con la Serial.available() riscontri la presenza di dati entri nell'if li con un while leggi i caratteri e li metti in un array di char (carattere per carattere) finché non incontri il tuo terminatore oppure se i caratteri sono finiti, a quel punto esci e fai ciò che devi, ovvero se hai incontrato il terminatore processi il dato, altrimenti scarti tutto in quanto la trasmissione non è completa.
Ma vista la tua casistica specifica eviterei tutta questa complessità in quanto tu trasmetti valori che rientrano nel singolo byte per volta, quindi con una semplice Serial.read() leggi il carattere che ti è arrivato lo casti come byte in modo da poterlo trattare direttamente (il byte accetta valori da 0 a 255, tu hai da 0 a 40 quindi perfettamente lecito), non aspetti null'altro ma processi il carattere ricevuto, al prossimo giro di loop se hai ancora caratteri nella seriale ripeti la cosa, sta a te lato Java inviare un singolo byte che contenga il tuo valore, ovvero non devi inviare caratteri ma valori (Cerca Arduino Serial.write e Serial.print per maggiori chiarimenti) a quel punto non hai protocollo da gestire, hai la massima velocità possibile e non usando classi complesse avrai possibilità vicine allo zero che il tuo programma si blocchi per problemi legati all'occupazione di memoria.

Concondo anche sul rimuovere (temporanemante perché alla fine ci arrivi alla soluzione, quasi garantito :wink: ) il risolto

Intanto grazie mille a tutti per il tempo e le importanti informazioni che mi avete dato.
Ecco la soluzione attuale che sembra funzioni alla perfezione. Questa volta non esulto prima di avere un vostro riscontro.

Lato PC (Java)
La variabile che ricevo per la velocità (speed) è Float, quindi la converto in numero intero arrotondando con la funzione Math.round, poi converto il valore intero in byte che a sua volta converto in char per trasmetterlo tramite la funzione arduino.serialWrite che accetta solo caratteri o stringhe.

int velocita = Math.round(speed);
byte velo = (byte) velocita;
char v = (char) velo;
arduino.serialWrite(v);

Lato Arduino
Come mi avete consigliato e grazie al range di valori numerici limitato che ricevo sfrutto ogni byte ricevuto da Arduino per attivare i relay secondo il codice che segue.

const int r1 = 7;
const int r2 = 6;
byte V;

void setup() {
  Serial.begin(9600);
  pinMode(r1, OUTPUT);
  pinMode(r2, OUTPUT);
  digitalWrite(r1, HIGH);
  digitalWrite(r2, HIGH);
}

void loop() {
  if (Serial.available() > 0) {
    V = Serial.read();
    if(V > 0 ){
      if(V > 12 ){
        digitalWrite(r1,HIGH);
        digitalWrite(r2,LOW);
      } else{
        if(V > 2 ){
          digitalWrite(r1,LOW);
          digitalWrite(r2,HIGH);
        } else {
          digitalWrite(r1,HIGH);
          digitalWrite(r2,HIGH);
        }
      }
    } 
  }
}

Ho lasciato così il timeout di default e la reattività non è cambiata di una virgola. Sembra abbia raggiunto la soluzione grazie al vostro prezioso aiuto. Ribadisco che prima di considerare chiusa la questione attendo vostro parere. :wink:

Mi sembra possa andare così ad una prima occhiata.
Mi resta un piccolo dubbio una curiosità diciamo, vista l'alta frequenza d'aggiornamento come fanno i relé a starti dietro??? Sono davvero relé o sono SSR?