# MySmartHouse3
Система "Умный дом" основанная на интерфейсе CAN/TWAI. В качестве контроллера устройств сети выбрана микросхема ESP32-C6.
Полное описание во вкладке "Wiki" данного репозитария.
ПРОГРАММА: "controller_can"
Разработана и предназначена для "КОНТРОЛЛЕРА CAN" работающего в локальной сети на основе интерфейса CAN 2.0.
Контроллер работает на микросхеме ESP32-C6-WROOM-1-N8. Подключение к роутеру по интерфейсу Ethernet, через
модуль типа WIZ820 на микросхеме W5500 (подключается по внутреннему интерфейсу SPI). При разработке
программы использовался алгоритм обмена информацией по сети CAN описанный в проекте пользовательского уровня
интерфейса.
Программа разработана на основе ESP-IDF v5.4. Компиляция и отладка произведена с помощью среды(framework):
ESP-IDF 5.4 CMD.
ПРИМЕЧАНИЕ - Контроллер с локальным адресом "1" считается всегда подключенным к Ethernet.
**Принципы построения программы**
Программа является единой для устройств сети CAN, как "master" так и "slave". Работа программы определяется
свойством "подчиненность" задаваемым в информационном сервисе устройства. Кроме того в информационном сервисе
каждого устройства содержатся перечисление функциональных типов выполняемых устройством, по наличию которых
производится автоматическое конфигурирование программы работы устройства с датчиками и исполнительными
устройствами.
Программа основана на принципе многозадачности с использованием очередей запросов и семафоров.
**Алгоритм работы**
При подаче питания, или по сбросу, каждое устройство загружает из флеш-памяти ранее записанный профиль
устройства и выстраивает свою работу на основе записанной там информации. Затем устройство с локальным
номером 1, которое подключается к роутеру через Ethernet, запускает программу установки драйвера Ethernet.
В случае неудачной установки драйвера светодиод "ALARM" периодически светится (период 2 с). Если драйвер
установлен правильно (модуль Ethernet работает), запускается программа получения ip-адреса от роутера и
включается светодиод "WORK". В случае неудачи создания соединения по протоколу TCP или потеря связи с
выбранным сервером светодиод "WORK" выключается.
По кратковременному нажатию кнопки "INIT" устройства "master" происходит опрос подключенных в сеть CAN
устройств "slave" в режиме разделения во времени, причем в связи с неустойчивым приемом данных
сканирование сети происходит несколько раз при суммировании адресов ответивших устройств.
По результату опроса составляется список обнаруженных устройств. Затем у этих устройств последовательно
запрашиваются ИНФОРМАЦИОННЫЕ СЕРВИСЫ. Сервисы передаются в пакетном режиме: каждая характеристика сервиса
это отдельный кадр данных CAN. Полученная информация заносится во флеш-память устройства "master", откуда
может быть востребована дальнейшей работы сети или по команде внешнего устройства (сервер, сотовый телефон).
ПРИМЕЧАНИЕ -
- Все запросы данных в сети CAN повторяются несколько раз при отсутствии ответа.
- Все данные передаваемые в пакетном режиме содержат номер кадра и проверяются приемником на соответствие в порядке очередности.
- При потере хотя бы одного кадра все данные запрашиваются повторно, возможно несколько раз.
- При отсылке одного пакета данных мастеру всегда подтверждение от него, иначе повтор до нескольких раз.
- При опубликовании "события" подчиненным устройством согласно сценария всегда производятся повторы оговоренное число раз.
Каждое устройство хранит в отведенной области памяти-nvs "profile" в пространстве имен "device_inf" информационный сервис. Кроме информационного сервиса там же хранятся сервисы функциональности, расположенные в пространстве имен типа "type*", где * - это номер функциональности в списке перечесления кодов информационного сервиса. Кроме того для оперативной работы программы (чтобы каждый раз не обращаться к nvs-памяти) используются таблицы расположенные в оперативной памяти устройства:
-
таблица параметров устройства (до 8 параметров на каждый из возможных 8 типов функциональности);
-
таблица событий сети CAN на которое подписано устройство(до 16 событий на каждый из возможных 8 типов функциональности);
-
таблица исполнения по этим событиям (по коду действия на каждое из возможных 16 событий на каждый из возможных 8 типов функциональности).
Для связи между номером функциональности в списке перечисления кодов информационного сервиса кодом функциональности служит таблица в ОЗУ устройства, где номер ячейки это код типа, а содержимое первый индекс таблиц для обслуживания сервисов типа. На каждый тип функциональности отведен свой файл в программе для инициализации при сбросе и драйвера для работы с подключаемыми датчиками/исполнителями. Для исключения сбоев при пропадании питания, при изменении любой из характеристик устройства она заносится в nvs-память. Кроме того устройство мастер отслеживает обмен данными по сети CAN и всю информацию заносит также в отведенные для устройств разделы nvs-памяти.
Состав программы Программа состоит из нескольких программных блоков:
-
"app_main.c" главная программа, объединяющая все другие програмные блоки.
-
"app_const.h" файл где указаны константы устанавливаемые перед компиляцией. Для оперативного изменения параметров программы перед компиляцией.
-
"app_init.c" содержит функции запускаемые при включении устройства и функции для работы с памятью-nvs.
-
"app_driver.c" программа обработки нажатия кнопки "INIT".
-
"app_priv.h" файл объявления используемых функций.
-
"app_utilit.c" содержит пользовательские програмные утилиты.
-
"CAN.c" драйвер интерфейса CAN.
-
"MAKE_PARAMETR.c" обработка изменения параметра, согласно присвоенному ему коду.
-
"TYPE.c" функции общие для всех типов функциональности.
-
группа программ, где * означает код функциональности "TYPE_*.c" собранные в одном месте функции относящиеся только к данному типу функциональности (драйвера и прочее...).
-
"ethernet.c" драйвер ethernet на основе SPI модуля W5500, на основе конфигурации Espressif IoT Development Framework (используется стандарт IEEE 802.3u Ethernet 10/100 Base Tx). После установки драйвера и получив от роутера ip адрес, можно проверить работу в "командной строке" с помощью команды, например:"ping 192.168.2.151".
-
"tcp_client_v4.c" cоздание и запуск задачи соединения socket/tcp_client в неблокирующем режиме.
-
"app_websocket.c" создание и запуск задачи соединения websocket client. Используется режим WebSocket через TLS, только с проверкой сертификата сервера.
Функция "app_main() в файле "app_main.c" По сбросу или включению питания всегда первой запускается функция "app_main()" и происходит выполнение следующих операций в порядке очередности:
1 Содержится в файле "app_init.c":
- init_GPIO();//инициализация используемых выводов модуля ESP.
- get_DeviceInf();//из nvs памяти считывается информация информационного сервиса: локальный номер, подчиненность, наименования, коды типа функциональности. И происходит инициализация типов функциональности в данном контроллере (подключение используемых выводов модуля ESP и создание в nvs-памяти сервиса типа данной функциональности)путем запуска функций "init_type_", которые находятся в одноименных файлах "TYPE_.c" (там же для отладки можно задать сценарии автономной работы данного типа функциональности контроллера по событиям CAN).
ПРИМЕЧАНИЕ - Для записи информации в nvs-память можно использовать только ключи-наименования только типа "const", поэтому созданы таблицы имен:
- Пространство имен устройств в nvs-памяти "const char namespase[64][10]" согласно локальному номеру устройства в сети из полученного списка при сканировании системы;
- Пространство имен для для записи сервиса функциональности устройства "const char namespase_type[9][6]" согласно порядку перечисления типов в информационном сервисе устройства;
- Пространство имен для записи величины параметров "const char namespace_value_par[9][10]"
2 Содержится в файле "app_driver.c":
- app_driver_init();//инициализация кнопки "INIT". Данная программа написана на языке С++ и содержится в главной директории папки "ESP_CAN" в подпапке "components". Работа ее построена на прерывании от входного сигнала по входу модуля и есть возможность задать разную функциональность работы в зависимости от длительности нажатия кнопки.
3 Изложенное в данном пункте относится к настройкам работы с CAN.
- создание очередей запроса для обслуживания событий приема/передачи сообщений по интерфейсу CAN.
- создание бинарного семафора для выполнения заданной задачи до конца.
- создание задач для управления работой интерфейса CAN: задача приема сообщения (twai_receive_task); задача передачи сообщения (twai_transmit_task); задача контроля и управления работой контроллера (ctrl_task).
4 Инициализация сторожевого таймера задач, для исключения зависания программы и подписка задачи контроля на этот таймер.
5 Программа в этом пункте выполняется если локальный номер контроллера - 1, т.е. контроллер подключен к сети LAN:
- запуск функции "eth_init" из файла ethernet.c. Инициализация драйвера ethernet на основе SPI модуля W5500
- Инициализировать сетевой интерфейс TCP/IP, он же esp-netif через цикл обработки событий по умолчанию, работающий в фоновом режиме. Создать экземпляр esp-netif для Ethernet с заданными параметрами и подключить драйвер Ethernet к стеку TCP/IP. Организовать задержку на получение адреса Ip.
- "mqtt_wss_client_task()" создание и запуск задачи соединения клиента с брокером MQTT через wss (websocket secure). Файл "app_mqtt_wss.c". Для надежной приема/передачи данных использовать уровень качества обслуживания QoC 1 (подтвержденная доставка) с запоминанием сообщения до замены его новым по тому же топику. По умолчанию размер буферов приема/передачи установлен в файле sdkconfig - 1024 байт(по умолчанию).
6 Инициализация интерфейса CAN через запуск функции "init_CAN(CAN_TX,CAN_RX)", файл CAN.c. Передача семафора, старт задачи контроля.
ПРИЛОЖЕНИЕ 1 Содержимое файла sdkconfig.defaults:
==============================================================
CONFIG_IDF_TARGET="esp32c6"//используемый тип микросхемы
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y//размер флеш-памяти
//--нижеперечисленное относится к разбивке флаш-памяти---
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
CONFIG_PARTITION_TABLE_OFFSET=0x8000
CONFIG_PARTITION_TABLE_MD5=y//наличие контрольной суммы для проверки таблицы
//нижеперечисленное относится к ETHERNET
CONFIG_ETH_ENABLED=y
CONFIG_ETH_USE_SPI_ETHERNET=y
CONFIG_ETH_SPI_ETHERNET_W5500=y
//нижеперечисленное относится к Websocket
CONFIG_WS_TRANSPORT=y //установлено по умолчанию
CONFIG_WS_BUFFER_SIZE=1024 //установлено по умолчанию
//нижеперечисленное относится к MQTT
CONFIG_MQTT_PROTOCOL_311=y //установлено по умолчанию
CONFIG_MQTT_TRANSPORT_SSL=y //установлено по умолчанию
CONFIG_MQTT_TRANSPORT_WEBSOCKET=y //установлено по умолчанию
CONFIG_MQTT_TRANSPORT_WEBSOCKET_SECURE=y //установлено по умолчанию
==============================================================
ПРИМЕЧАНИЕ - Перед первой компиляцией необходимо задать целевое устройство: idf.py set-target esp32c6
ПРИЛОЖЕНИЕ 2 Содержимое таблицы разбивки флеш-памяти
==============================================================
Смещение раздела прошивки должно быть выровнено на 64 КБ, начальные 36 КБ (9 секторов) зарезервированы для загрузчика и таблицы разделов.
Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x6000,24K,
otadata, data, ota, 0xf000, 0x2000,8K,
phy_init, data, phy, 0x11000, 0x1000,4K,
ota_0, app, ota_0, 0x20000, 3000K,
ota_1, app, ota_1, 0x310000, 3000K,
profile, data, nvs, 0x5FE000, 0x6000,24K,
user_nvs, data, nvs, 0x604000, 0x6000,24K,
net_dev, data, nvs, 0x60A000, 0x6000,24K,
device1 data, nvs, 0x610000, 0x6000,24K,
..................................................
device63 data, nvs, 0x784000, 0x6000,24K
==================================================
Область памяти "nvs" предназначена для энергонезависимого хранения пар ключ-значение, в частности данных Wi-Fi.
Область памяти "otadata" это раздел данных OTA(обновления прошивки) , в котором хранится информация о
выбранном в данный момент слоте приложения OTA.Когда используется OTA , раздел данных OTA определяет,
какой слот приложения должен загружать загрузчик.
Область памяти "phy_init" предназначена для хранения данных инициализации PHY. Это позволяет настраивать PHY
для каждого устройства, а не во встроенном ПО. В конфигурации по умолчанию раздел phy не используется,
а данные инициализации PHY компилируются в само приложение
Области памяти "ota_0" и "ota_1" используются для обновления прошивки программы. При использовании OTA
приложение должно иметь как минимум два слота для приложений OTA.
Область памяти "profile" предназначена для энергонезависимого хранения пар ключ-значение,в ней будут
храниться профиль устройства.
Область памяти "user_nvs" предназначена для энергонезависимого хранения пар ключ-значение для данной
программы.
Область памяти "net_dev" предназначена для энергонезависимого хранения пар ключ-значение для записи данных
общих для сети CAN.
Область памяти "device1 -: device63" предназначена для энергонезависимого хранения пар ключ-значение для
записи данных профилей устройств подключенных к сети CAN.
==============================================================
ПРИМЕЧАНИЕ - За основу взято: https://docs.espressif.com/projects/esp-jumpstart/en/latest/ https://docs.espressif.com/projects/esp-idf/en/v5.4/esp32c6/api-guides/partition-tables.html
ПРИЛОЖЕНИЕ 3 СОЗДАНИЕ ОБЛАСТИ ПАМЯТИ ХРАНЕНИЯ ДАННЫХ ПРОФИЛЯ УСТРОЙСТВА
Откройте в главной директории проекта файл profile.csv с помощью редактора "блокнот", а если его нет создайте
в соответствии с требованиями изложенными в
https://github.com/espressif/esp-idf/tree/v5.4/components/nvs_flash/nvs_partition_generator
файл с расширением .csv со следующим содержимым:
key,type,encoding,value
d_inf,namespace,,
s/n,data,string,"00001"
obey,data,string,"master"
name_d,data,string,"device_one"
place,data,string,"no_name"
v_program,data,string,"v1"
type_c,data,string,"070 210 0A0 220#"
где выражение в "" задается серийный номер, тип изделия и т. д.
Созданный/измененный файл превращается в бинарный файл с помощью команды: "python nvs_partition_gen.py generate profile.csv profile.bin 0x6000" Затем записывается утилитой esptool.py в NVS память микропроцессора. Например команда записи через порт вывода "com8" во "flash память" с адреса "0x5FE000" файла "profile.bin": "esptool.py -p com8 write_flash 0x5FE000 profile.bin"
ВНИМАНИЕ Через USB порт модуля ESP32C6 не происходит запись с помощью утилиты esptool.py. Для записи необходимо использовать инструмент для загрузки Flash, ссылка на описание и файл для скачивания: https://docs.espressif.com/projects/esp-test-tools/en/latest/esp32/production_stage/tools /flash_download_tool.html Кроме того через USB порт модуля ESP32C6 некоректно работает JTAG (при работе по команде idf.py monitor, если произошел сбой невозможно по сбросу "RESET" запустить монитор с начальной точки программы).
ПРИЛОЖЕНИЕ 4 НАСТРОЙКИ И РАБОТА С ПРОТОКОЛОМ MQTT
Соединение с брокером осуществляется согласно беспроводному протоколу WSS (Web Socket Secure).
Адрес в интернете (uri), порт, сертификат, имя пользователя, пароль, идентификационный номер клиента
- предоставляется брокером и записывается в программе, файл "app_mqtt_wss.c" конфигурация mqtt.
Кроме того в конфигурацию mqtt заносятся следующие настройки:
- keepalive = 30 сек. максимальное время, в течение которого клиент или брокер могут не отправлять управляющий пакет;
- формирование сообщения для подписанных пользователей при отключении от брокера LWT (topic = "status", msg = "off"). Сообщение отсылается через время равное: 1.5 х keepalive; При установлении соединения (новый сеанс) первым посылается сообщение: topic = "status", msg = "on", означающее что система подключилась.
ПРИЛОЖЕНИЕ 5 СПИСОК ИСПОЛЬЗОВАННОЙ ЛИТЕРАТУРЫ И ПРИМЕРОВ
1 При написании программы обмена информацией по TWAI использовалось описание в ESP-IDF: https://docs.espressif.com/projects/esp-idf/en/v5.4.1/esp32c6/api-reference/peripherals/twai.html Описание компонента: https://github.com/espressif/esp-idf/tree/v5.4.1/components/driver/twai А за основу взят пример из githab: https://github.com/espressif/esp-idf/tree/v5.4.1/examples/peripherals/twai/twai_network/twai_network_master
2 При написании программы подключения Ethernet через модуль по SPI использовалось описание в ESP-IDF: https://docs.espressif.com/projects/esp-idf/en/v5.4.1/esp32c6/api-reference/network/esp_eth.html А за основу взят пример из githab: https://github.com/espressif/esp-idf/tree/v5.4.1/examples/ethernet/basic
3 При написании программы подключения (soket) TCP в качестве клиента использовалось описание: https://circuitlabs.net/tcp-socket-programming-basics/ А за основу взят пример из githab: https://github.com/espressif/esp-idf/tree/v5.4.1/examples/protocols/sockets/tcp_client
4 В дальнейшем предыдущая программа была переработана под неблокирующий сокет, при этом использовалось описание действий и пример в: https://circuitlabs.net/non-blocking-socket-i-o-with-esp-idf/
5 При написании программы обмена данными по беспроводному интерфейсу wss (websocket security)за основу взят пример: https://circuitlabs.net/websocket-client-implementation/
6 При написании программы клиента MQTT с подключением через протокол WebSocket Secure использовалось описание ESP-IDF: https://docs.espressif.com/projects/esp-idf/en/v5.4.2/esp32c6/api-reference/protocols/mqtt.html Также протокол MQTT хорошо описан в: https://circuitlabs.net/courses/esp32-masterclass/ А за основу взят пример из githab: https://github.com/espressif/esp-idf/tree/v5.4.1/examples/protocols/mqtt/wss