Несколько абзацев про одноплатные компьютеры, встроенные системы и про DIY проекты :-) Можно пропустить и читать сразу про проект. Кто же давно собирался открыть для себя мир проектов на одноплатниках, embedded-программирование, работу с Linux или же искал учебные проекты для домашних занятий с ребёнком и открыл эту статью с мыслью "может это знак и пора начать" - то Вам стоит прочитать краткий обзор перед тем, как сделать свой выбор одноплатника, читать эту статью дальше по существу и начать собирать проект.
Если Вы хотите начать свой Путь в изучении Мира автоматики и телеметрии, начать практиковать Embedded-разработку, т.е. разрабатывать встраиваемые программно-аппаратные решения и собственные системы управления и сбора данных или же если Вы в этом дока и хотите найти готовое решение и материал для занятия со своими детьми, то эта статья для Вас. А ещё если Вы искали прикольные практические проекты с которыми хотели бы начать работать с одноплатными компьютерами даже просто как повод для изучения Linux, то этот материал так же Вам подходит. Сейчас в IT отрасли происходят такие масштабные изменения, что Embedded-разработку становится одним из наиболее перспективных и развивающихся направлений, открывающих Путь и в разработку промышленных и встраиваемых систем, а так же является специализацией в рамках которой разработчики получают широкий круг знаний и могут успешнее работать в любых других направлениях.
Небольшое вступление про мир одноплатных компьютеров
Одноплатник - часто используемое разговорное обозначение одноплатных компьютеров. Одноплатный компьютер (SBC, англ. single-board computer) - это такой вполне полноценный компьютер, только собранный на одной печатной плате, на которой установлены и микропроцессор и оперативная память и накопитель данных и всё прочее. Как правило одноплатные компьютеры имеют небольшие размеры и относительно небольшое энергопотребление и при этом меньшую производительность, чем большие и дорогие десктопные компьютеры. Одноплатники бывают разные - в качестве полноценных офисных и домашних персоналок (например, неттопы), тонких клиентов, всяких там демонстрационных систем. Или же в качестве систем для разработчиков или образования. А ещё для выполнения роли промышленных или встраиваемых компьютеров во всякие сложные и крутые устройства (от стиральных машин и умных контроллеров до дронов). И так повелось, что чаще всего одноплатники снабжаются ARM процессорами с RISC архитектурой, а это значит, что в основном для работы на них используется операционная система Linux. Но так уж получилось, что в последние годы этот сегмент процессоров значительно прибавил в производительности. Поэтому сейчас на современных одноплатных компьютерах можно учиться, работать со всякими там браузерами и редакторами, программировать и строить и запускать проекты - уж только для развлечения или и для обучения или вообще для создания какого то продукта и решения - это решать только Вам. Главное, что это стало очень удобно и доступно.
Часто одноплатники очень компактны, поэтому все компоненты должны располагаться на одной плате и могут работать только с пассивным охлаждением или с очень незначительным активным.
В целом одноплатные компьютеры можно разделить на две категории:
универсальные - для обучения, разработки, домашнего использования и "малой" автоматизации - они универсальны, доступны по стоимости и широко распространены,
специализированные - для промышленного использования - они максимально надёжны в работе, заточены под какие то типы задач и для работы только с пассивным охлаждением, но не очень удобны для широкого спектра задач и для разработки и имеют, как правило, стоимость в несколько раз выше чем универсальные одноплатники,
сверхмалые и особо компактные - для установки внутри приборов и дронов, имеют минимальные размеры и вес, меньшую производительность и не очень удобны для разработки и прототипирования.
Какое то время назад (больше десяти лет) известный бренд Raspberry Pi представил форм-фактор универсального одноплатного компьютера, ставший по сути отраслевым стандартом. Назовём его "формфактор raspberry" - в нём совместился компактный размер, удобное подключение стандартных HDMI монитора и USB клавиатуры и мыши, а так же просто огромный - 40 пиновый штырьковый разъём с доступными на нём промышленными интерфейсами и портами ввода вывода. Этот разъём позволяет подключать разные внешние устройства в виде всяких датчиков, исполнительных механизмов и устройств ввода/вывода информации. Этот стандарт "формфактора raspberry" стали использовать другие производители и теперь выбор удобных одноплатниклов большой.
На Российском рынке есть решения в этих формфакторах. Универсальные одноплатники: проект Repka Pi (Репка) и ELTAY (Алтай). Repka Pi на рынке уже три года, выпустила два поколения одноплатников и готовит третье, всё c документацией на русском языке, c разными примерами проектов, широкой экосистемой, доступными конкурентными ценами и с короткими сроками поставки. Его и берём для нашего первого проекта автоматики и телеметрии, так как доступен в продаже и в интернет магазине проекта и почти на всех популярных интернет магазинах, имеет небольшую стоимость, высокое качество, стабильную зрелую операционную систему на базе Linux Ubuntu с утилитами конфигурации и, что особенно важно, библиотеки для программирования управления портами ввода/вывода, что для нашей задачи особенно актуально.
Самое главное в универсальном формфакторе - чаще всего это 40 пиновый разъём - он даёт уникальные возможности всем, кто уже умеет, ещё учится или хочет научится программировать - вроде обычный компьютер и при этом к нему можно подключить всё что только захочется и можно программировать всё это как только вздумается. Получается, что становится доступной для самостоятельной разработки в домашних условиях любая автоматика. Вот такие проекты часто и называют DIY проектами, т.е. сделай сам (англ. DIY — do it yourself). Автоматика, робототехника, программирование и теперь ещё и работа с Linux - это в современном мире почти уже обязательная часть навыков часто уже не только для любого инженера, т.е. это то, чем можно решать кучу задач, чем можно зарабатывать и чем можно развлекаться и то, чему нужно обучать детей в роли сопутствующих навыков и знаний даже если они выберут совсем другие профессии.

Итак, к проекту!
Это первая статья цикла статей с примерами проектов на базе Repka Pi 4
У нас под рукой вышедшая в прошлом году вторая модель Repka Pi - это Repka Pi 4 (с номером 4 - видимо для условного сопоставления с модельным рядом Raspberry). Так как недавно проект анонсировал под него новые библиотеки для работы с интерфейсами 40-пинового разъёма - WiringRP и RepkaPi.GPIO и все примеры и документация оказались на очень высоком уровне - то мы выбираем его на роль одноплатника для нашего первого своего DIY проекта по автоматике, а точнее пока телеметрии. В качестве первого проекта будет проект условной метеостанции - он простой и лучше всего подойдёт в роли первого проекта. Для проекта берём датчик температуры, давления и влажности и самый простой экран для вывода цифро-символьной информации:

Содержание
О проекте
Наш первый проект - система для мониторинга основных показателей погодных условий в реальном времени или проект “Метеостанция”. Электрическая принципиальная и монтажная схема, исходные коды и подробное описание алгоритма работы будет раскрыты в данной статье, а так же инструкции для более простого монтажа в домашних условиях.
Построим пример такого проекта с использованием датчика BME280, который измеряет атмосферное давление, температуру и влажность воздуха, а отображение данных реализуем на ЖК дисплее 1602 I2C, а так же продублируем в монитор консоли одноплатника Repka Pi 4.
Все необходимые материалы и схемы подключения доступны в репозитории на платформе GitFlic.
Используемые в проекте компоненты:

1. Датчик атмосферного давления и температуры (BME280) см. рисунок 1. Измеряет атмосферное давление, температуру и влажность.

