Очередной "Умный дом", на этот раз модульная система...

riv
Offline
Зарегистрирован: 20.07.2017

Повтор случайно вылез.

MaksVV
Offline
Зарегистрирован: 06.08.2015

дак конечно шилд делать по размеру самой ноды

riv
Offline
Зарегистрирован: 20.07.2017

Добился более менее устойчивой работы сети.

В то же время все равно много ошибок на контроллерах RX/TX, до bus-off не доходит но серьезно смущает. 

riv
Offline
Зарегистрирован: 20.07.2017

Еще по какой то причине лезут дубли (а то и 3-5) отправленных сообщений. Т.е отправляю одно сообщение а по сети их принимают по 2-5 раз.

Seltvik
Seltvik аватар
Offline
Зарегистрирован: 24.10.2013
MaksVV
Offline
Зарегистрирован: 06.08.2015

riv пишет:
Добился более менее устойчивой работы сети.

а каким образом? все-таки репитеры шлюзы? можешь схему сети нарисовать ? топологию

ВН
Offline
Зарегистрирован: 25.02.2016

riv пишет:

Добился более менее устойчивой работы сети.

 

Извините, лень все читать, что вашу сеть держит? т.е. какое устройство обеспечивает стабильность работы потока мастер- слейв?

 

riv
Offline
Зарегистрирован: 20.07.2017

Подкрутил все кабели, все разъемы, соединил все экраны, подобрал места установки терминаторов путем отслеживания ошибок. Кстати в центре я все соединил без коммутатора (это узлы 6,8) на одном из них терминатор. Без него были ошибки. (На каком то буржуйском сайте производителя умных домов прочитал что в центре звезды тоже надо ставить терминатор)

Вот топология, но от коммутатора отказался. 

riv
Offline
Зарегистрирован: 20.07.2017

ВН пишет:

riv пишет:

Добился более менее устойчивой работы сети.

Извините, лень все читать, что вашу сеть держит? т.е. какое устройство обеспечивает стабильность работы потока мастер- слейв?

На базе нашей совместной с MaxVV работы я пишу библиотеку https://github.com/graynet-dev/aHomeBus2

Пока основная задача добиться стабильной работы сети, ее контроля и авто восстановления узлов при сбоях.

Если заинтересует выложу здесь последнюю версию. (так и не разобрался как на гитхаб синхронизироваться)

В центре 2 DUE они держат CAN сеть и являются шлюзами к Ethernet (socket, ModbusTCP, MQTT)

Абоненты Mega2560 pro у них GPIO + ModbusRTU для связи с контроллерами управления шторами, управление вентиляцией и отоплением.

riv
Offline
Зарегистрирован: 20.07.2017

По поводу повторителя. Так с ним и не разобрался. Думаю просто не совместимость MCP2551 и TJA1050 с модуля MCP2515.

Проблема выражалась в дублировании сообщений ( а то и 2-5 повторов) коммутатором, причем и ошибок тоже. В итоге ошибки быстро нарастали и гасили контроллер.  Эту статью уже наизусть почти выучил http://microsin.net/adminstuff/hardware/mcp2515-stand-alone-can-controller-with-spi-interface.html

Уже и контроль регистров TEC и REC  устроил. И прерывание на прием настроил. А все сыпались ошибки. Причем как уже написал выше малейшая ошибка превращалась за счет дублей в шторм и поэтапную блокировку контроллеров (bus-off) при TEC>255

MCP2515 error modes state diagram fig6 1

 

riv
Offline
Зарегистрирован: 20.07.2017

Кстати прикупил тут по случаю для мультирума Комуутатор Kramer vs848 и Регулятор уровня Kramer VA-8xl у них 232, 485 есть.

Соответственно подключу через CAN в MQTT к Majordomo.  Нашел 8 канальный усилитель на али (У меня 7 помещений, в одном стерео, остальные моно)

 

ВН
Offline
Зарегистрирован: 25.02.2016

Спасибо за подробный ответ. 

riv пишет:
Пока основная задача добиться стабильной работы сети, ее контроля и авто восстановления узлов при сбоях.

По CАN, ничего не подскажу, могу только сказать, что, по описанным вами причина, у нас от нее отказались в пользу RS-422.  Лет 20-ть назад, это уже в другой конторе, мне также довелось разрабатывать протоколы местной сети устройств и тогда выбор тоже был сделан в пользу RS-422.  До сих пор прекрасно работает на сотнях объектов. 

