Pulse Power Meter: Sleep mode and clear last pulse count



  • Hello,

    I have built a power meter that works just fine when I use sleep mode = false but that uses up the battery in no-time

    As soon as I switch to true it stops sending values, I waited for one hour without any transfers and then I gave up

    How is it supposed to work?

    Another thing I can't figure out is how to clear the stored pulse count in the gateway, after the tryout is done I would like to start from scratch

    Anyone that can help me with these two problems?

    Attaching my code below

    /**
    * The MySensors Arduino library handles the wireless radio link and protocol
    * between your home built sensors/actuators and HA controller of choice.
    * The sensors forms a self healing radio network with optional repeaters. Each
    * repeater and gateway builds a routing tables in EEPROM which keeps track of the
    * network topology allowing messages to be routed to nodes.
    *
    * Created by Henrik Ekblad <henrik.ekblad@mysensors.org>
    * Copyright (C) 2013-2015 Sensnology AB
    * Full contributor list: https://github.com/mysensors/Arduino/graphs/contributors
    *
    * Documentation: http://www.mysensors.org
    * Support Forum: http://forum.mysensors.org
    *
    * This program is free software; you can redistribute it and/or
    * modify it under the terms of the GNU General Public License
    * version 2 as published by the Free Software Foundation..
    *
    *******************************
    *
    * REVISION HISTORY
    * Version 1.0
    *
    * DESCRIPTION
    * Power Meter for S0 pulses
    * 
    *
    */
    
    // Enable debug prints to serial monitor
    //#define MY_DEBUG 
    
    // Define a static node address, remove if you want auto address assignment
    //#deine MY_NODE_ID 3
    
    // Enable and select radio type attached
    #define MY_RADIO_NRF24
    //#define MY_RADIO_RFM69
    
    #define DIGITAL_INPUT_SENSOR 3  // The digital input you attached your light sensor.  (Only 2 and 3 generates interrupt!)
    #define PULSE_FACTOR 1000       // Nummber of blinks per KWH of your meeter
    #define SLEEP_MODE false        // Watt-value can only be reported when sleep mode is false.
    #define MAX_WATT 10000          // Max watt value to report. This filetrs outliers.
    
    #define RELEASE "1.0"
    
    #include <MySensors.h>
    
    // Child sensor ID's
    //#define CHILD_ID_TEMP  1
    #define CHILD_ID_POWER   2
    
    // Sensor messages
    MyMessage wattMsg(CHILD_ID_POWER, V_WATT);
    MyMessage kwhMsg(CHILD_ID_POWER, V_KWH);
    MyMessage pcMsg(CHILD_ID_POWER, V_VAR1);
    
    // Global settings
    unsigned long SEND_FREQUENCY = 20000; // Minimum time between send (in milliseconds). We don't wnat to spam the gateway.
    double ppwh = ((double)PULSE_FACTOR) / 1000; // Pulses per watt hour
    bool pcReceived = false;
    volatile unsigned long pulseCount = 0;
    volatile unsigned long lastBlink = 0;
    volatile unsigned long watt = 0;
    unsigned long oldPulseCount = 0;
    unsigned long oldWatt = 0;
    double oldKwh;
    unsigned long lastSend;
    boolean transmission_occured = false;
    int sendBattery = 0;
    
    // Storage of old measurements
    long lastBattery = -100;
    
    /****************************************************
    *
    * Setup code
    *
    ****************************************************/
    void setup() {
    
    	Serial.begin(9600);
    	Serial.print(F("Power Meter FW "));
    	Serial.print(RELEASE);
    	Serial.flush();
    
    	Serial.flush();
    	Serial.println(F(" - Online!"));
    
    	// Fetch last known pulse count value from gw
    	request(CHILD_ID_POWER, V_VAR1);
    
    	// Use the internal pullup to be able to hook up this sketch directly to an energy meter with S0 output
    	// If no pullup is used, the reported usage will be too high because of the floating pin
    	pinMode(DIGITAL_INPUT_SENSOR, INPUT_PULLUP);
    
    	attachInterrupt(digitalPinToInterrupt(DIGITAL_INPUT_SENSOR), onPulse, RISING);
    	lastSend = millis();
    
    }
    
    void presentation() {
    	sendSketchInfo("Power Meter", RELEASE);
    
    	present(CHILD_ID_POWER, S_POWER);
    
    }
    
    /***********************************************
    *
    *  Main loop function
    *
    ***********************************************/
    void loop() {
    	unsigned long now = millis();
    	// Only send values at a maximum frequency or woken up from sleep
    	bool sendTime = now - lastSend > SEND_FREQUENCY;
    	if (pcReceived && (SLEEP_MODE || sendTime)) {
    		// New watt value has been calculated  
    		if (!SLEEP_MODE && 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)) {
    				send(wattMsg.set(watt));  // Send watt value to gw 
    			}
    			Serial.print("Watt:");
    			Serial.println(watt);
    			oldWatt = watt;
    		}
    
    		// Pulse count has changed
    		if (pulseCount != oldPulseCount) {
    			send(pcMsg.set(pulseCount));  // Send pulse count value to gw 
    			//sendBattLevel();
    			double kwh = ((double)pulseCount / ((double)PULSE_FACTOR));
    			oldPulseCount = pulseCount;
    			if (kwh != oldKwh) {
    				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
    		request(CHILD_ID_POWER, V_VAR1);
    		lastSend = now;
    	}
    	if (SLEEP_MODE) {
    		sleep(SEND_FREQUENCY);
    	}
    
    	
    
    }
    
    void onPulse()
    {
    	if (!SLEEP_MODE) {
    		unsigned long newBlink = micros();
    		unsigned long interval = newBlink - lastBlink;
    		if (interval<10000L) { // Sometimes we get interrupt on RISING
    			return;
    		}
    		watt = (3600000000.0 / interval) / ppwh;
    		lastBlink = newBlink;
    	}
    	pulseCount++;
    }
    
    void receive(const MyMessage &message) {
    	if (message.type == V_VAR1) {
    		pulseCount = oldPulseCount = message.getLong();
    		Serial.print("Received last pulse count from gw:");
    		Serial.println(pulseCount);
    		pcReceived = true;
    	}
    }
    
    void sendBattLevel()
    {
    	long vcc = readVcc();
    	Serial.print("VCC: ");Serial.println(vcc);
    	if (vcc != lastBattery) {
    		lastBattery = vcc;
    
    		// Calculate percentage
    
    		vcc = vcc - 1900; // subtract 1.9V from vcc, as this is the lowest voltage we will operate at
    
    		long percent = vcc / 14.0;
    		sendBatteryLevel(percent);
    		transmission_occured = true;
    	}
    }
    /*******************************************
    *
    * Internal battery ADC measuring
    *
    *******************************************/
    long readVcc() {
    	// Read 1.1V reference against AVcc
    	// set the reference to Vcc and the measurement to the internal 1.1V reference
    #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    	ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    	ADMUX = _BV(MUX5) | _BV(MUX0);
    #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    	ADcdMUX = _BV(MUX3) | _BV(MUX2);
    #else
    	ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
    #endif  
    
    	delay(2); // Wait for Vref to settle
    	ADCSRA |= _BV(ADSC); // Start conversion
    	while (bit_is_set(ADCSRA, ADSC)); // measuring
    
    	uint8_t low = ADCL; // must read ADCL first - it then locks ADCH  
    	uint8_t high = ADCH; // unlocks both
    
    	long result = (high << 8) | low;
    
    	result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
    	return result; // Vcc in millivolts
    
    }
    
    


  • @miljume said:

    I have worked with this sensor for over a week now but still can't get it to work in sleep mode

    So far I have noticed some strange things, please correct me if my assumptions are wrong

    • It seems like request(CHILD_ID_POWER, V_VAR1); in setup() is not being run when in sleep mode
    • When that function is not executed, the boolean pcReceived is not set to true
    • When pcReceived is false the code never enters if (pcReceived && (SLEEP_MODE || sendTime)) where all the "magic" happens
    • Pulse counter gets updated (i.e. interrupt occurs) but as the code never enters the if statement where everything is calculated and sent to the GW things dont work

    And I still havent figured out how to clear the V_VAR1 in the gateway ...


  • Admin

    Hmm.. When running in sleepmode, you should probably put a wait(500) or something after each request.

    Please report back after testing.



  • @hek Thank you for the suggestion but I tried that with no luck

    But I realized that you need to detach the interrupt when entering loop() and then attach it again in the end of the loop(). Then it seems like at least the receive() function is entered. However I still have troubles with the SLEEP mode and getting it to report kwh to the GW

    Meanwhile I have been looking at This Example and it seems like its working better even if I have some conversion problems but I guess that it is due to an incorrect formula on how to calculate watts

    One big problem though that you might know the solution to. How do I clear the gateway value that the sensor reads at startup (V_VAR1)?

    I've been looking at This Link but I am not sure if that's the way to go?


  • Admin

    Clearing the V_VAR1 value on the controller side (I suspect this is what you mean) depends on which controller you're running.

    Clearing EEPROM won't help as it will be requested at each startup.



  • Yes, I am using Domoticz and have cleared the nodes & childs (Settings/Hardware/MySensorsGateway) from the tables but still it the deleted value is being reported back to the sensor at startup


  • Admin

    I think I read somewhere hat you have to go into the database and clear the value. I don't run Domoticz myself.. so I'm not sure.



  • @hek Thank you, you are absolutely correct!

    I found a description on how to edit the SQLite table to remove old values

    Now I think I am up and running ☺


Log in to reply
 

Suggested Topics

0
Online

11.2k
Users

11.1k
Topics

112.5k
Posts