Руководство Arduino по проектированию с низким энергопотреблением

Изучите основы низкоэнергетического дизайна с использованием оборудования и ПО Arduino.

Авторы: Taddy Chung, José Bagur

Последняя ревизия: 28.02.2025

Цель Low Power — снизить потребление энергии устройством, управляя его поведением для продления времени работы. Электронные устройства, питающиеся напрямую от источника, обычно не требуют реализации Low Power или подобных техник для продления срока службы. С другой стороны, для устройств, работающих от источников вроде аккумуляторов, необходимо экономить потребление, чтобы продлить время работы.

Настоящее руководство по достижению низкоэнергетической системы применимо к каждой плате Arduino. Например, платы Arduino на базе процессора Arm Cortex-M0 32-bit SAMD21 могут использовать преимущества low-power возможностей. Платы Arduino SAMD21 с беспроводным протоколом LoRa® на модуле Murata CMWX1ZZABZ, представленном на MKR WAN 1310, могут совмещаться с low-power для длительной работы. С продвинутыми техниками — такими как руководство по источнику питания и скорости саморазряда — для проектирования энергоэффективных систем подходит каждая плата Arduino.

Библиотека Low Power

Чтобы включить функцию Low Power на платах Arduino, можно скачать библиотеку Arduino Low Power из менеджера библиотек Arduino IDE. Эта библиотека включает low-power возможности для плат семейства MKR.

  • Библиотекой можно управлять через Sketch -> Include Library -> Manage Libraries в Arduino IDE, выполнив поиск «Arduino Low Power» и выбрав последнюю версию.

  • Для обновления компонентов плат — через Tools -> Board -> Boards Manager с поиском соответствующего семейства плат Arduino.

Скачать различные версии Arduino IDE можно по ссылке:

Чтобы узнать больше об Arduino IDE, перейдите по ссылкам:

Техники проектирования с низким потреблением

Существует несколько способов снижения потребления в микроконтроллерах:

  • Sleep Mode (режим сна)

  • External Events (внешние события)

  • ADC (АЦП)

  • Stand-by

Техника Sleep

Лучший способ задействовать low-power — отправить процессор в сон. Режим Deep Sleep позволяет отключать множество внутренних модулей, что экономит большую часть потребления. Deep Sleep может настраиваться с таймером для пробуждения через заданный интервал. Также есть режим Light Sleep, при котором часть внутренних модулей остаётся включённой для поддержания нужных задач. Обычно Deep Sleep применяют для максимальной экономии, а Light Sleep — для отслеживания состояния внешних модулей при пробуждении.

Внутри этих режимов сна Stand-By и Idle обеспечивают разные уровни конфигурации перевода микроконтроллера в low-power. В зависимости от требований устройства, некоторые компоненты или модули могут оставаться работающими во время сна. Stand-By — один из режимов с самым низким потреблением для плат на базе SAMD21. Он останавливает все источники тактирования микроконтроллера и переводит регуляторы напряжения в low-power. Осцилляторы могут находиться в трёх состояниях: остановлены, работают или работают по запросу периферии. Устройство будет в глубоком сне, пока активно WFI (Wait For Interrupt). Триггерами пробуждения служат прерывание или WDT (Watchdog Timer).

То же касается Idle, как и Stand-By. В режиме Idle периферия продолжает работать при активном WFI. Однако устройство не находится в глубоком сне, что означает более высокое потребление по сравнению со Stand-By. Источники тактирования, в свою очередь, зависят от архитектуры ПО — оставлять их работающими или отключить для снижения потребления. Триггерами пробуждения также служат прерывание или WDT.

Внешние события для пробуждения из сна

Приложение не всегда должно оставаться в состоянии сна после завершения запланированных задач — может потребоваться возможность реагировать на изменения окружения. Внешние события — это те причины, которые заставляют микропроцессор просыпаться из сна при выполнении заданных условий. Обычно эти External Events инициируются АЦП (Analog to Digital Converter), периферией вроде UART и доступными портами ввода/вывода.

