Четырехсерийный сериал.

Программное обеспечение, часть 3. Создание и мониторинг доступной аккумуляторно-солнечной системы с помощью Raspberry Pi

Создайте мощную аккумуляторно-солнечную систему с мониторингом потока энергии менее чем за 150 фунтов стерлингов… О, и получайте удовольствие!

Ряд

Эта серия состоит из четырех частей. Если вы новичок, я предлагаю начать с части 1 для некоторого контекста.

Часть 1

Введение в солнечную энергию, список аппаратных частей и технические характеристики системы.



Часть 2

Обзор концепций схемы, схема системы и построение системы.



Часть 3

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



Часть 4

Подробные этапы развертывания для Raspberry Pi.



В предыдущих частях серии мы обсуждали детали физического оборудования электрической системы. Итак, теперь, предположив, что электрическая система на месте и подключена, в комплекте с Raspberry Pi и датчиками, мы обращаем внимание на детали программного обеспечения аккумуляторно-солнечной системы. Не волнуйтесь, вы уже прошли половину пути!

Программное обеспечение разделено на две части. Сначала мы подробно рассмотрим код, который нам понадобится в этой статье (часть 3). В частности, 1 скрипт на Python и 3 (простых) скрипта на bash. Затем мы соберем все вместе, настроив Raspberry Pi и развернув программное обеспечение в заключительной части серии (Часть 4).

Давайте начнем погружение… Программа Python3 постоянно работает на Raspberry Pi в качестве фоновой службы и является основным интерфейсом между физическими датчиками системы и самим Pi. Сценарии bash запускаются с помощью задач Cron Job при перезагрузке и в полночь и обеспечивают бесперебойную работу. Я рекомендую вам потратить время в этой части, чтобы полностью понять код, прежде чем реализовать его в следующей части — это может помочь в устранении неполадок позже. В конце концов, обучение — это цель этой серии! Кроме того, мы не должны слепо доверять всему, что находим в Интернете!

Весь код, который нам понадобится для этого проекта, размещен в этом репозитории GitHub, так что вы можете легко разветвить его для собственного использования. Почему бы не отметить его ⭐, чтобы вы могли легко вернуться к нему позже?

Давайте погрузимся в это!

Скрипт Python

Чтобы мы не были сразу перегружены (были там, сделали это), я разделил программу на три части, чтобы мы могли переваривать ее небольшими кусками.

Здесь мы сначала импортируем все пакеты, которые нам понадобятся, чтобы выполнить все необходимые измерения (time, datetime, ina219, influxdb, Adafruit_DHT, psutil и gpiozero) и вставить их в базу данных InfluxDB. Мы установим эти пакеты как часть развертывания в части 4. Затем мы создадим экземпляр объекта InfluxDB ifclient с необходимыми параметрами конфигурации. Мы установим эти параметры при развертывании программ в части 4, но в качестве краткого обзора:

  • ifuser — это имя пользователя InfluxDB (установленное нами)
  • ifpass — пароль InfluxDB (установленный нами)
  • ifdb — имя базы данных (установлено нами)
  • ifhost — это хост — для него установлено значение localhost, так как мы размещаем InfluxDB локально на этом Raspberry Pi.
  • ifport — это порт на ifhost, который мы будем использовать для доступа к базе данных — 8086 — это порт по умолчанию.
  • measurement_name — это таблица в базе данных, на которую мы хотим сослаться (устанавливается нами)

Обратите внимание, что если вы измените параметры ifdb или measurement_name, позже вам потребуется обновить ссылки на источники данных в Grafana для каждой панели панели мониторинга (довольно утомительно).

Затем мы настраиваем два объекта INA219 (ina_battery и ina_solar), соответствующие модулям INA219, измеряющим каждый соответствующий шунт. Мы также пишем несколько геттеров для питания, напряжения шины и напряжения шунта каждого датчика и создаем экземпляр объекта датчика температуры и влажности DHT22.

Единственное, что нам нужно сделать для датчика DHT22, это установить номер контакта GPIO. В данном случае мы используем контакт 4 GPIO, так как это контакт GPCLK0.

Чтобы настроить ina_battery и ina_solar, нам нужно установить некоторые константы, специфичные для системы, которую мы хотим измерить. Я установил SHUNT_OHMS, MAX_EXPECTED_AMPS, RANGE_16V и GAIN_2_80MV в соответствии со спецификацией примера системы, которую я изложил в предыдущих частях серии, поэтому, если ваша система отличается от моего примера, вам может потребоваться установить другие константы. К счастью для вас, я написал подробную статью, объясняющую, как работает этот пакет Python INA219, с практическими примерами для различных шунтов и систем, на которые вы можете ссылаться! В этой статье также объясняется адресация I2C (0x40 и 0x44), которая нам понадобится.



Для каждой функции-получателя мы округляем вывод до 3 или 4 знаков после запятой — нам нужна достаточная точность, поскольку мы измеряем в милливольтах (мВ).

Итак, теперь мы рассмотрим основной цикл, который используется для выполнения всех измерений каждые 3 секунды и вставки их в базу данных InfluxDB.

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

Функция gather_readings состоит из внешнего и внутреннего цикла while, причем внешний цикл повторяется каждые 3 секунды — именно так часто будет обновляться InfluxDB. Внутренний цикл просто берет на себя ответственность за чтение 10 необработанных данных датчиков INA219 примерно за 1 секунду — внешний цикл обрабатывает все остальное.

Каждые 3 секунды:

