Controlling a Hella VNT-actuator using PWM

I’ve had a couple of Garrett turbochargers laying around for some time now, a GT2056V from a E60 525d and a GT2260V from a E46 330d Euro4 to be specific. I originally bought these for my 320d as upgrade-turbos, but with so many other projects and stuff to do, i just haven’t got the time to swap in a bigger turbo. I’m working on my summer car at the moment, and it needs an actuator to control a flap/valve, so i thought why not borrow one from these turbos as they’re just wasting space at the moment.

The Actuator

I dismantled the actuator from the GT2056V, took it apart, and gave it a quick cleanup. Looking inside, everything looked OK. No excessive wear on the mechanical parts, and no broken solder joints or melted components on the circuit board.

VNT-actuator inside

Once put back together, it was time for some googling around. The actuator i have is labeled 6NW 008 412, which is the same number as most of these actuators seem to be. Another identification found on the plastic case is the number 2, and there is also a G-****-tag on the metal casing, these two identify the OEM manufacturer and type of the actuator.

VNT-actuator cover

It seems there are two ways of controlling a actuator like this, PWM and CAN. The control method depends on how the unit is programmed upon manufacturing, and cannot be easily changed. Some sources tell that the actuator can be controlled with either one, but i tested this with my PWM-controlled actuator, and it definitely didn’t send out any messages on the CAN-bus, as it’s manufactured for a PWM-application.

Actuator Pinout

Pin # Function
1 12V+

Well, how do you find out which type of actuator you have? In my case it was easy as the connector & a bit of stripped wiring was supplied with the turbocharger & actuator. On the connector, pins 1,2 and 4 were connected, in other words, +12V (red), GND (brown) and PWM (grey). If you don’t have the connector, Google will be your best friend.

VNT-actuator connector

Controlling the actuator

The PWM control signal depends on the OEM manufacturer request. Some actuators need a 140Hz PWM signal, while others accept 300Hz. There might also be other frequencies, but these two are the most typical ones on this type of Hella actuator. The signal is of grounding type, which means that the actuator outputs a steady signal on the PWM pin which is then modulated by a microcontroller grounding that pin in a timed sequence to achieve the desired PWM output.

