Avläsning av S0-utgången på elmätare (ESP8266)

Här kan man starta en egen tråd för sitt stora eller lilla projekt, för att få inspiration, beröm, hjälp om man kör fast etc.
Användarvisningsbild
admin
Webbplatsadministratör
Inlägg: 8
Blev medlem: tor 18 maj 2017, 16:42

Avläsning av S0-utgången på elmätare (ESP8266)

#1

Inläggav admin » tor 18 maj 2017, 20:31

Jag har skrivit om detta projekt i andra forum, som projekttråd med projektet under gradvis uppbyggnad. Jag lägger in det här för att ge lite inspiration för andra att komma igång med egna projekttrådar.

Projektet syftade till att kunna läsa av S0-pulsen på elmätaren i källaren, räkna pulser och räkna ut momentan effektförbrukning. Data beräknas av en ESP8266 (Wemos D1 mini) och kickas via Wifi till min linuxserver och därifrån kan data komma vidare till app i iPhone och Apple Watch där jag kan se effektförbrukningen i realtid och även se grafer över elanvändningen.

Här kan man läsa lite om hur man mäter el och om hur olika elmätare ger pulser som man kan räkna.
https://learn.openenergymonitor.org/ele ... e-counting

Principskiss:
file.png
file.png (72.23 KiB) Visad 2357 gånger


Avläsningen sker via fototransistor motsvarande enligt följande sida där det också finns bra översiktlig information om S0:

http://www.techtrade.se/sv/emc-schematics.asp

Senaste koden finns här (kan ha viss fulkod, varnas i förväg...) men har fungerat bra i flera månader.

Kod: Markera allt

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <EEPROM.h>

const char* version = "S0_Counter_mqtt 1.4";
const char* date_name = "2016-12-13 hanpa";

// Code for detection of S0 power meter signal and delivering data using mqtt
// Meter value and current effect in watts based on last detected pulses
// Measured S0 pulse width with max/min can be used for checking quality of pulse detection
// Code developed for ESP8266 using either Wemos D1 mini or NodeMCU

// Example use from server side for obtaining data from mqtt broker with data streams receceived:
// mosquitto_sub -h localhost -t "s0" -v
//   s0 42373.226
//   s0 42373.227
//   s0 42373.228
// mosquitto_sub -h localhost -t "watts" -v
//   watts 669
//   watts 665
//   watts 667
// mosquitto_sub -h localhost -t "s0width" -v
//   S0 width 65ms (min 64, max 89)
//   S0 width 64ms (min 64, max 89)
//   S0 width 64ms (min 64, max 89)

// Meter data can be set by the following command (the fraction part is set to 0)
// mosquitto_pub -h localhost -t "s0SetMeter" -m "42373"

// Wifi settings
const char* ssid = "SSID";
const char* password = "password";

// The follwing parameters are only required for fixed IP, otherwise uncomment, also uncomment WiFi.config(ip, gateway, subnet)
IPAddress ip(192, 168, 0, 170);
IPAddress gateway(192, 168, 0, 1);
IPAddress subnet(255, 255, 255, 0);

// MQTT server and topic settings
const char* mqtt_server = "192.168.0.xxx";
const char* outTopic_pulse = "s0";
const char* outTopic_effect = "watts";
const char* outTopic_S0_pulse_width = "s0width";
const char* inTopic = "s0SetMeter";

// S0 settings
const int IMPULSES_PER_KWH = 1000;

// Values used to filter out invalid pulses
const int MINIMUM_S0_PULSE_WIDTH = 25;  // E.g. 50ms for a 100ms pulse
const int MAXIMUM_S0_PULSE_WIDTH = 125; // E.g. 150ms for a 100ms pulse
const int MAX_VALID_EFFECT = 40000;     // Max valid value in effect calculation (W)

// Constant for calculation of current effect in W, based on 1 pulse per second
const int WS_PER_1HZ_IMPULSE = 1000*3600/IMPULSES_PER_KWH;

const unsigned long MAXULONG = 494967295;

const unsigned long MAX_S0_PULSE_INTERVAL = MAXULONG / 2; // Arbitrary large value

// GPIO settings
int ledPin = 2;

// NOTE: Interrupt handling and pinMode setting below assumes pullup to HIGH, LOW input when pulse arrives
int inputPin = D1;

volatile unsigned long s0_pulse_start = 0;  //ms
volatile unsigned long time_for_last_s0_pulse = MAX_S0_PULSE_INTERVAL; //ms

volatile unsigned long s0_counter = 0; //ms
volatile unsigned long watts = 0;
volatile unsigned long meter = 0;

