wip
This commit is contained in:
0
panda/examples/__init__.py
Normal file
0
panda/examples/__init__.py
Normal file
25
panda/examples/can_bit_transition.md
Normal file
25
panda/examples/can_bit_transition.md
Normal file
@@ -0,0 +1,25 @@
|
||||
# How to use can_bit_transition.py to reverse engineer a single bit field
|
||||
|
||||
Let's say our goal is to find a brake pedal signal (caused by your foot pressing the brake pedal vs adaptive cruise control braking).
|
||||
|
||||
The following process will allow you to quickly find bits that are always 0 during a period of time (when you know you were not pressing the brake with your foot) and always 1 in a different period of time (when you know you were pressing the brake with your foot).
|
||||
|
||||
Open up a drive in cabana where you can find a place you used the brake pedal and another place where you did not use the brake pedal (and you can identify when you were on the brake pedal and when you were not). You may want to go out for a drive and put something in front of the camera after you put your foot on the brake and take it away before you take your foot off the brake so you can easily identify exactly when you had your foot on the brake based on the video in cabana. This is critical because this script needs the brake signal to always be high the entire time for one of the time frames. A 10 second time frame worked well for me.
|
||||
|
||||
I found a drive where I knew I was not pressing the brake between timestamp 50.0 thru 65.0 and I was pressing the brake between timestamp 69.0 thru 79.0. Determine what the timestamps are in cabana by plotting any message and putting your mouse over the plot at the location you want to discover the timestamp. The tool tip on mouse hover has the format: timestamp: value
|
||||
|
||||
Now download the log from cabana (Save Log button) and run the script passing in the timestamps
|
||||
(replace csv file name with cabana log you downloaded and time ranges with your own)
|
||||
```
|
||||
./can_bit_transition.py ./honda_crv_ex_2017_can-1520354796875.csv 50.0-65.0 69.0-79.0
|
||||
```
|
||||
|
||||
The script will output bits that were always low in the first time range and always high in the second time range (and vice versa)
|
||||
```
|
||||
id 17c 0 -> 1 at byte 4 bitmask 1
|
||||
id 17c 0 -> 1 at byte 6 bitmask 32
|
||||
id 221 1 -> 0 at byte 0 bitmask 4
|
||||
id 1be 0 -> 1 at byte 0 bitmask 16
|
||||
```
|
||||
|
||||
Now I go back to cabana and graph the above bits by searching for the message by id, double clicking on the appropriate bit, and then selecting "show plot". I already knew that message id 0x17c is both user brake and adaptive cruise control braking combined, so I plotted one of those signals along side 0x221 and 0x1be. By replaying a drive I could see that 0x221 was not a brake signal (when high at random times that did not correspond to braking). Next I looked at 0x1be and I found it was the brake pedal signal I was looking for (went high whenever I pressed the brake pedal and did not go high when adaptive cruise control was braking).
|
||||
111
panda/examples/can_bit_transition.py
Normal file
111
panda/examples/can_bit_transition.py
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/usr/bin/env python3
|
||||
import csv
|
||||
import sys
|
||||
|
||||
CSV_KEYS = {
|
||||
"logger": {
|
||||
"time": "Time",
|
||||
"message_id": "MessageID",
|
||||
"data": "Message",
|
||||
"bus": "Bus"
|
||||
},
|
||||
"cabana": {
|
||||
"time": "time",
|
||||
"message_id": "addr",
|
||||
"data": "data",
|
||||
"bus": "bus"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class Message():
|
||||
"""Details about a specific message ID."""
|
||||
|
||||
def __init__(self, message_id):
|
||||
self.message_id = message_id
|
||||
self.ones = [0] * 64 # bit set if 1 is always seen
|
||||
self.zeros = [0] * 64 # bit set if 0 is always seen
|
||||
|
||||
def printBitDiff(self, other):
|
||||
"""Prints bits that transition from always zero to always 1 and vice versa."""
|
||||
for i in range(len(self.ones)):
|
||||
zero_to_one = other.zeros[i] & self.ones[i]
|
||||
if zero_to_one:
|
||||
print('id %s 0 -> 1 at byte %d bitmask %d' % (self.message_id, i, zero_to_one))
|
||||
one_to_zero = other.ones[i] & self.zeros[i]
|
||||
if one_to_zero:
|
||||
print('id %s 1 -> 0 at byte %d bitmask %d' % (self.message_id, i, one_to_zero))
|
||||
|
||||
|
||||
class Info():
|
||||
"""A collection of Messages."""
|
||||
|
||||
def __init__(self):
|
||||
self.messages = {} # keyed by MessageID
|
||||
|
||||
def load(self, filename, start, end):
|
||||
"""Given a CSV file, adds information about message IDs and their values."""
|
||||
with open(filename, newline='') as inp:
|
||||
reader = csv.DictReader(inp)
|
||||
dtype = None
|
||||
for row in reader:
|
||||
if not len(row):
|
||||
continue
|
||||
if dtype is None:
|
||||
dtype = "logger" if "Bus" in row else "cabana"
|
||||
|
||||
time = float(row[CSV_KEYS[dtype]["time"]])
|
||||
bus = int(row[CSV_KEYS[dtype]["bus"]])
|
||||
if time < start or bus > 127:
|
||||
continue
|
||||
elif time > end:
|
||||
break
|
||||
|
||||
message_id = row[CSV_KEYS[dtype]["message_id"]]
|
||||
if message_id.startswith('0x'):
|
||||
message_id = message_id[2:] # remove leading '0x'
|
||||
else:
|
||||
message_id = hex(int(message_id))[2:] # old message IDs are in decimal
|
||||
message_id = f'{bus}:{message_id}'
|
||||
|
||||
data = row[CSV_KEYS[dtype]["data"]]
|
||||
if data.startswith('0x'):
|
||||
data = data[2:] # remove leading '0x'
|
||||
else:
|
||||
data = data
|
||||
new_message = False
|
||||
if message_id not in self.messages:
|
||||
self.messages[message_id] = Message(message_id)
|
||||
new_message = True
|
||||
message = self.messages[message_id]
|
||||
bts = bytearray.fromhex(data)
|
||||
for i in range(len(bts)):
|
||||
ones = int(bts[i])
|
||||
message.ones[i] = ones if new_message else message.ones[i] & ones
|
||||
# Inverts the data and masks it to a byte to get the zeros as ones.
|
||||
zeros = (~int(bts[i])) & 0xff
|
||||
message.zeros[i] = zeros if new_message else message.zeros[i] & zeros
|
||||
|
||||
def PrintUnique(log_file, low_range, high_range):
|
||||
# find messages with bits that are always low
|
||||
start, end = list(map(float, low_range.split('-')))
|
||||
low = Info()
|
||||
low.load(log_file, start, end)
|
||||
# find messages with bits that are always high
|
||||
start, end = list(map(float, high_range.split('-')))
|
||||
high = Info()
|
||||
high.load(log_file, start, end)
|
||||
# print messages that go from low to high
|
||||
found = False
|
||||
for message_id in sorted(high.messages, key=lambda x: int(x.split(':')[1], 16)):
|
||||
if message_id in low.messages:
|
||||
high.messages[message_id].printBitDiff(low.messages[message_id])
|
||||
found = True
|
||||
if not found:
|
||||
print('No messages that transition from always low to always high found!')
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 4:
|
||||
print('Usage:\n%s log.csv <low-start>-<low-end> <high-start>-<high-end>' % sys.argv[0])
|
||||
sys.exit(0)
|
||||
PrintUnique(sys.argv[1], sys.argv[2], sys.argv[3])
|
||||
43
panda/examples/can_logger.py
Normal file
43
panda/examples/can_logger.py
Normal file
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import csv
|
||||
import time
|
||||
from panda import Panda
|
||||
|
||||
def can_logger():
|
||||
p = Panda()
|
||||
|
||||
try:
|
||||
outputfile = open('output.csv', 'w')
|
||||
csvwriter = csv.writer(outputfile)
|
||||
# Write Header
|
||||
csvwriter.writerow(['Bus', 'MessageID', 'Message', 'MessageLength', 'Time'])
|
||||
print("Writing csv file output.csv. Press Ctrl-C to exit...\n")
|
||||
|
||||
bus0_msg_cnt = 0
|
||||
bus1_msg_cnt = 0
|
||||
bus2_msg_cnt = 0
|
||||
|
||||
start_time = time.time()
|
||||
while True:
|
||||
can_recv = p.can_recv()
|
||||
|
||||
for address, _, dat, src in can_recv:
|
||||
csvwriter.writerow(
|
||||
[str(src), str(hex(address)), f"0x{dat.hex()}", len(dat), str(time.time() - start_time)])
|
||||
|
||||
if src == 0:
|
||||
bus0_msg_cnt += 1
|
||||
elif src == 1:
|
||||
bus1_msg_cnt += 1
|
||||
elif src == 2:
|
||||
bus2_msg_cnt += 1
|
||||
|
||||
print(f"Message Counts... Bus 0: {bus0_msg_cnt} Bus 1: {bus1_msg_cnt} Bus 2: {bus2_msg_cnt}", end='\r')
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print(f"\nNow exiting. Final message Counts... Bus 0: {bus0_msg_cnt} Bus 1: {bus1_msg_cnt} Bus 2: {bus2_msg_cnt}")
|
||||
outputfile.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
can_logger()
|
||||
103
panda/examples/can_unique.md
Normal file
103
panda/examples/can_unique.md
Normal file
@@ -0,0 +1,103 @@
|
||||
# How to use can_unique.py to reverse engineer a single bit field
|
||||
|
||||
Let's say our goal is to find the CAN message indicating that the driver's door is either open or closed.
|
||||
The following process is great for simple single-bit messages.
|
||||
However for frequently changing values, such as RPM or speed, Cabana's graphical plots are probably better to use.
|
||||
|
||||
|
||||
First record a few minutes of background CAN messages with all the doors closed and save it in background.csv:
|
||||
```
|
||||
./can_logger.py
|
||||
mv output.csv background.csv
|
||||
```
|
||||
Then run can_logger.py for a few seconds while performing the action you're interested, such as opening and then closing the
|
||||
front-left door and save it as door-fl-1.csv
|
||||
Repeat the process and save it as door-f1-2.csv to have an easy way to confirm any suspicions.
|
||||
|
||||
Now we'll use can_unique.py to look for unique bits:
|
||||
```
|
||||
$ ./can_unique.py door-fl-1.csv background*
|
||||
id 820 new one at byte 2 bitmask 2
|
||||
id 520 new one at byte 3 bitmask 7
|
||||
id 520 new zero at byte 3 bitmask 8
|
||||
id 520 new one at byte 5 bitmask 6
|
||||
id 520 new zero at byte 5 bitmask 9
|
||||
id 559 new zero at byte 6 bitmask 4
|
||||
id 804 new one at byte 5 bitmask 2
|
||||
id 804 new zero at byte 5 bitmask 1
|
||||
|
||||
$ ./can_unique.py door-fl-2.csv background*
|
||||
id 672 new one at byte 3 bitmask 3
|
||||
id 820 new one at byte 2 bitmask 2
|
||||
id 520 new one at byte 3 bitmask 7
|
||||
id 520 new zero at byte 3 bitmask 8
|
||||
id 520 new one at byte 5 bitmask 6
|
||||
id 520 new zero at byte 5 bitmask 9
|
||||
id 559 new zero at byte 6 bitmask 4
|
||||
```
|
||||
|
||||
One of these bits hopefully indicates that the driver's door is open.
|
||||
Let's go through each message ID to figure out which one is correct.
|
||||
We expect any correct bits to have changed in both runs.
|
||||
We can rule out 804 because it only occurred in the first run.
|
||||
We can rule out 672 because it only occurred in the second run.
|
||||
That leaves us with these message IDs: 820, 520, 559. Let's take a closer look at each one.
|
||||
|
||||
```
|
||||
$ fgrep ,559, door-fl-1.csv |head
|
||||
0,559,00ff0000000024f0
|
||||
0,559,00ff000000004464
|
||||
0,559,00ff0000000054a9
|
||||
0,559,00ff0000000064e3
|
||||
0,559,00ff00000000742e
|
||||
0,559,00ff000000008451
|
||||
0,559,00ff00000000949c
|
||||
0,559,00ff00000000a4d6
|
||||
0,559,00ff00000000b41b
|
||||
0,559,00ff00000000c442
|
||||
```
|
||||
Message ID 559 looks like an incrementing value, so it's not what we're looking for.
|
||||
|
||||
```
|
||||
$ fgrep ,520, door-fl-2.csv
|
||||
0,520,26ff00f8a1890000
|
||||
0,520,26ff00f8a2890000
|
||||
0,520,26ff00f8a2890000
|
||||
0,520,26ff00f8a1890000
|
||||
0,520,26ff00f8a2890000
|
||||
0,520,26ff00f8a1890000
|
||||
0,520,26ff00f8a2890000
|
||||
0,520,26ff00f8a1890000
|
||||
0,520,26ff00f8a2890000
|
||||
0,520,26ff00f8a1890000
|
||||
0,520,26ff00f8a2890000
|
||||
0,520,26ff00f8a1890000
|
||||
```
|
||||
Message ID 520 oscillates between two values. However I only opened and closed the door once, so this is probably not it.
|
||||
|
||||
```
|
||||
$ fgrep ,820, door-fl-1.csv
|
||||
0,820,44000100a500c802
|
||||
0,820,44000100a500c803
|
||||
0,820,44000300a500c803
|
||||
0,820,44000300a500c802
|
||||
0,820,44000300a500c802
|
||||
0,820,44000300a500c802
|
||||
0,820,44000100a500c802
|
||||
0,820,44000100a500c802
|
||||
0,820,44000100a500c802
|
||||
```
|
||||
Message ID 820 looks promising! It starts off at 44000100a500c802 when the door is closed.
|
||||
When the door is open it goes to 44000300a500c802.
|
||||
Then when the door is closed again, it goes back to 44000100a500c802.
|
||||
Let's confirm by looking at the data from our other run:
|
||||
```
|
||||
$ fgrep ,820, door-fl-2.csv
|
||||
0,820,44000100a500c802
|
||||
0,820,44000300a500c802
|
||||
0,820,44000100a500c802
|
||||
```
|
||||
Perfect! We now know that message id 820 at byte 2 bitmask 2 is set if the driver's door is open.
|
||||
If we repeat the process with the front passenger's door,
|
||||
then we'll find that message id 820 at byte 2 bitmask 4 is set if the front-right door is open.
|
||||
This confirms our finding because it's common for similar signals to be near each other.
|
||||
116
panda/examples/can_unique.py
Normal file
116
panda/examples/can_unique.py
Normal file
@@ -0,0 +1,116 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Given an interesting CSV file of CAN messages and a list of background CAN
|
||||
# messages, print which bits in the interesting file have never appeared
|
||||
# in the background files.
|
||||
|
||||
# Expects the CSV file to be in one of the following formats:
|
||||
|
||||
# can_logger.py
|
||||
# Bus,MessageID,Message,MessageLength
|
||||
# 0,0x292,0x040000001068,6
|
||||
|
||||
# The old can_logger.py format is also supported:
|
||||
# Bus,MessageID,Message
|
||||
# 0,344,c000c00000000000
|
||||
|
||||
# Cabana "Save Log" format
|
||||
# time,addr,bus,data
|
||||
# 240.47911496100002,53,0,0acc0ade0074bf9e
|
||||
|
||||
|
||||
import csv
|
||||
import sys
|
||||
|
||||
class Message():
|
||||
"""Details about a specific message ID."""
|
||||
|
||||
def __init__(self, message_id):
|
||||
self.message_id = message_id
|
||||
self.data = {} # keyed by hex string encoded message data
|
||||
self.ones = [0] * 64 # bit set if 1 is seen
|
||||
self.zeros = [0] * 64 # bit set if 0 has been seen
|
||||
|
||||
def printBitDiff(self, other):
|
||||
"""Prints bits that are set or cleared compared to other background."""
|
||||
for i in range(len(self.ones)):
|
||||
new_ones = ((~other.ones[i]) & 0xff) & self.ones[i]
|
||||
if new_ones:
|
||||
print('id %s new one at byte %d bitmask %d' % (
|
||||
self.message_id, i, new_ones))
|
||||
new_zeros = ((~other.zeros[i]) & 0xff) & self.zeros[i]
|
||||
if new_zeros:
|
||||
print('id %s new zero at byte %d bitmask %d' % (
|
||||
self.message_id, i, new_zeros))
|
||||
|
||||
|
||||
class Info():
|
||||
"""A collection of Messages."""
|
||||
|
||||
def __init__(self):
|
||||
self.messages = {} # keyed by MessageID
|
||||
|
||||
def load(self, filename):
|
||||
"""Given a CSV file, adds information about message IDs and their values."""
|
||||
with open(filename) as inp:
|
||||
reader = csv.reader(inp)
|
||||
header = next(reader, None)
|
||||
if header[0] == 'time':
|
||||
self.cabana(reader)
|
||||
else:
|
||||
self.logger(reader)
|
||||
|
||||
def cabana(self, reader):
|
||||
for row in reader:
|
||||
bus = row[2]
|
||||
message_id = hex(int(row[1]))[2:]
|
||||
message_id = f'{bus}:{message_id}'
|
||||
data = row[3]
|
||||
self.store(message_id, data)
|
||||
|
||||
def logger(self, reader):
|
||||
for row in reader:
|
||||
bus = row[0]
|
||||
if row[1].startswith('0x'):
|
||||
message_id = row[1][2:] # remove leading '0x'
|
||||
else:
|
||||
message_id = hex(int(row[1]))[2:] # old message IDs are in decimal
|
||||
message_id = f'{bus}:{message_id}'
|
||||
if row[1].startswith('0x'):
|
||||
data = row[2][2:] # remove leading '0x'
|
||||
else:
|
||||
data = row[2]
|
||||
self.store(message_id, data)
|
||||
|
||||
def store(self, message_id, data):
|
||||
if message_id not in self.messages:
|
||||
self.messages[message_id] = Message(message_id)
|
||||
message = self.messages[message_id]
|
||||
if data not in self.messages[message_id].data:
|
||||
message.data[data] = True
|
||||
bts = bytearray.fromhex(data)
|
||||
for i in range(len(bts)):
|
||||
message.ones[i] = message.ones[i] | int(bts[i])
|
||||
# Inverts the data and masks it to a byte to get the zeros as ones.
|
||||
message.zeros[i] = message.zeros[i] | ((~int(bts[i])) & 0xff)
|
||||
|
||||
|
||||
def PrintUnique(interesting_file, background_files):
|
||||
background = Info()
|
||||
for background_file in background_files:
|
||||
background.load(background_file)
|
||||
interesting = Info()
|
||||
interesting.load(interesting_file)
|
||||
for message_id in sorted(interesting.messages):
|
||||
if message_id not in background.messages:
|
||||
print('New message_id: %s' % message_id)
|
||||
else:
|
||||
interesting.messages[message_id].printBitDiff(
|
||||
background.messages[message_id])
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) < 3:
|
||||
print('Usage:\n%s interesting.csv background*.csv' % sys.argv[0])
|
||||
sys.exit(0)
|
||||
PrintUnique(sys.argv[1], sys.argv[2:])
|
||||
@@ -1,6 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
from typing import List, Optional
|
||||
from tqdm import tqdm
|
||||
from panda import Panda
|
||||
from panda.python.uds import UdsClient, MessageTimeoutError, NegativeResponseError, InvalidSubAddressError, \
|
||||
@@ -25,7 +24,7 @@ if __name__ == "__main__":
|
||||
addrs += [0x18da0000 + (i << 8) + 0xf1 for i in range(256)]
|
||||
results = {}
|
||||
|
||||
sub_addrs: List[Optional[int]] = [None]
|
||||
sub_addrs: list[int | None] = [None]
|
||||
if args.sub_addr:
|
||||
if args.sub_addr == "scan":
|
||||
sub_addrs = list(range(0xff + 1))
|
||||
|
||||
57
panda/examples/query_vin_and_stats.py
Normal file
57
panda/examples/query_vin_and_stats.py
Normal file
@@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env python3
|
||||
import time
|
||||
import struct
|
||||
from panda import Panda
|
||||
from hexdump import hexdump
|
||||
from panda.python.isotp import isotp_send, isotp_recv
|
||||
|
||||
# 0x7e0 = Toyota
|
||||
# 0x18DB33F1 for Honda?
|
||||
|
||||
|
||||
def get_current_data_for_pid(pid):
|
||||
# 01 xx = Show current data
|
||||
isotp_send(panda, b"\x01" + bytes([pid]), 0x7e0)
|
||||
return isotp_recv(panda, 0x7e8)
|
||||
|
||||
def get_supported_pids():
|
||||
ret = []
|
||||
pid = 0
|
||||
while 1:
|
||||
supported = struct.unpack(">I", get_current_data_for_pid(pid)[2:])[0]
|
||||
for i in range(1 + pid, 0x21 + pid):
|
||||
if supported & 0x80000000:
|
||||
ret.append(i)
|
||||
supported <<= 1
|
||||
pid += 0x20
|
||||
if pid not in ret:
|
||||
break
|
||||
return ret
|
||||
|
||||
if __name__ == "__main__":
|
||||
panda = Panda()
|
||||
panda.set_safety_mode(Panda.SAFETY_ELM327)
|
||||
panda.can_clear(0)
|
||||
|
||||
# 09 02 = Get VIN
|
||||
isotp_send(panda, b"\x09\x02", 0x7df)
|
||||
ret = isotp_recv(panda, 0x7e8)
|
||||
hexdump(ret)
|
||||
print("VIN: %s" % "".join(map(chr, ret[:2])))
|
||||
|
||||
# 03 = get DTCS
|
||||
isotp_send(panda, b"\x03", 0x7e0)
|
||||
dtcs = isotp_recv(panda, 0x7e8)
|
||||
print("DTCs:", "".join(map(chr, dtcs[:2])))
|
||||
|
||||
supported_pids = get_supported_pids()
|
||||
print("Supported PIDs:", supported_pids)
|
||||
|
||||
while 1:
|
||||
speed = struct.unpack(">B", get_current_data_for_pid(13)[2:])[0] # kph
|
||||
rpm = struct.unpack(">H", get_current_data_for_pid(12)[2:])[0] / 4.0 # revs
|
||||
throttle = struct.unpack(">B", get_current_data_for_pid(17)[2:])[0] / 255.0 * 100 # percent
|
||||
temp = struct.unpack(">B", get_current_data_for_pid(5)[2:])[0] - 40 # degrees C
|
||||
load = struct.unpack(">B", get_current_data_for_pid(4)[2:])[0] / 255.0 * 100 # percent
|
||||
print("%d KPH, %d RPM, %.1f%% Throttle, %d deg C, %.1f%% load" % (speed, rpm, throttle, temp, load))
|
||||
time.sleep(0.2)
|
||||
50
panda/examples/tesla_tester.py
Normal file
50
panda/examples/tesla_tester.py
Normal file
@@ -0,0 +1,50 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import binascii
|
||||
from panda import Panda
|
||||
|
||||
def tesla_tester():
|
||||
p = Panda()
|
||||
|
||||
body_bus_speed = 125 # Tesla Body busses (B, BF) are 125kbps, rest are 500kbps
|
||||
body_bus_num = 1 # My TDC to OBD adapter has PT on bus0 BDY on bus1 and CH on bus2
|
||||
p.set_can_speed_kbps(body_bus_num, body_bus_speed)
|
||||
|
||||
# Now set the panda from its default of SAFETY_SILENT (read only) to SAFETY_ALLOUTPUT
|
||||
# Careful, as this will let us send any CAN messages we want (which could be very bad!)
|
||||
print("Setting Panda to output mode...")
|
||||
p.set_safety_mode(Panda.SAFETY_ALLOUTPUT)
|
||||
|
||||
# BDY 0x248 is the MCU_commands message, which includes folding mirrors, opening the trunk, frunk, setting the cars lock state and more.
|
||||
# For our test, we will edit the 3rd byte, which is MCU_lockRequest. 0x01 will lock, 0x02 will unlock:
|
||||
print("Unlocking Tesla...")
|
||||
p.can_send(0x248, b"\x00\x00\x02\x00\x00\x00\x00\x00", body_bus_num)
|
||||
|
||||
#Or, we can set the first byte, MCU_frontHoodCommand + MCU_liftgateSwitch, to 0x01 to pop the frunk, or 0x04 to open/close the trunk (0x05 should open both)
|
||||
print("Opening Frunk...")
|
||||
p.can_send(0x248, b"\x01\x00\x00\x00\x00\x00\x00\x00", body_bus_num)
|
||||
|
||||
#Back to safety...
|
||||
print("Disabling output on Panda...")
|
||||
p.set_safety_mode(Panda.SAFETY_SILENT)
|
||||
|
||||
print("Reading VIN from 0x568. This is painfully slow and can take up to 3 minutes (1 minute per message; 3 messages needed for full VIN)...")
|
||||
|
||||
vin = {}
|
||||
while True:
|
||||
#Read the VIN
|
||||
can_recv = p.can_recv()
|
||||
for address, _, dat, src in can_recv:
|
||||
if src == body_bus_num:
|
||||
if address == 1384: # 0x568 is VIN
|
||||
vin_index = int(binascii.hexlify(dat)[:2]) # first byte is the index, 00, 01, 02
|
||||
vin_string = binascii.hexlify(dat)[2:] # rest of the string is the actual VIN data
|
||||
vin[vin_index] = vin_string.decode("hex")
|
||||
print("Got VIN index " + str(vin_index) + " data " + vin[vin_index])
|
||||
#if we have all 3 parts of the VIN, print it and break out of our while loop
|
||||
if 0 in vin and 1 in vin and 2 in vin:
|
||||
print("VIN: " + vin[0] + vin[1] + vin[2][:3])
|
||||
break
|
||||
|
||||
if __name__ == "__main__":
|
||||
tesla_tester()
|
||||
Reference in New Issue
Block a user