Compare commits

...

36 Commits

Author SHA1 Message Date
c33804c747 added command to build with no console on windows and optimise on build 2024-12-04 22:46:59 +13:00
bc9381627c fixed bug where the correct port wasnt being set per game type 2024-12-03 22:38:03 +13:00
1bf8b589a3 removed unnessessary code 2024-12-02 13:22:13 +13:00
9f0d7120ee updated command to build on linux 2024-12-02 13:20:25 +13:00
b60b053aed fixed logging filename 2024-12-02 13:17:02 +13:00
231862f06b connectd logging 2024-12-02 12:43:27 +13:00
ba50a8ce69 fixed start stop text button 2024-12-02 12:39:12 +13:00
8cc959504f fixed running and waiting text 2024-12-02 12:36:36 +13:00
e1474b162c connected main program 2024-12-02 12:35:16 +13:00
f54e0d2fdb completed GUI layout 2024-12-02 11:51:58 +13:00
3215bb2733 added init lable to show program state 2024-11-28 21:24:06 +13:00
2dd1612ec8 removed the fixed starting size of the window 2024-11-25 20:46:52 +13:00
44ef5ce6cb added start botton to the bottom left corner 2024-11-25 14:50:31 +13:00
eb116803d0 Merge branch 'main' into GUI 2024-11-25 13:43:25 +13:00
79986fc85f changed ip so it binds to all network interfaces 2024-11-25 13:32:14 +13:00
8f0661a73d wip 2024-11-17 21:26:32 +13:00
df6e27783e have two panes for ui 2024-11-15 17:37:03 +13:00
ac60af82a0 attempting to layer frames 2024-11-15 17:31:58 +13:00
f085cd8e8b made game select buttons look pretty 2024-11-15 15:17:55 +13:00
ceb2f538b8 radio buttons "work" 2024-11-15 15:06:31 +13:00
aa96a3b3c2 working on radio buttons 2024-11-14 23:07:13 +13:00
3f6aedffbc added serial selection screen 2024-11-14 22:34:55 +13:00
c6cd1c0b99 started branch that has a GUI interface vs text console has base window 2024-11-14 21:53:21 +13:00
2d42f090dd added check to see if code is running on python 3.11 2024-11-14 21:09:56 +13:00
c42864e46d program will let you know if arduino disconnected 2024-11-12 01:40:58 +13:00
d0196b4a31 added option to write csv for both forza and beamng 2024-11-12 00:44:59 +13:00
eb76c18efc added option toggle to write data to csv file 2024-11-12 00:35:02 +13:00
ec71dd1094 removed Dataout file 2024-11-12 00:02:58 +13:00
61160fcd70 added auto detection of system to set serial port and added ability to change serial port 2024-11-11 23:55:45 +13:00
cb8c861407 added command to create exe 2024-11-11 23:07:11 +13:00
81e704cf7a fixed incorrect numbering of Forza unpacked data 2024-11-11 00:16:08 +13:00
84da201ed8 added ability to output csv file in forza 2024-11-10 23:55:27 +13:00
237708e155 added ability to read speed from forza 2024-11-10 22:28:14 +13:00
9ec76323d7 made UI a little more clear 2024-11-10 16:36:38 +13:00
5d676828d4 added prep to read forza data 2024-11-10 16:30:00 +13:00
e55927dbe2 added safety checking so the program doesnt crash when no arduino attached 2024-11-10 16:09:51 +13:00
11 changed files with 323 additions and 15 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
/build /build
/dist /dist
OutGaugeInterpreter.spec OutGaugeInterpreter.spec
*.csv

View File

