Arduino логгер температуры, влажности, давления с минимальным потреблением энергии на Arduino Nano/Pro Mini (3.3V версия)

  • Михаил
  • 8 мин. на прочтение
  • 11
  • 31 Oct 2025
  • 31 Oct 2025

ATmega328P был выбран как оптимальное решение для задачи логера данных по нескольким ключевым причинам:

Преимущества ATmega328P для low-power проектов:

Энергоэффективность в режиме сна:

  • Active mode: ~10-20 mA
  • Power-down mode: ~0.1-1 μA (с отключенным BOD)
  • Idle mode: ~1-5 mA

Отличная поддержка библиотек:

  • Библиотека LowPower специально оптимизирована для AVR
  • Подробная документация по режимам сна

Простота схемотехники:

  • Минимальное количество обвязки
  • Стабильная работа от 3.3V
  • Низкое потребление в активном режиме

Стоимость и доступность:

  • Arduino Pro Mini 3.3V - идеальный кандидат
  • Широкая распространенность компонентов

Тонкости реализации режима сна

Критически важные моменты:

void goToSleep() {
 // 1. ОСТАНОВКА ВСЕХ ПЕРИФЕРИЙНЫХ УСТРОЙСТВ
 Serial.end();    // Отключаем UART
 SD.end();        // Отключаем SD карту
 
 // 2. ОТКЛЮЧЕНИЕ АЦП
 ADCSRA &= ~(1 << ADEN); // Disable ADC
 
 // 3. НАСТРОЙКА PIN-ОВ ДЛЯ МИНИМАЛЬНОГО ПОТРЕБЛЕНИЯ
 setupPinsForSleep();
 
 // 4. ПЕРЕХОД В РЕЖИМ POWER-DOWN
 LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
 
 // 5. ВОССТАНОВЛЕНИЕ ПОСЛЕ СНА
 wakeUpProcedure();
}

Подробнее о каждом этапе:

1. Подготовка пинов

void setupPinsForSleep() {
 // Все цифровые пины в INPUT_PULLUP для минимизации потребления
 for (int i = 0; i < 20; i++) {
   pinMode(i, INPUT_PULLUP);
 }
 
 // Особое внимание пинам с подключенной периферией
 pinMode(SD_CS, INPUT_PULLUP);
 pinMode(10, INPUT_PULLUP); // SPI SS
}

2. Режимы сна AVR

  • IDLE - CPU остановлен, периферия работает
  • ADC NOISE REDUCTION - CPU и АЦП остановлены
  • POWER-DOWN - Полная остановка (самый экономичный)
  • POWER-SAVE - Аналогично POWER-DOWN, но с дополнительными опциями
  • STANDBY - Быстрый выход из сна

3. Пробуждение и восстановление

void wakeUpProcedure() {
 // Включение обратно АЦП
 ADCSRA |= (1 << ADEN);
 
 // Повторная инициализация периферии
 Wire.begin();
 SPI.begin();
 
 // SD карта требует полной переинициализации
 if (!SD.begin(SD_CS)) {
   // Обработка ошибки
 }
}

Особенности работы с датчиками

BMP280/BME280 в режиме Forced

void setupSensorForLowPower() {
 // Режим FORCED - датчик спит между измерениями
 bmp.setSampling(Adafruit_BMP280::MODE_FORCED,
                 Adafruit_BMP280::SAMPLING_X1,  // Минимальное разрешение
                 Adafruit_BMP280::SAMPLING_X1,
                 Adafruit_BMP280::FILTER_OFF);  // Без фильтра для скорости
}

RTC DS3231 как будильник

Альтернативный подход - использование RTC для пробуждения:

void setupRTCAlarm() {
 // Настройка будильника на каждые N минут
 DateTime now = rtc.now();
 DateTime nextAlarm = now + TimeSpan(0, 0, LOG_INTERVAL_MINUTES, 0);
 
 rtc.setAlarm1(nextAlarm, DS3231_A1_Hour);
 rtc.clearAlarm(1);
}

Потребление в цифрах

 

РежимПотреблениеДлительность
Активный режим~15 mA2-3 секунды
Измерение датчиков~1.5 mA100-200 ms
Запись на SD~20 mA500 ms
Сон (POWER-DOWN)0.1 μAОсновное время

Среднее потребление: 0.2-0.5 mA при интервале 5 минут

Расчет автономности

