wieder mal Flowmeter delay vs millis

hab da wieder mal ein vielleicht verstädnissproblem,
mit delay funktioniert der flowmeter ziehmlich genau, aber das delay stört halt wieder andere funktionen die ich noch einfügen möchte, mit millis geht garnix, vielleicht weis ja jemmand woran das liegt.

mit delay

int flowPin = 20;
float flowRate;
volatile int count;
unsigned long totalMilliLitres;
float Liters;

void setup() {
  pinMode(flowPin, INPUT);
  attachInterrupt(20, Flow, RISING);
  Serial1.begin(9600);
}
void loop() {
  count = 0;
  interrupts();
  delay (1000);    
  noInterrupts();

  flowRate = (count * 0.1295);
  flowRate = flowRate * 3600;

  flowRate = flowRate / 1000;
  totalMilliLitres += flowRate;
  Liters = totalMilliLitres / 1000.0;

  Serial1.print("t13.txt=");
  Serial1.write(0x22);
  Serial1.print(flowRate);
  Serial1.write(0x22);
  Serial1.write(0xff);
  Serial1.write(0xff);
  Serial1.write(0xff);

  Serial1.print("t5.txt=");
  Serial1.write(0x22);
  Serial1.print(Liters);
  Serial1.write(0x22);
  Serial1.write(0xff);
  Serial1.write(0xff);
  Serial1.write(0xff);
}

void Flow()
{
  count++;
}

und millis

int flowPin = 20;

unsigned long count_zeit;
unsigned long totalMilliLitres;

float flowRate;
float Liters;
volatile int count;

void setup(void) {
  pinMode(flowPin, INPUT);
  attachInterrupt(20, Flow, RISING);
  Serial1.begin(9600);
}

void loop(void) {
  count = 0;
  interrupts();
  if (millis() - count_zeit > 1000) {
    noInterrupts();
    count_zeit = millis();

    flowRate = (count * 0.1295);
    flowRate = flowRate * 3600;
    flowRate = flowRate / 1000;
    totalMilliLitres += flowRate;
    Liters = totalMilliLitres / 1000.0;

    Serial.println(totalMilliLitres);
    Serial.println(flowRate);

    Serial1.print("t13.txt=");
    Serial1.write(0x22);
    Serial1.print(flowRate);
    Serial1.write(0x22);
    Serial1.write(0xff);
    Serial1.write(0xff);
    Serial1.write(0xff);

    Serial1.print("t5.txt=");
    Serial1.write(0x22);
    Serial1.print(Liters);
    Serial1.write(0x22);
    Serial1.write(0xff);
    Serial1.write(0xff);
    Serial1.write(0xff);
  }
}
void Flow()
{
  count++;
}

Hi

Nimm count=0 auf loop() raus und resette den Wert innerhalb der Abfrage - dann wird langsam ein Schuh daraus.

MfG

PS:
const byte flowPin=20; //der Pin wird sich NIE ändern - dann nenne Ihn auch so, also const - Byte reicht
volatile uint16_t count; // Du erwartest doch keine negativen Werte, oder? Dann unsigned (Vorzeichenlos)

so geht es zwar, aber dann bleibt der momentandurchluss am letzten wert stehen wo es aufhört durchzulaufen, und der durchgelaufene wert steigt permanent
oder hab ich das count an falscher stelle gesetzt ??

const byte flowPin=20;

unsigned long count_zeit;
unsigned long totalMilliLitres;

float flowRate;
float Liters;
volatile uint16_t count;

void setup(void) {
  pinMode(flowPin, INPUT);
  attachInterrupt(20, Flow, RISING);
  Serial1.begin(9600);
  count = 0;
}

void loop(void) {
  interrupts();
  if (millis() - count_zeit > 1000) {
    noInterrupts();
    count_zeit = millis();

    flowRate = (count * 0.1295);
    flowRate = flowRate * 3600;
    flowRate = flowRate / 1000;
    totalMilliLitres += flowRate;
    Liters = totalMilliLitres / 1000.0;

    Serial.println(totalMilliLitres);
    Serial.println(flowRate);

    Serial1.print("t13.txt=");
    Serial1.write(0x22);
    Serial1.print(flowRate);
    Serial1.write(0x22);
    Serial1.write(0xff);
    Serial1.write(0xff);
    Serial1.write(0xff);

    Serial1.print("t5.txt=");
    Serial1.write(0x22);
    Serial1.print(Liters);
    Serial1.write(0x22);
    Serial1.write(0xff);
    Serial1.write(0xff);
    Serial1.write(0xff);
  }
}
void Flow()
{
  count++;
}

Die Interrupts nur zum Messen einzuschalten ist eine ganz dumme Idee. Dein Interrupt ist ja nicht der einzige, der da ausgeführt werden möchte. Deshalb die Interrupts immer nur kurz ausschalten. Versuchs mal so ( ungetestet ):

void loop(void) {
  if (millis() - count_zeit > 1000) {
    noInterrupts();
    count_zeit = millis();
    flowRate = (count * 0.1295);
    count = 0;
    interrupts();
    flowRate = flowRate * 3600;
    ...

Hallo,
um welche Durchflüsse , bzw. Frequenzen gehts denn da überhaupt.
bei Frequenzen unter 100Hz misst man ja besser die Periodendauer, damit man wenigstens 1% Auflösung hinbekommt. mechanische Durchflussmesser ( Zahnrad, Turbine)die ich so kenne liegen zwischen 20-2000Hz. Mann nimmt da gerne ehr eine grössere Baugrösse, damit ist der Druckverlust dann kleiner, aber leider auch die Frequenz.

Heinz

MicroBahner:
Die Interrupts nur zum Messen einzuschalten ist eine ganz dumme Idee. Dein Interrupt ist ja nicht der einzige, der da ausgeführt werden möchte. Deshalb die Interrupts immer nur kurz ausschalten. Versuchs mal so ( ungetestet ):

void loop(void) {

if (millis() - count_zeit > 1000) {
   noInterrupts();
   count_zeit = millis();
   flowRate = (count * 0.1295);
   count = 0;
   interrupts();
   flowRate = flowRate * 3600;
   ...

Das wars, jetzt gehts, es handelt sich um ein Biotech Sensor der 10500 pulse pro liter liefert

Hallo,

wegen dem Interrupt, würde ich so machen …

#include <util/atomic.h>

const byte flowPin=20;

unsigned long count_zeit;
unsigned long totalMilliLitres;

float flowRate;
float Liters;
volatile unsigned long count;

void setup(void) {
  pinMode(flowPin, INPUT);
  attachInterrupt(flowPin, flow, RISING);
  Serial1.begin(9600);
}

void loop(void)
{
  messung(1000);

  anzeige(2000);
}


void flow()
{
  count++;
}

void messung (const unsigned int interval)                        
{
  static unsigned long last_ms = 0;  
  unsigned long ms = millis();
  
  if (ms - last_ms >= interval) {
    last_ms = ms;

    unsigned long countTemp = 0;
    ATOMIC_BLOCK (ATOMIC_RESTORESTATE) {
      countTemp = count;
      count = 0;
    }
    flowRate = (countTemp * 0.1295);
    flowRate = flowRate * 3600;
    flowRate = flowRate / interval;
    totalMilliLitres += flowRate;
    Liters = 1.0 * totalMilliLitres / interval;
  }
}

void anzeige (unsigned int interval)                        
{
  static unsigned long last_ms = 0; 
  unsigned long ms = millis();
  
  if (ms - last_ms >= interval) {
    last_ms = ms;
    
    Serial.println(totalMilliLitres);
    Serial.println(flowRate);

    Serial1.print("t13.txt=");
    Serial1.write(0x22);
    Serial1.print(flowRate);
    Serial1.write(0x22);
    Serial1.write(0xff);
    Serial1.write(0xff);
    Serial1.write(0xff);

    Serial1.print("t5.txt=");
    Serial1.write(0x22);
    Serial1.print(Liters);
    Serial1.write(0x22);
    Serial1.write(0xff);
    Serial1.write(0xff);
    Serial1.write(0xff);
  }
}

funktioniert alles super, ich frag mich nur, warum das so stark abgestuft ist mit der literanzeige pro std,

8,39
7,93
7,46
6,99

das sind schon fast 0.5 liter abstufungen

Hallo,

lasse dir erstmal nur die counts ohne Umrechnung ausgeben. Alles weitere ergibt sich daraus.
Nennt sich allgemeine Fehlersuche ... falls es ein Fehler ist.

Medolino73:
funktioniert alles super, ich frag mich nur, warum das so stark abgestuft ist mit der literanzeige pro std,

8,39
7,93
7,46
6,99

das sind schon fast 0.5 liter abstufungen

Rechne mal nach: Dein Sensor liefert 10500 Impulse/Liter, das sind also 5250 Impulse für einen halben Liter. Du rechnest auf 'pro Std' um, misst aber nur 1 Sekunde lang. Ein halber Liter pro Stunde sind aber nur 1,46 Impulse / sekunde. Da reicht deine Auflösung nicht. Wenn Du eine bessere Auflösung / Std willst, muss Du länger messen.
Oder umgekehrt gerechnet: Dein Sensor hat eine Auflösung von 1/10500 l = 0,095 ml . Kling gut.
Aber wenn Du nur 1 Sec misst, und auf eine Stunde hochrechnest ist die theoretische Auflösung schon nur noch 0,095 * 3600 = 0,34l.

Edit: bei deinen Angaben zum Sensor verstehe ich auch deine Umrechnungswerte im Sketch nicht.

Hallo

ich hatte es bereits vermutet siehe #4 .

wenn du es Dir leisten kannst 5 oder 10s zu messen dann mach das, ansonsten gehts halt nur mit Messung der Periodendauer. Eventuell schwankt der Messwert dann immer noch. Eine gängige Methode für kleine Frequenzen ist dann die Messung über mehrere Perioden.

Wenn das mit den ca. 8l/h in Etwa stimmt ist deine Frequenz etwa .

8/3600*10500=23,33Hz.

damit misst Du 23, 24 Impulse bei 1s und erhälst eine Auglösung von etwa 5% was sich in etwa mit Deinen Messwerten deckt.

Heinz

Hi

Du kannst auch 10x eine Sekunde messen und die 10 Einzelwerte zusammen zählen.
(Array mit 10 Feldern als Ringspeicher)
Dann gleicht sich Dein errechneter Wert binnen 10 Sekunden 'smooth' an die Realität an.

MfG

postmaster-ino:
Hi

Du kannst auch 10x eine Sekunde messen und die 10 Einzelwerte zusammen zählen.
(Array mit 10 Feldern als Ringspeicher)
Dann gleicht sich Dein errechneter Wert binnen 10 Sekunden 'smooth' an die Realität an.

MfG

oh je, wie das geht weis ich leider nicht, bei BMW gibts da auch so eine Momentanverbrauchsanzeige, so wäre das perfekt gewesen

Hi

Du misst, wie jetzt auch, jede Sekunde die Impulse.
Dann speicherst Du diesen Wert in einem Array.
Jedes Mal an die nächste Stelle (0...9) - am Ende angekommen (wir haben in 9 gespeichert), den Index wieder auf 0 (Null) setzen.
Für Deine Liter addierst Du jetzt alle 10 Array-Inhalte und bekommst die Impulse für 10 Sekunden - der Rest ist wie jetzt, nur Dein 'Grundwert' entspricht 10 Sekunden, nicht Einer.

Das Ganze ist träger, da wir erst nach 10 Sekunden 'ein Mal durch' sind - bei kontinuierlicher Förderung oder Änderungen >>10 Sekunden, wird die Anzeige aber ruhig die Fördermenge wiedergeben.
Bei spontanen Extremen - Pumpe stoppt, wandert die Anzeige erst innerhalb 10 Sekunden auf Null - da ja diese 10 Sekunden noch alte Messwerte mit bereits gezählten Impulsen im Array liegen.

...
const byte anz_messwerte=10;   //hier kannst Du 'spielen' - also auch weniger Messwerte gruppieren
uint16_t messwerte[anz_messwerte];  //Platz für die Werte - reicht hier 16 Bit? (65535)
byte akt_messwert=0;  //wir beginnen bei Null
uint32_t summe_messwerte;   //wird mit 0 (Null) initialisiert weil global
...
setup(){
   ... //hier brauchen wir keine Extrawurst
}
...
loop(){
   ...
   //Messwert wurde hier drüber ermittelt
   summe_messwerte-=messwerte[akt_messwert]; //den alten Messwert dieser Stelle von der Summe abziehen
   messwerte[akt_messwert]=countTemp; //den aktuellen Wert an die aktuelle Stelle schreiben
   summe_messwerte+=messwerte[akt_messwert]; //den neuen Messwert dieser Stelle auf der Summe addieren

   akt_messwert=(akt_messwert+1)%anz_messwerte;  //die Nummer + 1, auf die Max-Anzahl begrenz - Eintrag-Stelle für den NÄCHSTEN Messwert
   //Berechnung für Fördermenge Stunde mit summe_messwerte
   ...
}

Denke, Das sollte so klappen

MfG

Hallo,

das Zauberwort heißt gleitender Mittelwert, nicht einfach die Messzeit erhöhen.

Hi

Jupp - daher stammt der Ansatz - Das 'erhöht' aber ebenfalls die Messzeit, ist ja nichts Anderes - nur ohne Lib drum herum und ohne Teilung durch die Anzahl der Einzelwerte am Ende.

MfG

Hallo,

das mag fast so sein. Ich empfinde es nur schöner und praktischer wenn ich aller Sekunde einen aktuellen Mittelwert bekomme und nicht aller 10 Sekunden einen der dann schon 10s alt ist. Man kann sehr früh den Trend erkennen. Der gleitende Mittelwert hinkt zwar auch bedingt hinterher, wird aber im Hintergrund ständig aktuell gehalten. Der abgefragte Wert ist auch keine 10s alt. Beim Messwerte sammeln mit Array ist man gezwungen zu warten. Der kleine feine Unterschied. Wenn das nicht wichtig ist, dann nehmt das Array. Vielleicht auch einfacher umsetzen für den TO.

Hi

Du bekommst ja jede Sekunde den aktuellen Mittelwert, jede Sekunde geht der älteste Wert aus der Summe raus und der Neueste kommt rein - genau das Gleiche wie beim gleitenden Mittelwert - hier aber eine gleitende Summe.
Der gleitende Mittelwert macht's intern nicht anders - daher kam ja diese 'Idee'.
Wo Du 'gleitender Mittelwert' schriebst, war mir auch wieder klar, 'woher der Wind kam' :slight_smile:

Hier brauchen wir halt nicht zurück auf den Mittelwert (also durch die Anzahl der Einzelmessungen teilen), da wir danach eh wieder in die andere Richtung wollen.

MfG

Hallo,

jetzt verstehe ich dein Messverfahren. Leider kommt das in #13 so nicht zur Sprache. Es geht nicht hervor das du aller Sekunde einen Mittelwert von allen 10 Wert bildest. Deshalb das Missverständnis. Ansonsten wäre es dann egal wie man das macht.

Von GuntherB kann ich noch sein Rechenmodell beitragen. Habs nur zum template abgeändert.

struct Sensor {
  byte pin;  
  const float FILTERFAKTOR = 20.0; 
  unsigned long pulse;
  float mittelwert;
};
Sensor sensor;

void setup() {
 
}

void loop()
{  
  Filtern(sensor.mittelwert, sensor.pulse, sensor.FILTERFAKTOR);
}


/*****************************************************************************************
** Funktion Filtern()  by GuntherB                                                      **
******************************************************************************************
** Bildet einen Tiefpassfilter (RC-Glied) nach.                                         **
** FF = Filterfaktor;  Tau = FF / Aufruffrequenz                                        **
**                                                                                      **
** Input: FiltVal der gefilterte Wert, NewVal der neue gelesene Wert; FF Filterfaktor   **
** Output:  FiltVal                                                                     **
** genutzte Globale Variablen: Val  (call by Reference)                                 **
******************************************************************************************/
template <class V, class N, class F>                            // zum template abgeändert
void Filtern(V &FiltVal, N NewVal, F FF){
  FiltVal= ((FF * FiltVal) + NewVal) / (FF+1); 
}

Der gleitende Mittelwert ist eine gute Methode die Auflösung zu steigern, und gleichzeitig eine 'ruhigere' Anzeige zu bekommen. Die ist dann aber auch eben träger, und reagiert nicht so schnell auf Durchflußänderungen.

Will man näher 'am Geschehen' bleiben und trotzdem eine höhere Auflösung erreichen, muss man auf die Methode von Heinz ( Abstand zwischen Impulsen messen ) zurückgreifen. Dabei muss man dann aber auch berücksichtigen, dass ein Meßzyklus gegebenenfalls sehr lange dauert oder gar nicht endet ( wenn der Durchfluß stoppt und gar kein Impuls mehr kommt ). Bei sehr hohen Durchflüssen wird wieder das Zeitintervall sehr kurz und man muss gegebenenfalls dann doch wieder mehrere Impulse messen. Das ist also auch nicht ganz so ohne.

Was er will muss letztendlich der TO entscheiden.