Сделать прототип на dev-board сегодня можно за пару вечеров. Гораздо сложнее превратить его в законченное устройство со своей платой, корпусом, аккумуляторным питанием, беспроводной связью и сервером, который хранит результаты пользователей.
Именно так начинался этот проект. За год он успел пережить три версии печатной платы, сменить три микроконтроллера, отказаться от аналогового датчика, «съесть» больше 400 евро на изготовлении PCB и несколько раз заставить искать ошибки мультиметром там, где их вообще не ожидали.
В результате получился карманный алкометр, который измеряет содержание алкоголя в выдохе, передаёт данные по BLE прямо в браузер через Web Bluetooth, хранит историю измерений в облаке и даже строит недельный рейтинг пользователей.
Разберём весь путь проекта, от первого макета до полностью собственной платы, и посмотрим, какие инженерные решения оказались удачными, а какие пришлось полностью переделывать.
Сценарий простой. Пользователь дует в датчик, микроконтроллер считает значение, показывает его на экране и тут же отправляет по Bluetooth Low Energy. Принимает данные не телефон, а веб-страница в браузере. Дальше результат сохраняется в облачную базу, привязывается к аккаунту и попадает в историю и недельный рейтинг.
Прежде чем спускаться ниже, посмотрим на весь стек по слоям:
|
Слой |
Ревизия 1 (макет) |
Ревизия 2 (своя плата) |
|---|---|---|
|
Микроконтроллер |
FireBeetle 2 на ESP32-C6 |
ESP32-WROOM-32E |
|
Датчик |
аналоговый MQ-3 |
SC03-C2H5OH, UART |
|
Экран |
SSD1306, I2C |
ST7735S, SPI |
|
Связь |
BLE, GATT-сервер |
BLE, GATT-сервер |
|
Приём данных |
браузер через Web Bluetooth |
браузер через Web Bluetooth |
|
Бэкенд |
Next.js и Supabase на Vercel |
Next.js и Supabase на Vercel |
Две ревизии железа
Зачем понадобились две версии? Первый прототип собран на готовом модуле, чтобы быстро проверить идею, не разводя плату. Вторая ревизия довела устройство до своего форм-фактора с корпусом, питанием от батареи и нормальным датчиком.
Вторая ревизия далась не с первого раза. Своя плата прошла три заказа за период с мая 2025 по январь 2026, в сумме больше четырёхсот евро. Первый вариант нарисован в KiCad на чипе ESP32-C3 со старым аналоговым датчиком, заказан, но даже не собран: сразу последовал переход к следующему.
На этом этапе случился ещё один сюрприз. NextPCB почему-то насчитал за сборку платы 515 долларов. Разбираться, почему небольшая плата внезапно стоит как неплохой ноутбук, желания уже не было, поэтому проект просто ушёл в следующую итерацию.
Второй вариант уже на ESP32-WROOM-32E, но плата не завелась. Питание просто не доходило до контроллера. Виновником оказался защитный диод: схема была нарисована верно, но у его посадочного места в библиотеке перепутаны выводы, и на плате он встал задом наперёд, перекрыв ток. Нашёлся он мультиметром, а лечением стала перепайка диода правильной стороной, но и на этом проблемы не закончились.
После перепайки казалось, что плата полностью исправна. Питание есть, ESP32 запускается, но прошивка категорически не хотела загружаться. Причина оказалась до смешного простой: линии RX и TX были разведены напрямую, хотя их нужно перекрещивать. Из-за этого программатор и контроллер буквально разговаривали сами с собой.
Рабочей оказалась только третья версия платы. Она сохранила ESP32-WROOM-32E, но получила уже цифровой датчик. Проблемы не покидали и тут. На этот раз был забыт подтягивающий резистор на EN, а без него ESP32 не собирался работать. Пришлось припаять небольшой навесной резистор в 10 кОм между выводом EN и +3V3. К счастью, выводы EN и +3V3 находятся рядом, поэтому исправление удалось сделать буквально одним навесным резистором.
Вместе с третьей версией изменился не только сам PCB, но и один из самых важных компонентов устройства — датчик.
Датчик: от аналога к цифре
Третья версия устройства получила цифровой электрохимический датчик SC03-C2H5OH. Он сам отдаёт готовое значение кадром по UART, а прогревается меньше пяти секунд, тогда как аналоговый датчик MQ-3 был заметно хуже: долгий прогрев, заметный шум и принципиально некалиброванная оценка вместо честных промилле. Именно проблемы MQ-3 и подтолкнули к переходу на цифровой датчик во время разработки второй ревизии.
Новый датчик заметно повысил удобство использования, но это был не единственный компонент, который изменился между ревизиями. Вместе с ним полностью обновился и пользовательский интерфейс устройства.
Экран: от монохрома к цвету
В первой ревизии монохромный SSD1306. На нём меню из двух пунктов и крупное число с результатом. Для макета этого достаточно.
Во второй ревизии цветной дисплей ST7735S. Здесь уже появились заставки (встроенные JPEG декодируются), экран с надписью BLOW ME и обратным отсчётом, вывод среднего значения.
Новое железо потребовало и новой логики работы. Вместе с компонентами постепенно менялась и архитектура прошивки.
Прошивка
Обе прошивки написаны на ESP-IDF и FreeRTOS, BLE на стеке Bluedroid. Обе выросли из официальных примеров ESP-IDF.
Ревизия 1: задачи и очереди
Архитектура собрана из независимых задач FreeRTOS, связанных очередями. Идея в том, что есть один источник данных и два независимых потребителя. Отдельная задача читает значение с АЦП и через приём fan-out дублирует его сразу в две очереди, для BLE и для экрана. Очереди глубиной в один элемент с перезаписью дают политику «только последнее значение».
// один источник ADC, два потребителя через fan-outadc_init(adc_src_q);adc_fanout_init(adc_src_q, ble_q, oled_q);
Сверху навешаны ещё три модуля. Кнопки с антидребезгом через прерывание и отдельную задачу. Модуль результатов хранит три лучших пика под мьютексом и сохраняет их в энергонезависимую память, чтобы рекорд переживал перезагрузку.
Ревизия 2: проще и со сном
Здесь структура проще: экран, UART и BLE. Главный цикл показывает заставки, ждёт нажатия, запускает измерение и уходит в сон по таймауту. Датчик читается в отдельной задаче, которая разбирает кадр UART.
// разбор кадра цифрового датчика по UARTif (len >= 9 && buf[0] == 0xFF && buf[1] == 0x17) { uint16_t raw = ((uint16_t)buf[4] << 8) | buf[5]; float bac = raw / 10.0f;}
Сам процесс измерения выглядит так. На экране идёт обратный отсчёт от четырёх до одного с надписью BLOW ME, параллельно набираются отсчёты. Дальше берётся среднее, выводится на экран и отправляется в BLE.
Однако сама прошивка была лишь половиной проекта. После получения результата его ещё нужно было передать пользователю, сохранить и показать историю измерений без отдельного мобильного приложения.
Веб-часть и приём данных в браузере
Данные с устройства ловит не мобильное приложение, а веб-страница прямо в браузере, через Web Bluetooth API. Однако именно здесь появилась одна из самых странных проблем всего проекта.
Работает это так. Подключение запускается только по клику пользователя, иначе браузер не пустит, и только в браузерах на движке Chromium. Страница фильтрует устройство по имени, подписывается на уведомления и читает байты.
// байты от ESP32 читаем как float, little-endianconst dv = new DataView(value.buffer);const data = dv.getFloat32(0, true);
Первая ревизия с FireBeetle 2, кстати, почти никогда не подключается к Bluetooth с первого раза. Несколько вечеров ушло на поиск ошибки в коде, пока не выяснилось, что проблема вообще не в нём. После перехода на ESP32-WROOM проблема исчезла сама собой. Предположительно, дело было в аппаратной реализации BLE на модуле FireBeetle, но однозначную причину установить так и не удалось.
Бэкенд — это Next.js и Supabase на Vercel. Авторизация и защита маршрутов сделаны через middleware. Измерение сохраняется одной вставкой. История с серверной пагинацией и недельный рейтинг считаются на стороне базы через представления. Есть и профиль с ростом, весом, возрастом и полом.
Недельный рейтинг не был изначально целью, но всё началось с простой идеи: если результаты всё равно хранятся в базе, почему бы не сделать недельный рейтинг пользователей? Так появился самый странный раздел проекта — рейтинг по алкоголизму, который в итоге и дал статье её название.
Когда история измерений уже хранилась в базе, появилась ещё одна идея: использовать эти данные не только для отображения статистики, но и как контекст для персональных рекомендаций.
AI-советник по здоровью
В приложение встроен чат-советник по здоровью. Он работает на модели Gemini и отвечает на вопросы пользователя, опираясь на его собственные данные. Перед каждым запросом сервер собирает контекст: профиль с ростом, весом, возрастом и полом, плюс последние пять измерений промилле из истории. Этот контекст подставляется в системный промпт, так что советы учитывают данные конкретного пользователя.
В системном промпте сразу задаётся роль помощника по здоровью и очерчиваются границы: не ставить диагнозов и не назначать лекарств. В шапке чата висит дисклеймер, что советник не заменяет врача.
Всё это хорошо, но главный вопрос оставался прежним: насколько вообще можно доверять показаниям устройства?
Проверка точности против магазинного алкотестера
Чтобы понять, чего стоят показания, финальную версию устройства проверили против обычного магазинного алкотестера. Проверять устройство решили в максимально бытовом сценарии — сравнить его показания с обычным магазинным алкотестером при постепенном употреблении алкоголя. Для этого доброволец выпил десять банок пива по 330 миллилитров крепостью 4.2 процента за семь часов, до измерения пил немного воды для более чистого замера, а между шестой и седьмой поел. После каждой порции снимались показания обоих приборов.
Цифры в промилле, самодельный прибор против магазинного:
|
Порция |
Самодельный прибор |
Магазинный |
|---|---|---|
|
1 |
0.1 |
0.3 |
|
2 |
0.2 |
0.17 |
|
3 |
0.18 |
0.25 |
|
4 |
0.2 |
0.33 |
|
5 |
0.45 |
0.2 |
|
6 |
0.47 |
0.4 |
|
7 |
0.18 |
0.16 |
|
8 |
0.4 |
0.39 |
|
9 |
0.28 |
0.14 |
|
10 |
0.5 |
0.49 |
Иногда цифры почти совпадают, но разброс есть. Для самодельного устройства результат достойный, хотя всерьёз доверять ему не стоит.
Цель эксперимента заключалась не в сертификации прибора, а в сравнении общей динамики показаний с коммерческим устройством в одинаковых условиях. Несмотря на ограничения такого сравнения, эксперимент показал главное — устройство уверенно повторяет общую динамику изменений.
Итого
Год назад всё начиналось с макетной платы и идеи «посмотреть, получится ли вообще». В итоге проект дорос до собственного железа, BLE-связи с браузером, облачного сервиса, недельного рейтинга и AI-помощника. И, пожалуй, самым полезным результатом оказалась не готовая плата, а инженерный опыт, который появился после трёх ревизий, нескольких неочевидных ошибок и сотен евро, ушедших на их исправление.
Ссылка на сайт этого проекта: https://iot-smart-al.vercel.app/
Также можно посмотреть видео-версию проекта на YouTube.
ссылка на оригинал статьи https://habr.com/ru/articles/1062992/