Доставили мне датчик DHT22 (за 13! дней с момента заказа, ехал из Латвии). Измерил потребление: 3 мкА в неактивном режиме и импульсно до 1,4 мА во время опроса. Для коммутации повесил питание датчика на цифровой пин D6. Информационный вывод подключил к D7. "Поковырял" библиотеку DHT-sensor-library, вытянул только то, что мне нужно, немного переписав. На 1 МГц пока не получилось опросить датчик (надо прошаривать код библиотеки - функции dht22_read и dht22_expectPulse см. ниже, а также даташит к DHT22), поэтому пришлось вернуться к 16 МГц (что привело к увеличению энергопотребления, но только в активном режиме).
Схема:Код:
//код++
#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <util/delay.h>
// для ЖК дисплея 1602 с I2C
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// кнопка
#define BUTTON_PIN 2
// подключение ЖК дисплея 1602 I2C
// LCD 1602 I2C <---> Arduino
// GND <------------> GND
// VCC <------------> D4
// LCD SDA <--------> A4
// LCD SCL <--------> A5
// A <--------------> D5
#define LCD_POWER_PIN 4
#define LCD_BACKLIGHT_PIN 5
#define LCD_BACKLIGHT_VALUE 50
// подключение датчика температуры/влажности DHT22
// DHT22 <---> Arduino
// VCC <------------> D6
// DATA <-----------> D7
#define DHT22_POWER_PIN 6
#define DHT22_DATA_PIN 7
// устанавливаем адрес дисплея
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
// переменные для храниения "адреса" датчика DHT22 (порт/пин)
uint8_t dht22_port;
uint8_t dht22_bit;
uint32_t dht22_maxcycles;
// в этот массив будем принимать данные от датчика DHT22
uint8_t data[5];
// переменные для хранения текущих и предыдущих значений температуры и влажности
float currentTemperature = 0;
int8_t currentHumidity = 0;
float previousTemperature = 0;
int8_t previousHumidity = 0;
// символ градуса
uint8_t symbolDegree[8] = {
B11100,
B10100,
B11100,
B00000,
B00111,
B01000,
B01000,
B00111
};
// символ процента
uint8_t symbolPercent[8] = {
B11000,
B11001,
B00010,
B00100,
B00100,
B01000,
B10011,
B00011
};
// да, да, получилось только через переменные вывести символы :)
int symbolDegreeNumber = 0;
int symbolPercentNumber = 1;
void setup() {
// все пины на выход и в низкий уровень
for (byte i = 0; i <= A3; i++) {
pinMode(i, OUTPUT);
digitalWrite(i, LOW);
}
// установливаем на пине с кнопкой подтяжку к VCC
pinMode(BUTTON_PIN, INPUT_PULLUP);
digitalWrite(BUTTON_PIN, HIGH);
// устанавливаем обработчик прерывания INT0
attachInterrupt(0, m_wake_up, FALLING);
// инициализируем датчик DHT22
dht22_init(DHT22_DATA_PIN);
}
void loop() {
// засыпаем
m_sleep();
if(dht22_read()) {
currentTemperature = dht22_getTemperature();
currentHumidity = round(dht22_getHumidity());
dht22_off();
lcd_show(false);
previousTemperature = currentTemperature;
previousHumidity = currentHumidity;
}
else {
lcd_show(true);
}
}
// функция для погружения МК в сон
void m_sleep() {
// все пины на выход и в низкий уровень
for (byte i = 0; i <= A7; i++) {
// на второй ноге висит прерывание, поэтому ее не трогаем
if (i == 2) continue;
pinMode(i, OUTPUT);
digitalWrite(i, LOW);
}
// отключаем АЦП
ADCSRA = 0;
// отключаем всю периферию
power_all_disable ();
// устанавливаем режим сна - самый глубокий, здоровый сон :)
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
// разрешаем спящий режим
sleep_enable();
// разрешаем прерывания
sei();
// собственно засыпаем
sleep_cpu();
}
// обработчик прерывания по нажатию кнопки
void m_wake_up () {
// запрещаем прерывания
cli();
// запрещаем режим сна
sleep_disable();
// включаем периферию
power_all_enable();
}
///////////////////////////////////////////////////////////////////////////////
// LCD 1602 I2C
void lcd_on() {
digitalWrite(LCD_POWER_PIN, HIGH);
analogWrite(LCD_BACKLIGHT_PIN, LCD_BACKLIGHT_VALUE);
}
void lcd_off() {
digitalWrite(LCD_POWER_PIN, LOW);
digitalWrite(LCD_BACKLIGHT_PIN, LOW);
}
void lcd_show(bool error) {
lcd_on();
// инициализируем дисплей
lcd.begin(16,2);
// создаем символы градуса и процента
lcd.createChar(symbolDegreeNumber, symbolDegree);
lcd.createChar(symbolPercentNumber, symbolPercent);
if (error) {
lcd.setCursor(0,0);
lcd.print("Error DHT");
}
else {
lcd.setCursor(0,0);
lcd.print(currentTemperature, 1);
lcd.setCursor(4,0);
lcd.write(symbolDegreeNumber);
lcd.setCursor(7,0);
lcd.print(currentHumidity, 1);
lcd.setCursor(9,0);
lcd.write(symbolPercentNumber);
if (previousTemperature != 0) {
float deltaT = currentTemperature - previousTemperature;
lcd.setCursor(0,1);
lcd.print(deltaT, 1);
int8_t deltaH = currentHumidity - previousHumidity;
lcd.setCursor(7,1);
lcd.print(deltaH);
}
}
_delay_ms(2000);
lcd_off();
}
///////////////////////////////////////////////////////////////////////////////
// DHT22
void dht22_init(uint8_t pin) {
#ifdef __AVR
dht22_bit = digitalPinToBitMask(pin);
dht22_port = digitalPinToPort(pin);
#endif
dht22_maxcycles = microsecondsToClockCycles(1000); // 1 millisecond timeout for
// reading pulses from DHT sensor.
// Note that count is now ignored as the DHT reading algorithm adjusts itself
// basd on the speed of the processor.
pinMode(pin, INPUT_PULLUP);
}
void dht22_on() {
digitalWrite(DHT22_POWER_PIN, HIGH);
}
void dht22_off() {
digitalWrite(DHT22_POWER_PIN, LOW);
}
boolean dht22_read() {
dht22_on();
// немного подождем, чтобы датчик "пришел в себя" :)
_delay_ms(500);
// Reset 40 bits of received data to zero.
data[0] = data[1] = data[2] = data[3] = data[4] = 0;
// Send start signal. See DHT datasheet for full signal diagram:
// http://www.adafruit.com/datasheets/Digital%20humidity%20and%20temperature%20sensor%20AM2302.pdf
// Go into high impedence state to let pull-up raise data line level and
// start the reading process.
digitalWrite(DHT22_DATA_PIN, HIGH);
_delay_ms(250);
// First set data line low for 20 milliseconds.
pinMode(DHT22_DATA_PIN, OUTPUT);
digitalWrite(DHT22_DATA_PIN, LOW);
_delay_ms(20);
uint32_t cycles[80];
{
cli();
// End the start signal by setting data line high for 40 microseconds.
digitalWrite(DHT22_DATA_PIN, HIGH);
_delay_us(40);
// Now start reading the data line to get the value from the DHT sensor.
pinMode(DHT22_DATA_PIN, INPUT_PULLUP);
_delay_us(10); // Delay a bit to let sensor pull data line low.
// First expect a low signal for ~80 microseconds followed by a high signal
// for ~80 microseconds again.
if (dht22_expectPulse(LOW) == 0) {
sei();
dht22_off();
return false;
}
if (dht22_expectPulse(HIGH) == 0) {
sei();
dht22_off();
return false;
}
// Now read the 40 bits sent by the sensor. Each bit is sent as a 50
// microsecond low pulse followed by a variable length high pulse. If the
// high pulse is ~28 microseconds then it's a 0 and if it's ~70 microseconds
// then it's a 1. We measure the cycle count of the initial 50us low pulse
// and use that to compare to the cycle count of the high pulse to determine
// if the bit is a 0 (high state cycle count < low state cycle count), or a
// 1 (high state cycle count > low state cycle count). Note that for speed all
// the pulses are read into a array and then examined in a later step.
for (int i=0; i<80; i+=2) {
cycles[i] = dht22_expectPulse(LOW);
cycles[i+1] = dht22_expectPulse(HIGH);
}
} // Timing critical code is now complete.
// Inspect pulses and determine which ones are 0 (high state cycle count < low
// state cycle count), or 1 (high state cycle count > low state cycle count).
for (int i=0; i<40; ++i) {
uint32_t lowCycles = cycles[2*i];
uint32_t highCycles = cycles[2*i+1];
if ((lowCycles == 0) || (highCycles == 0)) {
sei();
dht22_off();
return false;
}
data[i/8] <<= 1;
// Now compare the low and high cycle times to see if the bit is a 0 or 1.
if (highCycles > lowCycles) {
// High cycles are greater than 50us low cycle count, must be a 1.
data[i/8] |= 1;
}
// Else high cycles are less than (or equal to, a weird case) the 50us low
// cycle count so this must be a zero. Nothing needs to be changed in the
// stored data.
}
// Check we read 40 bits and that the checksum matches.
if (data[4] == ((data[0] + data[1] + data[2] + data[3]) & 0xFF)) {
sei();
dht22_off();
return true;
}
else {
sei();
dht22_off();
return false;
}
}
float dht22_getTemperature() {
float t;
t = data[2] & 0x7F;
t *= 256;
t += data[3];
t *= 0.1;
if (data[2] & 0x80) {
t *= -1;
}
return t;
}
float dht22_getHumidity() {
float h;
h = data[0];
h *= 256;
h += data[1];
h *= 0.1;
return h;
}
uint32_t dht22_expectPulse(bool level) {
uint32_t count = 0;
// On AVR platforms use direct GPIO port access as it's much faster and better
// for catching pulses that are 10's of microseconds in length:
#ifdef __AVR
uint8_t portState = level ? dht22_bit : 0;
while ((*portInputRegister(dht22_port) & dht22_bit) == portState) {
if (count++ >= dht22_maxcycles) {
return 0; // Exceeded timeout, fail.
}
}
// Otherwise fall back to using digitalRead (this seems to be necessary on ESP8266
// right now, perhaps bugs in direct port access functions?).
#else
while (digitalRead(DHT22_DATA_PIN) == level) {
if (count++ >= dht22_maxcycles) {
return 0; // Exceeded timeout, fail.
}
}
#endif
return count;
}
//код--
Или здесь.
Подключил все это дело к одному аккуму 18650 (аккум одолжил). Измерил энергопотребление девайса: 7 мкА - режим сна, 12,6 мА - отображение показаний датчика при 4,2 В напряжения питания.
Фото:
В нижней строке отображается разница в показаниях между текущим и предыдущим замерами. Пока как-то так. Буду ждать датчик давления.