GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 8.7% 31 / 0 / 357
Functions: 15.8% 3 / 0 / 19
Branches: 8.4% 22 / 0 / 261
Decisions: 4.9% 3 / - / 61

firmware/controllers/can/can_dash.cpp
Line Branch Decision Exec Source
1 /**
2 * @file can_dash.cpp
3 *
4 * This file handles transmission of ECU data to various OE dashboards.
5 *
6 * @date Mar 19, 2020
7 * @author Matthew Kennedy, (c) 2020
8 */
9
10 #include "pch.h"
11
12 #if EFI_CAN_SUPPORT || EFI_UNIT_TEST
13 #include "can_dash.h"
14 #include "can_dash_ms.h"
15 #include "can_dash_nissan.h"
16 #include "can_dash_haltech.h"
17 #include "board_overrides.h"
18 #include "can_bmw.h"
19 #include "can_vag.h"
20 #include "can_dash_honda.h"
21
22 #include "rusefi_types.h"
23 #include "rtc_helper.h"
24 #include "fuel_math.h"
25
26 // CAN Bus ID for broadcast
27 #define CAN_FIAT_MOTOR_INFO 0x561
28 #define CAN_MAZDA_RX_RPM_SPEED 0x201
29 #define CAN_MAZDA_RX_STEERING_WARNING 0x300
30 #define CAN_MAZDA_RX_STATUS_1 0x212
31 #define CAN_MAZDA_RX_STATUS_2 0x420
32
33 //w202 DASH
34 #define W202_STAT_1 0x308 /* _20ms cycle */
35 #define W202_STAT_2 0x608 /* _100ms cycle */
36 #define W202_ALIVE 0x210 /* _200ms cycle */
37 #define W202_STAT_3 0x310 /* _200ms cycle */
38
39 //BMW E90 DASH
40 #define E90_ABS_COUNTER 0x0C0
41 #define E90_SEATBELT_COUNTER 0x0D7
42 #define E90_T15 0x130
43 #define E90_RPM 0x175
44 #define E90_BRAKE_COUNTER 0x19E
45 #define E90_SPEED 0x1A6
46 // https://github.com/HeinrichG-V12/E65_ReverseEngineering/blob/main/docs/0x1D0.md
47 #define E90_TEMP 0x1D0
48 // MECH Anzeige Getriebedaten
49 #define E90_GEAR 0x1D2
50 #define E90_FUEL 0x349
51 #define E90_EBRAKE 0x34F
52 #define E90_TIME 0x39E
53
54 #if !EFI_UNIT_TEST
55 static time_msecs_t mph_timer;
56 static time_msecs_t mph_ctr;
57 #endif
58
59 /**
60 * https://docs.google.com/spreadsheets/d/1IkP05ODpjNt-k4YQLYl58_TNlN9U4IBu5z7i0BPVEM4
61 */
62 #define GENESIS_COUPLE_RPM_316 0x316
63 #define GENESIS_COUPLE_COOLANT_329 0x329
64 #define GENESIS_COUPLE_SENSORS_382 0x382
65 // when A/C compressor is allowed to be on, these values need to be sent so the A/C panel activates the compressor
66 #define GENESIS_COUPLE_AC_ENABLE_18F 0x18F
67
68
69 static uint8_t rpmcounter;
70 static uint8_t seatbeltcnt;
71 static uint8_t abscounter = 0xF0;
72 static uint8_t brakecnt_1 = 0xF0, brakecnt_2 = 0xF0;
73 static uint8_t mph_a, mph_2a, mph_last, tmp_cnt, gear_cnt;
74 static uint16_t mph_counter = 0xF000;
75 static bool cluster_time_set;
76
77 constexpr uint8_t e90_temp_offset = 49;
78
79 // todo: those forward declarations are out of overall code style
80 void canDashboardFiat(CanCycle cycle);
81 void canMazdaRX8(CanCycle cycle);
82 void canDashboardW202(CanCycle cycle);
83 void canDashboardVagMqb(CanCycle cycle);
84 void canDashboardGenesisCoupe(CanCycle cycle);
85 void canDashboardAim(CanCycle cycle);
86
87 //BMW Dashboard
88 //todo: we use 50ms fixed cycle, trace is needed to check for correct period
89 1 static void canDashboardBmwE46(CanCycle cycle) {
90
91
1/2
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
1/2
✓ Decision 'true' taken 1 time.
✗ Decision 'false' not taken.
1 if (cycle.isInterval(CI::_10ms)) {
92 {
93
1/1
✓ Branch 2 taken 1 time.
1 CanTxMessage msg(CanCategory::NBC, CAN_BMW_E46_RPM);
94
1/1
✓ Branch 1 taken 1 time.
1 msg[0] = 0x05; // ASC message
95
1/1
✓ Branch 1 taken 1 time.
1 msg[1] = 0x0C; // Indexed Engine Torque in % of C_TQ_STND TBD
96
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
1 msg.setShortValue((int) (Sensor::getOrZero(SensorType::Rpm) * 6.4), 2);
97
1/1
✓ Branch 1 taken 1 time.
1 msg[4] = 0x0C;
98
1/1
✓ Branch 1 taken 1 time.
1 msg[5] = 0x15;
99
1/1
✓ Branch 1 taken 1 time.
1 msg[6] = 0x00;
100
1/1
✓ Branch 1 taken 1 time.
1 msg[7] = 0x35;
101 1 }
102
103 {
104
1/1
✓ Branch 2 taken 1 time.
1 CanTxMessage msg(CanCategory::NBC, CAN_BMW_E46_DME2);
105
1/1
✓ Branch 1 taken 1 time.
1 msg[0] = 0x11;
106
2/2
✓ Branch 1 taken 1 time.
✓ Branch 4 taken 1 time.
1 msg[1] = (Sensor::getOrZero(SensorType::Clt) + 48.373) / 0.75;
107
1/1
✓ Branch 1 taken 1 time.
1 msg[2] = 0x00; // baro sensor
108
1/1
✓ Branch 1 taken 1 time.
1 msg[3] = 0x08;
109
1/1
✓ Branch 1 taken 1 time.
1 msg[4] = 0x00; // TPS_VIRT_CRU_CAN, not used.
110
1/1
✓ Branch 1 taken 1 time.
1 msg[5] = 0x00; // TPS out, but we set to 0 just in case.
111
1/1
✓ Branch 1 taken 1 time.
1 msg[6] = 0x00; // brake system status Ok.
112
1/1
✓ Branch 1 taken 1 time.
1 msg[7] = 0x00; // not used
113 1 }
114 }
115 1 }
116
117 //todo: we use 50ms fixed cycle, trace is needed to check for correct period
118 void canMazdaRX8(CanCycle cycle) {
119 if (cycle.isInterval(CI::_50ms)) {
120 {
121 CanTxMessage msg(CanCategory::NBC, CAN_MAZDA_RX_STEERING_WARNING);
122 // todo: something needs to be set here? see http://rusefi.com/wiki/index.php?title=Vehicle:Mazda_Rx8_2004
123 }
124
125 {
126 CanTxMessage msg(CanCategory::NBC, CAN_MAZDA_RX_RPM_SPEED);
127
128 float kph = Sensor::getOrZero(SensorType::VehicleSpeed);
129
130 // todo: LSB+SWAP? lol, that's MSB?
131 msg.setShortValue(SWAP_UINT16(Sensor::getOrZero(SensorType::Rpm) * 4), 0);
132 msg.setShortValue(0xFFFF, 2);
133 // todo: LSB+SWAP? lol, that's MSB?
134 msg.setShortValue(SWAP_UINT16((int )(100 * kph + 10000)), 4);
135 msg.setShortValue(0, 6);
136 }
137
138 {
139 CanTxMessage msg(CanCategory::NBC, CAN_MAZDA_RX_STATUS_1);
140 msg[0] = 0xFE; //Unknown
141 msg[1] = 0xFE; //Unknown
142 msg[2] = 0xFE; //Unknown
143 msg[3] = 0x34; //DSC OFF in combo with byte 5 Live data only seen 0x34
144 msg[4] = 0x00; // B01000000; // Brake warning B00001000; //ABS warning
145 msg[5] = 0x40; // TCS in combo with byte 3
146 msg[6] = 0x00; // Unknown
147 msg[7] = 0x00; // Unused
148 }
149
150 {
151 CanTxMessage msg(CanCategory::NBC, CAN_MAZDA_RX_STATUS_2);
152 auto clt = Sensor::get(SensorType::Clt);
153 msg[0] = (uint8_t)(clt.value_or(0) + 69); //temp gauge //~170 is red, ~165 last bar, 152 centre, 90 first bar, 92 second bar
154 // TODO: fixme!
155 //msg[1] = ((int16_t)(engine->engineState.vssEventCounter*(engineConfiguration->vehicleSpeedCoef*0.277*2.58))) & 0xff;
156 msg[2] = 0x00; // unknown
157 msg[3] = 0x00; //unknown
158 msg[4] = 0x01; //Oil Pressure (not really a gauge)
159 msg[5] = 0x00; //check engine light
160 msg[6] = 0x00; //Coolant, oil and battery
161 if ((Sensor::getOrZero(SensorType::Rpm)>0) && (Sensor::get(SensorType::BatteryVoltage).value_or(VBAT_FALLBACK_VALUE)<13)) {
162 msg.setBit(6, 6); // battery light
163 }
164 if (!clt.Valid || clt.Value > 105) {
165 // coolant light, 101 - red zone, light means its get too hot
166 // Also turn on the light in case of sensor failure
167 msg.setBit(6, 1);
168 }
169 //oil pressure warning lamp bit is 7
170 msg[7] = 0x00; //unused
171 }
172 }
173
174 }
175
176 void canDashboardFiat(CanCycle cycle) {
177 if (cycle.isInterval(CI::_50ms)) {
178 {
179 //Fiat Dashboard
180 CanTxMessage msg(CanCategory::NBC, CAN_FIAT_MOTOR_INFO);
181 msg.setShortValue((int) (Sensor::getOrZero(SensorType::Clt) - 40), 3); //Coolant Temp
182 msg.setShortValue(Sensor::getOrZero(SensorType::Rpm) / 32, 6); //RPM
183 }
184 }
185 }
186
187 void canDashboardVAG(CanCycle cycle) {
188 if (cycle.isInterval(CI::_10ms)) {
189 {
190 // https://github.com/commaai/opendbc/blob/57c8340a180dd8c75139b18050eb17c72c9cb6e4/vw_golf_mk4.dbc#L394
191 //VAG Dashboard
192 CanTxMessage msg(CanCategory::NBC, CAN_VAG_Motor_1);
193 msg.setShortValue(Sensor::getOrZero(SensorType::Rpm) * 4, 2); //RPM
194 }
195
196 float clt = Sensor::getOrZero(SensorType::Clt);
197
198 {
199 CanTxMessage msg(CanCategory::NBC, CAN_VAG_Motor_2);
200 msg.setShortValue((int) ((clt + 48.373) / 0.75), 1); //Coolant Temp
201 }
202
203 {
204 CanTxMessage msg(CanCategory::NBC, CAN_VAG_CLT_V2);
205 msg.setShortValue((int) ((clt + 48.373) / 0.75), 4); //Coolant Temp
206 }
207
208 {
209 CanTxMessage msg(CanCategory::NBC, CAN_VAG_IMMO);
210 msg.setShortValue(0x80, 1);
211 }
212 }
213 }
214
215 void canDashboardW202(CanCycle cycle) {
216 if (cycle.isInterval(CI::_20ms)) {
217 {
218 CanTxMessage msg(CanCategory::NBC, W202_STAT_1);
219 msg[0] = 0x08; // Unknown
220 msg.setShortValueMsb(Sensor::getOrZero(SensorType::Rpm), 1);
221 msg[3] = 0x00; // 0x01 - tank blink, 0x02 - EPC
222 msg[4] = 0x00; // Unknown
223 msg[5] = 0x00; // Unknown
224 msg[6] = 0x00; // Unknown - oil info
225 msg[7] = 0x00; // Unknown - oil info
226 }
227 }
228
229 if (cycle.isInterval(CI::_100ms)) {
230 {
231 CanTxMessage msg(CanCategory::NBC, W202_STAT_2); //dlc 7
232 msg[0] = (int)(Sensor::getOrZero(SensorType::Clt) + 40); // CLT -40 offset
233 msg[1] = 0x3D; // TBD
234 msg[2] = 0x63; // Const
235 msg[3] = 0x41; // Const
236 msg[4] = 0x00; // Unknown
237 msg[5] = 0x05; // Const
238 msg[6] = 0x50; // TBD
239 msg[7] = 0x00; // Unknown
240 }
241 }
242
243 if (cycle.isInterval(CI::_200ms)) {
244 {
245 CanTxMessage msg(CanCategory::NBC, W202_ALIVE);
246 msg[0] = 0x0A; // Const
247 msg[1] = 0x18; // Const
248 msg[2] = 0x00; // Const
249 msg[3] = 0x00; // Const
250 msg[4] = 0xC0; // Const
251 msg[5] = 0x00; // Const
252 msg[6] = 0x00; // Const
253 msg[7] = 0x00; // Const
254 }
255
256 {
257 CanTxMessage msg(CanCategory::NBC, W202_STAT_3);
258 msg[0] = 0x00; // Const
259 msg[1] = 0x00; // Const
260 msg[2] = 0x6D; // TBD
261 msg[3] = 0x7B; // Const
262 msg[4] = 0x21; // TBD
263 msg[5] = 0x07; // Const
264 msg[6] = 0x33; // Const
265 msg[7] = 0x05; // Const
266 }
267 }
268 }
269
270 void canDashboardGenesisCoupe(CanCycle cycle) {
271 if (cycle.isInterval(CI::_50ms)) {
272 {
273 CanTxMessage msg(CanCategory::NBC, GENESIS_COUPLE_RPM_316, 8);
274 msg.setShortValueMsb(Sensor::getOrZero(SensorType::Rpm) * 4, /*offset*/ 3);
275 }
276 {
277 CanTxMessage msg(CanCategory::NBC, GENESIS_COUPLE_COOLANT_329, 8);
278 int clt = Sensor::getOrZero(SensorType::Clt) * 2;
279 msg[1] = clt;
280 }
281 }
282 }
283
284 /**
285 * https://docs.google.com/spreadsheets/d/1XMfeGlhgl0lBL54lNtPdmmFd8gLr2T_YTriokb30kJg
286 */
287 void canDashboardVagMqb(CanCycle cycle) {
288 if (cycle.isInterval(CI::_50ms)) {
289
290 { // 'turn-on'
291 CanTxMessage msg(CanCategory::NBC, 0x3C0, 4);
292 // ignition ON
293 msg[2] = 3;
294 }
295
296 { //RPM
297 CanTxMessage msg(CanCategory::NBC, 0x107, 8);
298 msg.setShortValue(Sensor::getOrZero(SensorType::Rpm) / 3.5, /*offset*/ 3);
299 }
300 }
301 }
302
303 static void canDashboardBmwE90(CanCycle cycle) {
304
305 if (cycle.isInterval(CI::_50ms)) {
306
307 { //T15 'turn-on'
308 CanTxMessage msg(CanCategory::NBC, E90_T15, 5);
309 msg[0] = 0x45;
310 msg[1] = 0x41;
311 msg[2] = 0x61;
312 msg[3] = 0x8F;
313 msg[4] = 0xFC;
314 }
315
316 { //Ebrake light
317 CanTxMessage msg(CanCategory::OBD, E90_EBRAKE, 2);
318 msg[0] = 0xFD;
319 msg[1] = 0xFF;
320 }
321
322 { //RPM
323 rpmcounter++;
324 if (rpmcounter > 0xFE)
325 rpmcounter = 0xF0;
326 CanTxMessage msg(CanCategory::OBD, E90_RPM, 3);
327 msg[0] = rpmcounter;
328 msg.setShortValue(Sensor::getOrZero(SensorType::Rpm) * 4, 1);
329 }
330
331 { //oil & coolant temp (all in C, despite gauge being F)
332 tmp_cnt++;
333 if (tmp_cnt >= 0x0F)
334 tmp_cnt = 0x00;
335 CanTxMessage msg(CanCategory::OBD, E90_TEMP, 8);
336 msg[0] = (int)(Sensor::getOrZero(SensorType::Clt) + e90_temp_offset); //coolant
337 msg[1] = (int)(Sensor::getOrZero(SensorType::AuxTemp1) + e90_temp_offset); //oil (AuxTemp1)
338 msg[2] = tmp_cnt;
339 msg[3] = 0xC8;
340 msg[4] = 0xA7;
341 msg[5] = 0xD3;
342 msg[6] = 0x0D;
343 msg[7] = 0xA8;
344 }
345 }
346
347 if (cycle.isInterval(CI::_100ms)) {
348 {
349 //Seatbelt counter
350 seatbeltcnt++;
351 if (seatbeltcnt > 0xFE)
352 seatbeltcnt = 0x00;
353 CanTxMessage msg(CanCategory::NBC, E90_SEATBELT_COUNTER, 2);
354 msg[0] = seatbeltcnt;
355 msg[1] = 0xFF;
356 }
357
358 {
359 //Brake counter 100ms
360 brakecnt_1 += 16;
361 brakecnt_2 += 16;
362 if (brakecnt_1 > 0xEF)
363 brakecnt_1 = 0x0F;
364 if (brakecnt_2 > 0xF0)
365 brakecnt_2 = 0xA0;
366 CanTxMessage msg(CanCategory::NBC, E90_BRAKE_COUNTER, 8);
367 msg[0] = 0x00;
368 msg[1] = 0xE0;
369 msg[2] = brakecnt_1;
370 msg[3] = 0xFC;
371 msg[4] = 0xFE;
372 msg[5] = 0x41;
373 msg[6] = 0x00;
374 msg[7] = brakecnt_2;
375 }
376
377 { //ABS counter
378 abscounter++;
379 if (abscounter > 0xFE)
380 abscounter = 0xF0;
381 CanTxMessage msg(CanCategory::NBC, E90_ABS_COUNTER, 2);
382 msg[0] = abscounter;
383 msg[1] = 0xFF;
384 }
385
386 { //Fuel gauge
387 CanTxMessage msg(CanCategory::NBC, E90_FUEL, 5); //fuel gauge
388 msg[0] = 0x76;
389 msg[1] = 0x0F;
390 msg[2] = 0xBE;
391 msg[3] = 0x1A;
392 msg[4] = 0x00;
393 }
394
395 { //Gear indicator/counter
396 gear_cnt++;
397 if (gear_cnt >= 0x0F)
398 gear_cnt = 0x00;
399 CanTxMessage msg(CanCategory::NBC, E90_GEAR, 6);
400 msg[0] = 0x78;
401 msg[1] = 0x0F;
402 msg[2] = 0xFF;
403 msg[3] = (gear_cnt << 4) | 0xC;
404 msg[4] = 0xF1;
405 msg[5] = 0xFF;
406 }
407
408 #if !EFI_UNIT_TEST
409 { //E90_SPEED
410 auto vehicleSpeed = Sensor::getOrZero(SensorType::VehicleSpeed);
411 float mph = vehicleSpeed * 0.6213712;
412 mph_ctr = ((TIME_I2MS(chVTGetSystemTime()) - mph_timer) / 50);
413 mph_a = (mph_ctr * mph / 2);
414 mph_2a = mph_a + mph_last;
415 mph_last = mph_2a;
416 mph_counter += mph_ctr * 100;
417 if(mph_counter >= 0xFFF0)
418 mph_counter = 0xF000;
419 mph_timer = TIME_I2MS(chVTGetSystemTime());
420 CanTxMessage msg(CanCategory::NBC, E90_SPEED, 8);
421 msg.setShortValue(mph_2a, 0);
422 msg.setShortValue(mph_2a, 2);
423 msg.setShortValue(mph_2a, 4);
424 msg[6] = mph_counter & 0xFF;
425 // todo: what are we packing into what exactly? note the '| 0xF0'
426 msg[7] = (mph_counter >> 8) | 0xF0;
427 }
428 #endif
429 }
430
431 {
432 if (!cluster_time_set) {
433 #if EFI_RTC
434 efidatetime_t dateTime = getRtcDateTime();
435 #else // EFI_RTC
436 efidatetime_t dateTime = {
437 .year = 0, .month = 0, .day = 0,
438 .hour = 0, .minute = 0, .second = 0,
439 };
440 #endif // EFI_RTC
441 CanTxMessage msg(CanCategory::NBC, E90_TIME, 8);
442 msg[0] = dateTime.hour;
443 msg[1] = dateTime.minute;
444 msg[2] = dateTime.second;
445 msg[3] = dateTime.day;
446 msg[4] = (dateTime.month << 4) | 0x0F;
447 msg[5] = dateTime.year & 0xFF;
448 msg[6] = (dateTime.year >> 8) | 0xF0; // collides CAN dash at 4096!
449 msg[7] = 0xF2;
450 cluster_time_set = 1;
451 }
452 }
453 }
454
455 //Based on AIM can protocol
456 //https://www.aimtechnologies.com/support/racingecu/AiM_CAN_101_eng.pdf
457
458 struct Aim5f0 {
459 scaled_channel<uint16_t, 1> Rpm;
460 scaled_channel<uint16_t, 650> Tps;
461 scaled_channel<uint16_t, 650> Pps;
462 scaled_channel<uint16_t, 100> Vss;
463 };
464
465 void populateFrame(Aim5f0& msg) {
466 msg.Rpm = Sensor::getOrZero(SensorType::Rpm);
467 msg.Tps = Sensor::getOrZero(SensorType::Tps1);
468 msg.Pps = Sensor::getOrZero(SensorType::AcceleratorPedal);
469 msg.Vss = Sensor::getOrZero(SensorType::VehicleSpeed);
470 }
471
472 struct Aim5f1 {
473 scaled_channel<uint16_t, 10> WheelSpeedFR;
474 scaled_channel<uint16_t, 10> WheelSpeedFL;
475 scaled_channel<uint16_t, 10> WheelSpeedRR;
476 scaled_channel<uint16_t, 10> WheelSpeedRL;
477 };
478
479 void populateFrame(Aim5f1& msg) {
480 // We don't handle wheel speed, just set to 0?
481 msg.WheelSpeedFR = 0;
482 msg.WheelSpeedFL = 0;
483 msg.WheelSpeedRR = 0;
484 msg.WheelSpeedRL = 0;
485 }
486
487 struct Aim5f2 {
488 scaled_channel<uint16_t, 190> Iat;
489 scaled_channel<uint16_t, 190> Ect;
490 scaled_channel<uint16_t, 190> FuelT;
491 scaled_channel<uint16_t, 190> OilT;
492 };
493
494 void populateFrame(Aim5f2& msg) {
495 msg.Iat = Sensor::getOrZero(SensorType::Iat) + 45;
496 msg.Ect = Sensor::getOrZero(SensorType::Clt) + 45;
497 msg.FuelT = Sensor::getOrZero(SensorType::FuelTemperature) + 45;
498 msg.OilT = Sensor::getOrZero(SensorType::OilTemperature) + 45;
499 }
500
501 struct Aim5f3 {
502 scaled_channel<uint16_t, 10> Map;
503 scaled_channel<uint16_t, 10> Baro;
504 scaled_channel<uint16_t, 1000> OilP;
505 scaled_channel<uint16_t, 20> FuelP;
506 };
507
508 void populateFrame(Aim5f3& msg) {
509 // MAP/Baro are sent in millibar -> 10 millibar per kpa
510 msg.Map = 10 * Sensor::getOrZero(SensorType::Map);
511 msg.Baro = 10 * Sensor::getOrZero(SensorType::BarometricPressure);
512
513 // Oil/Fuel P use bar -> 100 kpa per bar
514 msg.OilP = Sensor::getOrZero(SensorType::OilPressure) / 100;
515 msg.FuelP = Sensor::getOrZero(SensorType::FuelPressureInjector) / 100;
516 }
517
518 struct Aim5f4 {
519 scaled_channel<uint16_t, 10000> Boost;
520 scaled_channel<uint16_t, 3200> Vbat;
521 scaled_channel<uint16_t, 10> FuelConsumptionLH;
522 scaled_channel<int16_t, 1> Gear;
523 };
524
525 void populateFrame(Aim5f4& msg) {
526 float deltaKpa = Sensor::getOrZero(SensorType::Map)
527 - Sensor::get(SensorType::BarometricPressure).value_or(STD_ATMOSPHERE);
528 float boostBar = deltaKpa / 100;
529
530 #ifdef MODULE_ODOMETER
531 float gPerSecond = engine->module<TripOdometer>()->getConsumptionGramPerSecond();
532 #else
533 float gPerSecond = 0;
534 #endif // MODULE_ODOMETER
535
536 float gPerHour = gPerSecond * 3600.0f;
537 float literPerHour = gPerHour * 0.00139f;
538
539 msg.Boost = boostBar;
540 msg.Vbat = Sensor::getOrZero(SensorType::BatteryVoltage);
541 msg.FuelConsumptionLH = 10 * literPerHour;
542 msg.Gear = Sensor::getOrZero(SensorType::DetectedGear);
543 }
544
545 struct Aim5f5 {
546 scaled_channel<uint16_t, 1> ShiftFlag;
547 scaled_channel<uint16_t, 1> GearTime;
548 scaled_channel<uint16_t, 1> TpsV;
549 scaled_channel<uint16_t, 100> FuelLevel;
550 };
551
552 void populateFrame(Aim5f5& msg) {
553 msg.FuelLevel = Sensor::getOrZero(SensorType::FuelLevel);
554
555 // Dunno what to do with these
556 msg.ShiftFlag = 0;
557 msg.GearTime = 0;
558 msg.TpsV = 0;
559 }
560
561 struct Aim5f6 {
562 scaled_channel<uint16_t, 2000> Lambda1;
563 scaled_channel<uint16_t, 2000> Lambda2;
564 scaled_channel<uint16_t, 10> LambdaTemp1;
565 scaled_channel<uint16_t, 10> LambdaTemp2;
566 };
567
568 void populateFrame(Aim5f6& msg) {
569 msg.Lambda1 = Sensor::getOrZero(SensorType::Lambda1);
570 msg.Lambda2 = Sensor::getOrZero(SensorType::Lambda2);
571 msg.LambdaTemp1 = 0;
572 msg.LambdaTemp2 = 0;
573 }
574
575 struct Aim5f7 {
576 scaled_channel<uint16_t, 10> LambdaErr1;
577 scaled_channel<uint16_t, 10> LambdaErr2;
578 scaled_channel<uint16_t, 2000> LambdaTarget1;
579 scaled_channel<uint16_t, 2000> LambdaTarget2;
580 };
581
582 void populateFrame(Aim5f7& msg) {
583 #if EFI_ENGINE_CONTROL
584 // We don't handle wheel speed, just set to 0?
585 msg.LambdaErr1 = 0;
586 msg.LambdaErr2 = 0;
587 // both targets are the same for now
588 msg.LambdaTarget1 = (float)engine->fuelComputer.targetLambda;
589 msg.LambdaTarget2 = (float)engine->fuelComputer.targetLambda;
590 #endif // EFI_ENGINE_CONTROL
591 }
592
593 void canDashboardAim(CanCycle cycle) {
594 if (!cycle.isInterval(CI::_10ms)) {
595 return;
596 }
597
598 auto canChannel = engineConfiguration->canBroadcastUseChannelTwo;
599
600 transmitStruct<Aim5f0>(CanCategory::NBC, 0x5f0, false, canChannel);
601 transmitStruct<Aim5f1>(CanCategory::NBC, 0x5f1, false, canChannel);
602 transmitStruct<Aim5f2>(CanCategory::NBC, 0x5f2, false, canChannel);
603 transmitStruct<Aim5f3>(CanCategory::NBC, 0x5f3, false, canChannel);
604 transmitStruct<Aim5f4>(CanCategory::NBC, 0x5f4, false, canChannel);
605 transmitStruct<Aim5f5>(CanCategory::NBC, 0x5f5, false, canChannel);
606 transmitStruct<Aim5f6>(CanCategory::NBC, 0x5f6, false, canChannel);
607 transmitStruct<Aim5f7>(CanCategory::NBC, 0x5f7, false, canChannel);
608
609 // there are more, but less important for us
610 // transmitStruct<Aim5f8>(0x5f8, false);
611 // transmitStruct<Aim5f9>(0x5f9, false);
612 // transmitStruct<Aim5fa>(0x5fa, false);
613 // transmitStruct<Aim5fb>(0x5fb, false);
614 // transmitStruct<Aim5fc>(0x5fc, false);
615 // transmitStruct<Aim5fd>(0x5fd, false);
616 }
617
618 std::optional<board_can_update_dash_type> custom_board_update_dash;
619
620 1 PUBLIC_API_WEAK void boardUpdateDash(CanCycle cycle) { UNUSED(cycle); }
621
622 1 void updateDash(CanCycle cycle) {
623 // TODO: use call_board_override
624
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1 time.
1/2
✗ Decision 'true' not taken.
✓ Decision 'false' taken 1 time.
1 if (custom_board_update_dash.has_value()) {
625 custom_board_update_dash.value()(cycle);
626 }
627
628 1 boardUpdateDash(cycle);
629
630 // Transmit dash data, if enabled
631
1/15
✗ Branch 0 not taken.
✓ Branch 1 taken 1 time.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
1 switch (engineConfiguration->canNbcType) {
632 case CAN_BUS_NBC_NONE:
633 break;
634
1/1
✓ Decision 'true' taken 1 time.
1 case CAN_BUS_BMW_E46:
635 1 canDashboardBmwE46(cycle);
636 1 break;
637 case CAN_BUS_Haltech:
638 canDashboardHaltech(cycle);
639 break;
640 case CAN_BUS_NBC_FIAT:
641 canDashboardFiat(cycle);
642 break;
643 case CAN_BUS_NBC_VAG:
644 canDashboardVAG(cycle);
645 break;
646 case CAN_BUS_MAZDA_RX8:
647 canMazdaRX8(cycle);
648 break;
649 case CAN_BUS_W202_C180:
650 canDashboardW202(cycle);
651 break;
652 case CAN_BUS_BMW_E90:
653 canDashboardBmwE90(cycle);
654 break;
655 case CAN_BUS_MQB:
656 canDashboardVagMqb(cycle);
657 break;
658 case CAN_BUS_NISSAN_VQ:
659 canDashboardNissanVQ(cycle);
660 break;
661 case CAN_BUS_GENESIS_COUPE:
662 canDashboardGenesisCoupe(cycle);
663 break;
664 case CAN_BUS_HONDA_K:
665 canDashboardHondaK(cycle);
666 break;
667 case CAN_AIM_DASH:
668 canDashboardAim(cycle);
669 break;
670 case CAN_BUS_MS_SIMPLE_BROADCAST:
671 canDashboardTS(cycle);
672 break;
673 default:
674 criticalError("Nothing for canNbcType %d/%s", engineConfiguration->canNbcType, getCan_nbc_e(engineConfiguration->canNbcType));
675 break;
676 }
677 1 }
678
679 #endif // EFI_CAN_SUPPORT
680