// copy of variables for publishing
volatile int ps0_pulse_width = 0;
volatile int ps0_pulse_width_min = 1000;
volatile int ps0_pulse_width_max = 0;
volatile unsigned long ps0_counter = 0; //ms
volatile unsigned long pwatts = 0;
volatile unsigned long pmeter = 0;

volatile boolean new_valid_pulse_detected = false;
volatile boolean new_valid_effect_calculated = false;
volatile boolean active_pulse = false;

WiFiClient espClient;
PubSubClient client(espClient);
 
void setup() {
 
  Serial.begin(115200);
  delay(10);
  Serial.println("setup started ");
 
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, HIGH);

  read_from_EEPROM();

  pinMode(inputPin,INPUT_PULLUP);
 
  // Connect to WiFi network

  WiFi.config(ip, gateway, subnet); // Only required for fixed IP
  WiFi.begin(ssid, password);
 
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  client.setServer(mqtt_server, 1883);
  client.setCallback(callback);

  attachInterrupt(inputPin, lowInterrupt, FALLING);
}

void read_from_EEPROM() {
  // Read saved meter value from EEPROM
  EEPROM.begin(512);
  EEPROM.get(0, meter);
  EEPROM.end();
  Serial.print("meter vale ");
  Serial.print(meter);
  Serial.println(" read from EEPROM");
}

void save_to_EEPROM() {
  // Save meter value to EEPROM
  Serial.print("meter value ");
  Serial.print(meter);
  Serial.println(" saved in EEPROM");
  EEPROM.begin(512);
  EEPROM.put(0, meter);
  EEPROM.commit();
  EEPROM.end();
}

void start_of_s0_pulse() {
  s0_pulse_start = millis();
}

void end_of_s0_pulse() {
  unsigned long now = millis();
  unsigned long s0_pulse_width = now - s0_pulse_start;
 
  if ( (s0_pulse_width >= MINIMUM_S0_PULSE_WIDTH) && (s0_pulse_width <= MAXIMUM_S0_PULSE_WIDTH) ) {
    digitalWrite(ledPin, !digitalRead(ledPin)); // Toggle led, indicating detected pulse

    // Valid S0 pulse length, use this S0 pulse measurement
    s0_counter++;
       
    if (s0_counter >= 1000) { // 1000 pulses received, increase kWh meter value
       s0_counter=0;
       meter++;
       save_to_EEPROM();
    }
   
    pmeter=meter;
    ps0_pulse_width = s0_pulse_width;
    ps0_counter=s0_counter;
    new_valid_pulse_detected = true;

    unsigned long time_between_s0_pulses = now - time_for_last_s0_pulse;
    watts = WS_PER_1HZ_IMPULSE*1000/time_between_s0_pulses;

    if ( (watts <= MAX_VALID_EFFECT) && (time_between_s0_pulses < MAX_S0_PULSE_INTERVAL) ) {
        // watts > MAX_VALID_EFFECT indicates too short pulse interval between last detected S0 pulses, thus an invalid pulse interval
        // Also ignore first calulated value when time_between_s0_pulses is initialized to MAX_S0_PULSE_INTERVAL

        // Save last calculated values for publishing later, new measurements can start in parallel
        pwatts=watts;
        new_valid_effect_calculated = true;
    }
    time_for_last_s0_pulse = now;
  }
}

void highInterrupt() {
  // Assuming s0 pulse ends with LOW->HIGH transition
  detachInterrupt(inputPin);
  end_of_s0_pulse();
  attachInterrupt(inputPin, lowInterrupt, FALLING);
}

void lowInterrupt() {
  // Assuming s0 pulse begins with HIGH->LOW transition
  detachInterrupt(inputPin);
  start_of_s0_pulse();
  attachInterrupt(inputPin,  highInterrupt, RISING);
}

