Источник беспребойного питания на Arduino

На этот раз речь пойдет о проекте, который вынашивался давно, но руки, вместе с головой, никак не доходили. Речь пойдет о бесперебойном источнике питания (ИБП) устройств с малым напряжением. Прежде всего для роутера, медиаконвертера, устройств умного дома, да и просто для зарядки телефона при необходимости. Особенностью работы ИБП станет его управляемость от Arduino Nano на микроконтроллере ATmega328. Программирование микроконтроллеров – одна из причин, по которой у меня так долго не получалось приступить к проекту. Здесь у меня провал знаний и пришлось его устранять. Так что это мой первый проект на Arduino. Гуру программирования прошу не судить меня строго, работа велась не один месяц и, надеюсь, она того стоила. Теперь обо всем по порядку, кирпичик за кирпичиком.

Источник бесперебойного питания на Arduino

Основные элементы конструкции

Несмотря на то, что всеми процессами управления в моем «бесперебойнике» занимается микроконтроллер, на начальной стадии я бы отложил его в сторону или сунул в макетную плату, чтоб не поломать)).

Прежде всего в каждом ИБП должен быть аккумулятор. От типа аккумулятора будет зависеть какой будет ток, напряжение для заряда, в целом построение схемы и даже алгоритм заряда.  Поэтому начнем с него. Точнее с нескольких их).  Мой выбор пал на литиево-ионные аккумуляторы. Компактные, легкие, более емкие, более долговечные, при нормально организованной защите еще и надежные.  Для моих целей я приобрел у китайских товарищей восемь аккумуляторов NCR18650B фирмы PANASONIC.

Источник бесперебойного питания на Arduino. Аккумулятор PANASONIC NCR18650B
Аккумулятор PANASONIC NCR18650B

Ток разряда одного аккумулятора до 6,5 ампер, заряда до 1,62 ампер, минимальная емкость 3250 mAh — вполне достаточно. Почему мне понадобилось 8 аккумуляторов? Аккумуляторы соединил по схеме «4 на 2». Четыре последовательно и два параллельно, т.е. два параллельно соединенных аккумулятора выступают как один, только увеличенной емкости. Последовательное соединение дает мне 17 вольт заряженных и 13 вольт разряженных аккумуляторов. Меньшее напряжение не даст нормального питания роутеру (в моем случае роутер 12 вольтовый). Емкость, в принципе, можно увеличить, если позволит место в корпусе и деньги в кармане)). Правда, и ток заряда потребуется больше или время заряда.

Для защиты от короткого замыкания, перезаряда или переразряда аккумуляторы получили плату защиты 4s-30A-PRO. В плате присутствует балансировка т.н. BMS (все банки должны равномерно заряжаться).

Источник бесперебойного питания на Arduino. Плата защиты
Плата защиты

Отлично, теперь у меня есть аккумуляторы, которые можно заряжать при наличии напряжения в сети и разряжать при его отсутствии.

Следующий вопрос: чем заряжать?

Трансформаторный блок питания рассматривал и отложил в сторону. Почему? Габариты. Потребуется трансформатор ампер на 5, возможный гул и хуже всего выходное напряжение, зависящее от напряжения в сети (может быть слишком большим или недостаточным). Для заряда аккумуляторов до 17 В нужно напряжение, как минимум, чуть выше 17 В. А, если учесть падение напряжения на полупроводниковых ключах, то и того выше. Итак, лучший вариант, на мой взгляд, — блок питания от ноутбука. Не всякого, конечно. Отличный вариант блок питания на 19 вольт, 4.5А или больше, от ноутбука Asus или любого другого. Напряжение будет стабильным и оптимальным для заряда, блок займет мало места, будет абсолютно тихий, достаточно мощный.

Не мешало бы на начальном этапе присмотреться также к корпусу, в который поместится вся начинка. Мне на глаза попался старый корпус от сетевого ИБП. Под него потом распаивал платы подходящих размеров.

Для вывода информации я использовал дисплей LCD1602 IIC/I2C с подключённым конвертером для преобразования параллельного интерфейса дисплея в шину I2C. Для его использования понадобится библиотека LiquidCrystal_I2C и два свободных пина на  Arduino  нано А4(SDA), А5(SCA) и еще два провода питания: + и — 5 вольт. Дисплей имеет две строки по 16 символов в каждой. Есть русскоязычные библиотеки, но мне попались годные только для вывода текста. А я хочу выводить различные значения. Поэтому оставляю как есть. Излишние заморочки на работу не повлияют.

Источник бесперебойного питания на Arduino. Дисплей LCD1602 IIC/I2C
Дисплей LCD1602 IIC/I2C

_

Что касается нагрузок

Основная нагрузка как я уже говорил роутер. Для его подключения в корпусе следует предусмотреть подходящий разъем.

Источник бесперебойного питания на Arduino. Разъем для роутера
Разъем для роутера

А от разъема организовать переходник. Впрочем, это уже тонкости. Роутер требует, в основном, 9-12 В питающего напряжения. При подключенной сети 220 вольт на нагрузку лягут 19 вольт входного напряжения. Если использовать линейные преобразователи на 12 вольт, вся разница в напряжении уйдет в тепло. При работе от аккумуляторов пустой нагрев радиаторов – роскошь. Потому я решил использовать DC-DC преобразователи на основе ШИМ-регуляторов. КПД их в любом случае буде выше. Качество напряжения (тока) на выходе будет конечно же хуже линейных. Но загляните внутрь роутера, не стоят ли там линейные стабилизаторы. Стоят, и они доведут качество до нормы.

