Шина умного дома на базе CAN. С шиной сопрягаются MODBUS, MQTT и пр.
Библиотека, примеры и собственно код умного дома использующего CAN как основной протокол обмена между контроллерами, с интеграцией с прочими коммуникационными протоколами. Для сопряжения будут поддерживаться интерфейсы Ethernet, UART, RS-485, NRF24, NRF433, Bee... Обмен будет возможен с сопрягаемыми устройствами по ModBus RTU b Modbus TCP, MQTT... Реализована возможность удаленного опроса и установки значений GPIO.
Библиотека разрабатывалась под Arduino Mega 2560 (Arduino Mega 2560 pro от RobotDin https://robotdyn.com/mega-2560-pro-embed-ch340g-atmega2560-16au.html) в качестве узлов и Arduino DUE в качестве мастера и резерва мастера сети.
Библиотека предназначена для самодельщиков которые используют кабель для построения сети домашней автоматизации. Для профессионалов проект будет слабоинтересен в связи использование других типов процессоров.
На разработку библиотеки и собственно протокола подвигло во первых мономастерность ModBus RTU и собственно отказ от RS-485 как основного проотокола обмена умного дома, а во вторых сложность и избыточность существующих протоколов 3 уровня для CAN (Таких как CANopen, DeviceNet и пр.)
Общий каркас библиотеке основан на библиотеке aSysBus уважаемого Florian Knodt https://github.com/adlerweb/asysbus, которая в свою очередь основана на iSysBus Patrick Amrhein, www.isysbus.org.
Идеология (и только идеология) работы протокола поверх CAN основывается на CANOpen. Реализация кода частично заимствована у Jens Geisler AGCON https://github.com/jgeisler0303/AGCON и CANFestivino https://github.com/jgeisler0303/CANFestivino Самая важная часть, работа с контроллером MCP2515 осуществляется через MCP_CAN_lib https://github.com/coryjfowler/MCP_CAN_lib
От CANopen взята концепция и реализация сервисов. По адресации, принципe формирования OD, передаче PDO, SDO, работе LSS, NMT (NG и HB) собпадения только в названиях и логике работы. Протокольной совместимости нет и не планируется. (Для скептиков а почему не взять готовый CANFestival сразу отвечу, протокол сложен в понимании, программировании и как не странно в настройке и нужен для больших сетей с разнотипным оборудованием. Умный дом же использует достаточно фиксированные конфигурации контроллера, датчиков и GPIO).
В общем случае из пользовательского приложения вызывается конструктор базового класса ahb ahb0(0,100,node_type), со считыванием адреса из EEPROM, где 0 стартовый и 100 конечный адрес EEPROM (возможно использовать lengthEEPROM() как конечный адрес EEPROM), node_type принимает значение Master, Slave, Node. Есть упрощенный вызов конструктора класса ahb ahb0(16,node_type), где 16 номер узла в десятичной системе.
Для подключения протоколов к узлу вызываются конструкторы данных протоколов с последующим назначением протоколу ID шины - BusID. Для CAN это выглядит как ahbCAN ahbCAN0(cs) где cs это pin arduino к которому подключен cs pin контроллера mcp2515.
Подключение шины CAN выглядит следующим образом bus_ID char; bus_ID=ahb0.busAttach(&ahbCan0); bus_ID это номер конкретной шины от 1 до 120, которую возвращает busAttach() и bus_ID = -1 при ошибке.
В части CAN реализовано
- Отправка и прием сообщений с использованием EXT 29 битного заголовка в котором закладывается
- 0-7 бит - 256 адресов source (0 запрещен, при получении считаем ошибочным)
- 8-11 бит - 16 портов target (destination)
- 12-19 бит - 256 адресов target (destination) (0 broadcast/multicast)
- 20-27 бит - 256 команд для обслуживания протокола и передачи команд с/на узлы
- 28 бит - 2 состояния - 0 unicast, 1 broadcast/multicast
- NMT Управление и мониторинг сети.
- Каждый узел имеет предопределенную до компиляции единую таблицу адресов узлов Node_Address_table[] от 1 до 255. В которой до компиляции можно определить будет ли узел включен в сеть, для этого используется флаг ON и OFF в таблице адресов узлов.
2.1 BOOT-UP Отправка On_Boot сообщение после включения питания, которым узел сообщает сети что он включен. В зависимости от node_type отправляются 3 разные On_Boot сообщения. On_Boot_Master, On_Boot_Slave, On_Boot_Node. Если Master включен раньше узлов node то по приему On_Boot сообщений от включенных узлов Master заполняет таблицу включенных узлов Node_Status_table[], проверяет статус узла, устанавливает время узла, и начинает мониторинг узла. Master каждые 5 секунд выдает boot_request(nodeID) узлам со статусами On_Boot_fail. Если таблица узлов не имеет статусов On_Boot_fail для всех узлов с предопределенным флагом ON то Master прекращает рассылку boot_request. И начинает ее снова если в таблице появляется узел с флагом ON и статусом On_Boot_fail. Статус может появится при работе сервисов NodeGuarding или Heartbeat. Если Master включается позже узлов то он шлет широковещательно On_Boot_Master на который ждет ответ, в случае неответа начинаем цикл запросов boot_request(nodeID).
У себя в сети я использую дополнительный механизм для загрузки прошивки, контроля и управления узом по MQTT и SLIP с использованием подключенного к Arduino Mega 2560 Wi-Fi контроллера esp8266 с прошивкой esp-link https://github.com/jeelabs/esp-link версия v3.2.47 https://github.com/jeelabs/esp-link/releases/tag/v3.2.47.alpha, только она позволяет прошивать удаленно Arduino Mega 2560, причем мне удалось добиться нормальной работы только через Linux.
Так как у меня есть дополнительный аппаратный механизм для управления и перезагрузки контроллера, то я его использую. При появлении у узла статуса On_Boot_fail и и более 10 сек отсуствие реакци на NMT запросы, Master отправляет через дублирующую IP Wi-Fi сеть команду на esp8266 подать ресет на Arduino Mega 2560. Дополнительно Master отправляет сообщение на вышестоящий узел сообщение о аварии узла сети.
2.2 Node Control это следующий механизм и этап инициализации узла это перевод его в одно из состояний Initialization, Pre-Operational, Operational и Stopped. Любой узел при отсутствии сбоев и неисправностей переходит в состояние Operational в котором он полностью работоспособен. В состоянии Stopped узем может принимать и отвечать только на On_Boot_reques от любого узла и принимать и отвечать на NMT запросы node_status_req(nodeID) и получать ответ node_status_resp(node_status) от всех узлов и принимать и отвечать на NMT команды node_status_setup(node_status) node_status_setup_ok(node_status) от Master и Slave при неисправности Master, + Heartbeat + Node-Guarding. В состоянии Pre-Operational узел может принимать и отвечать на команды аналогично Stopped а так же может принимать и сообщать свою конфигурацию и подключенные программные и аппаратные средства. * У узла Slave два состояния Operational_Slave и Operational_Master. В состояние Operational_Master Slave переходит при отказе мастера. * Статус On_Boot_fail это виртуальный статус узла в таблице узлов на Master и Slave который присваивается узлу если он не ответил на запрос и не прошел диагностику.
2.3 Heartbeat это механизм мониторинга узла, при котором узлы node с определенной периодичностьтю шлют на Master и Slave сообщение о своем состоянии node_status_resp(node_status). Причем мастер постоянно ждет это собщения и если оно не приходит более чем орпеделенный период то начинает проверку узла. Передача ведется с единым интервалом для всех узлов + время равное NodeID, что позволяет разгрузить сеть. Так как это сообщение с высоким приоритетом то попытка его отправки в цикле while идет 5 раз, после чего в случае неудачи отправки узел начинает диагностику сети и контроллера и может уйти в переинициализацию или перезагрузку.
2.4 Node-Guarding это механизм мониторинга узла, более сложный чем Heartbeat. В этом режиме мониторинга Master последовательно опрашивает все узлы node_status_req(nodeID), причем опрос идет фреймом CAN remote. В данном режиме узлы Node так же мониторят Master и Slave по наличию прихода от них node_status_req(nodeID) и при отсутствии ответов от мастера начинают его диагностику, после которой в случае его отказа запрашивают у Slave возможность выполнять функции мастера, т.е перейти из режима Operational_Slave в режим Operational_Мастер.
Реализован режим как Broadcast node_status_req(0) со смещением ответа на время равное NodeID от узлов Slave, так и режим Unicast node_status_req(nodeID) с последовательным перебором всех узлов из таблицы адресов узлов Node_Address_table[]. Узлы Node отвечают на node_status_req(nodeID) ответом node_status_resp(node_status). Master при не получении ответа на запрос в течении определенного времени начинает диагностику узла.
ВАЖНО! СПОРНО! ПРОДУМАТЬ! Heartbeat и Node-Guarding взаимоисключающие механизмы (Так в CANopen.) Используем только один. Вызов будет реализован в конструкторе главного класса как bool. Хотя можно определять динамически командами. Если получили от Master запрос Node-Guarding то блокируем Heartbeat. Если мастер а затем и Slave после этого перестали слать Node-Guarding начинаем слать Heartbeat до On_Boot_Master или On_Boot_Slave
2.5 Node-Node ВАЖНО! СПОРНО! ПРОДУМАТЬ! В сети много узлов Node которые обмениваются между собой. Для понимания статуса корреспондента проще всего получить таблицу статусов узлов от Master. Нужно реализовывать на SDO так как 8 байт CAN data frame не достаточно для передаче за такт PDO. Возможно так же заполнять таблицу прослушивая Heartbeat и/или Node-Guarding
2.6 LSS ВАЖНО! СПОРНО! ПРОДУМАТЬ! подстройка скорости и режимов контроллеров. Проще реализовать по Wi-Fi
-
PDO
-
SDO
-
SYNC
На 4 уровне модели OSI реализовано (спасибо за идею и реализацию Florian Knodt https://github.com/adlerweb/asysbus)
-
Удаленное считывание состояний Digital GPIO и Analog GPIO
-
Удаленная установка Digital GPIO и Analog GPIO (как быть с внутренней логикой узла, нужно учесть с выдачей флагов прерываний)
-
Удаленный вызов функций (например перейти в режим день, ночь, гости и пр.)
-
Удаленное выполнение команд
-
Получение значений датчиков, сенсоров и результатов работы внутренней логики.
-
Ретрансляция в другие сети и протоколы с и без модификации ID и данных AHB::ahbRetranslation(asbPacket &pkg, bool=true/false retranslation) (ЧЕМ ОТЛИЧАЕТСЯ РЕТРАНСЛЯЦИЯ от МАРШРУТИЗАЦИИ)
Ретрансяция без модификации позволяет делать форимализованый вывод в интерфейс для например сопряжения с ПК.
Ретрансляция с модификацией ID позволяет направить фрейм в P-t-P интерфейс (Например NRF, 485) адресуя устройство в адресном пространстве исходной сети.
- Маршрутизация в другие сети и протоколы Modbus RTU, Modbus TCP, MQTT, SLIP, REST на основании таблицы маршрутизации с и без модификацией ID и данных. AHB::ahbRetranslation(asbPacket &pkg, bool=true/false retranslation)
Заложены библиотеки 485 Bee NRF24 NRF433 IRDA
Так же заранее предопределены функции для работы с датчиками, заложены их библиотеки и назначены их pin
- BME280 - датчик температуры, влажности, давления I2C, SPI
- DS18 - 1 wire датчик температуры
- MH-Z19B - датчик наличия CO2
- E18-D80NK - датчик прохода (препятствий)
- Геркон - он же кнопка
- TEMT6000 - датчик освещенности
- HC-SR505 - датчик движения оптический
- XXXXXXXX - датчик движения микроволновой
- MQ-2 - датчик газа
- XXXXXXX - пожарный датчик (промышленный или набор датчиков)
- YYYYYYY - датчик шума
Всего в Arduino Mega 2560
16 аналоговых GPIO которые могут быть и аналоговыми
54 цифровых GPIO но - UART1(0,1) UART2(18,19) UART3(16,17) UART4(14,15) SDA/SDC(20,21)
Из них PWM 2-13, 44,45,46
Interrupt 21,20,19,18,2,3
PCINT 50,51,52,53, A8-A15, 10-15, 6, 0,1,
SDA/SDC 1 занимают 21,21
SPI 1 занимают 53 SS, 52 SCK,51 MOSI,50 MISO?
БИБЛИОТЕКИ Modbus RTU https://github.com/smarmengol/Modbus-Master-Slave-for-Arduino https://habr.com/ru/post/249043/ Modbus TCP https://github.com/goddland16/Modbus-TCP, https://code.google.com/archive/p/mudbus/downloads Сервера https://insat.ru/prices/info.php?pid=6944
ZegBee S2 https://amperka.ru/product/xbee http://wiki.amperka.ru/%D0%B1%D0%B5%D1%81%D0%BF%D1%80%D0%BE%D0%B2%D0%BE%D0%B4%D0%BD%D0%B0%D1%8F-%D1%81%D0%B2%D1%8F%D0%B7%D1%8C:%D0%BD%D0%B0%D1%81%D1%82%D1%80%D0%BE%D0%B9%D0%BA%D0%B0-xbee-series-2
BME280 https://github.com/finitespace/BME280/blob/master/src/BME280I2C.h https://go.mysku.ru/?r=https%3A%2F%2Fgithub.com%2Ffinitespace%2FBME280%2Farchive%2FRelease_Version_2.1.2.zip&key=ms&s=msab.8634524 https://mysku.ru/blog/aliexpress/52057.html
ТАБЛИЦА МАРШРУТИЗАЦИИ для связи с сетями (множество адресов)
- CAN1 to CAN2
- CAN2 to CAN1
- CAN to Modbus_RTU(master)
- Modbus_RTU(master) to CAN
- CAN to Modbus_TCP(master)
- Modbus_TCP(master) to CAN
- CAN to Modbus_TCP(slave)
- Modbus_TCP(slave) to CAN
ТАБЛИЦА РЕТРАНСЛЯЦИИ для связи с P_t_P (1 адрес) Может сделать как раз портами?
- CAN to P_t_P (485, NRF, Bee, UART)
- P_t_P(485, NRF, Bee, UART) to CAN
Добавлена поддержка часов реалного времени на мастере с рассылкой времени в запросах NodeGuarding Добавлена поддержка рассылки узлами Up Time в ответах на запросы NodeGuarding Добавлен Web интерфейс со статусами узлов для Master
Периодически пропадают узлы по CAN. Видимо зависает MCP2515
Пробуем Reinit MCP25515
ahb_comm_can _interface.mcp2515_reset(); _interface.begin(MCP_STDEXT, _speed, _clockspd);
ahb _busAddr[busId]->reset(); //сделать ф-ю byte AHB_CAN::reset() {_interface.mcp2515_reset();} _busAddr[busId]->begin();
если 1 минуту не приходит PING и время от мастера то отправлять PING мастеру в течении минуты если не получили ответ то либо мастер сдох либо завис мой CAN
проверяем не сдох ли мастер отправкой PING сначала на слейв, тоже ждем 1 минуту делаем N запросов, затем всем узлам сети, если есть ответ от слейва то мастер сдох, запуск процедуры смена мастера, если слейв не ответил отправляем всем в сети. Если нет ни одного ответа то либо завис CAN либо в сети нет живых узлов.
Если нет ответов ни от одного узла то пробуем переинициализировать CAN см. верх
После переинициализации CAN ждем PING от мастера, если нет, пинг слейву, если нет пинг всем, если нет рестарт узла через WTD.
Если после рестарта узла по WTD нет работы даже после реинит по CAN больше не перезагружаемся а отправляем сообщение через MQTT и REST через ETH (если есть) или ESP8266 ( Если есть)