VW Mild Hybrid Starter Alternator

Forum on Volkswagen related hardware, so VW, Audi, Seat, Skoda etc.
Adrianbb
Posts: 7
Joined: Thu Feb 26, 2026 11:12 pm
Has thanked: 1 time
Been thanked: 6 times

Re: VW Mild Hybrid Starter Alternator

Post by Adrianbb »

So, I got it working with OI sine firmware on olimex STM-H103 board. All needed components are used original (Igbt, drivers, current sensors and position sensor). Only added some voltage dividers where was needed...
Of course, not everything goes according to plan... I haven't been able to get the regen mode running yet. Motor still accelerates in regen mode. I don't know what to do with it anymore, I'll probably start a new thread about this problem.
User avatar
tom91
Posts: 2962
Joined: Fri Mar 01, 2019 9:15 pm
Location: Bicester, Oxfordshire
Has thanked: 328 times
Been thanked: 847 times

Re: VW Mild Hybrid Starter Alternator

Post by tom91 »

The alternator is not an induction motor is it? so it needs FOC and not Since
Creator of SimpBMS
Founder Volt Influx https://www.voltinflux.com/
Webstore: https://citini.com/
bananaman
Posts: 17
Joined: Tue Aug 26, 2025 5:38 pm
Has thanked: 2 times
Been thanked: 13 times

Re: VW Mild Hybrid Starter Alternator

Post by bananaman »

squidopolis wrote: Mon Jan 19, 2026 5:27 am Got this working, below is my python script:

#send_msg
# The CANlib library is initialized when the canlib module is imported. To be
# able to send a message, Frame also needs to be installed.
from canlib import canlib, Frame
import time

#CAN
ch_a = canlib.openChannel(channel=0)
ch_a.setBusParams(canlib.canBITRATE_500K)
ch_a.busOn()

#IGNITION MSG
IGN_counter = 0
IGN_DLC = 4
IGN_data_array = bytearray(4)
IGN_MSG_ID = (0x03C0 & 0xFFFF)

#MSG_EMG_01 MSG
EMG_01_counter = 0
EMG_01_DLC = 8
EMG_01_data_array = bytearray(8)

EMG_01_MSG_ID = (0x0151 & 0xFFFF)
MO_EMG_OperationModeReq = ( 1& 0xF)
MO_EMG_SpeedReq = (2048&0x7FFF)
MO_EMG_MaxTorqueGradient = (0x01&0x7F)
MO_EMG_Clamp15 = 1
MO_EMG_Deactivate_HV = 1

MO_EMG_MaxVoltage = (4&0xFF)

#MSG EMG_02 MSG
EMG_02_counter = 4
EMG_02_DLC = 8
EMG_02_data_array = bytearray(8)
EMG_02_MSG_ID = (0x0152 & 0xFFFF)

MO_EMG_MaxCurrent = (1027&0x7FF)
MO_EMG_MinCurrent = (1023&0x7FF)
MO_EMG_MinVoltage = (106&0xFF)
MO_EMG_TorqueReq = (32781&0xFFFF)

#Constant Bytes



# VAG MB 0 1 2 3 4 5 6 7 8 9 A B C D E F
MB0151 = bytearray([ 0x42, 0x7E, 0xB4, 0x01, 0x0A, 0x91, 0xDF, 0x10, 0xA1, 0x73, 0x59, 0x05, 0xF6, 0x79, 0x0B, 0xDE])
MB0152 = bytearray([ 0x58, 0x61, 0xCF, 0x30, 0x69, 0x6A, 0xC4, 0xC6, 0xB2, 0x53, 0x22, 0xB6, 0xEC, 0x95, 0xCA, 0xF8])
MB03C0 = bytearray([ 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3, 0xC3])

#Calculate the CRC checksum for VAG CAN Messages
def vw_crc_calc(byte_array: bytearray, DLC: byte, MSG_ID: int):
poly = 0x2F
xor_output = 0xFF
crc = 0XFF
magic_byte = 0x00
counter = byte_array[1]&0x0F

