
Современный автомобиль это не только средство передвижения, но и продвинутый гаджет с мультимедийными функциями и электронной системой управления агрегатами и кучей датчиков. Многие автопроизводители предлагают функции ассистентов движения, помощников при парковке, мониторинга и управления авто с телефона. Это возможно благодаря использованию в авто CAN шины к которой подключены все системы: двигатель, тормозная система, руль, мультимедиа, климат и др.
Мой автомобиль Skoda Octavia 2011 г. в. не предлагает возможностей управления с телефона, поэтому я решил исправить этот недостаток, а заодно и добавить функцию голосового управления. В качестве шлюза между CAN шиной и телефоном я использую Raspberry Pi с шилдом CAN BUS и WiFi роутер TP-Link. Протокол общения агрегатов авто закрытый, и на все мои письма предоставить документацию протокола Volkswagen отвечал отказом. Поэтому единственный способ узнать, как общаются устройства в авто и научиться ими управлять является реверс-инжиниринг протокола CAN шины VW.
Я действовал поэтапно:
- Разработка CAN шилда для Raspberry Pi
- Установка ПО для работы с CAN шиной
- Подключение к CAN шине авто
- Разработка сниффера и изучение протокола CAN шины
- Разработка приложения для телефона
- Голосовое управление с помощью Homekit и Siri
В конце видео голосового управления стеклоподъемником.
Разработка CAN шилда для Raspberry Pi
Схему шилда взял здесь lnxpps.de/rpie, там же и описание выводов, для общения с CAN используются 2 микросхемы MCP2515 и MCP2551. К шилду подключаются 2 провода CAN-High и CAN-Low. В SprintLayout 6 развел плату, может кому пригодится CANBoardRPi.lay (на заглавном фото прототип шилда на макетке).


Установка ПО для работы с CAN шиной
На Raspbian 2-x годичной давность мне потребовалось пропатчить bcm2708.c, чтобы добавить поддержку CAN (возможно сейчас это не требуется). Для работы с CAN шиной нужно установить пакет утилит can-utils с github.com/linux-can/can-utils, после этого подгрузить модули и поднять can интерфейс:
# initialize insmod spi-bcm2708 insmod can insmod can-dev insmod can-raw insmod can-bcm insmod mcp251x # Maerklin Gleisbox (60112 and 60113) uses 250000 # loopback mode for testing ip link set can0 type can bitrate 125000 loopback on ifconfig can0 up
Проверяем, что интерфейс CAN поднялся командой ifconfig:

Проверить, что все работает можно отправив команду и получив ее.
В одном терминале слушаем:
root@raspberrypi ~ # candump any,0:0,#FFFFFFFF
В другом терминале отправляем:
root@raspberrypi ~ # cansend can0 123#deadbeef
Более подробный процесс установки описан здесь lnxpps.de/rpie.
Подключение к CAN шине авто
Немного изучив открытую документацию на CAN шину VW я выяснил, что у меня используется 2 шины.
Шина CAN силового агрегата, передающая данные со скоростью 500 кбит/с, связывает все обслуживающие этот агрегат блоки управления.
Например, к шине CAN силового агрегата могут быть подключены следующие приборы:
- блок управления двигателем,
- блок управления АБС,
- блок управления системой курсовой стабилизации,
- блок управления коробкой передач,
- блок управления подушками безопасности,
- комбинация приборов.
Шина CAN системы «Комфорт» и информационнокомандной системы, позволяющая передавать данные со скоростью 100 кбит/с между обслуживающими эти системы блоками управления.
Например, к шине CAN системы «Комфорт» и информационно<командной системы могут быть
подключены следующие приборы:
- блок управления системой Climatronic или климатической установкой,
- блоки управления в дверях автомобиля,
- блок управления системой «Комфорт»,
- блок управления с дисплеем для радио и навигационной системы.
Получив доступ к первой можно у управлять движением (в моем варианте на механике, как минимум можно управлять круиз контролем), получив доступ ко второй можно управлять магнитолой, климатом, центральным замком, стеклоподъемниками, фарами и др.
Обе шины связаны через шлюз, который находится в области под рулем, так же к шлюзу подключен диагностический OBD2 разъем, к сожаление через OBD2 разъем нельзя послушать трафик от обеих шин, можно только передать команду и запросить состояние. Я решил, что буду работать только с шиной «Комфорт» и самым удобным местом подключения к шине оказался разъем в водительской двери.

