- Разработка панели приборов на основе raspberry pi и 7″ дисплея
- Can сниффер из arduino uno
- Видео работы цифровой панели приборов на базе raspberry pi
- Диагностика шины can доступным языком – автомастера.нет
- Карта установка автосигнализации на opel corsa 2021
- Подслушиваем запросы с помощью диагностической системы vag-com (vcds)
- Приложение на телефон виртуальная панель приборов
- Софт панели приборов на python и kivy (ui framework)
Разработка панели приборов на основе 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 Ом, то провод между измеряемыми точками не поврежден.
– Если сопротивление примерно равно 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 самореза. Один из саморезов находится под декоративной заглушкой.
Саморез
Саморез
Саморез
Общий вид
Снимаем облицовку левого порога. Крепление на защелках.
Порог снят
Вынимаем все разъемы с лицевой панели блока BCM. Начинать с разъёма 1! Нумерация разъемов условная.
Затем откручиваем 2 болта крепления блока к кронштейну кузова, проталкиваем его в сторону моторного щита, вынимаем 2 разъема с обратной стороны и вынимаем блок.
Блок ВСМ и номера разъемов
Верхний болт крепления блока ВСМ
Нижний болт крепления блока ВСМ
Нумерация разъемов с обратной стороны блока ВСМ
Устанавливаем под капотом сирену и датчик температуры. Концевик капота используем штатный.
Концевик капота штатный
Подключаем к разъему «6» провода зажигания, аксессуаров, стартера.
Зажигание IGN ( фиолетово-серый)
Аксессуары ACC (коричнево-желтый)
Стартер ST ( желто-фиолетовый)
В разрыв любого из двух проводов рамки штатного иммобилайзера на разъеме «6» подключаем модуль обхода.
Провода рамки штатного иммобилайзера
В разъеме «6» подключаем провода управления ЦЗ (альтернативное управление центральным замком) и CAN-шину.
По can видит двери, багажник, зажигание, заведённый двигатель, педаль тормоза. Поворотами управляем по аналогу, комфортом (закрытие стёкол) – по аналогу, центральным замком – с помощью альтернативного метода (проводом на разъёме can автосигнализации, в Starline E90 – это белый/синий провод).
Центральный замок (сине-белый)
CAN-шина (зеленый). CAN-H
В разъеме «4» подключаем поворотники.
Поворотники (зелено-фиолетовый и сине-белый)
Один из дополнительных каналов сигнализации подключаем к проводу комфорта в разъеме «2». Программируем его, как импульс при постановке в охрану. Длительность импульса? Секунд десять будет достаточно.
Комфорт (фиолетово-белый)
Подключаем провод концевика капота.
Концевик капота (коричнево-зеленый)
Подключаемся к проводу стоп-сигналов.
Тормоз (коричнево-желтый)
Дополнительную блокировку можно сделать в левом пороге.
Бензонасос (серый)
Проверяем, настраиваем. Собираем салон в обратном порядке (не забывайте, что разъём 1 вставляем последним).
Подслушиваем запросы с помощью диагностической системы vag-com (vcds)
Описание VCDS с официального сайта
Приложение на телефон виртуальная панель приборов
Если есть желание поддержать проект, то вот ссылка на приложение, принимаю любые замечания и предложения!
VAG Virtual Cockpit
Софт панели приборов на python и kivy (ui framework)
Параллельно со сборкой самой панели приборов я вел разработку приложения для отображения информации с датчиков. В самом начале я не планировал какой либо дизайн.
Первая версия панели приборов
По мере разработки решил визуализировать данные более наглядно. Хотел гоночный дизайн, а получилось, что-то в стиле 80-х.
Вторая версия панели приборов
Продолжив поиски более современного дизайна я обратил внимание какие цифровые приборки делают автопроизводители и постарался сделать что-то похожее.
Третья версия панели приборов
Ранее, я никогда не разрабатывал графические приложения под 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')
# -*- 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 потока:
- В главном потоке работаем с графическими элементы (спидометр, тахометр, часы, температуры и др) на экране
- Во втором потоке каждые 5 мс делаем опрос следующего датчика
- В третьем потоке слушаем CAN шину, получив ответ парсим его и обновляем соответствующий графический элемент
Работает стабильно, самый долгий процесс в разработке был связан с рисованием дизайна. На данный момент обкатываю решение и потихоньку пишу мобильное приложение для iOS, чтобы любой мог попробовать цифровую панель приборов.
Проект цифровой панель приборов открытый. Рад буду предложениям и комментариям!