Документация
Внешние устройства
Датчик давления в шине, самодельный экран, «Третий глаз» и GPS-приёмник Dragy: что телефон умеет находить рядом с собой.
Кроме колеса, телефон умеет разговаривать ещё с четырьмя вещами рядом: с датчиком давления в шине, с самодельным экраном, с зеркалом «Третий глаз» и с GPS-приёмником Dragy. Общего у них немного — каждое живёт на своём конце Bluetooth, и все настраиваются в разделе «Связь и датчики», — но ни одно не колесо, и потому собраны здесь.
Часы — тоже устройство на том же конце связи, но им отведён свой раздел: Часы.
Bluetooth-зарядка подключается из сканера как основное устройство, а не как датчик колеса. Подключение, пароль и настройки описаны в разделе Зарядки: СКАТ и HW.
После ручного «Отключиться» LoEUC больше не подключается к этому колесу автоматически, в том числе после перезапуска приложения. Запрет снимается только ручным подключением к тому же колесу. Другие колёса из гаража остаются доступны. Во время реплея приложение не должно прерывать просмотр попыткой автоподключения.
Датчик давления в шине (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: колесо его не сообщает, а угадывать по своим командам
телефон не берётся, фару могли включить кнопкой на самом колесе. Состояние есть у автосвета
телефона и у охраны, и у них стоит бит достоверности.
Для реализации прошивки:
- В сервисе
1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c01создайте обязательную характеристику телеметрии1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c02сWRITE_NO_RESPONSEи управляющую1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c08сNOTIFY,WRITE_NO_RESPONSEи дескриптором CCCD0x2902. - Рекламируйте UUID сервиса. Включите «Внешний экран» в LoEUC и выберите экран.
- Дождитесь, когда телефон включит уведомления через CCCD управляющей характеристики.
- На нажатие отправьте из неё Notify с семью бинарными байтами. Просто обновить
значение характеристики недостаточно. Например,
10 00 00 00 00 00 01— фара, номер нажатия 1;13 00 00 00 00 00 02— сигнал, номер 2. - В обработчике записи той же характеристики принимайте 15-байтные кадры телефона. Сверяйте опкод в байте 7 и номер в байтах 9–14 со своей отправленной просьбой.
Заголовка LE, CRC и завершающего нуля у семибайтной команды нет. Передавайте бинарные
байты, а не текст с hex-числами. Номер — unsigned 48-bit big-endian: новое нажатие получает
новый номер, повторная отправка того же нажатия сохраняет прежний. Используйте время в
миллисекундах или сохраняемый счётчик, чтобы перезагрузка экрана не повторяла старые номера.
Телефон помнит восемь последних исполненных пар «команда + номер».
Код 1 означает приём просьбы телефоном, а не подтверждение выполнения от колеса.
Неизвестный опкод или обрезанную команду телефон игнорирует. При неизвестной скорости
выключение тоже отклоняется с кодом 5; проверка допускает только конечную скорость
abs(speed) < 3 км/ч. Отказ не запоминается как исполнение команды.
Экран, который подключается сам#
Только на Android. В LoEUC для iPhone GATT-сервер и реклама телефона пока не реализованы; там телефон находит рекламирующийся экран.
Есть и обратный порядок: экран сам находит телефон и подписывается на нотификации той же характеристики. Этот путь появился раньше и остаётся рабочим — тот же сервис, та же характеристика, тот же кадр, только читать его надо подпиской, а не ждать записи.
Тот же выключатель держит его живым. Раньше телефон переставал рекламироваться, как только видел часы с LoEUC, и владелец часов автоматически оставался без экрана; теперь включённый «Внешний экран» держит канал поднятым независимо от часов.
Команды: экран пишет в телефон#
В этом варианте экран — BLE Central, телефон — Peripheral. Характеристики находятся на телефоне. Включите «Внешний экран» в LoEUC и подключите телефон к колесу.
- Найдите рекламируемый сервис
1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c01и подключитесь к нужному телефону. - Прочитайте
1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c06: это JSON в UTF-8. Полеphone_holds_wheelсообщает о подключении к колесу;available_commands— строка доступных команд через запятую:light,auto_light,shutdown,horn. - Отправьте семь бинарных байт через Write with response в
1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c05. - После ответа на запись снова прочитайте
1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c06и сопоставьте результат с номером нажатия.
Примеры пакетов, каждый с новым номером:
10 00 00 00 00 00 01 фара / следующий режим
11 00 00 00 00 00 02 автосвет самого колеса
12 00 00 00 00 00 03 выключение колеса
13 00 00 00 00 00 04 сигнал
Здесь действуют те же правила семибайтного формата и номеров нажатий, что выше.
0x14 и 0x15 этот канал не принимает. Подписка на телеметрию для отправки команд не нужна;
для получения телеметрии подпишитесь на
1d0f2f9c-6d1a-4d96-ae61-2a4fd97c2c02 через её CCCD.
Пример ответа после команды фары с номером 1:
{
"phone_holds_wheel": true,
"available_commands": "light,shutdown,horn",
"command_id": "light",
"command_outcome": "accepted",
"command_sent_at_millis": 1,
"answered_at_millis": 1790000000100
}
command_sent_at_millis — переданный номер нажатия, даже если это счётчик, а не время.
Сверяйте его вместе с command_id. Результат последней команды общий, поэтому отправляйте
следующую после получения ответа на предыдущую.
command_outcome |
Значение |
|---|---|
accepted |
Телефон принял просьбу к исполнению; это не подтверждение от колеса |
unsupported |
Команда не поддерживается |
not_connected |
Телефон не подключён к колесу |
duplicate |
Эта команда с этим номером уже исполнена |
unknown |
Неизвестный результат |
Причина отказа может быть в command_detail. До первой команды полей её результата нет.
Успешный ATT-ответ на запись означает только, что пакет разобран: результат читайте из JSON.
Неизвестный опкод или короткий пакет дают ошибку записи.
JSON может быть длиннее одного ATT-пакета: поддержите длинное чтение (Read Blob со
смещениями) или согласуйте достаточно большой MTU и проверьте, что получили JSON целиком.
Выключение: в текущем пути через …2c05 телефон не проверяет скорость, в отличие от
пути через …2c08. Блокируйте выключение на экране при движении и неизвестной скорости.
Третий глаз#
Раздел «Связь и датчики». Android и iPhone.
«Третий глаз» — маленькое зеркало-дисплей на руле, которое продаётся готовым. Своё приложение у него есть, но цифра на нём заметно отстаёт от происходящего; LoEUC пишет в то же зеркало и той же строкой, только чаще и без лишних задержек.
Включите «Третий глаз» — телефон сам найдёт зеркало в эфире (оно рекламируется именем,
начинающимся на ThirdEye_), подключится и начнёт писать. Под выключателем видно, идёт ли
поиск или зеркало уже принимает кадры, а ниже — список всех зеркал, которые слышно, с
уровнем сигнала. В первый раз телефон пишет в первое найденное зеркало и запоминает его, а
выбранное руками запоминается и не подменяется.
Выключатель живёт сам по себе, а не до конца поездки: включили один раз — и на следующей поездке телефон сам подключится к зеркалу, как только оно появится в эфире. Подключается он только к своему, тому, что было в прошлый раз: зеркало товарища в группе он не перехватит. Чтобы перейти на другое зеркало, выберите его в списке.
Пока колесо подключено, телефон ищет своё зеркало всю поездку: первые 10 минут раз в 10 секунд, дальше раз в 30. Зеркало, включённое посреди поездки или перезагрузившееся, подключится само в течение полуминуты.
У зеркала две короткие строки, и что в них показывать — ваше дело: в каждую годится любая метрика, которую отдаёт колесо, от скорости и заряда до ШИМ и напряжения ячейки. По умолчанию сверху скорость, снизу заряд — как в родном приложении. Любую строку можно выключить, тогда зеркало покажет одну.
Свой заряд зеркало сообщает само, раз в минуту. Он доступен как обычная метрика дашборда — поставьте её плиткой, если не хотите однажды обнаружить зеркало разряженным посреди поездки.
Ничего, кроме текста кадра, телефон зеркалу не шлёт и настройки его не меняет.
Dragy Pro#
Раздел «Связь и датчики». Android и iPhone.
Dragy Pro — отдельный GPS-приёмник, который берут с собой на замеры разгона. Он считает позицию и скорость по спутникам до 25 раз в секунду — в десятки раз чаще и точнее, чем GPS телефона, — и LoEUC умеет читать его прямо во время поездки, рядом с колесом.
Подключение. Отдельно искать приёмник не нужно. Когда колесо подключено и рядом включён Dragy, дашборд предложит подключить его к поездке. «Подключить» привязывает приёмник: дальше он подключается к каждой поездке сам, как только окажется рядом. «Не в этой поездке» — и до конца поездки дашборд больше не спрашивает, а в следующей спросит снова. Совсем убрать предложение можно тумблером «Предлагать подключение» в настройках приёмника.
Плитка. После подключения на дашборде сверху появляется плитка Dragy: сколько спутников в решении, есть ли 3D-фикс, сколько раз в секунду приходят точки, когда приёмник подключился, точность и заряд. Плитку можно передвинуть и изменить её размер, как любую другую: на узкой остаются спутники и фикс, на широкой видно всё. Тап по плитке открывает настройки приёмника; туда же ведёт пункт «Dragy» в разделе «Связь и датчики».
Что пишется в поездку. GPS телефона продолжает работать при подключённом Dragy: его скорость записывается отдельной метрикой. При включённом выборе Dragy его годные точки имеют приоритет для GPS-скорости, трека и калибровки спидометра. Потеряет приёмник небо — через три секунды снова подхватится GPS телефона. Замер разгона считает отдельные времена Dragy для 0–30, 0–60 и 0–100 км/ч.
Рядом с каждой записью колеса сохраняются скорость, число спутников, точность, курс, высота, тип фикса и заряд батареи Dragy. Эти метрики можно выбрать в отдельном разделе «Dragy» при добавлении плитки на дашборд и использовать в правилах оповещений. Координаты в трек попадают, только если запись трека включена — как и у телефонного GPS.
Настройки.
- Частота обновления — 10, 20 или 25 раз в секунду. Выше 10 умеет только Dragy Pro (модель DRG71). Сам Dragy при каждом подключении сбрасывает частоту на 10, поэтому телефон отправляет её заново при каждом подключении и показывает, сколько точек приходит на самом деле. Чем чаще, тем быстрее садится батарея приёмника.
- Трек и GPS-скорость от Dragy — выключите, если хотите, чтобы поездка шла по телефону, а Dragy только показывал своё состояние.
- Подсказка при старте — при подключении телефон передаёт приёмнику точное время и свою примерную позицию, чтобы тот быстрее нашёл спутники.
- Помехи — признаки глушения и подмены сигнала, о которых сообщает сам приёмник.
- Отключить до следующей поездки и Забыть приёмник — второе снимает привязку, и в следующей поездке Dragy снова будет предложен.
Поддержка новая: она собрана по описанию протокола Dragy и на живом приёмнике пока проверена мало. Если поток не пошёл или плитка показывает не то, пришлите журнал диагностики — в нём есть записи о подключении приёмника.