Какой DC-DC преобразователь выбрать каждому решать самостоятельно, благо выбор большой. Ассортимент пополняется. Для каждого выхода нагрузки я использовал отдельный преобразователь. Для роутера выходное напряжение установил 11.5 В, чтобы был побольше запас от минимального напряжения разряженных аккумуляторов (13 В). При этом и преобразователь не теряет выходное напряжение и роутер работает стабильно. Остальные нагрузки по моей задумке 5-ти вольтовые со стандартным USB подключением. Таких устройств сейчас большинство, можно даже подключить небольшую колонку от умного дома. А значит сохранить голосовое управление, любимую музыку и т.п. при отключении электричества.

Для 5-вольтовых  нагрузок использовал модули, так называемые, mini 360 с частотой 340 кГц  на трех выходах и на микросхеме LM2596 с частотой 1 МГц еще на  двух выходах под USB. Три выхода в передней части корпуса и два справа. Выход для роутера получил преобразователь на микросхеме IAGCH (MP2315) с частотой 500кГц. На все преобразователи насадил небольшие радиаторы и спаял на одной плате.

Источник бесперебойного питания на Arduino
Плата с преобразователями

Теоретически преобразователи без радиаторов держат нагрузку с током до 1,8 А, с радиатором до 3 А. Катушка индуктивности на самом деле греется и при гораздо меньшей нагрузке, так что радиатор лишним не будет.

По большому счету подключать по схеме 1 преобразователь 1 нагрузка необязательно, если нагрузки будут небольшой мощности. Так роутер в среднем будет потреблять вместе с преобразователем 300 мА при подключенном сетевом источнике питания. Нагрузка вполне сносная. Самый тяжелый вариант — зарядка двух телефонов одновременно. Тогда одному преобразователю будет не сладко. Именно для таких случаев лучше устанавливать один преобразователь на одну нагрузку.

В целом блок питания может отдать ток 4,5-4,7 А, аккумуляторы еще больше. Позже ограничу ток до 4 А. Более чем достаточно. 

Итак, подытожим, в качестве нагрузки будут постоянно подключены, по моей задумке, шесть преобразователей DC-DC. Один для роутера, остальные для подключения через USB. Все подключаются параллельно к входному напряжению (напряжение нагрузки). Суммарный ток нагрузки всех потребителей будет ограничен 4 амперами.

_

Схемотехника зарядного устройства

Микроконтроллеры могут многое, но сам по себе микроконтроллер не может принять и распределить ток большой мощности. Так что, прежде чем приступить к написанию программы, следует вспомнить принципиальные электрические схемы. Самый простой вариант подключить все параллельно: выход блока питания, аккумулятор, нагрузку. И это будет работать. Аккумулятор будет брать заряд, причем будет брать все что дают. Возможно, нагрузке при этом ничего не достанется)). Шучу, конечно, достанется, но аккумулятор будет очень прожорливым, будет греться и долго не проживет. А если аккумулятор будет заряжен, он также будет отдавать ток, даже при подключенном к сети блоке питания. Лебедь, рак и щука. Что-то должно управлять этим оркестром. Делаем все просто, но с расстановкой.

Потребуется один p-n-p транзистор с током коллектор-эмиттер хотя бы 5 ампер, желательно работающий с частотой до 200 кГц. Да, и радиатор к нему не будет лишним, размером минимум 5 на 5 см (впрочем, все зависит от общей площади ребер). И потребуется один транзистор n-p-n малой мощности, но с работой как минимум на той же частоте. И три мощных диода, рассчитанных на ток от 5 ампер (запас мощности лишним не будет). Диоды лучше выбирать с низким падением напряжения. Отлично будет использовать сдвоенные диоды Шоттки. В старых блоках питания мне удалось найти диоды STPS2045 с падением напряжения около 0,2 В.

Иногда попадаются и с лучшими результатами. В качестве мощного p-n-p транзистора я использовал А1357, маломощного n-p-n – 2n5551. Соединяем детали по следующей схеме:

Схема зарядного устройства с управлением от Arduino. Источник бесперебойного питания на Arduino

Предлагаю для начала посмотреть какую роль будут играть установленные диоды. Диод D1 выполняет больше защитную функцию. Защитит транзисторные цепи от попадания напряжения от аккумулятора при отключении сети 220 вольт и защитит от разряда аккумулятора на выходное сопротивление импульсного блока питания при выходе из строя транзистора Q2. Его важность не особо очевидна, я оставляю для увеличения надежности. Диод D2 при отключении питающего напряжения разрешит разряд аккумулятора только на нагрузку.  Диод D3 отключит аккумулятор от разряда при наличии напряжения на сетевом источнике. Вспоминаем свойства диода. Диоды D2 и D3 будут пропускать через себя ток на нагрузку, поочередно, в зависимости от наличия сетевого напряжения. Диод D1 будет выделять тепло при открытии транзистора Q2. На все диоды лучше подвесить небольшие радиаторы.

