Hacking CAN-Bus Auto für die Sprachsteuerung



Ein modernes Auto ist nicht nur ein Transportmittel, sondern auch ein fortschrittliches Gerät mit Multimedia-Funktionen und einem elektronischen Steuerungssystem für Einheiten und eine Reihe von Sensoren. Viele Autohersteller bieten die Funktionen von Bewegungsassistenten, Parkassistenten, Fahrzeugüberwachung und -steuerung über ein Telefon an. Dies ist möglich durch die Verwendung eines CAN-Busses im Auto, an den alle Systeme angeschlossen sind: Motor, Bremssystem, Lenkrad, Multimedia, Klima usw.

Mein Auto ist 2011 Skoda Octavia. Da das Telefon keine Steuerungsfunktionen bietet, habe ich beschlossen, diesen Nachteil zu beheben und gleichzeitig die Sprachsteuerungsfunktion hinzuzufügen. Als Gateway zwischen dem CAN-Bus und dem Telefon verwende ich den Raspberry Pi mit dem CAN-BUS-Schild und dem TP-Link-WLAN-Router. Das Kommunikationsprotokoll der Autoaggregate ist geschlossen, und Volkswagen hat auf alle meine Briefe mit der Dokumentation des Protokolls geantwortet. Der einzige Weg, um herauszufinden, wie Geräte in Autos kommunizieren und wie man sie verwaltet, ist das Reverse Engineering des CAN-Protokolls des VW-Busses.

Ich habe in Stufen gehandelt:

  1. Entwicklung eines CAN-Schildes für Raspberry Pi
  2. Installieren von Software für die Arbeit mit dem CAN-Bus
  3. Verbindung zum CAN-Bus eines Autos
  4. Entwicklung eines Sniffers und Untersuchung des CAN-Bus-Protokolls
  5. Telefon App Entwicklung
  6. Sprachsteuerung mit Homekit und Siri

Am Ende des Video-Sprachsteuerungsfensters.

Entwicklung eines CAN-Schildes für Raspberry Pi


Hier wurde das Abschirmungsschema von lnxpps.de/rpie übernommen , es gibt auch eine Beschreibung der Schlussfolgerungen, 2 Mikroschaltungen MCP2515 und MCP2551 werden zur Kommunikation mit CAN verwendet. 2 Drähte CAN-High und CAN-Low sind mit der Abschirmung verbunden. In SprintLayout 6 habe ich das Board ausgebreitet, kann jemand in CANBoardRPi.lay (im Titelfoto den Prototyp des Schildes auf dem Steckbrett) zur Hand kommen .





Installieren von Software für die Arbeit mit dem CAN-Bus


Auf dem 2-jährigen Raspbian musste ich bcm2708.c patchen, um CAN-Unterstützung hinzuzufügen (möglicherweise ist dies jetzt nicht erforderlich). Um mit dem CAN-Bus arbeiten zu können, müssen Sie das Dienstprogramm can-utils von github.com/linux-can/can-utils installieren , dann die Module laden und die Dosenschnittstelle anheben:

# 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

Wir überprüfen, ob die CAN-Schnittstelle mit dem Befehl ifconfig gestiegen ist :



Sie können überprüfen, ob alles funktioniert, indem Sie den Befehl senden und empfangen.

In einem Terminal hören wir:

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

In einem anderen Terminal senden wir:

root@raspberrypi ~ # cansend can0 123#deadbeef

Ein detaillierterer Installationsprozess wird hier unter lnxpps.de/rpie beschrieben .

Verbindung zum CAN-Bus eines Autos


Nach einem kleinen Studium der offenen Dokumentation zum VW-CAN-Bus stellte ich fest, dass ich 2 Busse benutze.

Der CAN-Bus des Netzteils , der Daten mit einer Geschwindigkeit von 500 kbit / s überträgt, verbindet alle Steuergeräte, die dieses Gerät bedienen.

Beispielsweise können folgende Geräte an den CAN-Bus eines Netzteils angeschlossen werden:

  • Motorsteuergerät
  • ABS-Steuergerät
  • Steuergerät für Kursstabilisierung,
  • Getriebesteuergerät,
  • Airbag-Steuergerät,
  • Kombiinstrument.

Der CAN-Bus des Comfort-Systems und das Informationsbefehlssystem , mit dem Daten mit einer Geschwindigkeit von 100 kbit / s zwischen den Steuergeräten übertragen werden können, die diese Systeme bedienen.