Когда фиксируется изменение сигнала, микропроцессор просыпается и переходит к выполнению запланированной задачи. Например, для измерения интенсивности вибрации, низкого или избыточного уровня, или таких ресурсов, как количественная оценка газовых компонентов в воздухе.

Пробуждение по АЦП

Триггерный отклик от АЦП (Analog to Digital Converter) для пробуждения относится к случаям внешнего события. Он срабатывает на основе изменения напряжения, распознанного через аналоговый пин платы. Изменение напряжения можно интерпретировать по-разному, и обычно это означает, что подключённое через аналоговый пин устройство выключается, и напряжение опускается до нижнего порога.

Если то же подключённое устройство включается, напряжение достигает верхнего порога. Определение того, как использовать это поведение в качестве сигнала пробуждения — задача архитектора ПО для управления длительной автономностью при выполнении задач.

Руководство по источникам питания и скорости саморазряда

Руководство по источникам питания помогает понять: помимо управления платами Arduino для экономии потребления, важно знать, что у самого источника есть скорость разряда. Нет смысла экономить потребление всеми этими ресурсами и техниками, если источник питания саморазряжается с большим током, чем low-power ток устройства.

Аккумуляторы упоминаются в качестве источника питания. Существуют разные аккумуляторы с разными ёмкостями и скоростями саморазряда, которые не являются плоскими. То есть они быстрее разряжаются в начале. Этот фактор обычно зависит от температуры — аккумуляторы ведут себя по-разному в зависимости от среды. Это нужно учитывать при проектировании эффективного low-power устройства.

Аккумуляторы вроде CR2032 имеют ёмкость 210 мАч со скоростью разряда 1% в месяц. NiMH AAA имеют ёмкость 900 мАч (выше CR2032), но скорость разряда 30% в месяц; AA-вариант имеет 2400 мАч, но та же скорость разряда. Li-Ion — 4400 мАч, но 10% разряда в месяц.

Характеристики разряда Omnienergy CR2032 при разных нагрузках.

Характеристики разряда Omnienergy CR2032 при разных нагрузках.

Очевидно, существует несколько типов источников питания с разными характеристиками, которые можно использовать как факторы дизайна. Некоторые более специфические случаи могут требовать строгий химический состав, но мы используем ёмкость и скорость разряда как основные ингредиенты для проектирования энергоэффективных устройств.

Скорость саморазряда выражается следующим уравнением:

Формула скорости саморазряда.

Формула скорости саморазряда.

Это уравнение скорости саморазряда поможет узнать скорость разряда, которой источник питания подвержен за час. Это значение позволит понять, что оно близко к скорости постоянного потребления.

Несколько факторов проектирования вступают в игру при создании энергоэффективного устройства. Здесь источники питания страдают от этих условий, поэтому это нужно учитывать при проектировании. Это позволит избежать риска иметь более высокое потребление, чем фактическое потребление устройства.

Однако также важно знать форм-фактор источника питания, так как он определяет размер устройства и ограничивает выбор источников, если устройство должно быть очень компактным. Для этого продвинутые low-power техники могут помочь полностью использовать это руководство.

Примеры low-power приложений

С помощью простых примеров вы сможете понять и реализовать режим Low Power.

Стандартный пример Low Power

  • Необходимое оборудование: любая плата Arduino на SAMD21 (семейство MKR)

Это самый простой способ реализации режима Low Power. В качестве индикатора активности или сна используется светодиод. Устройство будет в состоянии сна 5 секунд.

#include "ArduinoLowPower.h"

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
  LowPower.sleep(5000);
}

Здесь можно изменить строку кода для перевода устройства в режим Deep Sleep.

//LowPower.sleep(5000);
LowPower.deepSleep(5000);

Это переведёт устройство в Deep Sleep при включении. В этом простом примере можно увидеть, что код написан внутри функции loop, но строку low-power можно записать и внутри setup.