2. ЖК дисплей (1602 I2C). Отображает показания с датчика BME280 и позволяет пользователю видеть информацию о текущих погодных условиях.
Ссылки на все электронные модули и коммутационные компоненты (провода, шлейф 40 пин, переходник с 40 пин шлейфа на макетную плату) приведены в таблице ниже, на случай если Вам будет интересно посмотреть на их подробные характеристики и приобрести:
Компонент |
Ссылка на приобретение |
|---|---|
Монтажная/макетная плата |
|
Шлейф |
|
Переходник с шлейфа на макетную плату |
|
Соединительные провода |
|
Датчик атмосферного давления и температуры (BME280) |
|
ЖК дисплей (1602 I2C) |
Не обязательно использовать шлейф-удлинитель и Т-образный переходник, просто с ними намного удобнее выполнять монтаж, особенно если Вы планируете на этом проекте попробовать свои силы и Вам понравится и Вы решите расширить этот проект своими Идеями или же собрать другие проекты. Так удобнее вести прототипирование и разработку, особенно когда проект носит образовательный характер. Можно не использовать не только шлейф и переходник, а даже и макетную монтажную плату и просто всё коммутировать проводами напрямую. Но мы рассмотрим более громоздкий и при этом более удобный вариант с макетной платой.
Во время сборки проекта будем обращаться к электрической принципиальной и монтажной схемам, (рисунки 3 и 4). Эти схемы будут служить основным ориентиром на всех этапах подключения компонентов, обеспечивая точность и правильность сборки устройства проекта.
Электрическая принципиальная схема проекта

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

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

В этом случае используемый переходник в виде Т-образная коммутационной платы полностью повторяет конфигурацию распиновки 40-pin разъёма Repka Pi:

Подготовка макетной монтажной платы, источника питания и IDE для разработки ПО

Если Вы выберете вариант использования дополнительного источника питания, то в качестве источника питания можно просто приобрести ещё один блок питания для Repka Pi 4 c Type-C разъёмом, т.е. один штатный блок питания для самого одноплатника, а второй для питания схем автоматики и телеметрии. И у шнура второго блока питания просто отрезать разъём со стороны подключения к одноплатнику.

В идеале нужно затем провода зачистить и залудить или даже поставить и обжать монтажные разъёмы - мы за такой вариант.

Но если Вы в своём первом проекте выберете использование макетной платы, но на первый раз сделаете вот так, то мы Вас не осуждаем :-) но рекомендуем после реализации и отладки Вашего первого проекта вернуться к этому вопросу и сделать надёжно и как полагается. Если нужны будут ссылки на монтажные наконечники, то так же пишите в коментариях, добавим ссылки.

Еще вариант - использовать лабораторный блок питания, если он у Вас есть.

И конечно можно использовать аккумуляторы или батарейки.
Для разработки кода будет использоваться текстовый редактор Geany, который входит в состав стандартного ПО Репка ОС.
Сборка и проверка
1. Подключение датчика BME280.
Как видно из схем на рисунках 3 и 4, датчик подключается через интерфейс I2C и питается от 3.3V. 3.3 вольта мы берём с пина 40 пинового разъёма Репки, там есть и 3.3 и 5 вольт, питание датчика не требует большого потребления, так что можно взять с одноплатника.
1.1. Подключаем BME280 к макетной плате согласно таблице 1:
Макетная плата |
BME280 |
|---|---|
3.3V |
VIN |
GND |
GND |
SCL1 |
SCL |
SDA1 |
SDA |
Таблица 1. Подключение датчика BME280 к макетной плате.
1.2. Результат подключения будет выглядеть следующим образом, см. рисунок 5.1 для варианта с переходником, удлинителем и дополнительным источником питания и рисунок 5.2 для варианта без переходника, удлинителя и дополнительного источника питания:


2. Для проверки правильности подключения используем Python скрипт из репозитория repka-pi_iot-examples.
2.1. Клонируем репозиторий:
git clone git@gitflic.ru:repka_pi/repka-pi_iot-examples.git
2.2. Переходим в репозиторий:
cd repka-pi_iot-examples/
2.3. Выполним установку зависимостей:
make setup-bme280
2.4. Запускаем скрипт для проверки:
make bme280
2.5. Если на этапе 2.4. возникает ошибка, то измените в python скрипте, который находится по пути: /devices/sensors/BME280_example/py, номер шины – bus_number, если ошибок нет, то пропустите данный пункт.
# Укажите номер шины
bus_number = 1
# Инициализация I2C-шины
bus = SMBus(bus_number)
sensor = BME280(i2c_dev=bus) # Инициализация
2.6. Из рисунка 6 видим, что скрипт успешно выполнился, нам удалось считать показания с датчика.

3. Подключение ЖК дисплея (1602 I2C).
Из схем на рисунках 3 и 4 видно, что устройство подключается аналогично датчику BME280 по интерфейсу I2C, но питается уже от 5V. Экран может потреблять уже большие токи, так что его мы запитаем от дополнительного внешнего источника питания, подключенного к макетной плате.
3.1. Выполним подключение к макетной плате согласно таблице 2.
Макетная плата |
1602 I2C |
5V |
VCC |
GND |
GND |
SCL1 |
SCL |
SDA1 |
SDA |
Таблица 2. Подключение ЖК дисплея (1602 I2C) к макетной плате.
3.2. Результат подключения будет выглядеть следующим образом, см. рисунок 7.1 для варианта с переходником, удлинителем и дополнительным источником питания и рисунок 7.2 для варианта без переходника, удлинителя и дополнительного источника питания:


4. Аналогично пункту 2 выполним проверку подключения датчика:
4.1. Установим зависимости для ЖК дисплея (1602 I2C), выполнив:
make setup-display-1602-i2c
4.2. Выполните команду:
make display-1602-i2c
4.3. Как видно на рисунка 8 - датчик успешно подключен:

Запуск проекта
Теперь, когда все компоненты подключены, можно запустить проект "Метеостанция". Для этого в репозитории repka-pi_iot-examples выполняем команду:
make weather-station
Данные о температуре, давлении и влажности теперь отображаются на дисплее и выводятся в консоль, см. рисунки 9 и 10.



Программная реализация
Программное обеспечение для проекта "Метеостанция" отвечает за инициализацию датчиков, периодическое считывание метеорологических данных и их отображение в удобном для пользователя виде. В этом разделе мы подробно разберем логику работы программы и несколько подходов к реализации на языке Python и на языке C.
Исходные коды проекта можно найти в репозитории на платформе Gitflic.
Алгоритм работы
Логика работы метеостанции очень проста и представляет собой бесконечный цикл опроса и отображения данных. Алгоритм наглядно представлен на блок-схеме.

