Files
2026-10-11 01:27:15 +03:00

240 lines
21 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# LightHub и шина CAN
> Источник: авторский документ «Lighthub и шина CAN» (docx, 2026-10-02), сверен с кодом `lighthub/candriver.cpp/h`.
> CAN-поддержка в прошивке появилась в **v4.4.0**.
## Что такое CAN шина
CAN-шины очень широко используются в промышленной и автомобильной автоматике как дешевый, но надежный транспорт для взаимодействия модулей и блоков автоматики между собой. Такие шины есть, фактически, в каждом современном автомобиле.
В прошивке LightHub используется фиксированная скорость **50 kbit/s** (`candriver.cpp`: `STMCan.setBaudRate(50000)` / `CAN.begin(50000)` / `Can0.begin(CAN_BPS_50K)`), что обеспечивает длину шины до нескольких сотен метров — достаточно для большинства домовладений.
Поддержка CAN встроена во многие современные (и не очень) микроконтроллеры. Из тех микроконтроллеров, на которых работает прошивка LightHub, встроенная поддержка используется в следующих чипах:
- ARM SAM3E (Arduino DUE) — библиотека `due_can`
- ESP32 — библиотека `arduino-CAN` (пины: GPIO 35 — RX, GPIO 5 — TX)
- STM32 — библиотека `STM32_CAN`
> Подключение внешнего CAN-контроллера по SPI для чипов без встроенной поддержки **не реализовано** в текущем коде (`candriver.cpp` использует только встроенные контроллеры).
## Какие возможности появляются у LightHub
Теперь нет необходимости организовывать подключение локальной сети (или WiFi) к каждому устройству. Необходим минимум один контроллер (лучше пара), который подключен одновременно и к локальной сети, и к шине CAN. Он будет выполнять роль шлюза и держателя настроек периферийных контроллеров.
Периферийные устройства могут подключаться только к электропитанию и двухпроводной шине CAN. А это значит, что устройств может стать кратно больше, можно использовать очень простые, небольшие и дешевые устройства и, в пределе, размещать их в любом удобном месте. Например, в подрозетниках, выключателях, кондиционерах, датчиках движения.
При всем прочем, даже такие упрощенные устройства умеют почти то же самое, что их «старшие братья»:
- тот же формат JSON-файла настройки позволяет организовывать как локальное, так и удаленное управление устройством;
- малое устройство может иметь на борту термостаты, возможность подключения термодатчиков и прочих сенсоров, PID-регуляторы, UART для управления кондиционером или для Modbus;
- к таким устройствам можно подключать элементы управления: кнопки, переключатели, энкодеры (элемент с вращающейся ручкой), герконы дверей и датчики движения/присутствия;
- они также поддерживают все возможности по определению одинарных-двойных-тройных-длительных нажатий кнопок, как и все LightHub.
Такая архитектура позволяет еще более децентрализовать управление умным домом: каждый маленький автономный контроллер (расположенный даже в выключателе) способен решить задачи простой автоматизации и локального управления даже в том случае, если часть системы вышла из строя.
Кроме того, CAN-шина позволяет таким простым устройствам напрямую общаться между собой, например:
- для реализации сценариев переключения сцен освещения, проходных выключателей;
- для передачи измерений сенсоров на удаленные контроллеры (например, измерения влажности помещений на контроллер форсуночного увлажнения, значения CO2 в помещениях на систему вентиляции).
Причем такое взаимодействие теперь может происходить без посредничества локальной сети и MQTT-брокера, что еще более увеличивает надежность и живучесть системы в целом.
### Примеры применения контроллеров, подключенных только по шине CAN (далее — lightspot)
- Автономный управляемый термостат теплого пола
- Автономное, управляемое локальным выключателем реле или диммер света (AC или RGB)
- Датчик движения, локально и удаленно управляющий светом, с сенсорами температуры, влажности, CO2 на борту
- Многоклавишный многофункциональный выключатель с сенсорами температуры-влажности, энкодером
- Удаленные счетчики воды-газа-электричества
- Управляемые клапаны вентиляции
- Удаленные ультразвуковые датчики приближения для управления подсветкой ступеней
## Как организована передача данных по шине CAN
Таким образом, каждое устройство в произвольный момент времени может инициировать передачу кадра данных размером до 8 байт, и этот кадр будет принят сразу всеми подключенными к шине устройствами.
### Формат идентификатора кадра
Идентификатор кадра (до 32 бит, `canid_t` в `candriver.h`):
| Поле | Биты | Назначение |
|--|--|--|
| reserve | 3 | Должно быть 0 |
| status | 1 | 1 — статус, 0 — запрос |
| payloadType | 4 | Тип передаваемых данных |
| deviceId | 8 | devID устройства (1..255) |
| itemId | 10 | 0..1023 (или sequence # для длинных кадров) |
| subItemId | 6 | 0..62 (63 — субайтем не задан) |
### Типы полезной нагрузки (payloadType)
| Значение | Имя | Назначение |
|--|--|--|
| 1 | `itemCommand` | Команда или статус для/от того или иного item |
| 2 | `lookupMAC` | Запрос devID по MAC-адресу устройства (в ответе передается devID) |
| 3 | `configFrame` | Кадр передачи конфигурации (JSON, частями) |
| 4 | `OTAFrame` | Кадр передачи прошивки по CAN |
| 5 | `auth` | Аутентификация |
| 6 | `metric` | Метрики (см. metricType) |
| 7 | `sysCmd` | Системные команды (см. commandType) |
| 8 | `rawPinCtrl` | Прямое управление пинами |
### Формат данных (datagram\_t)
```c
enum metricType { MAC=1, IP=2, NetMask=3, GW=4, DNS=5, UpTime=6, Salt=7 };
typedef union {
uint8_t data[8];
char payload[8];
struct {
itemCmdStore cmd; // команда
itemArgStore param; // аргумент команды
};
struct {
macAddress mac; // MAC-адрес
uint16_t currentConfCRC; // CRC текущей конфигурации
};
struct {
uint8_t sysCmd;
uint8_t sysCmdData[7];
};
struct {
uint32_t metric1;
uint32_t metric2;
};
} datagram_t;
```
Системные команды (sysCmd, `commandType`):
| Значение | Команда |
|--|--|
| 1 | `reboot` — перезагрузка контроллера |
| 2 | `get` — получить информацию |
| 3 | `save` — сохранить конфигурацию |
| 4 | `load` — загрузить конфигурацию |
## Процедура инициализации устройства и загрузки конфигурации
1. **Вычисление MAC-адреса.** Этот адрес является уникальным идентификатором устройства, даже если у него отсутствует сетевой интерфейс и имеется в наличии только CAN. MAC-адрес вычисляется из уникального серийного номера чипа для ARM-базированных устройств, для AVR считывается из EEPROM, для ESP используется MAC-адрес интерфейса WiFi.
2. Устройство отправляет запрос в CAN-шину, установив `payloadType = lookupMAC (2)` в пакете и передав 6 байт MAC-адреса.
3. Мастер-контроллер, содержащий конфигурацию устройства внутри своего файла настроек, формирует ответный пакет нулевой длины с установленным битом `status` в id и заполненным полем `deviceId`. С этого момента устройство знает свой devID.
4. Устройство отправляет запрос в CAN-шину, установив `payloadType = configFrame (3)` и полученный на предыдущем этапе devID. В качестве sequence # для первого пакета используется значение 0xFFFF (-1), запрос каждого последующего пакета осуществляется с увеличением sequence # на единицу. В ответ на каждый запрос устройство получает кадр данных; последовательность данных содержит JSON-конфигурацию.
### Пример конфигурации CAN внутри конфига мастер-устройства
```json
"can": {
"addr": 14,
"conf": {
"DE:54:71:48:17:2E": {
"can": { "addr": 21, "name": "olmexino" },
"items": {
"relay1": [[6, 1], 1],
"relay2": [[6, 2], 5],
"relays": [["GROUP", 3], ["relay1", "relay2"]]
},
"in": {
"41": { "T": 2, "item": "relays", "can": [10, 2], "scmd": "TOGGLE", "rcmd": "" }
}
},
"DE:48:4E:03:B3:DF": {
"can": { "addr": 22, "name": "sonar" },
"items": {
"relay1": [[6, 1], 1]
},
"in": [
{
"T": 7, "#": [14, 15],
"act": [
{ "map": [0, 100], "scmd": { "can": [13, 32], "icmd": "XON 10000" } },
{ "map": [0, 200], "scmd": { "can": [13, 31], "icmd": "XON 15000" } }
]
}
]
}
}
}
```
`can.addr` — devID этого контроллера. `can.conf` — объект «MAC-адрес → конфигурация периферийного контроллера»; каждая периферия содержит собственный `can` (devID и имя) и обычные секции `items`/`in`.
## Способ настройки взаимодействия контроллеров с использованием шины CAN
В процессе работы контроллеры имеют возможность отправки на один или несколько других контроллеров, подключенных по шине CAN, следующего набора пакетов:
| Тип управляющего воздействия | Числовое значение суффикса | Суффикс (символьное значение) |
|--|--:|--|
| Команды для управления удаленными устройствами | 1 | `cmd` |
| Уставки для удаленного устройства | 2 | `set` |
| Текущие значения сенсоров | 3 | `val` |
| Отложенная команда | 4 | `del` |
| Цвет в нотации HSV | 5 | `hsv` |
| Цвет в нотации RGB | 6 | `rgb` |
| Интенсивность вентиляции | 7 | `fan` |
| Режим работы климатической установки | 8 | `mode` |
| Вспомогательная команда (FREEZE / UNFREEZE / ENABLE / DISABLE) | 9 | `ctrl` |
| Цвет (hue) | 10 | `hue` |
| Насыщенность цвета | 11 | `sat` |
| Цветовая температура | 12 | `temp` |
### Адресация объекта назначения (item)
Для адресации объекта назначения (item), который обработает соответствующую команду в сети CAN, используется следующая нотация:
```
[ <DevID>, <ItemID>, <suffix> <subitem> ]
```
- **DevID** — адрес устройства, определяется на этапе конфигурирования (см. выше). DevID = 0 адресует сообщение всем устройствам сети.
- **ItemID** — числовой идентификатор объекта (item). Задается при настройке удаленного объекта в JSON-конфигурации того контроллера, к которому объект подключен: первый элемент массива настройки item должен быть массивом, в котором первый объект определяет тип объекта, а второй — требуемый ItemID. Пример:
```json
"items": {
"relay1": [[6, 1], 1],
"relay2": [[6, 2], 5],
"relays": [["GROUP", 3], ["relay1", "relay2"]]
}
```
В примере выше для объекта `relay1` определяется itemID = 1, для объекта `relay2` — itemID = 2, а для группы `relays` — itemID = 3.
В том случае, если ItemID пересекаются между контроллерами и пакет отправлен на широковещательный адрес 0, пакет будет выполнен сразу всеми контроллерами, в которых определен объект с данным ID.
- **Suffix** — числовое или символьное значение, определяющее тип управляющего воздействия согласно таблице выше. Допустимо определять дополнительные числовые суффиксы, например, для управления теми регистрами устройства Modbus, которые не укладываются в таблицу выше. В этом случае при описании устройства Modbus можно задать параметр `"id": <значение>`, где значение — число в интервале 13-31, и этот параметр можно будет адресовать, задавая данное число в массиве `can` на третьей позиции.
- **Subitem** — опциональный параметр, который может использоваться двояко:
- если указана **цифра** — адресация суб-объекта, входящего в удаленный управляемый объект. Примеры объектов, которые имеют внутри перечень суб-объектов: MULTIVENT (многоканальная вентиляция/кондиционирование), HUM (многоканальный форсуночный увлажнитель), адресная светодиодная лента. Для адресации соответствующего суб-объекта используется порядковый номер, согласно которому данный суб-объект описан в JSON-конфигурации контроллера назначения;
- если указана **строка** — делается попытка преобразовать данную строку в команду, и это значение передается удаленному устройству. Удаленное устройство исполнит пакет только в том случае, если текущее состояние устройства эквивалентно данной команде. Данная функция особо полезна для удаленных групп устройств: например, можно адресовать команды управления яркостью группе светильников, а исполнят их только светильники, находящиеся в состоянии «ON».
## Функция шлюза
Мастер-контроллер, который хранит конфигурацию периферийного, также выполняет функцию шлюза между шиной CAN и интерфейсами MQTT и HTTP.
Так как мастер-контроллер знает имя периферийного, при подключении к MQTT-брокеру он дополнительно подписывается на командный топик периферийного. Это позволяет управлять устройствами, подключенными через шину CAN, абсолютно прозрачно, с использованием стандартной нотации командных топиков `root/name/item/[subitem/]suffix`, например, через `myhome/sonar/relay1/cmd` для примера конфига выше.
Когда удаленный периферийный контроллер выполнит команду (либо вследствие изменения внутреннего состояния устройства) — изменение его статуса транслируется в шину CAN. Мастер-контроллер, который владеет конфигом периферийного, транслирует такое изменение статуса в свой стандартный статусный топик, например, в `myhome/s_out/relay1/cmd`.
## Доступность в сборках (сверка с кодом, 2026-10-02)
- Флаг `CANDRV` включен по умолчанию: **esp32-wifi, lighthub21, stm32-noip**; в **esp32c3-wifi** закомментирован (`build-flags/*`).
- Скорость: 50 kbit/s, фиксированная (`candriver.cpp`).
- ESP32: CAN на GPIO 35 (RX) / GPIO 5 (TX) (`candriver.cpp`).
- Поддержка SPI-кан-контроллеров для чипов без встроенной CAN — **не реализована** (см. выше).
## Не реализовано
Черновики из исходного документа, допущенные до релиза, но **не реализованные** в текущем коде:
- вход `T:99` с профилями, обработчиками `goto`/`onenter` и сценарным переходом между профилями;
- подключение CAN через внешний SPI-контроллер для чипов без встроенной поддержки.
## Связанные документы
- [Rotary Encoder](../core/rotary_encoder.md) — энкодер как орган управления (в т.ч. на lightspot)
- [Быстрая справка по MQTT](../core/mqtt_quick_reference.md)
- [Справочник типов каналов](../core/channel_types_reference.md)