come utilizzare lettura bilancia in un ciclo

Salve, sono nuovo e quello per cui vi chiederò aiuto è il mio progetto con Arduino uno. Fino ad oggi non avevo mai provato ad utilizzare Arduino, ma grazie alle poche nozioni di matlab sono riuscito a far funzionare il mio progetto. In poche parole si tratta di una bilancia che se rileva un peso compreso in un certo intervallo (io ho messo 40g e 160g) fa partire una serie di azioni. Se ciò non accade continua il monitoraggio. Le azioni che partono quando la bilancia rileva il peso dell'intervallo specificato, sono:

  1. azionamento di un servomotore, 179 gradi
  2. apertura di due relè per un certo tempo e chiusura dei due successivi
  3. chiusura dei primi due e apertura degli ultimi due
  4. azionamento del servomotore, 0 gradi

I relè servono per azionare un motore di un tergicristallo alimentato esternamente con 12v. Il perchè di 4 relè è per invertire l'alimentazione (i primi due sono + e - e gli altri due sono - e +) ed ottenere prima la rotazione oraria e poi antioraria.

Dopo qualche test mi sono accorto che il sensore ha come un transitorio, nel senso che se metto un oggetto di 300g sulla bilancia può anche fare due tre rilevazioni con peso incrementale (tipo 60g, 180g, 300g=peso corretto) e questo manda in crisi la condizione di azionamento del ciclo.
Per ovviare al problema ho pensato di cambiare il ciclo if, in maniera tale che scarti le prime letture facendo la media delle successive (ad esempio le 20 successive). In questo modo otterrei sicuramente una rilevazione più attendibile, perchè in questo progetto non conta tanto l'errore di rilevazione del peso ma che la variazione di peso tra il prima e dopo sia inclusa nell'intervallo 40g e 160g.
L'idea che mi sono fatto per fare tutto questo è costruire un array che contenesse tutte le pesate eccetto le prime 3-4 (quindi se le pesate fossero 20 dovrebbe essere un array di dimensioni 17-16) e fare la media di tutti gli elementi dell'array con la funzione average. A questo punto il numero ottenuto va verificato se è compreso nell'intervallo 40-160: se si partono le azioni, altrimenti no e la bilancia deve continuare il monitoraggio. Il problema è che sto provando da giorni ma non riesco, anche se capisco il senso generale non riesco a scrivere niente...sapreste aiutarmi?
Ecco lo sketch:

#include "HX711.h"
#define DOUT A1
#define CLK A0
HX711 bilancia(DOUT, CLK);
int peso = 0; // zona di memorizzazione del peso corrente
int pesoprec = 0; // zona di memorizzazione dell'ultimo peso esposto
int X = peso - pesoprec;
int RELAY1 = 2 ;   //PIN pulsante 1 azionamento motore
int RELAY2 = 5;    //PIN pulsante 1 azionamento motore
int RELAY3 = 6;    //PIN pulsante 1 azionamento motore
int RELAY4 = 7;    //PIN pulsante 1 azionamento motore
#include <Servo.h>
Servo fermo;
//
//
void setup()
{
  Serial.begin(9600);
  Serial.println("calcolo della tara - tare");
  Serial.println("non mettere nulla sul piatto – don’t place anything on the scale...");
  delay (94);
  bilancia.set_scale(100); // inserire il valore di scala, al posto di 2022 - insert scale value instead of 2022
  bilancia.tare(20); // il peso a vuoto e' considerato tara
  Serial.println("sistema pronto - system ready");

}
//
void loop()
{ peso = bilancia.get_units(50), 3;

  if ((peso > 40 && peso < 160)) // se e' variato il peso
  {
    pesoprec = peso; // memorizza il peso corrente
    Serial.print("peso - weigth: ");
    Serial.print(peso);
    Serial.println("g");

    fermo.attach(3);      //collegamento servomotore pin3


    fermo.write(179);    //gradi di rotazione servomotore, apertura perno
    delay(1000);

    //motore piano
    pinMode(RELAY1, OUTPUT);
    pinMode(RELAY2, OUTPUT);
    pinMode(RELAY3, OUTPUT);
    pinMode(RELAY4, OUTPUT);

    digitalWrite(RELAY1, LOW);
    digitalWrite(RELAY2, LOW);
    digitalWrite(RELAY3, HIGH);
    digitalWrite(RELAY4, HIGH);
    delay(2000);             //durata apertura relè, pulsante motore 2

    digitalWrite(RELAY1, HIGH);
    digitalWrite(RELAY2, HIGH);
    digitalWrite(RELAY3, LOW);
    digitalWrite(RELAY4, LOW);
    delay(2000);             //durata apertura relè, pulsante motore 1

    digitalWrite(RELAY1, HIGH);
    digitalWrite(RELAY2, HIGH);
    digitalWrite(RELAY3, HIGH);
    digitalWrite(RELAY4, HIGH);
    delay(2000);             //durata chiusura tutti i relè

    fermo.write(0);    //gradi di rotazione servomotore, chiusura perno

    delay(10000); // attende 100 millisecondi prima di procedere
    // alla successiva pesata
  }
}