Описание алгоритма:
Старт и инициализация: При запуске программа первым делом инициализирует все необходимые аппаратные компоненты: устанавливает соединение по шине I2C с датчиком BME280 и LCD-дисплеем.
Начало цикла: Программа входит в бесконечный цикл для обеспечения непрерывной работы.
Сбор данных: На каждой итерации цикла происходит опрос датчика BME280 для получения актуальных значений температуры, атмосферного давления и влажности.
Вывод в консоль: Полученные данные форматируются и выводятся в консоль для отладки и мониторинга.
Вывод на дисплей: Экран LCD-дисплея очищается, после чего на него выводятся те же метеоданные в компактном и читаемом формате.
Пауза: Программа делает паузу на 4 секунды. Это необходимо, чтобы данные на экране не менялись слишком быстро и были удобны для восприятия, а также для снижения нагрузки на процессор.
Повторение: После паузы цикл начинается заново с пункта 3.
Завершение: Цикл прерывается пользователем (например, нажатием Ctrl+C). При этом программа корректно завершает работу, очищая дисплей.
Варианты программной реализации алгоритма
Рассмотрим реализации алгоритма на языках Python и С++. А так же два подхода к программированию - а) функциональный и б) объектно ориентированный (ООП).
Python идеально подходит для этого проекта, так как существующие библиотеки для датчика BME280 и дисплея LCD1602 берут на себя всю сложность низкоуровневого взаимодействия по протоколу I2C.
Функциональный подход на Python
Этот подход является самым прямолинейным. Вся программа представляет собой один скрипт, который выполняется последовательно сверху вниз. С него лучше и начать.
Импорты и обработка аргументов
В начале скрипта импортируются все необходимые библиотеки и настраивается парсер аргументов командной строки для гибкой конфигурации.
import time
import argparse
from smbus2 import SMBus
from rpi_bme280 import BME280
from RPLCD.i2c import CharLCD
# Используется парсер аргументов командной строки для задания номера I2C-шины и адреса дисплея:
parser = argparse.ArgumentParser(description="BME280 + LCD1602 I2C вывод в консоль и на дисплей")
parser.add_argument('--bus', type=int, default=0, help="I2C шина (по умолчанию 1)")
parser.add_argument('--lcd_addr', type=lambda x: int(x,0), default=0x27, help="Адрес LCD дисплея (например, 0x27)")
args = parser.parse_args()
Объяснение:
argparse: Стандартная библиотека Python для создания гибких программ, запускаемых из командной строки. Она позволяет пользователю при запуске указать параметры, например, номер шины I2C (--bus 1) или адрес дисплея (--lcd_addr 0x3f), если они отличаются от стандартных.smbus2,rpi_bme280,RPLCD.i2c: Специализированные библиотеки, которые предоставляют готовые и удобные инструменты для работы с устройствами по шине I2C.
Инициализация и основной цикл
После получения аргументов происходит инициализация устройств и запуск главного цикла работы.
# Инициализация I2C-шины, датчика BME280 и LCD-дисплея:
bus = SMBus(args.bus)
sensor = BME280(i2c_dev=bus)
lcd = CharLCD('PCF8574', args.lcd_addr)
try:
while True:
# Считывание значений
temperature = sensor.get_temperature()
pressure = sensor.get_pressure()
humidity = sensor.get_humidity()
# Вывод в консоль
print(f"Температура = {temperature:.2f} °C")
print(f"Давление = {pressure:.2f} hPa")
print(f"Влажность = {humidity:.2f} %")
print("-------------------------------")
# Вывод на дисплей
lcd.clear()
lcd.write_string(f"T:{temperature:.1f}C P:{pressure:.0f}hPa")
lcd.crlf() # Переход на новую строку
lcd.write_string(f"H:{humidity:.1f}%")
# Пауза
time.sleep(4)
except KeyboardInterrupt:
lcd.clear()
print("Завершение работы")
Объяснение:
Инициализация: Создаются экземпляры объектов
SMBus(для шины I2C),BME280(для датчика) иCharLCD(для дисплея). Библиотеки берут на себя всю "магию" настройки связи с устройствами.-
Основной цикл: Вся логика заключена в бесконечном цикле
while True, обернутом вtry...except.Чтение: Вызовы
sensor.get_temperature(),get_pressure()иget_humidity()обращаются к датчику по I2C и возвращают уже обработанные значения в нужных единицах измерения.Вывод: Данные выводятся в двух местах: в консоль с помощью
print()и на дисплей с помощью методовlcd.clear()иlcd.write_string(). Форматирование строк (f-strings) позволяет красиво отображать числа с нужным количеством знаков после запятой.Завершение: Блок
except KeyboardInterruptотлавливает нажатиеCtrl+C, позволяя программе завершиться корректно: очистить экран дисплея и вывести прощальное сообщение.
Объектно-ориентированный подход (ООП) на Python
Этот подход инкапсулирует всю логику работы метеостанции в единый класс WeatherStation. Это делает код более структурированным, удобным для расширения и повторного использования в других проектах.
Определение класса WeatherStation
Класс содержит методы для инициализации, чтения данных и их отображения.
class WeatherStation:
def __init__(self, bus_num=0, lcd_addr=0x27):
"""
Конструктор класса. Здесь происходит инициализация всех необходимых устройств.
"""
self.bus = SMBus(bus_num)
self.sensor = BME280(i2c_dev=self.bus)
self.lcd = CharLCD('PCF8574', lcd_addr)
def read_data(self):
"""
Считывает данные с датчика BME280.
Возвращает кортеж: (температура, давление, влажность).
"""
temperature = self.sensor.get_temperature()
pressure = self.sensor.get_pressure()
humidity = self.sensor.get_humidity()
return temperature, pressure, humidity
def display_data(self, temperature, pressure, humidity):
"""
Выводит данные на LCD-дисплей.
"""
self.lcd.clear()
self.lcd.write_string(f"T:{temperature:.1f}C P:{pressure:.0f}hPa")
self.lcd.crlf()
self.lcd.write_string(f"H:{humidity:.1f}%")
def print_data(self, temperature, pressure, humidity):
"""
Выводит данные в консоль.
"""
print(f"Температура = {temperature:.2f} °C")
# ... (остальные print'ы)
Объяснение:
init(Конструктор): Этот метод вызывается при создании объектаWeatherStation. Он выполняет ту же инициализацию, что и в функциональном подходе, но сохраняет объектыbus,sensorиlcdкак атрибуты экземпляра (черезself.).Разделение ответственности: Вместо одной большой логики в цикле, мы разделили ее на три метода:
read_dataотвечает только за чтение с датчика,display_data— только за вывод на экран, аprint_data— только за вывод в консоль. Это делает код гораздо чище и проще для понимания.
Основной цикл и точка входа
Метод run() инкапсулирует главный цикл, а блок if name == "__main__" запускает всю систему.
def run(self):
"""
Основной цикл работы станции.
"""
try:
while True:
temperature, pressure, humidity = self.read_data()
self.print_data(temperature, pressure, humidity)
self.display_data(temperature, pressure, humidity)
time.sleep(4)
except KeyboardInterrupt:
self.lcd.clear()
print("Завершение работы")
if __name__ == "__main__":
# Создаём экземпляр класса WeatherStation
station = WeatherStation(bus_num=1, lcd_addr=0x27)
# Запускаем основной цикл работы станции
station.run()
Объяснение: Модуль инкапсулирует в себе протокол управления дисплеем через I2C-расширитель PCF8574. Функции send_command и send_data отправляют на дисплей либо управляющие команды (очистить экран, переместить курсор), либо байты данных (коды символов для отображения). Функция lcd_print просто перебирает символы в строке и отправляет каждый из них на дисплей. Для main.c вся эта сложность скрыта за простыми и понятными вызовами функций. Объяснение:
run(): Этот метод является сердцем объекта. Он организует работу, вызывая другие методы класса (self.read_data(),self.print_data()и т.д.) в правильном порядке внутри бесконечного цикла.if name == "__main__": Эта стандартная конструкция Python позволяет файлу работать в двух режимах. Если вы запускаете этот файл напрямую, она выполнится, создаст объектWeatherStationи запустит его. Если же вы импортируете этот файл в другой проект, чтобы, например, использовать классWeatherStation, этот блок выполнен не будет. Это делает код идеально подходящим для повторного использования.
Функционально-модульная реализация на C и C++ c использованием библиотеки WiringRP
Язык C в связке с библиотекой WiringRP представляет собой классический подход для программирования встраиваемых систем. Он обеспечивает максимальную производительность, низкое потребление ресурсов и полный, прямой контроль над аппаратным обеспечением, что критически важно для надежной и предсказуемой работы устройств.
Данный проект демонстрирует профессиональный подход к разработке на C, используя модульную структуру. Для каждого аппаратного компонента создан свой "драйвер" в виде пары файлов: заголовочного (.h) и файла реализации (.c). Это делает код чистым, переиспользуемым и легким для отладки.
Структура проекта:
main.c: Содержит основную логику приложения, инициализацию всех модулей и главный цикл работы.bme280_driver/: Драйвер для чтения и обработки данных с датчика BME280.lcd1602_i2c/: Драйвер для управления LCD-дисплеем 1602 по шине I2C.
Основной файл логики (main.c)
Этот файл является точкой входа и "мозгом" всей системы. Он инициализирует все модули и реализует основной цикл опроса и отображения данных.
#include <stdio.h>
#include <stdlib.h>
#include <wiringrp/wiringRP.h>
#include <wiringrp/wire.h>
// Подключение заголовочных файлов всех модулей
#include "bme280_driver/bme280_driver.h"
#include "lcd1602_i2c/lcd1602_i2c.h"
// --- НАСТРОЙКА АДРЕСОВ ---
#define BME280_ADDR 0x76
#define LCD_ADDR 0x27
#define I2C_BUS_ID I2C1_BUS
void setup() {
printf("Запуск метеостанции...\n");
if (setupWiringRP(WRP_MODE_PHYS) < 0) exit(EXIT_FAILURE);
// Инициализация каждого аппаратного модуля с проверкой ошибок
if (bme280_init(I2C_BUS_ID, BME280_ADDR) < 0) exit(EXIT_FAILURE);
if (lcd_init(I2C_BUS_ID, LCD_ADDR) < 0) exit(EXIT_FAILURE);
printf("Метеостанция готова.\n");
lcd_print("Weather Station");
delay(2000);
}
void loop() {
BME280_Data sensor_data;
char line1_buffer[17];
char line2_buffer[17];
if (bme280_read_data(&sensor_data) == 0) {
// Вывод в консоль
printf("Температура: %.2f C, Давление: %.2f hPa, Влажность: %.2f %%\n",
sensor_data.temperature, sensor_data.pressure, sensor_data.humidity);
// Форматирование строк и вывод на LCD
snprintf(line1_buffer, 17, "T:%.1fC P:%.0fhPa", sensor_data.temperature, sensor_data.pressure);
snprintf(line2_buffer, 17, "H: %.1f %%", sensor_data.humidity);
lcd_clear();
lcd_print(line1_buffer);
lcd_set_cursor(0, 1);
lcd_print(line2_buffer);
} else {
lcd_clear();
lcd_print("Sensor Error!");
}
delay(4000);
}
ONDESTROY() {
printf("\nЗавершение работы.\n");
lcd_clear();
bme280_release();
lcd_release();
releaseWiringRP();
exit(0);
}
MAIN_WIRINGRP();
Объяснение:
Структура
setup()иloop(): БиблиотекаWiringRPпредоставляет макросыMAIN_WIRINGRP(),setup()иloop(), которые эмулируют привычную и удобную среду программирования Arduino. Код вsetup()выполняется один раз при старте для инициализации всех систем, аloop()— в бесконечном цикле.Инициализация: В
setup()последовательно вызываются функции..._init()из каждого модуля, передавая им необходимые параметры (шину I2C и адрес устройства). При этом результат каждой функции проверяется, и в случае ошибки программа завершается с информативным сообщением.Основной цикл
loop(): Здесь реализована вся логика, описанная в блок-схеме. Каждые 4 секунды (delay(4000)) с помощью функцииbme280_read_dataсчитываются данные в структуруsensor_data. Затем они форматируются в строки с помощьюsnprintfи выводятся как в консоль, так и на LCD-дисплей.Безопасное завершение
ONDESTROY(): Этот макрос регистрирует функцию, которая будет вызвана при завершении программы (например, поCtrl+C). Это гарантирует, что дисплей будет очищен, а все аппаратные ресурсы — корректно освобождены.
Модуль датчика BME280 (bme280_driver.c)
Это самый сложный и интересный модуль, который берет на себя всю работу по взаимодействию с датчиком и преобразованию его "сырых" данных в реальные физические величины.
// Фрагмент из bme280_driver.c
#include "bme280_driver.h"
#include <wiringrp/wiringRP.h>
#include <wiringrp/wire.h>
#include <math.h>
// ... (объявление статических переменных для калибровочных коэффициентов) ...
static int32_t t_fine;
static void read_calibration_data() {
// ... (чтение ~24 байт калибровочных данных с датчика) ...
}
static float compensate_temperature(int32_t adc_T) {
// ... (сложные математические расчеты по формулам из документации) ...
}
static float compensate_pressure(int32_t adc_P) {
// ... (сложные математические расчеты по формулам из документации) ...
}
static float compensate_humidity(int32_t adc_H) {
// ... (сложные математические расчеты по формулам из документации) ...
}
int bme280_init(int i2c_bus, int addr) {
// ... (инициализация I2C, проверка Chip ID) ...
read_calibration_data(); // Считываем уникальные для каждого датчика коэффициенты
// ... (настройка режимов работы датчика) ...
return 0;
}
int bme280_read_data(BME280_Data* data) {
// ... (чтение 8 байт "сырых" данных температуры, давления и влажности) ...
// Передаем сырые данные в функции компенсации
data->temperature = compensate_temperature(adc_T);
data->pressure = compensate_pressure(adc_P);
data->humidity = compensate_humidity(adc_H);
return 0;
}
Объяснение: Датчик BME280 — это сложное устройство. Он не выдает готовые значения в градусах Цельсия или гектопаскалях. Вместо этого он возвращает "сырые" цифровые значения (adc_T, adc_P, adc_H).
Калибровка: При производстве в каждый чип BME280 записываются уникальные калибровочные коэффициенты. Функция
read_calibration_dataсчитывает их один раз при инициализации и сохраняет в статических переменных модуля.Компенсация: Функции
compensate_*реализуют сложные математические формулы, предоставленные производителем в документации (datasheet). Эти формулы используют "сырые" данные и уникальные калибровочные коэффициенты для вычисления точных физических величин.bme280_read_data: Эта функция, вызываемая изmain.c, считывает 8 байт сырых данных с датчика и пропускает их через функции компенсации, записывая итоговый результат в переданную по указателю структуруBME280_Data.
Модуль LCD-дисплея (lcd1602_i2c.c)
Этот драйвер предоставляет простой интерфейс для вывода текста на стандартный символьный дисплей 16x2.
// Фрагмент из lcd1602_i2c.c
#include "lcd1602_i2c.h"
#include <wiringrp/wiringRP.h>
#include <wiringrp/wire.h>
static void send_command(int command) { /* ... */ }
static void send_data(int data) { /* ... */ }
int lcd_init(int i2c_bus, int addr) {
// ... (инициализация I2C) ...
send_command(0x28); // Установка режима: 4-битный интерфейс, 2 строки
send_command(0x0C); // Включить дисплей без курсора
// ... (другие команды инициализации) ...
return 0;
}
void lcd_print(const char* str) {
for (size_t i = 0; i < strlen(str); i++) {
send_data(str[i]);
}
}
Гибридный подход к реализации (Python + WiringRP)
Этот мощный подход сочетает в себе простоту и гибкость Python для написания основной логики и высокую производительность и прямой доступ к аппаратуре C для выполнения низкоуровневых операций.
Вместо использования Python-библиотек для I2C (smbus2, periphery), мы будем вызывать функции I2C напрямую из скомпилированной C-библиотеки libwiringrp.so. Это позволяет полностью контролировать процесс обмена данными и избежать накладных расходов Python-оберток. Для этого мы напишем на Python собственные классы-драйверы, которые "под капотом" будут использовать C-функции.
1. Загрузка C-библиотеки и определение функций
Первым шагом является загрузка общей библиотеки libwiringrp.so в Python-скрипт с помощью стандартного модуля ctypes и описание прототипов C-функций, которые мы будем использовать.
import ctypes
# Загрузка библиотеки
try:
wiringrp = ctypes.CDLL("libwiringrp.so")
except OSError:
print("Ошибка: Не удалось найти 'libwiringrp.so'.")
exit(1)
# Определение прототипов I2C и временных функций
wiringrp.i2cSetup.argtypes = [ctypes.c_int, ctypes.c_int]
wiringrp.i2cSetup.restype = ctypes.c_int
wiringrp.i2cReadReg8.argtypes = [ctypes.c_int, ctypes.c_int]
wiringrp.i2cReadReg8.restype = ctypes.c_int
wiringrp.i2cWriteReg8.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_int]
# ... и так далее для всех нужных функций ...
Объяснение:
ctypes.CDLL("libwiringrp.so")загружает C-библиотеку в память и предоставляет объектwiringrp, через который можно обращаться к ее функциям.Определение прототипов (
argtypes,restype): Это критически важный шаг. Мы явно указываем Python, какие типы данных (c_int,c_uint) ожидает каждая C-функция и какой тип данных она возвращает. Это гарантирует корректную и безопасную передачу данных между двумя языками.
2. Python-драйвер для LCD1602 на базе WiringRP
Мы создаем Python-класс, который полностью повторяет логику C-драйвера, но вместо прямых вызовов i2cWrite использует их эквиваленты из wiringrp, вызванные через ctypes.
class LcdI2c:
def __init__(self, fd):
self.fd = fd # файловый дескриптор, полученный от wiringrp.i2cSetup()
# ... (команды инициализации дисплея) ...
def _i2c_write(self, value):
# Прямой вызов C-функции для записи байта по I2C
wiringrp.i2cWriteReg8(self.fd, 0, value | self.backlight_val)
def _pulse_enable(self, value):
# Эмуляция "моргания" пином Enable для отправки данных
self._i2c_write(value | 0x04)
time.sleep(0.0005)
self._i2c_write(value & ~0x04)
time.sleep(0.0005)
def write_string(self, text):
for char in text:
self._write_char(ord(char))
Объяснение: Этот класс инкапсулирует всю сложность управления дисплеем. Он принимает в конструкторе fd — файловый дескриптор, который является результатом работы C-функции wiringrp.i2cSetup(). Все методы, такие как i2cwrite, теперь напрямую вызывают функции из libwiringrp.so, обеспечивая низкоуровневый контроль над шиной I2C из Python-кода.
3. Python-драйвер для BME280 на базе WiringRP
Аналогично создается драйвер для датчика BME280, который также использует C-функции для чтения и записи регистров.
class BME280:
def __init__(self, fd):
self.fd = fd
self.cal_data = {}
# ... (инициализация) ...
def _read_s16_le(self, reg):
# Чтение 16-битного знакового числа (little-endian)
lsb = wiringrp.i2cReadReg8(self.fd, reg)
msb = wiringrp.i2cReadReg8(self.fd, reg + 1)
val = (msb << 8) | lsb
return ctypes.c_int16(val).value
def _read_calibration(self):
# Чтение калибровочных данных с помощью _read_s16_le и i2cReadReg8
self.cal_data['dig_T1'] = self._read_u16_le(0x88)
# ... (чтение остальных коэффициентов) ...
def compensate_temp(self, raw_t):
# ... (математика компенсации, полностью на Python) ...
Объяснение: Этот класс также полностью скрывает сложность работы с датчиком. Он использует wiringrp.i2cReadReg8 для чтения байтов с шины I2C, считывает калибровочные данные при инициализации, а затем выполняет математические расчеты для компенсации температуры, давления и влажности уже средствами Python.
4. Основная программа
Главная часть программы теперь использует наши Python-драйверы, которые работают на "движке" из C.
def main(bus_num, lcd_addr, bme_addr):
try:
# Инициализируем WiringRP
if wiringrp.setupWiringRP(0) < 0:
raise RuntimeError("Ошибка инициализации WiringRP.")
# Получаем файловые дескрипторы от C-библиотеки
lcd_fd = wiringrp.i2cSetup(bus_num, lcd_addr)
bme_fd = wiringrp.i2cSetup(bus_num, bme_addr)
# Создаем экземпляры наших Python-драйверов
lcd = LcdI2c(lcd_fd)
sensor = BME280(bme_fd)
while True:
# Работаем с объектами, как с обычными Python-объектами
raw_t, raw_p, raw_h = sensor.read_raw_data()
temperature = sensor.compensate_temp(raw_t)
# ...
lcd.write_string(f"T:{temperature:.1f}C")
time.sleep(4)
finally:
# Освобождаем ресурсы, вызванные в C
if lcd_fd >= 0: wiringrp.i2cRelease(lcd_fd)
if bme_fd >= 0: wiringrp.i2cRelease(bme_fd)
wiringrp.releaseWiringRP()
Объяснение:
Инициализация:
mainсначала инициализируетWiringRP, а затем получает от нее файловые дескрипторы для каждого I2C-устройства.Создание объектов: Эти дескрипторы передаются в конструкторы наших Python-классов
LcdI2cиBME280.Основной цикл: Вся логика внутри
while Trueостается высокоуровневой и читаемой. Мы работаем с методами наших классов (sensor.read_raw_data(),lcd.write_string()), даже не задумываясь, что под капотом они вызывают быстрый C-код.Очистка: В блоке
finallyмы корректно освобождаем все ресурсы, включая дескрипторы, полученные отWiringRP.
Вывод: Этот гибридный подход — самый гибкий. Он позволяет писать драйверы для устройств на Python, используя всю мощь и синтаксический сахар языка, но при этом полагаться на производительность и точность низкоуровневых C-функций для непосредственного взаимодействия с оборудованием.
Сравнение производительности: RepkaPi.GPIO (SysFS) vs WiringRP
В рамках наших проектов мы использовали два разных подхода для взаимодействия с GPIO-пинами:
Python с библиотекой
RepkaPi.GPIO: Высокоуровневый подход, работающий через стандартный интерфейс ядра Linux SysFS.C с библиотекой
WiringRP: Низкоуровневый подход, работающий максимально близко к "железу" через прямой доступ к памяти.
Возникает логичный вопрос: насколько велика разница в производительности и когда какой подход следует выбирать? Для ответа на этот вопрос был проведен объективный тест — бенчмарк.
Методика тестирования
Чтобы измерить чистую скорость работы с GPIO, была поставлена простая задача: переключать один и тот же GPIO-пин из высокого состояния (HIGH) в низкое (LOW) и обратно так быстро, как это возможно, в течение 5 секунд. Эта операция "включить-выключить" является фундаментальной для любого проекта, работающего с GPIO, и ее скорость напрямую отражает эффективность используемой библиотеки.
Были написаны два минималистичных скрипта, реализующих этот тест.
Код на C с WiringRP
// benchmark_c_counter.c
#include <wiringrp/wiringRP.h>
#include <stdio.h>
#include <time.h>
#define TEST_PIN 7
#define BENCHMARK_DURATION 5
int main(void) {
unsigned long long counter = 0;
time_t start_time = time(NULL);
if (setupWiringRP(WRP_MODE_PHYS) < 0) return 1;
pinMode(TEST_PIN, OUTPUT);
while (1) {
digitalWrite(TEST_PIN, HIGH);
digitalWrite(TEST_PIN, LOW);
counter++;
if (time(NULL) - start_time >= BENCHMARK_DURATION) {
break;
}
}
printf("Операций в секунду: %llu ops/sec\n", counter / BENCHMARK_DURATION);
return 0;
}
Код на Python с RepkaPi.GPIO
# benchmark_c_counter.py
import RepkaPi.GPIO as GPIO
import time
TEST_PIN = 7
BENCHMARK_DURATION = 5
GPIO.setmode(GPIO.BOARD)
GPIO.setup(TEST_PIN, GPIO.OUT)
counter = 0
start_time = time.time()
try:
while True:
GPIO.output(TEST_PIN, GPIO.HIGH)
GPIO.output(TEST_PIN, GPIO.LOW)
counter += 1
if time.time() - start_time >= BENCHMARK_DURATION:
break
finally:
GPIO.cleanup()
print(f"Операций в секунду: {counter // BENCHMARK_DURATION} ops/sec")
Результаты
После компиляции C-кода и запуска обоих скриптов на Repka Pi были получены следующие результаты:
Подход |
Операций в секунду (ops/sec) |
|---|---|
Python + RepkaPi.GPIO (SysFS) |
~6,679 |
C + WiringRP (прямой доступ) |
~484,638 |
Анализ результатов
Как видно из таблицы, разница в производительности колоссальна: подход на C с использованием WiringRP оказался примерно в 72 раза быстрее, чем его аналог на Python. Эта разница обусловлена фундаментальными различиями в том, как эти библиотеки взаимодействуют с оборудованием.
-
WiringRP(C):Компилируемый язык: Код на C преобразуется в нативные машинные инструкции, которые выполняются процессором напрямую без посредников.
Прямой доступ к памяти (
/dev/mem):WiringRPизменяет состояние GPIO-пина путем прямой записи нужных значений в физические адреса памяти, где расположены регистры управления GPIO. С точки зрения системы, это одна быстрая операция записи в память.
-
RepkaPi.GPIO(Python):Интерпретируемый язык: Код на Python выполняется через интерпретатор, который добавляет свои, хоть и небольшие, накладные расходы.
-
Интерфейс SysFS: Это ключевое отличие. Библиотека
RepkaPi.GPIOработает через стандартный для Linux интерфейс SysFS. Для операционной системы GPIO-пины представлены в виде файлов в директории/sys/class/gpio/. Чтобы изменить состояние пина, библиотека выполняет целую последовательность действий:Отправляет системный вызов на открытие файла (например,
/sys/class/gpio/gpio7/value).Отправляет системный вызов на запись в этот файл символа "1" или "0".
Отправляет системный вызов на закрытие файла.
Каждый такой системный вызов требует переключения контекста между пользовательским пространством (где работает наш скрипт) и пространством ядра, что является относительно медленной операцией. Таким образом, одно простое действие GPIO.output() на самом деле порождает целую цепочку более медленных файловых операций.
Выводы
Означают ли эти результаты, что RepkaPi.GPIO — плохая библиотека? Однозначно нет. Выбор инструмента всегда зависит от задачи.
Когда использовать Python и
RepkaPi.GPIO? Почти всегда. Во всех проектах, которые мы рассмотрели (метеостанция, система полива, парктроник, RFID-сейф), основной цикл программы имеет задержку от сотен миллисекунд до нескольких секунд (time.sleep(1)). На фоне таких задержек разница в скорости выполненияGPIO.output()в несколько микросекунд абсолютно несущественна. Преимущества Python — скорость разработки, простота отладки, читаемость кода и огромное количество готовых библиотек — многократно перевешивают проигрыш в "чистой" производительности GPIO.-
Когда использовать C и
WiringRP?WiringRPи C становятся незаменимы, когда требуется работа в реальном времени или генерация высокочастотных сигналов. Например:Программная реализация протоколов связи (например, "bit-banging" I2C или SPI).
Управление устройствами, требующими очень точных и коротких импульсов, недостижимых с помощью
time.sleep().Приложения, где критически важна минимальная и предсказуемая задержка реакции на событие.
Итог: Для подавляющего большинства образовательных и хобби-проектов удобство и скорость разработки на Python с RepkaPi.GPIO являются предпочтительным выбором. К мощи и производительности C и WiringRP следует обращаться тогда, когда вы точно знаете, что ваш проект упирается в пределы скорости или точности, которые может предоставить Python.
Итоги
Связка одноплатного компьютера с компонентами различных устройств в виде датчиков и исполнительных механизмов определённо является отличным образовательным инструментом, позволяющим изучать работу с датчиками, интерфейсами и осваивать программирование.
Проект легко расширяется, что открывает возможности для интеграции дополнительных датчиков, таких как измерители осадков или качества воздуха. Эти дополнения могут сделать систему более мощной для более точного мониторинга окружающей среды. Более того, возможность удаленного мониторинга через интернет или мобильные устройства позволяет собирать данные в реальном времени, что удобно для использования.
Проект "Метеостанция" можно значительно расширить, добавив новые функциональные возможности. Например, можно интегрировать дополнительные датчики, такие как:
Датчики для измерения уровня осадков — это позволит получить полную картину погодных условий, включая дождевые осадки.
Датчик UV-излучения — для измерения уровня ультрафиолетового излучения, что может быть полезно для контроля солнечной активности.
Сенсоры качества воздуха — для мониторинга загрязнения воздуха в реальном времени.
Глобальная система позиционирования (GPS) — для отслеживания точных координат метеостанции и анализа данных по географическому расположению.
Комментарии (26)

