RobTillaart / DHTNew

Arduino library for DHT11 and DHT22 with automatic sensor recognition
MIT License
98 stars 15 forks source link

Add enable / disable power supply pin #13

Closed RobTillaart closed 4 years ago

RobTillaart commented 4 years ago

To support low power usage e.g. when batteries power the sensor, one need to be able to switch of the power supply of the DHT sensor.

This would imply an additional power pin in the constructor. This power pin has a default of 0xFF that states ==>no power pin.

DHT(datapin, powerPin = 0xFF);

void DHT.enable()
{
  if (_powerPin == 0xFF) return;
  digitalWrite(_powerPin , HIGH):
}

void DHT.disable()
{
  if (_powerPin == 0xFF) return;
  digitalWrite(_powerPin , LOW):
}

bool DHT.sensorEnabled()
{
  return (_powerPin != 0xFF);
}

uint8_t  _powerPin ;

investigate depending on the processor it can or cannot deliver the power from an IO pin directly. Working with an extra mosfet is always possible

investigate After startup a delay is needed. Put this in the library or is the user responsible?

RobTillaart commented 4 years ago

The names in the interface above are not 100%

better alternative below, the extra function defines if the other 3 have meaning (user responsible)

bool DHT.usesPowerPin() 
{
  return (_powerPin != 0xFF);
};

void DHT.powerUp() 
{
  digitalWrite(_powerPin , HIGH):
}

void DHT.powerDown()   
{
  digitalWrite(_powerPin , LOW):
}

bool DHT.power();  
{
  return digitalRead(_powerPin));
} ;

Although the functions are super simple this way, the read() function could check if the sensor is powered up or shut it down. (still check if delay after powerup is needed)


do we need DHTLIB_ERROR_POWERDOWN ?

do we need AUTO_POWER flag?

RobTillaart commented 4 years ago

From the DHT11/21/22 datasheets

When power is supplied to sensor, don't send any instruction to the sensor within one second to pass unstable status.

This implies something like this or the user should take care of this second.

void DHT.powerUp() 
{
  uint32_t start = millis();
  digitalWrite(_powerPin , HIGH):
  while (millis() - start < 1000) yield();
}
RobTillaart commented 4 years ago

Note: when disabling the power pin, the processor should also set the (free running) data pin to LOW. And of course also set it HIGH again when powering up. Otherwise no gain.

void DHT.powerUp() 
{
  uint32_t start = millis();
  digitalWrite(_powerPin , HIGH):
  digitalWrite(_dataPin , HIGH):
  while (millis() - start < 1000) yield();   // blocking for a second... 
}

void DHT.powerDown()   
{
  digitalWrite(_powerPin , LOW):
  digitalWrite(_dataPin , LOW):
}
RobTillaart commented 4 years ago

A powerup timestamp might be needed to remove the blocking code in powerUp(). When read is called it should be checked similar like lastRead timestamp. merging the two timestamps is not an option. Such timestamps might affect measurements after millis() wraps around. Verify this. However it could be used instead of start in prev post.

rename DHT.power() to uint32_t DHT.uptime() => returns false == 0 if down.

do we need DHTLIB_ERROR_POWERDOWN ?

no, consider a DHTLIB_STATE_POWERDOWN and DHTLIB_STATE_STARTING.

RobTillaart commented 4 years ago

Current thoughts about code, it looks pretty complete

#define DHTLIB_STATE_DOWN        10
#define DHTLIB_STATE_STARTING    11

uint32_t _powerChange = 0;

DHTNEW::DHTNEW(uint8_t dataPin, uint8_t powerPin = 0xFF)
{
  _dataPin = dataPin;
  pinMode(_dataPin , OUTPUT);
  digitalWrite(_dataPin , HIGH):

  _powerPin = powerPin;
  if (_powerPin != 0xFF)
  {
    pinMode(_powerPin, OUTPUT);
    digitalWrite(_powerPin , HIGH):
  }
   _powerChange = millis();
}

void DHTNEW::power(const uint8_t mode) 
{
  if (_powerPin == 0xFF) return;
  if (digitalRead(_powerPin) == mode) return;
  _powerChange = millis();
  digitalWrite(_powerPin , mode):
  digitalWrite(_dataPin , mode):
}

uint32_t DHTNEW::uptime()
{
  if (_powerPin == 0xFF) return millis() - _powerChange;
  if (digitalRead(_powerPin) == LOW) return 0;
  return millis() - _powerChange;
}

/*
// not needed, very similar to uptime()
uint32_t DHTNEW::downtime()  
{
  if (_powerPin == 0xFF) return 0;
  if (digitalRead(_powerPin) == HIGH) return 0;
  return millis() - _powerChange;
}
*/

int DHTNEW::read()
{
  uint32_t ut = uptime();
  if (ut == 0) return DHTLIB_STATE_DOWN;
  if (ut < 1000) return DHTLIB_STATE_STARTING;
  ....
}

The function power() only works when a powerPin is set to a value other than 0xFF. The function uptime() works always. The function read() checks if the sensor is down or started long enough ago.

The user is responsible to use a valid power-pin.

TODO

RobTillaart commented 4 years ago

The control of the power pin can very well be done outside the library, probably even more optimal. The library (current 0.3.0) does pull the data pin HIGH between reads. No energy measurements have been made how much this costs.

For a low power application the library could support toggling this data pin to LOW / HIGH to reduce energy consumption. The minimal implementation:

// to be used by low power applications, not tested
void DHTNEW::power(const uint8_t mode) 
{
  digitalWrite(_dataPin , mode):
}

It makes sense to implement this and add documentation that the user should take care of the boot delay of the sensor after the power is enabled again.

RobTillaart commented 4 years ago

At least not in 0.3.x the library will not support switching of the power pin as this is breaking. To make a step in the right direction, these two functions shall be implemented as these would fit in the long term vision. described above. For now they will only affect the _dataPin.

void DHTNEW::powerUp();
void DHTNEW::powerDown();

After the powerUp(), the first read() can be out of sync, so one must make 1 dummy read() and wait for 2 seconds after it. The dummy read should be in the powerUp() function, but the delay should be users responsibility.