Optimierung TCRT5000 Reflexionslichtschranke für Stromzähler o.ä.

Begonnen von KarlHeinz2000, 08 Juni 2016, 21:58:01

Vorheriges Thema - Nächstes Thema

KarlHeinz2000

Ich hatte auch meine liebe Not die Scheibe des Energiezählers mit dem bekannten China-Modul abzutasten. Vor allem am Anfang/Ende der Markierung gab es immer mehrfache Pulse. Schlud ist die nicht ganz optimale Beschaltung des Komparators auf der Platine. Dieser arbeitet gänzlich ohne Hysterese und hat einen überflüssigen Kondensator am Eingang. Beides lässt die Schaltung super schwingen.
Enfernt man den Kondensator (im Kreis oben rechts) und bestückt einen Widerstand (180k) zwischen Pin 5 und 7, dann gehören die Probleme der Vergangenheit an es gibt genau eine Flanke am Beginn/Ende der Markierung.
Als Widerstand bietet sich ein 1206er an. Der passt direkt auf die Pins. Ich habe 180k bestückt, da gerade verfügbar. Andere Werte in der Größenordung gehen auch.

Marlen

Dann werde ich das auch mal versuchen, hab auch das Problem, dass es ständig hin und her springt (On/Off). In welchen Abstand hat du
die Lichtschranke zur Zählerscheibe? Wenn sie ganz nah dran ist geht garnichts!

KarlHeinz2000

Die Lichtschranke sitzt direkt auf der Scheibe vom Zähler.
Den Analogausgang zu verwenden macht eigentlich keinen Sinn, außer zum Einstellen der Schaltschwelle. Das macht aber auch der Komparator auf der Platine.
Also mit dem Poti die Schaltschwelle einstellen. Zur Kontrolle ist die grüne LED da. LED ist bei roter Markierung aus, sonst immer an.
Eingelesen in FHEM wird das mit einem 1-Wire-Zähler (AVR Nachbau) und Firmata. Läuft recht zuverlässig.
Zum Ausrichten des Reflexkopplers kann man gut ein Handy im Fotomodus nehmen. Damit sieht man das IR Licht.

Marlen

Zitat von: KarlHeinz2000 am 13 September 2016, 13:21:34
Eingelesen in FHEM wird das mit einem 1-Wire-Zähler (AVR Nachbau) und Firmata. Läuft recht zuverlässig.

1-Wire-Zähler .... Firmata....gibt es da einen Bauplan? Oder eine andere Doku?

KarlHeinz2000

