Перейти к содержимому

Документация

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

Датчик давления в шине, самодельный экран и «Третий глаз»: что телефон умеет находить рядом с собой.

Кроме колеса, телефон умеет разговаривать ещё с тремя вещами рядом: с датчиком давления в шине, с самодельным экраном и с зеркалом «Третий глаз». Общего у них немного — каждое живёт на своём конце Bluetooth, и все трое настраиваются в разделе «Связь и датчики», — но ни одно не колесо, и потому собраны здесь.

Часы — тоже устройство на том же конце связи, но им отведён свой раздел: Часы.

Датчик давления в шине (TPMS)#

Раздел «Связь и датчики».

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

Поддержаны iBar и совместимые, Pecham/SYTPMS и Sysgration. Добавлены экспериментальные разборы TPMSII, Michelin TMS, WODHMIEY/ITPMS K и Salutica FOBO по исходникам открытого проекта tpms_ble; эти дополнительные форматы пока не проверены на живых датчиках. Порода определяется по рекламе, в том числе пакетам только с manufacturer data.

Привязка. Найденный датчик приложение предлагает привязать к текущему колесу: «Подключить» привязывает, «Не сейчас» откладывает до следующего раза, «Игнорировать» прячет этот датчик насовсем — чтобы чужое колесо на парковке не спрашивало каждый раз. Передумали — «Искать скрытые датчики» возвращает игнорированные в поиск. Сменили датчик — «Заменить TPMS».

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

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

Последнее давление остаётся на плитке, когда датчик молчит, без ограничения в 30 секунд. Это не свежее измерение: устаревшие показания исключаются из алертов. Между перезапусками приложения последнее измерение не сохраняется. На iPhone manufacturer-only датчики без сервисов FBB0/27A5 обнаруживаются только при открытом приложении; в фоне сохраняется прежний фильтр по сервисам.

Внешний экран#

Раздел «Связь и датчики». Android и iPhone.

Телеметрию можно отдавать самодельному экрану — например, на ESP32. Включите «Внешний экран», и телефон сам найдёт его в эфире, подключится и начнёт слать кадры. Экран при этом ничего не ищет и не требует кнопок для выбора телефона: рекламируется он, подключается телефон.

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

Список живёт, пока открыт раздел настроек: держать поиск включённым всю поездку — это радио и батарея, а смотреть на список в это время всё равно некому.

Если экран остался дома, телефон продолжает его искать и больше ничего не делает: Android повторяет попытку раз в пять секунд, iPhone держит поиск включённым. Если экран пропал из эфира на ходу, телефон подключится к нему сам, как только тот вернётся.

Что должен уметь экран#

  • рекламировать сервис 1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c01;
  • держать в нём характеристику 1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c02, доступную на запись, лучше без подтверждения;
  • принимать в неё кадр из двадцати байт, пять раз в секунду.

Двадцать байт — это дефолтный MTU за вычетом заголовка ATT: кадр долетает без согласования MTU вообще. Телефон пишет без подтверждения — на пяти кадрах в секунду подтверждение каждого стоит дороже, чем экономит.

Кадр#

Тот же компактный кадр, что уходит на часы по запасному каналу. Порядок байтов — big-endian, целые со знаком.

Offset Размер Поле
0 1 0x4C, магия L
1 1 0x45, магия E
2 1 версия, сейчас 0x01
3 1 счётчик кадров, растёт по модулю 256
4..5 2 маска присутствия полей
6..7 2 скорость, км/ч ×10
8..9 2 ШИМ, % ×10
10..11 2 напряжение, В ×10
12..13 2 заряд, % ×10
14..15 2 температура, °C ×10
16..17 2 пробег за поездку, км ×10
18..19 2 предел ШИМ для тревоги, % ×10

Биты маски, от нулевого: скорость, ШИМ, напряжение, заряд, температура, пробег, предел ШИМ. Сброшенный бит означает, что поля в этом кадре нет — читать его значение не нужно. В самих двух байтах при этом лежит 0x8000 (Int16.MIN), так что экран, читающий поля напрямую, тоже отличит «нет величины» от нуля.