Учтите, что функция setup выполняется первой — это раздел, где плата конфигурируется до выполнения задач. Поэтому всегда рекомендуется сначала проектировать структуру ПО, а затем писать сам код, что поможет достичь энергоэффективного устройства.

Low-Power пример на основе внешних событий

  • Необходимое оборудование: любая плата Arduino на SAMD21 (семейство MKR)

Следующий пример демонстрирует, как плата просыпается каждые 10 секунд, если только не обнаружит внешнее событие на нужном пине.

#include "ArduinoLowPower.h"

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pin, mode);
  LowPower.attachInterruptWakeup(pin, callback, mode);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(1000);
  digitalWrite(LED_BUILTIN, LOW);
  delay(1000);
  LowPower.sleep(10000);
}

void callback() {
  // This function will be called once on device wakeup
  // You can do some little operations here (like changing variables which will be used in the loop)
  // Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
}

Здесь требуется определить функцию

LowPower.attachInterruptWakeup(pin, callback, mode)

с указанием пина устройства, оформить задачу внутри callback и режим обнаружения изменения сигнала на пине. Режим определяется одним из трёх значений: FALLING, RISING и CHANGE.

FALLING — сигнал на пине идёт по убыванию; RISING — сигнал нарастает; CHANGE — любое изменение сигнала. Для

pinMode(pin, mode)

режимы:

INPUT

,

OUTPUT

или

INPUT_PULLUP

. Полноценный пример low-power по внешним событиям доступен в Examples -> Arduino Low Power -> ExternalWakeup.

#include "ArduinoLowPower.h"

// Blink sequence number
// Declare it volatile since it's incremented inside an interrupt
volatile int repetitions = 1;

// Pin used to trigger a wakeup
const int pin = 8;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  // Set pin 8 as INPUT_PULLUP to avoid spurious wakeup
  pinMode(pin, INPUT_PULLUP);
  // Attach a wakeup interrupt on pin 8, calling repetitionsIncrease when the device is woken up
  LowPower.attachInterruptWakeup(pin, repetitionsIncrease, CHANGE);
}

void loop() {
  for (int i = 0; i < repetitions; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }
  // Triggers an infinite sleep (the device will be woken up only by the registered wakeup sources)
  // The power consumption of the chip will drop consistently
  LowPower.sleep();
}

void repetitionsIncrease() {
  // This function will be called once on device wakeup
  // You can do some little operations here (like changing variables which will be used in the loop)
  // Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
  repetitions ++;
}

Low-Power пример на основе АЦП

  • Необходимое оборудование: любая плата Arduino на SAMD21 (семейство MKR)

Слегка модифицировав предыдущий пример с внешними событиями, можно сконфигурировать АЦП как источник пробуждения по заданному диапазону напряжения. Этот пример доступен в Examples -> Arduino Low Power -> AdcWakeup.

#include "ArduinoLowPower.h"

// Blink sequence number
// Declare it volatile since it's incremented inside an interrupt
volatile int repetitions = 1;

// Pin used to trigger a wakeup
const int pin = A0;
// How sensitive to be to changes in voltage
const int margin = 10;

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(pin, INPUT);
}

void loop() {
  for (int i = 0; i < repetitions; i++) {
    digitalWrite(LED_BUILTIN, HIGH);
    delay(500);
    digitalWrite(LED_BUILTIN, LOW);
    delay(500);
  }

  // Read the voltage at the ADC pin
  int value = analogRead(pin);

  // Define a window around that value
  uint16_t lo = max(value - margin, 0);
  uint16_t hi = min(value + margin, UINT16_MAX);

  // Attach an ADC interrupt on pin A0, calling repetitionsIncrease when the voltage is outside the given range.
  // This should be called immediately before LowPower.sleep() because it reconfigures the ADC internally.
  LowPower.attachAdcInterrupt(pin, repetitionsIncrease, ADC_INT_OUTSIDE, lo, hi);

  // Triggers an infinite sleep (the device will be woken up only by the registered wakeup sources)
  // The power consumption of the chip will drop consistently
  LowPower.sleep();

  // Detach the ADC interrupt. This should be called immediately after LowPower.sleep() because it restores the ADC configuration after waking up.
  LowPower.detachAdcInterrupt();
}