Теперь я могу слушать, все что происходит в CAN шине «Комфорт» и отправлять команды.
Разработка сниффера и изучение протокола CAN шины

После того как я получил доступ к прослушиванию CAN шины, мне нужно расшифровать кто кому и что передает. Формат пакета CAN показан на рисунке.

Все утилиты из набора can-utils сами умеют разбирать CAN пакеты и отдают только полезную информацию, а именно:
- Идентификатор
- Длина данных
- Данные
Данные передаются в не зашифрованном виде, это облегчило изучение протокола. На Raspberry Pi я написал маленький сервер который перенаправляет данные с candump в TCP/IP, чтобы на компьютере разобрать поток данных и красиво показать их.
Для macOS я написал простое приложение, которое для каждого адреса устройства добавляет ячейку в табличку и в этой ячейке я уже вижу какие данные меняются.

Нажимаю кнопку стеклоподъемника я нашел ячейку в которой меняются данные, затем я и определил какие команды соответствуют нажатию вниз, нажатию вверх, удержанию вверх, удержанию вниз.
Проверить, что команда работает, можно отправив из терминала, например команду поднять левое стекло вверх:
cansend can0 181#0200
Команды, которые передают устройства по CAN шине в автомобилях VAG (Skoda Octavia 2011), полученные методом реверс-инжиниринг:
// Front Left Glass Up 181#0200 // Front Left Glass Down 181#0800 // Front Right Glass Up 181#2000 // Front Right Glass Down 181#8000 // Back Left Glass Up 181#0002 // Back Left Glass Down 181#0008 // Back Right Glass Up 181#0020 // Back Right Glass Down 181#0080 // Central Lock Open 291#09AA020000 // Central Lock Close 291#0955040000 // Update Light status of central lock (Когда отправляешь команду открыть/закрыть замок то на кнопке управления замком светодиод не изменяет состояние, чтобы он показал реальное состояние центрального замка, нужно отправить команду обновления) 291#0900000000
Мне было лень изучить все остальные устройства, поэтому в этом списке, только то что мне было интересно.
Разработка приложения для телефона
Используя полученные команды я написал приложение для iPhone, которое открывает/закрывает стекла и управляет центральным замком.
На Raspberry Pi я запустил 2 маленьких сервера, первый отправляет данные с candump в TCP/IP, второй принимает команды от iPhone и передает их cansend.