GennPen
30.08.2025 04:26Электрическая принципиальная схема проекта
Подключение по i2c без подтяжки?

randomsimplenumber
30.08.2025 04:26Подтяжка, скорее всего, внутри датчика.

GennPen
30.08.2025 04:26Это фиговое решение. Подтяжка всегда должна быть внешней.

select26
30.08.2025 04:26При этом два устройства: одно питается от 3.3В, а второе от 5В.
Куда подтягивает встроенная подтяжка?
А уж про диаграмму я молчу...

randomsimplenumber
30.08.2025 04:26Обучающий проект. (Пожимает плечами)

select26
30.08.2025 04:26Чему обучающий? Как делать не нужно?
Зачем делать плохо, если можно сделать хорошо?

BSOZ
30.08.2025 04:26А если устройств несколько? Это же шина. В автомобилях CAN последних лет так же организован, правда не подтяжка в каждом устройстве, а терминатор убрали. И это привело к тому, что с надёжностью стало очень плохо. И использовать её полноценно как шину стало практически невозможно, что частично нивелировано наличием хаба. Т.е. CAN придумали, чтобы избавить автомобили от километров проводов и разъёмов размером с кулак, но к тому и вернулись. В новой итерации решили перейти на Ethernet. Но и его найдут как испоганить.