void repetitionsIncrease() {
  // This function will be called once on device wakeup
  // You can do some little operations here (like changing variables which will be used in the loop)
  // Remember to avoid calling delay() and long running functions since this functions executes in interrupt context
  repetitions ++;
}

Углубляясь в код, мы определяем окно значений, в котором будет работать аналоговый пин A0:

uint16_t lo = max(value - margin, 0);
uint16_t hi = min(value + margin, UINT16_MAX);

Конфигурация прерывания АЦП выполняется внутри функции loop, чтобы можно было переконфигурировать её непосредственно перед уходом в сон.

LowPower.attachAdcInterrupt(pin, repetitionsIncrease, ADC_INT_OUTSIDE, lo, hi);

После пробуждения по сконфигурированному прерыванию АЦП оно отключается, чтобы восстановить конфигурацию АЦП.

LowPower.detachAdcInterrupt();

Callback-функции применяются, когда система просыпается из сна по сконфигурированному прерыванию. В таких функциях, как и во всей архитектуре ПО, обычно хорошей практикой является избегать delay() и долгих функций. Это позволит избежать Blocking Operation и проектировать в стиле Non-Blocking Operation, что очень полезно для подобных случаев. Это поможет создать энергоэффективную и одновременно отзывчивую систему.

Пример Low Power с передатчиком LoRa®

  • Необходимое оборудование: MKR WAN 1300/1310 (с встроенным модулем Murata)

Совет

Для расширенных деталей и примеров о технологии LoRa® на MKR WAN 1310 с модулем Murata смотрите эту документацию.

Этот пример показывает MKR WAN1300/1310 как удалённый передатчик, периодически отправляющий сообщения beacon. Это симулирует устройство, периодически передающее данные beacon и требующее длительной автономности. Принимающее устройство будет стационарной приёмной башней. Удалённый передатчик переводит SAMD21 в состояние сна, а также модуль Murata, чтобы исключить ненужное потребление.

Примечание

Подробнее о библиотеке LoRa см. этот репозиторий на GitHub.

// Low Power Library
#include "ArduinoLowPower.h"

// LoRa Library
#include <SPI.h>
#include <LoRa.h>

// LoRa Packet Content
char* message = "Hello LoRa!";

void setup() {
  Serial.begin(9600);
  while (!Serial);

  // LoRa Setup
  Serial.println(F("LoRa Sender"));
  if (!LoRa.begin(868E6)) {
    Serial.println(F("Starting LoRa failed!"));
    while (1);
  } else {
    Serial.println(F("Starting LoRa Successful!"));
  }
}

void loop() {
  LoRa_Packet_Sender();
  GoToSleep();
}

// LoRa Task
void LoRa_Packet_Sender() {
  Serial.print(F("Sending packet: "));
  Serial.println(message);

  // send packet
  LoRa.beginPacket();
  LoRa.print(message);
  LoRa.endPacket();

  // Putting LoRa Module to Sleep
  Serial.println(F("LoRa Going in Sleep"));
  LoRa.sleep();
}

// Sleep Task
void GoToSleep(){
  Serial.println(F("MKR WAN 1310 - Going in Sleep"));
  LowPower.deepSleep(20000);
}

Важно понимать, что задача Low Power применима только к микроконтроллеру. Это означает, что внешние модули, такие как Murata на MKR WAN 1310, должны быть переведены в состояние сна отдельной задачей в коде.

Предупреждение

Если используются внешние модули, такие как Murata (LPWAN) и датчики, не забывайте переводить их в состояние сна перед уходом MCU в сон. Иначе устройство не перейдёт в полное состояние сна, и максимальная экономия энергии не будет достигнута. Сюда же относится и отключение периферийных интерфейсов, таких как TWI и SPI.

Простой пример определения низкого напряжения

  • Необходимое оборудование: плата Arduino любого семейства

