I am working on a program that takes in high-speed CanBus Data and displays it to a speedometer-style display using PYQT (A digital Dash Board Display). I have run into the common problem of the speedometer display having some latency and I have not figured out a way to solve it.
The code below is split into two parts, the init is just setting up the display with the AnalogGaugeWidget being the Speedometer widget. The sorting function is a while loop that sends out a request for the CanBus data and decodes it based on its ID. I am only trying to get the RPM code to work for now, and then work my way to the others.
The code works fast whenever the "self.widget.updateVlue(self.RPM)" code is taken out, but when it is put in, there is roughtly a 1-second latency in the while loop.
If anyone knows how I can help reduce this latency either through better threading or a different code structure I'm all ears. Also if you need the "Speedometer" code let me know and I would be more than happy to provide it.
Thanks.
The important part of the code:
from PyQt5 import QtCore, QtGui, QtWidgets
from threading import Thread
from Speedometer import *
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.resize(1000, 600)
self.widget = AnalogGaugeWidget(self)
self.setCentralWidget(self.widget)
t1 = Thread(target=self.sorting)
t1.start()
def sorting(self):
while 1:
for i in range(1, 1000):
# Example of the data received for RPM
data = "Timestamp: 1647910233.332248 ID: 0140 S Rx DL: 8 f9 93 02 00 00 ff 02 02 Channel: can0"
Timestamp = float(data.split(" ")[1])
ID = data.split(" ")[10]
B0 = data.split(" ")[36]
B1 = data.split(" ")[37]
B2 = data.split(" ")[38]
B3 = data.split(" ")[39]
B4 = data.split(" ")[40]
B5 = data.split(" ")[41]
B6 = data.split(" ")[42]
B7 = data.split(" ")[43]
if ID == '0140':
rpm2 = int((bin(int(B3, base=16))[2:][-5:]), 2)
rpm1 = (int(B2, 16))
self.RPM = (rpm2 * 256 + rpm1)
self.widget.updateValue(self.RPM)
This is the full code that only works when plugged into a CanBus module (To give an idea of the size and complexity of the while loop).
global bitrate
bitrate = 500000
global can_interface
can_interface = "can0"
class MainWindow(QtWidgets.QMainWindow):
def __init__(self, *args, **kwargs):
super(MainWindow, self).__init__(*args, **kwargs)
self.resize(1000, 600)
self.widget = AnalogGaugeWidget(self)
self.setCentralWidget(self.widget)
t1 = Thread(target=self.sorting)
t1.start()
def sorting(self):
SaveData = 0
bus = can.interface.Bus(bustype='socketcan', channel=can_interface, bitrate=bitrate)
while 1:
for i in range(1, 1000):
msg = can.Message(arbitration_id=0x7DF, data=[2, 1, 0, 0, 0, 0, 0, 0], is_extended_id=False)
bus.send(msg)
data = str(bus.recv(timeout=2))
# Example of the data received for RPM
#data = "Timestamp: 1647910233.332248 ID: 0140 S Rx DL: 8 f9 93 02 00 00 ff 02 02 Channel: can0"
Timestamp = float(data.split(" ")[1])
ID = data.split(" ")[10]
B0 = data.split(" ")[36]
B1 = data.split(" ")[37]
B2 = data.split(" ")[38]
B3 = data.split(" ")[39]
B4 = data.split(" ")[40]
B5 = data.split(" ")[41]
B6 = data.split(" ")[42]
B7 = data.split(" ")[43]
# if SaveData == 1:
# with open('Can-Bus Data', 'a') as f:
# f.write('{} {} {} {} {} {} {} {} {} {}\n'.format(Timestamp, ID, B0, B1, B2, B3, B4, B5, B6, B7))
if ID == '0140':
rpm2 = int((bin(int(B3, base=16))[2:][-5:]), 2)
rpm1 = (int(B2, 16))
self.RPM = (rpm2 * 256 + rpm1)
self.widget.updateValue(self.RPM)
CP = int(B1, 16)
if CP >= 128 and CP <= 143:
CP = 1
# ("Clutch is engaged")
else:
CP = 0
# ("Clutch is disengaged")
# TP = round(((int(B0, 16) / 256) * 100), 1)
# TPD = round(((int(B4, 16) / 256) * 100), 1)
if SaveData == 1:
with open('ID 0140', 'a') as f:
f.write(
'{} {} {} {} {} {} {} {} {} {}\n'.format(Timestamp, ID, B0, B1, B2, B3, B4, B5, B6,
B7))
if ID == '0141':
EL = int(B3 + B2, 16)
if SaveData == 1:
with open('ID 0141', 'a') as f:
f.write(
'{} {} {} {} {} {} {} {} {} {}\n'.format(Timestamp, ID, B0, B1, B2, B3, B4, B5, B6,
B7))
if ID == '0360':
TO = int(B2, 16) - 40
TC = int(B3, 16) - 40
CC = int(B5, 16)
if CC == 16:
CC = 1
# ("Cruise Control Engaged")
else:
CC = 0
# ("CC off")
if SaveData == 1:
with open('ID 0360', 'a') as f:
f.write(
'{} {} {} {} {} {} {} {} {} {}\n'.format(Timestamp, ID, B0, B1, B2, B3, B4, B5, B6,
B7))
if ID == '0361':
GI = int(B0, 16)
if SaveData == 1:
with open('ID 0361', 'a') as f:
f.write(
'{} {} {} {} {} {} {} {} {} {}\n'.format(Timestamp, ID, B0, B1, B2, B3, B4, B5, B6,
B7))
if ID == '00d4':
FL = int(B1 + B0, 16) * 0.05747
FR = int(B3 + B2, 16) * 0.05747
BL = int(B5 + B4, 16) * 0.05747
BR = int(B7 + B6, 16) * 0.05747
Speed = round(((FL + FR + BL + BR) / 4), 1)
if SaveData == 1:
with open('ID 00d4', 'a') as f:
f.write(
'{} {} {} {} {} {} {} {} {} {}\n'.format(Timestamp, ID, B0, B1, B2, B3, B4, B5, B6,
B7))
if ID == '00d0':
SA = int(B1 + B0, 16)
if SA > 60000:
SA = SA - 65536
SA = round(SA * 0.1)
LatAcc = int(B6, 16) * 0.2
LonAcc = int(B7, 16) * 0.1
ComAcc = math.sqrt((LatAcc ** 2) + (LonAcc ** 2))
# Idle seems to give a value of 50 for lat and 25 for lon then it changes
if SaveData == 1:
with open('ID 00d0', 'a') as f:
f.write(
'{} {} {} {} {} {} {} {} {} {}\n'.format(Timestamp, ID, B0, B1, B2, B3, B4, B5, B6,
B7))
if ID == '00d1':
Speed1 = round((int(B1 + B0, 16) * 0.015694 * 4), 1)
BP = round(((int(B2, 16) / 77) * 100), 1)
if SaveData == 1:
with open('ID 00d1', 'a') as f:
f.write(
'{} {} {} {} {} {} {} {} {} {}\n'.format(Timestamp, ID, B0, B1, B2, B3, B4, B5, B6,
B7))
if ID == '00d3':
TRC = int(B0 + B1, 16)
if TRC == 2062:
TRC = ("Sport Mode Engaged")
# IDK maybe change the display
elif TRC == 10254:
TRC = ("Sport Mode Engaged and Traction Control Off")
elif TRC == 2006:
TRC = ("Drag Mode Engaged")
elif TRC == 8206:
TRC = ("Drift Mode Engaged")
elif TRC == 6:
TRC = ("Fully engaged")
if SaveData == 1:
with open('ID 00d3', 'a') as f:
f.write(
'{} {} {} {} {} {} {} {} {} {}\n'.format(Timestamp, ID, B0, B1, B2, B3, B4, B5, B6,
B7))
if __name__ == '__main__':
app = QtWidgets.QApplication(sys.argv)
main = MainWindow()
main.show()
sys.exit(app.exec_())
data.split()and int conversions (just compare the hex string).splitText = data.split()then directly access that:Timestamp = float(splitText[1])B0, B1, B2, B3, B4, B5, B6, B7 = splitText[36:44]; for the comparison, assuming values are always lowercase, use the string hex: for instance, instead of convertingB5and check if it's equal to 16, just do `if B5 == '10:'. I also strongly suggest you to use more verbose names for variables than TRC, SA, TC, etc (unless you are extremely familiar with those abbreviations): they don't provide any benefit, they just make code less readable.lower()(orupper()): suppose you have to check if a value is equal to 15:if value.lower() == '0f':.