RyabovA Автор
30.08.2025 04:26Сейчас на многих микроконтроллерах и тем более на серьёзных микропроцессорах для всех портов ввода-вывода, как для I/O и тем более для более высокоуровневых интерфейсов типа I2C, в т.ч. использующих дифференциальные пары, обязательно реализуется внутренняя подтяжка в самом. Более того, в портах I/O для комплексных интерфейсов (GPIO) для уровней напряжения задаются програмно явно или используются значения по умолчанию логических значений 0 (Pull-Down) и 1 (Pull-Up). Есть, конечно, порты у которых есть третье значение - на низком уровне для управления через регистры это GPIO_PuPd_NOPULL — значение, которое означает «нет подтяжки», то есть нет ни pull-up, ни pull-down, это бывает по какой то причине нужно по замыслу разработчика, особенно если как раз реализуется своя схема с большими токами или с разными уровнями напряжений логических значений и нужно делать свои подтяжки и преобразования уровней или ещё какой то хитрый замысел разработчиков проекта. Но это и не на всех портах есть и нужно смотреть значения по умолчанию для конкретных портов и на то, как реализовано в используемых библиотеках в момент их инициализации и установки состояния портов перед началом работы.
Т.е. в современной цифровой электронике, особенно именно микропроцессорной, т.е. на одноплатных компьютерах, данный уровень абстракции давно выделяется отдельно на уровне реализации контроллеров портов как IP-блоков процессора/контроллера или на уровне реализации драйверов и/или библиотек, предоставляющих API для взаимодействия с портами и интерфейсами. Задача цифровой электроники - предоставить возможность сосредоточится на решении высокоуровневых задач, компоновке и логике алгоритмов работы, а не схемотехнических решениях на уровне электротехники. Вот если Вы поставите задачу написать статью и обучать разработке на уровне транзисторной логики (ТТЛ), показать как делать логические элементы на транзисторах или на логических элементах, создавай свои порты ввода-вывода и реализуя для них обвязку в т.ч. с подтяжками, то тогда да, нужно про это рассказывать и показывать на схемах, но это задача другая и она или про базу или про сложные гибридные проекты, где логика частично реализуется на своих аппаратных схемах и только уже принятые ей решения передаются портам контроллеров и процессоров. И это уже точно не про embedded разработку, а про схемотехнику. Данный же материал а) про mbedded-программирование и б) для старта и изучения разработки встроенных решений с упором на программную часть реализации. Так что если бы Вы выразили замечания по структуре и полноте материала в части программной реализации - с Вами можно и нужно было бы согласиться, так как вижу, что эта часть материала тяжеловата и не достаточно чётко структурирована и для новичков вообще нужно добавить кое какие справочные данные и дополнительные инструкции. И это будет сделано в формате редактирования и расширения материала, но сейчас как есть, хотелось к выходным успеть опубликовать, может кто то задумает провести выходные с познавательным проектом сам или вместе с детьми.