Это пример простейшей задачи определения низкого напряжения. Это позволит определить, когда источник питания исчерпывает энергию, и избежать выключения устройства из-за отсутствия питания. Задача простая, но требует таких параметров, как опорное значение, иначе будут наблюдаться неправильные измерения. Следующий пример сконфигурирован для использования с MKR WAN 1310 с прямой подачей на аналоговый пин для извлечения процента заряда аккумулятора.

/*
Low Power - Low Voltage Detection - SAMD21 Specific Configuration Example
*/

// Manual Power Management
#include "ArduinoLowPower.h"

float voltValue, battery_volt, battery_percentage;
float minimal_voltage = 1800;
float battery_voltage_ref = 3.3;

void setup() {
  Serial.begin(57600);
  delay(100);

  // Low Power Indicator Set
  pinMode(LED_BUILTIN, OUTPUT);

  // Default Analog Reference of 3.3V
  analogReference(AR_DEFAULT);

  // Setting up for resolution of 12-Bits
  analogReadResolution(12);
}

void loop() {
  // Reading from the Battery Pin
  voltValue = analogRead(A0);

  // Calculate current voltage level
  battery_volt = ((voltValue*battery_voltage_ref)/4095)*1000;

  // Battery level expressed in percentage
  battery_percentage = 100*abs((battery_volt - minimal_voltage)/((battery_voltage_ref*1000) - minimal_voltage));

  Serial.print(F("Battery: "));
  Serial.print(battery_percentage);
  Serial.println(F(" % "));

  if (battery_volt <= minimal_voltage){
    //LED Notification for low voltage detection
    lowBatteryWarning();
  }

  delay(2000);

  // Going into Low Power for 20 seconds
  LowPower.deepSleep(20000);
}

// Low battery indicator
void lowBatteryWarning(){
  digitalWrite(LED_BUILTIN, HIGH);
  delay (1);
  digitalWrite(LED_BUILTIN, LOW);
  delay (999);
}

Здесь 4 важных конфигурации:

  • analogReference() используется для конфигурации опорного напряжения для аналогового входа.

  • analogReadResolution() — для определения разрешения значения, возвращаемого analogRead().

  • analogRead() — для считывания значения с аналогового пина.

  • Соответствующий делитель разрешения. В данном примере используется 4095 для 12-битного разрешения, применимого к MKR WAN 1310. Для других разрешений (например, 10 бит) нужно использовать 1023.

Если в приложении используется делитель напряжения, необходимо определить и установить соотношение резисторов в коде для получения корректного значения.

Совет

Для более продвинутого определения низкого напряжения см. последний раздел руководства о продвинутых техниках Low-Power в Arduino.

Продвинутые техники Low-Power в Arduino

Следующие продвинутые техники Low Power применимы к каждой плате Arduino, поскольку они задействуют всю плату для максимально возможного снижения потребления. Эти методики помогут гибко проектировать low-power системы с любыми платами Arduino.

Низкая частота и низкое напряжение

Для более продвинутых случаев есть методы дальнейшего снижения потребления. Перевод процессора на низкую частоту и низкое напряжение. Для низких частот многое зависит от функциональности устройства, так как для работы коммуникационных модулей вроде WiFi требуется минимальный уровень частоты. Также можно работать с низким напряжением 3,3 В, помогая снизить общее потребление. Эта конфигурация выполняется в Arduino IDE до загрузки скетча на устройство.

Конфигурация напряжения и частоты микроконтроллера.

Конфигурация напряжения и частоты микроконтроллера.

Процессор обычно меняет частоту в зависимости от нагрузки. Эта частота варьируется, чтобы ускорить процесс вычисления за минимальное время. Некоторые модули, например Wi-Fi и Bluetooth® Low Energy, требуют минимального уровня частоты. Иначе работа на ещё более низких частотах приведёт к неработоспособности модулей. В некоторых случаях дальнейшее снижение частоты требует внешнего кварцевого осциллятора.

