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 |