if MSG_ID == 0x151:
magic_byte = MB0151[counter]
elif MSG_ID == 0x152:
magic_byte = MB0152[counter]
elif MSG_ID == 0x3C0:
magic_byte = MB03C0[counter]

for i in range(1, DLC + 1):
if i<DLC:
crc ^= byte_array;
else:
crc ^= magic_byte;

for j in range(8):
if (crc&0x80):
crc = (crc << 1) ^ poly
else:
crc = (crc << 1);

crc ^= xor_output;
byte_array[0] = crc&0xFF

# Send command messages ~1ms for ~10seconds
for i in range(50000):

#load Ignition Frame
IGN_counter += 1
if IGN_counter>0xF:
IGN_counter = 0
#Load Data (Constant 0x23 Value)
IGN_data_array[1]=IGN_counter&0xF
IGN_data_array[2] = 0x2
#IGN_data_array[3] = 0x03
#Modify CRC byte[0]
vw_crc_calc(IGN_data_array, IGN_DLC, IGN_MSG_ID)
#Send Igniton CAN FRAME
frame = Frame(IGN_MSG_ID, IGN_data_array, flags=canlib.MessageFlag.STD )
ch_a.write(frame)

#load EMG_01 Frame
EMG_01_counter += 1
if EMG_01_counter>0xF:
EMG_01_counter = 0
#load Data
EMG_01_data_array[1]=((MO_EMG_OperationModeReq<<4)&0xF0)+(EMG_01_counter&0xF)
EMG_01_data_array[2]=(MO_EMG_SpeedReq&0xFF)
#EMG_01_data_array[3]=((MO_EMG_SpeedReq>>8)&0x7F)+((MO_EMG_Clamp15<<7)&0x80)
#EMG_01_data_array[4]= ((MO_EMG_Deactivate_HV<<2)&0x1C)
EMG_01_data_array[4]=0xFF;
EMG_01_data_array[5]=0x01;
#EMG_01_data_array[4]= 0x26;
#EMG_01_data_array[5]= (MO_EMG_MaxVoltage&0x1)
#=

EMG_01_data_array[7]=(MO_EMG_MaxTorqueGradient&0x7F)
#Modify CRC byte[0]
vw_crc_calc(EMG_01_data_array, EMG_01_DLC, EMG_01_MSG_ID)
#Send EMG 01 CAN FRAME
frame = Frame(EMG_01_MSG_ID, EMG_01_data_array, flags=canlib.MessageFlag.STD )
ch_a.write(frame)

#load EMG_02 Frame
EMG_02_counter += 1
if EMG_02_counter>0xF:
EMG_02_counter = 0
#load Data
EMG_02_data_array[1] = ((MO_EMG_MaxCurrent<<4)&0xF0)+(EMG_02_counter&0xF)
EMG_02_data_array[2] = ((MO_EMG_MaxCurrent>>4)&0x7F)+((MO_EMG_MinCurrent<<7)&0x80)
EMG_02_data_array[3] = ((MO_EMG_MinCurrent>>1)&0xFF)
# EMG_02_data_array[4] = ((MO_EMG_MinCurrent>>9)&0x3)
EMG_02_data_array[4] = 0x3D
EMG_02_data_array[5] = (MO_EMG_TorqueReq&0xFF)
EMG_02_data_array[6] = ((MO_EMG_TorqueReq>>8)&0xFF)
EMG_02_data_array[7] = (MO_EMG_MinVoltage&0xFF)
#Modify CRC byte[0]
vw_crc_calc(EMG_02_data_array, EMG_02_DLC, EMG_02_MSG_ID)
#Send EMG 01 CAN FRAME
frame = Frame(EMG_02_MSG_ID, EMG_02_data_array, flags=canlib.MessageFlag.STD )
ch_a.write(frame)

#Constants
B000_const = bytearray ([0x76,0x40,0x01,0x05,0x02,0x00,0x00,0x00])
frame = Frame(0x1B000076, B000_const, flags=canlib.MessageFlag.EXT)
ch_a.write(frame)