Открытие транзистора Q2 откроет протекание тока для заряда аккумулятора. Но литиево-ионные аккумуляторы слишком большим током заряжать нельзя.  Напомню, что максимальный ток заряда одного аккумулятора 1,62 ампера. При параллельном соединении двух аккумуляторов ток удваивается – 3,24 А. На практике, при заряде от тока 3 А аккумуляторы начинают понемногу нагреваться. Поэтому максимальный ток я установил на уровне 2,5 А. Вопрос как я это сделал? А вот здесь микроконтроллер нам поможет. Точнее свойства Arduino nano генерировать ШИМ-сигнал на определенных пинах.  Я выбрал пин D3 и не спроста, но об этом позже. Говорить о том, что такое ШИМ сигнал здесь не буду.  Скажем так, заполняемостью (скважностью) импульсов будем контролировать время открытия транзистора Q2, т.е. контролировать передаваемую на аккумулятор мощность.

Транзистор Q1 используется как усилитель тока, сама «ардуинка» не сможет раскачать мощный транзистор Q2. Резисторы R1 и R3 ограничивают токи через транзисторы. При этом резистор R3 будет рассеивать мощность около 0.7 Вт. Его мощность не должна быть меньше одного ватта. Резистор R4 заставит закрываться транзистор быстрее, для нас это важно. Его сопротивление не должно быть большим. Транзистор R2 «вешает» базу на ноль при отсутствии сигнала. И конденсатор С1 — поможет сгладить импульсы, которыми заряжается аккумулятор. Конденсатор чуть позже облегчит также задачу измерения напряжения на аккумуляторах во время заряда.

Итак, поступающий с микроконтроллера ШИМ-сигнал открывает первый транзистор, что в свою очередь заставляет открываться второй и ток от источника с большим напряжением поступает через диод D1 на заряд аккумулятора. Заметьте, напряжение на блоке питания должно быть больше напряжения аккумулятора, иначе протекание тока прекратится. Впрочем, это очевидно.

Данная схема поможет контролировать заряд аккумуляторов, а управлять зарядом будем с пина D3 командой:

По моей задумке ток заряда будет изменяться в зависимости от уровня заряда аккумулятора. На данном этапе можно провести измерения тока заряда с помощью мультиметра. В моем случае при заполнении 167 ток заряда соответствовал 2.5 А, 105 – 1.5 А, 67 – 1 А.

Здесь хотелось бы сделать небольшое отступление. Как известно, частота ШИМ-сигнала у Arduino менее одного кГц. Для зарядки аккумулятора это вполне приемлемо. Но при зарядке, да и не только, греются радиаторы. Из закрытого корпуса тепло придется выводить вентилятором. Если для работы вентилятора также использовать ШИМ-сигнал от Arduino, то частоту нужно подымать. Иначе вентилятор будет пищать. Пришлось устанавливать библиотеку GyverPWM от AlexGyver, за что ему большое спасибо! Писки ушли, но только после применения этой библиотеки не только к вентилятору, но и к ШИМ-сигналу заряда батарей. Так что все измерения тока и отладки лучше производить после подключения библиотеки GyverPWM.  В дальнейшем некоторые команды будут изменены под эту библиотеку.

Теперь подавая команды для различного значения напряжения на аккумуляторах можно изменять ток заряда. Что-то вроде быстрой зарядки, например:

Правда, на практике в таком случае, возникнет одно неприятное явление. При резком изменении заполнения (duty) величина напряжения на аккумуляторе также изменится. Оно упадет. А, если напряжение стало ниже программа снова увеличит заполнение. И возникнут продолжительные скачки на уровне 50% или 80%. Для избегания такого эффекта после 50% заряда я решил заполнение изменять плавно. Но об этом чуть позже. 

Измерение напряжения на аккумуляторах

Чтобы контролировать ток заряда нужно параллельно производить замеры напряжения на аккумуляторах. На вход контроллера можно подавать не более 5 вольт, а измерять нужно все 19 вольт. Как же быть? Ставим делитель напряжения.

Источник бесперебойного питания на Arduino

При номиналах резисторов, указанных на схеме, можно измерять напряжение до 20,5 вольт, если плата Arduino будет запитана от 5 вольт (может быть и ниже).  Т.е. 5 вольт от делителя на входе A3 будет соответствовать 20.5 вольт или числу 1023 (10 бит). Нельзя подавать на пин напряжение выше напряжения питания микроконтроллера.  Номиналы резисторов делителя нужно выбирать таким, чтобы они в сумме были не менее 10 кОм. Увеличение номиналов имеет смысл для сохранения энергии микромощных схем. При питании от сети рекомендуется минимальные значения, как более точные. Делитель на схеме выше предназначен для измерения напряжения аккумуляторов. Поскольку энергию нужно беречь, номиналы резисторов немного увеличил.

Конденсатор на 0.1 мкФ впаивается непосредственно возле микроконтроллера, сглаживает наведенные помехи.

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