@@ -0,0 +1,117 @@
import struct
FORZA_DATA_FORMAT = '<iIfffffffffffffffffffffffffffiiiiffffffffffffffffffffiiiiifffffffffffffffffHBBBBBBbbbffffi'
def unpackData(unpackedData):
carData = {
"IsRaceOn":unpackedData[0], #// = 1 when race is on. = 0 when in menus/race stopped …
#// Can overflow to 0 eventually
"TimestampMS": unpackedData[1],
"EngineMaxRpm": unpackedData[2],
"EngineIdleRpm": unpackedData[3],
"CurrentEngineRpm": unpackedData[4],
#// In the car's local space; X = right, Y = up, Z = forward
"AccelerationX": unpackedData[5],
"AccelerationY": unpackedData[6],
"AccelerationZ": unpackedData[7],
#// In the car's local space; X = right, Y = up, Z = forward
"VelocityX": unpackedData[8],
"VelocityY": unpackedData[9],
"VelocityZ": unpackedData[10],
#// In the car's local space; X = pitch, Y = yaw, Z = roll
"AngularVelocityX": unpackedData[11],
"AngularVelocityY": unpackedData[12],
"AngularVelocityZ": unpackedData[13],
"Yaw": unpackedData[14],
"Pitch": unpackedData[15],
"Roll": unpackedData[16],
#// Suspension travel normalized: 0.0f = max stretch; 1.0 = max compression
"NormalizedSuspensionTravelFrontLeft": unpackedData[17],
"NormalizedSuspensionTravelFrontRight": unpackedData[18],
"NormalizedSuspensionTravelRearLeft": unpackedData[19],
"NormalizedSuspensionTravelRearRight": unpackedData[20],
#// Tire normalized slip ratio, = 0 means 100% grip and |ratio| > 1.0 means loss of grip.
"TireSlipRatioFrontLeft": unpackedData[21],
"TireSlipRatioFrontRight": unpackedData[22],
"TireSlipRatioRearLeft": unpackedData[23],
"TireSlipRatioRearRight": unpackedData[24],
#// Wheels rotation speed radians/sec.
"WheelRotationSpeedFrontLeft": unpackedData[25],
"WheelRotationSpeedFrontRight": unpackedData[26],
"WheelRotationSpeedRearLeft": unpackedData[27],
"WheelRotationSpeedRearRight": unpackedData[28],
#// = 1 when wheel is on rumble strip, = 0 when off.
"WheelOnRumbleStripFrontLeft": unpackedData[29],
"WheelOnRumbleStripFrontRight": unpackedData[30],
"WheelOnRumbleStripRearLeft": unpackedData[31],
"WheelOnRumbleStripRearRight": unpackedData[32],
#// = from 0 to 1, where 1 is the deepest puddle
"WheelInPuddleDepthFrontLeft": unpackedData[33],
"WheelInPuddleDepthFrontRight": unpackedData[34],
"WheelInPuddleDepthRearLeft": unpackedData[35],
"WheelInPuddleDepthRearRight": unpackedData[36],
#// Non-dimensional surface rumble values passed to controller force feedback
"SurfaceRumbleFrontLeft": unpackedData[37],
"SurfaceRumbleFrontRight": unpackedData[38],
"SurfaceRumbleRearLeft": unpackedData[39],
"SurfaceRumbleRearRight": unpackedData[40],
#// Tire normalized slip angle, = 0 means 100% grip and |angle| > 1.0 means loss of grip.
"TireSlipAngleFrontLeft": unpackedData[41],
"TireSlipAngleFrontRight": unpackedData[42],
"TireSlipAngleRearLeft": unpackedData[43],
"TireSlipAngleRearRight": unpackedData[44],
#// Tire normalized combined slip, = 0 means 100% grip and |slip| > 1.0 means loss of grip.
"TireCombinedSlipFrontLeft": unpackedData[45],
"TireCombinedSlipFrontRight": unpackedData[46],
"TireCombinedSlipRearLeft": unpackedData[47],
"TireCombinedSlipRearRight": unpackedData[48],
#// Actual suspension travel in meters
"SuspensionTravelMetersFrontLeft": unpackedData[49],
"SuspensionTravelMetersFrontRight": unpackedData[50],
"SuspensionTravelMetersRearLeft": unpackedData[51],
"SuspensionTravelMetersRearRight": unpackedData[52],
#// Unique ID of the car make/model
"CarOrdinal": unpackedData[53],
#// Between 0 (D -- worst cars) and 7 (X class -- best cars) inclusive
"CarClass": unpackedData[54],
#// Between 100 (worst car) and 999 (best car) inclusive
"CarPerformanceIndex": unpackedData[55],
#// 0 = FWD, 1 = RWD, 2 = AWD
"DrivetrainType": unpackedData[56],
#// Number of cylinders in the engine
"NumCylinders": unpackedData[57],
"PositionX": unpackedData[58],
"PositionY": unpackedData[59],
"PositionZ": unpackedData[60],
"Speed": unpackedData[61],
"Power": unpackedData[62],
"Torque": unpackedData[63],
"TireTempFrontLeft": unpackedData[64],
"TireTempFrontRight": unpackedData[65],
"TireTempRearLeft": unpackedData[66],
"TireTempRearRight": unpackedData[67],
"Boost": unpackedData[68],
"Fuel": unpackedData[69],
"DistanceTraveled": unpackedData[70],
"BestLap": unpackedData[71],
"LastLap": unpackedData[72],
"CurrentLap": unpackedData[73],
"CurrentRaceTime": unpackedData[74],
"LapNumber": unpackedData[75],
"RacePosition": unpackedData[76],
"Accel": unpackedData[77],
"Brake": unpackedData[78],
"Clutch": unpackedData[79],
"HandBrake": unpackedData[80],
"Gear": unpackedData[81],
"Steer": unpackedData[82],
"NormalizedDrivingLine": unpackedData[83],
"NormalizedAIBrakeDifference": unpackedData[84],
"TireWearFrontLeft": unpackedData[85],
"TireWearFrontRight": unpackedData[86],
"TireWearRearLeft": unpackedData[87],
"TireWearRearRight": unpackedData[88],
#// ID for track
"TrackOrdinal": unpackedData[89]
}
return carData