I will ultimately control the actuator with my aftermarket ECU, but for testing purposes i hooked it up to my Arduino MEGA2560. Any Arduino or programmable microcontroller capable of PWM will be just fine though. Using Arduino, there are a couple things that needs to be taken into account:

  • The signal levels on the Arduino are 5V, while the VNT-actuator works with 12V, therefore some kind of level shifter has to be used. I used a TIP120-transistor.
  • With Arduino “real” PWM you cannot achieve 140Hz or 300Hz easily, or at least i couldn’t. I didn’t try to modify the timers/registers further, but used “software PWM” instead, manipulating the pins manually.
  • Wiring

    So, for connecting everything you need an Arduino, a transistor, a 1k resistor, a breadboard and some wires.

    Wiring the VNT actuator

    The power supply feeds power to the Arduino and VNT actuator. PWM-signal is applied through a current-limiting 1k resistor to the Base of the TIP120, this is the “control signal” for the transistor. The PWM signal from pin 4 on the actuator is routed to the Collector of the TIP120. When Arduino applies voltage to the Base of the TIP120, current will flow through the TIP120 from the Collector to the Emitter. As the Emitter side is tied to GND, the PWM line will be grounded. This sequence, done in correctly timed cycles, will produce the control signal needed for the VNT actuator to function.

    The Code

     * Simple sweep for testing a Hella VNT-actuator @ 300Hz.
     * Uses digital pin 13 for PWM.
    void setup() {
      pinMode(13, OUTPUT);
    void loop() {
        for (int sweep = 0 ; sweep <=3333 ; sweep += 6) {
      digitalWrite(13, HIGH);
      digitalWrite(13, LOW);
      delayMicroseconds(3333 - sweep);
        for (int sweep = 3000 ; sweep >=166 ; sweep -= 6) {
      digitalWrite(13, HIGH);
      digitalWrite(13, LOW);
      delayMicroseconds(3333 - sweep);
            for (int sweep = 0 ; sweep <=3333 ; sweep += 33) {
      digitalWrite(13, HIGH);
      digitalWrite(13, LOW);
      delayMicroseconds(3333 - sweep);
        for (int sweep = 3000 ; sweep >=166 ; sweep -= 33) {
      digitalWrite(13, HIGH);
      digitalWrite(13, LOW);
      delayMicroseconds(3333 - sweep);

    The result

    As seen on the video, the actuator works just fine. It seems that this kind of actuator doesn’t like under 5% or over 95% duty cycle, and will possibly wind itself to start position if those limits are exceeded. Good to know if you’re controlling something critical.


    BMW E9x K-CAN-bus hacking with Arduino & MCP2515

    Hi there!

    Lately i’ve been busy working on a way to get some relevant information (like RPM, throttle position, torque, coolant temp. etc) from my 320d, as i want to build my own “info-screen” which will be displayed on the iDrive-display of my car.
    The best way to get extensive amount of engine operational data on any E6x/E8x/E9x would surely be to hack into the DME/DDE K-Line and send BMW-specific polls (in the same way that INPA/ISTA/Testo works using EDIABAS), and at first i tried to go that way, but couldn’t get the communication between my Raspberry Pi and the DDE6.0 on K-Line to work, so i decided to try the K-CAN-bus instead. It turned out to be really simple, using only an Arduino and an MCP2515-CAN-module.

    My setup

    • Arduino Mega2560
    • MCP2515 CAN-module
    • some jumper wires



    I use the Mega2560 for testing, but if i’m about to mount one permanently, i will switch to a smaller board. I usually go for the Arduino Pro Mini, which is small in size but has a decent amount of I/O and is easy to program with an USB-to-TTL-programmer. The MCP2515 uses SPI, so it can be connected to about all Arduino-models, only pin numbers will be different.

    The finished wiring looks like this:

    Next, off to find a suitable spot to tap into the K-CAN-bus. I used the wiring at the iDrive-display power connector because i already was familiar with removing the display (really simple, undo 2x TX10 screws and the screen pops out). There are surely other better spots to tap into the wiring, but this is one way to do it.

    Wiring for the arduino (Black/Red cable) tapped into the K-CAN.
    CAN H+ Green/orange, CAN L- Green:

    Screen back in, ready to be tested:


    Next, the Arduino needs the correct software. Fortunately, there are many suitable CAN-libraries online that seem to work with the MCP2515. I used Cory J Fowlers MCP_CAN_lib. There is an example sketch in the library called CAN_receive which will work on the BMW K-CAN-bus with some minor modifications.

    MCP_CAN CAN0(10); // Set CS to pin 10
    MCP_CAN CAN0(53); // Set CS to pin 53
    Because i’m using pin 53 on the Arduino Mega2560 for CS.

    if(CAN0.begin(MCP_ANY, CAN_500KBPS, MCP_16MHZ) == CAN_OK)
    if(CAN0.begin(MCP_ANY, CAN_100KBPS, MCP_8MHZ) == CAN_OK)
    Because the bus on my pre-LCI E91 is 100kb/s K-CAN (the newer ones from 2007 onwards have D-CAN @ 500kb/s). The frequency change from 16MHz to 8MHz is because my MCP2515-chip has an 8MHz oscillator, your module could be different.

    After sketch was uploaded and ignition turned on, there was a nice continous stream of K-CAN-bus data on my serial terminal.

    Standard ID: 0x130 DLC: 5 Data: 0x41 0x51 0x2D 0x00 0x01
    Standard ID: 0x0AA DLC: 8 Data: 0xAD 0x81 0xFC 0x00 0x00 0x00 0x84 0x00
    Standard ID: 0x1B8 DLC: 6 Data: 0x0F 0xC0 0x83 0x02 0xA0 0x21
    Standard ID: 0x4A7 DLC: 8 Data: 0x40 0x52 0xFF 0x01 0xFF 0xFF 0xFF 0xFF
    Standard ID: 0x1A6 DLC: 8 Data: 0x00 0x00 0x00 0x00 0x00 0x00 0x60 0xF8
    Standard ID: 0x0C0 DLC: 2 Data: 0xFE 0xFF
    Standard ID: 0x1B8 DLC: 6 Data: 0x0F 0xC1 0x83 0x02 0xA0 0x21
    Standard ID: 0x349 DLC: 5 Data: 0x90 0x1A 0x9A 0x20 0x00
    Standard ID: 0x1B4 DLC: 8 Data: 0x00 0xC0 0xE2 0xF9 0x00 0x30 0xFC 0x80
    Standard ID: 0x0CE DLC: 8 Data: 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00
    Standard ID: 0x1A0 DLC: 8 Data: 0x00 0x80 0x00 0x00 0x80 0x00 0xC8 0x6B
    Standard ID: 0x1D0 DLC: 8 Data: 0x61 0xFF 0x42 0xC5 0x00 0x00 0xCD 0xB0

    I was surprised how much data is actually passing through all the time!
    So how about interpreting the data? That’s something i will write about later. 😉

    If you’re in a hurry, this site will get you started in understanding the data in the K-CAN-bus.