Одно дело измерять напряжение без подключенной зарядки — напряжение почти идеальное (без колебаний). Другое дело напряжение заряжающейся батареи. Такое напряжение пульсирует в соответствии с заполнением ШИМ-сигнала. Установленный параллельно конденсатор несколько улучшает ситуацию, превращая вершины-«прямоугольники» в «треугольники».  Но, если такое напряжение постоянно преобразовывать в проценты заряда, получится прыгающий хаос. Поэтому напряжение необходимо усреднять. Я поступил следующим образом. В диапазоне 20 мсек. Нахожу максимальное значение. Каждые 20 мсек значения складываю. И каждую секунду нахожу среднее значение максимальных значений. Фактически получаю средневыпрямленное значение напряжения. Эту величину можно преобразовывать затем в проценты. Программно выглядит так:

Переменная Vakb будет хранить целочисленные значения напряжения в цифрах от 0 до 1023. Чтобы вычислить значения в процентах нужно знать минимальное значение напряжения для разряженного аккумулятора (13 В) и максимальное – напряжение заряженного аккумулятора сразу после отключения сети без нагрузки (16,8 В).  Min – 0%, max – 100%. Для аккумулятора, который заряжается, картина другая. При подключении сетевого источника напряжение резко подскочит до величины в районе 14 вольт, затем будет заряжаться до уровня 17 вольт, пока плата защиты не перекроет протекание тока заряда. 17 вольт можно считать максимальным напряжением в данном случае.

Таким образом, уровень заряда будет вычисляться по двум формулам с различными значениями минимума и максимума. Формула, следующая:

z=(Vakb-minakb)/(maxakb-minakb)*100;

Где z – величина, значения которой на экран будут выводиться только в диапазоне от 0 до 99, но может принимать и отрицательные значения (когда заряд упал ниже 13 вольт, защита отключит батарею, напряжение приблизится к нулевому);

minakb и maxakb – минимальные и максимальные значения напряжения.

Для визуального отображения процесса заряда к пину D8 подключил зеленый светодиод через токоограничивающий резистор на 200 Ом.

Кусок кода ответственный за заряд батарей и выведение ее уровня заряда приведен ниже:

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

вывод процентов заряда 61
вывод процентов заряда 100

Логическая переменная flag2 будет принимать значения «0» или «1» в зависимости от наличия сетевого напряжения.

_

Проверка наличия сетевого напряжения 220 вольт

Чтобы знать по какой формуле вычислять уровень заряда, да и вообще подавать ли ШИМ-сигнал для заряда аккумуляторов, микроконтроллер должен знать есть напряжение от сетевого источника или нет. Для этого достаточно выделить один цифровой пин (в моем случае D4). На него будет подаваться напряжение через делитель от блока питания. Если напряжение на цифровом входе — более половины напряжения питания микроконтроллера, то оно будет воспринято как сигнал высокого уровня.

Источник бесперебойного питания на Arduino
Схема измерения наличия сетевого напряжения

Стабилитрон в этой схеме предназначен для защиты от подачи напряжения более 5 вольт на вход микроконтроллера. Рост напряжения может произойти при выходе из строя блока питания. Причем, защитный стабилитрон нужен с номиналом чуть ниже 5 вольт, тогда максимальное напряжение на выходе не превысит норму. При номинале 5,1 В выходное напряжение может превысить 5,6 вольт (уже при 19 В на входе), что может быть критично для микроконтроллера.

При напряжении до 5 вольт стабилитрон на схему не оказывает никакого влияния.   Назначение конденсатора повторять не буду, помехи могут играть злую шутку.  Именно поэтому делитель настроен на напряжение в районе 3 вольт, подальше от среднего. На среднем уровне небольшое болтание напряжения может стать причиной неверных значений. Номиналы резисторов на схеме указаны после измерения мультиметром.

Логическая переменная flag2   будет хранить полученное значение наличия сети. Это даст возможность задавать разные режимы работы ИБП при наличии или отсутствии сети.

_

Измерение напряжения на нагрузке

Кроме значения уровня заряда батарей по моей задумке на экран будет регулярно выводиться напряжение на нагрузке в вольтах и потребляемый нагрузкой ток. Для этого придётся провести больше вычислений. Начнем с напряжения на нагрузке. Сначала необходимо поработать паяльником и собрать схему, приведенную ниже:

Источник бесперебойного питания на Arduino. Схема измерения напряжения на нагрузке
Схема измерения напряжения на нагрузке

Делители в схеме рассчитаны на измерение напряжения порядка 20,5 вольт. С учетом потерь на диоде Шоттки максимальное измеряемое напряжение составит 19 с небольшим вольт (блок питания — 19.2 В).  Но, как я уже говорил, блок питания может выйти из строя. Если его напряжение возрастет, лучше этот момент зафиксировать. Поэтому 19 вольт не стоит делать максимально измеряемым напряжением, один вольт запаса лишним не будет.

Любые возможные скачки напряжения приведут также к автоматическому росту напряжения на входе микроконтроллера. Поэтому на входе предусмотрена защита в качестве диода D1 (на схеме). Почему на этот раз не стабилитрон? Если нужно проводить точные измерения стабилитрон не годится. При приближении напряжения к пороговому для стабилитрона значению, показания начнут нелинейно занижаться. Причем диапазон начала искажений довольно большой. Другое дело диод. Если использовать его как ограничитель напряжения, то единственное искажение вызовет ограничительное сопротивление R3. На нем упадет напряжение порядка 2 мВ. Но это совсем незначительная погрешность. В этой части схемы также лучше использовать диод Шоттки. На схеме, приведенной выше, максимальное напряжение на входе микроконтроллера будет 5,2 В с диодом Шоттки и 5,6 – с обычным диодом. В первом случае микроконтроллер точно не сгорит, а во втором — ?