riv
Offline
Зарегистрирован: 20.07.2017

От 485 я отказался из-за мономастерности.

Да и проблему удалось решить. Нужно просто соблюдать правила построения CAN и правила CKC для ВЧ сигналов. Как с 485 не прокатит.

Получил более или менее четкую картинку в сети.

MaksVV
Offline
Зарегистрирован: 06.08.2015

скорость 125 кбит/с?  думаю можно даже 100 поставить. Этого хватит

riv
Offline
Зарегистрирован: 20.07.2017

Сутки простоял на 100 кбит - без ошибок.

Сейчас поставил 250 кбит - пока ошибок нет.

tosic
Offline
Зарегистрирован: 14.05.2018

>На базе нашей совместной с MaxVV работы я пишу библиотеку https://github.com/graynet-dev/aHomeBus2
>Пока основная задача добиться стабильной работы сети, ее контроля и авто восстановления узлов при сбоях.
>
Если заинтересует выложу здесь последнюю версию. (так и не разобрался как на гитхаб синхронизироваться)

Это библиотека может работать только как мастер-слейв ?

как мастер-мастер сможет работать ? если в сети все мастера )

Например если выключатель, который при нажатии должен отправить сигнал на включение света, и получить статус что бы поменять цвет индикации. ( тут слейв-мастер может жить) Дальше усложнение. Если с телефона я отправил команду включить свет, свет включился, но выключатель должен все равно сменить цвет индикации (поменять состояния на "включен") 

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

riv назвал "мастер" "слейв" не в контексте канального уровня сети (это как в модбас и других сетях на RS485 нормальная коммуникация и порядок в сети обычно обеспечивается мастером, который общается со слейвами), а в контексте функционала узлов. (Имхо, действительно, чтобы не вносить путаницу, лучше бы назвал "шлюз").  При этом инициировать передачу сообщений может любой узел в любой момент времени.  Т.е. у riv, я так понял, мастер является шлюзом между CAN сетью и другими коммуникациями, а слейв - просто запасной - следит за работой мастера, и если тот погиб, берёт его обязанности на себя. 

Так что для осуществления вашей задачи никаких проблем нет. 

tosic
Offline
Зарегистрирован: 14.05.2018

проблема только остается в запихнуть это все в arduino nano на 8 битном)

Спасибо за толкование  

riv
Offline
Зарегистрирован: 20.07.2017

tosic пишет:

проблема только остается в запихнуть это все в arduino nano на 8 битном)

Спасибо за толкование  


MaxVV прав, насчёт логического уровня, 4 в в модели OSI. В сети есть 3 типа узлов master, slave, node. Master и Slave это шлюзы в Ethernet на Majordomo и контроллеры сети.

На Nano к сожалению либа не заточена, расчет на DUE master/slave и 2560 node. Хотя можно сделать либу и для Nano упростив код.

Просто исходя из названия темы мы делали модульную прошивку. Т. е. в node заливается одна и та же прошивка на всей сети. Функционал определяется настройками. Так же любой node работает шлюзом в modbus, uart и пр. сети.

MaksVV
Offline
Зарегистрирован: 06.08.2015

tosiс, есть вариант использовать 19 ver. нашей прошивки, которая была то того, как riv начал делать библиотеку. Она конечно не простая в понимании, но могу снять видео как всё настраивать. 

tosic
Offline
Зарегистрирован: 14.05.2018

спасибо MaksVV

я пока изучаю вашу реализацию, так как моя толком не заработала ( времени пока мало, но скоро я продолжу), вы выбрали CAN по определенным принципам, и у вас работает прототип. Я пытался на rsXXX ( но от can пока не отказался, так как есть ТЗ)

  Моя задача заключается в том, что бы это вся сборка уместилась в одном выключателе в 6см круглой коробочке. Куда подходит только витая пара ( топология звезда) А распайка дополнительно я can схемы состоящей их 2 немалых микросхем - усложняет реализацию. Также в выключателе помимо основной задачи включать и выключать (2-6 каналов) имеется дополнительные датчики температуры, света, IK приемник.

Данные с выключателя по температуре отправляются по интервалу. С ик датчика по активности. Датчик света тоже по активности, ну и включение света по действию. Если учесть что таких выключателей планируется 20 шт по дому, то сеть должна выдержать такой флуд. 

Учитывая размеры устройства, я думаю опять вернуться к rs485 \ 422 и продолжить тестировать доставки пакетов широковещательно.