Внешний цикл сначала инициирует пустые списки для каждого из соответствующих значений (например, battery_power), прежде чем внутренний цикл заполнит их 10 значениями каждое в течение 1 секунды. Затем эти списки будут усреднены после завершения внутреннего цикла в переменных, каждая из которых инициируется в 0 (например, battery_power_av). Это просто означает, что измерения менее мгновенные и более усредненные по времени.

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

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

Далее мы проводим измерения внутреннего состояния Raspberry Pi. Я включаю их, потому что они, по сути, являются бесплатными сведениями о системе (минимальные вычислительные ресурсы, используемые для ценного вывода), поскольку они помогают диагностировать системные проблемы, если это необходимо. Я включаю загрузку процессора и температурный статус, а также емкость диска и оперативной памяти. Существует множество других точек данных, которые вы можете зарегистрировать, если хотите — см. документы psutil и gpiozero!

Наконец, мы создаем словарь Python со всеми значениями, которые нам нужны для вставки в таблицу измерений в базе данных Influx, убедившись, что мы конвертируем все значения в типы данных float, если они еще не преобразованы. ifclient.write_points(body) берет этот словарь и вставляет его в базу данных. Сделанный!

Теперь мы просто используем изящную однострочную строку time.sleep(3.0 — ((time.time() — starttime) % 3.0)), чтобы вычислить, сколько времени прошло с начала внешнего цикла, и вычтем это из 3 секунд, чтобы вычислить, как долго нам нужно ждать, пока мы не запустим следующий цикл. Это объясняет изменение времени, необходимого для вычисления каждого внешнего цикла.

Обратите внимание, что мы записываем в базу данных некоторые значения, которые не имеют прямого отношения, например, slr_shnt_v и bat_shnt_v (очень маленькие напряжения в милливольтах). Вы можете удалить их, если хотите, но в какой-то момент они могут пригодиться, может быть… Я не скряга, клянусь!

Баш-скрипты

Сценарии bash — это то, что я считаю «клеем» системы. Они планируют и инициируют важные события, чтобы все работало гладко и изящно. При развертывании они контролируются задачами Cron Job, соответствующими временным триггерам. Как видите, нас интересуют триггеры при перезагрузке и в полночь. Нам нужны две задачи Cron Job для перезагрузки, так как у нас есть две задачи для запуска, обе из которых должны выполняться бесконечно (монтирование каталога Google Диска и main.py). Полуночное задание Cron запускает ежедневное расписание резервного копирования в смонтированный каталог Google Диска. Не волнуйтесь, они намного проще, чем скрипт Python!.

Перезагрузка.sh

Это «основной» bash-скрипт перезагрузки — он сначала ждет подключения к Интернету, затем проверяет, запущен ли InfluxDB, прежде чем запускать скрипт main.py Python. Это гарантирует, что база данных будет готова к работе до того, как мы бомбардируем ее вставками.

Комментарий к строке 3 показывает задачу cronjob, которую нам нужно запланировать при развертывании — обратите внимание на две вещи:

  1. Мы используем тег @reboot для планирования этого скрипта при перезагрузке.
  2. Мы используем >> /home/pi/battery-solar-rpi/logs/reboot_cron.log 2>&1 для направления стандартного вывода в файл журнала с именем reboot_cron.log в каталоге logs.

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

Мы пингуем github.com каждую секунду, пока не получим ответ 200, затем git pull последний код из репозитория battery-solar-rpi (строки 13–23 можно удалить, если вы не в сети). Затем мы ждем ответа 200 Health от локальной базы данных InfluxDB (иногда запуск на Raspberry Pi Zero может занять от 30 секунд до минуты), чтобы убедиться, что база данных готова для нас! Затем мы запускаем скрипт main.py Python3. Теперь мы в деле!

Reboot1.sh

Этот скрипт просто монтирует каталог диска Google, используя rclone.

Как и в случае с reboot.sh, стандартный вывод записывается в другой файл журнала reboot1_cron.log для облегчения диагностики. Также обратите внимание на немного отличающееся расписание Cron Job в строке 3.

Кроме этого, больше нечего сообщить! Мы рассмотрим rclone в части 4.

Полночь

Этот сценарий bash будет запущен в полночь, чтобы создать резервную копию всей базы данных на диске Google. Не волнуйтесь, после 10 месяцев измерений моя резервная копия по-прежнему составляет всего 37 МБ.

Здесь расписание Cron Job равно 0 0 * * *, что указывает на полночь, а журналы отправляются на midnight_cron.log.

После создания резервной копии InfluxDB (с флагом -portable для создания полной резервной копии) скрипт проверяет, сколько резервных копий находится в каталоге Influx-DB-Backup (для каждой резервной копии создается новый каталог).

Если их больше 2, самая старая резервная копия удаляется. Вот что делает эта строка: ls -lt | awk ‘{print $9}’ | tail -1 | xargs rm -r. Он перечисляет и сортирует содержимое каталога (ls), обрезает вывод (awk) и удаляет последнее из списка (tail, xargs). Вы можете изменить (или удалить) эту политику в зависимости от ваших требований.

После этого логи коммитятся в git.

Блин Бэш Бэш… Разобрался! (Извините)

Следующие шаги

Чудесно! Пожалуйста, выходите на воздух после такого глубокого погружения в код! Помните, что весь код доступен в репозитории GitHub, чтобы вы могли раскошелиться и пометить его сколько душе угодно. Молодцы, что справились с этим — давайте применим то, что мы узнали о физическом оборудовании и о том, как работает программное обеспечение, на следующем и последнем этапе — развертывании. Прежде чем вы уйдете, вот снова репозиторий кода:



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



Спасибо, что прочитали всю статью! Для меня очень важно, что вы нашли несколько минут своего дня, чтобы обдумать мои идеи :)

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



Хорошего дня!