Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Automotive Ecu Systems expertise. Covers 8 topics: Bcm Body Control, Bms Battery Management, Domain Controller Integration, Ivi Infotainment Systems, Pdu Power Distribution.
.claude/skills/pangzhenying2025-automotive-ecu-systems/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-08 | ✗→✓ | ▲ Improved | 743% | 0% |
| case-05 | ✓→✓ | = Same ✓ | 1559% | 0% |
| case-11 | ✓→✓ | = Same ✓ | 1006% | 0% |
| case-17 | ✓→✓ | = Same ✓ | 1226% | 0% |
| case-19 | ✓→✓ | = Same ✓ | 1244% | 0% |
The Body Control Unit (BCM) manages exterior/interior lighting, HVAC integration, door locks, windows, wipers, keyless entry, anti-theft systems, and power distribution across body electronics. This skill covers production-ready BCM development with LIN bus mastering.
c/* bcm_lighting_control.c - Comprehensive lighting management */ #include "bcm_lighting_control.h" #include <stdint.h> #include <stdbool.h> #define PWM_FREQUENCY_HZ 1000 #define DIM_STEP_PERCENT 5 #define AUTO_HEADLIGHT_THRESHOLD_LUX 100 typedef enum { LIGHT_MODE_OFF = 0, LIGHT_MODE_PARKING, LIGHT_MODE_DAYTIME_RUNNING, LIGHT_MODE_LOW_BEAM, LIGHT_MODE_HIGH_BEAM, LIGHT_MODE_AUTO } HeadlightMode_t; typedef struct { bool left_turn_signal_active; bool right_turn_signal_active; bool hazard_active; uint8_t turn_signal_phase; /* 0-100% for flashing */ uint32_t last_toggle_ms; } TurnSignalState_t; typedef struct { HeadlightMode_t mode; uint8_t brightness_percent; bool high_beam_assist_active; bool adaptive_lighting_active; } HeadlightState_t; static TurnSignalState_t g_turn_signals = {0}; static HeadlightState_t g_headlights = {0}; void BCM_Lighting_Init(void) { /* Initialize PWM channels for LED control */ PWM_Init(PWM_CHANNEL_LEFT_HEADLIGHT, PWM_FREQUENCY_HZ); PWM_Init(PWM_CHANNEL_RIGHT_HEADLIGHT, PWM_FREQUENCY_HZ); PWM_Init(PWM_CHANNEL_DOME_LIGHT, PWM_FREQUENCY_HZ); PWM_Init(PWM_CHANNEL_AMBIENT_LIGHT, PWM_FREQUENCY_HZ); /* Set initial state */ g_headlights.mode = LIGHT_MODE_OFF; g_headlights.brightness_percent = 100; /* Load saved settings from EEPROM */ NvM_ReadBlock(NVM_BLOCK_LIGHTING_SETTINGS, &g_headlights); } void BCM_TurnSignals_Update(void) { uint32_t current_time = GetSystemTime_ms(); /* Flash at 1 Hz (500ms on, 500ms off) */ if ((current_time - g_turn_signals.last_toggle_ms) > 500) { g_turn_signals.turn_signal_phase = (g_turn_signals.turn_signal_phase == 0) ? 100 : 0; g_turn_signals.last_toggle_ms = current_time; } /* Left turn signal */ if (g_turn_signals.left_turn_signal_active || g_turn_signals.hazard_active) { PWM_SetDutyCycle(PWM_CHANNEL_LEFT_TURN_FRONT, g_turn_signals.turn_signal_phase); PWM_SetDutyCycle(PWM_CHANNEL_LEFT_TURN_REAR, g_turn_signals.turn_signal_phase); } else { PWM_SetDutyCycle(PWM_CHANNEL_LEFT_TURN_FRONT, 0); PWM_SetDutyCycle(PWM_CHANNEL_LEFT_TURN_REAR, 0); } /* Right turn signal */ if (g_turn_signals.right_turn_signal_active || g_turn_signals.hazard_active) { PWM_SetDutyCycle(PWM_CHANNEL_RIGHT_TURN_FRONT, g_turn_signals.turn_signal_phase); PWM_SetDutyCycle(PWM_CHANNEL_RIGHT_TURN_REAR, g_turn_signals.turn_signal_phase); } else { PWM_SetDutyCycle(PWM_CHANNEL_RIGHT_TURN_FRONT, 0); PWM_SetDutyCycle(PWM_CHANNEL_RIGHT_TURN_REAR, 0); } } void BCM_Headlights_Update(void) { uint16_t ambient_light_lux = BCM_LightSensor_Read(); switch (g_headlights.mode) { case LIGHT_MODE_OFF: PWM_SetDutyCycle(PWM_CHANNEL_LEFT_HEADLIGHT, 0); PWM_SetDutyCycle(PWM_CHANNEL_RIGHT_HEADLIGHT, 0); break; case LIGHT_MODE_PARKING: /* 20% brightness for parking lights */ PWM_SetDutyCycle(PWM_CHANNEL_LEFT_HEADLIGHT, 20); PWM_SetDutyCycle(PWM_CHANNEL_RIGHT_HEADLIGHT, 20); break; case LIGHT_MODE_DAYTIME_RUNNING: /* 50% brightness for DRL */ PWM_SetDutyCycle(PWM_CHANNEL_LEFT_HEADLIGHT, 50); PWM_SetDutyCycle(PWM_CHANNEL_RIGHT_HEADLIGHT, 50); break; case LIGHT_MODE_LOW_BEAM: PWM_SetDutyCycle(PWM_CHANNEL_LEFT_HEADLIGHT, g_headlights.brightness_percent); PWM_SetDutyCycle(PWM_CHANNEL_RIGHT_HEADLIGHT, g_headlights.brightness_percent); break; case LIGHT_MODE_HIGH_BEAM: /* Full brightness for high beam */ PWM_SetDutyCycle(PWM_CHANNEL_LEFT_HEADLIGHT, 100); PWM_SetDutyCycle(PWM_CHANNEL_RIGHT_HEADLIGHT, 100); PWM_SetDutyCycle(PWM_CHANNEL_HIGH_BEAM, 100); break; case LIGHT_MODE_AUTO: /* Automatic headlight control based on ambient light */ if (ambient_light_lux < AUTO_HEADLIGHT_THRESHOLD_LUX) { /* Dark: enable low beams */ g_headlights.mode = LIGHT_MODE_LOW_BEAM; } else { /* Bright: enable DRL only */ g_headlights.mode = LIGHT_MODE_DAYTIME_RUNNING; } break; } /* High beam assist: automatically switch to low beam when oncoming traffic detected */ if (g_headlights.high_beam_assist_active && g_headlights.mode == LIGHT_MODE_HIGH_BEAM) { bool oncoming_detected = ADAS_Camera_DetectOncomingVehicle(); if (oncoming_detected) { g_headlights.mode = LIGHT_MODE_LOW_BEAM; } } } /* Interior dome light with fade-in/fade-out */ void BCM_DomeLight_SetState(bool on, bool fade) { static uint8_t current_brightness = 0; uint8_t target_brightness = on ? 100 : 0; if (fade) { /* Fade gradually */ while (current_brightness != target_brightness) { if (current_brightness < target_brightness) { current_brightness += DIM_STEP_PERCENT; } else { current_brightness -= DIM_STEP_PERCENT; } PWM_SetDutyCycle(PWM_CHANNEL_DOME_LIGHT, current_brightness); OsTask_Sleep(50); /* 50ms steps for smooth fade */ } } else { /* Immediate switch */ current_brightness = target_brightness; PWM_SetDutyCycle(PWM_CHANNEL_DOME_LIGHT, current_brightness); } }
c/* bcm_door_control.c - Central locking and keyless entry */ #include "bcm_door_control.h" #define KEYFOB_UNLOCK_TIMEOUT_MS 3000 #define AUTO_LOCK_SPEED_THRESHOLD_KPH 10 #define PASSIVE_ENTRY_RANGE_M 2.0 typedef enum { DOOR_FL = 0, DOOR_FR, DOOR_RL, DOOR_RR, DOOR_TRUNK, DOOR_COUNT } DoorID_t; typedef struct { bool locked; bool open; uint32_t last_lock_timestamp_ms; } DoorState_t; typedef struct { uint32_t keyfob_id; int8_t rssi_dbm; float distance_m; /* Estimated from RSSI */ bool authenticated; } KeyFobState_t; static DoorState_t g_doors[DOOR_COUNT] = {0}; static KeyFobState_t g_active_keyfob = {0}; void BCM_DoorControl_Init(void) { /* Initialize door lock actuators */ for (int i = 0; i < DOOR_COUNT; i++) { GPIO_ConfigOutput(DOOR_LOCK_PINS[i]); g_doors[i].locked = true; } /* Initialize door open sensors (switches) */ for (int i = 0; i < DOOR_COUNT; i++) { GPIO_ConfigInput(DOOR_SWITCH_PINS[i], GPIO_PULL_UP); } /* Initialize BLE for passive keyless entry */ BLE_Init(); BLE_StartAdvertising("VehicleKey"); } void BCM_DoorControl_LockAll(void) { for (int i = 0; i < DOOR_COUNT; i++) { if (!g_doors[i].locked) { /* Activate lock actuator (pulse for 500ms) */ GPIO_Set(DOOR_LOCK_PINS[i], true); OsTask_Sleep(500); GPIO_Set(DOOR_LOCK_PINS[i], false); g_doors[i].locked = true; g_doors[i].last_lock_timestamp_ms = GetSystemTime_ms(); } } /* Chirp horn once to confirm lock */ BCM_Horn_Chirp(1); /* Flash turn signals once */ BCM_TurnSignals_Flash(1); } void BCM_DoorControl_UnlockAll(void) { /* Unlock driver door first (common in luxury vehicles) */ BCM_DoorControl_UnlockSingle(DOOR_FL); /* Wait 2 seconds, then unlock all if button pressed again */ uint32_t start_time = GetSystemTime_ms(); while ((GetSystemTime_ms() - start_time) < 2000) { if (KeyFob_ButtonPressed(KEYFOB_BUTTON_UNLOCK)) { /* Second press: unlock all doors */ for (int i = 0; i < DOOR_COUNT; i++) { BCM_DoorControl_UnlockSingle((DoorID_t)i); } break; } OsTask_Sleep(10); } /* Flash turn signals twice */ BCM_TurnSignals_Flash(2); } void BCM_DoorControl_UnlockSingle(DoorID_t door) { if (g_doors[door].locked) { /* Activate unlock actuator */ GPIO_Set(DOOR_UNLOCK_PINS[door], true); OsTask_Sleep(500); GPIO_Set(DOOR_UNLOCK_PINS[door], false); g_doors[door].locked = false; } } /* Passive keyless entry: unlock when approaching with authenticated key */ void BCM_PassiveEntry_Update(void) { /* Scan for BLE key fobs */ if (BLE_ScanForDevice(g_active_keyfob.keyfob_id)) { g_active_keyfob.rssi_dbm = BLE_GetRSSI(); /* Estimate distance from RSSI (simplified model) */ g_active_keyfob.distance_m = pow(10, (-59 - g_active_keyfob.rssi_dbm) / (10 * 2.0)); /* Authenticate key fob */ if (!g_active_keyfob.authenticated) { uint8_t challenge[16]; uint8_t response[16]; BCM_Crypto_GenerateChallenge(challenge); BLE_SendChallenge(challenge); if (BLE_ReceiveResponse(response) && BCM_Crypto_VerifyResponse(challenge, response)) { g_active_keyfob.authenticated = true; } } /* Unlock if authenticated and within range */ if (g_active_keyfob.authenticated && g_active_keyfob.distance_m < PASSIVE_ENTRY_RANGE_M) { /* Check if door handle touched (capacitive sensor) */ if (GPIO_Read(DOOR_HANDLE_SENSOR_FL)) { BCM_DoorControl_UnlockSingle(DOOR_FL); } } } } /* Auto-lock when driving */ void BCM_AutoLock_Update(void) { uint16_t vehicle_speed = VCU_GetVehicleSpeed_kph(); if (vehicle_speed > AUTO_LOCK_SPEED_THRESHOLD_KPH) { /* Vehicle is moving: auto-lock all doors */ bool any_unlocked = false; for (int i = 0; i < DOOR_COUNT; i++) { if (!g_doors[i].locked) { any_unlocked = true; break; } } if (any_unlocked) { BCM_DoorControl_LockAll(); } } }
c/* bcm_window_control.c - Power window management with anti-pinch */ #include "bcm_window_control.h" #define WINDOW_FL 0 #define WINDOW_FR 1 #define WINDOW_RL 2 #define WINDOW_RR 3 #define WINDOW_COUNT 4 #define ANTI_PINCH_FORCE_THRESHOLD_N 100 #define WINDOW_POSITION_SAMPLES 10 typedef enum { WINDOW_STATE_STOPPED = 0, WINDOW_STATE_MOVING_UP, WINDOW_STATE_MOVING_DOWN, WINDOW_STATE_PINCH_DETECTED } WindowState_t; typedef struct { WindowState_t state; uint8_t position_percent; /* 0=closed, 100=fully open */ uint16_t motor_current_ma; bool one_touch_up_active; bool one_touch_down_active; } WindowControl_t; static WindowControl_t g_windows[WINDOW_COUNT] = {0}; void BCM_WindowControl_Init(void) { /* Initialize window motor drivers (H-bridge) */ for (int i = 0; i < WINDOW_COUNT; i++) { GPIO_ConfigOutput(WINDOW_MOTOR_UP_PINS[i]); GPIO_ConfigOutput(WINDOW_MOTOR_DOWN_PINS[i]); } /* Initialize window position sensors (Hall effect) */ for (int i = 0; i < WINDOW_COUNT; i++) { ADC_ConfigChannel(WINDOW_POSITION_ADC_CHANNELS[i]); } /* Initialize current sensing for anti-pinch */ for (int i = 0; i < WINDOW_COUNT; i++) { ADC_ConfigChannel(WINDOW_CURRENT_ADC_CHANNELS[i]); } } void BCM_Window_MoveUp(uint8_t window_id) { if (window_id >= WINDOW_COUNT) return; WindowControl_t* window = &g_windows[window_id]; if (window->position_percent == 0) { return; /* Already fully closed */ } /* Activate motor upward */ GPIO_Set(WINDOW_MOTOR_UP_PINS[window_id], true); GPIO_Set(WINDOW_MOTOR_DOWN_PINS[window_id], false); window->state = WINDOW_STATE_MOVING_UP; } void BCM_Window_MoveDown(uint8_t window_id) { if (window_id >= WINDOW_COUNT) return; WindowControl_t* window = &g_windows[window_id]; if (window->position_percent == 100) { return; /* Already fully open */ } /* Activate motor downward */ GPIO_Set(WINDOW_MOTOR_UP_PINS[window_id], false); GPIO_Set(WINDOW_MOTOR_DOWN_PINS[window_id], true); window->state = WINDOW_STATE_MOVING_DOWN; } void BCM_Window_Stop(uint8_t window_id) { if (window_id >= WINDOW_COUNT) return; /* Stop motor */ GPIO_Set(WINDOW_MOTOR_UP_PINS[window_id], false); GPIO_Set(WINDOW_MOTOR_DOWN_PINS[window_id], false); g_windows[window_id].state = WINDOW_STATE_STOPPED; } /* Anti-pinch detection: monitor motor current during closing */ void BCM_Window_AntiPinchUpdate(uint8_t window_id) { WindowControl_t* window = &g_windows[window_id]; if (window->state != WINDOW_STATE_MOVING_UP) { return; /* Only check during closing */ } /* Read motor current */ uint16_t adc_value = ADC_Read(WINDOW_CURRENT_ADC_CHANNELS[window_id]); window->motor_current_ma = (adc_value * 5000) / 4096; /* 12-bit ADC, 0-5A range */ /* Detect excessive current (indicates obstruction) */ if (window->motor_current_ma > ANTI_PINCH_FORCE_THRESHOLD_N) { /* Pinch detected: reverse window */ window->state = WINDOW_STATE_PINCH_DETECTED; BCM_Window_Stop(window_id); OsTask_Sleep(100); /* Move down slightly to release obstruction */ GPIO_Set(WINDOW_MOTOR_DOWN_PINS[window_id], true); OsTask_Sleep(500); GPIO_Set(WINDOW_MOTOR_DOWN_PINS[window_id], false); window->state = WINDOW_STATE_STOPPED; /* Log event */ DTC_SetFault(DTC_WINDOW_ANTI_PINCH_TRIGGERED + window_id); } } /* One-touch up/down */ void BCM_Window_OneTouchUp(uint8_t window_id) { g_windows[window_id].one_touch_up_active = true; while (g_windows[window_id].position_percent > 0 && g_windows[window_id].state != WINDOW_STATE_PINCH_DETECTED) { BCM_Window_MoveUp(window_id); BCM_Window_UpdatePosition(window_id); BCM_Window_AntiPinchUpdate(window_id); OsTask_Sleep(10); } BCM_Window_Stop(window_id); g_windows[window_id].one_touch_up_active = false; }
c/* bcm_lin_master.c - LIN bus control for door modules */ #include "bcm_lin_master.h" #define LIN_BAUDRATE 19200 #define LIN_BREAK_DURATION_US 750 #define LIN_FRAME_TIMEOUT_MS 50 typedef struct { uint8_t frame_id; uint8_t data[8]; uint8_t length; uint8_t checksum; } LINFrame_t; /* Door module addresses */ #define LIN_DOOR_FL_ID 0x01 #define LIN_DOOR_FR_ID 0x02 #define LIN_DOOR_RL_ID 0x03 #define LIN_DOOR_RR_ID 0x04 void BCM_LIN_Init(void) { /* Configure UART for LIN */ UART_Init(LIN_UART_PORT, LIN_BAUDRATE); UART_SetMode(LIN_UART_PORT, UART_MODE_LIN); } void BCM_LIN_SendBreak(void) { /* Generate LIN break field (dominant for 750µs) */ GPIO_Set(LIN_TX_PIN, false); usleep(LIN_BREAK_DURATION_US); GPIO_Set(LIN_TX_PIN, true); } bool BCM_LIN_SendFrame(const LINFrame_t* frame) { /* Send break + sync byte + frame ID */ BCM_LIN_SendBreak(); UART_WriteByte(LIN_UART_PORT, 0x55); /* Sync byte */ UART_WriteByte(LIN_UART_PORT, frame->frame_id); /* Send data */ for (int i = 0; i < frame->length; i++) { UART_WriteByte(LIN_UART_PORT, frame->data[i]); } /* Send checksum */ UART_WriteByte(LIN_UART_PORT, frame->checksum); return true; } /* Command door module to lock/unlock */ void BCM_LIN_DoorLockCommand(uint8_t door_module_id, bool lock) { LINFrame_t frame; frame.frame_id = door_module_id; frame.length = 2; frame.data[0] = lock ? 0x01 : 0x02; /* 0x01=lock, 0x02=unlock */ frame.data[1] = 0x00; frame.checksum = BCM_LIN_CalculateChecksum(&frame); BCM_LIN_SendFrame(&frame); } /* Read door status from LIN module */ bool BCM_LIN_ReadDoorStatus(uint8_t door_module_id, bool* door_open, bool* window_position) { LINFrame_t request; request.frame_id = door_module_id | 0x40; /* Read request */ request.length = 0; request.checksum = BCM_LIN_CalculateChecksum(&request); BCM_LIN_SendFrame(&request); /* Wait for response */ LINFrame_t response; if (BCM_LIN_ReceiveFrame(&response, LIN_FRAME_TIMEOUT_MS)) { *door_open = (response.data[0] & 0x01) != 0; *window_position = response.data[1]; return true; } return false; }
VERSION ""
NS_ :
BS_:
BU_: BCM VCU IVI
/* BCM Lighting Status */
BO_ 512 BCM_LightingStatus: 8 BCM
SG_ BCM_HeadlightMode : 0|8@1+ (0,0) [0|5] "" IVI
SG_ BCM_LeftTurnSignal : 8|1@1+ (0,0) [0|1] "" VCU,IVI
SG_ BCM_RightTurnSignal : 9|1@1+ (0,0) [0|1] "" VCU,IVI
SG_ BCM_HazardActive : 10|1@1+ (0,0) [0|1] "" VCU,IVI
SG_ BCM_HighBeamActive : 11|1@1+ (0,0) [0|1] "" VCU,IVI
SG_ BCM_BrakeLight : 12|1@1+ (0,0) [0|1] "" VCU
/* BCM Door Status */
BO_ 513 BCM_DoorStatus: 8 BCM
SG_ BCM_DoorLocked_FL : 0|1@1+ (0,0) [0|1] "" IVI
SG_ BCM_DoorLocked_FR : 1|1@1+ (0,0) [0|1] "" IVI
SG_ BCM_DoorLocked_RL : 2|1@1+ (0,0) [0|1] "" IVI
SG_ BCM_DoorLocked_RR : 3|1@1+ (0,0) [0|1] "" IVI
SG_ BCM_DoorOpen_FL : 8|1@1+ (0,0) [0|1] "" VCU,IVI
SG_ BCM_DoorOpen_FR : 9|1@1+ (0,0) [0|1] "" VCU,IVI
SG_ BCM_DoorOpen_RL : 10|1@1+ (0,0) [0|1] "" VCU,IVI
SG_ BCM_DoorOpen_RR : 11|1@1+ (0,0) [0|1] "" VCU,IVI
SG_ BCM_TrunkOpen : 12|1@1+ (0,0) [0|1] "" VCU,IVI
VAL_ 512 BCM_HeadlightMode 0 "Off" 1 "Parking" 2 "DRL" 3 "LowBeam" 4 "HighBeam" 5 "Auto";The Battery Management System (BMS) monitors cell voltages, estimates SOC/SOH, performs cell balancing, manages thermal systems, controls contactors, and ensures ISO 26262 ASIL-D safety compliance for high-voltage battery packs.
c/* bms_cell_monitoring.c - Multi-cell voltage acquisition */ #include "bms_cell_monitoring.h" #define MAX_CELLS_PER_MODULE 12 #define MAX_MODULES 10 #define TOTAL_CELLS (MAX_CELLS_PER_MODULE * MAX_MODULES) #define CELL_OVERVOLTAGE_MV 4200 #define CELL_UNDERVOLTAGE_MV 2500 typedef struct { uint16_t voltage_mv; int16_t temperature_c_x10; /* 0.1°C resolution */ bool balancing_active; } CellData_t; typedef struct { CellData_t cells[MAX_CELLS_PER_MODULE]; uint8_t module_id; int16_t module_temperature_c_x10; bool communication_ok; } ModuleData_t; static ModuleData_t g_modules[MAX_MODULES]; /* LTC6811 Battery Monitor IC interface */ void BMS_CellMonitoring_Init(void) { /* Initialize SPI for LTC6811 daisy chain */ SPI_Init(SPI_BMS, 1000000); /* 1 MHz */ /* Wake up all LTC6811 ICs */ BMS_LTC6811_Wakeup(); /* Configure cell measurement mode */ uint8_t config[6] = { 0xF8, /* GPIO pull-downs off, REFON=1 */ 0x00, /* Discharge switches off */ 0x00, 0x00, 0x00, 0x00 }; BMS_LTC6811_WriteConfig(config); } void BMS_CellMonitoring_Update(void) { /* Start cell voltage conversion (all cells, all modules) */ BMS_LTC6811_StartCellConversion(ADC_MODE_NORMAL, ADC_FILTER_7KHZ); /* Wait for conversion (2.3ms for normal mode) */ OsTask_Sleep(3); /* Read cell voltages from all modules */ for (uint8_t module = 0; module < MAX_MODULES; module++) { uint16_t cell_voltages[MAX_CELLS_PER_MODULE]; if (BMS_LTC6811_ReadCellVoltages(module, cell_voltages)) { for (uint8_t cell = 0; cell < MAX_CELLS_PER_MODULE; cell++) { g_modules[module].cells[cell].voltage_mv = cell_voltages[cell] / 10; /* 100µV resolution */ /* Check overvoltage/undervoltage */ if (cell_voltages[cell] > CELL_OVERVOLTAGE_MV) { BMS_Fault_SetOvervoltage(module, cell); } if (cell_voltages[cell] < CELL_UNDERVOLTAGE_MV) { BMS_Fault_SetUndervoltage(module, cell); } } g_modules[module].communication_ok = true; } else { g_modules[module].communication_ok = false; BMS_Fault_SetCommunicationLoss(module); } } /* Read temperatures via GPIO (NTC thermistors) */ BMS_LTC6811_ReadGPIO(); } uint16_t BMS_GetMinCellVoltage_mV(void) { uint16_t min_voltage = 0xFFFF; for (uint8_t mod = 0; mod < MAX_MODULES; mod++) { for (uint8_t cell = 0; cell < MAX_CELLS_PER_MODULE; cell++) { if (g_modules[mod].cells[cell].voltage_mv < min_voltage) { min_voltage = g_modules[mod].cells[cell].voltage_mv; } } } return min_voltage; } uint16_t BMS_GetMaxCellVoltage_mV(void) { uint16_t max_voltage = 0; for (uint8_t mod = 0; mod < MAX_MODULES; mod++) { for (uint8_t cell = 0; cell < MAX_CELLS_PER_MODULE; cell++) { if (g_modules[mod].cells[cell].voltage_mv > max_voltage) { max_voltage = g_modules[mod].cells[cell].voltage_mv; } } } return max_voltage; }
c/* bms_soc_estimation.c - State of Charge / State of Health algorithms */ #include "bms_soc_estimation.h" #include <math.h> #define BATTERY_CAPACITY_AH 75.0 #define COULOMB_EFFICIENCY 0.98 /* Charge efficiency */ typedef struct { float soc_percent; /* 0-100% */ float soh_percent; /* 0-100%, degrades over time */ float coulomb_count_ah; /* Accumulated amp-hours */ float ocv_voltage_v; /* Open circuit voltage */ uint32_t cycle_count; /* Full charge/discharge cycles */ float remaining_capacity_ah; } SOCState_t; static SOCState_t g_soc_state = { .soc_percent = 50.0, .soh_percent = 100.0, .coulomb_count_ah = BATTERY_CAPACITY_AH / 2.0, .remaining_capacity_ah = BATTERY_CAPACITY_AH }; /* OCV-SOC lookup table (Open Circuit Voltage to SOC mapping) */ static const struct { float voltage_v; float soc_percent; } OCV_SOC_TABLE[] = { {3.27, 0.0}, {3.61, 10.0}, {3.69, 20.0}, {3.71, 30.0}, {3.73, 40.0}, {3.77, 50.0}, {3.83, 60.0}, {3.92, 70.0}, {4.01, 80.0}, {4.08, 90.0}, {4.20, 100.0} }; void BMS_SOC_Init(void) { /* Load last known SOC from EEPROM */ NvM_ReadBlock(NVM_BLOCK_SOC_STATE, &g_soc_state); /* Initialize Kalman filter for SOC estimation */ BMS_KalmanFilter_Init(); } void BMS_SOC_Update(float current_a, uint32_t delta_time_ms) { /* Coulomb counting: integrate current over time */ float delta_time_h = delta_time_ms / 3600000.0; float delta_ah = current_a * delta_time_h; /* Positive current = discharge, negative = charge */ if (current_a > 0) { g_soc_state.coulomb_count_ah -= delta_ah; } else { g_soc_state.coulomb_count_ah -= delta_ah * COULOMB_EFFICIENCY; } /* Clamp to capacity limits */ if (g_soc_state.coulomb_count_ah < 0) { g_soc_state.coulomb_count_ah = 0; } if (g_soc_state.coulomb_count_ah > g_soc_state.remaining_capacity_ah) { g_soc_state.coulomb_count_ah = g_soc_state.remaining_capacity_ah; } /* Calculate SOC from coulomb count */ g_soc_state.soc_percent = (g_soc_state.coulomb_count_ah / g_soc_state.remaining_capacity_ah) * 100.0; /* Kalman filter fusion with OCV-based SOC (when current near zero) */ if (fabs(current_a) < 1.0) { /* Low current: use OCV */ float pack_voltage_v = BMS_GetPackVoltage() / 1000.0; float avg_cell_voltage_v = pack_voltage_v / TOTAL_CELLS; float ocv_soc = BMS_SOC_LookupOCV(avg_cell_voltage_v); /* Kalman filter update */ g_soc_state.soc_percent = BMS_KalmanFilter_Update( g_soc_state.soc_percent, ocv_soc); } /* Persist SOC every 1% change */ static float last_saved_soc = 0; if (fabs(g_soc_state.soc_percent - last_saved_soc) > 1.0) { NvM_WriteBlock(NVM_BLOCK_SOC_STATE, &g_soc_state); last_saved_soc = g_soc_state.soc_percent; } } float BMS_SOC_LookupOCV(float voltage_v) { /* Linear interpolation in OCV-SOC table */ for (int i = 0; i < (sizeof(OCV_SOC_TABLE) / sizeof(OCV_SOC_TABLE[0])) - 1; i++) { if (voltage_v >= OCV_SOC_TABLE[i].voltage_v && voltage_v <= OCV_SOC_TABLE[i+1].voltage_v) { float v_range = OCV_SOC_TABLE[i+1].voltage_v - OCV_SOC_TABLE[i].voltage_v; float soc_range = OCV_SOC_TABLE[i+1].soc_percent - OCV_SOC_TABLE[i].soc_percent; float v_delta = voltage_v - OCV_SOC_TABLE[i].voltage_v; return OCV_SOC_TABLE[i].soc_percent + (v_delta / v_range) * soc_range; } } return 50.0; /* Default fallback */ } /* SOH estimation based on capacity fade */ void BMS_SOH_Update(void) { /* Detect full charge cycle: SOC goes 100% -> 0% -> 100% */ static bool charging = false; static bool discharged = false; if (g_soc_state.soc_percent > 99.0 && !charging) { charging = true; if (discharged) { /* Full cycle completed */ g_soc_state.cycle_count++; /* Estimate capacity fade: 80% at 1000 cycles (linear model) */ g_soc_state.remaining_capacity_ah = BATTERY_CAPACITY_AH * (1.0 - (g_soc_state.cycle_count / 5000.0)); g_soc_state.soh_percent = (g_soc_state.remaining_capacity_ah / BATTERY_CAPACITY_AH) * 100.0; discharged = false; } } if (g_soc_state.soc_percent < 5.0) { discharged = true; charging = false; } }
c/* bms_cell_balancing.c - Cell voltage equalization */ #include "bms_cell_balancing.h" #define BALANCE_THRESHOLD_MV 10 /* Start balancing if cell delta > 10mV */ #define BALANCE_TARGET_MV 5 /* Stop when delta < 5mV */ #define MAX_BALANCE_CURRENT_MA 200 void BMS_CellBalancing_Update(void) { uint16_t min_voltage = BMS_GetMinCellVoltage_mV(); uint16_t max_voltage = BMS_GetMaxCellVoltage_mV(); if ((max_voltage - min_voltage) < BALANCE_THRESHOLD_MV) { /* Cells well balanced: disable all balancing */ BMS_CellBalancing_DisableAll(); return; } /* Passive balancing: discharge high cells through resistors */ for (uint8_t mod = 0; mod < MAX_MODULES; mod++) { uint16_t balance_mask = 0; for (uint8_t cell = 0; cell < MAX_CELLS_PER_MODULE; cell++) { uint16_t cell_voltage = g_modules[mod].cells[cell].voltage_mv; /* Balance if above minimum + threshold */ if (cell_voltage > (min_voltage + BALANCE_TARGET_MV)) { balance_mask |= (1 << cell); g_modules[mod].cells[cell].balancing_active = true; } else { g_modules[mod].cells[cell].balancing_active = false; } } /* Write balance control register to LTC6811 */ BMS_LTC6811_SetBalancing(mod, balance_mask); } }
c/* bms_contactor_control.c - High-voltage contactor sequencing */ #include "bms_contactor_control.h" #define PRECHARGE_TIMEOUT_MS 5000 #define PRECHARGE_THRESHOLD_PERCENT 95 typedef enum { CONTACTOR_STATE_OPEN = 0, CONTACTOR_STATE_PRECHARGING, CONTACTOR_STATE_CLOSED, CONTACTOR_STATE_FAULT } ContactorState_t; static ContactorState_t g_contactor_state = CONTACTOR_STATE_OPEN; void BMS_Contactor_Close(void) { /* Safety checks before closing */ if (!BMS_Safety_PreCloseCheck()) { g_contactor_state = CONTACTOR_STATE_FAULT; return; } /* Step 1: Close negative contactor */ GPIO_Set(GPIO_CONTACTOR_NEGATIVE, true); OsTask_Sleep(50); /* Step 2: Precharge positive side through resistor */ g_contactor_state = CONTACTOR_STATE_PRECHARGING; GPIO_Set(GPIO_PRECHARGE_RELAY, true); uint32_t start_time = GetSystemTime_ms(); uint16_t pack_voltage_v = BMS_GetPackVoltage(); uint16_t link_voltage_v = ADC_ReadHVLinkVoltage(); /* Wait for DC link to charge to 95% of pack voltage */ while ((GetSystemTime_ms() - start_time) < PRECHARGE_TIMEOUT_MS) { link_voltage_v = ADC_ReadHVLinkVoltage(); if (link_voltage_v > (pack_voltage_v * PRECHARGE_THRESHOLD_PERCENT / 100)) { break; /* Precharge complete */ } OsTask_Sleep(10); } if (link_voltage_v < (pack_voltage_v * PRECHARGE_THRESHOLD_PERCENT / 100)) { /* Precharge timeout: fault */ GPIO_Set(GPIO_PRECHARGE_RELAY, false); GPIO_Set(GPIO_CONTACTOR_NEGATIVE, false); g_contactor_state = CONTACTOR_STATE_FAULT; DTC_SetFault(DTC_PRECHARGE_TIMEOUT); return; } /* Step 3: Close positive contactor */ GPIO_Set(GPIO_CONTACTOR_POSITIVE, true); OsTask_Sleep(50); /* Step 4: Open precharge relay */ GPIO_Set(GPIO_PRECHARGE_RELAY, false); g_contactor_state = CONTACTOR_STATE_CLOSED; } void BMS_Contactor_Open(void) { /* Open positive first, then negative */ GPIO_Set(GPIO_CONTACTOR_POSITIVE, false); OsTask_Sleep(50); GPIO_Set(GPIO_CONTACTOR_NEGATIVE, false); g_contactor_state = CONTACTOR_STATE_OPEN; }
VERSION ""
NS_ :
BS_:
BU_: BMS VCU MCU
/* BMS Battery Status */
BO_ 768 BMS_BatteryStatus: 8 BMS
SG_ BMS_PackVoltage_V : 0|16@1+ (0.1,0) [0|600] "V" VCU,MCU
SG_ BMS_PackCurrent_A : 16|16@1- (0.1,-320) [-320|320] "A" VCU,MCU
SG_ BMS_SOC_percent : 32|8@1+ (0.5,0) [0|100] "%" VCU,MCU
SG_ BMS_SOH_percent : 40|8@1+ (0.5,0) [0|100] "%" VCU
SG_ BMS_MaxCellTemp_C : 48|8@1+ (1,-40) [-40|100] "C" VCU,MCU
SG_ BMS_ContactorState : 56|8@1+ (0,0) [0|3] "" VCU,MCU
VAL_ 768 BMS_ContactorState 0 "Open" 1 "Precharging" 2 "Closed" 3 "Fault";Domain Controller architecture centralizes ECU functions into fewer, more powerful computing platforms: Chassis Domain, Powertrain Domain, Body/Comfort Domain, ADAS Domain, with cross-domain communication via service-oriented architecture (SOA) and resource sharing.
c/* chassis_domain_controller.c - Integrated chassis functions */ #include "chassis_domain.h" /* Consolidated functions: ESC, ABS, TCS, EPS, ADAS braking */ typedef struct { /* Electronic Stability Control */ bool esc_active; float yaw_rate_deg_s; float lateral_acceleration_g; /* Anti-lock Braking System */ uint8_t wheel_speeds_kph[4]; bool abs_active[4]; /* Traction Control System */ bool tcs_active; uint16_t tcs_torque_reduction_nm; /* Electric Power Steering */ float steering_angle_deg; float steering_torque_nm; /* ADAS Braking Interface */ bool adas_brake_request; float adas_decel_mps2; } ChassisDomain_t; static ChassisDomain_t g_chassis = {0}; void ChassisDomain_Main_10ms(void) { /* Read sensors from CAN/FlexRay */ ChassisDomain_ReadSensors(); /* ESC control loop */ ChassisDomain_ESC_Update(); /* ABS control per wheel */ for (int i = 0; i < 4; i++) { ChassisDomain_ABS_UpdateWheel(i); } /* TCS integration with powertrain domain */ if (g_chassis.tcs_active) { /* Send torque reduction request to powertrain domain */ SOMEIP_SendRequest(POWERTRAIN_DOMAIN_SERVICE_ID, METHOD_REDUCE_TORQUE, &g_chassis.tcs_torque_reduction_nm, sizeof(uint16_t)); } /* ADAS brake arbitration */ if (g_chassis.adas_brake_request) { ChassisDomain_ADAS_BrakeControl(); } /* Publish chassis status to Ethernet backbone */ ChassisDomain_PublishStatus(); } void ChassisDomain_ESC_Update(void) { /* Read IMU (gyroscope + accelerometer) */ float yaw_rate = IMU_GetYawRate(); float lat_accel = IMU_GetLateralAcceleration(); /* Calculate desired yaw rate from steering angle */ float desired_yaw = (g_chassis.steering_angle_deg * VCU_GetVehicleSpeed_kph()) / 15.0; /* ESC intervention if yaw error exceeds threshold */ float yaw_error = desired_yaw - yaw_rate; if (fabs(yaw_error) > 5.0) { g_chassis.esc_active = true; /* Apply differential braking to correct yaw */ if (yaw_error > 0) { /* Understeer: brake inside rear wheel */ ChassisDomain_ApplyBrake(WHEEL_RL, 30); } else { /* Oversteer: brake outside front wheel */ ChassisDomain_ApplyBrake(WHEEL_FL, 30); } /* Reduce engine torque */ SOMEIP_SendRequest(POWERTRAIN_DOMAIN_SERVICE_ID, METHOD_REDUCE_TORQUE, &(uint16_t){100}, 2); } else { g_chassis.esc_active = false; } }
c/* powertrain_domain_controller.c - EV powertrain integration */ #include "powertrain_domain.h" /* Consolidated: VCU, BMS, MCU functions */ typedef struct { /* Motor control */ int16_t motor_torque_cmd_nm; uint16_t motor_speed_rpm; float motor_temperature_c; /* Battery management */ float battery_soc_percent; uint16_t battery_voltage_v; float battery_current_a; /* Thermal management */ bool cooling_pump_active; uint8_t radiator_fan_speed_percent; } PowertrainDomain_t; static PowertrainDomain_t g_powertrain = {0}; void PowertrainDomain_Main_10ms(void) { /* Motor control */ PowertrainDomain_MotorControl(); /* Battery management */ PowertrainDomain_BMS_Update(); /* Thermal management */ PowertrainDomain_ThermalControl(); /* Service-oriented communication */ PowertrainDomain_HandleSOARequests(); } void PowertrainDomain_HandleSOARequests(void) { /* Handle SOME/IP service requests from other domains */ SOMEIP_Request_t req; if (SOMEIP_ReceiveRequest(&req)) { switch (req.method_id) { case METHOD_REDUCE_TORQUE: { uint16_t reduction_nm = *(uint16_t*)req.payload; g_powertrain.motor_torque_cmd_nm -= reduction_nm; /* Send response */ SOMEIP_SendResponse(&req, RESPONSE_OK, NULL, 0); break; } case METHOD_GET_SOC: { uint8_t soc = (uint8_t)g_powertrain.battery_soc_percent; SOMEIP_SendResponse(&req, RESPONSE_OK, &soc, 1); break; } case METHOD_SET_CHARGING_LIMIT: { uint8_t limit_percent = *(uint8_t*)req.payload; PowertrainDomain_SetChargingLimit(limit_percent); SOMEIP_SendResponse(&req, RESPONSE_OK, NULL, 0); break; } } } }
c/* body_domain_controller.c - Comfort and convenience functions */ #include "body_domain.h" /* Consolidated: BCM, HVAC, seats, ambient lighting */ typedef struct { /* Climate control */ float cabin_temperature_c; uint8_t hvac_fan_speed; bool ac_compressor_active; /* Lighting */ HeadlightMode_t headlight_mode; uint8_t ambient_light_brightness; /* Seats */ uint8_t driver_seat_heating_level; uint8_t passenger_seat_heating_level; } BodyDomain_t; static BodyDomain_t g_body = {0}; void BodyDomain_Main_50ms(void) { /* Climate control */ BodyDomain_HVAC_Update(); /* Lighting control */ BodyDomain_Lighting_Update(); /* Seat control */ BodyDomain_Seats_Update(); /* User preference synchronization (cloud) */ BodyDomain_SyncUserPreferences(); } void BodyDomain_SyncUserPreferences(void) { /* Load user profile from cloud (via TCU) */ UserProfile_t profile; if (Cloud_GetUserProfile(g_authenticated_user_id, &profile)) { /* Apply preferences */ g_body.driver_seat_heating_level = profile.seat_heat_pref; g_body.ambient_light_brightness = profile.ambient_light_pref; g_body.headlight_mode = profile.headlight_mode_pref; /* Adjust seat position (via LIN to seat ECU) */ LIN_SendSeatPosition(profile.seat_position); } }
c/* adas_domain_controller.c - Perception, planning, control */ #include "adas_domain.h" /* Consolidated: camera, radar, lidar fusion, path planning */ typedef struct { /* Perception */ Object_t detected_objects[32]; uint8_t object_count; /* Localization */ float ego_position_x; float ego_position_y; float ego_heading_deg; /* Path planning */ Trajectory_t planned_path; /* Control */ float target_acceleration_mps2; float target_steering_angle_deg; } ADASDomain_t; static ADASDomain_t g_adas = {0}; void ADASDomain_Main_20ms(void) { /* Sensor fusion */ ADASDomain_SensorFusion(); /* Object detection and tracking */ ADASDomain_ObjectTracking(); /* Path planning */ ADASDomain_PathPlanning(); /* Send control commands to chassis/powertrain domains */ ADASDomain_SendControlCommands(); } void ADASDomain_SendControlCommands(void) { /* Request steering via chassis domain */ SOMEIP_SendRequest(CHASSIS_DOMAIN_SERVICE_ID, METHOD_SET_STEERING_ANGLE, &g_adas.target_steering_angle_deg, sizeof(float)); /* Request acceleration via powertrain domain */ if (g_adas.target_acceleration_mps2 > 0) { /* Acceleration */ int16_t torque_nm = (int16_t)(g_adas.target_acceleration_mps2 * 50); SOMEIP_SendRequest(POWERTRAIN_DOMAIN_SERVICE_ID, METHOD_SET_TORQUE, &torque_nm, sizeof(int16_t)); } else { /* Braking */ float decel_mps2 = -g_adas.target_acceleration_mps2; SOMEIP_SendRequest(CHASSIS_DOMAIN_SERVICE_ID, METHOD_APPLY_BRAKE, &decel_mps2, sizeof(float)); } }
xml<!-- adas_services.arxml --> <AUTOSAR> <AR-PACKAGES> <AR-PACKAGE> <SHORT-NAME>ADAS_Services</SHORT-NAME> <ELEMENTS> <SOMEIP-SERVICE-INTERFACE> <SHORT-NAME>ADAS_Control_Service</SHORT-NAME> <SERVICE-INTERFACE-ID>0x1234</SERVICE-INTERFACE-ID> <MAJOR-VERSION>1</MAJOR-VERSION> <MINOR-VERSION>0</MINOR-VERSION> <METHODS> <SOMEIP-METHOD> <SHORT-NAME>SetSteeringAngle</SHORT-NAME> <METHOD-ID>0x0001</METHOD-ID> <CALL-SEMANTIC>REQUEST-RESPONSE</CALL-SEMANTIC> </SOMEIP-METHOD> <SOMEIP-METHOD> <SHORT-NAME>ApplyBrake</SHORT-NAME> <METHOD-ID>0x0002</METHOD-ID> <CALL-SEMANTIC>REQUEST-RESPONSE</CALL-SEMANTIC> </SOMEIP-METHOD> </METHODS> <EVENTS> <SOMEIP-EVENT> <SHORT-NAME>ObjectDetected</SHORT-NAME> <EVENT-ID>0x8001</EVENT-ID> </SOMEIP-EVENT> </EVENTS> </SOMEIP-SERVICE-INTERFACE> </ELEMENTS> </AR-PACKAGE> </AR-PACKAGES> </AUTOSAR>
c/* domain_communication.c - SOME/IP client/server example */ #include "someip.h" /* Client: ADAS domain requests torque from powertrain domain */ void ADAS_RequestTorque(int16_t torque_nm) { SOMEIP_Message_t msg; msg.service_id = POWERTRAIN_SERVICE_ID; msg.method_id = METHOD_SET_TORQUE; msg.client_id = ADAS_DOMAIN_CLIENT_ID; msg.session_id = GetNextSessionID(); msg.payload_length = sizeof(int16_t); memcpy(msg.payload, &torque_nm, sizeof(int16_t)); SOMEIP_Send(&msg); /* Wait for response */ SOMEIP_Message_t response; if (SOMEIP_WaitForResponse(&response, 100)) { if (response.return_code == SOMEIP_RETURN_OK) { /* Request acknowledged */ } } } /* Server: Powertrain domain handles torque request */ void Powertrain_SOMEIP_Handler(const SOMEIP_Message_t* request) { if (request->method_id == METHOD_SET_TORQUE) { int16_t requested_torque = *(int16_t*)request->payload; /* Apply safety limits */ if (requested_torque > MAX_TORQUE_NM) { requested_torque = MAX_TORQUE_NM; } /* Set motor torque */ VCU_SetMotorTorque(requested_torque); /* Send response */ SOMEIP_Message_t response; response.service_id = request->service_id; response.method_id = request->method_id; response.client_id = request->client_id; response.session_id = request->session_id; response.return_code = SOMEIP_RETURN_OK; response.payload_length = 0; SOMEIP_Send(&response); } }
c/* hypervisor_config.h - QNX Hypervisor partition configuration */ /* Chassis Domain - Guest VM #1 */ #define CHASSIS_DOMAIN_RAM_BASE 0x80000000 #define CHASSIS_DOMAIN_RAM_SIZE 512MB #define CHASSIS_DOMAIN_CPU_MASK 0x03 /* CPU 0-1 */ #define CHASSIS_DOMAIN_PRIORITY CRITICAL /* Powertrain Domain - Guest VM #2 */ #define POWERTRAIN_DOMAIN_RAM_BASE 0xA0000000 #define POWERTRAIN_DOMAIN_RAM_SIZE 512MB #define POWERTRAIN_DOMAIN_CPU_MASK 0x0C /* CPU 2-3 */ #define POWERTRAIN_DOMAIN_PRIORITY CRITICAL /* Body Domain - Guest VM #3 */ #define BODY_DOMAIN_RAM_BASE 0xC0000000 #define BODY_DOMAIN_RAM_SIZE 256MB #define BODY_DOMAIN_CPU_MASK 0x10 /* CPU 4 */ #define BODY_DOMAIN_PRIORITY NORMAL /* ADAS Domain - Guest VM #4 (highest compute) */ #define ADAS_DOMAIN_RAM_BASE 0xD0000000 #define ADAS_DOMAIN_RAM_SIZE 2GB #define ADAS_DOMAIN_CPU_MASK 0xE0 /* CPU 5-7 */ #define ADAS_DOMAIN_PRIORITY HIGH
The In-Vehicle Infotainment (IVI) system manages navigation, multimedia, connectivity (CarPlay/Android Auto), voice assistant, HMI frameworks (Qt/Flutter), and runs on Android Automotive OS, QNX, or Linux platforms.
java// VehicleHalService.java - Android Automotive HAL integration package com.example.ivi; import android.car.Car; import android.car.VehiclePropertyIds; import android.car.hardware.CarPropertyValue; import android.car.hardware.property.CarPropertyManager; public class VehicleHalService { private CarPropertyManager mCarPropertyManager; public void init(Context context) { Car car = Car.createCar(context); mCarPropertyManager = (CarPropertyManager) car.getCarManager(Car.PROPERTY_SERVICE); // Subscribe to vehicle speed updates mCarPropertyManager.registerCallback( new CarPropertyManager.CarPropertyEventCallback() { @Override public void onChangeEvent(CarPropertyValue value) { if (value.getPropertyId() == VehiclePropertyIds.PERF_VEHICLE_SPEED) { float speedMs = (Float) value.getValue(); updateSpeedUI(speedMs * 3.6f); // Convert to km/h } } @Override public void onErrorEvent(int propId, int zone) { Log.e("VehicleHal", "Property error: " + propId); } }, VehiclePropertyIds.PERF_VEHICLE_SPEED, CarPropertyManager.SENSOR_RATE_NORMAL); } public void setHvacTemperature(float tempCelsius) { mCarPropertyManager.setFloatProperty( VehiclePropertyIds.HVAC_TEMPERATURE_SET, VehicleAreaType.VEHICLE_AREA_TYPE_SEAT, tempCelsius); } }
c/* qnx_ivi_service.c - QNX CAR platform integration */ #include <qnxcar/carcontrol.h> #include <screen/screen.h> void IVI_QNX_Init(void) { /* Initialize QNX CAR framework */ car_control_t *control = car_control_create(); /* Register for CAN message callbacks */ car_control_set_can_callback(control, IVI_CAN_MessageHandler); /* Initialize Screen Graphics Subsystem */ screen_context_t screen_ctx; screen_create_context(&screen_ctx, SCREEN_APPLICATION_CONTEXT); /* Create display window */ screen_window_t window; screen_create_window(&window, screen_ctx); screen_set_window_property_iv(window, SCREEN_PROPERTY_SIZE, (int[]){1920, 1080}); } void IVI_CAN_MessageHandler(car_can_message_t *msg) { if (msg->id == 0x100) { /* VCU Motor Command */ uint16_t torque = (msg->data[0] << 8) | msg->data[1]; IVI_UpdatePowerMeter(torque); } }
kotlin// NavigationService.kt - HERE SDK integration package com.example.ivi.navigation import com.here.sdk.core.GeoCoordinates import com.here.sdk.routing.CalculateRouteCallback import com.here.sdk.routing.Route import com.here.sdk.routing.RoutingEngine class NavigationService { private lateinit var routingEngine: RoutingEngine fun initialize() { routingEngine = RoutingEngine() } fun calculateRoute( origin: GeoCoordinates, destination: GeoCoordinates, callback: (Route?) -> Unit ) { val waypoints = listOf( Waypoint(origin), Waypoint(destination) ) val carOptions = CarOptions().apply { routeOptions.alternatives = 3 avoidanceOptions.avoidTollRoads = false optimizationMode = OptimizationMode.FASTEST } routingEngine.calculateRoute(waypoints, carOptions) { routingError, routes -> if (routingError == null && routes?.isNotEmpty() == true) { callback(routes[0]) } else { callback(null) } } } }
java// AndroidAutoService.java - Android Auto projection package com.example.ivi.projection; import android.content.Intent; import com.google.android.apps.auto.sdk.CarActivity; public class AndroidAutoService extends CarActivity { @Override public void onCreate() { super.onCreate(); // Start Android Auto projection Intent intent = new Intent("com.google.android.gms.car.PROJECTION_SERVICE"); startService(intent); } @Override public void onCarConnectionStateChanged(int state) { if (state == CarConnection.STATE_CONNECTED) { // Phone connected: mirror Android Auto UI enableProjectionMode(); } } }
python# voice_assistant.py - Voice command handler import speech_recognition as sr import pyttsx3 class VoiceAssistant: def __init__(self): self.recognizer = sr.Recognizer() self.tts = pyttsx3.init() def listen_for_command(self): with sr.Microphone() as source: print("Listening...") audio = self.recognizer.listen(source) try: command = self.recognizer.recognize_google(audio) self.process_command(command) except sr.UnknownValueError: self.speak("Sorry, I didn't understand that.") def process_command(self, command): if "navigate to" in command.lower(): destination = command.lower().replace("navigate to", "").strip() self.navigate(destination) elif "set temperature" in command.lower(): temp = int(command.split()[-1]) self.set_hvac_temperature(temp) def speak(self, text): self.tts.say(text) self.tts.runAndWait()
qml/* DashboardView.qml - Main instrument cluster */ import QtQuick 2.15 import QtQuick.Controls 2.15 Rectangle { width: 1920 height: 720 // Speedometer Item { id: speedometer x: 100 y: 100 Canvas { id: speedArc width: 400 height: 400 onPaint: { var ctx = getContext("2d"); ctx.clearRect(0, 0, width, height); // Draw arc for speed (0-240 km/h) ctx.beginPath(); ctx.arc(200, 200, 150, 0.75 * Math.PI, (0.75 + 1.5 * (vehicleSpeed / 240)) * Math.PI); ctx.lineWidth = 20; ctx.strokeStyle = "#00FF00"; ctx.stroke(); } Connections { target: vehicleData onSpeedChanged: speedArc.requestPaint() } } Text { text: vehicleData.speed + " km/h" font.pixelSize: 48 color: "white" anchors.centerIn: parent } } // Battery SOC gauge Rectangle { x: 600 y: 100 width: 300 height: 50 color: "transparent" border.color: "white" Rectangle { width: parent.width * (vehicleData.batterySOC / 100) height: parent.height color: vehicleData.batterySOC > 20 ? "#00FF00" : "#FF0000" } Text { text: vehicleData.batterySOC + "%" color: "white" anchors.centerIn: parent } } }
VERSION ""
NS_ :
BS_:
BU_: IVI VCU BCM BMS
/* IVI User Commands */
BO_ 1024 IVI_UserCommand: 8 IVI
SG_ IVI_DriveMode : 0|8@1+ (0,0) [0|3] "" VCU
SG_ IVI_ACTempSet : 8|8@1+ (0.5,10) [10|35] "C" BCM
SG_ IVI_ACFanSpeed : 16|8@1+ (0,0) [0|7] "" BCM
VAL_ 1024 IVI_DriveMode 0 "Eco" 1 "Normal" 2 "Sport" 3 "Custom";The Power Distribution Unit (PDU) manages high-voltage DC/DC converters, low-voltage power distribution, fuse/relay control, load shedding, power budgeting, battery voltage monitoring, and wake-up source management.
c/* pdu_hv_dcdc.c - High-voltage to 12V DC/DC conversion */ #include "pdu_hv_dcdc.h" #define HV_INPUT_MIN_V 200 #define HV_INPUT_MAX_V 450 #define LV_OUTPUT_TARGET_V 14.0 #define MAX_OUTPUT_CURRENT_A 150 typedef struct { uint16_t hv_input_voltage_v; float lv_output_voltage_v; float output_current_a; float efficiency_percent; bool enabled; bool fault_active; } DCDC_State_t; static DCDC_State_t g_dcdc = {0}; void PDU_DCDC_Init(void) { /* Configure PWM for DC/DC converter control */ PWM_Init(PWM_CHANNEL_DCDC, 100000); /* 100 kHz switching */ /* Set initial duty cycle to 0 */ PWM_SetDutyCycle(PWM_CHANNEL_DCDC, 0); g_dcdc.enabled = false; } void PDU_DCDC_Enable(void) { /* Safety checks */ g_dcdc.hv_input_voltage_v = ADC_ReadHVInput(); if (g_dcdc.hv_input_voltage_v < HV_INPUT_MIN_V || g_dcdc.hv_input_voltage_v > HV_INPUT_MAX_V) { g_dcdc.fault_active = true; return; } /* Enable DC/DC converter */ GPIO_Set(GPIO_DCDC_ENABLE, true); g_dcdc.enabled = true; /* Start voltage regulation loop */ PDU_DCDC_RegulationLoop(); } void PDU_DCDC_RegulationLoop(void) { /* PI controller for output voltage regulation */ static float integral = 0; const float Kp = 0.5; const float Ki = 0.1; while (g_dcdc.enabled) { /* Read output voltage and current */ g_dcdc.lv_output_voltage_v = ADC_ReadLVOutput(); g_dcdc.output_current_a = ADC_ReadOutputCurrent(); /* Calculate error */ float error = LV_OUTPUT_TARGET_V - g_dcdc.lv_output_voltage_v; /* PI control */ integral += error * 0.01; /* 10ms loop time */ float duty_cycle = (Kp * error) + (Ki * integral); /* Clamp duty cycle */ if (duty_cycle > 95) duty_cycle = 95; if (duty_cycle < 5) duty_cycle = 5; PWM_SetDutyCycle(PWM_CHANNEL_DCDC, duty_cycle); /* Overcurrent protection */ if (g_dcdc.output_current_a > MAX_OUTPUT_CURRENT_A) { PDU_DCDC_Disable(); g_dcdc.fault_active = true; DTC_SetFault(DTC_DCDC_OVERCURRENT); break; } OsTask_Sleep(10); } } void PDU_DCDC_Disable(void) { PWM_SetDutyCycle(PWM_CHANNEL_DCDC, 0); GPIO_Set(GPIO_DCDC_ENABLE, false); g_dcdc.enabled = false; }
c/* pdu_lv_distribution.c - 12V power distribution and load management */ #include "pdu_lv_distribution.h" #define MAX_POWER_CHANNELS 16 typedef enum { LOAD_PRIORITY_CRITICAL = 0, /* Safety: always on */ LOAD_PRIORITY_HIGH, /* Powertrain */ LOAD_PRIORITY_MEDIUM, /* Comfort */ LOAD_PRIORITY_LOW /* Infotainment */ } LoadPriority_t; typedef struct { const char* name; uint8_t relay_pin; uint8_t current_sense_adc; float max_current_a; LoadPriority_t priority; bool enabled; float measured_current_a; } PowerChannel_t; static PowerChannel_t g_power_channels[MAX_POWER_CHANNELS] = { {"BCM", GPIO_RELAY_BCM, ADC_CH_BCM_CURRENT, 15.0, LOAD_PRIORITY_CRITICAL, true, 0}, {"VCU", GPIO_RELAY_VCU, ADC_CH_VCU_CURRENT, 10.0, LOAD_PRIORITY_CRITICAL, true, 0}, {"BMS", GPIO_RELAY_BMS, ADC_CH_BMS_CURRENT, 8.0, LOAD_PRIORITY_CRITICAL, true, 0}, {"MCU", GPIO_RELAY_MCU, ADC_CH_MCU_CURRENT, 12.0, LOAD_PRIORITY_HIGH, true, 0}, {"IVI", GPIO_RELAY_IVI, ADC_CH_IVI_CURRENT, 20.0, LOAD_PRIORITY_LOW, true, 0}, {"HVAC", GPIO_RELAY_HVAC, ADC_CH_HVAC_CURRENT, 25.0, LOAD_PRIORITY_MEDIUM, true, 0}, {"Headlights", GPIO_RELAY_LIGHTS, ADC_CH_LIGHTS_CURRENT, 10.0, LOAD_PRIORITY_HIGH, false, 0}, {"USB_Ports", GPIO_RELAY_USB, ADC_CH_USB_CURRENT, 5.0, LOAD_PRIORITY_LOW, false, 0} }; void PDU_LV_Init(void) { /* Initialize all relay control pins */ for (int i = 0; i < MAX_POWER_CHANNELS; i++) { GPIO_ConfigOutput(g_power_channels[i].relay_pin); /* Enable critical and high priority loads by default */ if (g_power_channels[i].priority <= LOAD_PRIORITY_HIGH) { PDU_LV_EnableChannel(i); } } } void PDU_LV_EnableChannel(uint8_t channel_id) { if (channel_id >= MAX_POWER_CHANNELS) return; GPIO_Set(g_power_channels[channel_id].relay_pin, true); g_power_channels[channel_id].enabled = true; } void PDU_LV_DisableChannel(uint8_t channel_id) { if (channel_id >= MAX_POWER_CHANNELS) return; GPIO_Set(g_power_channels[channel_id].relay_pin, false); g_power_channels[channel_id].enabled = false; } /* Monitor current and detect overcurrent faults */ void PDU_LV_MonitorCurrents(void) { for (int i = 0; i < MAX_POWER_CHANNELS; i++) { if (!g_power_channels[i].enabled) continue; /* Read current sensor (Hall effect sensor, 185mV/A) */ uint16_t adc_value = ADC_Read(g_power_channels[i].current_sense_adc); float voltage_mv = (adc_value * 5000.0) / 4096.0; g_power_channels[i].measured_current_a = (voltage_mv - 2500.0) / 185.0; /* Check for overcurrent */ if (g_power_channels[i].measured_current_a > g_power_channels[i].max_current_a) { /* Overcurrent detected: disable channel */ PDU_LV_DisableChannel(i); DTC_SetFault(DTC_OVERCURRENT_BASE + i); /* Log event */ Log("Overcurrent on %s: %.2f A (max %.2f A)", g_power_channels[i].name, g_power_channels[i].measured_current_a, g_power_channels[i].max_current_a); } } }
c/* pdu_load_shedding.c - Intelligent load management under power constraints */ #include "pdu_load_shedding.h" #define BATTERY_CRITICAL_VOLTAGE_V 11.0 #define BATTERY_LOW_VOLTAGE_V 11.5 void PDU_LoadShedding_Update(void) { float battery_voltage = ADC_ReadBatteryVoltage(); float total_current = 0; /* Calculate total current draw */ for (int i = 0; i < MAX_POWER_CHANNELS; i++) { if (g_power_channels[i].enabled) { total_current += g_power_channels[i].measured_current_a; } } /* Check if battery voltage is low */ if (battery_voltage < BATTERY_CRITICAL_VOLTAGE_V) { /* Critical: shed all non-critical loads */ for (int i = 0; i < MAX_POWER_CHANNELS; i++) { if (g_power_channels[i].priority > LOAD_PRIORITY_CRITICAL) { PDU_LV_DisableChannel(i); } } Log("Critical battery voltage: %.2f V - load shedding active", battery_voltage); } else if (battery_voltage < BATTERY_LOW_VOLTAGE_V) { /* Low: shed low-priority loads */ for (int i = 0; i < MAX_POWER_CHANNELS; i++) { if (g_power_channels[i].priority >= LOAD_PRIORITY_LOW) { PDU_LV_DisableChannel(i); } } Log("Low battery voltage: %.2f V - reducing load", battery_voltage); } /* Check DC/DC converter output current limit */ if (total_current > (MAX_OUTPUT_CURRENT_A * 0.9)) { /* Approaching limit: shed lowest priority loads */ for (int i = MAX_POWER_CHANNELS - 1; i >= 0; i--) { if (g_power_channels[i].priority == LOAD_PRIORITY_LOW && g_power_channels[i].enabled) { PDU_LV_DisableChannel(i); /* Recalculate total current */ total_current -= g_power_channels[i].measured_current_a; if (total_current < (MAX_OUTPUT_CURRENT_A * 0.85)) { break; /* Sufficient headroom */ } } } } }
c/* pdu_wakeup_sources.c - Network wake-up coordination */ #include "pdu_wakeup_sources.h" #define WAKEUP_CAN_TIMEOUT_MS 100 #define SLEEP_DELAY_MS 5000 typedef enum { WAKEUP_SOURCE_CAN = 0, WAKEUP_SOURCE_LIN, WAKEUP_SOURCE_IGNITION, WAKEUP_SOURCE_DOOR, WAKEUP_SOURCE_TIMER, WAKEUP_SOURCE_COUNT } WakeupSource_t; static bool g_wakeup_pending[WAKEUP_SOURCE_COUNT] = {false}; void PDU_Wakeup_OnCANActivity(void) { g_wakeup_pending[WAKEUP_SOURCE_CAN] = true; /* Power up CAN transceivers */ GPIO_Set(GPIO_CAN_POWERTRAIN_ENABLE, true); GPIO_Set(GPIO_CAN_CHASSIS_ENABLE, true); /* Notify ECUs of wake-up */ CAN_SendWakeupNotification(); } void PDU_Sleep_Prepare(void) { /* Wait for all ECUs to enter sleep */ uint32_t start_time = GetSystemTime_ms(); while ((GetSystemTime_ms() - start_time) < SLEEP_DELAY_MS) { /* Check for wake-up requests */ for (int i = 0; i < WAKEUP_SOURCE_COUNT; i++) { if (g_wakeup_pending[i]) { /* Wake-up requested: abort sleep */ return; } } OsTask_Sleep(10); } /* Enter sleep mode */ PDU_EnterSleep(); } void PDU_EnterSleep(void) { /* Disable non-critical power channels */ for (int i = 0; i < MAX_POWER_CHANNELS; i++) { if (g_power_channels[i].priority > LOAD_PRIORITY_CRITICAL) { PDU_LV_DisableChannel(i); } } /* Configure wake-up sources */ CAN_ConfigureWakeup(CAN_WAKEUP_ENABLED); GPIO_ConfigureWakeup(GPIO_IGNITION, GPIO_WAKEUP_RISING_EDGE); /* Enter low-power mode */ Mcu_SetMode(MCU_MODE_SLEEP); }
VERSION ""
NS_ :
BS_:
BU_: PDU VCU BMS BCM
/* PDU Power Status */
BO_ 896 PDU_PowerStatus: 8 PDU
SG_ PDU_BatteryVoltage_V : 0|16@1+ (0.01,0) [0|16] "V" VCU,BMS
SG_ PDU_DCDCOutputCurrent_A : 16|16@1+ (0.1,0) [0|200] "A" VCU
SG_ PDU_LoadSheddingActive : 32|1@1+ (0,0) [0|1] "" VCU,BCM
SG_ PDU_PowerChannelStatus : 40|16@1+ (0,0) [0|65535] "" VCU
/* Each bit in PowerChannelStatus represents one load (0=off, 1=on) */The Telematics Control Unit (TCU) provides 4G/5G cellular connectivity, GNSS positioning, remote diagnostics, OTA updates, eCall/bCall emergency services, and fleet management integration. This skill covers production-ready TCU development with modem integration.
c/* tcu_modem_manager.c - Cellular modem control (Quectel/Sierra Wireless) */ #include "tcu_modem_manager.h" #include <string.h> #include <stdio.h> #define MODEM_UART_PORT "/dev/ttyUSB2" #define MODEM_BAUD_RATE 115200 #define AT_COMMAND_TIMEOUT_MS 5000 #define MAX_AT_RESPONSE_LENGTH 512 typedef enum { MODEM_STATE_OFF = 0, MODEM_STATE_INITIALIZING, MODEM_STATE_REGISTERING, MODEM_STATE_CONNECTED, MODEM_STATE_ERROR } ModemState_t; typedef struct { int uart_fd; ModemState_t state; char imei[16]; char iccid[21]; int signal_strength_dbm; char network_operator[32]; char ip_address[16]; bool data_session_active; } ModemContext_t; static ModemContext_t g_modem = {0}; /* AT command send/receive */ bool TCU_Modem_SendATCommand(const char* cmd, char* response, uint16_t response_size) { /* Send AT command */ char cmd_buffer[128]; snprintf(cmd_buffer, sizeof(cmd_buffer), "%s\r\n", cmd); int bytes_written = write(g_modem.uart_fd, cmd_buffer, strlen(cmd_buffer)); if (bytes_written < 0) { return false; } /* Wait for response */ uint32_t start_time = GetSystemTime_ms(); int total_bytes = 0; while ((GetSystemTime_ms() - start_time) < AT_COMMAND_TIMEOUT_MS) { int bytes_available = 0; ioctl(g_modem.uart_fd, FIONREAD, &bytes_available); if (bytes_available > 0) { int bytes_read = read(g_modem.uart_fd, &response[total_bytes], response_size - total_bytes - 1); if (bytes_read > 0) { total_bytes += bytes_read; response[total_bytes] = '\0'; /* Check for "OK" or "ERROR" */ if (strstr(response, "OK\r\n") != NULL) { return true; } if (strstr(response, "ERROR\r\n") != NULL) { return false; } } } usleep(10000); /* 10ms polling interval */ } return false; /* Timeout */ } void TCU_Modem_Init(void) { /* Open UART port */ g_modem.uart_fd = open(MODEM_UART_PORT, O_RDWR | O_NOCTTY); if (g_modem.uart_fd < 0) { g_modem.state = MODEM_STATE_ERROR; return; } /* Configure UART: 115200 8N1 */ struct termios tty; tcgetattr(g_modem.uart_fd, &tty); cfsetospeed(&tty, B115200); cfsetispeed(&tty, B115200); tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8; tty.c_cflag &= ~PARENB; tty.c_cflag &= ~CSTOPB; tcsetattr(g_modem.uart_fd, TCSANOW, &tty); g_modem.state = MODEM_STATE_INITIALIZING; char response[MAX_AT_RESPONSE_LENGTH]; /* Basic AT command check */ if (!TCU_Modem_SendATCommand("AT", response, sizeof(response))) { g_modem.state = MODEM_STATE_ERROR; return; } /* Disable echo */ TCU_Modem_SendATCommand("ATE0", response, sizeof(response)); /* Get IMEI */ if (TCU_Modem_SendATCommand("AT+GSN", response, sizeof(response))) { sscanf(response, "%15s", g_modem.imei); } /* Get ICCID (SIM card ID) */ if (TCU_Modem_SendATCommand("AT+CCID", response, sizeof(response))) { sscanf(response, "+CCID: %20s", g_modem.iccid); } /* Check SIM status */ if (!TCU_Modem_SendATCommand("AT+CPIN?", response, sizeof(response))) { g_modem.state = MODEM_STATE_ERROR; return; } /* Start network registration */ TCU_Modem_StartNetworkRegistration(); } void TCU_Modem_StartNetworkRegistration(void) { char response[MAX_AT_RESPONSE_LENGTH]; /* Set network mode: LTE only for 4G, NR+LTE for 5G */ TCU_Modem_SendATCommand("AT+QCFG=\"nwscanmode\",3", response, sizeof(response)); /* Enable network registration URC */ TCU_Modem_SendATCommand("AT+CREG=2", response, sizeof(response)); /* Check registration status */ if (TCU_Modem_SendATCommand("AT+CREG?", response, sizeof(response))) { int n, stat; if (sscanf(response, "+CREG: %d,%d", &n, &stat) == 2) { if (stat == 1 || stat == 5) { /* Registered (home or roaming) */ g_modem.state = MODEM_STATE_REGISTERED; TCU_Modem_GetNetworkInfo(); } else { g_modem.state = MODEM_STATE_REGISTERING; } } } } void TCU_Modem_GetNetworkInfo(void) { char response[MAX_AT_RESPONSE_LENGTH]; /* Get signal strength */ if (TCU_Modem_SendATCommand("AT+CSQ", response, sizeof(response))) { int rssi, ber; if (sscanf(response, "+CSQ: %d,%d", &rssi, &ber) == 2) { /* Convert RSSI to dBm: dBm = -113 + 2*rssi */ g_modem.signal_strength_dbm = -113 + (2 * rssi); } } /* Get operator name */ if (TCU_Modem_SendATCommand("AT+COPS?", response, sizeof(response))) { char operator_name[32]; if (sscanf(response, "+COPS: 0,0,\"%31[^\"]\"", operator_name) == 1) { strncpy(g_modem.network_operator, operator_name, sizeof(g_modem.network_operator)); } } } bool TCU_Modem_StartDataSession(const char* apn) { char response[MAX_AT_RESPONSE_LENGTH]; char cmd[128]; /* Configure PDP context */ snprintf(cmd, sizeof(cmd), "AT+QICSGP=1,1,\"%s\",\"\",\"\",1", apn); if (!TCU_Modem_SendATCommand(cmd, response, sizeof(response))) { return false; } /* Activate PDP context */ if (!TCU_Modem_SendATCommand("AT+QIACT=1", response, sizeof(response))) { return false; } /* Get IP address */ if (TCU_Modem_SendATCommand("AT+QIACT?", response, sizeof(response))) { char ip_addr[16]; if (sscanf(response, "+QIACT: 1,1,1,\"%15[^\"]\"", ip_addr) == 1) { strncpy(g_modem.ip_address, ip_addr, sizeof(g_modem.ip_address)); g_modem.data_session_active = true; g_modem.state = MODEM_STATE_CONNECTED; return true; } } return false; } /* HTTP client for cloud connectivity */ bool TCU_Modem_HTTPPost(const char* url, const char* json_payload, char* response) { char cmd[256]; char at_response[MAX_AT_RESPONSE_LENGTH]; /* Configure HTTP context */ snprintf(cmd, sizeof(cmd), "AT+QHTTPCFG=\"contextid\",1"); TCU_Modem_SendATCommand(cmd, at_response, sizeof(at_response)); /* Set URL */ snprintf(cmd, sizeof(cmd), "AT+QHTTPURL=%zu,80", strlen(url)); TCU_Modem_SendATCommand(cmd, at_response, sizeof(at_response)); /* Send URL */ write(g_modem.uart_fd, url, strlen(url)); usleep(100000); /* POST data */ snprintf(cmd, sizeof(cmd), "AT+QHTTPPOST=%zu,80,80", strlen(json_payload)); TCU_Modem_SendATCommand(cmd, at_response, sizeof(at_response)); /* Send payload */ write(g_modem.uart_fd, json_payload, strlen(json_payload)); /* Wait for response */ sleep(2); /* Read response */ TCU_Modem_SendATCommand("AT+QHTTPREAD=80", response, MAX_AT_RESPONSE_LENGTH); return true; }
c/* tcu_gnss_manager.c - GPS/GLONASS/BeiDou positioning */ #include "tcu_gnss_manager.h" #include <math.h> #define EARTH_RADIUS_KM 6371.0 typedef struct { double latitude; double longitude; float altitude_m; float speed_kph; float heading_deg; uint8_t satellites_used; float hdop; /* Horizontal dilution of precision */ bool fix_valid; uint32_t timestamp_ms; } GNSSPosition_t; static GNSSPosition_t g_gnss_position = {0}; void TCU_GNSS_Init(void) { char response[MAX_AT_RESPONSE_LENGTH]; /* Enable GNSS */ TCU_Modem_SendATCommand("AT+QGPS=1", response, sizeof(response)); /* Configure GNSS to use GPS+GLONASS+BeiDou */ TCU_Modem_SendATCommand("AT+QGPSCFG=\"gnssconfig\",7", response, sizeof(response)); } bool TCU_GNSS_GetPosition(GNSSPosition_t* position) { char response[MAX_AT_RESPONSE_LENGTH]; /* Query GNSS position */ if (!TCU_Modem_SendATCommand("AT+QGPSLOC=2", response, sizeof(response))) { return false; } /* Parse NMEA-like response: +QGPSLOC: <time>,<lat>,<lon>,<hdop>,<alt>,<fix>,<cog>,<spkm>,<spkn>,<date>,<nsat> */ char time_str[16], date_str[16]; int fix_type, nsat; int parsed = sscanf(response, "+QGPSLOC: %15[^,],%lf,%lf,%f,%f,%d,%f,%f,%*f,%15[^,],%d", time_str, &position->latitude, &position->longitude, &position->hdop, &position->altitude_m, &fix_type, &position->heading_deg, &position->speed_kph, date_str, &nsat); if (parsed >= 9) { position->satellites_used = nsat; position->fix_valid = (fix_type >= 2); /* 2D or 3D fix */ position->timestamp_ms = GetSystemTime_ms(); /* Update global position */ memcpy(&g_gnss_position, position, sizeof(GNSSPosition_t)); return true; } return false; } /* Calculate distance between two GPS coordinates (Haversine formula) */ float TCU_GNSS_CalculateDistance_km(double lat1, double lon1, double lat2, double lon2) { double dLat = (lat2 - lat1) * M_PI / 180.0; double dLon = (lon2 - lon1) * M_PI / 180.0; lat1 = lat1 * M_PI / 180.0; lat2 = lat2 * M_PI / 180.0; double a = sin(dLat / 2) * sin(dLat / 2) + sin(dLon / 2) * sin(dLon / 2) * cos(lat1) * cos(lat2); double c = 2 * atan2(sqrt(a), sqrt(1 - a)); return EARTH_RADIUS_KM * c; } /* Geofencing: check if vehicle is inside defined boundary */ bool TCU_GNSS_IsInsideGeofence(double center_lat, double center_lon, float radius_km) { if (!g_gnss_position.fix_valid) { return false; } float distance = TCU_GNSS_CalculateDistance_km( g_gnss_position.latitude, g_gnss_position.longitude, center_lat, center_lon); return (distance <= radius_km); }
c/* tcu_remote_diagnostics.c - Cloud-based diagnostic services */ #include "tcu_remote_diagnostics.h" #include "json.h" #define CLOUD_DIAGNOSTICS_URL "https://telemetry.example.com/api/v1/diagnostics" typedef struct { uint32_t dtc_code; uint8_t status; uint32_t occurrence_count; uint32_t first_occurrence_timestamp; } DTC_Entry_t; void TCU_RemoteDiagnostics_SendDTCs(void) { /* Read DTCs from all ECUs via CAN */ DTC_Entry_t dtc_list[32]; uint8_t dtc_count = 0; /* Query VCU for DTCs */ uint8_t uds_request[] = {0x19, 0x02, 0xFF}; /* ReadDTCByStatusMask */ uint8_t uds_response[256]; uint16_t response_length; if (UDS_SendRequest(VCU_DIAGNOSTIC_ADDR, uds_request, 3, uds_response, &response_length)) { /* Parse DTC response */ for (int i = 3; i < response_length; i += 4) { dtc_list[dtc_count].dtc_code = (uds_response[i] << 16) | (uds_response[i+1] << 8) | uds_response[i+2]; dtc_list[dtc_count].status = uds_response[i+3]; dtc_count++; } } /* Build JSON payload */ char json_payload[1024]; snprintf(json_payload, sizeof(json_payload), "{" "\"vin\":\"%s\"," "\"timestamp\":%u," "\"dtcs\":[", g_vehicle_vin, GetSystemTime_ms()); for (int i = 0; i < dtc_count; i++) { char dtc_entry[128]; snprintf(dtc_entry, sizeof(dtc_entry), "{\"code\":\"0x%06X\",\"status\":0x%02X}%s", dtc_list[i].dtc_code, dtc_list[i].status, (i < dtc_count - 1) ? "," : ""); strcat(json_payload, dtc_entry); } strcat(json_payload, "]}"); /* Send to cloud via HTTPS */ char response[512]; TCU_Modem_HTTPPost(CLOUD_DIAGNOSTICS_URL, json_payload, response); } void TCU_RemoteDiagnostics_ExecuteCommand(const char* command_json) { /* Parse remote diagnostic command */ json_object* root = json_tokener_parse(command_json); json_object* cmd_type_obj = json_object_object_get(root, "command"); const char* cmd_type = json_object_get_string(cmd_type_obj); if (strcmp(cmd_type, "READ_DTC") == 0) { TCU_RemoteDiagnostics_SendDTCs(); } else if (strcmp(cmd_type, "CLEAR_DTC") == 0) { /* Send UDS ClearDiagnosticInformation */ uint8_t uds_clear_dtc[] = {0x14, 0xFF, 0xFF, 0xFF}; uint8_t response[8]; uint16_t response_length; UDS_SendRequest(VCU_DIAGNOSTIC_ADDR, uds_clear_dtc, 4, response, &response_length); } else if (strcmp(cmd_type, "READ_DATA") == 0) { /* Read live data via UDS ReadDataByIdentifier */ json_object* did_obj = json_object_object_get(root, "did"); uint16_t did = json_object_get_int(did_obj); uint8_t uds_read_data[] = {0x22, (did >> 8) & 0xFF, did & 0xFF}; uint8_t response[256]; uint16_t response_length; if (UDS_SendRequest(VCU_DIAGNOSTIC_ADDR, uds_read_data, 3, response, &response_length)) { /* Send response back to cloud */ char response_json[512]; snprintf(response_json, sizeof(response_json), "{\"vin\":\"%s\",\"did\":\"0x%04X\",\"data\":\"", g_vehicle_vin, did); for (int i = 0; i < response_length; i++) { char hex[4]; snprintf(hex, sizeof(hex), "%02X", response[i]); strcat(response_json, hex); } strcat(response_json, "\"}"); TCU_Modem_HTTPPost(CLOUD_DIAGNOSTICS_URL, response_json, NULL); } } json_object_put(root); }
c/* tcu_ecall.c - Automatic emergency call (eCall) - EU regulation */ #include "tcu_ecall.h" #define ECALL_EMERGENCY_NUMBER "112" #define BCALL_ROADSIDE_NUMBER "1234567890" typedef struct { uint8_t msd_format_version; uint8_t message_identifier; uint32_t timestamp; double latitude; double longitude; uint8_t vehicle_class; char vin[18]; uint8_t propulsion_storage_type; bool recent_engine_status; } MSD_t; /* Minimum Set of Data for eCall */ void TCU_eCall_Trigger(bool automatic) { /* Build MSD (Minimum Set of Data) */ MSD_t msd = {0}; msd.msd_format_version = 1; msd.message_identifier = automatic ? 1 : 2; /* 1=automatic, 2=manual */ msd.timestamp = GetSystemTime_ms() / 1000; /* Get current GPS position */ GNSSPosition_t position; if (TCU_GNSS_GetPosition(&position)) { msd.latitude = position.latitude; msd.longitude = position.longitude; } msd.vehicle_class = 1; /* M1 (passenger car) */ strncpy(msd.vin, g_vehicle_vin, sizeof(msd.vin)); msd.propulsion_storage_type = 0x05; /* Electric */ msd.recent_engine_status = VCU_IsVehicleOn(); /* Encode MSD to ASN.1 format */ uint8_t msd_encoded[140]; /* Max 140 bytes for MSD */ uint16_t msd_length = TCU_eCall_EncodeMSD(&msd, msd_encoded); /* Initiate voice call to emergency services */ char at_cmd[64]; char response[MAX_AT_RESPONSE_LENGTH]; snprintf(at_cmd, sizeof(at_cmd), "ATD%s;", ECALL_EMERGENCY_NUMBER); TCU_Modem_SendATCommand(at_cmd, response, sizeof(response)); /* Wait for call connection */ sleep(3); /* Send MSD over in-band modem (IVS - In-Vehicle System) */ TCU_eCall_SendMSDInBand(msd_encoded, msd_length); /* Keep call active for voice communication */ /* Operator will speak with occupants if possible */ } void TCU_bCall_Trigger(void) { /* Breakdown call: non-emergency roadside assistance */ char at_cmd[64]; char response[MAX_AT_RESPONSE_LENGTH]; snprintf(at_cmd, sizeof(at_cmd), "ATD%s;", BCALL_ROADSIDE_NUMBER); TCU_Modem_SendATCommand(at_cmd, response, sizeof(response)); /* Send vehicle data to roadside assistance */ char json_payload[512]; GNSSPosition_t position; TCU_GNSS_GetPosition(&position); snprintf(json_payload, sizeof(json_payload), "{" "\"vin\":\"%s\"," "\"latitude\":%.6f," "\"longitude\":%.6f," "\"issue\":\"Breakdown assistance requested\"" "}", g_vehicle_vin, position.latitude, position.longitude); TCU_Modem_HTTPPost("https://roadside.example.com/api/assist", json_payload, NULL); }
c/* tcu_ota_manager.c - Over-the-Air software updates */ #include "tcu_ota_manager.h" #define OTA_SERVER_URL "https://ota.example.com/api/v1/updates" #define OTA_CHUNK_SIZE 4096 typedef struct { char version[16]; char ecu_target[32]; uint32_t file_size; char download_url[256]; uint8_t sha256_hash[32]; } OTAPackage_t; typedef struct { bool update_available; OTAPackage_t package; uint32_t bytes_downloaded; uint8_t download_progress_percent; bool download_complete; } OTAState_t; static OTAState_t g_ota_state = {0}; bool TCU_OTA_CheckForUpdates(void) { /* Query OTA server for available updates */ char json_request[256]; snprintf(json_request, sizeof(json_request), "{\"vin\":\"%s\",\"current_versions\":{" "\"vcu\":\"1.2.3\",\"bms\":\"2.0.1\",\"mcu\":\"3.1.0\"" "}}", g_vehicle_vin); char response[1024]; if (!TCU_Modem_HTTPPost(OTA_SERVER_URL, json_request, response)) { return false; } /* Parse JSON response */ json_object* root = json_tokener_parse(response); json_object* update_available_obj = json_object_object_get(root, "update_available"); if (json_object_get_boolean(update_available_obj)) { json_object* package_obj = json_object_object_get(root, "package"); /* Extract package info */ json_object* version_obj = json_object_object_get(package_obj, "version"); json_object* ecu_obj = json_object_object_get(package_obj, "ecu"); json_object* size_obj = json_object_object_get(package_obj, "size"); json_object* url_obj = json_object_object_get(package_obj, "url"); strncpy(g_ota_state.package.version, json_object_get_string(version_obj), sizeof(g_ota_state.package.version)); strncpy(g_ota_state.package.ecu_target, json_object_get_string(ecu_obj), sizeof(g_ota_state.package.ecu_target)); g_ota_state.package.file_size = json_object_get_int(size_obj); strncpy(g_ota_state.package.download_url, json_object_get_string(url_obj), sizeof(g_ota_state.package.download_url)); g_ota_state.update_available = true; json_object_put(root); return true; } json_object_put(root); return false; } bool TCU_OTA_DownloadPackage(void) { if (!g_ota_state.update_available) { return false; } /* Open file for writing */ int fd = open("/data/ota/update.bin", O_WRONLY | O_CREAT | O_TRUNC, 0644); if (fd < 0) { return false; } /* Download in chunks */ uint32_t offset = 0; uint8_t buffer[OTA_CHUNK_SIZE]; while (offset < g_ota_state.package.file_size) { uint32_t chunk_size = (g_ota_state.package.file_size - offset) > OTA_CHUNK_SIZE ? OTA_CHUNK_SIZE : (g_ota_state.package.file_size - offset); /* HTTP range request */ char range_header[64]; snprintf(range_header, sizeof(range_header), "Range: bytes=%u-%u", offset, offset + chunk_size - 1); /* Download chunk (simplified - use libcurl in production) */ if (!TCU_HTTP_DownloadChunk(g_ota_state.package.download_url, range_header, buffer, chunk_size)) { close(fd); return false; } /* Write to file */ write(fd, buffer, chunk_size); offset += chunk_size; g_ota_state.bytes_downloaded = offset; g_ota_state.download_progress_percent = (offset * 100) / g_ota_state.package.file_size; /* Notify user via CAN */ CAN_SendOTAProgress(g_ota_state.download_progress_percent); } close(fd); g_ota_state.download_complete = true; /* Verify SHA256 hash */ uint8_t calculated_hash[32]; TCU_OTA_CalculateSHA256("/data/ota/update.bin", calculated_hash); if (memcmp(calculated_hash, g_ota_state.package.sha256_hash, 32) != 0) { /* Hash mismatch: corrupted download */ unlink("/data/ota/update.bin"); return false; } return true; } void TCU_OTA_InstallPackage(void) { /* Flash update to target ECU */ if (strcmp(g_ota_state.package.ecu_target, "VCU") == 0) { /* Flash VCU via UDS RequestDownload / TransferData / RequestTransferExit */ TCU_OTA_FlashECU(VCU_DIAGNOSTIC_ADDR, "/data/ota/update.bin"); } else if (strcmp(g_ota_state.package.ecu_target, "BMS") == 0) { TCU_OTA_FlashECU(BMS_DIAGNOSTIC_ADDR, "/data/ota/update.bin"); } /* Cleanup */ unlink("/data/ota/update.bin"); memset(&g_ota_state, 0, sizeof(OTAState_t)); }
VERSION ""
NS_ :
BS_:
BU_: TCU VCU BCM IVI
/* TCU Status */
BO_ 768 TCU_Status: 8 TCU
SG_ TCU_ModemState : 0|8@1+ (0,0) [0|4] "" VCU,IVI
SG_ TCU_SignalStrength_dBm : 8|8@1- (-113,0) [-113|0] "dBm" IVI
SG_ TCU_DataSessionActive : 16|1@1+ (0,0) [0|1] "" IVI
SG_ TCU_GNSSFixValid : 17|1@1+ (0,0) [0|1] "" VCU,IVI
SG_ TCU_SatellitesUsed : 24|8@1+ (0,0) [0|32] "" IVI
SG_ TCU_OTAUpdateAvailable : 32|1@1+ (0,0) [0|1] "" IVI
SG_ TCU_OTADownloadProgress : 40|8@1+ (0,0) [0|100] "%" IVI
/* TCU GPS Position */
BO_ 769 TCU_Position: 8 TCU
SG_ TCU_Latitude : 0|32@1+ (0.0000001,-90) [-90|90] "deg" VCU,IVI
SG_ TCU_Longitude : 32|32@1+ (0.0000001,-180) [-180|180] "deg" VCU,IVI
/* TCU Speed and Heading */
BO_ 770 TCU_Navigation: 8 TCU
SG_ TCU_GPSSpeed_kph : 0|16@1+ (0.01,0) [0|300] "km/h" VCU,IVI
SG_ TCU_Heading_deg : 16|16@1+ (0.01,0) [0|360] "deg" VCU,IVI
SG_ TCU_Altitude_m : 32|16@1+ (0.1,-500) [-500|9000] "m" IVI
SG_ TCU_HDOP : 48|8@1+ (0.1,0) [0|25] "" IVI
VAL_ 768 TCU_ModemState 0 "Off" 1 "Initializing" 2 "Registering" 3 "Connected" 4 "Error";The Vehicle Control Unit (VCU) is the central brain for electric vehicles, managing torque arbitration, drive modes, power distribution, regenerative braking, and traction control. This skill covers production-ready VCU development with AUTOSAR BSW integration.
c/* vcu_torque_arbiter.c - Multi-source torque request arbitration */ #include "vcu_torque_arbiter.h" #include "autosar_rte.h" #include <stdint.h> #include <stdbool.h> #define MAX_TORQUE_NM 400 #define MIN_REGEN_TORQUE_NM -200 #define TORQUE_RATE_LIMIT_NM_PER_100MS 50 typedef enum { TORQUE_SOURCE_DRIVER = 0, TORQUE_SOURCE_CRUISE_CONTROL, TORQUE_SOURCE_TRACTION_CONTROL, TORQUE_SOURCE_STABILITY_CONTROL, TORQUE_SOURCE_POWER_LIMIT, TORQUE_SOURCE_THERMAL_LIMIT, TORQUE_SOURCE_COUNT } TorqueSource_t; typedef struct { int16_t requested_torque_nm; uint8_t priority; bool active; uint32_t timestamp_ms; } TorqueRequest_t; typedef struct { TorqueRequest_t requests[TORQUE_SOURCE_COUNT]; int16_t arbitrated_torque_nm; int16_t previous_torque_nm; TorqueSource_t active_source; } TorqueArbiter_t; static TorqueArbiter_t g_torque_arbiter = {0}; /* Priority levels (higher number = higher priority) */ static const uint8_t TORQUE_PRIORITIES[TORQUE_SOURCE_COUNT] = { [TORQUE_SOURCE_DRIVER] = 1, [TORQUE_SOURCE_CRUISE_CONTROL] = 2, [TORQUE_SOURCE_TRACTION_CONTROL] = 5, /* Safety critical */ [TORQUE_SOURCE_STABILITY_CONTROL] = 6, /* Highest priority */ [TORQUE_SOURCE_POWER_LIMIT] = 4, [TORQUE_SOURCE_THERMAL_LIMIT] = 3 }; void VCU_TorqueArbiter_Init(void) { memset(&g_torque_arbiter, 0, sizeof(TorqueArbiter_t)); /* Initialize priorities */ for (int i = 0; i < TORQUE_SOURCE_COUNT; i++) { g_torque_arbiter.requests[i].priority = TORQUE_PRIORITIES[i]; } } void VCU_TorqueArbiter_SetRequest(TorqueSource_t source, int16_t torque_nm, bool active) { if (source >= TORQUE_SOURCE_COUNT) return; /* Clamp torque to physical limits */ if (torque_nm > MAX_TORQUE_NM) torque_nm = MAX_TORQUE_NM; if (torque_nm < MIN_REGEN_TORQUE_NM) torque_nm = MIN_REGEN_TORQUE_NM; g_torque_arbiter.requests[source].requested_torque_nm = torque_nm; g_torque_arbiter.requests[source].active = active; g_torque_arbiter.requests[source].timestamp_ms = GetSystemTime_ms(); } int16_t VCU_TorqueArbiter_Arbitrate(void) { int16_t result_torque = 0; uint8_t highest_priority = 0; TorqueSource_t active_source = TORQUE_SOURCE_DRIVER; /* Find highest priority active request */ for (int i = 0; i < TORQUE_SOURCE_COUNT; i++) { if (g_torque_arbiter.requests[i].active && g_torque_arbiter.requests[i].priority > highest_priority) { highest_priority = g_torque_arbiter.requests[i].priority; result_torque = g_torque_arbiter.requests[i].requested_torque_nm; active_source = (TorqueSource_t)i; } } /* Apply rate limiter for smoothness */ int16_t delta = result_torque - g_torque_arbiter.previous_torque_nm; if (delta > TORQUE_RATE_LIMIT_NM_PER_100MS) { result_torque = g_torque_arbiter.previous_torque_nm + TORQUE_RATE_LIMIT_NM_PER_100MS; } else if (delta < -TORQUE_RATE_LIMIT_NM_PER_100MS) { result_torque = g_torque_arbiter.previous_torque_nm - TORQUE_RATE_LIMIT_NM_PER_100MS; } g_torque_arbiter.arbitrated_torque_nm = result_torque; g_torque_arbiter.previous_torque_nm = result_torque; g_torque_arbiter.active_source = active_source; /* Send to motor controller via CAN */ Rte_Write_MotorTorqueCmd_torque(result_torque); return result_torque; }
c/* vcu_drive_modes.c - Drive mode management */ #include "vcu_drive_modes.h" typedef enum { DRIVE_MODE_ECO = 0, DRIVE_MODE_NORMAL, DRIVE_MODE_SPORT, DRIVE_MODE_CUSTOM } DriveMode_t; typedef struct { uint8_t max_power_percent; /* 0-100% */ uint8_t throttle_response; /* 0-100%, sensitivity */ uint8_t regen_strength; /* 0-100%, aggressive regen */ uint8_t ac_power_limit_percent; /* HVAC power limit */ } DriveModeProfile_t; static const DriveModeProfile_t DRIVE_MODE_PROFILES[] = { [DRIVE_MODE_ECO] = { .max_power_percent = 70, .throttle_response = 50, .regen_strength = 80, .ac_power_limit_percent = 50 }, [DRIVE_MODE_NORMAL] = { .max_power_percent = 90, .throttle_response = 70, .regen_strength = 60, .ac_power_limit_percent = 80 }, [DRIVE_MODE_SPORT] = { .max_power_percent = 100, .throttle_response = 100, .regen_strength = 40, .ac_power_limit_percent = 100 }, [DRIVE_MODE_CUSTOM] = { .max_power_percent = 85, .throttle_response = 75, .regen_strength = 65, .ac_power_limit_percent = 75 } }; static DriveMode_t g_active_mode = DRIVE_MODE_NORMAL; static DriveModeProfile_t g_custom_profile; void VCU_DriveMode_Set(DriveMode_t mode) { if (mode >= DRIVE_MODE_CUSTOM) return; g_active_mode = mode; /* Apply profile to power management */ const DriveModeProfile_t* profile = &DRIVE_MODE_PROFILES[mode]; VCU_PowerManagement_SetMaxPower(profile->max_power_percent); VCU_ThrottleMap_SetResponse(profile->throttle_response); VCU_RegenBraking_SetStrength(profile->regen_strength); VCU_HVAC_SetPowerLimit(profile->ac_power_limit_percent); /* Persist to EEPROM */ NvM_WriteBlock(NVM_BLOCK_DRIVE_MODE, &mode); } DriveMode_t VCU_DriveMode_Get(void) { return g_active_mode; } /* Throttle pedal mapping with drive mode response curve */ int16_t VCU_ThrottleMap_ApplyResponse(uint8_t pedal_position_percent) { const DriveModeProfile_t* profile = &DRIVE_MODE_PROFILES[g_active_mode]; /* Non-linear response curve: torque = (pedal^2) * response_factor */ float normalized_pedal = pedal_position_percent / 100.0f; float response_factor = profile->throttle_response / 100.0f; /* Sport mode: more aggressive curve (pedal^1.5) */ /* Eco mode: gentler curve (pedal^2.5) */ float exponent = 2.0f; if (g_active_mode == DRIVE_MODE_SPORT) { exponent = 1.5f; } else if (g_active_mode == DRIVE_MODE_ECO) { exponent = 2.5f; } float torque_factor = powf(normalized_pedal, exponent) * response_factor; int16_t max_torque = (MAX_TORQUE_NM * profile->max_power_percent) / 100; return (int16_t)(torque_factor * max_torque); }
c/* vcu_regen_braking.c - Regenerative braking with blending */ #include "vcu_regen_braking.h" #define MIN_VEHICLE_SPEED_KPH 5 /* Below this, friction only */ #define MAX_REGEN_POWER_KW 80 #define BATTERY_HIGH_SOC_LIMIT 95 /* Reduce regen above 95% SOC */ #define REGEN_BLEND_THRESHOLD_MS 200 /* Blend window for smooth transition */ typedef struct { uint8_t regen_strength_percent; int16_t regen_torque_nm; int16_t friction_brake_request_nm; bool regen_available; uint32_t last_blend_timestamp_ms; } RegenBraking_t; static RegenBraking_t g_regen_state = {0}; bool VCU_Regen_IsAvailable(void) { /* Check conditions for regen availability */ uint8_t battery_soc = BMS_GetSOC_percent(); uint8_t battery_temp_c = BMS_GetTemperature_C(); uint16_t vehicle_speed_kph = VCU_GetVehicleSpeed_kph(); /* No regen if: SOC too high, battery too cold, vehicle too slow */ if (battery_soc > BATTERY_HIGH_SOC_LIMIT) return false; if (battery_temp_c < 0) return false; /* Below 0°C, limit regen */ if (vehicle_speed_kph < MIN_VEHICLE_SPEED_KPH) return false; /* Check motor controller readiness */ if (!MCU_IsRegenReady()) return false; return true; } void VCU_Regen_CalculateBlending(uint8_t brake_pedal_percent, int16_t* regen_torque_out, int16_t* friction_brake_out) { *regen_torque_out = 0; *friction_brake_out = 0; if (!VCU_Regen_IsAvailable()) { /* Full friction braking */ *friction_brake_out = (brake_pedal_percent * MAX_BRAKE_TORQUE_NM) / 100; return; } /* Calculate maximum regen torque based on battery limits */ uint8_t battery_soc = BMS_GetSOC_percent(); float soc_factor = 1.0f; if (battery_soc > 90) { soc_factor = (100.0f - battery_soc) / 10.0f; /* Linear reduction 90-100% */ } int16_t max_regen_torque = (MIN_REGEN_TORQUE_NM * g_regen_state.regen_strength_percent) / 100; max_regen_torque = (int16_t)(max_regen_torque * soc_factor); /* Apply regen based on brake pedal position */ int16_t requested_brake_torque = (brake_pedal_percent * MAX_BRAKE_TORQUE_NM) / 100; if (abs(requested_brake_torque) <= abs(max_regen_torque)) { /* Regen can handle it all */ *regen_torque_out = -requested_brake_torque; /* Negative = regen */ *friction_brake_out = 0; } else { /* Blend regen + friction */ *regen_torque_out = max_regen_torque; *friction_brake_out = requested_brake_torque - abs(max_regen_torque); } g_regen_state.regen_torque_nm = *regen_torque_out; g_regen_state.friction_brake_request_nm = *friction_brake_out; } /* One-pedal driving mode (aggressive regen on throttle release) */ void VCU_Regen_OnePedalMode(uint8_t throttle_percent) { if (throttle_percent > 5) { g_regen_state.regen_torque_nm = 0; return; } /* Throttle released: apply regen proportional to vehicle speed */ uint16_t speed_kph = VCU_GetVehicleSpeed_kph(); float speed_factor = (speed_kph > 100) ? 1.0f : (speed_kph / 100.0f); int16_t one_pedal_regen = (int16_t)(MIN_REGEN_TORQUE_NM * 0.7f * speed_factor); VCU_TorqueArbiter_SetRequest(TORQUE_SOURCE_DRIVER, one_pedal_regen, true); }
c/* vcu_traction_control.c - Wheel slip detection and mitigation */ #include "vcu_traction_control.h" #define WHEEL_SLIP_THRESHOLD_PERCENT 15 /* 15% slip triggers intervention */ #define TC_TORQUE_REDUCTION_STEP_NM 20 #define TC_RECOVERY_RATE_NM_PER_100MS 10 typedef struct { float wheel_speeds_kph[4]; /* FL, FR, RL, RR */ float wheel_slip_percent[4]; bool tc_active; int16_t torque_reduction_nm; } TractionControl_t; static TractionControl_t g_tc_state = {0}; void VCU_TractionControl_Update(void) { /* Read wheel speeds from ABS sensors via CAN */ g_tc_state.wheel_speeds_kph[0] = ABS_GetWheelSpeed_kph(WHEEL_FL); g_tc_state.wheel_speeds_kph[1] = ABS_GetWheelSpeed_kph(WHEEL_FR); g_tc_state.wheel_speeds_kph[2] = ABS_GetWheelSpeed_kph(WHEEL_RL); g_tc_state.wheel_speeds_kph[3] = ABS_GetWheelSpeed_kph(WHEEL_RR); /* Calculate average driven wheel speed (RWD: rear wheels) */ float driven_avg = (g_tc_state.wheel_speeds_kph[2] + g_tc_state.wheel_speeds_kph[3]) / 2.0f; /* Calculate average non-driven wheel speed (reference) */ float reference_avg = (g_tc_state.wheel_speeds_kph[0] + g_tc_state.wheel_speeds_kph[1]) / 2.0f; if (reference_avg < 5.0f) { g_tc_state.tc_active = false; return; /* Vehicle stopped */ } /* Calculate slip percentage */ float slip_percent = ((driven_avg - reference_avg) / reference_avg) * 100.0f; if (slip_percent > WHEEL_SLIP_THRESHOLD_PERCENT) { /* Excessive slip detected: reduce torque */ g_tc_state.tc_active = true; g_tc_state.torque_reduction_nm += TC_TORQUE_REDUCTION_STEP_NM; /* Cap reduction at 80% of requested torque */ int16_t driver_torque = VCU_TorqueArbiter_GetRequest(TORQUE_SOURCE_DRIVER); if (g_tc_state.torque_reduction_nm > driver_torque * 0.8f) { g_tc_state.torque_reduction_nm = (int16_t)(driver_torque * 0.8f); } /* Override driver request */ int16_t limited_torque = driver_torque - g_tc_state.torque_reduction_nm; VCU_TorqueArbiter_SetRequest(TORQUE_SOURCE_TRACTION_CONTROL, limited_torque, true); } else { /* No slip: gradually restore torque */ if (g_tc_state.torque_reduction_nm > 0) { g_tc_state.torque_reduction_nm -= TC_RECOVERY_RATE_NM_PER_100MS; if (g_tc_state.torque_reduction_nm < 0) { g_tc_state.torque_reduction_nm = 0; g_tc_state.tc_active = false; VCU_TorqueArbiter_SetRequest(TORQUE_SOURCE_TRACTION_CONTROL, 0, false); } } } }
xml<!-- vcu_rte_configuration.arxml --> <AUTOSAR xmlns="http://autosar.org/schema/r4.0"> <AR-PACKAGES> <AR-PACKAGE> <SHORT-NAME>VCU_ComponentTypes</SHORT-NAME> <ELEMENTS> <APPLICATION-SW-COMPONENT-TYPE> <SHORT-NAME>VCU_Controller</SHORT-NAME> <PORTS> <!-- Required Ports (inputs) --> <R-PORT-PROTOTYPE> <SHORT-NAME>ThrottlePedal</SHORT-NAME> <REQUIRED-INTERFACE-TREF DEST="SENDER-RECEIVER-INTERFACE"> /Interfaces/ThrottlePedal_IF </REQUIRED-INTERFACE-TREF> </R-PORT-PROTOTYPE> <R-PORT-PROTOTYPE> <SHORT-NAME>BrakePedal</SHORT-NAME> <REQUIRED-INTERFACE-TREF DEST="SENDER-RECEIVER-INTERFACE"> /Interfaces/BrakePedal_IF </REQUIRED-INTERFACE-TREF> </R-PORT-PROTOTYPE> <R-PORT-PROTOTYPE> <SHORT-NAME>BatteryStatus</SHORT-NAME> <REQUIRED-INTERFACE-TREF DEST="SENDER-RECEIVER-INTERFACE"> /Interfaces/BatteryStatus_IF </REQUIRED-INTERFACE-TREF> </R-PORT-PROTOTYPE> <R-PORT-PROTOTYPE> <SHORT-NAME>WheelSpeeds</SHORT-NAME> <REQUIRED-INTERFACE-TREF DEST="SENDER-RECEIVER-INTERFACE"> /Interfaces/WheelSpeeds_IF </REQUIRED-INTERFACE-TREF> </R-PORT-PROTOTYPE> <!-- Provided Ports (outputs) --> <P-PORT-PROTOTYPE> <SHORT-NAME>MotorTorqueCmd</SHORT-NAME> <PROVIDED-INTERFACE-TREF DEST="SENDER-RECEIVER-INTERFACE"> /Interfaces/MotorTorqueCmd_IF </PROVIDED-INTERFACE-TREF> </P-PORT-PROTOTYPE> <P-PORT-PROTOTYPE> <SHORT-NAME>BrakeRequest</SHORT-NAME> <PROVIDED-INTERFACE-TREF DEST="SENDER-RECEIVER-INTERFACE"> /Interfaces/BrakeRequest_IF </PROVIDED-INTERFACE-TREF> </P-PORT-PROTOTYPE> <P-PORT-PROTOTYPE> <SHORT-NAME>VehicleStatus</SHORT-NAME> <PROVIDED-INTERFACE-TREF DEST="SENDER-RECEIVER-INTERFACE"> /Interfaces/VehicleStatus_IF </PROVIDED-INTERFACE-TREF> </P-PORT-PROTOTYPE> </PORTS> <INTERNAL-BEHAVIORS> <SWC-INTERNAL-BEHAVIOR> <SHORT-NAME>VCU_InternalBehavior</SHORT-NAME> <RUNNABLES> <RUNNABLE-ENTITY> <SHORT-NAME>VCU_Main_10ms</SHORT-NAME> <MINIMUM-START-INTERVAL>0.01</MINIMUM-START-INTERVAL> <CAN-BE-INVOKED-CONCURRENTLY>false</CAN-BE-INVOKED-CONCURRENTLY> <SYMBOL>VCU_Main_Runnable</SYMBOL> </RUNNABLE-ENTITY> </RUNNABLES> <EVENTS> <TIMING-EVENT> <SHORT-NAME>TimingEvent_10ms</SHORT-NAME> <START-ON-EVENT-REF DEST="RUNNABLE-ENTITY"> /VCU_ComponentTypes/VCU_Controller/VCU_InternalBehavior/VCU_Main_10ms </START-ON-EVENT-REF> <PERIOD>0.01</PERIOD> </TIMING-EVENT> </EVENTS> </SWC-INTERNAL-BEHAVIOR> </INTERNAL-BEHAVIORS> </APPLICATION-SW-COMPONENT-TYPE> </ELEMENTS> </AR-PACKAGE> </AR-PACKAGES> </AUTOSAR>
VERSION ""
NS_ :
NS_DESC_
CM_
BA_DEF_
BA_
VAL_
CAT_DEF_
CAT_
FILTER
BA_DEF_DEF_
EV_DATA_
ENVVAR_DATA_
SGTYPE_
SGTYPE_VAL_
BA_DEF_SGTYPE_
BA_SGTYPE_
SIG_TYPE_REF_
VAL_TABLE_
SIG_GROUP_
SIG_VALTYPE_
SIGTYPE_VALTYPE_
BO_TX_BU_
BA_DEF_REL_
BA_REL_
BA_SGTYPE_REL_
SG_MUL_VAL_
BS_:
BU_: VCU MCU BMS BCM
/* VCU -> MCU: Motor Torque Command */
BO_ 256 VCU_MotorCmd: 8 VCU
SG_ VCU_TorqueRequest : 0|16@1+ (-2000,0) [-2000|4000] "0.1Nm" MCU
SG_ VCU_SpeedLimit : 16|16@1+ (0,0) [0|18000] "0.1rpm" MCU
SG_ VCU_ControlMode : 32|8@1+ (0,0) [0|3] "" MCU
SG_ VCU_TorqueValid : 40|1@1+ (0,0) [0|1] "" MCU
SG_ VCU_TorqueSource : 41|3@1+ (0,0) [0|7] "" MCU
SG_ VCU_ChecksumTorque : 56|8@1+ (0,0) [0|255] "" MCU
/* VCU -> BCM: Brake Request */
BO_ 257 VCU_BrakeCmd: 8 VCU
SG_ VCU_FrictionBrake_FL : 0|16@1+ (0,0) [0|3000] "0.1Nm" BCM
SG_ VCU_FrictionBrake_FR : 16|16@1+ (0,0) [0|3000] "0.1Nm" BCM
SG_ VCU_FrictionBrake_RL : 32|16@1+ (0,0) [0|3000] "0.1Nm" BCM
SG_ VCU_FrictionBrake_RR : 48|16@1+ (0,0) [0|3000] "0.1Nm" BCM
/* VCU -> CAN Bus: Vehicle Status */
BO_ 258 VCU_VehicleStatus: 8 VCU
SG_ VCU_DriveMode : 0|8@1+ (0,0) [0|3] "" BCM,MCU,BMS
SG_ VCU_TractionControlActive : 8|1@1+ (0,0) [0|1] "" BCM
SG_ VCU_RegenAvailable : 9|1@1+ (0,0) [0|1] "" BCM
SG_ VCU_PowerLimitActive : 10|1@1+ (0,0) [0|1] "" BCM
SG_ VCU_VehicleReady : 11|1@1+ (0,0) [0|1] "" BCM,MCU
SG_ VCU_EstimatedRange_km : 16|16@1+ (0,0) [0|1000] "km" BCM
VAL_ 256 VCU_ControlMode 0 "Torque_Mode" 1 "Speed_Mode" 2 "Power_Mode" 3 "Disabled";
VAL_ 256 VCU_TorqueSource 0 "Driver" 1 "CruiseControl" 2 "TractionControl" 3 "StabilityControl" 4 "PowerLimit" 5 "ThermalLimit";
VAL_ 258 VCU_DriveMode 0 "Eco" 1 "Normal" 2 "Sport" 3 "Custom";c/* vcu_power_distribution.c - Energy management and power budgeting */ #include "vcu_power_management.h" #define BATTERY_MAX_POWER_KW 150 #define HVAC_MAX_POWER_KW 6 #define DCDC_MAX_POWER_KW 3 #define AUXILIARY_MAX_POWER_KW 2 typedef struct { float available_battery_power_kw; float allocated_propulsion_kw; float allocated_hvac_kw; float allocated_auxiliary_kw; bool power_limit_active; } PowerBudget_t; static PowerBudget_t g_power_budget = {0}; void VCU_PowerManagement_Update(void) { /* Get battery discharge limit from BMS */ g_power_budget.available_battery_power_kw = BMS_GetMaxDischargePower_kW(); /* Propulsion has first priority */ float requested_propulsion_kw = VCU_GetRequestedPropulsionPower_kW(); /* HVAC second priority (can be reduced in power-limited situations) */ float requested_hvac_kw = HVAC_GetRequestedPower_kW(); /* Allocate power with priorities */ float total_requested = requested_propulsion_kw + requested_hvac_kw + DCDC_MAX_POWER_KW + AUXILIARY_MAX_POWER_KW; if (total_requested <= g_power_budget.available_battery_power_kw) { /* No power limiting needed */ g_power_budget.allocated_propulsion_kw = requested_propulsion_kw; g_power_budget.allocated_hvac_kw = requested_hvac_kw; g_power_budget.power_limit_active = false; } else { /* Power limiting: reduce HVAC first, then propulsion */ g_power_budget.power_limit_active = true; float available_for_hvac = g_power_budget.available_battery_power_kw - requested_propulsion_kw - DCDC_MAX_POWER_KW - AUXILIARY_MAX_POWER_KW; if (available_for_hvac >= requested_hvac_kw) { /* Can still power HVAC fully, limit propulsion */ g_power_budget.allocated_hvac_kw = requested_hvac_kw; g_power_budget.allocated_propulsion_kw = g_power_budget.available_battery_power_kw - requested_hvac_kw - DCDC_MAX_POWER_KW - AUXILIARY_MAX_POWER_KW; } else { /* Limit HVAC */ g_power_budget.allocated_hvac_kw = available_for_hvac > 0 ? available_for_hvac : 0; g_power_budget.allocated_propulsion_kw = requested_propulsion_kw; } /* Apply power limit to torque command */ int16_t limited_torque = VCU_CalculateTorqueFromPower( g_power_budget.allocated_propulsion_kw); VCU_TorqueArbiter_SetRequest(TORQUE_SOURCE_POWER_LIMIT, limited_torque, true); } }
c/* Safety monitor for torque command plausibility */ void VCU_Safety_TorquePlausibilityCheck(void) { int16_t commanded_torque = VCU_TorqueArbiter_GetArbitratedTorque(); int16_t measured_torque = MCU_GetActualTorque(); int16_t torque_error = abs(commanded_torque - measured_torque); if (torque_error > TORQUE_PLAUSIBILITY_THRESHOLD_NM) { /* Torque mismatch detected */ g_safety_fault_counter++; if (g_safety_fault_counter > SAFETY_FAULT_THRESHOLD) { /* Enter safe state: zero torque request */ VCU_EnterSafeState(); DTC_SetFault(DTC_TORQUE_PLAUSIBILITY_FAULT); } } else { if (g_safety_fault_counter > 0) { g_safety_fault_counter--; } } }
python# vcu_hil_test.py - Hardware-in-the-Loop test suite import can import pytest import time class TestVCUTorqueArbiter: def test_driver_torque_request_normal(self, vcu_hil): """Verify driver torque request in normal conditions""" # Set throttle pedal to 50% vcu_hil.set_analog_input("ThrottlePedal", 2.5) # 0-5V time.sleep(0.05) # Read CAN message VCU_MotorCmd msg = vcu_hil.can_bus.recv(timeout=0.1) assert msg.arbitration_id == 0x100 # 256 decimal torque_request = int.from_bytes(msg.data[0:2], 'little', signed=True) * 0.1 assert 150 < torque_request < 250 # Expected range for 50% throttle in Normal mode def test_traction_control_intervention(self, vcu_hil): """Verify traction control reduces torque on wheel slip""" # Simulate wheel slip: driven wheels faster than reference vcu_hil.inject_can_message(0x220, [0x64, 0x00, 0x64, 0x00, 0xC8, 0x00, 0xC8, 0x00]) # Front wheels: 100 kph, Rear wheels: 200 kph (100% slip) time.sleep(0.2) # Verify VCU_VehicleStatus shows TC active status_msg = vcu_hil.read_can_message(0x102) tc_active = (status_msg.data[1] & 0x01) == 0x01 assert tc_active # Verify torque reduction torque_msg = vcu_hil.read_can_message(0x100) torque_source = (torque_msg.data[5] >> 1) & 0x07 assert torque_source == 2 # TractionControl source
The Vehicle Gateway Unit (VGU) acts as the central network hub, routing messages between different vehicle networks (CAN-to-Ethernet, CAN-to-CAN), implementing security firewalls, handling diagnostic access (DoIP), and managing network wake-up. This skill covers production-ready gateway development with AUTOSAR COM stack.
c/* vgu_routing_engine.c - Multi-network message routing */ #include "vgu_routing_engine.h" #include "Com.h" #include "PduR.h" #include <stdint.h> #include <stdbool.h> #define MAX_ROUTING_ENTRIES 256 #define MAX_NETWORKS 8 typedef enum { NETWORK_CAN_POWERTRAIN = 0, NETWORK_CAN_CHASSIS, NETWORK_CAN_BODY, NETWORK_CAN_INFOTAINMENT, NETWORK_ETH_BACKBONE, NETWORK_LIN_DOOR, NETWORK_FLEXRAY_ADAS, NETWORK_INVALID } NetworkID_t; typedef enum { ROUTING_MODE_UNCONDITIONAL, /* Always route */ ROUTING_MODE_CONDITIONAL, /* Route based on vehicle mode */ ROUTING_MODE_FILTERED, /* Apply gateway filter */ ROUTING_MODE_BLOCKED /* Never route (security) */ } RoutingMode_t; typedef struct { uint32_t source_pdu_id; NetworkID_t source_network; uint32_t dest_pdu_id; NetworkID_t dest_network; RoutingMode_t routing_mode; uint16_t cycle_time_ms; /* For cyclic routing */ bool transform_required; /* Endianness/scaling conversion */ uint32_t route_count; /* Statistics */ } RoutingEntry_t; static RoutingEntry_t g_routing_table[MAX_ROUTING_ENTRIES]; static uint16_t g_routing_table_size = 0; /* Example routing table configuration */ static const RoutingEntry_t DEFAULT_ROUTING_TABLE[] = { /* VCU Motor Command: CAN Powertrain -> Ethernet Backbone */ { .source_pdu_id = 0x100, .source_network = NETWORK_CAN_POWERTRAIN, .dest_pdu_id = 0x100, .dest_network = NETWORK_ETH_BACKBONE, .routing_mode = ROUTING_MODE_UNCONDITIONAL, .cycle_time_ms = 10, .transform_required = false }, /* BMS Battery Status: CAN Powertrain -> CAN Infotainment (for display) */ { .source_pdu_id = 0x300, .source_network = NETWORK_CAN_POWERTRAIN, .dest_pdu_id = 0x300, .dest_network = NETWORK_CAN_INFOTAINMENT, .routing_mode = ROUTING_MODE_FILTERED, .cycle_time_ms = 100, .transform_required = false }, /* IVI User Input: CAN Infotainment -> Ethernet Backbone (blocked in drive) */ { .source_pdu_id = 0x400, .source_network = NETWORK_CAN_INFOTAINMENT, .dest_pdu_id = 0x400, .dest_network = NETWORK_ETH_BACKBONE, .routing_mode = ROUTING_MODE_CONDITIONAL, .cycle_time_ms = 50, .transform_required = false }, /* ADAS Camera: Ethernet -> CAN Chassis (lane keeping) */ { .source_pdu_id = 0x500, .source_network = NETWORK_ETH_BACKBONE, .dest_pdu_id = 0x500, .dest_network = NETWORK_CAN_CHASSIS, .routing_mode = ROUTING_MODE_UNCONDITIONAL, .cycle_time_ms = 20, .transform_required = true /* SOME/IP to CAN conversion */ } }; void VGU_RoutingEngine_Init(void) { /* Load default routing table */ g_routing_table_size = sizeof(DEFAULT_ROUTING_TABLE) / sizeof(RoutingEntry_t); memcpy(g_routing_table, DEFAULT_ROUTING_TABLE, sizeof(DEFAULT_ROUTING_TABLE)); /* Initialize network interfaces */ for (int i = 0; i < MAX_NETWORKS; i++) { VGU_Network_Init((NetworkID_t)i); } /* Load routing table from NVM if available */ NvM_ReadBlock(NVM_BLOCK_ROUTING_TABLE, g_routing_table); } Std_ReturnType VGU_RouteMessage(uint32_t source_pdu_id, NetworkID_t source_network, const uint8_t* data, uint8_t length) { /* Find routing entry */ for (uint16_t i = 0; i < g_routing_table_size; i++) { RoutingEntry_t* entry = &g_routing_table[i]; if (entry->source_pdu_id == source_pdu_id && entry->source_network == source_network) { /* Check routing mode */ if (!VGU_RoutingAllowed(entry)) { return E_NOT_OK; } /* Apply security filter */ if (!VGU_SecurityFilter_Check(entry, data, length)) { VGU_SecurityEvent_Log(SECURITY_EVENT_FILTER_REJECT, source_pdu_id); return E_NOT_OK; } /* Transform if required */ uint8_t transformed_data[64]; uint8_t transformed_length = length; if (entry->transform_required) { VGU_DataTransform(data, length, transformed_data, &transformed_length, entry); } else { memcpy(transformed_data, data, length); } /* Route to destination network */ Std_ReturnType result = VGU_Network_Transmit( entry->dest_network, entry->dest_pdu_id, transformed_data, transformed_length); if (result == E_OK) { entry->route_count++; } return result; } } /* No routing entry found */ return E_NOT_OK; } bool VGU_RoutingAllowed(const RoutingEntry_t* entry) { switch (entry->routing_mode) { case ROUTING_MODE_UNCONDITIONAL: return true; case ROUTING_MODE_CONDITIONAL: /* Example: block IVI messages when vehicle is driving */ if (entry->source_network == NETWORK_CAN_INFOTAINMENT) { uint16_t vehicle_speed = VCU_GetVehicleSpeed_kph(); return (vehicle_speed < 5); /* Only allow when stopped */ } return true; case ROUTING_MODE_FILTERED: /* Additional filtering logic */ return true; case ROUTING_MODE_BLOCKED: return false; default: return false; } } /* CAN-to-Ethernet transformation (CAN frame -> SOME/IP) */ void VGU_DataTransform_CANtoETH(const uint8_t* can_data, uint8_t can_length, uint8_t* eth_data, uint8_t* eth_length) { /* SOME/IP header: Service ID, Method ID, Length, Client ID, Session ID, ... */ uint16_t service_id = 0x1234; uint16_t method_id = 0x0001; /* Build SOME/IP message */ eth_data[0] = (service_id >> 8) & 0xFF; eth_data[1] = service_id & 0xFF; eth_data[2] = (method_id >> 8) & 0xFF; eth_data[3] = method_id & 0xFF; /* Length field */ uint32_t payload_length = can_length + 8; /* Payload + SOME/IP overhead */ eth_data[4] = (payload_length >> 24) & 0xFF; eth_data[5] = (payload_length >> 16) & 0xFF; eth_data[6] = (payload_length >> 8) & 0xFF; eth_data[7] = payload_length & 0xFF; /* Copy CAN payload */ memcpy(ð_data[16], can_data, can_length); *eth_length = 16 + can_length; }
c/* vgu_security_firewall.c - Message filtering and intrusion detection */ #include "vgu_security_firewall.h" #define MAX_FIREWALL_RULES 128 #define MAX_ALLOWED_CAN_IDS 512 #define ANOMALY_THRESHOLD 10 typedef enum { FIREWALL_ACTION_ALLOW = 0, FIREWALL_ACTION_BLOCK, FIREWALL_ACTION_LOG, FIREWALL_ACTION_ALERT } FirewallAction_t; typedef struct { uint32_t can_id; NetworkID_t network; FirewallAction_t action; uint32_t min_cycle_time_ms; /* Minimum expected cycle time */ uint32_t max_cycle_time_ms; /* Maximum expected cycle time */ uint8_t expected_dlc; bool require_authentication; } FirewallRule_t; typedef struct { uint32_t can_id; uint32_t last_rx_timestamp_ms; uint32_t rx_count; uint32_t anomaly_count; } MessageMonitor_t; static FirewallRule_t g_firewall_rules[MAX_FIREWALL_RULES]; static MessageMonitor_t g_message_monitors[MAX_ALLOWED_CAN_IDS]; /* Example firewall rules */ static const FirewallRule_t DEFAULT_FIREWALL_RULES[] = { /* VCU Motor Command: strict timing, authenticated */ { .can_id = 0x100, .network = NETWORK_CAN_POWERTRAIN, .action = FIREWALL_ACTION_ALLOW, .min_cycle_time_ms = 8, .max_cycle_time_ms = 12, .expected_dlc = 8, .require_authentication = true }, /* BMS Battery Status: allow with timing check */ { .can_id = 0x300, .network = NETWORK_CAN_POWERTRAIN, .action = FIREWALL_ACTION_ALLOW, .min_cycle_time_ms = 90, .max_cycle_time_ms = 110, .expected_dlc = 8, .require_authentication = false }, /* Diagnostic request: block unless diagnostic session active */ { .can_id = 0x7DF, /* OBD-II diagnostic request */ .network = NETWORK_CAN_POWERTRAIN, .action = FIREWALL_ACTION_LOG, .min_cycle_time_ms = 0, .max_cycle_time_ms = 0xFFFFFFFF, .expected_dlc = 8, .require_authentication = true }, /* Unknown high-priority CAN ID: block and alert */ { .can_id = 0x000, /* High priority range 0x000-0x0FF */ .network = NETWORK_CAN_POWERTRAIN, .action = FIREWALL_ACTION_BLOCK, .min_cycle_time_ms = 0, .max_cycle_time_ms = 0xFFFFFFFF, .expected_dlc = 0, .require_authentication = false } }; void VGU_SecurityFirewall_Init(void) { memcpy(g_firewall_rules, DEFAULT_FIREWALL_RULES, sizeof(DEFAULT_FIREWALL_RULES)); memset(g_message_monitors, 0, sizeof(g_message_monitors)); } bool VGU_SecurityFilter_Check(const RoutingEntry_t* route, const uint8_t* data, uint8_t length) { uint32_t can_id = route->source_pdu_id; uint32_t current_time_ms = GetSystemTime_ms(); /* Find firewall rule */ FirewallRule_t* rule = NULL; for (int i = 0; i < MAX_FIREWALL_RULES; i++) { if (g_firewall_rules[i].can_id == can_id && g_firewall_rules[i].network == route->source_network) { rule = &g_firewall_rules[i]; break; } } if (rule == NULL) { /* No rule defined: default deny */ return false; } /* Check DLC */ if (rule->expected_dlc > 0 && length != rule->expected_dlc) { VGU_SecurityEvent_Log(SECURITY_EVENT_DLC_MISMATCH, can_id); return false; } /* Find message monitor entry */ MessageMonitor_t* monitor = NULL; for (int i = 0; i < MAX_ALLOWED_CAN_IDS; i++) { if (g_message_monitors[i].can_id == can_id) { monitor = &g_message_monitors[i]; break; } else if (g_message_monitors[i].can_id == 0) { /* Create new monitor entry */ monitor = &g_message_monitors[i]; monitor->can_id = can_id; break; } } if (monitor != NULL) { /* Check cycle time */ if (monitor->last_rx_timestamp_ms > 0) { uint32_t delta_ms = current_time_ms - monitor->last_rx_timestamp_ms; if (delta_ms < rule->min_cycle_time_ms || delta_ms > rule->max_cycle_time_ms) { monitor->anomaly_count++; if (monitor->anomaly_count > ANOMALY_THRESHOLD) { VGU_SecurityEvent_Log(SECURITY_EVENT_TIMING_VIOLATION, can_id); /* Don't block, but alert */ } } else { /* Reset anomaly counter on valid timing */ if (monitor->anomaly_count > 0) { monitor->anomaly_count--; } } } monitor->last_rx_timestamp_ms = current_time_ms; monitor->rx_count++; } /* Check authentication if required */ if (rule->require_authentication) { if (!VGU_Security_VerifyMAC(data, length)) { VGU_SecurityEvent_Log(SECURITY_EVENT_AUTH_FAIL, can_id); return false; } } /* Apply firewall action */ switch (rule->action) { case FIREWALL_ACTION_ALLOW: return true; case FIREWALL_ACTION_BLOCK: VGU_SecurityEvent_Log(SECURITY_EVENT_BLOCKED, can_id); return false; case FIREWALL_ACTION_LOG: VGU_SecurityEvent_Log(SECURITY_EVENT_LOGGED, can_id); return true; case FIREWALL_ACTION_ALERT: VGU_SecurityEvent_Log(SECURITY_EVENT_ALERT, can_id); return true; default: return false; } } /* SecOC (Secure Onboard Communication) - MAC verification */ bool VGU_Security_VerifyMAC(const uint8_t* data, uint8_t length) { /* Extract MAC from last 8 bytes */ uint64_t received_mac = 0; for (int i = 0; i < 8; i++) { received_mac = (received_mac << 8) | data[length - 8 + i]; } /* Calculate expected MAC using CMAC-AES */ uint64_t calculated_mac = VGU_Crypto_CalculateMAC(data, length - 8); return (received_mac == calculated_mac); }
c/* vgu_doip_gateway.c - ISO 13400 Diagnostic over IP */ #include "vgu_doip_gateway.h" #define DOIP_UDP_PORT 13400 #define DOIP_TCP_PORT 13400 #define MAX_DOIP_CONNECTIONS 4 typedef enum { DOIP_VEHICLE_ANNOUNCEMENT = 0x0004, DOIP_ROUTING_ACTIVATION_REQUEST = 0x0005, DOIP_ROUTING_ACTIVATION_RESPONSE = 0x0006, DOIP_DIAGNOSTIC_MESSAGE = 0x8001, DOIP_DIAGNOSTIC_MESSAGE_ACK = 0x8002, DOIP_DIAGNOSTIC_MESSAGE_NACK = 0x8003 } DoIPMessageType_t; typedef struct { uint8_t protocol_version; uint8_t inverse_protocol_version; uint16_t payload_type; uint32_t payload_length; } DoIPHeader_t; typedef struct { int socket_fd; bool active; uint16_t source_address; uint16_t target_address; uint8_t activation_type; uint32_t last_activity_ms; } DoIPConnection_t; static DoIPConnection_t g_doip_connections[MAX_DOIP_CONNECTIONS]; void VGU_DoIP_Init(void) { /* Create UDP socket for vehicle announcement */ int udp_socket = socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = htons(DOIP_UDP_PORT), .sin_addr.s_addr = INADDR_ANY }; bind(udp_socket, (struct sockaddr*)&addr, sizeof(addr)); /* Create TCP socket for diagnostic communication */ int tcp_socket = socket(AF_INET, SOCK_STREAM, 0); bind(tcp_socket, (struct sockaddr*)&addr, sizeof(addr)); listen(tcp_socket, MAX_DOIP_CONNECTIONS); /* Send periodic vehicle announcement */ VGU_DoIP_SendVehicleAnnouncement(udp_socket); } void VGU_DoIP_SendVehicleAnnouncement(int udp_socket) { uint8_t announcement[32]; DoIPHeader_t* header = (DoIPHeader_t*)announcement; header->protocol_version = 0x02; /* ISO 13400-2:2012 */ header->inverse_protocol_version = 0xFD; header->payload_type = htons(DOIP_VEHICLE_ANNOUNCEMENT); header->payload_length = htonl(14); /* Payload: VIN (17 bytes) + Logical Address (2 bytes) + EID (6 bytes) + GID (6 bytes) */ const char* vin = "1HGBH41JXMN109186"; memcpy(&announcement[8], vin, 17); uint16_t logical_address = 0x0001; /* Gateway address */ memcpy(&announcement[25], &logical_address, 2); /* Broadcast announcement */ struct sockaddr_in broadcast_addr = { .sin_family = AF_INET, .sin_port = htons(DOIP_UDP_PORT), .sin_addr.s_addr = htonl(INADDR_BROADCAST) }; sendto(udp_socket, announcement, 32, 0, (struct sockaddr*)&broadcast_addr, sizeof(broadcast_addr)); } void VGU_DoIP_HandleRoutingActivation(int tcp_socket, const uint8_t* request, uint16_t length) { /* Parse routing activation request */ uint16_t source_address = (request[8] << 8) | request[9]; uint8_t activation_type = request[10]; /* Find available connection slot */ DoIPConnection_t* conn = NULL; for (int i = 0; i < MAX_DOIP_CONNECTIONS; i++) { if (!g_doip_connections[i].active) { conn = &g_doip_connections[i]; break; } } uint8_t response_code; if (conn != NULL) { conn->socket_fd = tcp_socket; conn->active = true; conn->source_address = source_address; conn->activation_type = activation_type; conn->last_activity_ms = GetSystemTime_ms(); response_code = 0x10; /* Routing successfully activated */ } else { response_code = 0x02; /* All sockets in use */ } /* Send routing activation response */ uint8_t response[13]; DoIPHeader_t* header = (DoIPHeader_t*)response; header->protocol_version = 0x02; header->inverse_protocol_version = 0xFD; header->payload_type = htons(DOIP_ROUTING_ACTIVATION_RESPONSE); header->payload_length = htonl(5); response[8] = (source_address >> 8) & 0xFF; response[9] = source_address & 0xFF; response[10] = 0x00; /* Logical address of gateway */ response[11] = 0x01; response[12] = response_code; send(tcp_socket, response, 13, 0); } void VGU_DoIP_RouteDiagnosticMessage(const uint8_t* doip_message, uint16_t length) { /* Extract source and target addresses */ uint16_t source_addr = (doip_message[8] << 8) | doip_message[9]; uint16_t target_addr = (doip_message[10] << 8) | doip_message[11]; /* Extract UDS payload */ const uint8_t* uds_payload = &doip_message[12]; uint16_t uds_length = length - 12; /* Route to target ECU based on logical address */ NetworkID_t target_network; uint32_t target_can_id; switch (target_addr) { case 0x0010: /* VCU */ target_network = NETWORK_CAN_POWERTRAIN; target_can_id = 0x7E0; /* VCU diagnostic address */ break; case 0x0020: /* BMS */ target_network = NETWORK_CAN_POWERTRAIN; target_can_id = 0x7E1; break; case 0x0030: /* MCU */ target_network = NETWORK_CAN_POWERTRAIN; target_can_id = 0x7E2; break; default: /* Unknown target */ return; } /* Send diagnostic request over CAN */ VGU_Network_Transmit(target_network, target_can_id, uds_payload, uds_length); }
c/* vgu_wakeup_management.c - Network wake-up and power management */ #include "vgu_wakeup_management.h" typedef enum { WAKEUP_SOURCE_CAN_POWERTRAIN = 0, WAKEUP_SOURCE_CAN_CHASSIS, WAKEUP_SOURCE_LIN_DOOR, WAKEUP_SOURCE_ETHERNET, WAKEUP_SOURCE_TIMER, WAKEUP_SOURCE_IGNITION, WAKEUP_SOURCE_COUNT } WakeupSource_t; typedef struct { bool wakeup_enabled[WAKEUP_SOURCE_COUNT]; WakeupSource_t last_wakeup_source; uint32_t wakeup_timestamp_ms; } WakeupState_t; static WakeupState_t g_wakeup_state = {0}; void VGU_WakeupManagement_Init(void) { /* Enable relevant wakeup sources */ g_wakeup_state.wakeup_enabled[WAKEUP_SOURCE_CAN_POWERTRAIN] = true; g_wakeup_state.wakeup_enabled[WAKEUP_SOURCE_LIN_DOOR] = true; g_wakeup_state.wakeup_enabled[WAKEUP_SOURCE_IGNITION] = true; /* Configure CAN transceivers for selective wake-up */ CanTrcv_SetOpMode(CAN_POWERTRAIN, CANTRCV_WUMODE_ENABLE); CanTrcv_SetOpMode(CAN_CHASSIS, CANTRCV_WUMODE_ENABLE); } void VGU_WakeupManagement_OnWakeup(WakeupSource_t source) { g_wakeup_state.last_wakeup_source = source; g_wakeup_state.wakeup_timestamp_ms = GetSystemTime_ms(); /* Notify EcuM of wakeup event */ EcuM_SetWakeupEvent((EcuM_WakeupSourceType)(1 << source)); /* Start network initialization sequence based on wakeup source */ switch (source) { case WAKEUP_SOURCE_CAN_POWERTRAIN: /* High-priority startup: VCU, BMS, MCU needed */ VGU_Network_Start(NETWORK_CAN_POWERTRAIN); VGU_Network_Start(NETWORK_CAN_CHASSIS); break; case WAKEUP_SOURCE_LIN_DOOR: /* Body network startup: BCM, door modules */ VGU_Network_Start(NETWORK_CAN_BODY); VGU_Network_Start(NETWORK_LIN_DOOR); break; case WAKEUP_SOURCE_IGNITION: /* Full network startup */ for (NetworkID_t net = 0; net < NETWORK_INVALID; net++) { VGU_Network_Start(net); } break; default: break; } } void VGU_WakeupManagement_EnterSleep(void) { /* Shutdown sequence: least critical networks first */ VGU_Network_Stop(NETWORK_CAN_INFOTAINMENT); VGU_Network_Stop(NETWORK_CAN_BODY); /* Wait for pending transmissions */ while (VGU_Network_HasPendingTx(NETWORK_CAN_CHASSIS)) { OsTask_Sleep(10); } VGU_Network_Stop(NETWORK_CAN_CHASSIS); /* Powertrain network last (safety-critical) */ VGU_Network_Stop(NETWORK_CAN_POWERTRAIN); /* Configure transceivers for wake-up */ CanTrcv_SetOpMode(CAN_POWERTRAIN, CANTRCV_WUMODE_ENABLE); /* Enter low-power mode */ Mcu_SetMode(MCU_MODE_SLEEP); }
xml<!-- vgu_pdur_configuration.arxml --> <AUTOSAR xmlns="http://autosar.org/schema/r4.0"> <AR-PACKAGES> <AR-PACKAGE> <SHORT-NAME>PduR_RoutingTables</SHORT-NAME> <ELEMENTS> <PDU-R-ROUTING-TABLE> <SHORT-NAME>VGU_RoutingTable</SHORT-NAME> <ROUTING-PATHS> <!-- CAN Powertrain -> Ethernet Backbone --> <PDU-R-ROUTING-PATH> <SHORT-NAME>VCU_MotorCmd_CANtoETH</SHORT-NAME> <PDU-R-SOURCE-PDU-REF DEST="I-PDU"> /CAN_Powertrain/VCU_MotorCmd </PDU-R-SOURCE-PDU-REF> <PDU-R-DESTINATION-PDU-REF DEST="I-PDU"> /ETH_Backbone/VCU_MotorCmd_ETH </PDU-R-DESTINATION-PDU-REF> <PDU-R-DEFAULT-VALUE>0</PDU-R-DEFAULT-VALUE> </PDU-R-ROUTING-PATH> <!-- Ethernet -> CAN Chassis (ADAS commands) --> <PDU-R-ROUTING-PATH> <SHORT-NAME>ADAS_SteeringCmd_ETHtoGAN</SHORT-NAME> <PDU-R-SOURCE-PDU-REF DEST="I-PDU"> /ETH_Backbone/ADAS_SteeringCmd </PDU-R-SOURCE-PDU-REF> <PDU-R-DESTINATION-PDU-REF DEST="I-PDU"> /CAN_Chassis/ADAS_SteeringCmd_CAN </PDU-R-DESTINATION-PDU-REF> <PDU-R-DEFAULT-VALUE>0</PDU-R-DEFAULT-VALUE> </PDU-R-ROUTING-PATH> </ROUTING-PATHS> </PDU-R-ROUTING-TABLE> </ELEMENTS> </AR-PACKAGE> </AR-PACKAGES> </AUTOSAR>
VERSION ""
NS_ :
BS_:
BU_: VGU VCU BMS MCU BCM IVI
/* Gateway Status Message */
BO_ 1024 VGU_Status: 8 VGU
SG_ VGU_NetworkStatus_Powertrain : 0|2@1+ (0,0) [0|3] "" VCU,BMS,MCU
SG_ VGU_NetworkStatus_Chassis : 2|2@1+ (0,0) [0|3] "" BCM
SG_ VGU_NetworkStatus_Body : 4|2@1+ (0,0) [0|3] "" BCM
SG_ VGU_NetworkStatus_Infotainment : 6|2@1+ (0,0) [0|3] "" IVI
SG_ VGU_NetworkStatus_Ethernet : 8|2@1+ (0,0) [0|3] "" VCU,IVI
SG_ VGU_RoutingActive : 10|1@1+ (0,0) [0|1] "" ALL
SG_ VGU_FirewallActive : 11|1@1+ (0,0) [0|1] "" ALL
SG_ VGU_DiagnosticSessionActive : 12|1@1+ (0,0) [0|1] "" ALL
SG_ VGU_SecurityAnomalyCount : 16|16@1+ (0,0) [0|65535] "" ALL
SG_ VGU_RoutedMessageCount : 32|32@1+ (0,0) [0|4294967295] "" ALL
VAL_ 1024 VGU_NetworkStatus_Powertrain 0 "Offline" 1 "Initializing" 2 "Active" 3 "Error";
VAL_ 1024 VGU_NetworkStatus_Chassis 0 "Offline" 1 "Initializing" 2 "Active" 3 "Error";
VAL_ 1024 VGU_NetworkStatus_Body 0 "Offline" 1 "Initializing" 2 "Active" 3 "Error";
VAL_ 1024 VGU_NetworkStatus_Infotainment 0 "Offline" 1 "Initializing" 2 "Active" 3 "Error";
VAL_ 1024 VGU_NetworkStatus_Ethernet 0 "Offline" 1 "Initializing" 2 "Active" 3 "Error";python# vgu_hil_test.py - Hardware-in-the-Loop testing for VGU import can import socket import pytest class TestVGURouting: def test_can_to_ethernet_routing(self, vgu_hil): """Verify CAN message is routed to Ethernet""" # Send VCU_MotorCmd on CAN Powertrain can_msg = can.Message(arbitration_id=0x100, data=[0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0xAB], is_extended_id=False) vgu_hil.can_powertrain.send(can_msg) # Verify message appears on Ethernet backbone (SOME/IP) eth_packet = vgu_hil.eth_backbone.recv(timeout=0.1) assert eth_packet is not None assert eth_packet[0:2] == b'\x12\x34' # Service ID def test_security_firewall_blocks_invalid_dlc(self, vgu_hil): """Verify firewall blocks message with wrong DLC""" # Send message with incorrect DLC invalid_msg = can.Message(arbitration_id=0x100, data=[0x64, 0x00], # DLC=2, expected=8 is_extended_id=False) vgu_hil.can_powertrain.send(invalid_msg) # Verify VGU logs security event security_log = vgu_hil.read_can_message(0x400, timeout=0.1) assert security_log.data[0] == 0x01 # DLC_MISMATCH event # Verify message NOT routed to Ethernet eth_packet = vgu_hil.eth_backbone.recv(timeout=0.1) assert eth_packet is None def test_doip_routing_activation(self, vgu_hil): """Verify DoIP routing activation over TCP""" # Connect to DoIP port sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect(("192.168.1.100", 13400)) # Send routing activation request activation_request = bytes([ 0x02, 0xFD, # Protocol version 0x00, 0x05, # Payload type: Routing Activation Request 0x00, 0x00, 0x00, 0x07, # Payload length 0x0E, 0x80, # Source address (tester) 0x00, # Activation type 0x00, 0x00, 0x00, 0x00 # Reserved ]) sock.send(activation_request) # Receive routing activation response response = sock.recv(13) assert len(response) == 13 assert response[2:4] == b'\x00\x06' # Routing Activation Response assert response[12] == 0x10 # Response code: Successfully activated sock.close()
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-08 | fail→pass | 28,082 | 25,934 | -8% | 1 | 1 | 0% | 6,199 | 52,240 | +743% | 0 | 0 | — |
case-02 | fail→fail | 23,393 | 22,439 | -4% | 1 | 1 | 0% | 5,306 | 51,183 | +865% | 0 | 0 | — |
case-01 | fail→fail | 29,263 | 27,413 | -6% | 1 | 1 | 0% | 6,223 | 52,263 | +740% | 0 | 0 | — |
case-03 | fail→fail | 20,292 | 27,477 | +35% | 1 | 1 | 0% | 5,351 | 52,259 | +877% | 0 | 0 | — |
case-04 | fail→fail | 24,355 | 25,934 | +6% | 1 | 1 | 0% | 5,506 | 51,797 | +841% | 0 | 0 | — |
case-05 | pass→pass | 13,444 | 20,313 | +51% | 1 | 1 | 0% | 3,058 | 50,736 | +1559% | 0 | 0 | — |
case-06 | fail→fail | 23,907 | 28,285 | +18% | 1 | 1 | 0% | 5,078 | 51,974 | +924% | 0 | 0 | — |
case-07 | fail→fail | 28,246 | 22,979 | -19% | 1 | 1 | 0% | 6,084 | 51,407 | +745% | 0 | 0 | — |
case-09 | fail→fail | 26,711 | 27,696 | +4% | 1 | 1 | 0% | 6,016 | 52,230 | +768% | 0 | 0 | — |
case-10 | fail→fail | 19,369 | 23,344 | +21% | 1 | 1 | 0% | 3,957 | 50,980 | +1188% | 0 | 0 | — |
case-11 | pass→pass | 22,395 | 33,298 | +49% | 1 | 1 | 0% | 4,715 | 52,135 | +1006% | 0 | 0 | — |
case-12 | fail→fail | 25,542 | 29,106 | +14% | 1 | 1 | 0% | 5,405 | 52,096 | +864% | 0 | 0 | — |
case-13 | fail→fail | 26,367 | 27,238 | +3% | 1 | 1 | 0% | 6,185 | 52,226 | +744% | 0 | 0 | — |
case-14 | fail→fail | 24,199 | 30,402 | +26% | 1 | 1 | 0% | 5,518 | 52,246 | +847% | 0 | 0 | — |
case-15 | fail→fail | 28,586 | 29,270 | +2% | 1 | 1 | 0% | 6,188 | 52,229 | +744% | 0 | 0 | — |
case-16 | fail→fail | 24,563 | 27,915 | +14% | 1 | 1 | 0% | 5,771 | 52,224 | +805% | 0 | 0 | — |
case-17 | pass→pass | 18,007 | 27,184 | +51% | 1 | 1 | 0% | 3,938 | 52,220 | +1226% | 0 | 0 | — |
case-18 | fail→fail | 45,460 | 22,690 | -50% | 1 | 1 | 0% | 4,910 | 50,970 | +938% | 0 | 0 | — |
case-19 | pass→pass | 17,441 | 18,262 | +5% | 1 | 1 | 0% | 3,710 | 49,855 | +1244% | 0 | 0 | — |
case-20 | fail→fail | 29,536 | 27,889 | -6% | 1 | 1 | 0% | 6,195 | 52,236 | +743% | 0 | 0 | — |
case-21 | fail→fail | 27,749 | 34,710 | +25% | 1 | 1 | 0% | 6,200 | 52,241 | +743% | 0 | 0 | — |
case-22 | fail→fail | 20,868 | 34,866 | +67% | 1 | 1 | 0% | 4,186 | 52,232 | +1148% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +5 percentage points is the difference between those two pass rates over the 22 comparable cases. 3 cases got worse with the skill loaded, and they are included in that figure.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.