riv
Offline
Зарегистрирован: 20.07.2017

tosic, самый простой для Вас вариант выдрать из нашего кода формирователь и парсер сообщений CAN, или использовать пример из библиотеки mcp2515. Там есть даже CAN to Ethernet. А контроль сети организовать либо более мощным контроллером либо Линукс машиной. Универсальности не добьётесь но работать на nano будет. Ещё вариант использовать esp32, в нем встроенный can нужен только преобразователь уровней сигнала.

MaksVV
Offline
Зарегистрирован: 06.08.2015

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

riv
Offline
Зарегистрирован: 20.07.2017

MaksVV, приветствую.

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

Работа на 250 кбит.

 

Следующая итерация контроль доставки на уровне нашего протокола. Пока думаю простую реализацию с передачей сообщения, ожиданием подтверждения ее получения в заданный период от получателя, в случае не получения подтверждения переповтор и так 3 раза, потом выставляем этот узел как аварийный в своей таблице, кидаем мастеру/слейву сообщение о аварийном соседе, приступаем к диагностике (периодически кидаем запрос на ответы от узла) если по результатам диагностики узел появился в сети опять включаем его в таблицы доступных и разрешаем на него маршрутизацию.

Ни контрольных сумм ни транзакционных механизмов делать пока не хочу. Can как бы делает все за нас на своем урони и поднимать в наш протокол не хочу.

Да основной ответ на вопрос зачем. Т.к мы на ардуине то работаем в жестком последовательном цикле. Увеличение кол-ва функционала приведет иногда к тому что мы просто сбросим принятый CAN конроллером пакет по переполнению обоих буферов не успев их изъять МК для обработки. Вот и вылез переповтор. Можно конечно лезть во FreeRTOS или писать свое разделение времени, и ли по пробовать attachInterrupt (у меня кстати не получилось его использовать внутри класса) но думаю этого должно хватить. 

В общем с выходных поставлю тест на передачу например 5 сообщений в секунду с каждого узла , а потом реализацию протокола гарантированной доставки.

P.S. Кстати я помню ты вроде разобрался с масками и фильтрами библиотеки mcp_can

1_interface.init_Mask(0,1,0x000FF000);    //Маскируем 1 байт                   // Init first mask...
1_interface.init_Filt(0,1,0x00000000);   //Ib бродкаст                    // Init first filter...
2_interface.init_Filt(1,1,0x00013000);  //адрес узла                    // Init second filter...
3 
4_interface.init_Mask(1,1,0x00000000);                       // Init second mask... 
5  
6_interface.init_Filt(2,1,0x00000000);                       // Init 3 filter...
7_interface.init_Filt(3,1,0x00000000);                       // Init 4 filter...
8_interface.init_Filt(4,1,0x00000000);                       // Init 5 filter...
9_interface.init_Filt(5,1,0x00000000);                       // Init 6 filter...
//     E                DD             CC              B            AA             
// pocket type     command&ansver  target addres  target port  source address 
//  1^2=2             2^8=256       2^8=256         2^4=16        2^8=256

Хочу принимать пакеты только для себя destination address = NodeID или широковещательно destination address = 0

Подскажи как сформировать маску и фильтры. 

sadman41
Offline
Зарегистрирован: 19.10.2016

А мне вот в стародавние времена пришлось в CAN frame payload засунуть свой CRC-byte. Почему-то иногда приходило не то, что ожидал. Времени на раскачку не было, поэтому рассказать почему так - не могу. Но могу предупредить об этом ))

riv
Offline
Зарегистрирован: 20.07.2017

sadman41 пишет:

А мне вот в стародавние времена пришлось в CAN frame payload засунуть свой CRC-byte. Почему-то иногда приходило не то, что ожидал. Времени на раскачку не было, поэтому рассказать почему так - не могу. Но могу предупредить об этом ))

Честно говоря, CANHacker мне не показал битых пакетов по крайней мере в заголовке. А там все же 4 байта без сбоев. 

Жалко 1 байт из 8 отдавать под CRC. Но буду иметь ввиду. 

Видимо SDO придется реализовывать. Я честно говоря не хотел. Думал лучше больше команд.

Да кстати я скоро дойду до логики следующего уровня. Не хочешь продолжить?

У меня сделано 3 класса которые различаются для мастера, слейва, узла. В них должны храниться базовые функции 4 уровня. З уровень с маршрутизацией я буду добивать. А в 4 уровне ты традиционно силен. Я про твою логику по установке пинов, контролю исполнения команд и пр.