Grazie

Per invertire la marcia del motore e spegnerlo bastano 2 relé. tra i due C il motore e sui NO + alimentazione e i NO a massa. Accendi un relé il motore gira in un verso, acendi l' altro gira oposto, Tutti i due rele accesi o spenti il motore é fermo (frenato perché esso in corto).
Quanto é il range del modulo bilancia.

Ciao Uwe

uwefed:
Per invertire la marcia del motore e spegnerlo bastano 2 relé. tra i due C il motore e sui NO + alimentazione e i NO a massa. Accendi un relé il motore gira in un verso, acendi l' altro gira oposto, Tutti i due rele accesi o spenti il motore é fermo (frenato perché esso in corto).
Quanto é il range del modulo bilancia.

Ciao Uwe

Il range bilancia è 0-20kg.

Ho optato per la configurazione con i 4 relè perchè per invertire la rotazione del motore bisogna invertire manualmente l'alimentazione e non ho trovato modo migliore per farlo. Quindi le connessioni nel contatto centrale dei relè è:
relè 1: +12v
relè 2: -12v

relè 3: -12v
relè 4: +12v

Quindi quando il relè 1 e 2 sono attivi alimentano il motore sui suoi contatti in quel modo, quando sono attivi il 3 e 4 agli stessi contatti sul motore arrivano invertiti.

Quale cella di carico stai usando? Prego Link al prodotto.
Ciao Uwe

uwefed:
Quale cella di carico stai usando? Prego Link al prodotto.
Ciao Uwe

Si scusami...preso dalla scrittura mi sono scordato di metterlo!

https://www.amazon.it/gp/product/B07912LX1S/ref=ppx_yo_dt_b_asin_title_o01__o00_s00?ie=UTF8&psc=1

C’è qualcuno che puó aiutarmi?

Non conosco la libreria che usi.

Per fare una media non usando i primi valori di misura

long media;
int array[20];

for (int i=0; i<20;i++)
{
array[i]= lettura HX711;
}

for (int i=5; i<20;i++)
{
media += array[i];
}

media = media/15;

ciao Uwe

Praticamente scarterebbe le prime 5 misure? La libreria ho preso questa perché è l’unica che aveva un esempio fatto molto bene, se pensi che una di quelle che conosci sia migliore dimmelo pure e la userò, in fondo sono qui per imparare :slight_smile:
Comunque grazie, in giornata provo e ti faccio sapere

Visto che non si fa avanti nessuno dico la mia perchè piuttosti di niente è meglio piuttosto :wink:

Inizio con un dubbio perchè c'è una riga che mi suona strana ma forse è solo per la mia ignoranza:

peso = bilancia.get_units(50), 3;

cosa dovrebbe fare? a naso mi aspetterei un errore di compilazione, la virgola non dovrebbe essere un operatore aritmetico giusto?

Non ho mai usato questo sensore anche se ne ho acquistato uno perchè mi ha incuriosito e prima o poi lo proverò.

