Хакаем CAN шину авто. Виртуальная панель приборов / Хабр

Хакаем CAN шину авто. Виртуальная панель приборов / Хабр ОБД2

Разработка панели приборов на основе raspberry pi и 7″ дисплея

В качестве аппаратной части я выбрал Raspberry Pi. Была идея использовать Android планшет, но показалось, что на Raspberry Pi будет проще и быстрее. В итоге докупил официальный 7″ дисплей, и сделал CAN шилд из модуля TJA1050 Niren.

OBD2 штекер использовал от старого ELM327 адаптера.

Используются контакты: CAN_L, CAN_H, 12, GND.

Тесты в машине прошли успешно и теперь нужно было все собрать. Плату дисплея, Raspberry Pi и блок питания разместил на куске черного пластика, очень удачно подобрал пластмассовые втулки, с ними ничего не болтается и надежно закреплено.

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

Напильником довел лист черного пластика до размера крышки бардачка, к нему прикрепил бутерброд и дисплей. Для прототипа сойдет, а 3D модель с крышкой для дисплея и всеми нужными крепежами уже в разработке.

Can сниффер из arduino uno

Чтобы послушать, что отправляет VCDS в CAN шину я собрал сниффер на макетке из Arduino и модуля MCP2515 TJA1050 Niren.

Схема подключения следующая:

Видео работы цифровой панели приборов на базе raspberry pi

ОБНОВЛЕНО 24.06.2021

Диагностика шины can доступным языком – автомастера.нет

Измерение и диагностика шины CAN

  Часто основной причиной неисправности в электронной системе управления транспортным средством – являются механические повреждения шины CAN или выход из строя блоков управления, висящих на шине CAN.

  Ниже в статье приведены способы диагностики шины CAN при различных неисправностях. В качестве примера показана типичная схема CAN шины на тракторе Valtra T ‘ серии.

  Условные обозначения:

  Измерения шины CAN BUS

– Оконечные резисторы 120 Ом (Иногда эти резисторы называют терминаторы) внутри блока управления EC и резистор, расположенный рядом с блоком TC1

– Если на дисплее (на боковой стойке) отображается код неисправности, имеющий отношение к шине CAN, то это означает неисправность проводки шины CAN или блока управления.

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

– Если дисплей мигает или сообщение шины CAN не может быть передано через шину, то для обнаружения места повреждения проводки шины CAN (или неисправного блока управления) можно использовать мультиметр.

Шина CAN не имеет физических повреждений

– Если сопротивление между проводами Hi (Высокое) и Lo (Низкое) шины CAN (в любой точке) примерно равно 60 Ом, то шина CAN не имеет физических повреждений.

– Блоки управления EC и TC1 исправны, так как оконечные резисторы (120 Ом) расположены в блоке EC и рядом с блоком TC1.

– Блок управления TC2 и приборная панель ICL также не повреждены, поскольку шина CAN проходит через эти блоки.

Шина CAN повреждена

– Если сопротивление между проводами Hi и Lo шины CAN (в любой точке) примерно равно 120 Ом, то проводка шины CAN повреждена (один или оба провода).

Шина CAN имеет физические повреждения

Если шина CAN повреждена, следует определить место повреждения.

– Сначала замеряется сопротивление провода CAN-Lo, например, между блоками управления EC и TC2.

-Таким образом, измерения должны быть выполнены между разъемами Lo-Lo или Hi-Hi. Если сопротивление примерно равно 0 Ом, то провод между измеряемыми точками не поврежден.

Код ошибки:  Диагностический разъём на новой мериве - Опель Мерива , форум Meriva - АвтоВызов

– Если сопротивление примерно равно 240 Ом, то между измеряемыми точками шина повреждена. На рисунке показано повреждение провода CAN-Lo между блоком управления TC1 и приборной панелью ICL.

Короткое замыкание в шине CAN

– Если сопротивление между проводами CAN-Hi и CAN-Lo примерно равно 0 Ом, то в шине CAN произошло короткое замыкание.

– Отсоедините один из блоков управления и измерьте сопротивление между контактами разъемов CAN-Hi и CAN-Lo на блоке управления. Если устройство исправно, установите его на место.

– Затем отсоедините следующее устройство, выполните измерения. Действуйте таким образом до тех пор, пока не будет обнаружено неисправное устройство. Блок неисправен, если сопротивление примерно равно 0 Ом.

– Если все блоки проверены, а измерения по-прежнему сигнализируют о коротком замыкании, это означает неисправность проводки шины CAN. Чтобы найти место повреждения проводов, их следует проверить визуально.

Измерение напряжения шины CAN

– Включите питание и измерьте напряжение между проводами CAN-Hi, CAN-Lo и проводом заземления.

– Напряжение должно находиться в диапазоне 2,4 – 2,7 В.

Карта установка автосигнализации на opel corsa 2021

Были установлены автосигнализация Starline E90 с установленным в неё can-модулем и модуль обхода штатного иммобилайзера Starline BP-03.

Снимаем подторпедник. Для этого необходимо снять крышку блока переключателя света и открутить 3 самореза. Один из саморезов находится под декоративной заглушкой.

Саморез
Карта установка автосигнализации на Opel Corsa 2021

Саморез
Карта установка автосигнализации на Opel Corsa 2021

Саморез
Карта установка автосигнализации на Opel Corsa 2021

Общий вид
Карта установка автосигнализации на Opel Corsa 2021

Снимаем облицовку левого порога. Крепление на защелках.

Порог снят
Карта установка автосигнализации на Opel Corsa 2021

Вынимаем все разъемы с лицевой панели блока BCM. Начинать с разъёма 1! Нумерация разъемов условная.

Затем откручиваем 2 болта крепления блока к кронштейну кузова, проталкиваем его в сторону моторного щита, вынимаем 2 разъема с обратной стороны и вынимаем блок.

Блок ВСМ и номера разъемов
Карта установка автосигнализации на Opel Corsa 2021

Верхний болт крепления блока ВСМ
Карта установка автосигнализации на Opel Corsa 2021

Нижний болт крепления блока ВСМ
Карта установка автосигнализации на Opel Corsa 2021

Нумерация разъемов с обратной стороны блока ВСМ
Карта установка автосигнализации на Opel Corsa 2021

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

Концевик капота штатный
Карта установка автосигнализации на Opel Corsa 2021

Подключаем к разъему «6» провода зажигания, аксессуаров, стартера.

Зажигание IGN ( фиолетово-серый)
Карта установка автосигнализации на Opel Corsa 2021

Аксессуары ACC (коричнево-желтый)
Карта установка автосигнализации на Opel Corsa 2021

Стартер ST ( желто-фиолетовый)
Карта установка автосигнализации на Opel Corsa 2021

В разрыв любого из двух проводов рамки штатного иммобилайзера на разъеме «6» подключаем модуль обхода.

Провода рамки штатного иммобилайзера
Карта установка автосигнализации на Opel Corsa 2021

В разъеме «6» подключаем провода управления ЦЗ (альтернативное управление центральным замком) и CAN-шину.

По can видит двери, багажник, зажигание, заведённый двигатель, педаль тормоза. Поворотами управляем по аналогу, комфортом (закрытие стёкол) – по аналогу, центральным замком – с помощью альтернативного метода (проводом на разъёме can автосигнализации, в Starline E90 – это белый/синий провод).

Центральный замок (сине-белый)
Карта установка автосигнализации на Opel Corsa 2021

CAN-шина (зеленый). CAN-H
Карта установка автосигнализации на Opel Corsa 2021

В разъеме «4» подключаем поворотники.

Поворотники (зелено-фиолетовый и сине-белый)
Карта установка автосигнализации на Opel Corsa 2021

Один из дополнительных каналов сигнализации подключаем к проводу комфорта в разъеме «2». Программируем его, как импульс при постановке в охрану. Длительность импульса? Секунд десять будет достаточно.

Комфорт (фиолетово-белый)
Карта установка автосигнализации на Opel Corsa 2021

Подключаем провод концевика капота.

Концевик капота (коричнево-зеленый)
Карта установка автосигнализации на Opel Corsa 2021

Подключаемся к проводу стоп-сигналов.

Тормоз (коричнево-желтый)
Карта установка автосигнализации на Opel Corsa 2021

Дополнительную блокировку можно сделать в левом пороге.

Бензонасос (серый)
Карта установка автосигнализации на Opel Corsa 2021

Проверяем, настраиваем. Собираем салон в обратном порядке (не забывайте, что разъём 1 вставляем последним).


Подслушиваем запросы с помощью диагностической системы vag-com (vcds)

Описание VCDS с официального сайта

Приложение на телефон виртуальная панель приборов

Хакаем CAN шину авто. Виртуальная панель приборов / Хабр
Хакаем CAN шину авто. Виртуальная панель приборов / Хабр
Если есть желание поддержать проект, то вот ссылка на приложение, принимаю любые замечания и предложения!
VAG Virtual Cockpit
Хакаем CAN шину авто. Виртуальная панель приборов / Хабр

Софт панели приборов на python и kivy (ui framework)

Параллельно со сборкой самой панели приборов я вел разработку приложения для отображения информации с датчиков. В самом начале я не планировал какой либо дизайн.

Код ошибки:  Электрическая газонокосилка Makita ELM4121 - цена, отзывы, фото, технические характеристики, инструкция

Хакаем CAN шину авто. Виртуальная панель приборов / Хабр
Первая версия панели приборов

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

Хакаем CAN шину авто. Виртуальная панель приборов / Хабр
Вторая версия панели приборов

Продолжив поиски более современного дизайна я обратил внимание какие цифровые приборки делают автопроизводители и постарался сделать что-то похожее.

Хакаем CAN шину авто. Виртуальная панель приборов / Хабр
Третья версия панели приборов

Ранее, я никогда не разрабатывал графические приложения под Linux поэтому не знал с чего начать. Вариант на вебе простой в разработке, но слишком много лишних компонентов: иксы, браузер, nodejs, хотелось быстрой загрузки. Попробовав Qt PySide2 я понял, что это займет у меня много времени, т.к. мало опыта.

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()

Код ошибки:  Диагностика лада веста через obd2

Алгоритм работы следующий, используется 3 потока:

  1. В главном потоке работаем с графическими элементы (спидометр, тахометр, часы, температуры и др) на экране
  2. Во втором потоке каждые 5 мс делаем опрос следующего датчика
  3. В третьем потоке слушаем CAN шину, получив ответ парсим его и обновляем соответствующий графический элемент

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

Проект цифровой панель приборов открытый. Рад буду предложениям и комментариям!

Оцените статью
OBD
Добавить комментарий

Adblock
detector