Compare commits

..

33 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
10 changed files with 303 additions and 33 deletions

1
.gitignore vendored
View File

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

View File

@@ -1 +1,117 @@
FROZA_DATA_FORMAT = '<iIfffffffffffffffffffffffffffffffffffffffffffffffffffiiiiifffffffffffffffffHBBBBBBbbb'
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

Binary file not shown.

Binary file not shown.

View File

@@ -1,60 +1,210 @@
#please use python 3.11
import socket
import struct
import tkinter.ttk
import serial
import time
import platform
import sys
import threading
from enum import Enum
import tkinter
from GAME_METHODS import *
##import BEAMNG_FILES as BEAM
import functools #used for passing variables into callbacks
COM_PORT = 'COM5'
UDP_IP = "127.0.0.1"
##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.")
##system init
runningOs = platform.system()
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
dataFormat = ""
carData = 0
csvOut = False
while gameSelected == False:
gameNo = input("1:BEAMNG\n2:FORZA\n")
if gameNo == "1":
portToConnect = BEAMNG_UDP_PORT
dataFormat =BEAMNG_METHODS.BEAMNG_DATA_FORMAT
gameSelected = True
print("BeamNG Selected")
elif gameNo == "2":
portToConnect = FORZA_UDP_PORT
dataFormat = FORZA_METHODS.FROZA_DATA_FORMAT
print("Forza Selected")
gameSelected=True
#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:
print("please select a number from the list")
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
while connectedWebSocket == False:
try:
sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
sock.bind((UDP_IP,portToConnect))
connectedWebSocket = True
except:
print("please check you are able to open the socket on this system\ntired to open port: "+portToConnect)
exit()
while connectedArduino == False:
try:
toPi=serial.Serial(COM_PORT,115200) #connect to arduino
connectedArduino = True
toPi=serial.Serial(SERIAL_PORT,115200,timeout=2) #connect to arduino
except:
print("please check connection to arduino and verify the correct COM port")
time.sleep(1)
print("please check connection to arduino and verify the correct serial port")
exit()
print("ready:\n")
csvOut = tkLoggingEnabled.get()
while True:
data, addr = sock.recvfrom(1024)
unpackedData = struct.unpack(dataFormat,data)
carData = BEAMNG_METHODS.unpackData(unpackedData)
kmh=carData["speed"]*3.6
toPi.write(str(kmh).encode()+":".encode())
if csvOut == True:
csvFile = open(loggingLocationText.get(),"a")
while not stopThread:
data, addr = sock.recvfrom(1024)
if gameType == GameType.BEAMNG.value:
unpackedData = struct.unpack(BEAMNG_METHODS.BEAMNG_DATA_FORMAT,data)
carData = BEAMNG_METHODS.unpackData(unpackedData)
kmh=carData["speed"]*3.6
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