Я добьюсь гарантии доставки сообщения/команды а ты гарантии исполнения команды. Я имею ввиду перенос логики из твоей 19 версии. Кстати еще работу с EEPROM нужно добить. Конфиг писать и читать. У меня пока только адрес.

sadman41
Offline
Зарегистрирован: 19.10.2016

Это , наверное, Максу предложение? У меня никакой 19 версии нет.

riv
Offline
Зарегистрирован: 20.07.2017

Вот же незадача. Сюда редко кто кроме MaxVV пишет. С телефона не обратил внимания на автора.

Да это Максу предложение.

Хотя я думаю он не будет против соавторов.

sadman41
Offline
Зарегистрирован: 19.10.2016

Соавтором я не выступлю, у меня нет такой системы для тестов. Но поумничать могу. Например - насчет attachInterrupt. Если у вас и так цикл крутится с проверкой, то видимого смысла в прерывании нет. В обработчике нормально со SPI не поработать, а выставление в нем флага "message arrived" и проверка его в лупе эквивалентна простой проверке ноги, куда INT заведен.

Кстати, Макс, как в автомобилях разруливается ситуация убийства фреймов при коллизиях? Например - фрейм от двигателя убил фрейм для управления фарой... Перепосылка идёт периодическая или используется механизм "посылка - подверждение получения"

riv
Offline
Зарегистрирован: 20.07.2017

attachInterrupt нужен чтобы прервать цикл, т.к если на МК крутится много логик, то как не крутись в момент прихода сообщения МК может быть занят, причем если сообщений много то буфер будет переполнен и CAN контроллер перестанет принимать сообщения. Поэтому нужно по приему сообщения выйти из loop и функций которые внутри него крутятся и обработать Receive.

А у нас во первых библиотека, а во во вторых класс.  А чудная функция attachInterrupt(interrupt, function, mode) не может ни принимать не возвращать параметров.

riv
Offline
Зарегистрирован: 20.07.2017

.

sadman41
Offline
Зарегистрирован: 19.10.2016

Флеймить не буду, однако не очень себе представляю этот "выход из loop по прерыванию".

riv
Offline
Зарегистрирован: 20.07.2017

sadman41 пишет:

Флеймить не буду, однако не очень себе представляю этот "выход из loop по прерыванию".

Если loop большой и в нем большое кол-во операций то пока мы их не выполним мы не начнем считывать буфер CAN в МК. Прерывание позволяет прервать текущее исполнение и выполнить код считывания и обработки буфера а потом вернуться в то же место продолжать считывать датчики вычислять что нибудь и пр.

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну, теперь более-менее ясно. Вы пробовали в обработчике прерывания оперировать интерфейсами (в частности - SPI), которые требуют прерываний для работы?

MaksVV
Offline
Зарегистрирован: 06.08.2015

riv пишет:

MaksVV, приветствую.

P.S. Кстати я помню ты вроде разобрался с масками и фильтрами библиотеки mcp_can

//     E                DD             CC              B            AA             
// pocket type     command&ansver  target addres  target port  source address 
//  1^2=2             2^8=256       2^8=256         2^4=16        2^8=256

Хочу принимать пакеты только для себя destination address = NodeID или широковещательно destination address = 0

Подскажи как сформировать маску и фильтры. 

01byte node_address = 0x13;             // my address
02_interface.begin(MCP_STDEXT, CAN_250KBPS, MCP_8MHZ);
03_interface.init_Mask(0,1,0x000FF000); // first маска на разряд ID "target_ADDR"
04_interface.init_Filt(0,1,(node_address & 0xFFFFFFFF)<<12); // пропускаем мессаги только которые нам
05_interface.init_Filt(1,1,0x00000000); // пропускаем мессаги броадкасты
06_interface.init_Mask(1,1,0x000FF000); // second маска на разряд ID "target_ADDR"
07_interface.init_Filt(2,1,0x00000000); // пропускаем мессаги броадкасты
08_interface.init_Filt(3,1,0x00000000); // пропускаем мессаги броадкасты
09_interface.init_Filt(4,1,0x00000000); // пропускаем мессаги броадкасты
10_interface.init_Filt(5,1,0x00000000); // пропускаем мессаги броадкасты
11_interface.setMode(MCP_NORMAL);       // Change to normal mode
MaksVV
Offline
Зарегистрирован: 06.08.2015