Для аккумулятора 2000 mAh:

2000 mAh / 0.3 mA ≈ 6666 часов ≈ 277 дней

Реальные показатели: 6-9 месяцев работы

Проблемы и их решения

1. SD карта не инициализируется после сна

Решение: Полное отключение питания SD карты через MOSFET

2. "Плавающие" пины увеличивают потребление

Решение: INPUT_PULLUP на всех неиспользуемых пинах

3. Стабильность RTC

Решение: Резервная батарейка CR2032 для DS3231

4. Потеря данных при прерывании питания

Решение: Буферизация в EEPROM + корректное закрытие файла

Альтернативные процессоры

Для сравнения:

  • ESP32: Deep Sleep ~10 μA, но сложнее схема
  • STM32: ~2 μA в sleep, но сложнее в программировании
  • ATtiny85: ~0.1 μA, но мало памяти и периферии

Схема подключения

// Необходимые компоненты:
// - Arduino Nano/Pro Mini (3.3V версия)
// - DS3231 RTC модуль
// - BMP280/BME280 (температура, давление, влажность)
// - SD кард-ридер (SPI)
// - Резисторы 4.7кОм для I2C

Код проекта

#include 
#include 
#include 
#include 
#include 
#include 
// Пины
#define SD_CS 10
#define BMP280_CS 9  // Если используем hardware SPI
#define LED_PIN 13
// Настройки
const unsigned long LOG_INTERVAL = 300000; // 5 минут в миллисекундах
const float SEA_LEVEL_PRESSURE = 1013.25; // hPa
// Объекты
RTC_DS3231 rtc;
Adafruit_BMP280 bmp;
File logFile;
// Структура для данных
struct SensorData {
 float temperature;
 float pressure;
 float humidity;
 float altitude;
};
void setup() {
 pinMode(LED_PIN, OUTPUT);
 digitalWrite(LED_PIN, LOW);
 
 Serial.begin(115200);
 while (!Serial) {
   ; // Ждем подключения Serial
 }
 // Инициализация I2C
 Wire.begin();
 
 // Инициализация RTC
 if (!rtc.begin()) {
   Serial.println("Не найден RTC модуль!");
   while (1);
 }
 // Автоматическая установка времени при компиляции
 if (rtc.lostPower()) {
   Serial.println("RTC потерял питание, устанавливаем время!");
   rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
 }
 // Инициализация BMP280
 if (!bmp.begin(0x76)) { // Адрес 0x76 или 0x77
   Serial.println("Не найден BMP280!");
   while (1);
 }
 // Настройка BMP280
 bmp.setSampling(Adafruit_BMP280::MODE_FORCED,
                 Adafruit_BMP280::SAMPLING_X1,
                 Adafruit_BMP280::SAMPLING_X1,
                 Adafruit_BMP280::FILTER_OFF);
 // Инициализация SD карты
 if (!SD.begin(SD_CS)) {
   Serial.println("Ошибка инициализации SD карты!");
   return;
 }
 // Создание файла лога если не существует
 if (!SD.exists("datalog.csv")) {
   logFile = SD.open("datalog.csv", FILE_WRITE);
   if (logFile) {
     logFile.println("DateTime,TemperatureC,PressurehPa,Humidity%,Altitudem");
     logFile.close();
   }
 }
 Serial.println("Логер инициализирован");
 blinkLED(3); // Сигнал успешной инициализации
}
void loop() {
 DateTime now = rtc.now();
 
 // Сбор данных
 SensorData data = readSensors();
 
 // Запись на SD карту
 logData(now, data);
 
 // Вывод в Serial для отладки
 printData(now, data);
 
 // Подготовка к сну
 goToSleep();
}
SensorData readSensors() {
 SensorData data;
 
 // Принудительное измерение BMP280
 bmp.takeForcedMeasurement();
 
 data.temperature = bmp.readTemperature();
 data.pressure = bmp.readPressure() / 100.0F; // Pa to hPa
 data.altitude = bmp.readAltitude(SEA_LEVEL_PRESSURE);
 
 // Для BME280 добавляем влажность
 // data.humidity = bmp.readHumidity();
 data.humidity = 0.0; // Заглушка для BMP280
 
 return data;
}
void logData(DateTime now, SensorData data) {
 logFile = SD.open("datalog.csv", FILE_WRITE);
 
 if (logFile) {
   // Форматирование даты и времени
   logFile.print(now.year());
   logFile.print("-");
   logFile.print(now.month());
   logFile.print("-");
   logFile.print(now.day());
   logFile.print(" ");
   logFile.print(now.hour());
   logFile.print(":");
   logFile.print(now.minute());
   logFile.print(":");
   logFile.print(now.second());
   logFile.print(",");
   
   // Запись данных
   logFile.print(data.temperature);
   logFile.print(",");
   logFile.print(data.pressure);
   logFile.print(",");
   logFile.print(data.humidity);
   logFile.print(",");
   logFile.print(data.altitude);
   logFile.println();
   
   logFile.close();
   blinkLED(1); // Сигнал успешной записи
 } else {
   Serial.println("Ошибка открытия файла!");
 }
}
void printData(DateTime now, SensorData data) {
 Serial.print("Время: ");
 Serial.print(now.year());
 Serial.print("-");
 Serial.print(now.month());
 Serial.print("-");
 Serial.print(now.day());
 Serial.print(" ");
 Serial.print(now.hour());
 Serial.print(":");
 Serial.print(now.minute());
 Serial.print(":");
 Serial.println(now.second());
 
 Serial.print("Температура: ");
 Serial.print(data.temperature);
 Serial.println(" °C");
 
 Serial.print("Давление: ");
 Serial.print(data.pressure);
 Serial.println(" hPa");
 
 Serial.print("Влажность: ");
 Serial.print(data.humidity);
 Serial.println(" %");
 
 Serial.print("Высота: ");
 Serial.print(data.altitude);
 Serial.println(" m");
 Serial.println();
}
void goToSleep() {
 // Отключаем все периферийные устройства
 Serial.end();
 SD.end();
 
 // Переводим процессор в сон
 // 8 секунд сна * N циклов = LOG_INTERVAL
 unsigned long sleepCycles = LOG_INTERVAL / 8000;
 
 for (unsigned long i = 0; i < sleepCycles; i++) {
   LowPower.powerDown(SLEEP_8S, ADC_OFF, BOD_OFF);
 }
 
 // Просыпаемся и переинициализируем
 setupPeripherals();
}
void setupPeripherals() {
 // Включаем Serial для отладки
 Serial.begin(115200);
 
 // Инициализируем SD карту
 SD.begin(SD_CS);
}
void blinkLED(int times) {
 for (int i = 0; i < times; i++) {
   digitalWrite(LED_PIN, HIGH);
   delay(100);
   digitalWrite(LED_PIN, LOW);
   delay(100);
 }
}