Принудительное снижение частоты не поможет, так как нарушит работу модулей. Оставлять частоту неограниченной тоже не поможет получить долгий срок службы аккумулятора. Рекомендуется устанавливать уровни частоты в зависимости от архитектуры ПО для поддержания низкого потребления при выполнении задач. Уровни частоты можно посмотреть в datasheet платы.

Что касается напряжения, обычно платы определяются для работы при 5 В или 3,3 В. Это рабочее напряжение зависит от внешних компонентов. Однако не всегда внешние компоненты требуют высокого напряжения. В таких случаях можно установить рабочее напряжение 3,3 В, что даёт возможность создания энергоэффективного устройства. Потребление, начиная с этих 2 факторов, может снизиться или даже уменьшиться вдвое.

Управление питанием на уровне микроконтроллера

В большинстве случаев использования класса library достаточно для достижения low-power. Однако если требуется отключать вещи индивидуально внутри микроконтроллера, поможет режим управления питанием или соответствующий регистр.

Микроконтроллер SAMD21 — Power Reduction Mode

Для микроконтроллеров SAMD21, найденных на платах семейства MKR, применяется Power Reduction Mode.

Power Reduction Mode привязан к контроллеру энергонезависимой памяти, и регистр CTRLB используется для управления его режимом. Бит Power Reduction Mode устанавливается в регистре статуса (STATUS.PRM), когда контроллер NVM и блок находятся в power reduction mode. Команды Set/Clear Power Reduction Mode используются для перевода NVM-контроллера в этот режим и обратно.

Регистр CTRLB микроконтроллера SAMD21.
Бит SLEEPPRM из регистра CTRLB микроконтроллера SAMD21.

Микроконтроллер Atmega328P — Power Reduction Register

Семейство Classic и Arduino Nano, использующие Atmega328P, имеют Power Reduction Register.

Этот регистр может отключать несколько вещей внутри микроконтроллера: TWI, SPI, USART0, таймеры/счётчики и АЦП. Если бит АЦП отключается, ADCRSA должен быть установлен в ноль, иначе он останется зафиксированным в активном состоянии.

Power Reduction Register.

Power Reduction Register.

Периферия и внутренние модули

Отключение ненужных внутренних модулей в зависимости от приложения очень помогает снизить потребление. Это могут быть SPI, I2C, Serial и АЦП. Поверх этого — Brown-out Detection и Watchdog timer.

Периферийные интерфейсы — SPI, I2C, Serial — регулярно используются для подключения датчиков. По требованиям проекта известно, что некоторые периферийные интерфейсы не будут использоваться. Вместо того чтобы оставлять их в плавающем состоянии, хорошей практикой является отключение неиспользуемой периферии для экономии. Сами датчики также могут переходить в low-power, если им скомандовать. Через установленные периферийные интерфейсы можно отправить команды перевести датчики в low-power, прежде чем отключать саму периферию.

АЦП может использоваться как источник пробуждения, как обсуждалось ранее. Но его также можно отключить для экономии, если он не будет использоваться как источник пробуждения. Эта периферия иногда потребляет значительную мощность, поэтому её отключение может дать дополнительную экономию.

Brown-out Detection и Watchdog timer — внутренние модули, которыми можно управлять вручную. Brown-out detection обычно используется для сравнения при низком напряжении. Watchdog timer важен для мониторинга состояния микроконтроллера.

Если микроконтроллер зависает или попадает в нелегальный процесс, watchdog timer сработает для перезапуска. С другой стороны, watchdog timer можно временно отключить для экономии. Это рекомендуется делать после того, как ПО проверено на стабильность. Иначе это не поможет снизить потребление и сломает систему.

Внешние устройства

Если есть внешние устройства, такие как SD-устройства и датчики, их можно отключать через MOSFET. MOSFET помогает обрезать питание для SD-устройств, подключённых к плате Arduino, для снижения потребления, если устройство уходит в deep sleep. Для датчиков, если требуется полностью сэкономить, их можно отрезать MOSFET. Этот метод полезен, когда устройство периодически уходит в сон.