Comunque, siccome il principio di base è meccanico, mi aspetto il transitorio rilevato a meno che non venga bypassato dalla libreria.
Io personalmente non lo nasconderei con la libreria per cui parto dal presupposto che ci sia e soprattutto che sia corretto che ci sia, quindi va solo gestito.

Perchè mi aspetto che ci sia? per due motivi:

  1. perchè il sensore misura una distorsione sul sensore e questo non avviene mai in modo istantaneo
  2. perchè le letture del sensore avvengono alla velocità della luce, se non viene rilevato il peso, il loop fa solo letture alla massima velocità possibile, per cui becca il transitorio anche se molto breve.

Visto che lo scopo di questo progetto è di fare una singola operazione se si verifica una lettura di peso di un certo tipo, non mi scandalizzo per l'uso massiccio che è stato fatto della funzione delay e suggerisco un approccio che la usa ancora di più :wink:

Abbiamo il transitorio e dobbiamo conviverci al meglio, quindi la proposta è:
Faccio X letture in sequenza distanziate di Y per dare tempo al sensore di stabilizzare la lettura, registro le letture, faccio la media e vedo se l'ultima lettura è in media oppure no, se lo è, la lettura è stabile e lancio la logica di verifica.

Ho buttato giù una ristrutturazione del codice come esempio, fatta qui, per cui potrei aver fatto errori di battitura.

#include "HX711.h"
#define DOUT A1
#define CLK A0
HX711 bilancia(DOUT, CLK);
/ Non servono più.
// int peso = 0; // zona di memorizzazione del peso corrente
// int pesoprec = 0; // zona di memorizzazione dell'ultimo peso esposto
// int X = peso - pesoprec;
int RELAY1 = 2 ;   //PIN pulsante 1 azionamento motore
int RELAY2 = 5;    //PIN pulsante 1 azionamento motore
int RELAY3 = 6;    //PIN pulsante 1 azionamento motore
int RELAY4 = 7;    //PIN pulsante 1 azionamento motore
#include <Servo.h>
Servo fermo;

#define MAX_ELEMENT 5 // Numero di letture sequenziali per verificare se la lettura è stabile.
#define DELAY_LETTURE = 10 // Delai in millisecondi tra una lettura e l'altra.
#define DELTA_PESO = 2 // Differenza di peso in più o in meno tollerata per dichiarare la lettura stabile.
//
//
void setup()
{
  Serial.begin(9600);
  Serial.println("calcolo della tara - tare");
  Serial.println("non mettere nulla sul piatto - don't place anything on the scale...");
  delay (94);
  bilancia.set_scale(100); // inserire il valore di scala, al posto di 2022 - insert scale value instead of 2022
  bilancia.tare(20); // il peso a vuoto e' considerato tara
  Serial.println("sistema pronto - system ready");

}
//
void loop() {
  int pesoRilevato = test();
  if (pesoRilevato >= 0) {
    azione(pesoRilevato);
  }
}

// Ritorna -1 se il peso non è stabile.
int test() {
  int peso = 0;
  int pesoTot = 0;
  
  for (int i = 0; i < MAX_ELEMENTI; i++) {
    peso = bilancia.get_units(50); // Lettura sensore
    pesoTot += peso;
    delay(DELAY_LETTURE);
  }
  int pesoMedio = pesoTot / MAX_EMENENTI;
  if (peso >= pesoMedio - DELTA_PESO && peso <= pesoMedio + DELTA_PESO) {
    // Il peso è stabile all'interno dell'intervallo pesoMedio +/- DELTA_PESO
    return peso;
  }
  return -1;
}