Версия с конфигурацией через Serial

// Добавьте эту функцию для настройки интервала
void configureLogger() {
 if (Serial.available()) {
   String input = Serial.readString();
   input.trim();
   
   if (input.startsWith("INTERVAL=")) {
     unsigned long newInterval = input.substring(9).toInt() * 60000; // минуты в мс
     if (newInterval >= 60000) { // Минимум 1 минута
       LOG_INTERVAL = newInterval;
       Serial.print("Интервал установлен: ");
       Serial.print(LOG_INTERVAL / 60000);
       Serial.println(" минут");
     }
   }
   
   if (input == "STATUS") {
     Serial.print("Текущий интервал: ");
     Serial.print(LOG_INTERVAL / 60000);
     Serial.println(" минут");
   }
 }
}
// Вызовите в loop() перед сном:
// configureLogger();

Библиотеки

Установите через менеджер библиотек:

  • RTClib by Adafruit
  • Adafruit BMP280 Library
  • Adafruit Unified Sensor
  • Low-Power by RocketScream

Оптимизации для минимального потребления

  1. Используйте Arduino Pro Mini (3.3V)
  2. Отключайте все светодиоды (питание, RX/TX)
  3. Питание от Li-ion аккумулятора
  4. Используйте режим Forced для датчиков
  5. Отключайте BOD (Brown-Out Detection)

Потребление

Сон: ~0.1 mA

Работа: ~15-20 mA

Среднее: ~0.5 mA (при интервале 5 минут)

Этот логер может работать от аккумулятора 2000mAh несколько месяцев!

Заключение

ATmega328P предоставляет идеальный баланс между:

  • 🔋 Сверхнизким потреблением
  • 💻 Достаточной производительностью
  • 📚 Богатой экосистемой библиотек
  • 💰 Доступной ценой
  • 🔧 Простотой отладки