pulsenteller dmv interrupts

Hoi allen,

Gebeten door het Arduino virus naar aanleiding van deze webpagina:
http://www.fun-tech.se/FunTechHouse/ElectricityMeter/index.php

Ben ik druk bezig om ongeveer hetzelfde te bouwen met mijn Arduino Uno rev3
een weerstand van 10K tussen GND en pin2
+5V via de opto coupel van de kWh meter verbonden met pin 2
Alleen registreert de Arduino soms meerdere pulsen van de kWh teller terwijl er in werkelijkheid maar ééntje geweest is.

Ik heb al geprobeerd om een delay in de interrupt functie te zetten maar dat is helaas niet de oplossing. Blijkbaar worden alle pulsen gebufferd en als er 5 pulsen tegelijk komen gebeurt die vertraging ook 5x na mekaar.

Bestaat hier een oplossing voor ?

Alvast bedankt,
Bart

Ik heb al een tijdje iets soortgelijks, ik lees echter het knipperledje uit. blijkbaar heb jij een echt schakelcontact.

Google sites webpage https://sites.google.com/site/yaeuls/
De energiemeter(kWh) via de knipperled die daar opzit (1pulse/Wh) uitlezen en via ethernet/internet versturen naar een 'internet of things' logging server Cosm.

Laat ff je code zien, kijken we even mee.

Ik heb de code niet zo direkt bij de hand maar ze is zowat één op één dezelfde als in de link hierboven, alleen dan voor één meter ipv 2.

void onPulse1()
{
    //pulseCounter
    pulseCount1_Wh++;
    if(pulseCount1_Wh == 1000)
    {
        pulseCount1_Wh = 0;
        pulseCount1_kWh++;
    }
}

maar dit stukje van uw link is zeer interessant:

/-------------------------------------------------------------------------------------
//- Interupt routine - count number of pulses, initialized in setup()
//-                                                                                   -
//-------------------------------------------------------------------------------------
void Int_LedPulse()
{ if ( digitalRead(LEDPIN) == LOW )            // to flush false triggers
  { LedPulse.EventMillis = millis();           // capture timer
    LedPulse.Event=true;                       // set flag to signal PulseSensor_manager()
    LedPulse.Watthour++;
    if (LedPulse.Cnt++ == 0)
      LedPulse.Overflow = true;
  }
}//Int_LedPulse

// to flush false triggers XD

... eigelijk is dat hele programma wel interessant, bedankt hiervoor !
Ook het ethernet gedeelte, voorlopig zit ik met een laptop die op de kast ligt te prutsen, maar je moet ergens beginnen, toch ?

Ik hou jullie op de hoogte :wink:

Wat je moet doen is de tijd vd puls meten en onthouden en vergelijken met de vorige tijdstempel
Dit is zo ongeveer de simpelste vorm

volatile unsigned long prevTime = 0;

ISR()
{
  if (millis() - prevTime < 10) // minder dan 10 millisec geleden?
  {
    return;  // ignore irq
  }
  // langer dan 10 millis.. dat is een echte 
  prevTime = millis();
  kwhCount++;
}

Om een of andere reden kreeg ik tussendoor en snachts af en toe nog een 'puls' ingelezen terwijl dit onmogelijk kon. Na wat ge-experimenteer lijkt deze code redelijk waterdicht te zijn als ik mijn pulsenteller vergelijk met de kWh meter zelf.
Mocht iemand hier nog wat aan hebben :

void onPulse1()
{
 
   if (millis() - prevTime < 10000) // minder dan 10 sec geleden?
  {
    return;  // dit kan niet goed zijn  ...
  }
 
  if ( digitalRead(PULSEPIN) == HIGH ) {           // kijken hoelang de pin hoog blijft ...
    delay(10);   //30 mili sec  
    if ( digitalRead(PULSEPIN) == HIGH ) {         // na 30 ms nog steeds hoog ?
       pulseCount1_10Wh++;                              // is een echte puls 
        prevTime = millis();                                  // start 10 sec timer
        Serial.println("PULS");
    }
  }
  return;
}

Thanks, werkende code is altijd nuttig

delay(10);   //30 mili sec

conflict between comment and code ....

nog een 'puls' ingelezen terwijl dit onmogelijk kon