Die Pulse vom Reflexkoppler werden mit einem DS2423 (bzw. alternativ mit https://forum.fhem.de/index.php/topic,10962.msg63018.html#msg63018)gezählt.
Der DS2324 hat ein 1-Wire Interface, um seinen Zählerstand auszulesen. Dass heißt, man braucht noch einen 1-Wire Busmaster, der an FHEM angebunden ist, um die Anzahl Pulse aus dem Zähler auszulesen. Bei mir ist der Busmaster ein AVR mit Firmata (http://www.fhemwiki.de/wiki/Arduino_mit_OneWireFirmata). Da gibt es aber auch noch alternative 1-WIre Busmaster, z.B. mit I2C oder UART.

Marlen

Ich hab gestern auf den Digitalausgang gesteckt, und die Schwelle eingestellt.
Den Digitalausgang vom TCR5000 hab ich direkt auf eine GPIO geschalten.
Funktioniert eigentlich, oder es ist nur Zufall?
Ich zähl das dann per notify in einen dummy hoch!
Wollte jetzt die Zeit ermitteln in der sich die Schiebe einmal dreht, um so die Watt zu errechnen die das Haus "zieht".
Hab aber keine Ahnung wie ich das mit Timestamp berechnen kann.

rippi46

Hallo,

ich benutze auch einen Reflexkoppler zum Auslesen der Stromzähler.
Allerdings habe ich das ganze auf Basis von Mysensor geregelt.

Die Auswertung der Pulse läuft ziemlich stabil.
D.h. die Anzeige in fhem stimmt mit der Anzeige auf dem Stromzähler überein.

hier den Sketch den ich verwende:

// Use this sensor to measure KWH and Watt of your house meeter
// You need to set the correct pulsefactor of your meeter (blinks per KWH).
// The sensor starts by fetching current KWH value from gateway.
// Reports both KWH and Watt back to gateway.
//
// Unfortunately millis() won't increment when the Arduino is in
// sleepmode. So we cannot make this sensor sleep if we also want
// to calculate/report watt-number.

#include <SPI.h>
#include <MySensor.h> 

#define SENSOR_ANALOG_PIN_ENERGY 0  // analog pin for the sensor -> for energy meter
#define SENSOR_ANALOG_PIN_GAS 1 // gas sensor at pin 1

#define PULSE_FACTOR 75         // Nummber of blinks per KWH of your meter
#define PULSE_FACTOR_GAS 100    // Nummber of blinks per m3 of your meter (One rotation/liter)

#define MAX_WATT 10000          // Max watt value to report. This filetrs outliers.
#define MAX_FLOW 40             // Max flow (l/min) value to report. This filters outliers.

#define CHILD_ID 1              // Id of the sensor child ENERGY
#define CHILD_ID_GAS 2          // Id of the sensor child GAS

#define TRIGGERLEVELEHEIGH 303  // highest level -> greater than this, red mark detected
#define TRIGGERLEVELLOW 285     // lowet level -> lower than this, red mark is gone

boolean triggerState = false;   // false = not detected / true = detected
boolean triggerStateGas = false;// false = not detected / true = detected

unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't wnat to spam the gateway.

MySensor gw;
double ppwh = ((double)PULSE_FACTOR)/1000;  // Pulses per watt hour
boolean pcReceived = false;     // energy value received from gateway
boolean pcGasReceived = false;  // gas value received from gateway

volatile unsigned long pulseCount = 0;
unsigned long oldPulseCount = 0;

double ppl = ((double)PULSE_FACTOR_GAS)/1000;       // Pulses per liter
volatile unsigned long pulseCountGas = 0;
unsigned long oldPulseCountGas = 0;
unsigned long lastPulseGas = 0;
unsigned long lastSendGas = 0;
volatile double flow = 0;
double oldflow = 0;
double volume = 0;                     
double oldvolume = 0;

volatile unsigned long lastBlinkEnergy = 0; // energy last detection of red mark
volatile unsigned long lastBlinkGas = 0;    // gas last detection of red mark

volatile unsigned long watt = 0;
 
unsigned long oldWatt = 0;
double oldKwh;
unsigned long lastSend;

#define DEBOUNCERARRAYENERGY 15
int debounceArray[DEBOUNCERARRAYENERGY];
byte debounceArrayIndex = 0;

#define DEBOUNCERARRAYGAS 20
int debounceGasArray[DEBOUNCERARRAYGAS];
byte debounceGasArrayIndex = 0;

MyMessage wattMsg(CHILD_ID,V_WATT);
MyMessage kwhMsg(CHILD_ID,V_KWH);
MyMessage pcMsg(CHILD_ID,V_VAR1);

MyMessage flowMsg(CHILD_ID_GAS,V_FLOW);
MyMessage volumeMsg(CHILD_ID_GAS,V_VOLUME);
MyMessage gasValueMsg(CHILD_ID_GAS,V_VAR1);

void setup() 
{
  // init debouncer array ENERGY
  for(int i = 0; i < DEBOUNCERARRAYENERGY; i++) {
    debounceArray[i] = 999;
  }
 
  // init debouncer array GAS
  for(int i = 0; i < DEBOUNCERARRAYGAS; i++) {
    debounceGasArray[i] = 999;
  }
 
  gw.begin(incomingMessage);

  // Send the sketch version information to the gateway and Controller
  gw.sendSketchInfo("Energy and Gas Meter", "1.0");

  // Register power sensor
  gw.present(CHILD_ID, S_POWER);

  // register gas meter
  gw.present(CHILD_ID_GAS, S_POWER);

  pulseCountGas = oldPulseCountGas = 0;

  // Fetch last known pulse count value from gw
  gw.request(CHILD_ID, V_VAR1);
  gw.request(CHILD_ID_GAS, V_VAR1);
 
  lastSend = millis();
 
  lastSendGas = lastPulseGas = millis();
}


void loop()     
{
  gw.process();
  unsigned long now = millis();
  // Only send values at a maximum frequency
  bool sendTime = now - lastSend > SEND_FREQUENCY;
  if (pcReceived && sendTime) {
    // New watt value has been calculated 
    if (watt != oldWatt) {
      // Check that we dont get unresonable large watt value.
      // could hapen when long wraps or false interrupt triggered
      if (watt<((unsigned long)MAX_WATT)) {
        gw.send(wattMsg.set(watt));  // Send watt value to gw
      } 
      Serial.print("Watt:");
      Serial.println(watt);
      oldWatt = watt;
    }
 
    // Pulse cout has changed
    if (pulseCount != oldPulseCount) {
      gw.send(pcMsg.set(pulseCount));  // Send pulse count value to gw
      double kwh = ((double)pulseCount/((double)PULSE_FACTOR));     
      oldPulseCount = pulseCount;
      if (kwh != oldKwh) {
        gw.send(kwhMsg.set(kwh, 4));  // Send kwh value to gw
        oldKwh = kwh;
      }
    }   
    lastSend = now;
  } else if (sendTime && !pcReceived) {
    // No count received. Try requesting it again
    gw.request(CHILD_ID, V_VAR1);

    lastSend=now;
  }

  if (pcGasReceived && sendTime) {
    if (flow != oldflow) {
      oldflow = flow;

      Serial.print("l/min:");
      Serial.println(flow);

      // Check that we dont get unresonable large flow value.
      // could hapen when long wraps or false interrupt triggered
      if (flow<((unsigned long)MAX_FLOW)) {
        gw.send(flowMsg.set(flow, 2));                   // Send flow value to gw
      } 
    }
   
    // No Pulse count received in 2min
    if(now - lastPulseGas > 120000) {
      flow = 0;
    }
   
    // Pulse count has changed
    if (pulseCountGas != oldPulseCountGas) {
      oldPulseCountGas = pulseCountGas;

      Serial.print("pulsecount:");
      Serial.println(pulseCountGas);

      gw.send(gasValueMsg.set(pulseCountGas));                  // Send  pulsecount value to gw in VAR1

      double volume = ((double)pulseCountGas/((double)PULSE_FACTOR_GAS));     
      if (volume != oldvolume) {
        oldvolume = volume;

        Serial.print("volume:");
        Serial.println(volume, 3);
       
        gw.send(volumeMsg.set(volume, 3));               // Send volume value to gw
      }
    }
   
    lastSendGas = now;
  } else if(sendTime && !pcGasReceived) {
  // No count received. Try requesting it again
    gw.request(CHILD_ID_GAS, V_VAR1);
  }
 
  // detect red mark and increment count
  CheckEnergyAnalogValueToDetect();

  // detect magnet at gas meter
  CheckGasAnalogValueToDetect();
}

void incomingMessage(const MyMessage &message) {
  if (message.type == V_VAR1 && message.sensor == CHILD_ID) { // energy meter

    pulseCount = oldPulseCount = message.getLong();
    Serial.print("Received last energy pulse count from gw: ");
    Serial.println(pulseCount);
    pcReceived = true;

  } else if (message.type == V_VAR1 && message.sensor == CHILD_ID_GAS) {  // gas meter

    pulseCountGas += message.getLong();
    Serial.print("Received last gas pulse count from gw: ");
    Serial.println(pulseCountGas);
    pcGasReceived = true;
  }
}

/**
* Detect the red mark at the energy meter
*/
void CheckEnergyAnalogValueToDetect() {

  unsigned long newBlink = micros(); 
  unsigned long interval = newBlink-lastBlinkEnergy;

  boolean nextState = triggerState;
  int val = analogRead(SENSOR_ANALOG_PIN_ENERGY);
  int debounceSum = 0;

  debounceArray[debounceArrayIndex] = val;
  for(int i = 0; i < DEBOUNCERARRAYENERGY; i++) {
    debounceSum += debounceArray[i];
  }

  if(debounceArrayIndex == DEBOUNCERARRAYENERGY-1)
    debounceArrayIndex = 0;
  else
    debounceArrayIndex++;
 
  // average of DEBOUNCERARRAYENERGY
  val = debounceSum / DEBOUNCERARRAYENERGY;

  if (val > TRIGGERLEVELEHEIGH) {
    nextState = true;
  } else if (val < TRIGGERLEVELLOW) {
    nextState = false;
  }
  if (nextState != triggerState) {
    triggerState = nextState;
    Serial.println(triggerState? 1 : 0);

    if(triggerState)
    {
      if (interval > 0) {
        watt = (3600000000.0 /interval) / ppwh;
      }
     
      lastBlinkEnergy = newBlink;

      pulseCount++;
           
      Serial.print("Pulse detected: ");
      Serial.println(pulseCount);
    }
  }
}

/**
* Detect the magnet at the gas meter
*/
void CheckGasAnalogValueToDetect() {

  unsigned long newBlink = micros(); 
  unsigned long interval = newBlink-lastBlinkGas;

  boolean nextState = triggerStateGas;

  int value = analogRead(SENSOR_ANALOG_PIN_GAS);
  Serial.print("GasSensor value: ");
  Serial.println(value);

  int debounceSum = 0;

  debounceGasArray[debounceGasArrayIndex] = value;
  for(int i = 0; i < DEBOUNCERARRAYGAS; i++) {
    debounceSum += debounceGasArray[i];
  }

  if(debounceGasArrayIndex == DEBOUNCERARRAYGAS-1)
    debounceGasArrayIndex = 0;
  else
    debounceGasArrayIndex++;

  // average of DEBOUNCERARRAYGAS
  value = debounceSum / DEBOUNCERARRAYGAS;

  // 145 old value
  if (value > 200) {
    nextState = false;
  } else if (value == 0) {
    nextState = true;
  }
   
  if(nextState != triggerStateGas) {
    triggerStateGas = nextState;

    if(triggerStateGas) {
      if (interval!=0) {
        lastPulseGas = millis();
        if (interval<1000000L) {
          // Sometimes we get interrupt on RISING,  1000000 = 1sek debounce ( max 60 l/min)
          return;   
        }
        flow = (60000000.0 / interval) / ppl;
      }
      lastBlinkGas = newBlink;
     
      pulseCountGas++;

      Serial.print("Pulse gas detected: ");
      Serial.println(pulseCountGas);
    }
  }
}


und hier ein typischer Plot in fhem:

Gruß rippi


FHEM, LMS, VDR ,Dell 9010 Ubuntu 20.04,Raspimatic, HM/HMIP, Max, Elro, Brennenstuhl u. Intertechno mit Connair.
Picoreplayer, Raspi IR-Lanadapter, Firmata(wifi), LaCrosse,
nanocul433, nanocul868, Signalduino, Connexoon,
MySensor-GW+Sensoren, RGBWW, Zigbee2mqtt,Xiaomi,Nextion,LEDMatrix,Alexa

Marlen

Wow....ziemlich komplexer Code!!  ::)
Da steig ich nicht durch!
Habs gestern auch geschaft mit den Momentanen verbrauch berechnen zu lassen! Einfach mit time.

Aber was anderes? Brauchst du echt 15kWh am Tag?

rippi46

Hallo,

Code stammt aber nicht von mir. Habe ich im Mysensorforum gefunden.

Wasch-, Trocken-, Brotbacktag, dann kann das schon mal vorkommen!

Gruß rippi
FHEM, LMS, VDR ,Dell 9010 Ubuntu 20.04,Raspimatic, HM/HMIP, Max, Elro, Brennenstuhl u. Intertechno mit Connair.
Picoreplayer, Raspi IR-Lanadapter, Firmata(wifi), LaCrosse,
nanocul433, nanocul868, Signalduino, Connexoon,
MySensor-GW+Sensoren, RGBWW, Zigbee2mqtt,Xiaomi,Nextion,LEDMatrix,Alexa

KarlHeinz2000

Hier mal ein Bild von der aktuellen Montage des Sensors. Falls es jemanden interessiert  ;)

