Sensor NRF24L01+ sleep current



  • Hi I just finished my first MySensor setup.
    Everything is working but the uA(micro) for the NRF24L01+ is confusing me.

    I am using a Arduino Nano ATmega328(clone), removed LED and power regulator.

    Maybe to much information: I wake Arduino after 60 seconds and send the data to my Serial gateway. For controller I am using Domoticz.

    I have a BMP180 sensor for pressure and temperature.

    When my arduino is going to sleep it consume ~75uA when I remove 3.3V for the NRF it goes down to ~15uA.

    1. Shouldn't the NRF draw ~0,900 uA when sleeping?
    2. Is there any way to change the sleep mode for NRF? How?

    Any comment is welcome, if you found other error in my code.

    /**
     * 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 - Henrik Ekblad
     * 
     * DESCRIPTION
     * Pressure sensor example using BMP085 module  
     * http://www.mysensors.org/build/pressure
     *
     */
     
    #include <SPI.h>
    #include <MySensor.h>  
    #include <Wire.h>
    #include <Adafruit_BMP085.h>
    
    #define BARO_CHILD 0
    #define TEMP_CHILD 1
    #define BATT_CHILD 2
    
    const float ALTITUDE = 8; // <-- adapt this value to your own location's altitude.
    
    // Sleep time between reads (in seconds). Do not change this value as the forecast algorithm needs a sample every minute.
    const unsigned long SLEEP_TIME = 60000; 
    
    const char *weather[] = { "stable", "sunny", "cloudy", "unstable", "thunderstorm", "unknown" };
    enum FORECAST
    {
    	STABLE = 0,			// "Stable Weather Pattern"
    	SUNNY = 1,			// "Slowly rising Good Weather", "Clear/Sunny "
    	CLOUDY = 2,			// "Slowly falling L-Pressure ", "Cloudy/Rain "
    	UNSTABLE = 3,		// "Quickly rising H-Press",     "Not Stable"
    	THUNDERSTORM = 4,	// "Quickly falling L-Press",    "Thunderstorm"
    	UNKNOWN = 5			// "Unknown (More Time needed)
    };
    
    Adafruit_BMP085 bmp = Adafruit_BMP085();      // Digital Pressure Sensor 
    
    int BATTERY_SENSE_PIN = A0;  // select the input pin for the battery sense point
    int oldBatteryPcnt = 0;
    
    MySensor gw;
    
    float lastPressure = -1;
    float lastTemp = -1;
    int lastForecast = -1;
    
    const int LAST_SAMPLES_COUNT = 5;
    float lastPressureSamples[LAST_SAMPLES_COUNT];
    
    // this CONVERSION_FACTOR is used to convert from Pa to kPa in forecast algorithm
    // get kPa/h be dividing hPa by 10 
    #define CONVERSION_FACTOR (1.0/10.0)
    
    int minuteCount = 0;
    bool firstRound = true;
    // average value is used in forecast algorithm.
    float pressureAvg;
    // average after 2 hours is used as reference value for the next iteration.
    float pressureAvg2;
    
    float dP_dt;
    boolean metric;
    MyMessage tempMsg(TEMP_CHILD, V_TEMP);
    MyMessage pressureMsg(BARO_CHILD, V_PRESSURE);
    MyMessage forecastMsg(BARO_CHILD, V_FORECAST);
    MyMessage battMsg(BATT_CHILD, V_VOLTAGE);
    
    void setup() 
    {
         // use the 1.1 V internal reference
    #if defined(__AVR_ATmega2560__)
       analogReference(INTERNAL1V1);
    #else
       analogReference(INTERNAL);
    #endif
    
    	gw.begin();
    
    	// Send the sketch version information to the gateway and Controller
    	gw.sendSketchInfo("Pressure Sensor", "1.1");
    
    	if (!bmp.begin()) 
    	{
    		Serial.println("Could not find a valid BMP085 sensor, check wiring!");
    		while (1) {}
    	}
    
    	// Register sensors to gw (they will be created as child devices)
    	gw.present(BARO_CHILD, S_BARO);
    	gw.present(TEMP_CHILD, S_TEMP);
      gw.present(BATT_CHILD, S_MULTIMETER);
    	metric = gw.getConfig().isMetric;
    }
    
    void loop() 
    {
       // get the battery Voltage
       int sensorValue = analogRead(BATTERY_SENSE_PIN);
       #ifdef DEBUG
       Serial.println(sensorValue);
       #endif
       
       // 1M, 470K divider across battery and using internal ADC ref of 1.1V
       // Sense point is bypassed with 0.1 uF cap to reduce noise at that point
       // ((1e6+470e3)/470e3)*1.1 = Vmax = 3.44 Volts
       // 3.44/1023 = Volts per bit = 0.003363075
       float batteryV  = sensorValue * 0.003363075;
       int batteryPcnt = sensorValue / 10;
    
       #ifdef DEBUG
       Serial.print("Battery Voltage: ");
       Serial.print(batteryV);
       Serial.println(" V");
    
       Serial.print("Battery percent: ");
       Serial.print(batteryPcnt);
       Serial.println(" %");
       #endif
      
    	float pressure = bmp.readSealevelPressure(ALTITUDE) / 100.0;
    	float temperature = bmp.readTemperature();
    
    	if (!metric) 
    	{
    		// Convert to fahrenheit
    		temperature = temperature * 9.0 / 5.0 + 32.0;
    	}
    
    	int forecast = sample(pressure);
    
    	Serial.print("Temperature = ");
    	Serial.print(temperature);
    	Serial.println(metric ? " *C" : " *F");
    	Serial.print("Pressure = ");
    	Serial.print(pressure);
    	Serial.println(" hPa");
    	Serial.print("Forecast = ");
    	Serial.println(weather[forecast]);
    
    
    	if (temperature != lastTemp) 
    	{
    		gw.send(tempMsg.set(temperature, 1));
    		lastTemp = temperature;
    	}
    
    	if (pressure != lastPressure) 
    	{
    		gw.send(pressureMsg.set(pressure, 1));
    		lastPressure = pressure;
    	}
    
    	if (forecast != lastForecast)
    	{
    		gw.send(forecastMsg.set(weather[forecast]));
    		lastForecast = forecast;
    	}
      //if (oldBatteryPcnt != batteryPcnt) {
       // Power up radio after sleep
       //gw.sendBatteryLevel(batteryPcnt);
       gw.send(battMsg.set(batteryV, 3));
       oldBatteryPcnt = batteryPcnt;
      //}
    	gw.sleep(SLEEP_TIME);
    }
    
    float getLastPressureSamplesAverage()
    {
    	float lastPressureSamplesAverage = 0;
    	for (int i = 0; i < LAST_SAMPLES_COUNT; i++)
    	{
    		lastPressureSamplesAverage += lastPressureSamples[i];
    	}
    	lastPressureSamplesAverage /= LAST_SAMPLES_COUNT;
    
    	return lastPressureSamplesAverage;
    }
    
    
    
    // Algorithm found here
    // http://www.freescale.com/files/sensors/doc/app_note/AN3914.pdf
    // Pressure in hPa -->  forecast done by calculating kPa/h
    int sample(float pressure)
    {
    	// Calculate the average of the last n minutes.
    	int index = minuteCount % LAST_SAMPLES_COUNT;
    	lastPressureSamples[index] = pressure;
    
    	minuteCount++;
    	if (minuteCount > 185)
    	{
    		minuteCount = 6;
    	}
    
    	if (minuteCount == 5)
    	{
    		pressureAvg = getLastPressureSamplesAverage();
    	}
    	else if (minuteCount == 35)
    	{
    		float lastPressureAvg = getLastPressureSamplesAverage();
    		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
    		if (firstRound) // first time initial 3 hour
    		{
    			dP_dt = change * 2; // note this is for t = 0.5hour
    		}
    		else
    		{
    			dP_dt = change / 1.5; // divide by 1.5 as this is the difference in time from 0 value.
    		}
    	}
    	else if (minuteCount == 65)
    	{
    		float lastPressureAvg = getLastPressureSamplesAverage();
    		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
    		if (firstRound) //first time initial 3 hour
    		{
    			dP_dt = change; //note this is for t = 1 hour
    		}
    		else
    		{
    			dP_dt = change / 2; //divide by 2 as this is the difference in time from 0 value
    		}
    	}
    	else if (minuteCount == 95)
    	{
    		float lastPressureAvg = getLastPressureSamplesAverage();
    		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
    		if (firstRound) // first time initial 3 hour
    		{
    			dP_dt = change / 1.5; // note this is for t = 1.5 hour
    		}
    		else
    		{
    			dP_dt = change / 2.5; // divide by 2.5 as this is the difference in time from 0 value
    		}
    	}
    	else if (minuteCount == 125)
    	{
    		float lastPressureAvg = getLastPressureSamplesAverage();
    		pressureAvg2 = lastPressureAvg; // store for later use.
    		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
    		if (firstRound) // first time initial 3 hour
    		{
    			dP_dt = change / 2; // note this is for t = 2 hour
    		}
    		else
    		{
    			dP_dt = change / 3; // divide by 3 as this is the difference in time from 0 value
    		}
    	}
    	else if (minuteCount == 155)
    	{
    		float lastPressureAvg = getLastPressureSamplesAverage();
    		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
    		if (firstRound) // first time initial 3 hour
    		{
    			dP_dt = change / 2.5; // note this is for t = 2.5 hour
    		}
    		else
    		{
    			dP_dt = change / 3.5; // divide by 3.5 as this is the difference in time from 0 value
    		}
    	}
    	else if (minuteCount == 185)
    	{
    		float lastPressureAvg = getLastPressureSamplesAverage();
    		float change = (lastPressureAvg - pressureAvg) * CONVERSION_FACTOR;
    		if (firstRound) // first time initial 3 hour
    		{
    			dP_dt = change / 3; // note this is for t = 3 hour
    		}
    		else
    		{
    			dP_dt = change / 4; // divide by 4 as this is the difference in time from 0 value
    		}
    		pressureAvg = pressureAvg2; // Equating the pressure at 0 to the pressure at 2 hour after 3 hours have past.
    		firstRound = false; // flag to let you know that this is on the past 3 hour mark. Initialized to 0 outside main loop.
    	}
    
    	int forecast = UNKNOWN;
    	if (minuteCount < 35 && firstRound) //if time is less than 35 min on the first 3 hour interval.
    	{
    		forecast = UNKNOWN;
    	}
    	else if (dP_dt < (-0.25))
    	{
    		forecast = THUNDERSTORM;
    	}
    	else if (dP_dt > 0.25)
    	{
    		forecast = UNSTABLE;
    	}
    	else if ((dP_dt > (-0.25)) && (dP_dt < (-0.05)))
    	{
    		forecast = CLOUDY;
    	}
    	else if ((dP_dt > 0.05) && (dP_dt < 0.25))
    	{
    		forecast = SUNNY;
    	}
    	else if ((dP_dt >(-0.05)) && (dP_dt < 0.05))
    	{
    		forecast = STABLE;
    	}
    	else
    	{
    		forecast = UNKNOWN;
    	}
    
    	// uncomment when debugging
    	//Serial.print(F("Forecast at minute "));
    	//Serial.print(minuteCount);
    	//Serial.print(F(" dP/dt = "));
    	//Serial.print(dP_dt);
    	//Serial.print(F("kPa/h --> "));
    	//Serial.println(weather[forecast]);
    
    	return forecast;
    }```

  • Mod

    @flopp How do you measure the small sleep currents?



  • @Yveaux
    With a multimeter



  • True the NRF24L01+ shall use 900nA (0,9uA) in power down mode according to the spec.

    What do you mean when stating "when I remove 3.3V for the NRF it goes down to ~15uA." - is the NRF still connected with data pins to the arduino? (I hope that NO arduino pins are providing HIGH output, while there is no power to the NRF, since this will kill the NRF24L01+

    I understand that you are using a "good Quality" multimeter? else you can't trust your measured values.
    and the gw.sleep(SLEEP_TIME) should sleep both Arduino + NRF24L01+


  • Admin



  • @bjacobse said:

    What do you mean when stating "when I remove 3.3V for the NRF it goes down to ~15uA." - is the NRF still connected with data pins to the arduino? (I hope that NO arduino pins are providing HIGH output, while there is no power to the NRF, since this will kill the NRF24L01+

    While arduino and NRF is in sleep I was measuring ~75 uA I disconnected the positive power cable for NRF and uA droped down to 15uA, BMP180 and stepdown was still connected.

    I understand that you are using a "good Quality" multimeter? else you can't trust your measured values.
    and the gw.sleep(SLEEP_TIME) should sleep both Arduino + NRF24L01+

    I am using a Fluke 87V, not calibrated for a couple of years but I trust it within 5% that's ok for me at the moment
    OK, that's perfect, then I don't have to think about sleep modes



  • @tbowmo

    I have ordered 10 pcs NRF, lets see if they can go down lower then ~60uA when sleeping

    Today I only have 2 NRF :grin:



  • @flopp
    Yes replace NRFL2401+ with another NRF24L01+ to see if this make the current draw smaller - I think you can trust your Fluke 87V :-)



  • I change the NRF for sensor and now I measure 0,04mA(41,8uA) with all equipment connected

    I am happy



  • I tried, before I changed NRF, to have use a digital output to feed my NRF.

    i put

    digitalWrite(4,HIGH);
    

    at first row in SETUP

    I also put

    digitalWrite(4,LOW);
    

    just before it go to sleep

    but I then got "radio init fail", is it even possible to to it this way or can I have this type of function anywhere else in the code/file,?


  • Hero Member

    @flopp When you power down the nRF you need to reinitialize it. But I doubt if it is worth the trouble as the radio consumes only a fraction of the total while sleeping.
    Try making a node which powers up on interrupt only. The total consumption will go down to a little more than 1uA.



  • @AWI said:

    @flopp When you power down the nRF you need to reinitialize it. But I doubt if it is worth the trouble as the radio consumes only a fraction of the total while sleeping.
    Try making a node which powers up on interrupt only. The total consumption will go down to a little more than 1uA.

    It is a sensor(node?) that measures temp and pressure. Is it possible to have interrupt?
    today I use timer, 60 seconds.
    I also use a clone NRF(maybe) because firt one i tried did use much more than 1uA, more something like 60uA



  • The timer can be set as an interupt
    But sleep as MUCH as you can, this preserves your battery. So why measure every minute? if you can wait to measure every 5 minute, do so and prolong your battery :-) I know this depends on what you need the measurements to control, but "if its only" because you want to look at measurements in a graph, sleep for 5 min



  • At the moment I am measuring every minute to verify that it works correctly.

    Later I will measure every 5-15 minutes.

    gw.sleep(sleeptime);
    

    is what I use, I have seen other have used Interrupt together with sleeptime, but it works fine for me, right now in sleep it is 40uA

    Do I need to use interrupt for better power saving?



  • @flopp
    This is the correct way, to sleep and use interrupt for this :-) so you are using interrupt
    http://forum.mysensors.org/topic/417/gw-sleep-and-milis



  • @bjacobse said:

    @flopp
    This is the correct way, to sleep and use interrupt for this :-) so you are using interrupt
    http://forum.mysensors.org/topic/417/gw-sleep-and-milis

    Thanks


Log in to reply
 

Looks like your connection to MySensors Forum was lost, please wait while we try to reconnect.