Low-Dropout регулятор

Некоторые проекты требуют регуляторов напряжения. Если регулятор нужен, ищите Low-Dropout регуляторы с низким Quiescent Current. Это поможет устройству быть на гораздо более низком уровне потребления в режиме сна. Эти детали можно найти в datasheet производителей регуляторов.

Бюджет питания

Хорошей практикой является ведение записи бюджета питания системы. Это даёт обзор и детали для анализа распределения мощности по всему дизайну. Это может помочь спроектировать лучшее потребление, например, информацию о возможном увеличении таймера сна.

Метод измерения потребления энергии

Со всеми методами для достижения low-power на платах Arduino вам также нужно измерить потребление системы для подтверждения правильной работы. Можно использовать Основы работы с мультиметром в разделе Reading Current для измерения тока и фактического потребления системы.

Определение низкого напряжения

Устройство, корректно выполняющее свои задачи и имеющее низкое потребление, — уже хороший дизайн. Тем не менее устройство в какой-то момент исчерпает источник питания, как бы долго оно ни работало. Возможно, оно сможет работать целый год при правильной конфигурации. Однако избежать снижения уровня заряда невозможно. Можно узнать о низком напряжении до того, как устройство выйдет из работы — это Low Voltage Detection.

Совет

Для оценки времени работы можно использовать калькулятор: https://www.omnicalculator.com/other/battery-life

Для этого мы используем метод управления питанием avr микроконтроллеров, на которых основан Atmega328P (например, в Arduino Nano и Classic). Метод определения низкого напряжения, разработанный Nick Gammon, Retrolefty и Coding Badly, будет совмещён с возможностью перехода в low-power.

// Nick Gammon
// Code courtesy of "Coding Badly" and "Retrolefty" from the Arduino forum

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

const long InternalReferenceVoltage = 1062;  // Adjust this value to your board's specific internal BG voltage

void setup() {
  Serial.begin(57600);
  delay(100);

  // Low Power Indicator Set
  pinMode(LED_BUILTIN, OUTPUT);

  // Pre-eliminary Low Power
  resetWatchdog(); // In Case WDT fires
}

void loop() {
  // Tuning on peripherals, timers, and ADC
  manual_periph_ctrl(1);

  // Voltage Level Detection
  Serial.println(getBandgap());
  // Low voltage detection at 3V (300)
  if (getBandgap() < 300){
    //LED Notification for low voltage detection
    lowBatteryWarning();

    // Turns off peripherals, timers, and ADC
    manual_periph_ctrl(0);
  }

  // Low Level Handler
  i2c_switch_off();
  Manual_LowPower_Mode(1);
}

// Fixed at 8 second variant - On the contrary IT TURNS EVERYTHING OFF
void Manual_LowPower_Mode(uint8_t multiplier){
  delay(70);                                              // Requires at least 68ms of buffer head time for module booting time
  for(int i = 0; i <= multiplier; i++){                   // Multiplier for Power Down Tick
    Deep_Sleep_Manual();
  }
}

/*
* Low Voltage Detection Task
*/
// Nick Gammon
// Code courtesy of "Coding Badly" and "Retrolefty" from the Arduino forum
// results are Vcc * 100
// So for example, 5V would be 500.
int getBandgap(){
  // REFS0 : Selects AVcc external reference
  // MUX3 MUX2 MUX1 : Selects 1.1V (VBG)
   ADMUX = bit (REFS0) | bit (MUX3) | bit (MUX2) | bit (MUX1);
   ADCSRA |= bit( ADSC );  // start conversion
   while (ADCSRA & bit (ADSC)){
   }  // wait for conversion to complete
   int results = (((InternalReferenceVoltage * 1024) / ADC) + 5) / 10;
   return results;
} // end of getBandgap