lufi

Hallo Karl Heinz,

ich habe die Probleme mit dem Prellen dank deiner Tipps beseitigt, jetzt muss ich den Sensor aber alle 1-2 Tage nachjustieren.
Sonst hört er einfach auf zu zählen.

Hast du dazu eine Idee ?

Danke und Gruß

Lutz

KarlHeinz2000

Was musst du denn justieren? Die Empfindlichkeit auf der Platine, oder die Position über der Scheibe?
Zur Positionierung kann man auch wunderbar das Handy nehmen. Mit der Kamera sieht man das IR Licht.

Allodo

Ich würde meinen TCRT5000 auch gerne entprellen.

Da ich jedoch nicht so firm bin bzgl. Widerständen, kann mir vielleicht jemand genau sagen, wonach ich suchen muss?
180K ist klar, aber wieviel mW/W usw.

Möchte da nix falsches anlöten ;)

KarlHeinz2000

Leistung, Toleranz, .... spielt bei dem Widerstand keine Rolle. Irgendwas um 180k sollte es sein. SMD Bauform 0603 oder 0805, müsste sich beides bestücken lassen. Ein bedrahteter R geht natürlich auch mit etwas Biege-Akrobatik.

lufi

Hallo Karl Heinz,

was ich immer mal wieder nachjustieren muss, ist die Empfindlichkeit.
Der Sensor hört einfach nach 2-3 tagen auf zu zählen.

Der Widerstand ist bei mir 220k weil ich keine 180k hatte.

Danke und Gruß

Lutz