panda/python/__init__.py

664 lines
20 KiB
Python
Raw Permalink Normal View History

2017-04-06 19:11:36 -06:00
# python library to interface with panda
import datetime
2017-04-06 19:11:36 -06:00
import struct
2017-04-28 17:56:40 -06:00
import hashlib
import socket
2017-04-06 19:11:36 -06:00
import usb1
2017-07-17 16:29:31 -06:00
import os
import time
2017-08-12 19:42:24 -06:00
import traceback
import sys
from .dfu import PandaDFU # pylint: disable=import-error
from .flash_release import flash_release # noqa pylint: disable=import-error
from .update import ensure_st_up_to_date # noqa pylint: disable=import-error
from .serial import PandaSerial # noqa pylint: disable=import-error
from .isotp import isotp_send, isotp_recv # pylint: disable=import-error
2017-04-06 19:11:36 -06:00
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
__version__ = '0.0.9'
2017-07-24 16:16:22 -06:00
BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)), "../")
DEFAULT_FW_FN = os.path.join(BASEDIR, "board", "obj", "panda.bin.signed")
2017-07-24 16:16:22 -06:00
2018-01-23 19:40:53 -07:00
DEBUG = os.getenv("PANDADEBUG") is not None
def parse_can_buffer(dat):
ret = []
for j in range(0, len(dat), 0x10):
ddat = dat[j:j + 0x10]
f1, f2 = struct.unpack("II", ddat[0:8])
extended = 4
if f1 & extended:
address = f1 >> 3
else:
address = f1 >> 21
dddat = ddat[8:8 + (f2 & 0xF)]
2018-01-23 19:40:53 -07:00
if DEBUG:
print(f" R 0x{address:x}: 0x{dddat.hex()}")
ret.append((address, f2 >> 16, dddat, (f2 >> 4) & 0xFF))
return ret
class PandaWifiStreaming(object):
def __init__(self, ip="192.168.0.10", port=1338):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock.setblocking(0)
self.ip = ip
self.port = port
self.kick()
def kick(self):
# must be called at least every 5 seconds
self.sock.sendto("hello", (self.ip, self.port))
def can_recv(self):
ret = []
while True:
try:
dat, addr = self.sock.recvfrom(0x200 * 0x10)
if addr == (self.ip, self.port):
ret += parse_can_buffer(dat)
2017-08-12 19:42:24 -06:00
except socket.error as e:
2017-08-21 14:57:05 -06:00
if e.errno != 35 and e.errno != 11:
2017-08-12 19:42:24 -06:00
traceback.print_exc()
break
return ret
# stupid tunneling of USB over wifi and SPI
class WifiHandle(object):
def __init__(self, ip="192.168.0.10", port=1337):
self.sock = socket.create_connection((ip, port))
def __recv(self):
ret = self.sock.recv(0x44)
2017-04-18 08:34:56 -06:00
length = struct.unpack("I", ret[0:4])[0]
return ret[4:4 + length]
def controlWrite(self, request_type, request, value, index, data, timeout=0):
Revert commits that broke USB for openpilot. Revert "fix openpilot board flashing" This reverts commit 8ff93ad5da39f8dc4bf6fe632f26418b696fd230. Revert "Fixed output_enabled led not turning off when mode changed to no output." This reverts commit 27a8af11075d92d03c389713694a879905877cf0. Revert "Fixed loopback test for new GMLAN 'can4' behavior." This reverts commit 59592f599af01a667b4fd966e613b8f504d62dc2. Revert "GMLAN is now always mapped through CAN4 (index 3)" This reverts commit 329c09102435bfd9b1fbb60694139a5ff7bf4148. Revert "Removed compile time config for CAN loopback, implemented as usb message." This reverts commit e1a4c3298557fccf854ed5cbda448f8c0015b7ea. Revert "Change all output safety mode identifier to prevent user mistakes." This reverts commit 6b363e2e92fcd5e7f25b5458fe9008ff8f9fd664. Revert "untabify" This reverts commit 191f67b083e182323ba956c3ab75df10bec2f863. Revert "Refactor of safety to support more modular additions of safety policies." This reverts commit e5b524eddc82e53587cc47dcf15b22fd35890a92. Revert "Split up some more header files into compilation units." This reverts commit e2a78912f5b649822974fc0e974ec50d9d9c7d10. Revert "Enabled emulated control writes over USB." This reverts commit 133cfe970379d6881de26289616d1d9085bb5986. Revert "Moved CAN and USART code out of main.c and into more appropriate files." This reverts commit daad2dc0620d629e7db0dd68dee5595ed2b57160. Revert "Large Panda CAN cleanup. Restrict GMLAN to valid baud rates." This reverts commit a0616a2bc2ac2bfd99223aaa84912e6f649c9d54. Revert "Panda library now correctly sends USB direction bit." This reverts commit 1712c901d4b46b2726b3165a7cb2e91c281c662b. Revert "Board makefile now automatically calculates header file dependencies." This reverts commit 4a8d4e597b397ca6d68dd5dd2a376c8354dc3422. Revert "Loopback test works over wifi. (Disable trying to send over wifi)" This reverts commit dae636968af482e170aade1d785a1e197e9f3c04. Revert "Fix legacy board build" This reverts commit 62bf4e575686c84c672eb0d341ad41f174141c2d. Revert "Style cop" This reverts commit c439f43726feb30cf2ec486ffcad6ac94ab5e128. Revert "Untabify" This reverts commit 41e5eec6211c23836535af49380f74350a0ceb12. Revert "Fixed disabling gmlan." This reverts commit 5e1e45a4afade384b628e44587dd8e37d3dcd8cd. Revert "Removed dead code, standardized canid in more commands, better erroring behavior." This reverts commit b59aeb6d87ddd85406ec42e4ed8a74a232d506a4. Revert "loopback test works with new CAN bus ids." This reverts commit 75970861cf2b025173afb906e4e243861bed506a. Revert "Large reorganization of code and early integration of can bitrate setting." This reverts commit a1ed7b62ee66ec8f56bba488c38c67b69eead8cf.
2017-07-12 12:25:10 -06:00
# ignore data in reply, panda doesn't use it
return self.controlRead(request_type, request, value, index, 0, timeout)
def controlRead(self, request_type, request, value, index, length, timeout=0):
2017-04-18 08:34:56 -06:00
self.sock.send(struct.pack("HHBBHHH", 0, 0, request_type, request, value, index, length))
return self.__recv()
def bulkWrite(self, endpoint, data, timeout=0):
if len(data) > 0x10:
raise ValueError("Data must not be longer than 0x10")
self.sock.send(struct.pack("HH", endpoint, len(data)) + data)
self.__recv() # to /dev/null
def bulkRead(self, endpoint, length, timeout=0):
2017-04-18 08:34:56 -06:00
self.sock.send(struct.pack("HH", endpoint, 0))
return self.__recv()
2017-04-28 18:54:23 -06:00
def close(self):
self.sock.close()
2017-08-22 15:02:07 -06:00
# *** normal mode ***
2017-04-06 19:11:36 -06:00
class Panda(object):
# matches cereal.car.CarParams.SafetyModel
SAFETY_SILENT = 0
SAFETY_HONDA_NIDEC = 1
SAFETY_TOYOTA = 2
SAFETY_ELM327 = 3
SAFETY_GM = 4
SAFETY_HONDA_BOSCH_GIRAFFE = 5
SAFETY_FORD = 6
SAFETY_HYUNDAI = 8
SAFETY_CHRYSLER = 9
SAFETY_TESLA = 10
SAFETY_SUBARU = 11
SAFETY_MAZDA = 13
SAFETY_NISSAN = 14
SAFETY_VOLKSWAGEN_MQB = 15
SAFETY_ALLOUTPUT = 17
SAFETY_GM_ASCM = 18
SAFETY_NOOUTPUT = 19
SAFETY_HONDA_BOSCH_HARNESS = 20
SAFETY_VOLKSWAGEN_PQ = 21
SAFETY_SUBARU_LEGACY = 22
2020-06-12 19:03:31 -06:00
SAFETY_HYUNDAI_LEGACY = 23
2017-07-17 16:29:31 -06:00
2017-07-18 13:15:19 -06:00
SERIAL_DEBUG = 0
SERIAL_ESP = 1
SERIAL_LIN1 = 2
SERIAL_LIN2 = 3
2017-07-18 22:05:09 -06:00
GMLAN_CAN2 = 1
GMLAN_CAN3 = 2
2017-07-17 19:59:16 -06:00
REQUEST_IN = usb1.ENDPOINT_IN | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
REQUEST_OUT = usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
HW_TYPE_UNKNOWN = b'\x00'
HW_TYPE_WHITE_PANDA = b'\x01'
HW_TYPE_GREY_PANDA = b'\x02'
HW_TYPE_BLACK_PANDA = b'\x03'
HW_TYPE_PEDAL = b'\x04'
HW_TYPE_UNO = b'\x05'
CLOCK_SOURCE_MODE_DISABLED = 0
CLOCK_SOURCE_MODE_FREE_RUNNING = 1
CLOCK_SOURCE_MODE_EXTERNAL_SYNC = 2
2017-04-06 19:11:36 -06:00
def __init__(self, serial=None, claim=True):
self._serial = serial
2017-07-24 16:16:22 -06:00
self._handle = None
self.connect(claim)
def close(self):
self._handle.close()
self._handle = None
2017-07-29 18:21:22 -06:00
def connect(self, claim=True, wait=False):
if self._handle is not None:
2017-07-24 16:16:22 -06:00
self.close()
if self._serial == "WIFI":
self._handle = WifiHandle()
print("opening WIFI device")
2017-07-30 09:26:48 -06:00
self.wifi = True
else:
context = usb1.USBContext()
self._handle = None
2017-07-30 09:26:48 -06:00
self.wifi = False
2017-04-06 19:11:36 -06:00
2017-07-29 18:21:22 -06:00
while 1:
try:
for device in context.getDeviceList(skip_on_error=True):
if device.getVendorID() == 0xbbaa and device.getProductID() in [0xddcc, 0xddee]:
2017-08-28 19:54:31 -06:00
try:
this_serial = device.getSerialNumber()
except Exception:
continue
if self._serial is None or this_serial == self._serial:
self._serial = this_serial
2017-08-28 11:42:23 -06:00
print("opening device", self._serial, hex(device.getProductID()))
2017-07-29 18:21:22 -06:00
self.bootstub = device.getProductID() == 0xddee
self.legacy = (device.getbcdDevice() != 0x2300)
self._handle = device.open()
2021-04-16 07:32:27 -06:00
if sys.platform not in ["win32", "cygwin", "msys", "darwin"]:
self._handle.setAutoDetachKernelDriver(True)
2017-07-29 18:21:22 -06:00
if claim:
self._handle.claimInterface(0)
# self._handle.setInterfaceAltSetting(0, 0) # Issue in USB stack
2017-07-29 18:21:22 -06:00
break
except Exception as e:
print("exception", e)
2017-08-21 14:32:28 -06:00
traceback.print_exc()
if not wait or self._handle is not None:
2017-07-29 18:21:22 -06:00
break
2020-05-31 15:07:01 -06:00
context = usb1.USBContext() # New context needed so new devices show up
assert(self._handle is not None)
2017-07-29 18:21:22 -06:00
print("connected")
2017-07-24 16:16:22 -06:00
2018-03-10 12:22:22 -07:00
def reset(self, enter_bootstub=False, enter_bootloader=False):
2017-07-24 16:16:22 -06:00
# reset
try:
2017-07-27 15:29:07 -06:00
if enter_bootloader:
self._handle.controlWrite(Panda.REQUEST_IN, 0xd1, 0, 0, b'')
2017-07-24 16:16:22 -06:00
else:
2017-07-27 15:29:07 -06:00
if enter_bootstub:
self._handle.controlWrite(Panda.REQUEST_IN, 0xd1, 1, 0, b'')
else:
self._handle.controlWrite(Panda.REQUEST_IN, 0xd8, 0, 0, b'')
2017-07-24 16:16:22 -06:00
except Exception:
pass
2017-07-27 15:29:07 -06:00
if not enter_bootloader:
2018-03-10 12:22:22 -07:00
self.reconnect()
def reconnect(self):
self.close()
time.sleep(1.0)
success = False
# wait up to 15 seconds
for i in range(0, 15):
try:
self.connect()
success = True
break
except Exception:
print("reconnecting is taking %d seconds..." % (i + 1))
try:
2018-03-10 12:22:22 -07:00
dfu = PandaDFU(PandaDFU.st_serial_to_dfu_serial(self._serial))
dfu.recover()
except Exception:
2018-03-10 12:22:22 -07:00
pass
time.sleep(1.0)
if not success:
raise Exception("reconnect failed")
@staticmethod
def flash_static(handle, code):
# confirm flasher is present
fr = handle.controlRead(Panda.REQUEST_IN, 0xb0, 0, 0, 0xc)
2019-09-25 17:52:03 -06:00
assert fr[4:8] == b"\xde\xad\xd0\x0d"
2018-03-10 12:22:22 -07:00
# unlock flash
print("flash: unlocking")
handle.controlWrite(Panda.REQUEST_IN, 0xb1, 0, 0, b'')
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
# erase sectors 1 through 3
2018-03-10 12:22:22 -07:00
print("flash: erasing")
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
for i in range(1, 4):
handle.controlWrite(Panda.REQUEST_IN, 0xb2, i, 0, b'')
2018-03-10 12:22:22 -07:00
# flash over EP2
STEP = 0x10
print("flash: flashing")
for i in range(0, len(code), STEP):
handle.bulkWrite(2, code[i:i + STEP])
2018-03-10 12:22:22 -07:00
# reset
print("flash: resetting")
try:
handle.controlWrite(Panda.REQUEST_IN, 0xd8, 0, 0, b'')
except Exception:
pass
def flash(self, fn=DEFAULT_FW_FN, code=None, reconnect=True):
print("flash: main version is " + self.get_version())
2017-07-24 16:16:22 -06:00
if not self.bootstub:
2017-07-27 15:29:07 -06:00
self.reset(enter_bootstub=True)
2017-07-24 16:16:22 -06:00
assert(self.bootstub)
2017-08-22 14:36:20 -06:00
if code is None:
2019-09-25 17:52:03 -06:00
with open(fn, "rb") as f:
2017-08-22 14:36:20 -06:00
code = f.read()
2017-07-24 16:16:22 -06:00
# get version
print("flash: bootstub version is " + self.get_version())
2018-03-10 12:22:22 -07:00
# do flash
Panda.flash_static(self._handle, code)
2017-07-24 16:16:22 -06:00
2018-03-10 12:22:22 -07:00
# reconnect
if reconnect:
self.reconnect()
2017-04-06 19:11:36 -06:00
def recover(self, timeout=None):
self.reset(enter_bootstub=True)
2017-08-28 11:42:23 -06:00
self.reset(enter_bootloader=True)
t_start = time.time()
2017-08-28 11:42:23 -06:00
while len(PandaDFU.list()) == 0:
print("waiting for DFU...")
time.sleep(0.1)
if timeout is not None and (time.time() - t_start) > timeout:
return False
2017-08-28 11:42:23 -06:00
dfu = PandaDFU(PandaDFU.st_serial_to_dfu_serial(self._serial))
dfu.recover()
# reflash after recover
self.connect(True, True)
self.flash()
return True
2017-08-28 11:42:23 -06:00
2017-07-30 09:49:53 -06:00
@staticmethod
def flash_ota_st():
ret = os.system("cd %s && make clean && make ota" % (os.path.join(BASEDIR, "board")))
time.sleep(1)
return ret == 0
2017-07-30 09:49:53 -06:00
@staticmethod
def flash_ota_wifi(release=False):
release_str = "RELEASE=1" if release else ""
ret = os.system("cd {} && make clean && {} make ota".format(os.path.join(BASEDIR, "boardesp"), release_str))
2017-07-30 09:49:53 -06:00
time.sleep(1)
return ret == 0
2017-07-30 09:49:53 -06:00
2017-04-06 19:11:36 -06:00
@staticmethod
def list():
context = usb1.USBContext()
ret = []
2017-09-12 21:49:28 -06:00
try:
for device in context.getDeviceList(skip_on_error=True):
if device.getVendorID() == 0xbbaa and device.getProductID() in [0xddcc, 0xddee]:
try:
ret.append(device.getSerialNumber())
except Exception:
continue
except Exception:
2017-09-12 21:51:05 -06:00
pass
# TODO: detect if this is real
# ret += ["WIFI"]
2017-04-06 19:11:36 -06:00
return ret
2017-07-18 13:29:16 -06:00
def call_control_api(self, msg):
self._handle.controlWrite(Panda.REQUEST_OUT, msg, 0, 0, b'')
2017-04-06 19:11:36 -06:00
# ******************* health *******************
def health(self):
2021-05-03 06:59:36 -06:00
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd2, 0, 0, 43)
a = struct.unpack("<IIIIIIIIBBBBBBBHBB", dat)
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
return {
"uptime": a[0],
"voltage": a[1],
"current": a[2],
2019-12-19 19:16:31 -07:00
"can_rx_errs": a[3],
"can_send_errs": a[4],
"can_fwd_errs": a[5],
"gmlan_send_errs": a[6],
"faults": a[7],
"ignition_line": a[8],
"ignition_can": a[9],
"controls_allowed": a[10],
"gas_interceptor_detected": a[11],
"car_harness_status": a[12],
"usb_power_mode": a[13],
"safety_mode": a[14],
2021-05-03 06:59:36 -06:00
"safety_param": a[15],
"fault_status": a[16],
"power_save_enabled": a[17]
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
}
2017-04-06 19:11:36 -06:00
2017-04-28 17:56:40 -06:00
# ******************* control *******************
def enter_bootloader(self):
try:
self._handle.controlWrite(Panda.REQUEST_OUT, 0xd1, 0, 0, b'')
except Exception as e:
print(e)
2017-04-28 17:56:40 -06:00
def get_version(self):
2019-09-25 17:52:51 -06:00
return self._handle.controlRead(Panda.REQUEST_IN, 0xd6, 0, 0, 0x40).decode('utf8')
@staticmethod
def get_signature_from_firmware(fn):
f = open(fn, 'rb')
f.seek(-128, 2) # Seek from end of file
return f.read(128)
def get_signature(self):
part_1 = self._handle.controlRead(Panda.REQUEST_IN, 0xd3, 0, 0, 0x40)
part_2 = self._handle.controlRead(Panda.REQUEST_IN, 0xd4, 0, 0, 0x40)
return bytes(part_1 + part_2)
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
def get_type(self):
return self._handle.controlRead(Panda.REQUEST_IN, 0xc1, 0, 0, 0x40)
def is_white(self):
return self.get_type() == Panda.HW_TYPE_WHITE_PANDA
2018-02-16 12:07:20 -07:00
def is_grey(self):
return self.get_type() == Panda.HW_TYPE_GREY_PANDA
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
def is_black(self):
return self.get_type() == Panda.HW_TYPE_BLACK_PANDA
2018-02-16 12:07:20 -07:00
def is_uno(self):
return self.get_type() == Panda.HW_TYPE_UNO
2019-11-05 19:25:42 -07:00
def has_obd(self):
return (self.is_uno() or self.is_black())
2017-04-28 17:56:40 -06:00
def get_serial(self):
2017-07-17 19:59:16 -06:00
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 0, 0, 0x20)
hashsig, calc_hash = dat[0x1c:], hashlib.sha1(dat[0:0x1c]).digest()[0:4]
2017-07-17 20:43:30 -06:00
assert(hashsig == calc_hash)
return [dat[0:0x10].decode("utf8"), dat[0x10:0x10 + 10].decode("utf8")]
2017-04-28 17:56:40 -06:00
2017-04-28 18:49:55 -06:00
def get_secret(self):
2017-07-17 19:59:16 -06:00
return self._handle.controlRead(Panda.REQUEST_IN, 0xd0, 1, 0, 0x10)
2017-04-28 18:49:55 -06:00
2017-04-25 16:16:23 -06:00
# ******************* configuration *******************
2017-04-06 19:11:36 -06:00
2017-08-23 10:28:52 -06:00
def set_usb_power(self, on):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe6, int(on), 0, b'')
def set_power_save(self, power_save_enabled=0):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe7, int(power_save_enabled), 0, b'')
2017-07-17 16:29:31 -06:00
def set_esp_power(self, on):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xd9, int(on), 0, b'')
2017-08-22 15:17:59 -06:00
def esp_reset(self, bootmode=0):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xda, int(bootmode), 0, b'')
time.sleep(0.2)
def set_safety_mode(self, mode=SAFETY_SILENT):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, 0, b'')
def set_can_forwarding(self, from_bus, to_bus):
2017-07-17 16:29:31 -06:00
# TODO: This feature may not work correctly with saturated buses
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdd, from_bus, to_bus, b'')
2017-05-02 00:40:49 -06:00
2017-07-18 22:05:09 -06:00
def set_gmlan(self, bus=2):
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
# TODO: check panda type
2017-07-18 22:05:09 -06:00
if bus is None:
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdb, 0, 0, b'')
elif bus in [Panda.GMLAN_CAN2, Panda.GMLAN_CAN3]:
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdb, 1, bus, b'')
2017-04-06 19:11:36 -06:00
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
def set_obd(self, obd):
# TODO: check panda type
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdb, int(obd), 0, b'')
def set_can_loopback(self, enable):
2017-07-17 16:29:31 -06:00
# set can loopback mode for all buses
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe5, int(enable), 0, b'')
def set_can_enable(self, bus_num, enable):
2020-09-28 08:19:09 -06:00
# sets the can transceiver enable pin
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf4, int(bus_num), int(enable), b'')
2017-07-17 20:43:30 -06:00
def set_can_speed_kbps(self, bus, speed):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xde, bus, int(speed * 10), b'')
2017-07-17 20:43:30 -06:00
2017-04-25 16:16:23 -06:00
def set_uart_baud(self, uart, rate):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe4, uart, int(rate / 300), b'')
2017-04-25 16:16:23 -06:00
def set_uart_parity(self, uart, parity):
# parity, 0=off, 1=even, 2=odd
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe2, uart, parity, b'')
2017-04-25 16:16:23 -06:00
2017-04-25 21:23:05 -06:00
def set_uart_callback(self, uart, install):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe3, uart, int(install), b'')
2017-04-25 21:23:05 -06:00
2017-04-25 16:16:23 -06:00
# ******************* can *******************
# The panda will NAK CAN writes when there is CAN congestion.
# libusb will try to send it again, with a max timeout.
# Timeout is in ms. If set to 0, the timeout is infinite.
CAN_SEND_TIMEOUT_MS = 10
def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS):
2017-04-06 19:11:36 -06:00
snds = []
transmit = 1
extended = 4
2017-04-06 19:11:36 -06:00
for addr, _, dat, bus in arr:
2017-06-26 00:03:08 -06:00
assert len(dat) <= 8
2018-01-23 19:40:53 -07:00
if DEBUG:
print(f" W 0x{addr:x}: 0x{dat.hex()}")
2017-05-08 01:18:50 -06:00
if addr >= 0x800:
rir = (addr << 3) | transmit | extended
else:
rir = (addr << 21) | transmit
snd = struct.pack("II", rir, len(dat) | (bus << 4)) + dat
snd = snd.ljust(0x10, b'\x00')
2017-04-06 19:11:36 -06:00
snds.append(snd)
while True:
2017-04-06 19:11:36 -06:00
try:
2017-07-30 09:26:48 -06:00
if self.wifi:
for s in snds:
self._handle.bulkWrite(3, s)
else:
self._handle.bulkWrite(3, b''.join(snds), timeout=timeout)
2017-04-06 19:11:36 -06:00
break
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
print("CAN: BAD SEND MANY, RETRYING")
2017-04-06 19:11:36 -06:00
def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS):
self.can_send_many([[addr, None, dat, bus]], timeout=timeout)
2017-04-06 19:11:36 -06:00
def can_recv(self):
dat = bytearray()
while True:
2017-04-06 19:11:36 -06:00
try:
dat = self._handle.bulkRead(1, 0x10 * 256)
2017-04-06 19:11:36 -06:00
break
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
print("CAN: BAD RECV, RETRYING")
time.sleep(0.1)
return parse_can_buffer(dat)
2017-04-06 19:11:36 -06:00
def can_clear(self, bus):
"""Clears all messages from the specified internal CAN ringbuffer as
though it were drained.
Args:
bus (int): can bus number to clear a tx queue, or 0xFFFF to clear the
global can rx queue.
"""
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf1, bus, 0, b'')
2018-03-09 18:40:03 -07:00
# ******************* isotp *******************
2018-03-10 11:16:53 -07:00
def isotp_send(self, addr, dat, bus, recvaddr=None, subaddr=None):
2018-03-09 18:40:03 -07:00
return isotp_send(self, dat, addr, bus, recvaddr, subaddr)
2018-03-10 11:16:53 -07:00
def isotp_recv(self, addr, bus=0, sendaddr=None, subaddr=None):
2018-03-10 11:17:57 -07:00
return isotp_recv(self, addr, bus, sendaddr, subaddr)
2018-03-09 18:40:03 -07:00
2017-04-06 19:11:36 -06:00
# ******************* serial *******************
def serial_read(self, port_number):
2017-07-18 13:15:19 -06:00
ret = []
while 1:
lret = bytes(self._handle.controlRead(Panda.REQUEST_IN, 0xe0, port_number, 0, 0x40))
if len(lret) == 0:
break
ret.append(lret)
return b''.join(ret)
2017-04-06 19:11:36 -06:00
def serial_write(self, port_number, ln):
2018-01-19 12:04:21 -07:00
ret = 0
for i in range(0, len(ln), 0x20):
ret += self._handle.bulkWrite(2, struct.pack("B", port_number) + ln[i:i + 0x20])
2018-01-19 12:04:21 -07:00
return ret
2017-04-06 19:11:36 -06:00
def serial_clear(self, port_number):
"""Clears all messages (tx and rx) from the specified internal uart
ringbuffer as though it were drained.
Args:
port_number (int): port number of the uart to clear.
"""
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf2, port_number, 0, b'')
2017-04-06 19:11:36 -06:00
# ******************* kline *******************
# pulse low for wakeup
def kline_wakeup(self, k=True, l=True):
assert k or l, "must specify k-line, l-line, or both"
2018-02-28 18:03:04 -07:00
if DEBUG:
print("kline wakeup...")
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf0, 2 if k and l else int(l), 0, b'')
2018-02-28 18:03:04 -07:00
if DEBUG:
print("kline wakeup done")
2017-04-06 19:11:36 -06:00
def kline_5baud(self, addr, k=True, l=True):
assert k or l, "must specify k-line, l-line, or both"
if DEBUG:
print("kline 5 baud...")
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf4, 2 if k and l else int(l), addr, b'')
if DEBUG:
print("kline 5 baud done")
2017-04-06 19:11:36 -06:00
def kline_drain(self, bus=2):
# drain buffer
bret = bytearray()
while True:
2017-07-17 19:59:16 -06:00
ret = self._handle.controlRead(Panda.REQUEST_IN, 0xe0, bus, 0, 0x40)
2017-04-06 19:11:36 -06:00
if len(ret) == 0:
break
2018-02-28 18:45:54 -07:00
elif DEBUG:
print(f"kline drain: 0x{ret.hex()}")
bret += ret
2017-06-28 15:50:00 -06:00
return bytes(bret)
2017-04-06 19:11:36 -06:00
def kline_ll_recv(self, cnt, bus=2):
echo = bytearray()
2017-04-06 19:11:36 -06:00
while len(echo) != cnt:
ret = self._handle.controlRead(Panda.REQUEST_OUT, 0xe0, bus, 0, cnt - len(echo))
2018-02-28 18:03:04 -07:00
if DEBUG and len(ret) > 0:
print(f"kline recv: 0x{ret.hex()}")
2018-02-28 18:03:04 -07:00
echo += ret
2020-06-14 10:22:39 -06:00
return bytes(echo)
2017-04-06 19:11:36 -06:00
def kline_send(self, x, bus=2, checksum=True):
self.kline_drain(bus=bus)
if checksum:
x += bytes([sum(x) % 0x100])
2017-04-18 08:34:56 -06:00
for i in range(0, len(x), 0xf):
ts = x[i:i + 0xf]
2018-02-28 18:03:04 -07:00
if DEBUG:
print(f"kline send: 0x{ts.hex()}")
self._handle.bulkWrite(2, bytes([bus]) + ts)
2017-04-06 19:11:36 -06:00
echo = self.kline_ll_recv(len(ts), bus=bus)
if echo != ts:
print(f"**** ECHO ERROR {i} ****")
print(f"0x{echo.hex()}")
print(f"0x{ts.hex()}")
2017-04-06 19:11:36 -06:00
assert echo == ts
def kline_recv(self, bus=2, header_len=4):
# read header (last byte is length)
msg = self.kline_ll_recv(header_len, bus=bus)
# read data (add one byte to length for checksum)
msg += self.kline_ll_recv(msg[-1]+1, bus=bus)
2017-04-06 19:11:36 -06:00
return msg
Black (#254) * late usb * Added type support for black panda * Added harness presence and orientation detection * harness relay driving code * Added intercept support in black panda code. Switched around can0 and can2 * Disable ADCs after orientation detection. Ignition interrupts via harness * WIP: Hardware abstraction layer + black panda bringup * Fixed bootstub build * Fixed bootstub for pedal * Fixed infinite loops * Got CAN buses working on white again * Fixed pedal build and black can interfaces * Got CAN buses working on black panda * Finished loopback test for black panda * Erase all flash sectors on the panda. Increased binary limit. Added extra python functions. * Fixed python * Made new code MISRA compliant * Cleaned up ignition. Fixed build * Fixed health packet * Fixed CAN mode on black bug. Changed OBD to switch on ELM mode * Fixes from Github review * Fixed MISRA issue for pedal * Fixed failing gmlan tests * ELM327 safety: allow diagnostic on all buses * Cleaned up EON relay code * delete only 3 sectors instead of 11 to allow a new build to be flashed. Much faster to flash * Removed CAN only can0 output mode. Does not make sense on black panda due to reversibility issues. * Added heartbeat logic for EON code on panda. Go to NOOUTPUT if EON does not send a heartbeat for 5 seconds. * Remove all CAN buses live on EON startup. Shouldn't be necessary to have this separate case * Formatting * Added file I forgot to push * Added heartbeat to testing code to make sure EON tests don't fail. Should probably find a better way to do this though. Heartbeat thread didn't work, concurrent USB connection issues... * Safety: support black panda for Honda Bosch * Disable OBD2 if setting to NOOUTPUT mode * Run safety tests for all hw_types * Fail test if subtest fails * fix safety tests
2019-07-23 16:07:06 -06:00
def send_heartbeat(self):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf3, 0, 0, b'')
# ******************* RTC *******************
def set_datetime(self, dt):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xa1, int(dt.year), 0, b'')
self._handle.controlWrite(Panda.REQUEST_OUT, 0xa2, int(dt.month), 0, b'')
self._handle.controlWrite(Panda.REQUEST_OUT, 0xa3, int(dt.day), 0, b'')
self._handle.controlWrite(Panda.REQUEST_OUT, 0xa4, int(dt.isoweekday()), 0, b'')
self._handle.controlWrite(Panda.REQUEST_OUT, 0xa5, int(dt.hour), 0, b'')
self._handle.controlWrite(Panda.REQUEST_OUT, 0xa6, int(dt.minute), 0, b'')
self._handle.controlWrite(Panda.REQUEST_OUT, 0xa7, int(dt.second), 0, b'')
def get_datetime(self):
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xa0, 0, 0, 8)
a = struct.unpack("HBBBBBB", dat)
return datetime.datetime(a[0], a[1], a[2], a[4], a[5], a[6])
# ******************* IR *******************
def set_ir_power(self, percentage):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xb0, int(percentage), 0, b'')
# ******************* Fan ******************
def set_fan_power(self, percentage):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xb1, int(percentage), 0, b'')
def get_fan_rpm(self):
dat = self._handle.controlRead(Panda.REQUEST_IN, 0xb2, 0, 0, 2)
a = struct.unpack("H", dat)
return a[0]
# ****************** Phone *****************
def set_phone_power(self, enabled):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xb3, int(enabled), 0, b'')
# ************** Clock Source **************
def set_clock_source_mode(self, mode):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf5, int(mode), 0, b'')
# ****************** Siren *****************
def set_siren(self, enabled):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf6, int(enabled), 0, b'')
# ****************** Debug *****************
def set_green_led(self, enabled):
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf7, int(enabled), 0, b'')