Стоит еще раз заметить, что питающее напряжение микроконтроллера является опорным, его нужно хорошо стабилизировать и провести точные измерения после подключения всех нагрузок. Это напряжение будет прописано как глобальная переменная VREF, в моем случае 5,03 вольта. Также стоит сразу прописать значения R1 и R2. Измерения будут проводиться на аналоговом пине А0.

Как и при измерении напряжения на аккумуляторных батареях сначала нужно провести считывание значения в единицах от 0 до 1023.

V0 = analogRead(0);  // считать напряжение с пина A0

Далее провести усреднение измеренных значений за единицу времени.

Переводим цифры в вольты с учетом делителя. И получаем формулу:

 float Vnagr = vnagr_*VREF*(R1+R2)/R2/1024; // вычисляем среднее реальное напряжение на A0 в вольтах (будет отображаться на дисплее)

Вычисленные значения выводятся на экран с помощью команды:

 lcd.print(Vnagr,2); 

2 – количество знаков после запятой.
Так как вычисления будут происходить раз в секунду, то и вывод на дисплей будет происходить с той же периодичностью. Мерцания показаний не будет, функции задержки перед выводом не требуются.

Зная величину измеренного напряжения на нагрузке, необходимо предусмотреть своевременное отключение нагрузки от аккумуляторов при сильном разряде. Приблизительно за 10% от полного разряда (от 13 вольт). В спроектированном ИБП будет предусмотрен отдельный зацикленный на себя цикл «while». В цикле будет выводится на экран предупреждение о разряде аккумуляторов. Выход из цикла произойдет только после подачи сетевого напряжения. Подробности в полной версии скетча в конце статьи.

_

Измерение тока нагрузки, защита от короткого замыкания

Измерение тока нагрузки оказалась более сложной задачей, чем я предполагал.  Для измерения тока понадобится шунт – резистор с малым и относительно точным сопротивлением и температурным коэффициентом. Я провел эксперименты с установкой такого шунта до нагрузки. Проводил измерения напряжения до и после шунта с помощью делителей, с последующим вычислением разницы и расчетом тока. Каждый делитель вносил свои погрешности, плюс погрешности вносит дискретизация сигнала на микроконтроллере. В результате при отключенной нагрузке наблюдались даже отрицательные значения тока.

Чтобы не использовать делители шунт в результате перенес одним концом к «земле», другим к нагрузке. Таким образом напряжение измеряется только на контактах шунта. И, поскольку оно довольно малое, проводить измерение через делитель не требуется. Убираются лишние погрешности.

Возникает другая проблема. При задуманном мной максимальном токе в 4А напряжение на шунте с сопротивлением 0,25 Ом составит всего лишь один вольт. Для максимально точных вычислений сопротивление шунта должно быть 1,25 Ом (при таких значениях падение напряжения подымится до пяти опорных вольт). Но тогда шунт должен рассеивать мощность 4А х 5В = 20 Вт – гигантский шунт). Значит повышать сопротивление шунта не практично.

Хороший вариант снизить опорное напряжение до 1.1 В. Но такое напряжение придется сделать опорным для всех других измерений (так показало экспериментирование с командами). Боюсь, другим измерениям это не понравится)).

Пробовал вариант с дифференциальным усилителем. В частности, классическую схему на операционном усилителе. Схему пришлось спаять с минимальными расстояниями, подогнанными сопротивлениями. В результате, снова разочароваться. Начнем с того, что при нулевом входящем напряжении на выходе уже имеется небольшое напряжение (и это не тот случай, когда его можно принять за нулевое). Последующие измерения показали нелинейности работы усилителя — различный коэффициент усиления при росте напряжения на входе. Привязать такие измерения к какой-либо формуле у меня не получилось. Дифференциальный усилитель планировался для увеличения точности, а получилось скорее наоборот.

В результате решил вернуться к доступной точности и остановился на схеме, приведённой ниже:

Источник бесперебойного питания на Arduino. Схема измерения напряжения на шунте.
Схема измерения напряжения на шунте

Для ограничения напряжения на входе микроконтроллера все также использовал диод. Если кому-то все же понадобится встроенный в микроконтроллер источник опорного напряжения на 1,1 В, то защитный диод можно подключить как показано на схеме. При этом входной сигнал будет ограничен одним вольтом плюс падение напряжения на диоде.

В схему добавлен транзистор: n-канальный MOSFET. Его функции строго защитные. Транзистор должен будет отключить нагрузку при превышении тока или при коротком замыкании. Для его управления пришлось задействовать цифровой пин D2.   Напряжением открытия транзистора будет высокий уровень (5 вольт) с выхода микроконтроллера. Нужен транзистор, с достаточно хорошим открытием при таком напряжении. Я использовал транзистор IRLB3034. Брал у китайцев. Его сопротивление в открытом состоянии мизерно мало, и он практически не греется. Блокировки по току будут организованы программно.