sadman41 пишет:
Кстати, Макс, как в автомобилях разруливается ситуация убийства фреймов при коллизиях? Например - фрейм от двигателя убил фрейм для управления фарой... Перепосылка идёт периодическая или используется механизм "посылка - подверждение получения"

у всех по разному, так глубоко я не копал. Но важные данные по-любому с меньшим ID будут , т.к. они выиграют арбитраж. Поэтому в 11 ID битных сетях диагностика вся идёт с ID  0x7_ _ , потому как это не особо важные данные .

Просто данные (ну например датчиков каких-то, особенно которые очень часто обновляются - 10...50мс) естественно без всяких подтверждений в шину сыплются. А вот команды обычно с подтверждением. При коллизиях команды , скорее всего перепосылаются, если нет ответа.

Трудно такие моменты отслеживать, редко они. Да и не было нужды. 

sadman41
Offline
Зарегистрирован: 19.10.2016

MaksVV пишет:

Просто данные (ну например датчиков каких-то, особенно которые очень часто обновляются - 10...50мс) естественно без всяких подтверждений в шину сыплются. А вот команды обычно с подтверждением. При коллизиях команды , скорее всего перепосылаются, если нет ответа.

Вот у меня тоже метрики уходят с ноды на гейтвей без подтверждения. Пока нод немного, вроде всё ок. Но, думаю, что это не очень мило, ибо есть вероятность, что нода с самым мелким ID теоретически может оккупировать канал. Да и надо бы срез из ~20 метрик передавать с гарантией получения гейтвеем в течении TTL...

MaksVV
Offline
Зарегистрирован: 06.08.2015

какая частота обновления метрик? сделай с гейтвея бродкаст текущее время. Ответом от нодов будут метрики. Каждая нода отвечает метриками с задержкой (которая зависит от ID ноды) относительно получения бродкаста от гейтвея, так ответы от всех нодов будут разнесены по времени и шанс коллизий минимизируется

sadman41
Offline
Зарегистрирован: 19.10.2016

Где-то секунд 5 ставил +/- рандомайз. Гейтвей всё равно выстреливает вверх раз в пять минут где-то - чаще смысла нет.

Но коллизии всё равно могут быть и в "выстрел" гейтвея могут попадать метрики из разных срезов. 

Поэтому интересно, как это в Real-time системах устроено.

MaksVV
Offline
Зарегистрирован: 06.08.2015

да ты чё, нифига там не будет, если раз в 5 сек. Для кана это вообще ниочем. ведь там аппаратные буферы, если арбитраж проиграла метрика, она отправится опять, как только шина освободится. Это надо умудриться отправку полностью убить, для этого надо шину загрузить по самое не хочу

sadman41
Offline
Зарегистрирован: 19.10.2016

Heartbreath я в шину выдаю для автоподбора скорости шины.  Но на транзакции пока не хочу переходить - сильно уж геморно и сразу всё сваливается к логике 485-й шины, убивая всю прелесть CAN.

MaksVV
Offline
Зарегистрирован: 06.08.2015

не согласен. В кане пусть порядок все же будет (синхра), но если экстренно что то нужно можно и без спросу данные послать (понятно что лучше с меньшим ID). В 485 так низя. 

Я сделал отправку данных (параметров  с датчиков и т.д.) по синхре времени, а всё остальное по желанию ноды -  когда хочет тогда шлёт. В основном это команды. И команды у меня с подтверждением выполнения. 

sadman41
Offline
Зарегистрирован: 19.10.2016

Ну, вишь, у меня односторонний поток - метрики к гейтвею. Основная цель - сохранить срез, чтобы напряжение фазы не пришло на мониторинг с током от предыдущего измерения. Так что идея с синхрой по hearthbreath вроде нормальная. Пока не пришлось досрочно срезы с максимумами, к примеру, отсылать на гейтвей...  

MaksVV
Offline
Зарегистрирован: 06.08.2015

сделай тогда уникальный номер измерения, и пусть вверх отправляются только с одинаковыми номерами

sadman41
Offline
Зарегистрирован: 19.10.2016

Measure number я уже внедрил, токо payload тесен стал - не влезаю в 8 байт )) Хотел от CRC избавиться, но вспомнил, что хрень получал как-то вместо пакетов. Хотя, казалось бы, CAN их должен был на входе отстрелить.

MaksVV
Offline
Зарегистрирован: 06.08.2015