randomsimplenumber
30.08.2025 04:26Правильный ответ - читайте datasheet. Там есть и про значение подтяжек в зависимости от скорости, и про величину встроенных подтяжек.

evil_HFT
30.08.2025 04:26Только встроенная подтяжка не всегда соответствует той, что должна быть по стандарту

RyabovA Автор
30.08.2025 04:26Как выше уже сказал @randomsimplenumber читайте datasheet и проверяйте, если не будет как нужно, то не пользуйтесь этими процессорами и контроллерами :-) или тогда уже делайте свою подтяжку, а если соответствует, то Ок, в данном случае соответствует и потому схема в проекте статье тоже нормальная и потому, собственно, всё работает без нареканий. Но в целом то маслом каши не испортить :-)

ksoone
30.08.2025 04:26Спасибо за проделанную работу! Как статья так и проект собраны на интуитивном уровне с идеальной последовательностью, приятно и удобно изучать. Будем попробовать. :)

RyabovA Автор
30.08.2025 04:26Спасибо Вам за отзыв по существу :-) Вы верно подметили - в таких материалах важно правильно выстроить структуру и подачу. Не всё идеально пока по части программной реализации, но для самостоятельного повторения, изучения и расширения уже достаточно. В ближайшее время доработаю эту часть статьи, чтобы было ещё удобнее и понятнее. Очень хотелось сделать материал понятным и хотя бы кому то полезным или для самообразования или для интересного проведения времени или для занятий с детьми.

ddv2005
30.08.2025 04:26Во-первых, это проект для микроконтроллеров, а не для одноплатников. Ну а самое главное, что перейдя с микроконтроллера на одноплатник вы забыли сделать самое главное без чего проект никогда не станет законченным - разработка и генерация системы. На микроконтроллере система встроена в ваше приложение и есть много библиотек которые позволяют настроить IP адрес, сделать примитивный WEB интерфейс для управления, сделать обновление и в конце получить законченное устройство которое реально будет работать годами. Есть даже системы типа ESPHome которые позволяют забыть об этом и сосредоточится только на функционале. А на одноплатнике система отдельно, приложение отдельно. Для законченного устройства нужно чтобы основная система была только для чтения (иначе вы убъете любой накопитель), если нужна сеть то требуется сделать систему управления по WEB и возможность настройки IP адреса, далее нужно удалить все не нужные сервисы и запустить свое приложение как сервис, ну и наконец продумать систему генерации образов и обновления. Без всего этого ваш проект на одноплатнике так и останется глючной недоделкой, требующей постоянного обслуживания по SSH. При этом не существует ни одной открытой реализации всего вышеописанного для одноплатников.

RyabovA Автор
30.08.2025 04:26У контроллеров и у процессорных систем есть свои достоинства и недостатки. Выбор стека может быть разный в разных задачах. В любом роутере сейчас стоит процессорная система с Linux на борту, работает годами и законченное устройство. При этом качественно другой уровень возможностей. Другое дело, что энергопотребление повыше и это не везде подходит, а так же у процессорных систем и у контроллеров всё же разные итоговые стоимости в продукте. И это не Хорошо и не плохо, это разные инструменты и работать нужно уметь и с тем и с тем. И этот материал для старту обучения работе с процессорными системами. Что касается Linux для embedded систем - да вот хотя бы Вам пример российского проекта NapiLinux вот сайт проекта
загружается за три секунды, поставьте на EMMC, настраивайте через Веб или через API, из коробки система обновлений. Цитата: «NapiLinux — минималистичная и отказоустойчивая Linux‑среда, оптимизированная под встраиваемые устройства с ограниченными ресурсами и длительным сроком эксплуатации.» И есть другие варианты, просто этот Российский и портирован на Repka Pi.
Я не газую только за одноплатники или только за контроллеры. И то и то прекрасно может решать свои задачи. Тут вопрос скорее погруженности и как ручки из плеч растут и каким местом инженеры думают. Реально хороший embedded разработчик должен одинаково успешно уметь работать и с тем и с другим. Просто с контроллерами порог вхождения ниже и потому тема изъезженная и хорошо раскрытая, а вот с процессорными системами уделяется меньше внимания и потому с автоматикой и робототехникой есть вопросы в промышленности в стране, нужно это исправлять и обучать, для этого данный материал и создан.
Так что как когда то говорили на одном известном радио - "если Вы не любите кошек, то значит Вы просто не умеете их готовить". Так что что тут спорить и холиварить. Нужно и на том и на том делать больше материалов и проектов, а где уместно, там вообще гибридные решения строить.
А ещё есть информация, что команда проекта Repka Pi собирается портировать на свои одноплдатники FreeRTOS, вот это будет пушка для встраиваемых проектов с потребностями и амбициями - разработка как на контроллеры, а производительность и возможности одноплатников с многоядерностью и реальной многопоточностью только с настоящим real-time и плюшками в виде сети, большой оперативной памяти, хранилищем, мониторами, мышками и прочими клавиатурами из коробки. Но это уже другая история, будем посмотреть.