Не стоит забывать о мощности шунта. Я использовал два SMD-резистора мощностью по три ватта, соединенные параллельно. Таким образом, суммарная мощность составила 6 Вт. При максимальном токе в 4А будет рассеиваться 4Вт. По мощности есть небольшой запас.

шунт 0,25 Ом

При пробое транзистора и последующем коротком замыкании, шунт вероятнее всего примет удар на себя, диод защитит Arduino. Защитились как смогли)). Кстати, в случае короткого замыкания при работе от одного только аккумулятора, напряжение на нагрузке резко упадет. Плата защиты аккумулятора войдет в защиту, отключит ток. Защита не снимется пока не будет подано напряжение заряда. Все эти мелочи надо учитывать при проектировании.

Что касается расчета тока, я его провел немного по другому алгоритму, который выглядит более точным. Для начала считываем напряжение:

v2 = analogRead(1); // считать напряжение - пин A1 (шунт)

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

Подсчет напряжения происходит также как в случае с аккумуляторами, только с одним «но». Полученному значению присваивается тип данных не unsigned int, а float. Полученный результат не будет целочисленным от 0 до 1023, как обычно, а будет иметь кучу знаков после запятой. Это многое меняет особенно при малых нагрузках. Так, если мы проведем 50 измерений в секунду и часть значений будет «0», другая часть «1», то при использовании unsigned int конечный результат останется «0», а при типе данных float сохранятся все цифры после запятой. Значение получится, скажем 0,4.

Если разбить 5.03 вольт на 1024, получится 1-ца приблизительно равна 0,0049 В. При сопротивлении шунта 0,25 Ом, получится что минимальный ток, который возможно измерить – 0,0196 А. Если вычисленное значение 0,4, то напряжение уже 0,00196 В, а ток – 0,00784 А. Таким образом, вычисления можно сделать более точным, но несколько более долгим.

Если вычисления окажутся заниженными по сравнению с показаниями вольтметра, можно провести умножение на коэффициент формы сигнала (например, белый гауссовский шум — 1.1547). В моем случае умножение на коэффициент формы не понадобилось, отличие от показаний мультиметра были небольшие.

_

Защита от завышенного напряжения на нагрузке

Теоретически импульсный блок питания может выйти из строя и выдать на выход двойное напряжение. Слишком большое напряжение может оказаться критическим как для аккумулятора, так и для схем линейных стабилизаторов (максимальное напряжение 30-35 вольт). В связи с такой вероятность, пришлось пристроить к схеме следующую цепь:

Источник бесперебойного питания на Arduino
Защитное отключение блока питания

В основе обычное 5-вольтовое реле. Контакты реле по умолчанию подключают блок питания к нагрузке. Но, если измеренное на нагрузке напряжение выходит за рамки дозволенного, с пина D5 подается сигнал высокого уровня. Контакты реле размыкаются.

Транзистор в схеме усиливает ток, диод защищает транзистор. Реле можно заменить на 12-ти вольтовое и подключать к линейному стабилизатору на 12 вольт. Вместо реле можно использовать p-канальный MOSFET, в моих «закромах» просто такого не оказалось.

Для оповещения о состоянии аварии в схему добавил зуммер и красный светодиод.

Источник бесперебойного питания на Arduino. световая и звуковая индикация
Световая и звуковая индикация

 В зависимости от наступившего события (отсутствие сети, авария, разряд батарей и т.д.) зуммер будет подавать различные звуковые сигналы. При аварии блока питания красный светодиод будет моргать, при аварийных отключениях нагрузки – просто загораться. Красный светодиод получил пин D7, а зуммер — D6. Более подробный алгоритм можно увидеть в скетче в конце статьи.

Для обнаружения аварии нужно проверить напряжение на нагрузке до его точного вычисления (для ускорения). Например:

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

Возврат в нормальный режим можно реализовать автоматически или при нажатии кнопки. Я решил не рисковать и выбрал кнопку.  Схема подключения кнопки приведена ниже:

Источник бесперебойного питания на Arduino
Схема подключения кнопки к Arduino

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

Сообщения об аварии могут вызываться одноразовыми скачками напряжения при зарядке аккумулятора, тогда стоит попробовать снизить частоту ШИМ-сигнала на пине D3. Также можно увеличить диапазон измерения напряжения на нагрузке, хоть и с некоторой потерей точности. Для этого придётся изменить номиналы резисторов на делителе так, чтобы задуманное напряжение на входе соответствовало 5 В на выходе (1024 – не 20,5 В входного, а, например, 22 В)

Защита от перегрева

Как известно, литиево-ионные аккумуляторы быстрее выходят из строя, когда они греются. Изучая различные источники, я сделал вывод, что максимальной температурой для эксплуатации таких аккумуляторов является 50 градусов по Цельсию. Значит нужно научиться измерять температуру, (пусть не идеально точно) чтобы предотвратить возможность перегрева. За неимением ничего другого, решил использовать терморезистор NTC (сопротивление падает при нагреве) на 100 кОм при температуре 25 °С. Терморезистор физически разместил вплотную к аккумуляторам.

Размещение термодатчика. Источник бесперебойного питания на Arduino
Размещение термодатчика