Hoe weet je dat zo zeker?

Ik heb de commentaar achteraf toegevoegd blijkbaar is daar dan wat mis mee :*

Dat ik valse pulsen kreeg weet ik omdat de zonnepanelen in het midden van de nacht electriciteit aant produceren waren :slight_smile:

omdat de zonnepanelen in het midden van de nacht electriciteit aant produceren waren

Die zou ik eens heel vlug patenteren als ik jou was :wink:

Maar inderdaad een onwaarschijnlijke puls! (ik moet wel denken aan die meteoriet in Rusland pas, gaf ook veel licht)

De pulsenteller werkt ondertussen vlekkeloos en nu hebben we er een Ethernet shield op gezet zodat het opzet zelfstandig kan werken.
We hebben er ook enkele temp sensoren bij gehangen om de status van de zonneboiler te meten en dan gelijk ook een warmluchtcollector 'sturing' bijgebouwd.
Dit alles werkt netjes :slight_smile: ... voor een paar uur en dan stopt de arduino ermee :~
Wel lastig om de oorzaak te vinden aangezien je een dag of een halve dag moet wachten bij elke verandering om te kijken of het nog mis loopt maar dat zie ik als onderdeel van de hobby ]:smiley:

Mocht iemand interesse hebben hier is de code die ik verzonnen heb als daar grote hiaten in zitten hoor ik dat graag natuurlijk:

#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <Ethernet.h>

// PIN definition
#define PULSEPIN 2      // kWh meter puls pin
#define ONE_WIRE_BUS 3  // temp data wire
#define FAN_RELAY 4     // fan for solar collector
#define ERROR_LED 5     // Error led


#define TEMP_VERSCHIL 10  // delta-T to activate keuken fa


// ethernet
byte mac[] = {  
  0x00, 0xAA, 0xBB, 0xCC, 0xDE, 0x02 };
char serverName[] = "emon.site";
EthernetClient client;
// temperature stuff
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
DeviceAddress BruineThermometerKeuken = { 
  0x28, 0xE6, 0xAC, 0x00, 0x04, 0x00, 0x00, 0x09 };//bruin
DeviceAddress BlauweThermometerKeuken = { 
  0x28, 0x2D, 0xC5, 0x00, 0x04, 0x00, 0x00, 0x8C}; //blauw
//  DeviceAddress BruineThermometer = { 0x28, 0x36, 0xD7, 0x00, 0x04, 0x00, 0x00, 0xD0 };//bruin
DeviceAddress BruineThermometer = { 
  0x28, 0xED, 0xD4, 0x00, 0x04, 0x00, 0x00, 0xA2 };//bruin
DeviceAddress BlauweThermometer = { 
  0x28, 0xFF, 0xDA, 0x00, 0x04, 0x00, 0x00, 0x35 }; //blauw

//interupt pulse counter
volatile unsigned int pulseCount1_10Wh  = 0;   
volatile unsigned long prevTime = 0;
int busy = 0;

// global variabeles
char urlStr[128];

/******************************************
 *  Pulse interupt handler
 ******************************************/
void onPulse1()
{
    Serial.println("interupt");
  if (millis() - prevTime < 5000){ // less than 5 sec ago?
    return;  // ignore irq
  }
  if ( digitalRead(PULSEPIN) == HIGH ) {           // to flush false triggers
    delay(10);   //30 mili sec  
    if ( digitalRead(PULSEPIN) == HIGH ) {         // still low
      pulseCount1_10Wh++;            // it must be a pulse
      prevTime = millis();
      Serial.println("PULS");
    }
  }
  return;
}
/******************************************
 *  SETUP
 ******************************************/
void setup(void)
{
  // start serial port
  Serial.begin(9600);  //start serial port
  Serial.println("Hello");
 
  sensors.begin();     // Start up the library
  sensors.setResolution(BruineThermometerKeuken, 9); // set the resolution to 10 bit (good enough?)
  sensors.setResolution(BlauweThermometerKeuken, 9);
  sensors.setResolution(BruineThermometer, 9); // set the resolution to 10 bit (good enough?)
  sensors.setResolution(BlauweThermometer, 9);  
  //sensors.requestTemperatures();
  delay(3000);
  pinMode(FAN_RELAY, OUTPUT); 
  pinMode(ERROR_LED, OUTPUT); 
  initEth();
  delay(3000);
   attachInterrupt(0,onPulse1,RISING);

}
/******************************************
 *  Initialise Ethernet
 ******************************************/
