Part 1A long while ago, I
wrote about how to send bespoke CANBUS messages to the ECU, which could be added/subtracted from a map’s output with a bit of ASM, effectively changing the map’s output by changing the value in the incoming CANBUS message. Interesting maybe, but it also relied upon specific CAN hardware and ideally RAM dumps, plus some obfuscated ASM and programming of external hardware. Stuff not available in everyone’s workshop so I guess it remained a curiosity for the most part.
Now this topic is similar but uses VCDS (or a similar tool that can log MB’s) so no need for bespoke microprocessors or CANBUS transceivers. You don’t even need any RAM dumps. Hopefully that makes it more appealing to the MED9.1 community – all you need is VCDS. It’s based upon Measuring Blocks and if you can create the custom measuring blocks described
here, then you can do this.
You need a way to talk to the ECU and I chose to use a channel that’s already working and well understood – the Measuring Blocks. But while you usually use MB’s to read values in the ECU, I’ve changed it around so the act of reading the MB causes a byte of RAM to be changed. So when VCDS requests a specific MB and the ECU steps through the specific ASM for that MB, instead of using a
lbz instruction (read from a byte of RAM), it uses a
stb instruction (write to a byte of RAM).
- Reading MB x causes the byte to increment (rolling over at 255 to 0)
- Reading MB y causes the byte to decrement (rolling over at 0 to 255)
- Reading MB z show the current value of the byte
Since VCDS polls the MB’s periodically (several time a second), if you keep reading MB x the byte will count up. Stop reading the MB and the byte stops. Read MB y and the byte counts down. With a bit of practice you can use the up and down MB’s to get the count to stop at whatever value you want. The count changes about once a second (or faster if you set VCDS to Turbo [High-Speed] mode).
In the example below, MB49 contains the byte value. MB48 decrements the value and MB47 increments the value. I used MB 47, 48 & 49 because they were unused in my ecu bin and also because MB 175, 176 & 177 were free as well (47 + 128 = 175, 48 + 128 = 176, 49 + 128 = 177). VCDS reads your current block plus another block 128 higher, that’s just the way the protocol works.
To look at the current value without changing it. Just look at MB49:
To decrement the value. Look at MB 48:
To increment the value. Look at MB 47:
When the count in MB49 has reached the value you want, stop looking at MB 47 or MB48 (change VCDS to another MB) to stop reading them and stop the value changing.
Q. But why not use diagnostic services like DynamicallyDefineLocalIdentifier and WriteDataByLocalIdentifier? Or Adaptation Channels or Output Tests? Surely that’s why they are there in the first place?
A. Firstly I never could get
WriteDataByLocalIdentifier to work and secondly I wanted to keep it within the capabilities of what VCDS can do. It can definitely do MB’s. If someone wants to show me how to reverse-engineer and re-task the adaptation, that would be nice as well
But for now it’s Measuring Blocks.
But why would you want to change a byte? Well, if we write the byte like this:
255 dec = 1111 1111 bin
We can then start using different parts of the byte to do different things. For example:
1111 11
xx00 – add nothing to KFZW
01 – subtract 0.75 degrees from KFZW
10 – add 1.5 degrees to KFZW
11 – add 0.75 degrees to KFZW
1111
yy11
00 – add nothing to LDRXN
01 – subtract 20 from LDRXN
10 – add 25 to LDRXN
11 – add 10 to LDRXN
11
zz 1111
00 – add nothing to LAMFA
01 – subtract 0.02343 from LAMFA
10 – add 0.05468 to LAMFA
11 – add 0.02343 to LAMFA
The byte itself is part of NVRAM (Non-volatile RAM) so it remembers the value when you switch the ignition key off. But if you remove the battery or flash a new bin, then it resets. It’s not stored in EEPROM (E2P) which is a different topic described here. All I did to find a free NVRAM location was to look at the FR for a random variable with NV (for NVRAM) written next to it in the pictures, look up where that variable was in IDA and choose an empty (unused) location as close to that as possible. The ECU won’t store individual variables in NVRAM, it’ll do an entire range so if you are close to an existing variable, you should be OK.
Q. Could you use E2P instead of NVRAM?
A. Sure, but then you have to figure out where the E2P is and also calculate a checksum for the E2P. It’s all described
here, but honestly being able to reset it back to default by just unplugging the battery is not always a bad thing.
Looking at the actual code for the measuring blocks in more detail, here’s a step-through of the count-up routine as an example. VCDS polls several times a second and that means the count flies past too quickly to read. I used an outer loop and inner loop to slow things down - for every four outer loop cycles the inner loop cycles once (just like a gearbox, what a terrific pun on an automotive forum). The net effect is to slow the count to once a second with VCDS in non-turbo mode and if you want it to change faster then put VCDS in turbo mode.
ROM:0047E200 lbz r12, byte_807FD0 <- temporary scratch variable for the outer loop (count 0 – 1 – 2 – 3 – 0 …) Just normal RAM, not NVRAM
ROM:0047E204 cmplwi r12, 3
ROM:0047E208 bne loc_47E230
ROM:0047E20C lbz r11, byte_7FA04E <- load NVRAM into r11, wrap back to 0 if 255
ROM:0047E210 cmplwi r11, 0xFF
ROM:0047E214 bne loc_47E220
ROM:0047E218 li r11, 0
ROM:0047E21C b loc_47E224
ROM:0047E220 addi r11, r11, 1 <- increment r11 and store back in NVRAM
ROM:0047E224 stb r11, byte_7FA04E
ROM:0047E228 li r12, 0
ROM:0047E22C b loc_47E234
ROM:0047E230 addi r12, r12, 1
ROM:0047E234 stb r12, byte_807FD0
ROM:0047E238 li r3, 0x11 <- measuring block type 0x11 which is a ASCII character MB type
ROM:0047E23C li r4, 0x55 <- ASCII ‘U’
ROM:0047E240 li r5, 0x50 <- ASCII ‘P’
ROM:0047E244 b sub_43930C
More comments about the code in the attached spreadsheet as well