void azione(int peso) {
  if ((peso > 40 && peso < 160)) // se e' variato il peso
  {
    pesoprec = peso; // memorizza il peso corrente
    Serial.print("peso - weigth: ");
    Serial.print(peso);
    Serial.println("g");

    fermo.attach(3);      //collegamento servomotore pin3


    fermo.write(179);    //gradi di rotazione servomotore, apertura perno
    delay(1000);

    //motore piano
    pinMode(RELAY1, OUTPUT);
    pinMode(RELAY2, OUTPUT);
    pinMode(RELAY3, OUTPUT);
    pinMode(RELAY4, OUTPUT);

    digitalWrite(RELAY1, LOW);
    digitalWrite(RELAY2, LOW);
    digitalWrite(RELAY3, HIGH);
    digitalWrite(RELAY4, HIGH);
    delay(2000);             //durata apertura relè, pulsante motore 2

    digitalWrite(RELAY1, HIGH);
    digitalWrite(RELAY2, HIGH);
    digitalWrite(RELAY3, LOW);
    digitalWrite(RELAY4, LOW);
    delay(2000);             //durata apertura relè, pulsante motore 1

    digitalWrite(RELAY1, HIGH);
    digitalWrite(RELAY2, HIGH);
    digitalWrite(RELAY3, HIGH);
    digitalWrite(RELAY4, HIGH);
    delay(2000);             //durata chiusura tutti i relè

    fermo.write(0);    //gradi di rotazione servomotore, chiusura perno

    delay(10000); // attende 100 millisecondi prima di procedere
    // alla successiva pesata
  }
}

Se la lettura non è stabile ripeto il test immediatamente, per cui al massimo ritardo di qualche mS l'azione.

Meno letture faccio e più intercetto una lettura stabile.
Inizialmente pensavo a 3 poi ho messo 5, non ne farei di più, tanto male che vada esce e rifa il test immediatamente con altre X letture.

Come delay tra le letture ho messo 10 ma dai risultati indicati potrebbe essere ancora più piccolo, se senza delay si stabilizza in 3 o 4 letture potrebbe essere sufficiente anche 1mS o ancora meno.

Ho messo il tuo codice dentro a delle funzioni così il loop è brevissimo e si capisce subito come funziona, il resto è isolato nelle funzioni specifiche e si dovrebbe leggere agevolmente.

Sulla dichiarazione delle variabili ho ripulito solo quello che mi serviva, non ho controllato tutto.

maubarzi:
Inizio con un dubbio perchè c'è una riga che mi suona strana ma forse è solo per la mia ignoranza:

peso = bilancia.get_units(50), 3;

... è strana, ma NON errata, è l'operatore ',' (comma operator).