void callback(char* topic, byte* payload, unsigned int length) {
  // Check incoming message using really ugly code
  String nstring = "";
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    nstring = String (nstring + (char)payload[i]);
  }
  nstring = String (nstring + '\0');
  Serial.println("");
  Serial.print("nstring = ");
  Serial.println(nstring);
  int value;
  if (String(topic + '\0' == inTopic)) {
    value = nstring.toInt(); // (long) 0 is returned if unsuccessful)
    if (value > 0 ) {
      Serial.print("METER value change to ");
      Serial.print(value);
      Serial.println(" requested");
      meter=value;
      s0_counter=0; // Clear S0 counter when resetting meter value
      save_to_EEPROM();   
    }
  }
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect("ESP8266_S0")) {
      Serial.println("connected");
      // Once connected, publish an announcement...
      // ... and resubscribe
      client.subscribe(inTopic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}
 
void loop() {
 
  // Check if a client has connected
  if (!client.connected()) {
    reconnect();
  }

  // Perform PubSubClient tasks
  client.loop();
   
  // Check if a new valid pulse has been detected with saved data in "p" variables, publish!
  if (new_valid_pulse_detected) {
    char msg[25];
   
    snprintf (msg, 75, "%lu.%lu", pmeter, ps0_counter);
    client.publish(outTopic_pulse, msg);
   
    if (ps0_pulse_width < ps0_pulse_width_min) {
      ps0_pulse_width_min = ps0_pulse_width;
    }
    if (ps0_pulse_width > ps0_pulse_width_max) {
      ps0_pulse_width_max = ps0_pulse_width;
    }

    snprintf (msg, 75, "S0 width %dms (min %d, max %d)", ps0_pulse_width,ps0_pulse_width_min,ps0_pulse_width_max);
    client.publish(outTopic_S0_pulse_width, msg);
   
    new_valid_pulse_detected=false;
  }

  // Check if a new valid effect calculation has been made with saved data in "p" variable, publish!
  if (new_valid_effect_calculated) {
    char msg[25];
    snprintf (msg, 75, "%lu", pwatts);
    client.publish(outTopic_effect, msg);
    new_valid_effect_calculated=false;
  }
 
  delay(1);
}

Användarvisningsbild
admin
Webbplatsadministratör
Inlägg: 8
Blev medlem: tor 18 maj 2017, 16:42

Re: Avläsning av S0-utgången på elmätare

#2

Inläggav admin » tor 18 maj 2017, 20:33

Demofilm med utläsning i realtid från app och visning på Apple Watch:

https://www.youtube.com/watch?v=FVSz2KYMw7s

Obs att jag valde att låta lysdioden på Wemosen växla mellan tänd och släckt för varje puls på elmätaren stället för att bara blinka i takt med den pulsen. Tyckte att det var lättare att se då nere i källaren...

Användarvisningsbild
admin
Webbplatsadministratör
Inlägg: 8
Blev medlem: tor 18 maj 2017, 16:42

Re: Avläsning av S0-utgången på elmätare

#3

Inläggav admin » tor 18 maj 2017, 20:39

Exempel på plottar som skapas via rrdtool på servern var 5:e minut och som jag kan kolla på via mitt Dropboxkonto.

effekt.png
effekt.png (41.79 KiB) Visad 2350 gånger

paha
Inlägg: 2
Blev medlem: tis 22 aug 2017, 21:22

Re: Avläsning av S0-utgången på elmätare (ESP8266)

#4

Inläggav paha » tis 22 aug 2017, 21:27

Hej,

Har precis börjat meka lite med detta själv och snubblade över denna tråden.
Jag har fått igång ESP8266 och MQTT kopplet är uppe.
Men jag vet inte riktigt hur man skall koppla fototransitorn (den länken du har bifogat visar ju hur man kopplar för seriell-port?)
Kan inte så mkt om kopplingsscheman, men vad behöver jag förutom fototransistorn?

admin skrev:
Avläsningen sker via fototransistor motsvarande enligt följande sida där det också finns bra översiktlig information om S0:

http://www.techtrade.se/sv/emc-schematics.asp


Användarvisningsbild
hanpa
Inlägg: 116
Blev medlem: tor 18 maj 2017, 20:11

Re: Avläsning av S0-utgången på elmätare (ESP8266)

#5

Inläggav hanpa » tor 24 aug 2017, 07:50

Jo du har rätt i att det kanske var lite mycket .... och läsa mellan raderna. Andra kretsen som jag länkade till, det räcker med att koppla så här.

Skärmavbild 2017-08-24 kl. 07.43.34.png
Skärmavbild 2017-08-24 kl. 07.43.34.png (6.79 KiB) Visad 1956 gånger


Den övre till höger ansluter du till ingången men du behöver en pull-up för att det ska fungera bra. Du kan använda en ingång som har detta inbyggt, dvs du programmerar ingången med pull-up. Eller så ansluter du ett motstånd på c:a 1k till 3.3V. Den undre ansluter du till jord (GND).

Det är möjligt att det funkar med bara fototransistorn, alltså utan efterföljande transistor, detta har jag dock inte provat. Komponenterna är inte dyra så det är bara att beställa och prova om det fungerar. Dock behöver man få det ljustätt så att man inte får störningar från belysning i närheten. Man kan ha en låda eller så gör man som jag gjorde. Jag har komponenterna på ett litet kretskort (experimentkort) och så har jag tejpat dit det över mätaren med svart eltejp.

Användarvisningsbild
hanpa
Inlägg: 116
Blev medlem: tor 18 maj 2017, 20:11

Re: Avläsning av S0-utgången på elmätare (ESP8266)

#6

Inläggav hanpa » tor 24 aug 2017, 07:58

OBS även att koden är gjord för min mätare som har 1000 pulser per kWh. Den kan säkert fungera även om man har 10 000 pulser, med rätt värden på konstanterna för pulslängd osv, men då kommer det att skickas data över MQTT väldigt tätt, kanske för tätt. Man bör då ändra koden så att man bara skickar var 10:e puls, max en gång per sekund eller hur man nu vill ha det. Anledningen till att jag skickar data efter varje puls är att jag vill kunna se i realtid vilken effektförbrukning jag har och med 1000 pulser per kWh kommer de oftast inte så tätt att detta är ett problem.

paha
Inlägg: 2
Blev medlem: tis 22 aug 2017, 21:22

Re: Avläsning av S0-utgången på elmätare (ESP8266)

#7

Inläggav paha » tor 24 aug 2017, 17:23

Okej, då förstår jag hur du har gjort, jag har fått det att fungera med

fototransistor + motstånd in i ESP8266:an dock så verkar den inte reagera på energimätarens puls, kanske beror på att det är en plast/glasskiva framför den, det är typ en display och så är leden under den...

Någon erfarenhet av att läsa av leds som ligger bakom plast/glas?

hanpa skrev:Jo du har rätt i att det kanske var lite mycket .... och läsa mellan raderna. Andra kretsen som jag länkade till, det räcker med att koppla så här.

Skärmavbild 2017-08-24 kl. 07.43.34.png

Den övre till höger ansluter du till ingången men du behöver en pull-up för att det ska fungera bra. Du kan använda en ingång som har detta inbyggt, dvs du programmerar ingången med pull-up. Eller så ansluter du ett motstånd på c:a 1k till 3.3V. Den undre ansluter du till jord (GND).

Det är möjligt att det funkar med bara fototransistorn, alltså utan efterföljande transistor, detta har jag dock inte provat. Komponenterna är inte dyra så det är bara att beställa och prova om det fungerar. Dock behöver man få det ljustätt så att man inte får störningar från belysning i närheten. Man kan ha en låda eller så gör man som jag gjorde. Jag har komponenterna på ett litet kretskort (experimentkort) och så har jag tejpat dit det över mätaren med svart eltejp.

Användarvisningsbild
hanpa
Inlägg: 116
Blev medlem: tor 18 maj 2017, 20:11

Re: Avläsning av S0-utgången på elmätare (ESP8266)

#8

Inläggav hanpa » tor 24 aug 2017, 22:04

Du kanske måste ha transistorn efter i alla fall?

Du kan ju prova med enklare kod först, typ att du speglar läget från sensorn direkt på inbyggda lysdioden, då ser du om avläsningen som sådan fungerar. Sen måste man anpassa sig till pulslängd och antalet pulser/kWh.

Sen måste man få det ljustätt eller ha släckt i rummet åtminstone när man provar om pulsen kan tas emot från mätaren. Omgivningsljuset kan påverka. Om jag inte har det ljustätt så fungerar det inte för mig då sensorn är för ljuskänslig och blir "mättad" av omgivningsljuset.

roggan
Inlägg: 1
Blev medlem: sön 03 sep 2017, 12:44

Re: Avläsning av S0-utgången på elmätare (ESP8266)

#9

Inläggav roggan » lör 14 okt 2017, 19:54

Hej
Först vill jag tacka för artikeln det här är precis vad jag har letat efter!!

Har försökt att få igång MQTT men jag får inte det att fungera.
Kör med Mosqito på en Raspberry Pi 3, har labbat lite innan med MQTT och det har gått.
Har inlagt användare och lösen i mqtt servern

Jag har inte hittat någon stans där jag lägger in password till brokern, antar att användaren är ESP8266_S0 enligt nedan.?
// Attempt to connect
if (client.connect("ESP8266_S0")) {
Serial.println("connected");

Vad gör jag för fel? :) OBS jag är inte så värst bra på programmering

Mvh Roger

Användarvisningsbild
hanpa
Inlägg: 116
Blev medlem: tor 18 maj 2017, 20:11

Re: Avläsning av S0-utgången på elmätare (ESP8266)

#10

Inläggav hanpa » ons 18 okt 2017, 17:20

Jag behöver inte password till brokern men du kanske har det inställt så.

Det finns en massa olika connect-varianter med olika antal parametrar. Exempel:

connect (clientID, username, password)

https://pubsubclient.knolleary.net/api.html


Återgå till "Egna projekt"

Vilka är online

Användare som besöker denna kategori: 1 och 0 gäst