Магию и версию приёмник обязан проверять и молча отбрасывать всё, что не 4C 45 01: версия кадра — единственный способ его когда-нибудь расширить.

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

Если экран умеет больше#

Семь величин — это всё, что влезает в двадцать байт. Экран, которому нужно больше, держит вторую характеристику 1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c07, тоже на запись, и получает в неё раз в секунду всё: до тридцати трёх метрик колеса и итог текущей поездки.

Характеристика необязательная. Нет её — телефон шлёт только двадцатибайтовый кадр, ровно как раньше, и экран, собранный по этой странице год назад, ничего не заметит.

Как снимок доезжает#

Снимок весит пару сотен байт и в одну запись при обычном MTU не влезает, поэтому едет кусками. У каждого куска свой заголовок, а у первого — ещё длина и контрольная сумма всей передачи:

Смещение Размер Поле
0 1 0x4C
1 1 0x45
2 1 версия нарезки, 0x01
3 1 флаги: бит 0 — последний кусок
4..5 2 номер куска, с нуля
6..9 4 только в куске 0: длина всей передачи
10..13 4 только в куске 0: CRC32 всей передачи
дальше данные

Собирать надо по номерам, а в конце проверять длину и CRC32 (обычный IEEE, полином 0xEDB88320). Без этой проверки потерянный кусок склеится в мусор, который выглядит как данные. Пишет телефон с подтверждением: потеря любого куска роняет весь снимок, и раз в секунду подтверждение себя окупает.

Размер куска телефон берёт от согласованного MTU минус три байта. Android просит 517 и берёт то, что дали; iPhone MTU не просит — его согласует система. Экран, который согласится на большой MTU, получит снимок одной-двумя записями вместо десятка.

Что внутри снимка#

Смещение Размер Поле
0 1 0x4C
1 1 0x45
2 1 версия, 0x03
3 1 флаги: бит 0 — следом блок поездки
4..11 8 метка времени, мс
12 1 N — сколько метрик
13.. 5×N записи: код метрики (1 байт) и значение (4 байта, со знаком)

Дальше, если бит 0 стоит, блок поездки на 22 байта: длительность в мс (4), пробег (4), максимум скорости (4), максимум ШИМ (4), минимум заряда (4), сколько раз сработали алерты (2).

Все числа — big-endian. Значения метрик и поездки умножены на 1000, а не на 10, как в компактном кадре: разбег ячеек меряется милливольтами, и одна шкала обязана вместить и его, и стотысячный одометр. 0x80000000 (Int32.MIN) означает «телефон эту величину не знает».

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

Код Метрика Код Метрика
1 скорость, км/ч 18 температура АКБ макс., °C
2 ШИМ, % 19 тангаж, °
3 напряжение пакета, В 20 крен, °
4 среднее ячейки, В 21 угол защиты от падения, °
5 перепад ячеек, В 22 опасная скорость, км/ч
6 ток АКБ, А 23 давление в шине, бар
7 ток шины, А 24 пробег поездки, км
8 фазный ток, А 25 одометр, км
9 мощность, Вт 26 напряжение зарядки, В
10 мощность мотора, Вт 27 ток зарядки, А
11 полная мощность, ВА 28 мощность зарядки, Вт
12 заряд, % 29 температура зарядника, °C
13 температура контроллера, °C 30 целевое напряжение, В
14 температура мосфетов, °C 31 залито, А·ч
15 температура мотора, °C 32 залито, Вт·ч
16 температура платы, °C 33 зарядка идёт (0 или 1)
17 температура АКБ мин., °C

Метрики, которых ваше колесо не сообщает, в снимок не попадают вовсе — их там просто нет, и это не то же самое, что ноль.

Кнопки на экране#

Экран с кнопками может управлять колесом: включить и выключить фару, посигналить, выключить колесо, переключить автосвет телефона, автосвет самого колеса (Inmotion V12 и новее) и охрану (плагин парковочной сигнализации). Автосвет колеса с экрана пока переключается только с Android; остальное — и с Android, и с iPhone. Байты для колеса собирает телефон — тем же кодом, что и кнопки дашборда, — поэтому команды одинаковы для всех колёс: экрану не нужно знать, какое колесо на связи. Кнопки, которых у колеса нет, телефон экрану и не предлагает.