Исходники приложения управления авто для iOS
// // FirstViewController.m // Car Control // // Created by Vitaliy Yurkin on 17.05.15. // Copyright (c) 2015 Vitaliy Yurkin. All rights reserved. // #import "FirstViewController.h" #import "DataConnection.h" #import "CommandConnection.h" @interface FirstViewController () <DataConnectionDelegate> @property (nonatomic, strong) DataConnection *dataConnection; @property (nonatomic, strong) CommandConnection *commandConnection; @property (weak, nonatomic) IBOutlet UILabel *Door_1; @property (weak, nonatomic) IBOutlet UILabel *Door_2; @property (weak, nonatomic) IBOutlet UILabel *Door_3; @property (weak, nonatomic) IBOutlet UILabel *Door_4; @property (weak, nonatomic) IBOutlet UIButton *CentralLock; - (IBAction)lockUnlock:(UIButton *)sender; @end @implementation FirstViewController - (void)viewDidLoad { self.dataConnection = [DataConnection new]; self.dataConnection.delegate = self; [self.dataConnection connectToCanBus]; self.commandConnection = [CommandConnection new]; [self.commandConnection connectToCanBus]; } - (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated. } - (void)doorStatusChanged:(char)value { /* 1 - Front Left Door 2 - Front Right Door 4 - Back Left Door 8 - Back Right Door 3 - Front Left&Right Door = 1 + 3 5 - Front& Back left Door = 1 + 4 */ // Front Left Door if (value & 1) { self.Door_1.backgroundColor = [UIColor yellowColor]; self.Door_1.text = @"Открыто"; NSLog(@"1"); } else { self.Door_1.backgroundColor = [UIColor lightGrayColor]; self.Door_1.text = @"Закрыто"; } // Front Right Door if (value & 2) { self.Door_2.backgroundColor = [UIColor yellowColor]; self.Door_2.text = @"Открыто"; NSLog(@"2"); } else { self.Door_2.backgroundColor = [UIColor lightGrayColor]; self.Door_2.text = @"Закрыто"; } // Back Left Door if (value & 4) { self.Door_3.backgroundColor = [UIColor yellowColor]; self.Door_3.text = @"Открыто"; NSLog(@"4"); } else { self.Door_3.backgroundColor = [UIColor lightGrayColor]; self.Door_3.text = @"Закрыто"; } // Back Right Door if (value & 8) { self.Door_4.backgroundColor = [UIColor yellowColor]; self.Door_4.text = @"Открыто"; NSLog(@"8"); } else { self.Door_4.backgroundColor = [UIColor lightGrayColor]; self.Door_4.text = @"Закрыто"; } } BOOL firstStatusChange = YES; BOOL lastStatus; -(void) centralLockStatusChanged:(BOOL)status { // At first status changes set lastStatus variable if (firstStatusChange) { firstStatusChange = NO; // Invert status, to pass the next test lastStatus = !status; } // Change Lock image only if status changed if (!(lastStatus == status)) { // Check status if (status) { [self.CentralLock setBackgroundImage:[UIImage imageNamed:@"lock_close"] forState:UIControlStateNormal]; } else { [self.CentralLock setBackgroundImage:[UIImage imageNamed:@"lock_open"] forState:UIControlStateNormal]; } lastStatus = status; } } // Front Left Glass - (IBAction)frontLeftUp:(UIButton *)sender { [self.commandConnection sendMessage:@"cansend can0 181#0200"]; } - (IBAction)frontLeftDown:(id)sender { [self.commandConnection sendMessage:@"cansend can0 181#0800"]; } // Front Right Glass - (IBAction)frontRightUp:(UIButton *)sender { [self.commandConnection sendMessage:@"cansend can0 181#2000"]; } - (IBAction)frontRightDown:(id)sender { [self.commandConnection sendMessage:@"cansend can0 181#8000"]; } // Back Left Glass - (IBAction)backLeftUp:(UIButton *)sender { [self.commandConnection sendMessage:@"cansend can0 181#0002"]; } - (IBAction)backLeftDown:(id)sender { [self.commandConnection sendMessage:@"cansend can0 181#0008"]; } // Back Right Glass - (IBAction)backRightUp:(UIButton *)sender { [self.commandConnection sendMessage:@"cansend can0 181#0020"]; } - (IBAction)backtRightDown:(id)sender { [self.commandConnection sendMessage:@"cansend can0 181#0080"]; } - (IBAction)lockUnlock:(UIButton *)sender { // If central lock closed if (lastStatus) { // Open [self.commandConnection sendMessage:@"cansend can0 291#09AA020000"]; int64_t delayInSeconds = 1; // 1 sec dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [self.commandConnection sendMessage:@"cansend can0 291#0900000000"]; }); } else { // Close [self.commandConnection sendMessage:@"cansend can0 291#0955040000"]; int64_t delayInSeconds = 1; // 1 sec dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ [self.commandConnection sendMessage:@"cansend can0 291#0900000000"]; }); } } @end
Есть способ не писать свое приложение для телефона, а воспользоваться готовым из мира умных домов, всего лишь потребуется установиться на Raspberry Pi систему автоматизации Z-Way командой:
wget -q -O - razberry.z-wave.me/install | sudo bash
После этого добавляем наши CAN устройства в Z-Way систему автоматизации

И управляем стеклоподъемником как обычным выключателем:

Мобильный приложения для Z-Way: ZWay Home Control и ZWay Control.
Голосовое управление с помощью Homekit и Siri
В одной из своих статей я описывал процесс установки Homebridge на Raspberry Pi для голосового управления домашней системой автоматизации Z-Way. После установки Homebridge вы получите возможность голосового управления с помощью Siri. Уверен, что для Android есть множество приложений позволяющих голосом отправлять HTTP запросы для управления Z-Way.
Видео голосовогу управления стеклоподъемником прилагаю.