/*
* Low Power Related Tasks
*/
// Enabling Watchdog Timer
void WatchdogEnable() {
  // clear various "reset" flags
  MCUSR = 0;
  // allow changes, disable reset
  WDTCSR = bit (WDCE) | bit (WDE);
  // set interrupt mode and an interval
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  wdt_reset();  // pat the dog

  // disable ADC
  ADCSRA = 0;

  // ready to sleep
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  // Should throw ~0.4mA at best case
  noInterrupts();
  sleep_enable();

  // turn off brown-out enable in software
  MCUCR = bit (BODS) | bit (BODSE);
  MCUCR = bit (BODS);
  interrupts();       // Guarantees next instruction executed
  sleep_cpu ();

  // cancel sleep as a precaution
  sleep_disable();
}

void resetWatchdog (){
  // clear various "reset" flags
  MCUSR = 0;
  // allow changes, disable reset, clear existing interrupt
  WDTCSR = bit (WDCE) | bit (WDE) | bit (WDIF);
  // set interrupt mode and an interval (WDE must be changed from 1 to 0 here)
  WDTCSR = bit (WDIE) | bit (WDP3) | bit (WDP0);    // set WDIE, and 8 seconds delay
  // pat the dog
  wdt_reset();
}  // end of resetWatchdog

void i2c_switch_off(){
  // turn off I2C
  TWCR &= ~(bit(TWEN) | bit(TWIE) | bit(TWEA));

  // turn off I2C pull-ups
  digitalWrite (A4, LOW);
  digitalWrite (A5, LOW);
}

// Runs for 8 seconds aprox counting as a cycle
void Deep_Sleep_Manual(){
  // CORE State
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  ADCSRA = 0;            // turn off ADC
  power_all_disable ();  // power off ADC, Timer 0 and 1, serial interface

  // Interrupts are not counted as ADXL require
  noInterrupts ();       // timed sequence coming up
  resetWatchdog ();      // get watchdog ready
  sleep_enable ();       // ready to sleep
  interrupts ();         // interrupts are required now
  sleep_cpu ();          // sleep
  sleep_disable ();      // precaution
  power_all_enable ();   // power everything back on

}  // end of goToSleep

// Manual Peripheral controller
void manual_periph_ctrl(uint8_t selector){
  byte old_ADCSRA = ADCSRA;
  // disable ADC
  ADCSRA = 0;

  if (selector == 0){
    power_adc_disable();
    power_spi_disable();
    power_timer0_disable();
    power_timer1_disable();
    power_timer2_disable();
    power_twi_disable();

    UCSR0B &= ~bit (RXEN0);  // disable receiver
    UCSR0B &= ~bit (TXEN0);  // disable transmitter
  }
  if (selector >= 1){
    power_adc_enable();
    power_spi_enable();
    power_timer0_enable();
    power_timer1_enable();
    power_timer2_enable();
    power_twi_enable();

    UCSR0B |= bit (RXEN0);  // enable receiver
    UCSR0B |= bit (TXEN0);  // enable transmitter
  }

  ADCSRA = old_ADCSRA;
}

// Low battery indicator
void lowBatteryWarning(){
  digitalWrite(LED_BUILTIN, HIGH);
  delay (1);
  digitalWrite(LED_BUILTIN, LOW);
  delay (999);
}

Чтобы найти внутреннее опорное напряжение, можно запустить один из следующих скетчей. При запуске измерьте пин AREF мультиметром и умножьте полученное значение на 1000, чтобы использовать его в качестве Internal Reference Voltage.

  • Используя регистр ADMUX:

// Find internal 1.1 reference voltage on AREF pin
void setup ()
{
  ADMUX = bit (REFS0) | bit (REFS1);
}

void loop () { }
  • Используя функции analogReference и analogRead:

// Find internal 1.1 reference voltage on AREF pin
void setup ()
{
  analogReference (INTERNAL);
  analogRead (A0);  // force voltage reference to be turned on
}

void loop () { }

Совет

Подробнее о Low Power системах: https://www.gammon.com.au/power

Товарные знаки

  • LoRa® — зарегистрированный товарный знак Semtech Corporation.