Выключение телефон выполняет только на стоящем колесе, медленнее 3 км/ч, как бы экран ни просил: погасить колесо под едущим — уронить человека.

Для этого экран держит ещё одну характеристику, 1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c08, с уведомлениями и записью без подтверждения. Нет её — всё работает как раньше, только без кнопок.

Нажатие экран шлёт уведомлением из семи байт: опкод и номер нажатия (шесть байт, big-endian). Номер нужен, чтобы повтор того же уведомления не выключил только что включённую фару; для нового нажатия — новый номер.

Опкод Команда
0x10 фара — переключить
0x11 автосвет колеса — переключить
0x12 выключить колесо
0x13 сигнал
0x14 автосвет телефона — переключить
0x15 охрана — снять или поставить

Состояние телефон пишет в ту же характеристику кадром из пятнадцати байт — при каждой смене, сразу после нажатия и раз в секунду:

Offset Размер Поле
0..2 3 4C 45 20 — магия и версия
3 1 счётчик кадров
4 1 переключатели по два бита: 0-1 фара, 2-3 автосвет телефона, 4-5 охрана, 6-7 автосвет колеса
5 1 достоверность, по биту на переключатель в том же порядке
6 1 бит 0 — сигнал есть, бит 1 — выключение есть
7 1 опкод последнего нажатия, 0 — нажатий не было
8 1 чем оно кончилось: 1 выполнено, 2 колесо не умеет, 3 нет колеса, 4 повтор, 5 выключение на ходу отклонено
9..14 6 номер того нажатия

Два бита переключателя: 00 — у колеса этого нет, кнопку не показывать; 01 — есть, но неизвестно, включено ли; 10 — выключено; 11 — включено. У фары и автосвета колеса состояния нет — они всегда 01: колесо его не сообщает, а угадывать по своим командам телефон не берётся, фару могли включить кнопкой на самом колесе. Состояние есть у автосвета телефона и у охраны, и у них стоит бит достоверности.

Экран, который подключается сам#

Только на Android. iPhone не умеет рекламироваться сам, поэтому там работает только первый порядок — телефон находит экран.

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

Тот же выключатель держит его живым. Раньше телефон переставал рекламироваться, как только видел часы с LoEUC, и владелец часов автоматически оставался без экрана; теперь включённый «Внешний экран» держит канал поднятым независимо от часов.

Третий глаз#

Раздел «Связь и датчики». Android и iPhone.

«Третий глаз» — маленькое зеркало-дисплей на руле, которое продаётся готовым. Своё приложение у него есть, но цифра на нём заметно отстаёт от происходящего; LoEUC пишет в то же зеркало и той же строкой, только чаще и без лишних задержек.

Включите «Третий глаз» — телефон сам найдёт зеркало в эфире (оно рекламируется именем, начинающимся на ThirdEye_), подключится и начнёт писать. Под выключателем видно, идёт ли поиск или зеркало уже принимает кадры, а ниже — список всех зеркал, которые слышно, с уровнем сигнала. В первый раз телефон пишет в первое найденное зеркало и запоминает его, а выбранное руками запоминается и не подменяется.

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

Пока колесо подключено, телефон ищет своё зеркало всю поездку: первые 10 минут раз в 10 секунд, дальше раз в 30. Зеркало, включённое посреди поездки или перезагрузившееся, подключится само в течение полуминуты.

У зеркала две короткие строки, и что в них показывать — ваше дело: в каждую годится любая метрика, которую отдаёт колесо, от скорости и заряда до ШИМ и напряжения ячейки. По умолчанию сверху скорость, снизу заряд — как в родном приложении. Любую строку можно выключить, тогда зеркало покажет одну.

Свой заряд зеркало сообщает само, раз в минуту. Он доступен как обычная метрика дашборда — поставьте её плиткой, если не хотите однажды обнаружить зеркало разряженным посреди поездки.

Ничего, кроме текста кадра, телефон зеркалу не шлёт и настройки его не меняет.