B504_const = bytearray ([0xC4,0x09,0x00,0x00,0x26,0x19,0x14,0x51])
frame = Frame(0x504, B504_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)

B3A3_const = bytearray ([0x17,0x9C,0xFB,0x41,0x42,0x3F,0xFE,0xCC])
frame = Frame(0x3A3, B3A3_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)

B18B_const = bytearray ([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
frame = Frame(0x18B, B18B_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)

B18D_const = bytearray ([0x00,0xF0,0x7F,0xC1,0x00,0x80,0x26,0x2])
frame = Frame(0x18D, B18D_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)

B171_const = bytearray ([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
frame = Frame(0x171, B171_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)

BEE_const = bytearray ([0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00])
frame = Frame(0xEE, BEE_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)



if i % 2 == 0:
B040_const = bytearray ([0x07,0x0E,0x00,0x00,0x80,0x00,0x20,0x62])
frame = Frame(0x040, B040_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)
else:
B040_const = bytearray ([0x4B,0x0F,0x00,0x00,0x80,0x00,0x20,0x64])
frame = Frame(0x040, B040_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)

if i % 2 == 0:
B0FD_const = bytearray ([0x23,0xE0,0x3F,0x00,0x00,0x00,0x00,0x00])
frame = Frame(0x0FD, B0FD_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)
else:
B0FD_const = bytearray ([0x29,0xE1,0x3F,0x00,0x00,0x00,0x00,0x00])
frame = Frame(0x0FD, B0FD_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)



B0A8_const = bytearray ([0x00,0xE0,0x3F,0x57,0x7F,0x64,0x00,0x00])
frame = Frame(0x0A8, B0A8_const, flags=canlib.MessageFlag.STD )
ch_a.write(frame)


# Example: print first 5 rows of data
#print(f"{msgID_INT:04x}: ,{d0_INT:04x},{d1_INT:04x},{d2_INT:04x},{d3_INT:04x},{d4_INT:04x},{d5_INT:04x},{d6_INT:04x},{d7_INT:04x}")
#print(i)
time.sleep(0.01)

# Lastly, close all channels with close() to finish up.
ch_a.close()
#ch_b.close()

Sorry, I seem to have missed this post before. The signals in your EM_01 and EM_02 largely match what I was seeing in the firmware and the behavior of the motor. Where did you find those signal definitions and names?


Adrianbb wrote: Tue Mar 10, 2026 4:43 pm So, I got it working with OI sine firmware on olimex STM-H103 board. All needed components are used original (Igbt, drivers, current sensors and position sensor). Only added some voltage dividers where was needed...
Of course, not everything goes according to plan... I haven't been able to get the regen mode running yet. Motor still accelerates in regen mode. I don't know what to do with it anymore, I'll probably start a new thread about this problem.

Nice! Can you elaborate on what firmware you are using? Did you write it yourself or are you using some open source example?


tom91 wrote: Tue Mar 10, 2026 5:56 pm The alternator is not an induction motor is it? so it needs FOC and not Since

It should be an induction motor. It has what looks like a squirrel cage rotor.
Adrianbb
Posts: 7
Joined: Thu Feb 26, 2026 11:12 pm
Has thanked: 1 time
Been thanked: 6 times

Re: VW Mild Hybrid Starter Alternator

Post by Adrianbb »

bananaman wrote: Tue Mar 10, 2026 11:35 pm Nice! Can you elaborate on what firmware you are using? Did you write it yourself or are you using some open source example?
https://github.com/jsphuebner/stm32-sine
bananaman
Posts: 17
Joined: Tue Aug 26, 2025 5:38 pm
Has thanked: 2 times
Been thanked: 13 times

Re: VW Mild Hybrid Starter Alternator

Post by bananaman »

Adrianbb wrote: Wed Mar 11, 2026 3:06 pm https://github.com/jsphuebner/stm32-sine
Ah, nice! Now I'm wondering how hard it would be to port to the SPC56 on the stock board...
Post Reply