View File

@@ -1 +1 @@
__all__= ["BEAMNG_METHODS"] __all__= ["BEAMNG_METHODS","FORZA_METHODS"]

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,23 +1,210 @@
#please use python 3.11
import socket import socket
import struct import struct
import tkinter.ttk
import serial import serial
import time
import platform
import sys
import threading
from enum import Enum
import tkinter
from GAME_METHODS import * from GAME_METHODS import *
##import BEAMNG_FILES as BEAM import functools #used for passing variables into callbacks
##check if python 3.11 is running code and exit if not
if not(sys.version_info[0] == 3 and sys.version_info[1] == 11):
raise Exception("code must be run in python verion 3.11.\ninput struct for network needs to be changed per python version.")
toPi=serial.Serial('COM5',115200) #connect to arduino
UDP_IP = "127.0.0.1"
UDP_PORT = 4444
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) ##system init
sock.bind((UDP_IP,UDP_PORT)) runningOs = platform.system()
print("waiting for data:\n")
UDP_IP = "0.0.0.0"
BEAMNG_UDP_PORT = 4444
FORZA_UDP_PORT= 4843
SERIAL_PORT = ""
firstRun= True #used to do the headers for csv files
pushingToArduino = False
stopThread = False
class GameType(Enum):
NONE=0
BEAMNG = "BEAMNG"
FORZA = "FORZA"
class ProgramState(Enum):
PUSHING_DATA = 1
WAITNG = 2
ERROR = 3
appState =ProgramState.WAITNG
connectedArduino = False
connectedWebSocket = False
gameSelected = False
portToConnect= 0
carData = 0
csvOut = False
#Functions <-------------------------------------------------------------------------------------------
def checkBoxChange():
if tkLoggingEnabled.get()==True:
loggingLocationEntry.config(state='normal')
loggingLocationText.set(value= "./"+ tkGametype.get()+"_"+str(int(time.time()))+".csv")
loggingLocationEntry.insert(0,loggingLocationText.get())
else:
loggingLocationText.set(value="")
loggingLocationEntry.delete(0,tkinter.END)
loggingLocationEntry.config(state='disabled')
def csvWriteOut(firstRun,carData,csvFile):
if firstRun == True:
out=""
for im,nm in carData.items():
out = out + im + ","
csvFile.write(out + "\n")
outString=""
for itm,num in carData.items():
outString = outString + str(num) +","
csvFile.write(outString+"\n")
def runningThread():
global stopThread
gameType= tkGametype.get()
#check everything is connected
if gameType==GameType.BEAMNG.value:
portToConnect=BEAMNG_UDP_PORT
elif gameType==GameType.FORZA.value:
portToConnect=FORZA_UDP_PORT
try:
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.bind((UDP_IP,portToConnect))
except:
print("please check you are able to open the socket on this system\ntired to open port: "+portToConnect)
exit()
try:
toPi=serial.Serial(SERIAL_PORT,115200,timeout=2) #connect to arduino
except:
print("please check connection to arduino and verify the correct serial port")
exit()
csvOut = tkLoggingEnabled.get()
if csvOut == True:
csvFile = open(loggingLocationText.get(),"a")
while True:
while not stopThread:
data, addr = sock.recvfrom(1024) data, addr = sock.recvfrom(1024)
if gameType == GameType.BEAMNG.value:
unpackedData = struct.unpack(BEAMNG_METHODS.BEAMNG_DATA_FORMAT,data) unpackedData = struct.unpack(BEAMNG_METHODS.BEAMNG_DATA_FORMAT,data)
carData = BEAMNG_METHODS.unpackData(unpackedData) carData = BEAMNG_METHODS.unpackData(unpackedData)
kmh=carData["speed"]*3.6 kmh=carData["speed"]*3.6
toPi.write(str(kmh).encode()+":".encode()) if csvOut == True:
csvWriteOut(firstRun,carData,csvFile)
firstRun=False
elif gameType == GameType.FORZA.value:
unpackedData = struct.unpack(FORZA_METHODS.FORZA_DATA_FORMAT,data)
carData = FORZA_METHODS.unpackData(unpackedData)
kmh = carData["Speed"]*3.6
if csvOut == True:
csvWriteOut(firstRun,carData,csvFile)
firstRun=False
try:
toPi.write(str(kmh).encode()+":".encode())
except:
print("arduino disconnected please check connection\n")
#code runs from here<-----------------------------------------------------------------------------------------------------------
if runningOs == 'Windows':
print("Windows detacted setting serial port to 'COM5'\n")
SERIAL_PORT= 'COM5'
elif runningOs == 'Linux':
print("Linux detected setting serial port to '/dev/ttyAMC0'\n")
SERIAL_PORT = '/dev/ttyACM0'
else:
print("OS detection failed setting serial port to 'COM5'\n")
SERIAL_PORT = 'COM5'
def startStopButtonFunc():
global appState
if appState == ProgramState.WAITNG:
startStopButton.config(text="stop")
appState = ProgramState.PUSHING_DATA
stopThread=False
workThread = threading.Thread(target=runningThread)
workThread.start()
statusTextVar.set("Running...")
elif appState == ProgramState.PUSHING_DATA:
startStopButton.config(text="start")
appState = ProgramState.WAITNG
statusTextVar.set("Waiting...")
stopThread = True
##GUI init
rootFrameGui= tkinter.Tk()
rootFrameGui.title("Car speed to arduino")
##rootFrameGui.geometry("400x200")
bottomBarFrameGui = tkinter.ttk.Frame(rootFrameGui,padding=0,relief="groove",borderwidth=2,)
bottomBarFrameGui.pack(anchor="se",side="bottom",fill="x")
firstFrameGui=tkinter.ttk.Frame(rootFrameGui,padding=2,relief="groove",borderwidth=2)
firstFrameGui.pack(anchor="ne",side="left",expand=True)
secondFrameGui=tkinter.ttk.Frame(rootFrameGui,padding=2,relief="groove",borderwidth=2)
secondFrameGui.pack(anchor="nw",side="right",expand=True)
serialFrameGui = tkinter.ttk.Frame(firstFrameGui,padding=5,relief="groove",borderwidth=2)
serialFrameGui.pack(anchor="nw")
serialLableText =tkinter.StringVar()
serialLableText.set("Serial Port:")
serialLable = tkinter.Label(serialFrameGui,textvariable=serialLableText)
serialLable.pack(side="top")
serialEntry=tkinter.Entry(serialFrameGui)
serialEntry.insert(0,SERIAL_PORT)
serialEntry.pack(side="left")
tkGametype = tkinter.StringVar()
tkGametype.set(GameType.BEAMNG.value)
gameSelectFrame= tkinter.ttk.Frame(secondFrameGui,padding=5,relief="groove",borderwidth=2)
gameSelectFrame.pack(anchor="se")
gameSelectText= tkinter.StringVar()
gameSelectText.set("Select Game:")
gameSelectLable = tkinter.Label(gameSelectFrame,textvariable=gameSelectText)
gameSelectLable.pack(side="top")
tkinter.Label(gameSelectFrame,textvariable=gameSelectText)
gameSelectLOptions= [GameType.BEAMNG,GameType.FORZA]
##gameSelectRadioButtons
##space after text is so the buttons are the same size
tkinter.Radiobutton(gameSelectFrame,text="BeamNG ",variable=tkGametype,value=GameType.BEAMNG.value,command=checkBoxChange).pack(anchor="w")
tkinter.Radiobutton(gameSelectFrame,text="Forza ",variable=tkGametype,value=GameType.FORZA.value,command=checkBoxChange).pack(anchor="w")
loggingFrameGui= tkinter.ttk.Frame(firstFrameGui,padding=5,relief="groove",borderwidth=2)
loggingFrameGui.pack(anchor="nw")
tkLoggingEnabled=tkinter.BooleanVar()
tkLoggingEnabled.set(False)
tkinter.Checkbutton(loggingFrameGui,text="Enable Logging",variable=tkLoggingEnabled,onvalue=True,offvalue=False,command=checkBoxChange).pack(side="top")
loggingLocationText = tkinter.StringVar(value="")
loggingLocationEntry = tkinter.Entry(loggingFrameGui)
loggingLocationEntry.insert(0,loggingLocationText.get())
loggingLocationEntry.config(state='disabled')
loggingLocationEntry.pack(side="bottom")
startStopButton= tkinter.Button(bottomBarFrameGui,text= "start",command= startStopButtonFunc)
startStopButton.pack(side="right",fill="x")
statusTextVar = tkinter.StringVar(value="waiting...")
statusLable = tkinter.Label(bottomBarFrameGui,textvariable= statusTextVar)
statusLable.pack(anchor="w")
arduinoConnectedFrameGui = tkinter.ttk.Frame(secondFrameGui,padding=5,relief="groove",borderwidth=2)
arduinoConnectedFrameGui.pack()
arduinoConnectedStatusText=tkinter.StringVar(value="Connected OK")
arduinoConnectedLable = tkinter.Label(arduinoConnectedFrameGui,textvariable=arduinoConnectedStatusText)
arduinoConnectedLable.pack()
rootFrameGui.mainloop()

3
createExe.sh Normal file
View File

@@ -0,0 +1,3 @@
pyinstaller.exe --add-data .\GAME_METHODS\*:.\GAME_METHODS\ --onefile .\OutGaugeInterpreter.py
pyinstaller.exe --add-data .\GAME_METHODS\*:.\GAME_METHODS\ --onefile --noconsole --optimize 2 .\OutGaugeInterpreter.py
pyinstaller --add-data ./GAME_METHODS/*:./GAME_METHODS/ --onefile ./OutGaugeInterpreter.py