mirror of
https://github.com/dragonpilot/dragonpilot.git
synced 2026-02-25 21:03:54 +08:00
openpilot v0.9.8 release
date: 2025-03-15T21:10:51 master commit: fb7b9c0f9420d228f03362970ebcfb7237095cf3
This commit is contained in:
committed by
Adeeb Shihadeh
parent
d64fb1838d
commit
dd778596b7
@@ -6,27 +6,24 @@ import usb1
|
||||
import struct
|
||||
import hashlib
|
||||
import binascii
|
||||
import logging
|
||||
from functools import wraps, partial
|
||||
from itertools import accumulate
|
||||
|
||||
from opendbc.car.structs import CarParams
|
||||
|
||||
from .base import BaseHandle
|
||||
from .constants import FW_PATH, McuType
|
||||
from .dfu import PandaDFU
|
||||
from .isotp import isotp_send, isotp_recv
|
||||
from .spi import PandaSpiHandle, PandaSpiException, PandaProtocolMismatch
|
||||
from .usb import PandaUsbHandle
|
||||
from .utils import logger
|
||||
|
||||
__version__ = '0.0.10'
|
||||
|
||||
# setup logging
|
||||
LOGLEVEL = os.environ.get('LOGLEVEL', 'INFO').upper()
|
||||
logging.basicConfig(level=LOGLEVEL, format='%(message)s')
|
||||
|
||||
CANPACKET_HEAD_SIZE = 0x6
|
||||
DLC_TO_LEN = [0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 16, 20, 24, 32, 48, 64]
|
||||
LEN_TO_DLC = {length: dlc for (dlc, length) in enumerate(DLC_TO_LEN)}
|
||||
PANDA_BUS_CNT = 4
|
||||
PANDA_BUS_CNT = 3
|
||||
|
||||
|
||||
def calculate_checksum(data):
|
||||
@@ -35,17 +32,17 @@ def calculate_checksum(data):
|
||||
res ^= b
|
||||
return res
|
||||
|
||||
def pack_can_buffer(arr):
|
||||
def pack_can_buffer(arr, fd=False):
|
||||
snds = [b'']
|
||||
for address, _, dat, bus in arr:
|
||||
for address, dat, bus in arr:
|
||||
assert len(dat) in LEN_TO_DLC
|
||||
#logging.debug(" W 0x%x: 0x%s", address, dat.hex())
|
||||
#logger.debug(" W 0x%x: 0x%s", address, dat.hex())
|
||||
|
||||
extended = 1 if address >= 0x800 else 0
|
||||
data_len_code = LEN_TO_DLC[len(dat)]
|
||||
header = bytearray(CANPACKET_HEAD_SIZE)
|
||||
word_4b = address << 3 | extended << 2
|
||||
header[0] = (data_len_code << 4) | (bus << 1)
|
||||
header[0] = (data_len_code << 4) | (bus << 1) | int(fd)
|
||||
header[1] = word_4b & 0xFF
|
||||
header[2] = (word_4b >> 8) & 0xFF
|
||||
header[3] = (word_4b >> 16) & 0xFF
|
||||
@@ -85,7 +82,7 @@ def unpack_can_buffer(dat):
|
||||
data = dat[CANPACKET_HEAD_SIZE:(CANPACKET_HEAD_SIZE+data_len)]
|
||||
dat = dat[(CANPACKET_HEAD_SIZE+data_len):]
|
||||
|
||||
ret.append((address, 0, data, bus))
|
||||
ret.append((address, data, bus))
|
||||
|
||||
return (ret, dat)
|
||||
|
||||
@@ -105,49 +102,15 @@ ensure_health_packet_version = partial(ensure_version, "health", "HEALTH_PACKET_
|
||||
|
||||
|
||||
|
||||
class ALTERNATIVE_EXPERIENCE:
|
||||
DEFAULT = 0
|
||||
DISABLE_DISENGAGE_ON_GAS = 1
|
||||
DISABLE_STOCK_AEB = 2
|
||||
RAISE_LONGITUDINAL_LIMITS_TO_ISO_MAX = 8
|
||||
ALLOW_AEB = 16
|
||||
|
||||
class Panda:
|
||||
|
||||
# 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 = 20
|
||||
SAFETY_VOLKSWAGEN_PQ = 21
|
||||
SAFETY_SUBARU_PREGLOBAL = 22
|
||||
SAFETY_HYUNDAI_LEGACY = 23
|
||||
SAFETY_HYUNDAI_COMMUNITY = 24
|
||||
SAFETY_STELLANTIS = 25
|
||||
SAFETY_FAW = 26
|
||||
SAFETY_BODY = 27
|
||||
SAFETY_HYUNDAI_CANFD = 28
|
||||
|
||||
SERIAL_DEBUG = 0
|
||||
SERIAL_ESP = 1
|
||||
SERIAL_LIN1 = 2
|
||||
SERIAL_LIN2 = 3
|
||||
SERIAL_SOM_DEBUG = 4
|
||||
|
||||
USB_VIDS = (0xbbaa, 0x3801) # 0x3801 is comma's registered VID
|
||||
USB_PIDS = (0xddee, 0xddcc)
|
||||
REQUEST_IN = usb1.ENDPOINT_IN | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
|
||||
REQUEST_OUT = usb1.ENDPOINT_OUT | usb1.TYPE_VENDOR | usb1.RECIPIENT_DEVICE
|
||||
@@ -187,49 +150,7 @@ class Panda:
|
||||
HARNESS_STATUS_NORMAL = 1
|
||||
HARNESS_STATUS_FLIPPED = 2
|
||||
|
||||
# first byte is for EPS scaling factor
|
||||
FLAG_TOYOTA_ALT_BRAKE = (1 << 8)
|
||||
FLAG_TOYOTA_STOCK_LONGITUDINAL = (2 << 8)
|
||||
FLAG_TOYOTA_LTA = (4 << 8)
|
||||
|
||||
FLAG_HONDA_ALT_BRAKE = 1
|
||||
FLAG_HONDA_BOSCH_LONG = 2
|
||||
FLAG_HONDA_NIDEC_ALT = 4
|
||||
FLAG_HONDA_RADARLESS = 8
|
||||
|
||||
FLAG_HYUNDAI_EV_GAS = 1
|
||||
FLAG_HYUNDAI_HYBRID_GAS = 2
|
||||
FLAG_HYUNDAI_LONG = 4
|
||||
FLAG_HYUNDAI_CAMERA_SCC = 8
|
||||
FLAG_HYUNDAI_CANFD_HDA2 = 16
|
||||
FLAG_HYUNDAI_CANFD_ALT_BUTTONS = 32
|
||||
FLAG_HYUNDAI_ALT_LIMITS = 64
|
||||
FLAG_HYUNDAI_CANFD_HDA2_ALT_STEERING = 128
|
||||
|
||||
FLAG_TESLA_POWERTRAIN = 1
|
||||
FLAG_TESLA_LONG_CONTROL = 2
|
||||
FLAG_TESLA_RAVEN = 4
|
||||
|
||||
FLAG_VOLKSWAGEN_LONG_CONTROL = 1
|
||||
|
||||
FLAG_CHRYSLER_RAM_DT = 1
|
||||
FLAG_CHRYSLER_RAM_HD = 2
|
||||
|
||||
FLAG_SUBARU_GEN2 = 1
|
||||
FLAG_SUBARU_LONG = 2
|
||||
|
||||
FLAG_SUBARU_PREGLOBAL_REVERSED_DRIVER_TORQUE = 1
|
||||
|
||||
FLAG_NISSAN_ALT_EPS_BUS = 1
|
||||
|
||||
FLAG_GM_HW_CAM = 1
|
||||
FLAG_GM_HW_CAM_LONG = 2
|
||||
|
||||
FLAG_FORD_LONG_CONTROL = 1
|
||||
FLAG_FORD_CANFD = 2
|
||||
|
||||
def __init__(self, serial: str | None = None, claim: bool = True, disable_checks: bool = True, can_speed_kbps: int = 500):
|
||||
self._connect_serial = serial
|
||||
def __init__(self, serial: str | None = None, claim: bool = True, disable_checks: bool = True, can_speed_kbps: int = 500, cli: bool = True):
|
||||
self._disable_checks = disable_checks
|
||||
|
||||
self._handle: BaseHandle
|
||||
@@ -237,9 +158,37 @@ class Panda:
|
||||
self.can_rx_overflow_buffer = b''
|
||||
self._can_speed_kbps = can_speed_kbps
|
||||
|
||||
if cli and serial is None:
|
||||
self._connect_serial = self._cli_select_panda()
|
||||
else:
|
||||
self._connect_serial = serial
|
||||
|
||||
# connect and set mcu type
|
||||
self.connect(claim)
|
||||
|
||||
def _cli_select_panda(self):
|
||||
dfu_pandas = PandaDFU.list()
|
||||
if len(dfu_pandas) > 0:
|
||||
print("INFO: some attached pandas are in DFU mode.")
|
||||
|
||||
pandas = self.list()
|
||||
if len(pandas) == 0:
|
||||
print("INFO: panda not available")
|
||||
return None
|
||||
if len(pandas) == 1:
|
||||
print(f"INFO: connecting to panda {pandas[0]}")
|
||||
return pandas[0]
|
||||
while True:
|
||||
print("Multiple pandas available:")
|
||||
pandas.sort()
|
||||
for idx, serial in enumerate(pandas):
|
||||
print(f"{[idx]}: {serial}")
|
||||
try:
|
||||
choice = int(input("Choose serial [0]:") or "0")
|
||||
return pandas[choice]
|
||||
except (ValueError, IndexError):
|
||||
print("Enter a valid index.")
|
||||
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
@@ -259,7 +208,7 @@ class Panda:
|
||||
self._handle = None
|
||||
while self._handle is None:
|
||||
# try USB first, then SPI
|
||||
self._context, self._handle, serial, self.bootstub, bcd = self.usb_connect(self._connect_serial, claim=claim)
|
||||
self._context, self._handle, serial, self.bootstub, bcd = self.usb_connect(self._connect_serial, claim=claim, no_error=wait)
|
||||
if self._handle is None:
|
||||
self._context, self._handle, serial, self.bootstub, bcd = self.spi_connect(self._connect_serial)
|
||||
if not wait:
|
||||
@@ -290,7 +239,7 @@ class Panda:
|
||||
self._handle_open = True
|
||||
self._mcu_type = self.get_mcu_type()
|
||||
self.health_version, self.can_version, self.can_health_version = self.get_packets_versions()
|
||||
logging.debug("connected")
|
||||
logger.debug("connected")
|
||||
|
||||
# disable openpilot's heartbeat checks
|
||||
if self._disable_checks:
|
||||
@@ -300,6 +249,10 @@ class Panda:
|
||||
# reset comms
|
||||
self.can_reset_communications()
|
||||
|
||||
# disable automatic CAN-FD switching
|
||||
for bus in range(PANDA_BUS_CNT):
|
||||
self.set_canfd_auto(bus, False)
|
||||
|
||||
# set CAN speed
|
||||
for bus in range(PANDA_BUS_CNT):
|
||||
self.set_can_speed_kbps(bus, self._can_speed_kbps)
|
||||
@@ -351,21 +304,23 @@ class Panda:
|
||||
return None, handle, spi_serial, bootstub, None
|
||||
|
||||
@classmethod
|
||||
def usb_connect(cls, serial, claim=True):
|
||||
def usb_connect(cls, serial, claim=True, no_error=False):
|
||||
handle, usb_serial, bootstub, bcd = None, None, None, None
|
||||
context = usb1.USBContext()
|
||||
context.open()
|
||||
try:
|
||||
for device in context.getDeviceList(skip_on_error=True):
|
||||
if device.getVendorID() == 0xbbaa and device.getProductID() in cls.USB_PIDS:
|
||||
if device.getVendorID() in cls.USB_VIDS and device.getProductID() in cls.USB_PIDS:
|
||||
try:
|
||||
this_serial = device.getSerialNumber()
|
||||
except Exception:
|
||||
logging.exception("failed to get serial number of panda")
|
||||
# Allow to ignore errors on reconnect. USB hubs need some time to initialize after panda reset
|
||||
if not no_error:
|
||||
logger.exception("failed to get serial number of panda")
|
||||
continue
|
||||
|
||||
if serial is None or this_serial == serial:
|
||||
logging.debug("opening device %s %s", this_serial, hex(device.getProductID()))
|
||||
logger.debug("opening device %s %s", this_serial, hex(device.getProductID()))
|
||||
|
||||
usb_serial = this_serial
|
||||
bootstub = (device.getProductID() & 0xF0) == 0xe0
|
||||
@@ -383,7 +338,7 @@ class Panda:
|
||||
|
||||
break
|
||||
except Exception:
|
||||
logging.exception("USB connect error")
|
||||
logger.exception("USB connect error")
|
||||
|
||||
usb_handle = None
|
||||
if handle is not None:
|
||||
@@ -393,6 +348,12 @@ class Panda:
|
||||
|
||||
return context, usb_handle, usb_serial, bootstub, bcd
|
||||
|
||||
def is_connected_spi(self):
|
||||
return isinstance(self._handle, PandaSpiHandle)
|
||||
|
||||
def is_connected_usb(self):
|
||||
return isinstance(self._handle, PandaUsbHandle)
|
||||
|
||||
@classmethod
|
||||
def list(cls):
|
||||
ret = cls.usb_list()
|
||||
@@ -405,17 +366,17 @@ class Panda:
|
||||
try:
|
||||
with usb1.USBContext() as context:
|
||||
for device in context.getDeviceList(skip_on_error=True):
|
||||
if device.getVendorID() == 0xbbaa and device.getProductID() in cls.USB_PIDS:
|
||||
if device.getVendorID() in cls.USB_VIDS and device.getProductID() in cls.USB_PIDS:
|
||||
try:
|
||||
serial = device.getSerialNumber()
|
||||
if len(serial) == 24:
|
||||
ret.append(serial)
|
||||
else:
|
||||
logging.warning(f"found device with panda descriptors but invalid serial: {serial}", RuntimeWarning)
|
||||
logger.warning(f"found device with panda descriptors but invalid serial: {serial}", RuntimeWarning)
|
||||
except Exception:
|
||||
logging.exception("error connecting to panda")
|
||||
logger.exception("error connecting to panda")
|
||||
except Exception:
|
||||
logging.exception("exception while listing pandas")
|
||||
logger.exception("exception while listing pandas")
|
||||
return ret
|
||||
|
||||
@classmethod
|
||||
@@ -455,7 +416,7 @@ class Panda:
|
||||
# wait up to 15 seconds
|
||||
for _ in range(15*10):
|
||||
try:
|
||||
self.connect()
|
||||
self.connect(claim=False, wait=True)
|
||||
success = True
|
||||
break
|
||||
except Exception:
|
||||
@@ -483,22 +444,22 @@ class Panda:
|
||||
assert last_sector < 7, "Binary too large! Risk of overwriting provisioning chunk."
|
||||
|
||||
# unlock flash
|
||||
logging.warning("flash: unlocking")
|
||||
logger.info("flash: unlocking")
|
||||
handle.controlWrite(Panda.REQUEST_IN, 0xb1, 0, 0, b'')
|
||||
|
||||
# erase sectors
|
||||
logging.warning(f"flash: erasing sectors 1 - {last_sector}")
|
||||
logger.info(f"flash: erasing sectors 1 - {last_sector}")
|
||||
for i in range(1, last_sector + 1):
|
||||
handle.controlWrite(Panda.REQUEST_IN, 0xb2, i, 0, b'')
|
||||
|
||||
# flash over EP2
|
||||
STEP = 0x10
|
||||
logging.warning("flash: flashing")
|
||||
logger.info("flash: flashing")
|
||||
for i in range(0, len(code), STEP):
|
||||
handle.bulkWrite(2, code[i:i + STEP])
|
||||
|
||||
# reset
|
||||
logging.warning("flash: resetting")
|
||||
logger.info("flash: resetting")
|
||||
try:
|
||||
handle.controlWrite(Panda.REQUEST_IN, 0xd8, 0, 0, b'', expect_disconnect=True)
|
||||
except Exception:
|
||||
@@ -506,13 +467,13 @@ class Panda:
|
||||
|
||||
def flash(self, fn=None, code=None, reconnect=True):
|
||||
if self.up_to_date(fn=fn):
|
||||
logging.debug("flash: already up to date")
|
||||
logger.info("flash: already up to date")
|
||||
return
|
||||
|
||||
if not fn:
|
||||
fn = os.path.join(FW_PATH, self._mcu_type.config.app_fn)
|
||||
assert os.path.isfile(fn)
|
||||
logging.debug("flash: main version is %s", self.get_version())
|
||||
logger.debug("flash: main version is %s", self.get_version())
|
||||
if not self.bootstub:
|
||||
self.reset(enter_bootstub=True)
|
||||
assert(self.bootstub)
|
||||
@@ -522,7 +483,7 @@ class Panda:
|
||||
code = f.read()
|
||||
|
||||
# get version
|
||||
logging.debug("flash: bootstub version is %s", self.get_version())
|
||||
logger.debug("flash: bootstub version is %s", self.get_version())
|
||||
|
||||
# do flash
|
||||
Panda.flash_static(self._handle, code, mcu_type=self._mcu_type)
|
||||
@@ -554,7 +515,7 @@ class Panda:
|
||||
t_start = time.monotonic()
|
||||
dfu_list = PandaDFU.list()
|
||||
while (dfu_serial is None and len(dfu_list) == 0) or (dfu_serial is not None and dfu_serial not in dfu_list):
|
||||
logging.debug("waiting for DFU...")
|
||||
logger.debug("waiting for DFU...")
|
||||
time.sleep(0.1)
|
||||
if timeout is not None and (time.monotonic() - t_start) > timeout:
|
||||
return False
|
||||
@@ -566,7 +527,7 @@ class Panda:
|
||||
t_start = time.monotonic()
|
||||
serials = Panda.list()
|
||||
while (serial is None and len(serials) == 0) or (serial is not None and serial not in serials):
|
||||
logging.debug("waiting for panda...")
|
||||
logger.debug("waiting for panda...")
|
||||
time.sleep(0.1)
|
||||
if timeout is not None and (time.monotonic() - t_start) > timeout:
|
||||
return False
|
||||
@@ -749,10 +710,13 @@ class Panda:
|
||||
|
||||
# ******************* configuration *******************
|
||||
|
||||
def set_alternative_experience(self, alternative_experience):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdf, int(alternative_experience), 0, b'')
|
||||
|
||||
def set_power_save(self, power_save_enabled=0):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe7, int(power_save_enabled), 0, b'')
|
||||
|
||||
def set_safety_mode(self, mode=SAFETY_SILENT, param=0):
|
||||
def set_safety_mode(self, mode=CarParams.SafetyModel.silent, param=0):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xdc, mode, param, b'')
|
||||
|
||||
def set_obd(self, obd):
|
||||
@@ -775,6 +739,9 @@ class Panda:
|
||||
def set_canfd_non_iso(self, bus, non_iso):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xfc, bus, int(non_iso), b'')
|
||||
|
||||
def set_canfd_auto(self, bus, auto):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe8, bus, int(auto), b'')
|
||||
|
||||
def set_uart_baud(self, uart, rate):
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xe4, uart, int(rate / 300), b'')
|
||||
|
||||
@@ -796,23 +763,15 @@ class Panda:
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xc0, 0, 0, b'')
|
||||
|
||||
@ensure_can_packet_version
|
||||
def can_send_many(self, arr, timeout=CAN_SEND_TIMEOUT_MS):
|
||||
snds = pack_can_buffer(arr)
|
||||
while True:
|
||||
try:
|
||||
for tx in snds:
|
||||
while True:
|
||||
bs = self._handle.bulkWrite(3, tx, timeout=timeout)
|
||||
tx = tx[bs:]
|
||||
if len(tx) == 0:
|
||||
break
|
||||
logging.error("CAN: PARTIAL SEND MANY, RETRYING")
|
||||
break
|
||||
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
|
||||
logging.error("CAN: BAD SEND MANY, RETRYING")
|
||||
def can_send_many(self, arr, *, fd=False, timeout=CAN_SEND_TIMEOUT_MS):
|
||||
snds = pack_can_buffer(arr, fd=fd)
|
||||
for tx in snds:
|
||||
while len(tx) > 0:
|
||||
bs = self._handle.bulkWrite(3, tx, timeout=timeout)
|
||||
tx = tx[bs:]
|
||||
|
||||
def can_send(self, addr, dat, bus, timeout=CAN_SEND_TIMEOUT_MS):
|
||||
self.can_send_many([[addr, None, dat, bus]], timeout=timeout)
|
||||
def can_send(self, addr, dat, bus, *, fd=False, timeout=CAN_SEND_TIMEOUT_MS):
|
||||
self.can_send_many([[addr, dat, bus]], fd=fd, timeout=timeout)
|
||||
|
||||
@ensure_can_packet_version
|
||||
def can_recv(self):
|
||||
@@ -822,7 +781,7 @@ class Panda:
|
||||
dat = self._handle.bulkRead(1, 16384) # Max receive batch size + 2 extra reserve frames
|
||||
break
|
||||
except (usb1.USBErrorIO, usb1.USBErrorOverflow):
|
||||
logging.error("CAN: BAD RECV, RETRYING")
|
||||
logger.error("CAN: BAD RECV, RETRYING")
|
||||
time.sleep(0.1)
|
||||
msgs, self.can_rx_overflow_buffer = unpack_can_buffer(self.can_rx_overflow_buffer + dat)
|
||||
return msgs
|
||||
@@ -838,14 +797,6 @@ class Panda:
|
||||
"""
|
||||
self._handle.controlWrite(Panda.REQUEST_OUT, 0xf1, bus, 0, b'')
|
||||
|
||||
# ******************* isotp *******************
|
||||
|
||||
def isotp_send(self, addr, dat, bus, recvaddr=None, subaddr=None):
|
||||
return isotp_send(self, dat, addr, bus, recvaddr, subaddr)
|
||||
|
||||
def isotp_recv(self, addr, bus=0, sendaddr=None, subaddr=None):
|
||||
return isotp_recv(self, addr, bus, sendaddr, subaddr)
|
||||
|
||||
# ******************* serial *******************
|
||||
|
||||
def serial_read(self, port_number):
|
||||
|
||||
Reference in New Issue
Block a user