Beispielsweise können die
folgenden Geräte an den CAN-Bus des Comfort-Systems und das Informationsbefehlssystem angeschlossen werden :

  • Klimatronisches Steuergerät oder Klimaanlage,
  • Steuergeräte in den Autotüren,
  • Steuergerät für Komfortsystem,
  • Steuergerät mit Display für Radio und Navigationssystem.

Wenn Sie Zugriff auf den ersten haben, können Sie den Verkehr steuern (in meiner Version über die Mechanik können Sie zumindest den Tempomat steuern). Wenn Sie Zugriff auf den zweiten haben, können Sie Radio, Klima, Zentralverriegelung, elektrische Fensterheber, Scheinwerfer usw. steuern.

Beide Busse sind über das Gateway verbunden, das sich befindet Im Bereich unter dem Lenkrad ist auch der OBD2-Diagnoseanschluss mit dem Gateway verbunden. Leider kann der Verkehr von beiden Bussen nicht über den OBD2-Anschluss gehört werden. Sie können nur einen Befehl senden und einen Status anfordern. Ich entschied, dass ich nur mit dem Comfort-Bus arbeiten würde, und der Stecker in der Fahrertür erwies sich als der bequemste Ort, um eine Verbindung zum Bus herzustellen.



Jetzt kann ich alles hören, was auf dem Comfort CAN passiert, und Befehle senden.

Entwicklung eines Sniffers und Untersuchung des CAN-Bus-Protokolls




Nachdem ich Zugriff auf den CAN-Bus erhalten habe, muss ich entschlüsseln, wer an wen und was übergeht. Das CAN-Paketformat ist in der Abbildung dargestellt.



Alle Dienstprogramme aus dem Can-Utils-Set selbst können CAN-Pakete analysieren und nur nützliche Informationen liefern, nämlich:

  • ID
  • Datenlänge
  • Daten

Die Daten werden unverschlüsselt übertragen, was das Studium des Protokolls erleichtert. Auf Raspberry Pi habe ich einen kleinen Server geschrieben, der Daten von Candump nach TCP / IP umleitet, um den Datenstrom auf dem Computer zu analysieren und ihn wunderschön anzuzeigen.

Für macOS habe ich eine einfache Anwendung geschrieben, die für jede Geräteadresse eine Zelle zum Tablet hinzufügt. In dieser Zelle kann ich bereits sehen, welche Daten sich ändern.



Ich drücke die elektrische Fenstertaste, habe eine Zelle gefunden, in der sich die Daten ändern, und dann festgestellt, welche Befehle dem Drücken, Drücken, Halten, Halten entsprechen.

Sie können überprüfen, ob der Befehl funktioniert, indem Sie ihn vom Terminal aus senden, z. B. den Befehl zum Anheben des linken Glases:

cansend can0 181#0200

Teams, die Geräte über CAN-Bus in VAG-Fahrzeugen übertragen (Skoda Octavia 2011), empfangen durch Reverse Engineering:

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

Ich war zu faul, um alle anderen Geräte zu studieren, also in dieser Liste nur das, was mich interessierte.

Telefon App Entwicklung


Mit den empfangenen Befehlen habe ich eine Anwendung für das iPhone geschrieben, die Fenster öffnet / schließt und die Zentralverriegelung steuert.

Auf Raspberry Pi habe ich 2 kleine Server gestartet, der erste sendet Daten von Candump an TCP / IP, der zweite empfängt Befehle vom iPhone und sendet sie zum Senden.


Anwendungsquellen für iOS automatisch steuern
//
//  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


Es gibt eine Möglichkeit, Ihre Anwendung nicht für das Telefon zu schreiben, sondern die Vorteile der Smart Homes-Welt zu nutzen. Sie müssen lediglich das Z-Way- Automatisierungssystem mit dem folgenden Befehl auf dem Raspberry Pi installieren :

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

Danach fügen wir unsere CAN-Geräte dem Z-Way-Automatisierungssystem hinzu


und steuern den Fensterheber wie einen normalen Schalter:


Mobile Anwendungen für Z-Way: ZWay Home Control und ZWay Control.

Sprachsteuerung mit Homekit und Siri


In einem meiner Artikel habe ich den Prozess der Installation von Homebridge auf einem Raspberry Pi zur Sprachsteuerung des Z-Way-Hausautomationssystems beschrieben . Nach der Installation von Homebridge können Sie die Sprachsteuerung mit Siri durchführen. Ich bin sicher, dass es für Android viele Anwendungen gibt, mit denen Sprache HTTP-Anforderungen senden kann, um Z-Way zu steuern.

Ich hänge das Video an die Sprachsteuerung des Fensterreglers an.

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


All Articles