Quello che accade è che viene eseguita la funzione "bilancia.get_units(50)", il suo risultato scartato (ma se ha modificato delle globali o fatto altro, l'effetto rimane) e a "peso" viene assegnato il valore 3.

Naturalmente NON ho esaminato il programma e non so se la cosa abbia un senso o meno ...

Guglielmo

Io userei un approccio diverso, che poi è quello che uso quando devo pesarmi il pane o la frutta al supermercato:

  • Metto la roba sulla bilancia, il peso inizia a oscillare
  • Aspetto che sia stabile per un secondo o giù di lì e premo il pulsante.

SukkoPera:
Io userei un approccio diverso, che poi è quello che uso quando devo pesarmi il pane o la frutta al supermercato:

  • Metto la roba sulla bilancia, il peso inizia a oscillare
  • Aspetto che sia stabile per un secondo o giù di lì e premo il pulsante.

Qui da noi ho sempre visto, nei supermercati, bilance che fanno da sole esattamente ciò ... metti quello che devi pesare, passa qualche secondo (stabilizzazione del peso) e esce il foglietto da applicare sul prodotto :wink:

Guglielmo

Certo, in Svizzera funzionerà così. Qua è più probabile che ti considerino peso giusto quello di picco :D.

SukkoPera:
Qua è più probabile che ti considerino peso giusto quello di picco :D.

... bastardi!

Guglielmo

gpb01:
... è strana, ma NON errata, è l'operatore ',' (comma operator).

Grazie, avevo premesso la mia ignoranza in merito :wink:

@SukkoPera tu lanci la lettura a mano però, da quel che ho capito, invece, si vuole una cosa automatica.
Comunque, la mia soluzione era impostata per non stravolgere il codice dell'OP. Infatti non ho nemmeno proposto di debellare i delay, anzi... :wink:
Però, indirettamente, confermi che il transitorio è cosa normale...

SukkoPera:
Certo, in Svizzera funzionerà così. Qua è più probabile che ti considerino peso giusto quello di picco :D.

Ecco perchè nel mio supermercato hanno eliminato le casse automatiche... gli smaronava questo approccio...
Non riuscivo a spiegarmelo, tutti le montano e loro le tolgono...

gpb01:
... bastardi!

concordo!!!

maubarzi:
Visto che non si fa avanti nessuno dico la mia perchè piuttosti di niente è meglio piuttosto :wink:

Inizio con un dubbio perchè c'è una riga che mi suona strana ma forse è solo per la mia ignoranza .....

Era infatti l'idea iniziale, che avevo abbandonato perchè non riuscivo a capire come realizzarla.
Ho provato il codice e ho trovato due errori:
il primo era nella dichiarazione della variabile MAX_ELEMENTI che non aveva la I finale, l'ho aggiunta e la verifica è andata oltre. Successivamente ho un altro errore sulla variabile DELTA_PESO = 2 che non riesco a capire. Sono agli inizi perdonami, non ho una grande capacità diagnostica :smiley:
Di seguito riporto l'errore

Arduino:1.8.8 (Windows Store 1.8.19.0) (Windows 10), Scheda:"Arduino/Genuino Uno"

D:\Documents\Arduino\sketch_rev5\sketch_rev5.ino: In function 'int test()':

sketch_rev5:19:23: error: expected primary-expression before '=' token

 #define DELAY_LETTURE = 10 // Delai in millisecondi tra una lettura e l'altra.

                       ^

D:\Documents\Arduino\sketch_rev5\sketch_rev5.ino:50:11: note: in expansion of macro 'DELAY_LETTURE'

     delay(DELAY_LETTURE);

           ^

sketch_rev5:52:29: error: 'MAX_EMENENTI' was not declared in this scope

   int pesoMedio = pesoTot / MAX_EMENENTI;

                             ^

sketch_rev5:20:20: error: expected primary-expression before '=' token

 #define DELTA_PESO = 2 // Differenza di peso in più o in meno tollerata per dichiarare la lettura stabile.

                    ^

D:\Documents\Arduino\sketch_rev5\sketch_rev5.ino:53:27: note: in expansion of macro 'DELTA_PESO'

   if (peso >= pesoMedio - DELTA_PESO && peso <= pesoMedio + DELTA_PESO) {

                           ^

sketch_rev5:20:20: error: expected primary-expression before '=' token

 #define DELTA_PESO = 2 // Differenza di peso in più o in meno tollerata per dichiarare la lettura stabile.

                    ^

D:\Documents\Arduino\sketch_rev5\sketch_rev5.ino:53:61: note: in expansion of macro 'DELTA_PESO'

   if (peso >= pesoMedio - DELTA_PESO && peso <= pesoMedio + DELTA_PESO) {

                                                             ^

D:\Documents\Arduino\sketch_rev5\sketch_rev5.ino: In function 'void azione(int)':

sketch_rev5:63:5: error: 'pesoprec' was not declared in this scope

     pesoprec = peso; // memorizza il peso corrente

     ^

exit status 1
expected primary-expression before '=' token

Questo report potrebbe essere più ricco di informazioni abilitando l'opzione
"Mostra un output dettagliato durante la compilazione"
in "File -> Impostazioni"

>marcook: quando si "quota" un post, NON è necessario riportarlo (inutilmente) tutto; bastano poche righe per far capire di cosa si parla ed a cosa ci si riferisce. Gli utenti da device "mobile" ringrazieranno per la cortesia :wink:

Ho quindi editato io il tuo post.

Guglielmo

marcook:
Di seguito riporto l'errore

Nelle #define NON si mette il simbolo di '=' ... es. #define PIPPO 100

Guglielmo

si, mi sono scappati gli uguali, basta eliminarli come dice Guglielmo.
La I anche, all'inizio volevo scrivere il nome in inglese ma poi ho optato per l'italiano aggiungendo la I ma evidentemente non l'ho fatto ovunque :wink: