Funzione pulseIn riporta valori troppo elevati

Buonasra, il codice che segue mi legge la frequenza di un anemometro a coppette con magnete e sensore di hall il tutto ripulito da un circuito per sistemare il segnale quindi niente spurie o rimbalzi.
Purtroppo però, il valore in frequenza che poi andrò a convertire in metri al secondo del vento troppo spesso mi riporta valori esagerati che nulla hanno a che vedere con il reale.
Sono sicuro (quasi) che non è un problema di rimbalzi perchè con un altro sketch usando la funzione millis() non dava problemi.
Suggerimenti su dove mettere mano?

Grazie Alberto.

int WINDspin = 4;
unsigned long Htime;
float Ltime;
float Ttime;
float frequency;
float hightime;

void setup() {
pinMode(WINDspin, INPUT);
Serial.begin(9600);
}

void loop() {
Htime = pulseIn(WINDspin, HIGH);
Ltime = pulseIn(WINDspin, LOW);
Ttime = Htime+Ltime;
frequency = (1000000)/Ttime;
//frequency = (frequency/2)*1.44;
Serial.print("Velocità ");
Serial.print(frequency);
Serial.println(" m/s");
delay(1000);
}

Non credo sia questo il caso, però io per non sbagliare in C quando uso float/double ed uso costanti nei calcoli, forzo la costante con i decimali, nel tuo caso 1000000.0
Provare non costa nulla

non converrebbe gestire il tutto con interrupt !?

Stando a questo codice:

Htime = pulseIn(WINDspin, HIGH);
Ltime = pulseIn(WINDspin, LOW);
Ttime = Htime+Ltime;

mi sembra di capire che tu voglia determinare il tempo totale di un singolo impulso, ossia da quando passa da LOW a HIGH, e fino all'inizio del successivo impulso (HIGH), corretto?
In tal caso io avrei usato un interrupt che viene invocato su RISING, ma nel caso gestirei il timeout di PulseIn (restituisce zero, quindi la divisione per il calcolo frequenza darebbe infinito). A parte questo dettaglio però mi sfugge il senso di questa divisione:

frequency = (1000000)/Ttime;

Qual è il sensore esattamente e quale rapporto esiste tra il numero di impulsi (ossia di giri delle coppette) e la relativa velocità del vento?
Tu qui dividi un milione per il tempo rilevato, che è in millisecondi, quindi, per dire, se il sensore ha un impulso per giro e le coppette fanno 1 giro al secondo, avresti calcolato una velocità del vento di 1000 metri al secondo, il che è ovviamente impossibile a meno di non avere coppette lunghe un chilometro... :wink:

Anche io ma in questo caso è ininfluente, il risultato non cambia anche mettendo 1000000.0.

Grazie per le risposte.
L'anemometro ha due magneti quindi doppia "pulsazione".

Ho cercato uno sketch in rete per "Arduino low frequency counter" e ho trovato tre esempi molto simili. Ho tolto le parti che non mi servono relative ai display e aggiunto un fattore di conversione che riportasse valori uguali all'anemometro di precisione che ho.

Poi ora ho aggiunto parti codice per memorizzare le raffiche e inoltre effettivamente mi esce un valore infinito a strumento fermo quindi un altro if{} per forzare a 0.

Accetto consigli per migliorare la situazione quindi mi guarderò gli interrupt come funzionano.

... QUI tutto quello che c'è da sapere sugli Interrupts :wink:

Guglielmo

ok, quindi ogni impulso completo rappresenta mezzo giro, per calcolare i giri al secondo devi prendere il tempo in millisecondi di un impulso, raddoppiarlo e dividere 1000 per questo valore. Una volta che hai i giri al secondo, la velocità del vento la devi ottenere conoscendo le specifiche di quel sensore (io ho avuto a che fare professionalmente con anemometri che però non erano con le coppette o ventoline ma di tipo aeronautico a pressione) ossia cisarà pur scritto da qualche parte il calcolo per ottenere la velocità del vento in relazione alla velocità di rotazione no? (se tu avessi postato un link al tuo anemometro magari sarei stato più preciso...)

Basta una cosa di questo tipo:

int WINDspin = 10;
unsigned long Htime;
float Ltime;
float Ttime;
float frequency;
float hightime;

void setup() {
  pinMode(WINDspin, INPUT);
  Serial.begin(9600);
  delay(1000);
  Serial.println("START");
}

void loop() {
  Htime = pulseIn(WINDspin, HIGH);
  Ltime = pulseIn(WINDspin, LOW);
  if (Htime > 0 && Ltime > 0) {
    Ttime = (Htime+Ltime)*2;
    frequency = 1000.0/Ttime;
    Serial.println(frequency);
  } else
    Serial.println(0);  
  delay(1000);
}

Su wokwi ho la possibilità di creare un chip custom, ok, fatto il chip.
Una volta in esecuzione pigi sul chip custom è compare il cursore da 0 a 100.
Ora il 0÷100 sarebbe il numero di giri per minuto, cioè rpm.
Oppure il cursore da 0 a n.n in m/s.
Nel file diagram.json scrivo questo:

{
      "type": "chip-anemometro",
      "id": "chip1",
      "top": -114.18,
      "left": -465.6,
      "attrs": { "countPerRev": "2" }
    }

countPerRev sarebbe il numero di sensori.

In C ho il main timer autorepeat ogni 100ms per verificare la modifica del cursore da parte dell'utente.
Se il cursore è modificato devo modificare il timer (es.timer_gpio_clbk) che alza e abbassa il pin di out.

Ecco qual'è la formula inversa?
Ogni quanti millesimi di secondo devo chiamare la callback timer_gpio_clbk()?

Ciao.

Prendo ora in mano i vari suggerimenti e vi allego le foto di quello che è l'anemometro in mio possesso:
Sensore hall con scheda dedicata
Anemometro

Spero di aver linkato le immagini giuste.
L'anemometro è vuoto all'origine, solo meccanica.
Ha due sedi per incollare i magneti opposti a 180° uno dall'altro.
Io poi ho inserito e adattato il resto della basetta.

Qual'è il raggio tra centro asse e centro coppetta?
Secondo i miei calcoli:
raggio = 0.1metri
Vvento = 10km/h (~2.77 m/s)
rpm = ~265

Nel caso di 2 magneti 265 x 2 = 530 rpm.
giri/s = 8.8333 giri (530rpm)
periodo(s) = 1 / 8.8333 = 0,1132

Ma non sono per niente sicuro. Ad esempio 113.2ms per fare mezzo giro, sembra proprio così: 0.1132 x = 0,22641, 1 / 0,22641 = 4,4167 che è circa la metà di 8.83.

PS: scusa se non ti so dare risposta in merito, ma sto cercando di modellare il comportamento di un anemometro e ne so pari a zero.

Ciao.

Diametro anemometro 0.2 metri.

Uso questa formula per calcolare la velocità del vento con questo anemometro..... (r) raggio, o braccio ( distanza asse coppette in metri), il numero dei giri al secondo (f), e quindi:
Velocità = r * 6.28 * f = Velocità in m/s. Per Velocità in Km/h = (Vm/s * 3,6).

Ok quindi nel tuo caso io farei così (nota anche qualche "ritocco", come il tipo delle variabili Ltime e Ttime, le #define iniziali con la configurazione, e l'uso del simbolo PI ossia il valore di pi greco...):

#define BRACCIO 0.2
#define P_ANEM 10

unsigned long Htime;
unsigned long Ltime;
unsigned long  Ttime;
float frequency;
float velocity;

void setup() {
  Serial.begin(9600);

  pinMode(P_ANEM , INPUT);

  delay(1000);
  Serial.println("START");
}

void loop() {
  velocity = 0;
  Htime = pulseIn(P_ANEM, HIGH);
  Ltime = pulseIn(P_ANEM, LOW);
  if (Htime > 0 && Ltime > 0) {
    Ttime = (Htime+Ltime)*2;
    frequency = 1000.0/Ttime;
    velocity = 2.0*PI*BRACCIO*frequency*3.6;
  }
  Serial.println(velocity);
  delay(1000);
}

Funziona (credo). Solo che pulseIn restituisce il tempo in us, per cui ho modificato:

frequency = 1000000.0/Ttime;

Se al posto di velocity stampo la frequenza ottengo:

START      cursore g/s
0.00                1
0.00                1
6.03                6
6.03                6
11.06              11
11.06              11

Per ogni giro ci sono 2 impulsi negativi ognuno largo 10us. La larghezza di 10us è fissa e non dipendente dalla velocità di rotazione, come se il circuito rilevatore introducesse un impulso basso di durata fissa.

#define PIN_ANEMO 2

volatile uint8_t cntPulse;
uint32_t saveMillis;
uint32_t saveMicros;

void cbInt2() {
    cntPulse++;
}

void setup() {
    Serial.begin(115200);
    pinMode(PIN_ANEMO, INPUT);
    attachInterrupt(digitalPinToInterrupt(PIN_ANEMO), cbInt2, FALLING);
    saveMillis = millis();
    saveMicros = micros();
}

void loop() {
  if (micros() - saveMicros >= 1000000) {
    saveMicros = micros();
    cli();
      byte cnt = cntPulse;
      cntPulse = 0;
    sei();
      Serial.println(cnt);
      
  }
}

Con il codice sopra ottengo risultati senza virgola corretti in relazione al cursore.

Ciao.

Con il codice di @docdoc modificato così:

#define BRACCIO 0.1
#define P_ANEM 2

unsigned long Htime;
unsigned long Ltime;
unsigned long  Ttime;
float frequency;
float velocity;

void setup() {
  Serial.begin(9600);

  pinMode(P_ANEM , INPUT);

  delay(1000);
  Serial.println("START");
}

void loop() {
  velocity = 0;
  Htime = pulseIn(P_ANEM, HIGH);
  Ltime = pulseIn(P_ANEM, LOW);
  if (Htime > 0 && Ltime > 0) {
    // 4 low pulse per revolution
    Ttime = (Htime+Ltime)*4;
    frequency = 1000000.0/Ttime;
    velocity = 2.0*PI*BRACCIO*frequency;//*3.6;
  }
  Serial.println(frequency);
  delay(1000);
}

Spostando il cursore 1÷100.

Ok grazie per il tempo dedicatomi, ieri sera ho provato a leggere qualcosa sugli interrupt e fare prove.
Pin 2 e 3 per il nano.
Spero di rimettermi sopra al pc sabato per provare i vostri suggerimenti.
Grazie ancora.

Ho provato il codice e funziona, ho aggiunto le cose che mi servivano e cambiato qualche nome variabile.

nella riga:
Ttime = (Htime + Ltime) * 2;
Ho sostituito il 4 con un 2 perchè ho due magneti.

Il problema è unaltro ora, quando la frequenza va sotto il valore 1.0 smette di leggerla e passa tutto a 0.
Il valore della frequenza rimane memorizzato al valore ultimo più basso ecco perchè ho dovuto forzare un frequency = 0 prima del delay

//INIIO Definizione variabili anemometro coppette
#define braccio 0.1 //Diametro anemometro
#define Pin_Anem 3  //Pin ingresso (pin abilitato interrupt)
unsigned long Htime;
unsigned long Ltime;
unsigned long  Ttime;
float frequency;    //Frequenza impulsi
float Wind_velms;   //Velocità vento m/s
float Wind_velmsGUST;//Velocità raffica vento m/s
const long interval = 60000;    // Tempo in millisecondi dopo il quale azzerare la raffica 
/*
60000ms  = 1 minuto
300000ms = 5 minuti
600000ms = 10 minuti
*/
//FINE Definizione variabili anemometro coppette

void setup() {
  Serial.begin(9600);
  pinMode(Pin_Anem , INPUT);
  delay(1000);
}

void loop() {
  Wind_velms = 0;
  Htime = pulseIn(Pin_Anem, HIGH);
  Ltime = pulseIn(Pin_Anem, LOW);
  if (Htime > 0 && Ltime > 0) {
    // 4 low pulse per revolution
    Ttime = (Htime + Ltime) * 2;
    frequency = 1000000.0 / Ttime;
    Wind_velms = (2.0 * PI) * braccio * frequency;
  }

  if (Wind_velms <= Wind_velmsGUST){ //Ciclo if else per memoriare la raffica di vento
  }
  else{
    Wind_velmsGUST = Wind_velms;
  }

  unsigned long previousMillis = 0;    //Inizio funzione reset raffica vento dopo intervallo
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;
    Wind_velmsGUST = Wind_velms;
  }
  Serial.print("Frequenza "); 
  Serial.println(frequency);
  Serial.print("Vel. m/s "); 
  Serial.println(Wind_velms);
  Serial.print("Vel. raffica m/s "); 
  Serial.println(Wind_velmsGUST);
  Serial.println("---------------------");
  
frequency=0;

  delay(1000);
}

Ogni tanto con il simulatore aumentando la velocità di rotazione durante la simulazione mi da un valore inferiore, non ho capito se dipende dal fatto che posso variare la velocità all'istante passando da 1 a 1000 in un instante cosa nella realtà non può accadere.

Rimuovendo il delay e stampando con millis ogni 1 secondi dovrebbe essere ancora meglio.

PS: puoi copiare e incollare una decina di valori che stampa l'algoritmo?
PS1: usa il tag code.

Ciao.

Rieccomi, dopo quasi venti giorni ne sono venuto a capo.
Praticamente ho trovato la pappa quasi pronta proprio qui nel forum, linko qui sotto il post

https://forum.arduino.cc/t/wind-sensor/105709/3

E qua lo sketch da me modificato per le mie esigenze

//WEB_REF https://forum.arduino.cc/t/wind-sensor/105709
int pinIN = 3;                    // PIN input (interrupt)

const float pi = 3.14;            // piGRECO
int period = 10000;               // Periodo misurazione (milisecondi) [maggiore precisione > 5000ms]
int delaytime = 10000;            // Tempo tra i campionamenti (milisecondi) [maggiore precisione > 5000ms]
int radio = 100;                  // Raqggio anemometro (mm)
int MAGSnum = 2;                  // Numero magneti per rotazione
unsigned int Sample = 0;          // Numero campionamenti
unsigned int counter = 0;         // B/W counter
unsigned int RPM = 0;             // RPM
float WindSPEEDms = 0;            // Velocità vento (m/s)
float WindGUSTms = 0;             //Raffica vento (m/s)
unsigned long previousMillis = 0; // Variabile per memorizzare il tempo del ciclo raffica vento
const long GUSTinterval = 300000; // Tempo in millisecondi dopo il quale azzerare la raffica
/* 60000ms  = 1 minuto 300000ms = 5 minuti 600000ms = 10 minuti 900000ms = 15 minuti */

void setup()
{
  Serial.begin(9600);
}

void loop(){
  Sample ++;
  windvelocity();
  RPM = (((counter / 2) * 60) / (period / 1000)) / MAGSnum;  // Calcolo rivoluzioni per minuto (RPM)
  WindSPEEDms = ((2 * pi * radio * RPM) / 60) / 1000;  // CAlcolo velocità vento m/s
  windgust();
  Serial.print("Wind : ");
  Serial.print(WindSPEEDms);
  Serial.print(" m/s");
  Serial.print(" - Gust : ");
  Serial.print(WindGUSTms);
  Serial.print(" m/s");
  Serial.println();
}

void windvelocity(){// Misura velocità vento
  WindSPEEDms = 0;
  counter = 0;  
  attachInterrupt(digitalPinToInterrupt(pinIN), addcount, CHANGE);
  unsigned long millis();                     
  long startTime = millis();
  while(millis() < startTime + period) {
  }
  detachInterrupt(digitalPinToInterrupt(pinIN));
}

void addcount(){
  counter++;
}

void windgust(){// Funzione per memorizzare la raffica di vento
  if (WindSPEEDms <= WindGUSTms){
  }
  else{
    WindGUSTms = WindSPEEDms;
    previousMillis = 0;
  }
  unsigned long currentMillis = millis(); //Ciclo per azerare la raffica dopo tempo impostato
  if (currentMillis - previousMillis >= GUSTinterval) {
    previousMillis = currentMillis;
    WindGUSTms = WindSPEEDms;
  }
}

Mi sembra funzioni bene.
Alberto