void initEth()
{
  Serial.println("Init Ethernet");
  // start the Ethernet connection:
  if (Ethernet.begin(mac) == 0) {
    Serial.println("Failed to configure Ethernet using DHCP");
    // no point in carrying on, so do nothing forevermore:
    while(true);
  }
  // give the Ethernet shield a second to initialize:
  delay(3000);
  Serial.println("connecting...");
}

/******************************************
 *  get temperature from sensor
 ******************************************/
float getTemperature(DeviceAddress deviceAddress)
{
  sensors.requestTemperatures();
  float tempC = sensors.getTempC(deviceAddress);
  if (tempC == -127.00) {
    Serial.println("Failed to read temperature");
    return (0);
  }
    Serial.print("Temp is:");
    Serial.println(tempC); 

  return (tempC);
}
/******************************************
 *  Switch keuken ventilator
 ******************************************/
void fanControl()
{
  float deltaT = getTemperature(BruineThermometerKeuken) - getTemperature(BlauweThermometerKeuken);
  if (deltaT > TEMP_VERSCHIL){
    digitalWrite(FAN_RELAY, HIGH);
  }
  else {
    digitalWrite(FAN_RELAY, LOW);
  }
}
/******************************************
 *  Format URL to update emon site
 ******************************************/
void sendData2()
{
  if (client.connect(serverName, 80)) {
    Serial.println("connected");
    // Make a HTTP request:
    //client.println("GET http://emoncms.org/input/post.json?json={power:**************** HTTP/1.0");
    client.print("GET http://emoncms.org/input/post.json?node=2&csv=");
    client.print(pulseCount1_10Wh*10*12); // avge Watt 5min
    client.print(",");
    client.print(getTemperature(BruineThermometer));
    client.print(",");
    client.print(getTemperature(BlauweThermometer)); 
    client.print(",");
    client.print(getTemperature(BruineThermometerKeuken));
    client.print(",");
    client.print(getTemperature(BlauweThermometerKeuken)); 
    if ( digitalRead(FAN_RELAY) == HIGH ) {         // fan on
      client.print(",10");
    }
    else {
      client.print(",0");
    }
    client.println("&apikey=***************** HTTP/1.0");


    client.println();

    Serial.println("read from server."); 
    while (client.available()) { // print response ...
      char c = client.read();
      Serial.print(c);
    }

    Serial.println("done sending data, disconnect from emon.");
    client.stop();  //disconnect
    pulseCount1_10Wh = 0;  //reset counter
    digitalWrite(ERROR_LED, LOW); // disable alarm

  } 
  else {
    digitalWrite(ERROR_LED, HIGH);
    // kf you didn't get a connection to the server:
    Serial.println("connection failed");
  }

}

/******************************************
 *  Format URL to update emon site (1st attempt)
 ******************************************/
void sendData()
{

  if (client.connect(serverName, 80)) {
    Serial.println("start sending data to emon server :-) ");
    // Make a HTTP request:
    client.print("GET http://emoncms.org/input/post.json?node=2&cvs=0,");
    //client.print("GET http://emoncms.org/input/post.json?json={power:220}");
    client.print(pulseCount1_10Wh*10*12); // avge Watt 5min
    client.print(",");
    client.print(getTemperature(BruineThermometer));
    client.print(",");
    client.print(getTemperature(BlauweThermometer)); 
    client.print(",");
    client.print(getTemperature(BruineThermometerKeuken));
    client.print(",");
    client.print(getTemperature(BlauweThermometerKeuken)); 
    if ( digitalRead(FAN_RELAY) == HIGH ) {         // fan on
      client.print(",10");
    }
    else {
      client.print(",0");
    }
    client.println("&apikey=****************** HTTP/1.0");
    client.println();



    Serial.print("GET http://emoncms.org/input/post.json?node=2&csv=0,");
    //Serial.print("GET http://emoncms.org/input/post.json?json={power:220}");
    Serial.print(pulseCount1_10Wh*10*12); // avge Watt 5min
    Serial.print(",");
    Serial.print(getTemperature(BruineThermometer));
    Serial.print(",");
    Serial.print(getTemperature(BlauweThermometer)); 
    Serial.print(",");
    Serial.print(getTemperature(BruineThermometerKeuken));
    Serial.print(",");
    Serial.print(getTemperature(BlauweThermometerKeuken)); 
    if ( digitalRead(FAN_RELAY) == HIGH ) {         // fan on
      Serial.print(",10");
    }
    else {
      Serial.print(",0");
    }
    Serial.println("&apikey=**************** HTTP/1.0");


    Serial.println("read from server."); 
    while (client.available()) { // print response ...
      char c = client.read();
      Serial.print(c);
    }

    Serial.println("done sending data, disconnect from emon.");
    client.stop();  //disconnect
    pulseCount1_10Wh = 0;  //reset counter

  } 
  else {
    // if you didn't get a connection to the server:
    Serial.println("cannot connect to emon server :-(");
  }

}