Первоначально задача казалась более легкой, чем вышло на самом деле. Все что нужно превратить сопротивление в напряжение, пересчитать результат в температуру, вывести на экран или просто использовать в программе. С аппаратной стороны нужно собрать следующую схему:

Источник бесперебойного питания на Arduino. Схема подключения терморезистора к Arduino
Схема подключения терморезистора к Arduino

Схема не сложная. Задействуется еще один аналоговый пин, на этот раз седьмой (А7). Размещать термодатчик в верхнем плече делителя, мне показалось, более логично. Так с ростом температуры растет напряжение. Резистор на 51 кОм выбирался из математических расчетов таким образом, чтобы обеспечить наиболее линейное изменение температуры на участке от 20 до 50 градусов. За основу расчетов были взяты табличные данные из тех. описания на терморезистор типа В3950. В таблице указаны значения сопротивления терморезистора при различных температурах.

Зная значения сопротивлений при различных температурах (из тех. описания), не сложно вычислить напряжение на выходе делителя при соответствующих температурах. На делитель, правильно подать тоже напряжение, что и на микроконтроллер, оно же — опорное. Все напряжение делится дискретно на 1024 части. 1 = 0,0049 В.  Работать придется с узким участком: 290 (1,421В) будет соответствовать 20 °С, 601 (2,945В) – 50 °С.

Основная проблема в вычислении состоит в том, что нулевая температура не соответствует нулевому напряжению. 0°С будет соответствовать числу 140. При приближении к нулю нелинейность будет резко возрастать. В целом это понятно, ведь даже при очень низких температурах напряжение на делителе будет только стремиться к нулю, но нулевым не станет. Похоже, значения здесь меняются по экспоненте. Обычная пропорция работать не будет. Но для разных участков можно найти свои коэффициенты, если участки считать более или менее линейными. В диапазоне от 15 до 50°С участки можно сделать по 5 градусов. По мере приближения к нулю участки уменьшать. Ну, а на морозе «бесперебойник» вряд ли будет работать))

Если предположить, что усредненное напряжение от делителя записано в переменную float Vtavg, то дальнейшие вычисления могут выглядеть так:

Среднее напряжение вычисляется по аналогии с напряжением на нагрузке. В переходах между диапазонами температура будет проскакивать на большее или меньшее значение. Для моих целей это приемлемо. Показания при комнатной температуре были идентичные показаниям обычного термометра.  

Расчет температуры в цельсиях будет занимать какое-то время. Чтобы не загружать микроконтроллер постоянно, можно сделать это по требованию. Так мной была предусмотрена еще одна кнопка с двойным функционалом. Краткое нажатие включает подсветку дисплея, длительное нажатие приводит к началу вычислений температуры и выводу результатов на экран:

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

Схема подключения кнопки такая же как кнопки сброса аварии, только теперь задействован пин D11.

Снимаемые показания дают возможность быстро реагировать на критическую температуру.   В случае перегрева ИБП будет введен в цикл, из которого сможет выйти после возврата температуры в норму. В цикле предусмотрено отключение тока заряда, а также нагрузки при отсутствии сетевого напряжения. Выглядит цикл следующим образом:

Как видно из строки:

PWM_set(9,255); // выставляем максимальные обороты вентилятора

для качественного охлаждения в ИБП должен быть предусмотрен вентилятор.

_

Установка вентилятора охлаждения

Как не крути, а в закрытом корпусе, особенно во время заряда аккумулятора, радиаторы выделяют не мало тепла. Без вентилятора никак. Чтобы вентилятор не шумел в пустую, им нужно управлять. Причем желательно не просто включать и отключать, а регулировать его обороты. Схема подключения не сложная, потребляемый вентилятором ток не большой, достаточно одного транзистора.

Схема подключения вентилятора к Arduino. Источник бесперебойного питания на Arduino
Схема подключения вентилятора к Arduino

Резистор R1 подобран так, чтобы ток через базу был в 10 раз меньше тока вентилятора. Если вентилятор не разгоняется на желаемый максимум, стоит попробовать уменьшить номинал R1. R2 поможет транзистору быстрее закрыться (особенно важно с увеличением частоты).

 В схеме задействован еще один цифровой пин – D9. Согласно условиям использования библиотеки GyverPWM, количество ШИМ-пинов ограничено. Можно использовать D3 (таймер 2), D5 (таймер 0), D9 и D10 (таймер 1). D3 уже используется, использование D5 сломает все таймеры в скетче. Остается D9 или D10.  Выбор не большой, но для проекта достаточный. Частота прописывается в setup:

//нужно прописать 
pinMode(9, OUTPUT); // здесь ШИМ на вентилятор
pinMode(3, OUTPUT); // здесь ШИМ для управления током заряда АКБ
// задаем частоту и режим
PWM_frequency(9, 200000, CORRECT_PWM); // пин, частота, режим (CORRECT_PWM или FAST_PWM)
PWM_frequency(3, 5000, FAST_PWM);

Как я уже писал выше, библиотека GyverPWM нужна для устранения писка вентилятора. Если писк отсутствует, можно обойтись стандартными командами. Для «заполнения» в библиотеке используется другая команда:

PWM_set(пин,заполнение); // заполнение от 0 до 255

Для отключения ШИМ-сигнала возвращаем пин в обычный режим работы:

PWM_detach(9); // вентилятор не вращается
digitalWrite(9, LOW); // устанавливаем низкий уровень

В целом ничего сложного. Дальше все зависит от того, как составить алгоритм работы. Можно просто привязать к температуре внутри корпуса или поместить еще один датчик на самый горячий радиатор. Можно управлять вентилятором в зависимости от режима работы. Я применил некую комбинацию управления. Обороты будут зависеть от тока заряда, величины нагрузки и температуры вблизи аккумуляторов даже в малонагруженном состоянии. Так при увеличении температуры на величину более 30°С вентилятор начнет работать на минимальных оборотах. Характерно в жаркую погоду. Минимальные обороты находим экспериментально, уменьшая или увеличивая величину заполнения.  Весь скетч можно найти в конце статьи. Вставлять куски с примерами здесь не имеет смысла, нужно видеть картину в целом.

_

Режимы работы

В целом алгоритм построен таким образом, чтобы микроконтроллер прежде всего смог проверить все ли параметры в норме, только затем занялся вычислениями.

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

Источник бесперебойного питания на Arduino

При этом подсветка дисплея ограничится одной минутой в целях экономии заряда.
Следующий допустимый параметр – авария сети. При этом ИБП может продолжить штатно работать на аккумуляторе. Пользователь получит звуковой сигнал, красный светодиод перейдет в режим мигания, на дисплее появится предупреждение:

Авария

Выход из режима предусмотрен с помощью кнопки (будет принудительно подключен блок питания). Кстати, можно попытаться выйти из режима автоматически, при условии сильного разряда аккумуляторов. Так сказать, последний шанс перед полным разрядом.  Может позже обновлю прошивку)).

При низком заряде аккумуляторов (менее 10%) микроконтроллеру придется отключить нагрузку, хотя бы ради того, чтобы вывести предупреждение на дисплей и подать звуковые оповещения.

Аккумулятор разряжен

После отключения нагрузки напряжение батарей снова станет больше. Но подключение нагрузки снова не произойдет. Теперь для подключения нагрузки понадобится наличие 220 вольт. При обнаружении сети нагрузка подключится автоматически (проверка на наличие каждые 2 секунды). После полного разряда, появление сетевого напряжения приведет к подаче питания на микроконтроллер – цикл «void loop()» начнется сначала.

С отключенной нагрузкой ИБП уже не обеспечивает работу подключенных устройств. Так что низкий заряд аккумуляторов – это уже критический параметр.

Еще одним критическим параметром, который приведет к отключению нагрузки, будет короткое замыкание или превышение тока. На экран дисплея будет выведено предупреждение о превышении тока, с требованием отключить нагрузку. Загорится красный светодиод. При этом алгоритм войдет почти в бесконечный цикл. Каждые 20 минут будет подключаться нагрузка и проверяться ток на шунте. Такая автоматизация немного рискованная, ее можно убрать. Для подключения нагрузки можно также нажать кнопку сброса аварии. Если напряжение на шунте будет в норме, программа перейдет в основной цикл.

Температура вблизи аккумуляторов более 50 градусов – тоже критический параметр. Нагрузка останется подключенной только при наличии сетевого напряжения. Заряд аккумулятора будет отключен, вентилятор подключен на максимальные обороты. Из бесконечного цикла программа выйдет только после достаточного охлаждения.

Если все параметры в пределах нормы на дисплей будет выводиться значение напряжения на нагрузке, ток через нагрузку и заряд аккумуляторов «АКБ» (хоть они и не кислотные, но сокращение большинству понятно). В правом верхнем углу – «220 ОК». Что означает: «сетевое напряжение в наличии».

Источник бесперебойного питания на Arduino. Основная надпись на дисплее

После длительного удержания кнопки подсветки, на дисплее появится значение температуры в Цельсиях. Температура будет отображаться в течении минуты. Затем изображение переключится на основной экран. На основной экран можно также выйти кратким нажатием кнопки подсветки.
Подсветка дисплея включается в любом режиме, даже во время задержки при низком заряде батарей. Отключение подсветки автоматическое. Длительность подсветки зависит от наличия сетевого напряжения или степени разряда аккумулятора. При низком заряде подсветка включается менее чем на 2 сек.

Вот, пожалуй, и все. Проект получился не особо легким, но интересным. Можно пафосно сказать: «объединил в себе некоторые знания схемотехники и программирования, дал возможность применить теорию на практике, практическая работа и исследование позволили нарастить навыки»!)).

К тому же в результате появилось полезное устройство. Спасает мой роутер при включениях/отключениях сетевого напряжения (особенно коварные переключения при выходе сетевого стабилизатора из рабочего диапазона напряжений). Ушли глюки при раздаче IP-адресов после выключения электричества.

Касательно финального скетча, думаю, его еще можно оптимизировать, но в настоящем виде он выглядит более наглядно.

Источник бесперебойного питания на Arduino. содержимое корпуса
Содержимое корпуса

Всем спасибо, за потраченное время, надеюсь, материал был полезен.

Полная принципиальная схема здесь.

Полный скетч здесь.

Библиотека LiquidCrystal_I2C  или   на яндексе

Библиотека GyverPWM или на яндексе

Демонстрационное видео