diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..7e99e367 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc \ No newline at end of file diff --git a/debugEvent.py b/debugEvent.py index f0587571..555ed234 100644 --- a/debugEvent.py +++ b/debugEvent.py @@ -1,34 +1,45 @@ - #!/usr/bin/env python -########################################################################### -# obd_sensors.py -# -# Copyright 2004 Donour Sizemore (donour@uchicago.edu) -# Copyright 2009 Secons Ltd. (www.obdtester.com) -# -# This file is part of pyOBD. -# -# pyOBD is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# pyOBD is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with pyOBD; if not, write to the Free Software -# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -########################################################################### -import wx - -EVT_DEBUG_ID = 1010 - -class DebugEvent(wx.PyEvent): - """Simple event to carry arbitrary result data.""" - def __init__(self, data): - """Init Result Event.""" - wx.PyEvent.__init__(self) - self.SetEventType(EVT_DEBUG_ID) - self.data = data \ No newline at end of file + #!/usr/bin/env python +########################################################################### +# obd_sensors.py +# +# Copyright 2004 Donour Sizemore (donour@uchicago.edu) +# Copyright 2009 Secons Ltd. (www.obdtester.com) +# +# This file is part of pyOBD. +# +# pyOBD is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# pyOBD is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with pyOBD; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +########################################################################### +try: + import wx + + EVT_DEBUG_ID = 1010 + + def debug_display(window, position, message): + if window is None: + print message + else: + wx.PostEvent(window, DebugEvent([position, message])) + + class DebugEvent(wx.PyEvent): + """Simple event to carry arbitrary result data.""" + def __init__(self, data): + """Init Result Event.""" + wx.PyEvent.__init__(self) + self.SetEventType(EVT_DEBUG_ID) + self.data = data +except ImportError as e: + def debug_display(window, position, message): + print message + diff --git a/obd_io.py b/obd_io.py index 7dd6d1cb..79413af7 100644 --- a/obd_io.py +++ b/obd_io.py @@ -26,7 +26,7 @@ import string import time from math import ceil -import wx #due to debugEvent messaging +from datetime import datetime import obd_sensors @@ -36,7 +36,7 @@ CLEAR_DTC_COMMAND = "04" GET_FREEZE_DTC_COMMAND = "07" -from debugEvent import * +from debugEvent import debug_display #__________________________________________________________________________ def decrypt_dtc_code(code): @@ -74,16 +74,17 @@ class OBDPort: def __init__(self,portnum,_notify_window,SERTIMEOUT,RECONNATTEMPTS): """Initializes port by resetting device and gettings supported PIDs. """ # These should really be set by the user. - baud = 9600 + baud = 38400 databits = 8 par = serial.PARITY_NONE # parity sb = 1 # stop bits to = SERTIMEOUT self.ELMver = "Unknown" self.State = 1 #state SERIAL is 1 connected, 0 disconnected (connection failed) + self.port = None self._notify_window=_notify_window - wx.PostEvent(self._notify_window, DebugEvent([1,"Opening interface (serial port)"])) + debug_display(self._notify_window, 1, "Opening interface (serial port)") try: self.port = serial.Serial(portnum,baud, \ @@ -94,22 +95,32 @@ def __init__(self,portnum,_notify_window,SERTIMEOUT,RECONNATTEMPTS): self.State = 0 return None - wx.PostEvent(self._notify_window, DebugEvent([1,"Interface successfully " + self.port.portstr + " opened"])) - wx.PostEvent(self._notify_window, DebugEvent([1,"Connecting to ECU..."])) + debug_display(self._notify_window, 1, "Interface successfully " + self.port.portstr + " opened") + debug_display(self._notify_window, 1, "Connecting to ECU...") try: self.send_command("atz") # initialize + time.sleep(1) except serial.SerialException: self.State = 0 return None self.ELMver = self.get_result() - wx.PostEvent(self._notify_window, DebugEvent([2,"atz response:" + self.ELMver])) + if(self.ELMver is None): + self.State = 0 + return None + + debug_display(self._notify_window, 2, "atz response:" + self.ELMver) self.send_command("ate0") # echo off - wx.PostEvent(self._notify_window, DebugEvent([2,"ate0 response:" + self.get_result()])) + debug_display(self._notify_window, 2, "ate0 response:" + self.get_result()) self.send_command("0100") ready = self.get_result() - wx.PostEvent(self._notify_window, DebugEvent([2,"0100 response:" + ready])) + + if(ready is None): + self.State = 0 + return None + + debug_display(self._notify_window, 2, "0100 response:" + ready) return None def close(self): @@ -130,7 +141,7 @@ def send_command(self, cmd): for c in cmd: self.port.write(c) self.port.write("\r\n") - wx.PostEvent(self._notify_window, DebugEvent([3,"Send command:" + cmd])) + #debug_display(self._notify_window, 3, "Send command:" + cmd) def interpret_result(self,code): """Internal use only: not a public interface""" @@ -161,20 +172,34 @@ def interpret_result(self,code): def get_result(self): """Internal use only: not a public interface""" - time.sleep(0.1) - if self.port: + #time.sleep(0.01) + repeat_count = 0 + if self.port is not None: buffer = "" while 1: c = self.port.read(1) - if c == '\r' and len(buffer) > 0: - break - else: - if buffer != "" or c != ">": #if something is in buffer, add everything - buffer = buffer + c - wx.PostEvent(self._notify_window, DebugEvent([3,"Get result:" + buffer])) + if len(c) == 0: + if(repeat_count == 5): + break + print "Got nothing\n" + repeat_count = repeat_count + 1 + continue + + if c == '\r': + continue + + if c == ">": + break; + + if buffer != "" or c != ">": #if something is in buffer, add everything + buffer = buffer + c + + #debug_display(self._notify_window, 3, "Get result:" + buffer) + if(buffer == ""): + return None return buffer else: - wx.PostEvent(self._notify_window, DebugEvent([3,"NO self.port!" + buffer])) + debug_display(self._notify_window, 3, "NO self.port!") return None # get sensor value from command @@ -190,6 +215,7 @@ def get_sensor_value(self,sensor): data = sensor.value(data) else: return "NORESPONSE" + return data # return string of sensor name and value from sensor index diff --git a/obd_recorder.py b/obd_recorder.py new file mode 100755 index 00000000..9f0691c2 --- /dev/null +++ b/obd_recorder.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python + +import obd_io +import serial +import platform +import obd_sensors +from datetime import datetime +import time + +from obd_utils import scanSerial + +class OBD_Recorder(): + def __init__(self, path, log_items): + self.port = None + self.sensorlist = [] + localtime = time.localtime(time.time()) + filename = path+"bike-"+str(localtime[0])+"-"+str(localtime[1])+"-"+str(localtime[2])+"-"+str(localtime[3])+"-"+str(localtime[4])+"-"+str(localtime[5])+".log" + self.log_file = open(filename, "w", 128) + self.log_file.write("Time,RPM,MPH,Throttle,Load,Gear\n"); + + for item in log_items: + self.add_log_item(item) + + self.gear_ratios = [34/13, 39/21, 36/23, 27/20, 26/21, 25/22] + #log_formatter = logging.Formatter('%(asctime)s.%(msecs).03d,%(message)s', "%H:%M:%S") + + def connect(self): + portnames = scanSerial() + #portnames = ['COM10'] + print portnames + for port in portnames: + self.port = obd_io.OBDPort(port, None, 2, 2) + if(self.port.State == 0): + self.port.close() + self.port = None + else: + break + + if(self.port): + print "Connected to "+self.port.port.name + + def is_connected(self): + return self.port + + def add_log_item(self, item): + for index, e in enumerate(obd_sensors.SENSORS): + if(item == e.shortname): + self.sensorlist.append(index) + print "Logging item: "+e.name + break + + + def record_data(self): + if(self.port is None): + return None + + print "Logging started" + + while 1: + localtime = datetime.now() + current_time = str(localtime.hour)+":"+str(localtime.minute)+":"+str(localtime.second)+"."+str(localtime.microsecond) + log_string = current_time + results = {} + for index in self.sensorlist: + (name, value, unit) = self.port.sensor(index) + log_string = log_string + ","+str(value) + results[obd_sensors.SENSORS[index].shortname] = value; + + gear = self.calculate_gear(results["rpm"], results["speed"]) + log_string = log_string + "," + str(gear) + self.log_file.write(log_string+"\n") + + def calculate_gear(self, rpm, speed): + if speed == "" or speed == 0: + return 0 + if rpm == "" or rpm == 0: + return 0 + + rps = rpm/60 + mps = (speed*1.609*1000)/3600 + + primary_gear = 85/46 #street triple + final_drive = 47/16 + + tyre_circumference = 1.978 #meters + + current_gear_ratio = (rps*tyre_circumference)/(mps*primary_gear*final_drive) + + print current_gear_ratio + gear = min((abs(current_gear_ratio - i), i) for i in self.gear_ratios)[1] + return gear + + +logitems = ["rpm", "speed", "throttle_pos", "load"] +o = OBD_Recorder('/home/pi/logs/', logitems) +o.connect() +if not o.is_connected(): + print "Not connected" +o.record_data() diff --git a/obd_sensors.py b/obd_sensors.py index 7ae4650e..59b58329 100644 --- a/obd_sensors.py +++ b/obd_sensors.py @@ -126,47 +126,47 @@ def hex_to_bitstring(str): return bitstring class Sensor: - def __init__(self,sensorName, sensorcommand, sensorValueFunction, u): + def __init__(self, shortName, sensorName, sensorcommand, sensorValueFunction, u): + self.shortname = shortName self.name = sensorName self.cmd = sensorcommand self.value= sensorValueFunction self.unit = u SENSORS = [ - Sensor(" Supported PIDs", "0100", hex_to_bitstring ,"" ), - Sensor("Status Since DTC Cleared", "0101", dtc_decrypt ,"" ), - Sensor("DTC Causing Freeze Frame", "0102", cpass ,"" ), - Sensor(" Fuel System Status", "0103", cpass ,"" ), - Sensor(" Calculated Load Value", "0104", percent_scale ,"" ), - Sensor(" Coolant Temperature", "0105", temp ,"C" ), - Sensor(" Short Term Fuel Trim", "0106", fuel_trim_percent ,"%" ), - Sensor(" Long Term Fuel Trim", "0107", fuel_trim_percent ,"%" ), - Sensor(" Short Term Fuel Trim", "0108", fuel_trim_percent ,"%" ), - Sensor(" Long Term Fuel Trim", "0109", fuel_trim_percent ,"%" ), - Sensor(" Fuel Rail Pressure", "010A", cpass ,"" ), - Sensor("Intake Manifold Pressure", "010B", intake_m_pres ,"psi" ), - Sensor(" Engine RPM", "010C", rpm ,"" ), - Sensor(" Vehicle Speed", "010D", speed ,"MPH" ), - Sensor(" Timing Advance", "010E", timing_advance ,"degrees"), - Sensor(" Intake Air Temp", "010F", temp ,"C" ), - Sensor(" Air Flow Rate (MAF)", "0110", maf ,"lb/min" ), - Sensor(" Throttle Position", "0111", throttle_pos ,"%" ), - Sensor(" Secondary Air Status", "0112", cpass ,"" ), - Sensor(" Location of O2 sensors", "0113", cpass ,"" ), - Sensor(" O2 Sensor: 1 - 1", "0114", fuel_trim_percent ,"%" ), - Sensor(" O2 Sensor: 1 - 2", "0115", fuel_trim_percent ,"%" ), - Sensor(" O2 Sensor: 1 - 3", "0116", fuel_trim_percent ,"%" ), - Sensor(" O2 Sensor: 1 - 4", "0117", fuel_trim_percent ,"%" ), - Sensor(" O2 Sensor: 2 - 1", "0118", fuel_trim_percent ,"%" ), - Sensor(" O2 Sensor: 2 - 2", "0119", fuel_trim_percent ,"%" ), - Sensor(" O2 Sensor: 2 - 3", "011A", fuel_trim_percent ,"%" ), - Sensor(" O2 Sensor: 2 - 4", "011B", fuel_trim_percent ,"%" ), - Sensor(" OBD Designation", "011C", cpass ,"" ), - Sensor(" Location of O2 sensors", "011D", cpass ,"" ), - Sensor(" Aux input status", "011E", cpass ,"" ), - Sensor(" Time Since Engine Start", "011F", sec_to_min ,"min" ), - Sensor(" Engine Run with MIL on", "014E", sec_to_min ,"min" ), - + Sensor("pids" , " Supported PIDs", "0100", hex_to_bitstring ,"" ), + Sensor("dtc_status" , "Status Since DTC Cleared", "0101", dtc_decrypt ,"" ), + Sensor("dtc_ff" , "DTC Causing Freeze Frame", "0102", cpass ,"" ), + Sensor("fuel_status" , " Fuel System Status", "0103", cpass ,"" ), + Sensor("load" , " Calculated Load Value", "01041", percent_scale ,"" ), + Sensor("temp" , " Coolant Temperature", "0105", temp ,"C" ), + Sensor("short_term_fuel_trim_1", " Short Term Fuel Trim", "0106", fuel_trim_percent,"%" ), + Sensor("long_term_fuel_trim_1" , " Long Term Fuel Trim", "0107", fuel_trim_percent,"%" ), + Sensor("short_term_fuel_trim_2", " Short Term Fuel Trim", "0108", fuel_trim_percent,"%" ), + Sensor("long_term_fuel_trim_2" , " Long Term Fuel Trim", "0109", fuel_trim_percent,"%" ), + Sensor("fuel_pressure" , " Fuel Rail Pressure", "010A", cpass ,"" ), + Sensor("manifold_pressure" , "Intake Manifold Pressure", "010B", intake_m_pres ,"psi" ), + Sensor("rpm" , " Engine RPM", "010C1", rpm ,"" ), + Sensor("speed" , " Vehicle Speed", "010D1", speed ,"MPH" ), + Sensor("timing_advance" , " Timing Advance", "010E", timing_advance ,"degrees"), + Sensor("intake_air_temp" , " Intake Air Temp", "010F", temp ,"C" ), + Sensor("maf" , " Air Flow Rate (MAF)", "0110", maf ,"lb/min" ), + Sensor("throttle_pos" , " Throttle Position", "01111", throttle_pos ,"%" ), + Sensor("secondary_air_status" , " Secondary Air Status", "0112", cpass ,"" ), + Sensor("o2_sensor_positions" , " Location of O2 sensors", "0113", cpass ,"" ), + Sensor("o211" , " O2 Sensor: 1 - 1", "0114", fuel_trim_percent,"%" ), + Sensor("o212" , " O2 Sensor: 1 - 2", "0115", fuel_trim_percent,"%" ), + Sensor("o213" , " O2 Sensor: 1 - 3", "0116", fuel_trim_percent,"%" ), + Sensor("o214" , " O2 Sensor: 1 - 4", "0117", fuel_trim_percent,"%" ), + Sensor("o221" , " O2 Sensor: 2 - 1", "0118", fuel_trim_percent,"%" ), + Sensor("o222" , " O2 Sensor: 2 - 2", "0119", fuel_trim_percent,"%" ), + Sensor("o223" , " O2 Sensor: 2 - 3", "011A", fuel_trim_percent,"%" ), + Sensor("o224" , " O2 Sensor: 2 - 4", "011B", fuel_trim_percent,"%" ), + Sensor("obd_standard" , " OBD Designation", "011C", cpass ,"" ), + Sensor("o2_sensor_position_b" ," Location of O2 sensors" , "011D", cpass ,"" ), + Sensor("aux_input" , " Aux input status", "011E", cpass ,"" ), + Sensor("engine_time" , " Time Since Engine Start", "011F", sec_to_min ,"min" ), + Sensor("engine_mil_time" , " Engine Run with MIL on", "014D", sec_to_min ,"min" ), ] diff --git a/obd_utils.py b/obd_utils.py new file mode 100644 index 00000000..6452c7e7 --- /dev/null +++ b/obd_utils.py @@ -0,0 +1,48 @@ +import serial +import platform + +def scanSerial(): + """scan for available ports. return a list of serial names""" + available = [] + for i in range(256): + try: #scan standart ttyS* + s = serial.Serial(i) + available.append(s.portstr) + s.close() # explicit close 'cause of delayed GC in java + except serial.SerialException: + pass + for i in range(256): + try: #scan USB ttyACM + s = serial.Serial("/dev/ttyACM"+str(i)) + available.append(s.portstr) + s.close() # explicit close 'cause of delayed GC in java + except serial.SerialException: + pass + for i in range(256): + try: + s = serial.Serial("/dev/ttyUSB"+str(i)) + available.append(s.portstr) + s.close() # explicit close 'cause of delayed GC in java + except serial.SerialException: + pass + for i in range(256): + try: + s = serial.Serial("/dev/ttyd"+str(i)) + available.append(s.portstr) + s.close() # explicit close 'cause of delayed GC in java + except serial.SerialException: + pass + + # ELM-USB shows up as /dev/tty.usbmodemXXXX, where XXXX is a changing hex string + # on connection; so we have to search through all 64K options + if len(platform.mac_ver()[0])!=0: #search only on MAC + for i in range (65535): + extension = hex(i).replace("0x","", 1) + try: + s = serial.Serial("/dev/tty.usbmodem"+extension) + available.append(s.portstr) + s.close() + except serial.SerialException: + pass + + return available diff --git a/pyobd b/pyobd index a8180a47..dbc3b4e7 100755 --- a/pyobd +++ b/pyobd @@ -40,6 +40,7 @@ import webbrowser #open browser from python from obd2_codes import pcodes from obd2_codes import ptest +from obd_utils import scanSerial from wx.lib.mixins.listctrl import ListCtrlAutoWidthMixin @@ -599,59 +600,12 @@ the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 0211 self.ThreadControl=1 self.nb.SetSelection(3) - - def scanSerial(self): - """scan for available ports. return a list of serial names""" - available = [] - for i in range(256): - try: #scan standart ttyS* - s = serial.Serial(i) - available.append(s.portstr) - s.close() # explicit close 'cause of delayed GC in java - except serial.SerialException: - pass - for i in range(256): - try: #scan USB ttyACM - s = serial.Serial("/dev/ttyACM"+str(i)) - available.append(s.portstr) - s.close() # explicit close 'cause of delayed GC in java - except serial.SerialException: - pass - for i in range(256): - try: - s = serial.Serial("/dev/ttyUSB"+str(i)) - available.append(s.portstr) - s.close() # explicit close 'cause of delayed GC in java - except serial.SerialException: - pass - for i in range(256): - try: - s = serial.Serial("/dev/ttyd"+str(i)) - available.append(s.portstr) - s.close() # explicit close 'cause of delayed GC in java - except serial.SerialException: - pass - - # ELM-USB shows up as /dev/tty.usbmodemXXXX, where XXXX is a changing hex string - # on connection; so we have to search through all 64K options - if len(platform.mac_ver()[0])!=0: #search only on MAC - for i in range (65535): - extension = hex(i).replace("0x","", 1) - try: - s = serial.Serial("/dev/tty.usbmodem"+extension) - available.append(s.portstr) - s.close() - except serial.SerialException: - pass - - return available - def Configure(self,e = None): id = 0 diag = wx.Dialog(self.frame, id, title="Configure") sizer = wx.BoxSizer(wx.VERTICAL) - ports = self.scanSerial() + ports = scanSerial() rb = wx.RadioBox(diag, id, "Choose Serial Port", choices = ports, style = wx.RA_SPECIFY_COLS, majorDimension = 2)