/******************************************
 *  Main loop
 ******************************************/
void loop(void)
{ 
  //delay(2000);

  // Serial.print(",");
  //  Serial.println(getTemperature(BruineThermometer));
  //  Serial.println(",");
  //  Serial.println(getTemperature(BlauweThermometer)); 

  //  
  for (int i = 0; i < 5; i++) { // loop 5 times/minutes

    fanControl(); // check fan evry minute
    Serial.println("check fan");
    delay(60000);   //60 000 =1min  
  }
  Serial.println("send url");
  sendData2();  // evry 5 mins update site


}

Bart
Voor dit probleem is de watchog uitgevonden als "workaround".
Met vriendelijke groet.
Jantje

Bedankt voor de tip, hij blijft nu al enkele dagen draaien zonder vast te lopen ! :smiley:

leuk te horen dat het helpt

Ja, een topic van bijna drie jaar oud omhoog trappen, maar met reden:
Ik ben bezig een S0-naar-USB interface te maken met een Arduino-Uno en kwam bij het googlen regelmatig dit topic tegen, dus heb er ook wat inspiratie mee opgedaan - maar (volgens mij) ook een fout in de code gevonden. En daarom dus deze kick: als eerste even bevestiging vragen of ik het goed heb ingezien - en zo ja, dat mensen die hier na mij langskomen het ook meekrijgen.

Wat is er volgens mij fout: het stukje code om stoorpulsen te filteren, specifiek dit stukje:

if ( digitalRead(PULSEPIN) == HIGH ) {           // kijken hoelang de pin hoog blijft ...
    delay(10);   //30 mili sec 
    if ( digitalRead(PULSEPIN) == HIGH ) {         // na 30 ms nog steeds hoog ?
       pulseCount1_10Wh++;                              // is een echte puls
    ...

... hier wordt een stukje code in een interrupt uitgevoerd op een 0-1 overgang van een pin. In dit stukje wordt als eerste gecheckt of de pin '1' is, 10ms gewacht en nogmaals gekeken of de pin '1' is. Waarschijnlijk om te ont-denderen.

Echter, uit de Arduino-documentatie:

Note

Inside the attached function, delay() won't work and the value returned by millis() will not increment.

... dus volgens mij doet die 10ms delay niet wat er verwacht werd en kan die net zo goed weggehaald worden. Je krijgt dan enkel twee keer een '1' check in een hele korte tijd, maar geen 10ms ontdender-tijd.

Ofwel, vraag: klopt het wat ik hier als fout denk te zien?

Wat ik momenteel zelf als code gebruik (en het moet nog opgeschoont worden!):

volatile unsigned long count = 0;
volatile unsigned long kwhcount = 0;
volatile unsigned long risetime = 0;
unsigned long time_diff = 0;
unsigned long count_copy = 0;
int incomingByte = 0;
byte outbuf[8];

void My_Int() {
  if (digitalRead(2) == LOW) {
    risetime = millis();
  } else {
    time_diff = millis() - risetime;
    if ((time_diff>5) && (time_diff < 200)) {
      kwhcount++;
      count = time_diff;
    }
  }
  return;
}

void setup() {
    pinMode(13, OUTPUT);
    pinMode(2, INPUT_PULLUP);
    attachInterrupt(0, My_Int, CHANGE);
    Serial.begin(9600);               // opens serial port, sets data rate to 9600 bps
}

Ofwel, bij iedere verandering van de int-pin kijk ik naar de status van de pin; is 'ie laag dan onthoud ik de tijd (in 'risetime' - ja, ik moet nog opschonen). Is de pin hoog, dan kijk ik of de tijd vanaf het laag worden in een bepaalde range ligt. De min & max tijd (hier 5 en 200ms) zal per type kwh-meter anders zijn en moet ik zelf ook nog wat beter vastzetten.

delay(10); //30 mili sec

in case of conflict between code and comment, the code wins

Heb je deze gelezen - Arduino Playground - EEM12L-32AKWhMonitoring -

Dat van die code / comment ongelijkheid had ik wel gezien, het commentaar ook bewust genegeerd. Waar het mij meer om ging: die 'delay(10)' uit de code van 'postbus24' hierboven, die is toch behoorlijk zinloos in een interrupt?

Dus ALS je je S0-counter met filtering van stoorpulsen wil maken, dan gaat die code toch vrij zinloos zijn?

Daarom mijn stukje code erbij: enig filteren lijkt mij zinnig en dan is, denk ik, mijn code iets dat WEL zou moeten werken.

Jouw voorbeeld was ik al wel tegengekomen, maar heb ik weinig mee gedaan: ik was toen al tegen dit topic aangelopen en vond het idee van stoorpuls-filteren wel goed - iets wat in jouw code niet zit. (en wellicht is het in de praktijk ook compleet niet nodig, maar het zou ook geen kwaad moeten doen).

Plus, mijn main-loop moet iets compleet anders doen: ik wil enkel pulsen tellen en op verzoek van buitenaf enkel de pulstellerstand doorgeven - USB communicatie heeft mij tot nu toe het meeste tijd gekost om werkend te krijgen. :frowning:

Een delay is vrijwel altijd (net niet helemaal) zinloos.
Maar een delay in een interrupt is waanzin onzin.
Ik heb de code helemaal niet bekeken en heb dus geen idee of er zo'n stukje in een interrupt staat.
Maar als je een delay in een interrupt wil stoppen, dan snap je niet helemaal waar je mee bezig bent.

Een honderdste van een seconde (10 ms) is heel veel tijd.
Het zijn 160.000 handelingen van je controller die je overslaat, terwijl je in je interrupt zit...
Ik weet niet eens wat daarvan te zeggen.

Dan kun je beter kijken hoe veel tijd er zit tussen twee interrupts, en als die tijd groter is dan een bepaald minimum, dan pas die laatste interrupt als een geldige puls verwerken en dus puls++ doen.
Ik vermoed dat dat zo ongeveer is wat je wilde doen.
Een interrupt kan naar verschillende zaken kijken.
Vaak word er dan gekozen naar een overgang van laag naar hoog of vice versa.
Dat is net weer ff iets anders als kijken of iets hoog is.
Of je dit per se met interrupts moet doen is maar de vraag.
Er zijn vast meer oplossingen te vinden om de pin vaker in een iteratie binnen te halen en op die manier betrouwbaar en toch snel genoeg pulsen te registreren.

Zoals je wellicht merkt aan dit antwoord heb ik helemaal niet naar enige code in deze thread gekeken, want dan moet ik eerst alle code gaan lezen en ontleden voor ik er commentaar op wil gaan geven.
Daar heb ik nu ff geen trek in.

Nou, nuanceren: als je main loop vrijwel niets doet, dan kan een delay in een interruptfunctie niet heel veel kwaad (hij 'steelt' tijd van de main loop, maar als die die tijd toch niet nodig heeft...)

Maar m'n punt is dus: volgende de Arduino-documentatie WERKT zo'n delay niet eens in een interrupt! Dus nog afgezien van het nut on nutteloosheid van de oplossing, de code die postbus24 hier heeft geplaatst functioneert dus niet eens zoals bedoeld.

En DAT was de belangrijkste reden om dit topic na bijna drie jaar bij te werken: code die door anderen mogelijk als voorbeeld gebruikt wordt bekritiseren.

Dan denk ik dat de oplossing zoals ik die hier plaatste wellicht een stuk beter is.

Volgens mij zitten iedereen vrijwel op dezelfde lijn, enkel anders verwoord :slight_smile: