Hacking BISA bus otomatis. Dasbor virtual



Pada artikel pertama, "Meretas bus CAN untuk mobil untuk kontrol suara," saya terhubung langsung ke bus CAN CAN di pintu mobil saya dan memeriksa lalu lintas yang lewat, ini memungkinkan saya untuk menentukan perintah untuk mengendalikan jendela, kunci sentral, dll.

Pada artikel ini saya akan memberi tahu Anda cara memasang dasbor virtual atau digital saya sendiri yang unik dan mendapatkan data dari sensor di mobil VAG (Volkswagen, Audi, Seat, Skoda).

Saya membuat sniffer CAN baru dan pelindung CAN untuk Raspberry Pi berdasarkan pada modul Nirp5015 TJA1050 MCP2515, data yang diperoleh bersama mereka saya gunakan dalam pengembangan panel instrumen digital menggunakan layar 7 ”untuk Raspberry Pi. Selain tampilan informasi yang sederhana, panel digital merespons tombol sakelar kolom kemudi dan acara lainnya di dalam mobil.

Sebagai kerangka kerja untuk menggambar instrumen, Kivy untuk Python sangat bagus. Ia bekerja tanpa Xs dan menggunakan GL untuk menampilkan grafik.

  1. BISA sniffer dari Arduino Uno
  2. Mendengarkan permintaan menggunakan Sistem Diagnostik VAG-COM (VCDS)
  3. Pengembangan panel instrumen berdasarkan Raspberry Pi dan layar 7.
  4. Perangkat lunak dasbor dengan Python dan Kivy (kerangka UI)
  5. Video panel instrumen digital berdasarkan Raspberry Pi

Di bawah potongan, implementasi penuh proyek akan menarik!


Pintu pengemudi terbuka

BISA sniffer dari Arduino Uno


Untuk mendengarkan apa yang dikirim VCDS ke bus CAN, saya mengumpulkan sniffer di atas papan tempat memotong roti dari Arduino dan modul MCP2515 TJA1050 Niren.



Diagram koneksi adalah sebagai berikut:


Untuk mendengarkan lalu lintas, saya menggunakan penganalisis CanHackerV2 dan firmware arduino-canhacker untuk Arduino, yang mengimplementasikan API yang kompatibel dengan program ini. Firmware di git https://github.com/autowp/arduino-canhacker .

CanHackerV2 memungkinkan Anda untuk menonton lalu lintas yang lewat, merekam dan memainkan perintah pada interval tertentu, yang sangat membantu dalam analisis data.



Mendengarkan permintaan menggunakan Sistem Diagnostik VAG-COM (VCDS)


Deskripsi VCDS dari situs web resmi ru.ross-tech.com :

Pemindai perangkat keras-perangkat lunak VCDS dirancang untuk mendiagnosis sistem kontrol elektronik yang dipasang pada kendaraan VAG. Akses ke semua sistem: engine, ACP, ABS, kontrol iklim, elektronik tubuh, dll., Membaca dan menghapus kode kesalahan, menampilkan parameter saat ini, aktivasi, pengaturan dasar, adaptasi, pengkodean, dll.



Dengan menghubungkan sniffer ke garis CAN_L dan CAN_H dalam string diagnostik, saya dapat melihat permintaan apa yang VCDS lakukan dan apa jawaban mobil.



Keunikan dari grup VAG otomatis adalah bahwa konektor OBD2 terhubung ke bus CAN melalui gateway dan gateway tidak melewati semua lalu lintas yang berjalan melalui jaringan, mis. dengan menghubungkan ke konektor OBD2 dengan sniffer, Anda tidak akan melihat apa pun. Untuk menerima data dalam konektor OBD2, Anda harus mengirim permintaan khusus ke gateway. Permintaan dan tanggapan ini terlihat saat mendengarkan lalu lintas dari VCDS. Sebagai contoh, ini adalah bagaimana Anda bisa mendapatkan jarak tempuh.


Di VCDS, Anda bisa mendapatkan informasi dari hampir semua sensor di mobil. Pertama-tama, saya tertarik pada informasi yang pada umumnya tidak rapi, ini:

  • suhu minyak
  • pintu mana yang terbuka

Saya juga menerima kecepatan, revolusi, suhu cairan pendingin, jarak tempuh, aliran, ruang tangki, dan permintaan lainnya, saya akan mempostingnya untuk referensi.

// 
714 03 22 22 0D 55 55 55 55
77E 05 62 22 0D 55 65 AA AA -  
77E 05 62 22 0D 00 65 AA AA -  
77E 05 62 22 0D 54 65 AA AA -  
77E 05 62 22 0D 51 65 AA AA -  
77E 05 62 22 0D 50 65 AA AA -    
77E 05 62 22 0D 45 65 AA AA -   
77E 05 62 22 0D 15 65 AA AA -   
77E 05 62 22 0D 44 65 AA AA -     
77E 05 62 22 0D 40 65 AA AA - , ,   
01010101 = 0x55 ( )
0  - 
2  - 
4  -  
6  -  

// 
714 03 22 22 05 55 55 55 55
77E 05 62 22 05 21 AA AA AA - 
77E 05 62 22 05 20 AA AA AA -  

//  
714 03 22 22 0 55 55 55 55
77E 04 62 22 0C 55 AA AA AA - -7.5°
77E 04 62 22 0C 65 AA AA AA - 0.5 101°
77E 04 62 22 0C 66 AA AA AA - 1 = 102°
77E 04 62 22 0C 68 AA AA AA - 2 = 104°

//   
714 03 22 10 14 55 55 55 55
77E 04 62 10 14 84 AA AA AA - 16°

//  
714 03 22 22 94 55 55 55 55
77E 05 62 22 94 00 8E AA AA - 142

//  
714 03 22 22 06 55 55 55 55
77E 04 62 22 06 2C AA AA AA - 44
77E 04 62 22 06 16 AA AA AA - 22

//     
714 03 22 22 96 55 55 55 55
77E 05 62 22 96 01 9A AA AA - 41.0°

//  
714 03 22 F4 05 55 55 55 55
77E 04 62 F4 05 85 AA AA AA - 52.5°
77E 04 62 F4 05 87 AA AA AA - 54°
77E 04 62 F4 05 5 AA AA AA - 100.5°

//  
714 03 22 F4 0C 55 55 55 55
77E 05 62 F4 0C 0B C6 AA AA - 753.5 /; 0BC6 = 3014/4 = 753
77E 05 62 F4 0C 0B DC AA AA - 759 /; 0BDC = 3036
77E 05 62 F4 0C 0B E8 AA AA - 762 /; 0BE8 = 3048
77E 05 62 F4 0C 1C 32 AA AA - 1804.5 /

//  
714 03 22 20 2F 55 55 55 55
77E 04 62 20 2F 36 AA AA AA - -4°
77E 04 62 20 2F 67 AA AA AA - 45°
77E 04 62 20 2F 68 AA AA AA - 46°

//    
746 03 22 26 13 55 55 55 55
7B0 05 62 26 13 00 5B AA AA - 9.1, 91 == 0x5B
7B0 05 62 26 13 00 5C AA AA - 9.2°
7B0 05 62 26 13 00 5D AA AA - 9.3°

// 
714 03 22 22 16 55 55 55 55
77E 05 62 22 16 11 1E AA AA - 17:30

//   
714 03 22 22 1B 55 55 55 55
77E 05 62 22 1B 80 AA AA AA -  
77E 05 62 22 1B 81 AA AA AA -  
77E 05 62 22 1B 84 AA AA AA -  

//   2
714 03 22 22 99 55 55 55 55
77E 03 62 22 99 00 91 AA AA - 14.5/100

//  
714 03 22 22 98 55 55 55 55
77E 05 62 22 98 00 00 AA AA - 0.0/100

// 
714 03 22 22 03 55 55 55 55
77E 05 62 22 03 24 0 AA AA - 94080  0x240 * 10

Raspberry Pi 7″


Raspberry Pi. Android , , Raspberry Pi . 7″ , CAN TJA1050 Niren.



OBD2 ELM327 .



: CAN_L, CAN_H, +12, GND.



. , Raspberry Pi , , .



, . .



, . , 3D .



Python Kivy (UI framework)


. .




. , , - 80-.




- .




, Linux . , : , , nodejs, . Qt PySide2 , , .. . Kivy — Python, .

Kivy , , OpenGL. 10 .

import can
import os
import sys
from threading import Thread
import time

os.environ['KIVY_GL_BACKEND'] = 'gl'
os.environ['KIVY_WINDOW'] = 'egl_rpi'

from kivy.app import App
from kivy.properties import NumericProperty
from kivy.properties import BoundedNumericProperty
from kivy.properties import StringProperty
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.animation import Animation

messageCommands = {
    'GET_DOORS_COMMAND': 0x220D,
    'GET_OIL_TEMPERATURE' : 0x202F,
    'GET_OUTDOOR_TEMPERATURE' : 0x220C,
    'GET_INDOOR_TEMPERATURE' : 0x2613,
    'GET_COOLANT_TEMPERATURE' : 0xF405,
    'GET_SPEED' : 0xF40D,
    'GET_RPM' : 0xF40C,
    'GET_KM_LEFT': 0x2294,
    'GET_FUEL_LEFT': 0x2206,
    'GET_TIME': 0x2216
}

bus = can.interface.Bus(channel='can0', bustype='socketcan')

python
# -*- coding: utf-8 -*-

import can
import os
import sys
from threading import Thread
import time

os.environ['KIVY_GL_BACKEND'] = 'gl'
os.environ['KIVY_WINDOW'] = 'egl_rpi'

from kivy.app import App
from kivy.properties import NumericProperty
from kivy.properties import BoundedNumericProperty
from kivy.properties import StringProperty
from kivy.uix.label import Label
from kivy.uix.image import Image
from kivy.uix.floatlayout import FloatLayout
from kivy.uix.widget import Widget
from kivy.uix.scatter import Scatter
from kivy.animation import Animation

messageCommands = {
    'GET_DOORS_COMMAND': 0x220D,
    'GET_OIL_TEMPERATURE' : 0x202F,
    'GET_OUTDOOR_TEMPERATURE' : 0x220C,
    'GET_INDOOR_TEMPERATURE' : 0x2613,
    'GET_COOLANT_TEMPERATURE' : 0xF405,
    'GET_SPEED' : 0xF40D,
    'GET_RPM' : 0xF40C,
    'GET_KM_LEFT': 0x2294,
    'GET_FUEL_LEFT': 0x2206,
    'GET_TIME': 0x2216
}

bus = can.interface.Bus(channel='can0', bustype='socketcan')

class PropertyState:
    def __init__(self, last, current):
        self.last = last
        self.current = current

    def lastIsNotNow(self):
        return self.last is not self.current

class CanListener(can.Listener):
    def __init__(self, dashboard):
        self.dashboard = dashboard
        self.speedStates = PropertyState(None,None)
        self.rpmStates = PropertyState(None,None)
        self.kmLeftStates = PropertyState(None,None)
        self.coolantTemperatureStates = PropertyState(None,None)
        self.oilTempratureStates = PropertyState(None,None)
        self.timeStates = PropertyState(None,None)
        self.outDoorTemperatureStates = PropertyState(None,None)
        self.doorsStates = PropertyState(None,None)
        self.carMinimized = True

    def on_message_received(self, message):
	 messageCommand = message.data[3] | message.data[2] << 8

        if message.arbitration_id == 0x77E and messageCommand == messageCommands['GET_SPEED']:
            self.speedStates.current = message.data[4]
            if self.speedStates.lastIsNotNow():
                self.dashboard.speedometer.text = str(self.speedStates.current)
                self.speedStates.last = self.speedStates.current

        if message.arbitration_id == 0x77E and messageCommand == messageCommands['GET_RPM']:
            self.rpmStates.current = message.data[5] | message.data[4] << 8
            if self.rpmStates.lastIsNotNow():
                self.dashboard.rpm.value = self.rpmStates.current/4
                self.rpmStates.last = self.rpmStates.current
        if message.arbitration_id == 0x35B:
            self.rpmStates.current = message.data[2] | message.data[1] << 8
            if self.rpmStates.lastIsNotNow():
                self.dashboard.rpm.value = self.rpmStates.current/4
                self.rpmStates.last = self.rpmStates.current

        if message.arbitration_id == 0x77E and messageCommand == messageCommands['GET_KM_LEFT']:
            self.kmLeftStates.current = message.data[5] | message.data[4] << 8
            if self.kmLeftStates.lastIsNotNow():
                self.dashboard.kmLeftLabel.text = str(self.kmLeftStates.current)
                self.kmLeftStates.last = self.kmLeftStates.current

        if message.arbitration_id == 0x77E and messageCommand == messageCommands['GET_COOLANT_TEMPERATURE']:
            self.coolantTemperatureStates.current = message.data[4]
            if self.coolantTemperatureStates.lastIsNotNow():
                self.dashboard.coolantLabel.text = str(self.coolantTemperatureStates.current-81)
                self.coolantTemperatureStates.last = self.coolantTemperatureStates.current

        if message.arbitration_id == 0x77E and messageCommand == messageCommands['GET_OIL_TEMPERATURE']:
            self.oilTempratureStates.current = message.data[4]
            if self.oilTempratureStates.lastIsNotNow():
                self.dashboard.oilLabel.text = str(self.oilTempratureStates.current-58)
                self.oilTempratureStates.last = self.oilTempratureStates.current

        if message.arbitration_id == 0x77E and messageCommand == messageCommands['GET_TIME']:
            self.timeStates.current = message.data[5] | message.data[4] << 8
            if self.timeStates.lastIsNotNow():
                self.dashboard.clock.text = str(message.data[4]) + ":" + str(message.data[5])
                self.timeStates.last = self.timeStates.current

        if message.arbitration_id == 0x77E and messageCommand == messageCommands['GET_OUTDOOR_TEMPERATURE']:
            self.outDoorTemperatureStates.current = float(message.data[4])
            if self.outDoorTemperatureStates.lastIsNotNow():
                self.dashboard.outDoorTemperatureLabel.text = str((self.outDoorTemperatureStates.current - 100)/2)
                self.outDoorTemperatureStates.last = self.outDoorTemperatureStates.current

        if message.arbitration_id == 0x77E and messageCommand == messageCommands['GET_DOORS_COMMAND']:
            self.doorsStates.current = message.data[4]
            if self.doorsStates.lastIsNotNow():
                self.doorsStates.last = self.doorsStates.current
                self.dashboard.car.doorsStates=message.data[4]

                # all doors closed -> minimize car
                if self.doorsStates.current == 0x55:
                    self.dashboard.minimizeCar()
                    self.carMinimized = True
                else:
                    if self.carMinimized:
                        self.dashboard.maximizeCar()
                        self.carMinimized = False
          
class Dashboard(FloatLayout):
    def __init__(self,**kwargs):
        super(Dashboard,self).__init__(**kwargs)

        # Background
        self.backgroundImage = Image(source='bg.png')
        self.add_widget(self.backgroundImage)

        # RPM
        self.rpm = Gauge(file_gauge = "gauge512.png", unit = 0.023, value=0, size_gauge=512, pos=(0,0))
        self.add_widget(self.rpm)
        self.rpm.value = -200

        # Speedometer
        self.speedometer = Label(text='0', font_size=80, font_name='hemi_head_bd_it.ttf', pos=(0,-15))
        self.add_widget(self.speedometer)

        # KM LEFT
        self.kmLeftLabel = Label(text='000', font_name='Avenir.ttc', halign="right", text_size=self.size, font_size=25, pos=(278,233))
        self.add_widget(self.kmLeftLabel)

        # COOLANT TEMPEARATURE
        self.coolantLabel = Label(text='00', font_name='hemi_head_bd_it.ttf', halign="right", text_size=self.size, font_size=27, pos=(295,-168))
        self.add_widget(self.coolantLabel)

        # OIL TEMPERATURE
        self.oilLabel = Label(text='00', font_name='hemi_head_bd_it.ttf', halign="right", text_size=self.size, font_size=27, pos=(-385,-168))
        self.add_widget(self.oilLabel)

        # CLOCK
        self.clock = Label(text='00:00', font_name='Avenir.ttc', font_size=27, pos=(-116,-202))
        self.add_widget(self.clock)

        # OUTDOOR TEMPERATURE
        self.outDoorTemperatureLabel = Label(text='00.0', font_name='Avenir.ttc', halign="right", text_size=self.size, font_size=27, pos=(76,-169))
        self.add_widget(self.outDoorTemperatureLabel)

        # CAR DOORS
        self.car = Car(pos=(257,84))
        self.add_widget(self.car)

    def minimizeCar(self, *args):
        print("min")
        anim = Animation(scale=0.5, opacity = 0, x = 400, y = 240, t='linear', duration=0.5)
        anim.start(self.car)

        animRpm = Animation(scale=1, opacity = 1, x = 80, y = -5, t='linear', duration=0.5)
        animRpm.start(self.rpm)

    def maximizeCar(self, *args):
        print("max")
        anim = Animation(scale=1, opacity = 1, x=257, y=84, t='linear', duration=0.5)
        anim.start(self.car)

        animRpm = Animation(scale=0.5, opacity = 0, x = 80, y = -5, t='linear', duration=0.5)
        animRpm.start(self.rpm)


class Car(Scatter):
    carImage = StringProperty("car362/car.png")

    driverDoorClosedImage = StringProperty("car362/driverClosedDoor.png")
    driverDoorOpenedImage = StringProperty("car362/driverOpenedDoor.png")

    passangerDoorClosedImage = StringProperty("car362/passangerClosedDoor.png")
    passangerDoorOpenedImage = StringProperty("car362/passangerOpenedDoor.png")

    leftDoorClosedImage = StringProperty("car362/leftClosedDoor.png")
    leftDoorOpenedImage = StringProperty("car362/leftOpenedDoor.png")

    rightDoorClosedImage = StringProperty("car362/rightClosedDoor.png")
    rightDoorOpenedImage = StringProperty("car362/rightOpenedDoor.png")

    doorsStates = NumericProperty(0)

    size = (286, 362)

    def __init__(self, **kwargs):
        super(Car, self).__init__(**kwargs)

        _car = Image(source=self.carImage, size=self.size)

        self.driverDoorOpened = Image(source=self.driverDoorOpenedImage, size=self.size)
        self.passangerDoorOpened = Image(source=self.passangerDoorOpenedImage, size=self.size)
        self.leftDoorOpened = Image(source=self.leftDoorOpenedImage, size=self.size)
        self.rightDoorOpened = Image(source=self.rightDoorOpenedImage, size=self.size)

        self.driverDoorClosed = Image(source=self.driverDoorClosedImage, size=self.size)
        self.passangerDoorClosed = Image(source=self.passangerDoorClosedImage, size=self.size)
        self.leftDoorClosed = Image(source=self.leftDoorClosedImage, size=self.size)
        self.rightDoorClosed = Image(source=self.rightDoorClosedImage, size=self.size)

        self.add_widget(_car)
        self.add_widget(self.driverDoorOpened)
        self.add_widget(self.passangerDoorOpened)
        self.add_widget(self.leftDoorOpened)
        self.add_widget(self.rightDoorOpened)

        self.bind(doorsStates=self._update)

    def _update(self, *args):
        driverDoorStates = self.doorsStates&1
        passangerDoorStates = self.doorsStates&4
        leftDoorStates = self.doorsStates&16
        rightDoorStates = self.doorsStates&64
        if driverDoorStates != 0:
            try:
                self.remove_widget(self.driverDoorOpened)
                self.add_widget(self.driverDoorClosed)
            except:
                pass
        else:
            try:
                self.remove_widget(self.driverDoorClosed)
                self.add_widget(self.driverDoorOpened)
            except:
                pass
        if passangerDoorStates != 0:
            try:
                self.remove_widget(self.passangerDoorOpened)
                self.add_widget(self.passangerDoorClosed)
            except:
                pass
        else:
            try:
                self.remove_widget(self.passangerDoorClosed)
                self.add_widget(self.passangerDoorOpened)
            except:
                pass
        if leftDoorStates != 0:
            try:
                self.remove_widget(self.leftDoorOpened)
                self.add_widget(self.leftDoorClosed)
            except:
                pass
        else:
            try:
                self.remove_widget(self.leftDoorClosed)
                self.add_widget(self.leftDoorOpened)
            except:
                pass
        if rightDoorStates != 0:
            try:
                self.remove_widget(self.rightDoorOpened)
                self.add_widget(self.rightDoorClosed)
            except:
                pass
        else:
            try:
                self.remove_widget(self.rightDoorClosed)
                self.add_widget(self.rightDoorOpened)
            except:
                pass