ddv2005
30.08.2025 04:26Я совершенно не против одноплатников, а даже наоборот я начал свой путь в embedded именно с первого raspberry pi и активно учавствовал в доведении до ума его драйверов USB, SD и т.п. Мои претензии были именно к статье потому что проект, хоть и демо, выбран не правильно и рассказ идет как будто бы автор программирует микроконтроллер, а не делает систему на полноценном Linux. Автор заявляет что сделал примитивный, но полноценный проект метеостанции, а на деле оказалось что это какой то обрубок обучающих скриптов для ручной демонстрации студентам в доме пионеров. Метиостанция - это устройство, которое пользователь воткнул в розетку и оно работает, а не набор скриптов, которые надо запускать на Linux по SSH или через консоль.

randomsimplenumber
30.08.2025 04:26Ну и метеостанция должна как то обрабатывать показания датчиков. Иначе это термометр с линуксом на борту.

RyabovA Автор
30.08.2025 04:26Строго говоря вот что ИИ поисковика выдал на запрос "что такое домашняя метеостанция"
Домашняя метеостанция — это устройство для мониторинга микроклимата в доме и окружающей среды, помогающее предсказывать погодные условия. Оно совмещает функции термометра, гигрометра, барометра, а иногда — датчика качества воздуха. Современные метеостанции оснащают сенсорами, которые собирают информацию, и дисплеем или мобильным приложением для удобного отображения данных.
Т.е. в этом минимальном объёме пример и показан - есть датчики базовых показателей и есть монитор для вывода информации.
Остальное конечно прикольно сделать - отправка информации, расширенный набор датчиков (особенно если добавить датчики газа, горючего и угарного, например), добавит сигнализирование о выходе параметров за заданные граница и т.д. Но это уже, наверное, то что можно фантазировать почти до бесконечности и то, что можно делать по аналогии самостоятельно, расширяя и улучшая базовый пример.

RyabovA Автор
30.08.2025 04:26Тогда понятно о чём вы говорите - чтобы сделать полноценный продукт, т.е. что то типа домашней SCADA на одноплатнике или корпусной вариант метеостанции на контроллере. Тогда это будет не статья, а учебник и не для старта, а уже как профи-мастер класс для студетов старших курсов технических вузов или практика начинающих карьеру специалистов. В данной статье ставил задачу немного иную - когда уровень проекта начального среднего уровня, когда можно самому или с ребёнком позаниматься и есть поле для самостоятельного творческого развития - добавить вебку для вывода данных, добавить отправку данных по смс или в телеграм, добавить исполнительные механизмы и т.п.
А тут странное дело получается - пришёл после работы в пятницу и до глубокой ночи писал статью на базе ранее имевшихся образовательных наработок, чтобы сделать кому то может быть полезный материал на выходные. И потом налетают профессиональные комментаторы и критики, причём больше всего критикуют те, кто сам ни одной статьи не написал почему то.
В целом если среди этой критики выбрать конструктивные предложения и пожелания, то они вот такие, видимо - показать интеграцию с Веб интерфейсом для вывода дынных, добавить систему оповещения по СМС и через телеграм-бота, показать аналогичную реализацию на контроллере, корпусизировать исполнение на контроллере для получение законченного устройства, связать контроллер с одноплатником, сделать на одноплатнике сервер приложения для настройки и вывода данных, добавить датчиков и исполнительных механизмов, добавить помимо "метеостанции" ещё устройств разного назначения и связать всё это в единый центр управления "умным домом" на базе одноплатника.
Рекомендации и пожелания в таком виде понятны, попробуем сделать такой цикл в качестве продолжения к данному материалу. Понятно, что это изобретение велосипеда, так как есть прекрасные готовые решения типа Home Assistent со шлюзами и датчиками и энергосберегающем протоколе Zigbee , но тут обучающая задача чтобы делать своими руками и осваивать технологии, в т.ч. сквозные. Если интересно такое продолжение - пишите! Хотелось бы понять, нужно ли и интересно ли это кому то, чтобы понять, стоит ли тратить на это время и силы.

randomsimplenumber
30.08.2025 04:26Не так.. target group непонятна. Кто, по вашему, читатель статьи? Чувак, который зачем-то купил одноплатник, но не знает что с ним делать ?
больше всего критикуют те, кто сам ни одной статьи не написал почему то.
Чтобы критиковать яичницу, не обязательно быть курицей ;)

SpLab
30.08.2025 04:26Вы зря передергиваете и истерите. Вам сделали замечание по сути. Эмбиддед это система в целом, а не отдельный ее модуль. Ее результатом должна быть сборка прошивки. И раз уж изучение эмбиддед, то необходимо уделить и внимание сборке системы. Не надо сразу с крутыми вэбинтерфейсами и интеграциями. Должна быть самодостаточная сборка с возможностью минимального необходимого конфигурирования.
Ну и да, хотелось сразу написать о стрельбе из пушки по воробьям, но по сути для обучения решаемая задача не важна, а важно показать весь процесс ее решения. А вот этого как раз нету.
Надеюсь Вы адекватно примите конструктивную критику, а не начнете оправдываться. Возможно нужно было не до поздна в пятницу после работы писать пост, а спокойно в течении недели доработать материал и выпустить качественную, полную статью или цикл.

RyabovA Автор
30.08.2025 04:26Сударь, извольте перестать разговаривать в таком тоне, скрывая за высокоменрными плохо прикрытыми оскорблениями свои поучения. Иначе я вызову вас на дуэль :-) Наверное так ответ бы звучал двести лет назад. Сейчас же нужно говорить иначе :-) Но я против бранных слов. Так что так - зря вы в таких выражениях и в таком духе пытаетесь донести свою мысль, вы ошибаетесь в своих предположениях. По существу же вопроса ваша мысль понятна.
Сборка системы никому ничего не должна, специально узнавал ;-) а кому была должна, всем всё простила.
По существу же Ваших предложений - обучение сборке системы штука хорошая, спору нет, но, на мой взгляд, не для базового уровня и не для домашних проектов, это, скорее, более профессиональная задача и предмет отдельного материала и даже, возможно, без привязки к конкретным проектам (только если это не пример, когда для работы проекта нужно своё уникальное дерево устройств и/или специфичный загрузчик). В начале данной статьи специально сделан акцент на базовый уровень и на то, что это конкретный учебный пример функционала. Возможно используемый термин "embedded-разработка" создаёт ожидания в т.ч. про подготовку прошивки. Понятие embedded-разработка понятие широкое и многоуровневое. Задача оптимизации кода по производительности в соответствующем разделе затронута, кстати говоря.
Тоже давно думал про создание отдельных статей по DT (в т.ч. с оверлеями) и примерами настройки под конкретные устройства и, что важно, с заходом в тему создания драйверов как модулей ядра для своих периферийных устройств. План написать такую статью есть, материалы с примерами есть. А может и Вы предложите свой вариант раньше, раз видите такой недостаток в имеющихся статьях, было бы на самом деле очень здорово.
Что касается более четкого обозначения целевой аудитории и очерчивания круга затрагиваемых вопросов и задач - с этим понятно, такое замечание принято, будем учитывать. Но стоит ли вообще подобные материалы готовить и публиковать или это ненужная трата времени подобные материалы не имеют ценности на Ваш взгляд?
randomsimplenumber
Когда вместо Arduino Nano берут целое Raspberry - вызывает искреннее удивление.