-
Notifications
You must be signed in to change notification settings - Fork 0
/
CAN.py
274 lines (211 loc) · 9 KB
/
CAN.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
# 2/23/2020 Changelog
# Added getMilesRange(), temporary calculation, need to implement later
# 2/6/2020 Changelog
# Turned Blinkers class into Lights_Control class
# 1/20/2020 Changelog
# Added conversion of BMS signals to actual values
# helper function s16 to convert 16 bit hex into signed decimal
# 12/6/2019 Changelog
# Added class for Blinkers
# 11/29/2019 Changelog
# Added class for MCU and speed
# 11/28/2019 Changelog
# Added decoding of message for BMS statuses
import can
import can.interfaces
import subprocess
import traceback
from can.interface import Bus
from can import Message
from subprocess import call
class CAN_Control():
def __init__(self):
self.bus = None
self.bufferedReader = None
self.notifier = None
self.BMS = self.BMS_Control()
self.MCU = self.MCU_Control()
self.Lights = self.Lights_Control()
self.initCAN()
def initCAN(self):
"""Initialize the profile for the CAN controller."""
try:
# send out command to set up can0 from the shell to interface CAN controller hardware
call('sudo ip link set can0 up type can bitrate 500000', shell=True)
# initialize Bus object
self.bus = Bus(channel='can0', bustype='socketcan_native')
# initialize message buffer to store incoming CAN messages
self.bufferedReader = can.BufferedReader()
# notifier will notify when new message arrives on the bus and places it in the buffer
self.notifier = can.Notifier(self.bus, [self.bufferedReader])
except:
print(traceback.print_exc())
def readMessage(self, timeout=0.1):
"""Grabs a message from the buffered reader and returns the arbitration ID and data fields"""
try:
arbitrationID = None
data = None
msg = self.bufferedReader.get_message(timeout=timeout)
if msg is not None:
# arbitration id is the priority number of the CAN message. the lower the id, the higher the priority
arbitrationID = msg.arbitration_id
# data is a list of bytes. data[0] is byte 1 ..... all the way to data[7] is byte 8
data = msg.data
return arbitrationID, data
except:
print(traceback.print_exc())
def sendMessage(self, msg, timeout=0.1):
"""Sends a CAN message on the bus."""
try:
self.bus.send(msg, timeout=timeout)
except:
print(traceback.print_exc())
class BMS_Control():
def __init__(self):
# BMS CAN message1
self.msg1ID = 0x001
self.failsafeStat = 0
self.voltage = 0 # instantaneous voltage of battery pack
self.current = 0 # instantaneous current battery pack
self.highestTemperature = 0 # current highest temperature of pack
self.highestTemperatureThermistorID = 0 # id of thermistor with highest temperature
# BMS CAN message2
self.msg2ID = 0x002
self.avgPackCurrent = 0 # average temperature of pack
self.avgBatteryTemperature = 0 # average temperature of pack
self.stateOfCharge = 0 # state of charge
self.fanSpeed = 0 # speed of battery fan
# others
self.milesRange = 0
def getVoltage(self):
return self.voltage
def getCurrent(self):
return self.current
def getHighestTemp(self):
return self.highestTemperature
def getHighetTempThermistorID(self):
return self.highestTemperatureThermistorID
def getSOC(self):
return self.stateOfCharge
def getAvgBatteryTemp(self):
return self.avgBatteryTemperature
def getAvgPackCurrent(self):
return self.avgPackCurrent
def getFanSpeed(self):
return self.fanSpeed
def getMilesRange(self):
self.milesRange = self.stateOfCharge
return self.milesRange
def decodeMessage1(self, data):
"""Decode CAN message from BMS with ID# 0x001. Message includes Failsafe Statuses, Inst. Pack Voltage,
Inst. Pack Current, Highest Temperature, Thermistor ID with Highest Temperature"""
try:
# failsafe statuses
self.failsafeStat = (data[1] | data[0])
self.voltageFailsafeActive = self.failsafeStat & 0x1
self.currentFailsafeActive = (self.failsafeStat >> 1) & 0x1
self.relayFailsafeActive = (self.failsafeStat >> 2) & 0x1
self.cellBalancingActive = (self.failsafeStat >> 3) & 0x1
self.chargeInterlockFailsafeActive = (self.failsafeStat >> 4) & 0x1
self.thermistorBValueTableInvalid = (self.failsafeStat >> 5) & 0x1
self.inputPowerSupplyFailsafeActive = (self.failsafeStat >> 6) & 0x1
# pack instantaneous voltage
self.voltage = float(s16(data[2] << 8 | data[3])/10)
# pack instantaneous current
self.current = float(s16(data[4] << 8 | data[5])/10)
# highest temperature
self.highestTemperature = data[6]
# id of thermistor with highest temperature
self.highestTemperatureThermistorID = data[7]
except:
print(traceback.format_exc())
def decodeMessage2(self, data):
"""Decode CAN message from BMS with ID# 0x002. Message includes State of Charge, Average Temperature,
Average Pack Current. """
try:
# state of charge
self.stateOfCharge = float(data[0]/2)
# average battery temperature
self.avgBatteryTemperature = data[1]
# average pack current
self.avgPackCurrent = float(s16(data[2] << 8 | data[3])/10)
# fan speed
self.fanSpeed = data[4]
except:
print(traceback.format_exc())
class MCU_Control():
def __init__(self):
self.msgID = 0x003
self.speed = 0 # speed in mph
self.gearPosition = 0 # 0 = Park, 1 = Reverse, 2 = Neutral, 3 = Drive
self.cruiseControl = 0 # 0 = Cruise Control On, 1 = Cruise Control Off
self.brake = 0 # 0 = Brake is not pressed, 1 = Brake is pressed
def getSpeed(self):
return self.speed
def getGearPosition(self):
if self.gearPosition == 0:
return 'P'
elif self.gearPosition == 1:
return 'R'
elif self.gearPosition == 2:
return 'N'
elif self.gearPosition == 3:
return 'D'
def getCruiseControl(self):
return self.cruiseControl
def getBrake(self):
return self.brake
def decodeMessage(self, data):
"""Decode CAN message from MCU. Message includes Speed, gearPosition, cruiseControl"""
try:
# speed
self.speed = data[0]
# gear position
self.gearPosition = data[1] & 11
# cruise control status
self.cruiseControl = (data[1] >> 2) & 1
# brake status
self.brake = (data[1] >> 3) & 1
except:
print(traceback.format_exc())
class Lights_Control():
def __init__(self):
self.msgID = 0x004
self.rightTurn = 0 # 0 = off, 1 = right turn on
self.leftTurn = 0 # 0 = off, 1 = left turn on
self.hazards = 0 # 0 = off, 1 = hazards on
self.headlights = 0 # 0 = off, 1 = lowbeams
self.warning = 0 # 0 = off, 1 = warning on
def getRightTurnIndicator(self):
return self.rightTurn
def getLeftTurnIndicator(self):
return self.leftTurn
def getHazards(self):
return self.hazards
def getHeadlights(self):
return self.headlights
def getWarning(self):
return self.warning
def decodeMessage(self, data):
"""Decode CAN message from blinker. Message includes status of hazards, right, and left blinker"""
try:
# hazard
self.hazards = data[0] & 0x1
# right turn indicator
self.rightTurn = (data[0] >> 1) & 0x1
# left turn indicator
self.leftTurn = (data[0] >> 2) & 0x1
# headlights
self.headlights = (data[0] >> 3) & 0x1
# warning
self.warning = (data[0] >> 4) & 0x1
except:
print(traceback.format_exc())
# helper functions
def s16(value):
""" Converts a 16 bit hex to a signed decimal. """
return -(value & 0x8000) | (value & 0x7fff)
##if __name__ == '__main__':
## CAN = CAN_Control()
## while True:
## CAN.readMessage()