Hacking CAN bus automático para controle de voz



Um carro moderno não é apenas um meio de transporte, mas também um dispositivo avançado com funções multimídia e um sistema de controle eletrônico para unidades e um monte de sensores. Muitos fabricantes de automóveis oferecem as funções de assistentes de movimento, assistentes de estacionamento, monitoramento e controle de automóveis por telefone. Isso é possível devido ao uso de um barramento CAN no carro ao qual todos os sistemas estão conectados: motor, sistema de freio, volante, multimídia, clima, etc.

Meu carro é o Skoda Octavia 2011. Ele não oferece recursos de controle do telefone, então decidi corrigir essa desvantagem e, ao mesmo tempo, adicionar a função de controle de voz. Como gateway entre o barramento CAN e o telefone, eu uso o Raspberry Pi com a blindagem CAN BUS e o roteador WiFi TP-Link. O protocolo de comunicação dos agregados automáticos está fechado e a Volkswagen respondeu a todas as minhas cartas com a documentação do protocolo. Portanto, a única maneira de descobrir como os dispositivos se comunicam nos carros e aprender a gerenciá-los é a engenharia reversa do protocolo CAN do barramento VW.

Atuei em etapas:

  1. Desenvolvimento do escudo CAN para Raspberry Pi
  2. Instalando o software para trabalhar com o barramento CAN
  3. Conexão ao barramento CAN de um carro
  4. Desenvolvimento de um sniffer e estudo do protocolo de barramento CAN
  5. Desenvolvimento de aplicativos para telefone
  6. Controle de voz com Homekit e Siri

No final da janela de controle de voz do vídeo.

Desenvolvimento do escudo CAN para Raspberry Pi


Aqui, o esquema de blindagem foi adotado pelo lnxpps.de/rpie , também há uma descrição das conclusões, 2 microcircuitos MCP2515 e MCP2551 são usados ​​para se comunicar com o CAN. 2 fios CAN-High e CAN-Low estão conectados à blindagem. No SprintLayout 6, espalhei o quadro, alguém pode ser útil no CANBoardRPi.lay (na foto do título, o protótipo do escudo na placa de ensaio).





Instalando o software para trabalhar com o barramento CAN


No Raspbian de 2 anos, eu precisava corrigir o bcm2708.c para adicionar suporte ao CAN (talvez isso não seja necessário agora). Para trabalhar com o barramento CAN, você precisa instalar o pacote do utilitário can-utils em github.com/linux-can/can-utils , depois carregar os módulos e aumentar a interface do 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

Verificamos que a interface CAN aumentou com o comando ifconfig :



Você pode verificar se tudo funciona enviando e recebendo o comando.

Em um terminal, ouvimos:

root@raspberrypi ~ # candump any,0:0,#FFFFFFFF

Em outro terminal, enviamos:

root@raspberrypi ~ # cansend can0 123#deadbeef

Um processo de instalação mais detalhado é descrito aqui lnxpps.de/rpie .

Conexão ao barramento CAN de um carro


Após um pequeno estudo da documentação aberta no barramento VW CAN, descobri que utilizo 2 barramentos.

O barramento CAN da unidade de energia , transmitindo dados a uma velocidade de 500 kbit / s, conecta todas as unidades de controle que atendem a esta unidade.

Por exemplo, os seguintes dispositivos podem ser conectados ao barramento CAN de uma unidade de energia:

  • unidade de controle do motor
  • Unidade de controle ABS
  • unidade de controle de estabilização de percurso,
  • unidade de controle da caixa de velocidades,
  • unidade de controle do airbag,
  • painel de instrumentos.

O barramento CAN do sistema Comfort e o sistema de comando da informação , que permite transferir dados a uma velocidade de 100 kbit / s entre as unidades de controle que atendem a esses sistemas.

Por exemplo, os
seguintes dispositivos podem ser conectados ao barramento CAN do sistema Comfort e ao sistema de informações <comando :

  • Unidade de controle Climatronic ou sistema de ar condicionado,
  • unidades de controle nas portas do carro,
  • Unidade de controle do sistema Comfort,
  • unidade de controle com display para sistema de rádio e navegação.

Tendo acesso ao primeiro, você pode controlar o tráfego (na minha versão sobre mecânica, você pode pelo menos controlar o controle de cruzeiro), tendo acesso ao segundo, você pode controlar o rádio, o clima, o travamento central, as janelas elétricas, os faróis, etc.

Ambos os ônibus estão conectados através do gateway, localizado na área sob o volante, o conector OBD2 de diagnóstico também está conectado ao gateway. Infelizmente, o tráfego de ambos os barramentos não pode ser ouvido através do conector OBD2, você pode apenas enviar um comando e solicitar um status. Decidi que trabalharia apenas com o ônibus Comfort e o conector na porta do motorista era o local mais conveniente para conectar-se ao ônibus.



Agora eu posso ouvir tudo o que acontece no Comfort CAN e enviar comandos.

Desenvolvimento de um sniffer e estudo do protocolo de barramento CAN




Depois de ter acesso a ouvir o barramento CAN, preciso descriptografar quem passa para quem e o quê. O formato do pacote CAN é mostrado na figura.



Todos os utilitários do conjunto can-utils podem analisar pacotes CAN e fornecer apenas informações úteis, a saber:

  • ID
  • Comprimento dos dados
  • Dados

Os dados são transmitidos de forma não criptografada, facilitando o estudo do protocolo. No Raspberry Pi, escrevi um pequeno servidor que redireciona dados do candump para o TCP / IP para analisar o fluxo de dados no computador e mostrá-lo lindamente.

Para o macOS, escrevi um aplicativo simples que, para cada endereço de dispositivo, adiciona uma célula ao tablet e nessa célula já consigo ver quais dados estão sendo alterados.



Pressiono o botão da janela de energia, encontrei uma célula na qual os dados mudam e depois determinei quais comandos correspondem a pressionar, pressionar, segurar, segurar.

Você pode verificar se o comando funciona enviando-o do terminal, por exemplo, o comando para levantar o vidro esquerdo:

cansend can0 181#0200

Equipes que transmitem dispositivos via barramento CAN em carros VAG (Skoda Octavia 2011), recebidas por engenharia reversa:

// 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

Eu estava com preguiça de estudar todos os outros dispositivos, então nesta lista, apenas o que era interessante para mim.

Desenvolvimento de aplicativos para telefone


Usando os comandos recebidos, escrevi um aplicativo para o iPhone que abre / fecha janelas e controla a trava central.

No Raspberry Pi, lancei 2 pequenos servidores, o primeiro envia dados do candump para o TCP / IP, o segundo recebe comandos do iPhone e envia para eles.


Controle automático de fontes de aplicativos para 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


Existe uma maneira de não escrever seu aplicativo para o telefone, mas para tirar proveito do mundo pronto das residências inteligentes, você só precisa instalar o sistema de automação Z-Way no Raspberry Pi com o comando:

wget -q -O - razberry.z-wave.me/install | sudo bash

Depois disso, adicionamos nossos dispositivos CAN ao sistema de automação Z-Way.


E controlamos o


regulador de janelas como uma chave normal: aplicativos móveis para Z-Way: ZWay Home Control e ZWay Control.

Controle de voz com Homekit e Siri


Em um dos meus artigos, descrevi o processo de instalação do Homebridge em um Raspberry Pi para controle de voz do sistema de automação residencial Z-Way . Depois de instalar o Homebridge, você poderá fazer o controle de voz com o Siri. Estou certo de que, para o Android, existem muitos aplicativos que permitem que a voz envie solicitações HTTP para controlar o Z-Way.

Anexo o vídeo ao controle de voz do regulador da janela.

Source: https://habr.com/ru/post/pt399043/


All Articles