class Gauge(Scatter):
    unit = NumericProperty(1.125)
    zero = NumericProperty(116)
    value = NumericProperty(10) #BoundedNumericProperty(0, min=0, max=360, errorvalue=0)
    size_gauge = BoundedNumericProperty(512, min=128, max=512, errorvalue=128)
    size_text = NumericProperty(10)
    file_gauge = StringProperty("")

    def __init__(self, **kwargs):
        super(Gauge, self).__init__(**kwargs)

        self._gauge = Scatter(
            size=(self.size_gauge, self.size_gauge),
            do_rotation=False, 
            do_scale=False,
            do_translation=False
            )

        _img_gauge = Image(source=self.file_gauge, size=(self.size_gauge, self.size_gauge))

        self._needle = Scatter(
            size=(self.size_gauge, self.size_gauge),
            do_rotation=False,
            do_scale=False,
            do_translation=False
            )

        _img_needle = Image(source="arrow512.png", size=(self.size_gauge, self.size_gauge))


        self._gauge.add_widget(_img_gauge)
        self._needle.add_widget(_img_needle)

        self.add_widget(self._gauge)
        self.add_widget(self._needle)

        self.bind(pos=self._update)
        self.bind(size=self._update)
        self.bind(value=self._turn)

    def _update(self, *args):
        self._gauge.pos = self.pos
        self._needle.pos = (self.x, self.y)
        self._needle.center = self._gauge.center

    def _turn(self, *args):
        self._needle.center_x = self._gauge.center_x
        self._needle.center_y = self._gauge.center_y
        a = Animation(rotation=-self.value*self.unit + self.zero, t='in_out_quad',duration=0.05)
        a.start(self._needle)

class requestsLoop(Thread):
    def __init__(self):
        Thread.__init__(self)
        self.daemon = True
        self.start()

    canCommands = [
        can.Message(arbitration_id=0x714, data=[0x03, 0x22, messageCommands['GET_DOORS_COMMAND'] >> 8, messageCommands['GET_DOORS_COMMAND'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False),
        can.Message(arbitration_id=0x714, data=[0x03, 0x22, messageCommands['GET_SPEED'] >> 8, messageCommands['GET_SPEED'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False),
        can.Message(arbitration_id=0x714, data=[0x03, 0x22, messageCommands['GET_KM_LEFT'] >> 8, messageCommands['GET_KM_LEFT'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False),
        can.Message(arbitration_id=0x714, data=[0x03, 0x22, messageCommands['GET_RPM'] >> 8, messageCommands['GET_RPM'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False),
        can.Message(arbitration_id=0x714, data=[0x03, 0x22, messageCommands['GET_OIL_TEMPERATURE'] >> 8, messageCommands['GET_OIL_TEMPERATURE'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False),
        can.Message(arbitration_id=0x714, data=[0x03, 0x22, messageCommands['GET_FUEL_LEFT'] >> 8, messageCommands['GET_FUEL_LEFT'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False),
        can.Message(arbitration_id=0x714, data=[0x03, 0x22, messageCommands['GET_OUTDOOR_TEMPERATURE'] >> 8, messageCommands['GET_OUTDOOR_TEMPERATURE'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False),
        can.Message(arbitration_id=0x746, data=[0x03, 0x22, messageCommands['GET_INDOOR_TEMPERATURE'] >> 8, messageCommands['GET_INDOOR_TEMPERATURE'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False),
        can.Message(arbitration_id=0x714, data=[0x03, 0x22, messageCommands['GET_COOLANT_TEMPERATURE'] >> 8, messageCommands['GET_COOLANT_TEMPERATURE'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False),
        can.Message(arbitration_id=0x714, data=[0x03, 0x22, messageCommands['GET_TIME'] >> 8, messageCommands['GET_TIME'] & 0xff, 0x55, 0x55, 0x55, 0x55], extended_id=False)
    ]

    def run(self):
        while True:
            for command in self.canCommands:
                bus.send(command)
                time.sleep(0.005)

class BoxApp(App):
    def build(self):
        dashboard = Dashboard();
        listener = CanListener(dashboard)
        can.Notifier(bus, [listener])

        return dashboard
        
if __name__ == "__main__":
    # Send requests
    requestsLoop()

    _old_excepthook = sys.excepthook
    def myexcepthook(exctype, value, traceback):
        if exctype == KeyboardInterrupt:
            print "Handler code goes here"
        else:
            _old_excepthook(exctype, value, traceback)
    sys.excepthook = myexcepthook

    # Show dashboard
    BoxApp().run()


, 3 :

  1. (, , , )
  2. 5
  3. CAN ,

, . iOS, .

. !

https://github.com/aivs/blackCockpit

Raspberry Pi




24.06.2019




— , ELM327 Wi-Fi . OBD2 , CAN Wi-Fi.


VAG Virtual Cockpit AppStore. , iPhone/iPad, Android . .
, , !
VAG Virtual Cockpit


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


All Articles