странно, да, по идее если сообщение прошло, оно уже проCRCлось. 29 ID можно использовать если сидишь на 11 битных. Туда часть инфы запихать. 

MaksVV
Offline
Зарегистрирован: 06.08.2015

riv , ты опять сменил назначение разрядов ID ?

riv
Offline
Зарегистрирован: 20.07.2017

MaksVV пишет:

riv , ты опять сменил назначение разрядов ID ?

Если ты обратил внимание библиотека это форк https://github.com/adlerweb/asysbus

Поэтому временно я не стал менять его разряды пока не разберусь как работает ряд фич.

После этого перейду на нашу разрядность.

P.S.

У Florian Knodt есть 2 интересных фичи - это хуки пинов и хуки пользовательских функций. 

Я сейчас сетевой уровень добиваю. Контроль и маршрутизацию в сети. Поменять абстракцию можно в лет.

Вот смотри так реализована в классе CAN передача

01bool AHB_CAN::ahbSend_V(uint8_t type, uint8_t cmd, uint8_t target, uint8_t port, uint8_t source,  uint8_t len, byte data[8]) {
02        uint32_t addr = ahbCanAddrAssemble(type, cmd, target, port, source);
03        if(addr == 0){
04          //Serial.println(F("TX Message send addr=0"));
05          return false;
06        }
07         
08        lastErr = _interface.sendMsgBuf(addr, 1, len, data);
09        if(lastErr != CAN_OK) {
10          Serial.println("CAN ahbSend lastErr = CAN_FAIL");
11          return false;
12        }
13        else{
14            Serial.println("CAN ahbSend lastErr = CAN_OK");
15 
16            }
17            Serial.println();
18 
19        }
20        return true;
21}
01uint32_t AHB_CAN::ahbCanAddrAssemble(uint8_t f_type, uint8_t f_cmd, uint8_t f_target, uint8_t f_port, uint8_t f_source) {
02          uint32_t addr = 0x80000000;
03  
04          if(f_type == AHB_PKGTYPE_UNICAST) {
05            if(f_target > 255) {
06              //Serial.println("f_target > 255");
07              return 0;}
08            if(f_port < 0 || f_port > 15) {
09              //Serial.println("f_port < 0 || f_port > 15");
10              return 0;
11            }
12          }
13          else{
14            if(f_target > 255) {
15              //Serial.println("f_target > 255");
16              return 0;
17            }
18          }
19 
20          if(f_source > 255) {
21            //Serial.println("f_source > 255");
22            return 0;
23          }
24           
25          addr = (f_type & 0xFFFFFFFF)<<28 | (f_cmd & 0xFFFFFFFF)<<20 | (f_target & 0xFFFFFFFF)<<12 | (f_port & 0xFFFFFFFF)<<8 | f_source ;
26 
27          return addr;
28}

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

riv пишет:
Вот смотри так реализована в классе CAN передача

ага, всё как у нас почти, только разряды другие. 

riv
Offline
Зарегистрирован: 20.07.2017

MaksVV пишет:

riv пишет:
Вот смотри так реализована в классе CAN передача

ага, всё как у нас почти, только разряды другие. 

В оригинале было так. Он не использовал ниблы, а пошел более оптимальным путем с т.з. использования адресного пространства.

01unsigned long ASB_CAN::asbCanAddrAssemble(byte type, unsigned int target, unsigned int source, char port) {
02      unsigned long addr = 0x80000000;
03 
04      if(type > 0x03) return 0;
05      addr |= ((unsigned long)type << 28);
06 
07      if(type == ASB_PKGTYPE_UNICAST) {
08        if(target > 0x7FF) return 0;
09        if(port < 0 ||port > 0x1F) return 0;
10 
11        addr |= ((unsigned long)port << 23);
12      }else{
13        if(target > 0xFFFF) return 0;
14      }
15 
16      addr |= ((unsigned long)target << 11);
17 
18      if(source > 0x7FF) return 0;
19      addr |= source;
20 
21      return addr;
22}
23 
24bool ASB_CAN::asbSend(byte type, unsigned int target, unsigned int source, char port, byte len, const byte *data) {
25    unsigned long addr = asbCanAddrAssemble(type, target, source, port);
26    if(addr == 0) return false;
27 
28    lastErr = _interface.sendMsgBuf(addr, 1, len, data);
29    if(lastErr != CAN_OK) return false;
30    return true;
31}